Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6dd00c5f8f | |||
| e68337886f | |||
| 3b639f53fd | |||
| 3199ad88fd | |||
| a221d18d33 | |||
| a9eeb3bb1c | |||
| 844a282fd7 | |||
| 9da352a315 | |||
| 34b8f7c926 | |||
| 5d5cedec58 | |||
| 8441a8b752 | |||
| d64d88e0a5 | |||
| 5eeb9cc702 | |||
| fc3411e568 | |||
| 77ce9e8ad4 | |||
| 63dd8ebb83 | |||
| 7cf45c9ac8 | |||
| 2f8de0bed2 | |||
| 67ca030bb2 | |||
| da46399ded | |||
| e5d0b6a9a2 | |||
| f4ca2115d4 | |||
| 0cc7f8f445 | |||
| 767e894acd | |||
| 06e2afef1d | |||
| f614c14013 | |||
| 998c07b90b | |||
| 3d44c82e65 | |||
| 08a453b5d5 | |||
| be42963441 | |||
| 50ad374124 | |||
| f160e6d311 | |||
| 6d946a123f | |||
| c758d57f2a |
@@ -1,25 +1,37 @@
|
|||||||
# LinuxPA
|
# LinuxPA
|
||||||
The goal is to create a fully functional PortableApps.com type launcher that can properly parse data from the PortableApps.com format. Apps are launched by a .sh file in the app's directory. Currently pulls out the Name and Category from App/AppInfo/appinfo.ini
|
LinuxPA is a try to bring a [PortableApps.com](http://portableapps.com) type launcher to Linux.
|
||||||
Works well with AppImage apps.
|
|
||||||
|
|
||||||
# Why?
|
# How to use
|
||||||
I know that Linux only has about 2% desktop usage and I know that the traditional way to install apps isn't portable, but over the past year or so I've started to put linux apps on my flash drive (AppImage is a great example of a portable solution to linux apps. Also a lot of DRM-free games can be run portably), but there was no easy way to organize my linux apps, so I created one. I personally have used the PortableApps.com launcher for years now and I love how properly formated the apps are, which allows me to grab info about the app easily.
|
Just double click on an app to launch it! If there are multiple executables, you can either select the specific executable, or if you just double click the app it'll launch the first linux executable (.sh script files have priority), but if one isn't found it launches the first executable in general.
|
||||||
|
|
||||||
# Why script files?
|
# Apps:
|
||||||
In general linux executable files have no extensions and can be a pain when trying to figure out what is executable and what isn't. I figured script files are easy to detect and allow a large amount of flexibility for me (and others who want to make apps work with this launcher).
|
Both of the below places provide linux executables that don't need libs installed on the host system:
|
||||||
|
[AppImage](https://bintray.com/probono/AppImages)
|
||||||
|
|
||||||
# Why Go?
|
# PortableApps.com Compatibility
|
||||||
Because I like Go :) Also the way it includes all it needs into one friendly executable.
|
LinuxPA works will with the PortableApps.com launcher, as it looks for apps in the PortableApps folder and grabs the app's name and icon from where it should be in the PortableApps.com format.
|
||||||
|
My forum at PortableApps.com can be found [here](http://portableapps.com/node/54998).
|
||||||
|
|
||||||
# What is needed?
|
# common.sh
|
||||||
Basically you need go to compile the source, AND YOU ALSO NEED TO MOUNT YOUR FLASH DRIVE SO YOU CAN EXECUTE FILES ON IT!!!! I've found that the mount arguments of `exec,noauto,nodev,nosuid,umask=0000` works well (I personally put my flash drive into /etc/fstab).
|
common.sh is found in the PortableApps/LinuxPACom folder and is executed before the app. I mainly use it to set environment variables (such as HOME).
|
||||||
|
|
||||||
# Format
|
# Simple App Setup
|
||||||
The first place the program looks for an app's icon and info is in the /App/AppInfo directory (icon defaults to appicon_32.png, otherwise it just picks the last one it finds), but if it can't find the appinfo.ini or app icon, it looks in the apps root directory for appinfo.ini and appicon.png for info and icon respectively(Just to make it easier for custom settings in an app).
|
Because apps aren't natively formated in the PortableApps.com format, if LinuxPA doesn't find the AppInfo.ini or appicon_\*.png in the App/AppInfo folder of the app it looks for them in the root directory of the app (except it looks, nor for appicon_\*.png, but appicon.png). If an AppInfo.ini file isn't found then the name of the app is grabbed from the folder name and it's category is set to other. It specifically looks for the lines starting with `Name=` and `Category=`
|
||||||
|
|
||||||
# TODO
|
# AppImage Support
|
||||||
Add in a common.sh that is executed with each script. (Allows for setting environment variables such as HOME)
|
[AppImage Website](http://appimage.org)
|
||||||
MAKE IT BETTER
|
Right now AppImages are simply supported via the native linux executable support, but later I'm hoping to add downloading and automatic updating support later on.
|
||||||
Add an open button (I know, I just wanted to get the initial working before making it user friendly)
|
|
||||||
Check if all apps are closed when it closes and ask if you want to force stop the apps.
|
# USB mount
|
||||||
(Maybe)Create an installer.
|
Unfortunately Linux, by default, doesn't support running executables off of FAT formated flash drives, requiring you to mount your drive with special mount arguments or format in a linux friendly format (such as EXT4). I personally use the arguments `exec,noauto,nodev,nosuid,umask=0000`
|
||||||
|
|
||||||
|
# Screenshots
|
||||||
|
Photos are found [Here](https://goo.gl/photos/VtBUL6DyZTMidj5n6)
|
||||||
|
|
||||||
|
# TODO (Might be in order)
|
||||||
|
1. MAKE IT BETTER
|
||||||
|
1. Add settings menu
|
||||||
|
1. Add updater for .AppImage files
|
||||||
|
1. Download .AppImage files (maybe)
|
||||||
|
1. Check if all apps are closed when it closes and ask if you want to force stop the apps.
|
||||||
|
1. Portable wine (Should be able to get a working version from PlayOnLinux, but I need to add a check to see if filesystem is EXT as Wine doesn't like filesystems w/o permission control)
|
||||||
|
|||||||
@@ -0,0 +1,308 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gotk3/gotk3/gdk"
|
||||||
|
"github.com/gotk3/gotk3/gtk"
|
||||||
|
)
|
||||||
|
|
||||||
|
type app struct {
|
||||||
|
name string
|
||||||
|
cat string
|
||||||
|
appimg []string
|
||||||
|
lin []string
|
||||||
|
ex []string
|
||||||
|
icon *gdk.Pixbuf
|
||||||
|
dir string
|
||||||
|
ini *os.File
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *app) getTreeIter(store *gtk.TreeStore) *gtk.TreeIter {
|
||||||
|
it := store.Append(nil)
|
||||||
|
store.SetValue(it, 0, a.icon)
|
||||||
|
store.SetValue(it, 1, a.name)
|
||||||
|
if len(a.ex) > 1 {
|
||||||
|
for _, v := range a.ex {
|
||||||
|
i := store.Append(it)
|
||||||
|
store.SetValue(i, 1, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return it
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *app) launch() {
|
||||||
|
if len(a.ex) == 1 {
|
||||||
|
if wine {
|
||||||
|
var cmd *exec.Cmd
|
||||||
|
if !contains(a.lin, a.ex[0]) {
|
||||||
|
cmd = exec.Command("/bin/sh", "-c", "cd \""+a.dir+"\"; wine \""+a.ex[0]+"\"")
|
||||||
|
} else {
|
||||||
|
if comEnbld {
|
||||||
|
cmd = exec.Command("/bin/sh", "-c", ". PortableApps/LinuxPACom/common.sh || exit 1;cd \""+a.dir+"\"; \"./"+a.ex[0]+"\"")
|
||||||
|
} else {
|
||||||
|
cmd = exec.Command("/bin/sh", "-c", "cd \""+a.dir+"\"; \"./"+a.ex[0]+"\"")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
cmd.Start()
|
||||||
|
} else {
|
||||||
|
var cmd *exec.Cmd
|
||||||
|
if comEnbld {
|
||||||
|
cmd = exec.Command("/bin/sh", "-c", ". PortableApps/LinuxPACom/common.sh || exit 1;cd \""+a.dir+"\"; \"./"+a.ex[0]+"\"")
|
||||||
|
} else {
|
||||||
|
cmd = exec.Command("/bin/sh", "-c", "cd \""+a.dir+"\"; \"./"+a.ex[0]+"\"")
|
||||||
|
}
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
cmd.Start()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if wine {
|
||||||
|
var cmd *exec.Cmd
|
||||||
|
if len(a.lin) == 0 {
|
||||||
|
cmd = exec.Command("/bin/sh", "-c", "cd \""+a.dir+"\"; wine \""+a.ex[0]+"\"")
|
||||||
|
} else {
|
||||||
|
var ind int
|
||||||
|
for i, v := range a.lin {
|
||||||
|
if strings.HasSuffix(v, ".sh") {
|
||||||
|
ind = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if comEnbld {
|
||||||
|
cmd = exec.Command("/bin/sh", "-c", ". PortableApps/LinuxPACom/common.sh || exit 1;cd \""+a.dir+"\"; \"./"+a.lin[ind]+"\"")
|
||||||
|
} else {
|
||||||
|
cmd = exec.Command("/bin/sh", "-c", "cd \""+a.dir+"\"; \"./"+a.lin[ind]+"\"")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
cmd.Start()
|
||||||
|
} else {
|
||||||
|
if len(a.lin) != 0 {
|
||||||
|
var ind int
|
||||||
|
for i, v := range a.lin {
|
||||||
|
if strings.HasSuffix(v, ".sh") {
|
||||||
|
ind = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var cmd *exec.Cmd
|
||||||
|
if comEnbld {
|
||||||
|
cmd = exec.Command("/bin/sh", "-c", ". PortableApps/LinuxPACom/common.sh || exit 1;cd \""+a.dir+"\"; \"./"+a.lin[ind]+"\"")
|
||||||
|
} else {
|
||||||
|
cmd = exec.Command("/bin/sh", "-c", "cd \""+a.dir+"\"; \"./"+a.lin[ind]+"\"")
|
||||||
|
}
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
cmd.Start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *app) launchSub(sub int) {
|
||||||
|
if wine {
|
||||||
|
var cmd *exec.Cmd
|
||||||
|
if !contains(a.lin, a.ex[sub]) {
|
||||||
|
cmd = exec.Command("/bin/sh", "-c", "cd \""+a.dir+"\"; wine \""+a.ex[sub]+"\"")
|
||||||
|
} else {
|
||||||
|
if comEnbld {
|
||||||
|
cmd = exec.Command("/bin/sh", "-c", ". PortableApps/LinuxPACom/common.sh || exit 1;cd \""+a.dir+"\"; \"./"+a.ex[sub]+"\"")
|
||||||
|
} else {
|
||||||
|
cmd = exec.Command("/bin/sh", "-c", "cd \""+a.dir+"\"; \"./"+a.ex[sub]+"\"")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
cmd.Start()
|
||||||
|
}
|
||||||
|
var cmd *exec.Cmd
|
||||||
|
if comEnbld {
|
||||||
|
cmd = exec.Command("/bin/sh", "-c", ". PortableApps/LinuxPACom/common.sh || exit 1;cd \""+a.dir+"\"; \"./"+a.ex[sub]+"\"")
|
||||||
|
} else {
|
||||||
|
cmd = exec.Command("/bin/sh", "-c", "cd \""+a.dir+"\"; \"./"+a.ex[sub]+"\"")
|
||||||
|
}
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
cmd.Start()
|
||||||
|
}
|
||||||
|
|
||||||
|
// type appExNode struct {
|
||||||
|
// ap app
|
||||||
|
// exInd int
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func (a *appExNode) launch() {
|
||||||
|
// if wine {
|
||||||
|
// var cmd *exec.Cmd
|
||||||
|
// if !contains(a.ap.lin, a.ap.ex[a.exInd]) {
|
||||||
|
// cmd = exec.Command("/bin/sh", "-c", "cd \""+a.ap.dir+"\"; wine \""+a.ap.ex[a.exInd]+"\"")
|
||||||
|
// } else {
|
||||||
|
// if comEnbld {
|
||||||
|
// cmd = exec.Command("/bin/sh", "-c", ". PortableApps/LinuxPACom/common.sh || exit 1;cd \""+a.ap.dir+"\"; \"./"+a.ap.ex[a.exInd]+"\"")
|
||||||
|
// } else {
|
||||||
|
// cmd = exec.Command("/bin/sh", "-c", "cd \""+a.ap.dir+"\"; \"./"+a.ap.ex[a.exInd]+"\"")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// cmd.Stdout = os.Stdout
|
||||||
|
// cmd.Stderr = os.Stderr
|
||||||
|
// cmd.Start()
|
||||||
|
// }
|
||||||
|
// var cmd *exec.Cmd
|
||||||
|
// if comEnbld {
|
||||||
|
// cmd = exec.Command("/bin/sh", "-c", ". PortableApps/LinuxPACom/common.sh || exit 1;cd \""+a.ap.dir+"\"; \"./"+a.ap.ex[a.exInd]+"\"")
|
||||||
|
// } else {
|
||||||
|
// cmd = exec.Command("/bin/sh", "-c", "cd \""+a.ap.dir+"\"; \"./"+a.ap.ex[a.exInd]+"\"")
|
||||||
|
// }
|
||||||
|
// cmd.Stdout = os.Stdout
|
||||||
|
// cmd.Stderr = os.Stderr
|
||||||
|
// cmd.Start()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func (a *appExNode) Count() int {
|
||||||
|
// return 0
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func (a *appExNode) NodeAt(int) gxui.TreeNode {
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func (a *appExNode) ItemIndex(gxui.AdapterItem) int {
|
||||||
|
// return -1
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func (a *appExNode) Item() gxui.AdapterItem {
|
||||||
|
// if wine {
|
||||||
|
// return a.ap.ex[a.exInd]
|
||||||
|
// }
|
||||||
|
// return a.ap.lin[a.exInd]
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func (a *appExNode) Create(the gxui.Theme) gxui.Control {
|
||||||
|
// box := the.CreateLinearLayout()
|
||||||
|
// box.SetDirection(gxui.LeftToRight)
|
||||||
|
// box.SetVerticalAlignment(gxui.AlignMiddle)
|
||||||
|
// img := the.CreateImage()
|
||||||
|
// img.SetTexture(a.ap.icon)
|
||||||
|
// img.SetExplicitSize(math.Size{H: 32, W: 32})
|
||||||
|
// lbl := the.CreateLabel()
|
||||||
|
// lbl.SetText(a.ap.ex[a.exInd])
|
||||||
|
// box.AddChild(img)
|
||||||
|
// box.AddChild(lbl)
|
||||||
|
// box.OnDoubleClick(func(gxui.MouseEvent) {
|
||||||
|
// a.launch()
|
||||||
|
// })
|
||||||
|
// return box
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func (a *appNode) Count() int {
|
||||||
|
// if wine {
|
||||||
|
// if len(a.ex) > 1 {
|
||||||
|
// return len(a.ap.ex)
|
||||||
|
// }
|
||||||
|
// return 0
|
||||||
|
// }
|
||||||
|
// if len(a.ap.lin) > 1 {
|
||||||
|
// return len(a.ap.lin)
|
||||||
|
// }
|
||||||
|
// return 0
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func (a *appNode) NodeAt(i int) gxui.TreeNode {
|
||||||
|
// return &appExNode{ap: a.ap, exInd: i}
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func (a *appNode) ItemIndex(item gxui.AdapterItem) int {
|
||||||
|
// if wine {
|
||||||
|
// for i, v := range a.ap.ex {
|
||||||
|
// if v == item {
|
||||||
|
// return i
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// for i, v := range a.ap.lin {
|
||||||
|
// if v == item {
|
||||||
|
// return i
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return -1
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func (a *appNode) Item() gxui.AdapterItem {
|
||||||
|
// return a.ap.name
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func (a *appNode) Create(the gxui.Theme) gxui.Control {
|
||||||
|
// box := the.CreateLinearLayout()
|
||||||
|
// box.SetDirection(gxui.LeftToRight)
|
||||||
|
// box.SetPadding(math.CreateSpacing(2))
|
||||||
|
// box.SetVerticalAlignment(gxui.AlignMiddle)
|
||||||
|
// img := the.CreateImage()
|
||||||
|
// if a.ap.icon != nil {
|
||||||
|
// img.SetTexture(a.ap.icon)
|
||||||
|
// }
|
||||||
|
// img.SetExplicitSize(math.Size{H: 32, W: 32})
|
||||||
|
// lbl := the.CreateLabel()
|
||||||
|
// lbl.SetText(a.ap.name)
|
||||||
|
// box.AddChild(img)
|
||||||
|
// box.AddChild(lbl)
|
||||||
|
// box.OnDoubleClick(func(gxui.MouseEvent) {
|
||||||
|
// a.launch()
|
||||||
|
// })
|
||||||
|
// return box
|
||||||
|
// }
|
||||||
|
|
||||||
|
// type catAdap struct {
|
||||||
|
// gxui.AdapterBase
|
||||||
|
// cat string
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func (a *catAdap) setCat(cat string) {
|
||||||
|
// a.cat = cat
|
||||||
|
// a.DataChanged(false)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func (a *catAdap) refresh() {
|
||||||
|
// a.DataChanged(false)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func (a *catAdap) Count() int {
|
||||||
|
// if wine {
|
||||||
|
// return len(master[a.cat])
|
||||||
|
// }
|
||||||
|
// return len(linmaster[a.cat])
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func (a *catAdap) NodeAt(i int) gxui.TreeNode {
|
||||||
|
// if wine {
|
||||||
|
// return &appNode{ap: master[a.cat][i]}
|
||||||
|
// }
|
||||||
|
// return &appNode{ap: linmaster[a.cat][i]}
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func (a *catAdap) Size(gxui.Theme) math.Size {
|
||||||
|
// return math.Size{H: 34, W: math.MaxSize.W}
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func (a *catAdap) ItemIndex(item gxui.AdapterItem) int {
|
||||||
|
// if wine {
|
||||||
|
// for i, v := range master[a.cat] {
|
||||||
|
// if v.name == item {
|
||||||
|
// return i
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// for i, v := range linmaster[a.cat] {
|
||||||
|
// if v.name == item {
|
||||||
|
// return i
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return -1
|
||||||
|
// }
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
|
||||||
|
"github.com/nelsam/gxui"
|
||||||
|
"github.com/nelsam/gxui/drivers/gl"
|
||||||
|
"github.com/nelsam/gxui/themes/dark"
|
||||||
|
"github.com/nelsam/gxui/themes/light"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
version = "1.1.0.0"
|
||||||
|
defIni = "[basic]\ntheme=dk"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
dr gxui.Driver
|
||||||
|
th gxui.Theme
|
||||||
|
master map[string][]app
|
||||||
|
linmaster map[string][]app
|
||||||
|
cats []string
|
||||||
|
lin []string
|
||||||
|
wine bool
|
||||||
|
comEnbld bool
|
||||||
|
darkTheme = true
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
updated := false
|
||||||
|
os.MkdirAll("PortableApps/LinuxPACom", 0777)
|
||||||
|
stat, err := versionDL()
|
||||||
|
if stat {
|
||||||
|
res := getVersionFileInfo()
|
||||||
|
if res != "Error!" {
|
||||||
|
stat, err = checkForUpdate(res)
|
||||||
|
if stat {
|
||||||
|
downloadUpdate(res)
|
||||||
|
updated = true
|
||||||
|
} else {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Println("Failed Version File Info")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
if updated {
|
||||||
|
cmd := exec.Command("./LinuxPA")
|
||||||
|
cmd.Stdin = os.Stdin
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Start()
|
||||||
|
} else {
|
||||||
|
master = make(map[string][]app)
|
||||||
|
linmaster = make(map[string][]app)
|
||||||
|
gl.StartDriver(appMain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func appMain(dri gxui.Driver) {
|
||||||
|
dr = dri
|
||||||
|
setup()
|
||||||
|
if darkTheme {
|
||||||
|
th = dark.CreateTheme(dr)
|
||||||
|
} else {
|
||||||
|
th = light.CreateTheme(dr)
|
||||||
|
}
|
||||||
|
th = dark.CreateTheme(dr)
|
||||||
|
ui()
|
||||||
|
}
|
||||||
|
|
||||||
|
func contains(arr []string, str string) bool {
|
||||||
|
for _, v := range arr {
|
||||||
|
if v == str {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
+208
@@ -0,0 +1,208 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"image/draw"
|
||||||
|
_ "image/png"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/nelsam/gxui"
|
||||||
|
)
|
||||||
|
|
||||||
|
func setup() {
|
||||||
|
PortableAppsFold, err := os.Open("PortableApps")
|
||||||
|
if PAStat, _ := PortableAppsFold.Stat(); err != nil || !PAStat.IsDir() {
|
||||||
|
os.Mkdir("PortableApps", 0777)
|
||||||
|
PortableAppsFold, err = os.Open("PortableApps")
|
||||||
|
if err != nil {
|
||||||
|
panic("Can't find PortableApps folder and can't create one!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, err = os.Open("PortableApps/LinuxPACom"); err != nil {
|
||||||
|
os.Mkdir("PortableApps/LinuxPACom", 0777)
|
||||||
|
}
|
||||||
|
fmt.Println(err)
|
||||||
|
_, err = os.Open("PortableApps/LinuxPACom/common.sh")
|
||||||
|
if err == nil {
|
||||||
|
comEnbld = true
|
||||||
|
}
|
||||||
|
fi, err := os.Open("PortableApps/LinuxPACom/Info.ini")
|
||||||
|
if err != nil {
|
||||||
|
fi, err = os.Create("PortableApps/LinuxPACom/Info.ini")
|
||||||
|
if err == nil {
|
||||||
|
wrt := bufio.NewWriter(fi)
|
||||||
|
wrt.WriteString(defIni)
|
||||||
|
wrt.Flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
rdr := bufio.NewReader(fi)
|
||||||
|
for err != nil {
|
||||||
|
ln, _, error := rdr.ReadLine()
|
||||||
|
err = error
|
||||||
|
str := string(ln)
|
||||||
|
if strings.HasPrefix(str, "theme=") {
|
||||||
|
str = strings.TrimPrefix(str, "theme=")
|
||||||
|
if str == "lt" {
|
||||||
|
darkTheme = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PAFolds, _ := PortableAppsFold.Readdirnames(-1)
|
||||||
|
sort.Strings(PAFolds)
|
||||||
|
for _, v := range PAFolds {
|
||||||
|
fold, _ := os.Open("PortableApps/" + v)
|
||||||
|
if stat, _ := fold.Stat(); stat.IsDir() && stat.Name() != "PortableApps.com" && stat.Name() != "LinuxPACom" {
|
||||||
|
ap := processApp("PortableApps/" + v)
|
||||||
|
if !reflect.DeepEqual(ap, app{}) {
|
||||||
|
if _, ok := master[ap.cat]; !ok {
|
||||||
|
cats = append(cats, ap.cat)
|
||||||
|
sort.Strings(cats)
|
||||||
|
}
|
||||||
|
if len(ap.lin) != 0 {
|
||||||
|
if _, ok := linmaster[ap.cat]; !ok {
|
||||||
|
lin = append(lin, ap.cat)
|
||||||
|
sort.Strings(lin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
master[ap.cat] = append(master[ap.cat], ap)
|
||||||
|
if len(ap.lin) != 0 {
|
||||||
|
linmaster[ap.cat] = append(linmaster[ap.cat], ap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func processApp(fold string) (out app) {
|
||||||
|
wd, _ := os.Getwd()
|
||||||
|
out.dir = wd + "/" + fold
|
||||||
|
out.ini = findInfo(fold)
|
||||||
|
if out.ini != nil {
|
||||||
|
out.name = getName(out.ini)
|
||||||
|
out.ini = findInfo(fold)
|
||||||
|
out.cat = getCat(out.ini)
|
||||||
|
out.ini = findInfo(fold)
|
||||||
|
}
|
||||||
|
if out.name == "" {
|
||||||
|
out.name = strings.TrimPrefix(fold, "PortableApps/")
|
||||||
|
}
|
||||||
|
if out.cat == "" {
|
||||||
|
out.cat = "Other"
|
||||||
|
}
|
||||||
|
out.icon = getIcon(fold)
|
||||||
|
folder, _ := os.Open(fold)
|
||||||
|
fis, _ := folder.Readdirnames(-1)
|
||||||
|
for _, v := range fis {
|
||||||
|
tmp, _ := os.Open(fold + "/" + v)
|
||||||
|
if stat, _ := tmp.Stat(); stat.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(strings.ToLower(v), ".appimage") {
|
||||||
|
out.appimg = append(out.appimg, v)
|
||||||
|
out.ex = append(out.ex, v)
|
||||||
|
out.lin = append(out.lin, v)
|
||||||
|
} else if strings.HasSuffix(strings.ToLower(v), ".exe") {
|
||||||
|
out.ex = append(out.ex, v)
|
||||||
|
} else {
|
||||||
|
btys := make([]byte, 4)
|
||||||
|
rdr := bufio.NewReader(tmp)
|
||||||
|
rdr.Read(btys)
|
||||||
|
if (strings.Contains(strings.ToLower(string(btys)), "elf") && !strings.HasSuffix(strings.ToLower(v), ".so") && !strings.Contains(v, ".so.")) || strings.HasPrefix(strings.ToLower(string(btys)), "#!") {
|
||||||
|
out.ex = append(out.ex, v)
|
||||||
|
out.lin = append(out.lin, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(out.ex) == 0 {
|
||||||
|
return app{}
|
||||||
|
}
|
||||||
|
if len(out.lin) == 0 {
|
||||||
|
out.name += " (Wine)"
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCat(ini *os.File) string {
|
||||||
|
rdr := bufio.NewReader(ini)
|
||||||
|
var ret string
|
||||||
|
for line, _, err := rdr.ReadLine(); err == nil; line, _, err = rdr.ReadLine() {
|
||||||
|
if strings.HasPrefix(string(line), "Category=") {
|
||||||
|
ret = strings.TrimPrefix(string(line), "Category=")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rdr.Reset(ini)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func getName(ini *os.File) string {
|
||||||
|
rdr := bufio.NewReader(ini)
|
||||||
|
var ret string
|
||||||
|
for line, _, err := rdr.ReadLine(); err == nil; line, _, err = rdr.ReadLine() {
|
||||||
|
if strings.HasPrefix(string(line), "Name=") {
|
||||||
|
ret = strings.TrimPrefix(string(line), "Name=")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rdr.Reset(ini)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIcon(fold string) gxui.Texture {
|
||||||
|
var pic *os.File
|
||||||
|
if folder, err := os.Open(fold + "/App/AppInfo"); err == nil {
|
||||||
|
fis, _ := folder.Readdir(-1)
|
||||||
|
var pics []string
|
||||||
|
for _, v := range fis {
|
||||||
|
if !v.IsDir() && strings.HasSuffix(strings.ToLower(v.Name()), ".png") && strings.HasPrefix(strings.ToLower(v.Name()), "appicon_") {
|
||||||
|
pics = append(pics, v.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Strings(pics)
|
||||||
|
if len(pics) > 1 {
|
||||||
|
var ind int
|
||||||
|
if !contains(pics, "appicon_32.png") {
|
||||||
|
ind = len(pics) - 1
|
||||||
|
} else {
|
||||||
|
ind = sort.SearchStrings(pics, "appicon_32.png")
|
||||||
|
}
|
||||||
|
pic, _ = os.Open(fold + "/App/AppInfo/" + pics[ind])
|
||||||
|
}
|
||||||
|
} else if fi, err := os.Open(fold + "/appicon.png"); err == nil {
|
||||||
|
pic = fi
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
img, _, err := image.Decode(pic)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
rgba := image.NewRGBA(img.Bounds())
|
||||||
|
draw.Draw(rgba, img.Bounds(), img, image.ZP, draw.Src)
|
||||||
|
ret := dr.CreateTexture(rgba, 1)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func findInfo(fold string) *os.File {
|
||||||
|
tmp, err := os.Open(fold + "/App/AppInfo")
|
||||||
|
if err == nil {
|
||||||
|
fis, _ := tmp.Readdirnames(-1)
|
||||||
|
for _, v := range fis {
|
||||||
|
if strings.ToLower(v) == "appinfo.ini" {
|
||||||
|
tmp, _ := os.Open(fold + "/App/AppInfo/" + v)
|
||||||
|
return tmp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if fi, err := os.Open(fold + "/appinfo.ini"); err == nil {
|
||||||
|
return fi
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
Executable → Regular
+66
@@ -0,0 +1,66 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
|
||||||
|
"github.com/nelsam/gxui"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ui() {
|
||||||
|
catListAdap := &StrList{}
|
||||||
|
appListAdap := &catAdap{}
|
||||||
|
catListAdap.SetStrings(lin)
|
||||||
|
win := th.CreateWindow(500, 500, "LinuxPA")
|
||||||
|
top := th.CreateLinearLayout()
|
||||||
|
top.SetDirection(gxui.BottomToTop)
|
||||||
|
splBox := th.CreateLinearLayout()
|
||||||
|
spl := th.CreateSplitterLayout()
|
||||||
|
spl.SetOrientation(gxui.Horizontal)
|
||||||
|
catList := th.CreateList()
|
||||||
|
catList.SetAdapter(catListAdap)
|
||||||
|
catList.OnSelectionChanged(func(it gxui.AdapterItem) {
|
||||||
|
appListAdap.setCat(it.(string))
|
||||||
|
})
|
||||||
|
appList := th.CreateTree()
|
||||||
|
appList.SetAdapter(appListAdap)
|
||||||
|
spl.AddChild(catList)
|
||||||
|
spl.AddChild(appList)
|
||||||
|
splBox.AddChild(spl)
|
||||||
|
butBox := th.CreateLinearLayout()
|
||||||
|
butBox.SetDirection(gxui.LeftToRight)
|
||||||
|
if _, err := exec.LookPath("wine"); err == nil {
|
||||||
|
wineBut := th.CreateButton()
|
||||||
|
wineBut.SetType(gxui.ToggleButton)
|
||||||
|
wineBut.SetChecked(wine)
|
||||||
|
wineBut.SetText("Show Windows Apps")
|
||||||
|
wineBut.OnClick(func(gxui.MouseEvent) {
|
||||||
|
wine = wineBut.IsChecked()
|
||||||
|
appListAdap.refresh()
|
||||||
|
if wineBut.IsChecked() {
|
||||||
|
catListAdap.SetStrings(cats)
|
||||||
|
wineBut.SetText("Hide Windows Apps")
|
||||||
|
} else {
|
||||||
|
catListAdap.SetStrings(lin)
|
||||||
|
wineBut.SetText("Show Windows Apps")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
_, err := os.Open("Start.exe")
|
||||||
|
if err == nil {
|
||||||
|
pa := th.CreateButton()
|
||||||
|
pa.SetText("Open PortableApps Launcher")
|
||||||
|
pa.OnClick(func(gxui.MouseEvent) {
|
||||||
|
cmd := exec.Command("wine", "Start.exe")
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
cmd.Start()
|
||||||
|
})
|
||||||
|
butBox.AddChild(pa)
|
||||||
|
}
|
||||||
|
butBox.AddChild(wineBut)
|
||||||
|
}
|
||||||
|
top.AddChild(butBox)
|
||||||
|
top.AddChild(splBox)
|
||||||
|
win.AddChild(top)
|
||||||
|
win.OnClose(dr.Terminate)
|
||||||
|
}
|
||||||
@@ -1,109 +1,60 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/nelsam/gxui/drivers/gl"
|
"github.com/gotk3/gotk3/gtk"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
version = "2.0.1.0"
|
||||||
|
defIni = ""
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
appMaster map[string][]prtap
|
master map[string][]app
|
||||||
|
linmaster map[string][]app
|
||||||
cats []string
|
cats []string
|
||||||
|
lin []string
|
||||||
|
wine bool
|
||||||
|
comEnbld bool
|
||||||
|
wineAvail bool
|
||||||
|
showMsg = true
|
||||||
)
|
)
|
||||||
|
|
||||||
type prtap struct {
|
|
||||||
name string
|
|
||||||
cat string
|
|
||||||
ex string
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
appMaster = make(map[string][]prtap)
|
os.MkdirAll("PortableApps/LinuxPACom", 0777)
|
||||||
os.Mkdir("PortableApps", 0777)
|
master = make(map[string][]app)
|
||||||
pa, err := os.Open("PortableApps")
|
linmaster = make(map[string][]app)
|
||||||
|
uiStart()
|
||||||
|
}
|
||||||
|
|
||||||
|
func uiStart() {
|
||||||
|
gtk.Init(nil)
|
||||||
|
setup()
|
||||||
|
win, err := gtk.WindowNew(gtk.WINDOW_TOPLEVEL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
fmt.Println("Window not created", err)
|
||||||
}
|
}
|
||||||
appstmp, _ := pa.Readdir(-1)
|
win.SetTitle("LinuxPA")
|
||||||
var folds []string
|
win.Connect("destroy", func() {
|
||||||
for _, v := range appstmp {
|
gtk.MainQuit()
|
||||||
if v.IsDir() {
|
})
|
||||||
folds = append(folds, v.Name())
|
win.SetDefaultSize(500, 500)
|
||||||
}
|
win.SetPosition(gtk.WIN_POS_CENTER)
|
||||||
}
|
ui(win)
|
||||||
sort.Strings(folds)
|
win.ShowAll()
|
||||||
for _, v := range folds {
|
win.Show()
|
||||||
fi, _ := os.Open("PortableApps/" + v)
|
update(win)
|
||||||
pat := processApp(fi)
|
gtk.Main()
|
||||||
if (pat != prtap{}) {
|
|
||||||
if _, ok := appMaster[pat.cat]; !ok {
|
|
||||||
cats = append(cats, pat.cat)
|
|
||||||
}
|
|
||||||
appMaster[pat.cat] = append(appMaster[pat.cat], pat)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
gl.StartDriver(uiMain)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func processApp(fi *os.File) (out prtap) {
|
func contains(arr []string, str string) bool {
|
||||||
fis, _ := fi.Readdir(-1)
|
for _, v := range arr {
|
||||||
if fil, err := os.Open(fi.Name() + "/App/AppInfo/appinfo.ini"); err == nil {
|
if v == str {
|
||||||
out.name = getName(fil)
|
return true
|
||||||
fil, _ = os.Open(fi.Name() + "/App/AppInfo/appinfo.ini")
|
|
||||||
out.cat = getCat(fil)
|
|
||||||
} else if fil, err := os.Open(fi.Name() + "/appinfo.ini"); err == nil {
|
|
||||||
out.name = getName(fil)
|
|
||||||
fil, _ = os.Open(fi.Name() + "/appinfo.ini")
|
|
||||||
out.cat = getCat(fil)
|
|
||||||
} else {
|
|
||||||
out.cat = "other"
|
|
||||||
}
|
|
||||||
if out.name == "" {
|
|
||||||
out.name = path.Base(fi.Name())
|
|
||||||
}
|
|
||||||
if out.cat == "" {
|
|
||||||
out.cat = "other"
|
|
||||||
}
|
|
||||||
for _, v := range fis {
|
|
||||||
if !v.IsDir() && strings.HasSuffix(v.Name(), ".sh") {
|
|
||||||
//do os check here for possible cross platform support
|
|
||||||
out.ex = fi.Name() + "/" + v.Name()
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return prtap{}
|
return false
|
||||||
}
|
|
||||||
|
|
||||||
func getCat(fi *os.File) (out string) {
|
|
||||||
rdr := bufio.NewReader(fi)
|
|
||||||
var err error
|
|
||||||
var ln []byte
|
|
||||||
for err == nil {
|
|
||||||
ln, _, err = rdr.ReadLine()
|
|
||||||
str := string(ln)
|
|
||||||
if strings.HasPrefix(str, "Category=") {
|
|
||||||
out = strings.TrimPrefix(str, "Category=")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func getName(fi *os.File) (out string) {
|
|
||||||
rdr := bufio.NewReader(fi)
|
|
||||||
var err error
|
|
||||||
var ln []byte
|
|
||||||
for err == nil {
|
|
||||||
ln, _, err = rdr.ReadLine()
|
|
||||||
str := string(ln)
|
|
||||||
if strings.HasPrefix(str, "Name=") {
|
|
||||||
out = strings.TrimPrefix(str, "Name=")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|||||||
-103
@@ -1,103 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"image"
|
|
||||||
"image/draw"
|
|
||||||
_ "image/png"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/nelsam/gxui"
|
|
||||||
"github.com/nelsam/gxui/math"
|
|
||||||
)
|
|
||||||
|
|
||||||
type prtapAdap struct {
|
|
||||||
gxui.AdapterBase
|
|
||||||
apps []prtap
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *prtapAdap) SetApps(apps []prtap) {
|
|
||||||
p.apps = apps
|
|
||||||
p.DataChanged(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *prtapAdap) Count() int {
|
|
||||||
return len(p.apps)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *prtapAdap) Create(th gxui.Theme, index int) gxui.Control {
|
|
||||||
box := th.CreateLinearLayout()
|
|
||||||
box.SetPadding(math.CreateSpacing(2))
|
|
||||||
box.SetDirection(gxui.LeftToRight)
|
|
||||||
box.SetVerticalAlignment(gxui.AlignMiddle)
|
|
||||||
dir := path.Dir(p.apps[index].ex)
|
|
||||||
if fold, err := os.Open(dir + "/App/AppInfo"); err == nil {
|
|
||||||
var pics []string
|
|
||||||
fi, _ := fold.Readdirnames(-1)
|
|
||||||
for _, v := range fi {
|
|
||||||
if strings.HasPrefix(v, "appicon_") && strings.HasSuffix(v, ".png") {
|
|
||||||
pics = append(pics, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(pics) > 0 {
|
|
||||||
ind := sort.SearchStrings(pics, "appicon_128.png")
|
|
||||||
if ind == len(pics) {
|
|
||||||
ind = len(pics) - 1
|
|
||||||
}
|
|
||||||
imgfi, _ := os.Open(dir + "/App/AppInfo/" + pics[ind])
|
|
||||||
img, _, err := image.Decode(imgfi)
|
|
||||||
if err == nil {
|
|
||||||
rgba := image.NewRGBA(img.Bounds())
|
|
||||||
draw.Draw(rgba, img.Bounds(), img, image.ZP, draw.Src)
|
|
||||||
tex := dr.CreateTexture(rgba, 1)
|
|
||||||
icon := th.CreateImage()
|
|
||||||
icon.SetExplicitSize(math.Size{H: 32, W: 32})
|
|
||||||
icon.SetTexture(tex)
|
|
||||||
box.AddChild(icon)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if fi, err := os.Open(dir + "/appicon.png"); err == nil {
|
|
||||||
img, _, err := image.Decode(fi)
|
|
||||||
if err == nil {
|
|
||||||
rgba := image.NewRGBA(img.Bounds())
|
|
||||||
draw.Draw(rgba, img.Bounds(), img, image.ZP, draw.Src)
|
|
||||||
tex := dr.CreateTexture(rgba, 1)
|
|
||||||
icon := th.CreateImage()
|
|
||||||
icon.SetExplicitSize(math.Size{H: 32, W: 32})
|
|
||||||
icon.SetTexture(tex)
|
|
||||||
box.AddChild(icon)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//Creating empty Image so that names line up
|
|
||||||
icon := th.CreateImage()
|
|
||||||
icon.SetExplicitSize(math.Size{H: 32, W: 32})
|
|
||||||
box.AddChild(icon)
|
|
||||||
}
|
|
||||||
lbl := th.CreateLabel()
|
|
||||||
lbl.SetText(p.apps[index].name)
|
|
||||||
box.AddChild(lbl)
|
|
||||||
return box
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *prtapAdap) ItemAt(index int) gxui.AdapterItem {
|
|
||||||
return p.apps[index]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *prtapAdap) ItemIndex(item gxui.AdapterItem) int {
|
|
||||||
it, ok := item.(prtap)
|
|
||||||
if !ok {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
for i, v := range p.apps {
|
|
||||||
if v == it {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *prtapAdap) Size(gxui.Theme) math.Size {
|
|
||||||
return math.Size{W: math.MaxSize.W, H: 36}
|
|
||||||
}
|
|
||||||
+108
@@ -0,0 +1,108 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/gotk3/gotk3/gtk"
|
||||||
|
)
|
||||||
|
|
||||||
|
func settingsUI() {
|
||||||
|
win, _ := gtk.WindowNew(gtk.WINDOW_TOPLEVEL)
|
||||||
|
win.SetDefaultSize(600, 300)
|
||||||
|
win.SetPosition(gtk.WIN_POS_CENTER)
|
||||||
|
comTagTbl, _ := gtk.TextTagTableNew()
|
||||||
|
comBuf, _ := gtk.TextBufferNew(comTagTbl)
|
||||||
|
ntbk, _ := gtk.NotebookNew()
|
||||||
|
gnrl, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 5)
|
||||||
|
gnrl.SetMarginStart(10)
|
||||||
|
gnrl.SetMarginEnd(10)
|
||||||
|
gnrl.SetMarginTop(10)
|
||||||
|
gnrl.SetMarginBottom(10)
|
||||||
|
dlWine, _ := gtk.ButtonNewWithLabel("Download Wine")
|
||||||
|
wineLbl, _ := gtk.LabelNew("PortableApps/LinuxPACom/Wine present")
|
||||||
|
gnrl.Add(wineLbl)
|
||||||
|
dlWine.Connect("clicked", func() {
|
||||||
|
cb := make(chan bool)
|
||||||
|
downloadWine(win, cb)
|
||||||
|
go func() {
|
||||||
|
v := <-cb
|
||||||
|
if v {
|
||||||
|
setupTxt(comBuf)
|
||||||
|
wineLbl.Show()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
})
|
||||||
|
if !comEnbld {
|
||||||
|
dlWine.SetSensitive(false)
|
||||||
|
dlWine.SetTooltipText("common.sh needed")
|
||||||
|
}
|
||||||
|
gnrl.Add(dlWine)
|
||||||
|
ntbk.AppendPage(gnrl, getLabel("General"))
|
||||||
|
com, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 5)
|
||||||
|
com.SetMarginStart(10)
|
||||||
|
com.SetMarginEnd(10)
|
||||||
|
com.SetMarginTop(10)
|
||||||
|
com.SetMarginBottom(10)
|
||||||
|
comEdit, _ := gtk.TextViewNewWithBuffer(comBuf)
|
||||||
|
comEdit.SetVExpand(true)
|
||||||
|
comEdit.SetHExpand(true)
|
||||||
|
vScroll, _ := gtk.AdjustmentNew(0, 0, 0, 0, 0, 0)
|
||||||
|
hScroll, _ := gtk.AdjustmentNew(0, 0, 0, 0, 0, 0)
|
||||||
|
comScrl, _ := gtk.ScrolledWindowNew(hScroll, vScroll)
|
||||||
|
comScrl.Add(comEdit)
|
||||||
|
com.Add(comScrl)
|
||||||
|
svBox, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 5)
|
||||||
|
sv, _ := gtk.ButtonNewWithLabel("Save")
|
||||||
|
sv.Connect("clicked", func() {
|
||||||
|
beg, end := comBuf.GetBounds()
|
||||||
|
txt, _ := comBuf.GetText(beg, end, true)
|
||||||
|
ioutil.WriteFile("PortableApps/LinuxPACom/common.sh", []byte(txt), 0777)
|
||||||
|
})
|
||||||
|
cnl, _ := gtk.ButtonNewWithLabel("Cancel")
|
||||||
|
cnl.Connect("clicked", func() {
|
||||||
|
setupTxt(comBuf)
|
||||||
|
})
|
||||||
|
svBox.Add(sv)
|
||||||
|
svBox.Add(cnl)
|
||||||
|
com.Add(svBox)
|
||||||
|
ntbk.AppendPage(com, getLabel("common.sh"))
|
||||||
|
win.Add(ntbk)
|
||||||
|
win.ShowAll()
|
||||||
|
if !comEnbld {
|
||||||
|
comScrl.Hide()
|
||||||
|
svBox.Hide()
|
||||||
|
mkCom, _ := gtk.ButtonNewWithLabel("Create common.sh")
|
||||||
|
mkCom.Connect("clicked", func() {
|
||||||
|
err := ioutil.WriteFile("PortableApps/LinuxPACom/common.sh", []byte("export HOME=$PWD/PortableApps/LinuxPACom/Home"), 0777)
|
||||||
|
if err == nil {
|
||||||
|
mkCom.Hide()
|
||||||
|
comScrl.Show()
|
||||||
|
svBox.Show()
|
||||||
|
setupTxt(comBuf)
|
||||||
|
comEnbld = true
|
||||||
|
dlWine.SetSensitive(true)
|
||||||
|
dlWine.SetTooltipText("")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
mkCom.Show()
|
||||||
|
com.Add(mkCom)
|
||||||
|
} else {
|
||||||
|
setupTxt(comBuf)
|
||||||
|
}
|
||||||
|
if _, err := os.Open("PortableApps/LinuxPACom/Wine"); err != nil && os.IsNotExist(err) {
|
||||||
|
wineLbl.Hide()
|
||||||
|
}
|
||||||
|
win.Show()
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupTxt(buf *gtk.TextBuffer) {
|
||||||
|
fil, _ := os.Open("PortableApps/LinuxPACom/common.sh")
|
||||||
|
btys, _ := ioutil.ReadAll(fil)
|
||||||
|
buf.SetText(string(btys))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLabel(name string) *gtk.Label {
|
||||||
|
lbl, _ := gtk.LabelNew(name)
|
||||||
|
return lbl
|
||||||
|
}
|
||||||
@@ -0,0 +1,193 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
_ "image/png"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gotk3/gotk3/gdk"
|
||||||
|
"github.com/gotk3/gotk3/gtk"
|
||||||
|
)
|
||||||
|
|
||||||
|
func setup() {
|
||||||
|
if _, err := os.Open("PortableApps/LinuxPACom/Wine"); os.IsNotExist(err) {
|
||||||
|
if _, errd := exec.LookPath("wine"); errd == nil {
|
||||||
|
wineAvail = true
|
||||||
|
}
|
||||||
|
} else if err == nil {
|
||||||
|
wineAvail = true
|
||||||
|
}
|
||||||
|
PortableAppsFold, err := os.Open("PortableApps")
|
||||||
|
if PAStat, _ := PortableAppsFold.Stat(); err != nil || !PAStat.IsDir() {
|
||||||
|
os.Mkdir("PortableApps", 0777)
|
||||||
|
PortableAppsFold, err = os.Open("PortableApps")
|
||||||
|
if err != nil {
|
||||||
|
panic("Can't find PortableApps folder and can't create one!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, err = os.Open("PortableApps/LinuxPACom"); err != nil {
|
||||||
|
os.Mkdir("PortableApps/LinuxPACom", 0777)
|
||||||
|
}
|
||||||
|
fmt.Println(err)
|
||||||
|
_, err = os.Open("PortableApps/LinuxPACom/common.sh")
|
||||||
|
if err == nil {
|
||||||
|
comEnbld = true
|
||||||
|
}
|
||||||
|
_, err = os.Open("PortableApps/LinuxPACom/Info.ini")
|
||||||
|
if err != nil {
|
||||||
|
os.Create("PortableApps/LinuxPACom/Info.ini")
|
||||||
|
}
|
||||||
|
PAFolds, _ := PortableAppsFold.Readdirnames(-1)
|
||||||
|
sort.Strings(PAFolds)
|
||||||
|
for _, v := range PAFolds {
|
||||||
|
fold, _ := os.Open("PortableApps/" + v)
|
||||||
|
if stat, _ := fold.Stat(); stat.IsDir() && stat.Name() != "PortableApps.com" && stat.Name() != "LinuxPACom" {
|
||||||
|
ap := processApp("PortableApps/" + v)
|
||||||
|
if !reflect.DeepEqual(ap, app{}) {
|
||||||
|
if _, ok := master[ap.cat]; !ok {
|
||||||
|
cats = append(cats, ap.cat)
|
||||||
|
sort.Strings(cats)
|
||||||
|
}
|
||||||
|
if len(ap.lin) != 0 {
|
||||||
|
if _, ok := linmaster[ap.cat]; !ok {
|
||||||
|
lin = append(lin, ap.cat)
|
||||||
|
sort.Strings(lin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
master[ap.cat] = append(master[ap.cat], ap)
|
||||||
|
if len(ap.lin) != 0 {
|
||||||
|
linmaster[ap.cat] = append(linmaster[ap.cat], ap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func processApp(fold string) (out app) {
|
||||||
|
wd, _ := os.Getwd()
|
||||||
|
out.dir = wd + "/" + fold
|
||||||
|
out.ini = findInfo(fold)
|
||||||
|
if out.ini != nil {
|
||||||
|
out.name = getName(out.ini)
|
||||||
|
out.ini = findInfo(fold)
|
||||||
|
out.cat = getCat(out.ini)
|
||||||
|
out.ini = findInfo(fold)
|
||||||
|
}
|
||||||
|
if out.name == "" {
|
||||||
|
out.name = strings.TrimPrefix(fold, "PortableApps/")
|
||||||
|
}
|
||||||
|
if out.cat == "" {
|
||||||
|
out.cat = "Other"
|
||||||
|
}
|
||||||
|
out.icon = getIcon(fold)
|
||||||
|
folder, _ := os.Open(fold)
|
||||||
|
fis, _ := folder.Readdirnames(-1)
|
||||||
|
for _, v := range fis {
|
||||||
|
tmp, _ := os.Open(fold + "/" + v)
|
||||||
|
if stat, _ := tmp.Stat(); stat.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(strings.ToLower(v), ".appimage") {
|
||||||
|
out.appimg = append(out.appimg, v)
|
||||||
|
out.ex = append(out.ex, v)
|
||||||
|
out.lin = append(out.lin, v)
|
||||||
|
} else if strings.HasSuffix(strings.ToLower(v), ".exe") {
|
||||||
|
out.ex = append(out.ex, v)
|
||||||
|
} else {
|
||||||
|
btys := make([]byte, 4)
|
||||||
|
rdr := bufio.NewReader(tmp)
|
||||||
|
rdr.Read(btys)
|
||||||
|
if (strings.Contains(strings.ToLower(string(btys)), "elf") && !strings.HasSuffix(strings.ToLower(v), ".so") && !strings.Contains(v, ".so.")) || strings.HasPrefix(strings.ToLower(string(btys)), "#!") {
|
||||||
|
out.ex = append(out.ex, v)
|
||||||
|
out.lin = append(out.lin, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(out.ex) == 0 {
|
||||||
|
return app{}
|
||||||
|
}
|
||||||
|
if len(out.lin) == 0 {
|
||||||
|
out.name += " (Wine)"
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCat(ini *os.File) string {
|
||||||
|
rdr := bufio.NewReader(ini)
|
||||||
|
var ret string
|
||||||
|
for line, _, err := rdr.ReadLine(); err == nil; line, _, err = rdr.ReadLine() {
|
||||||
|
if strings.HasPrefix(string(line), "Category=") {
|
||||||
|
ret = strings.TrimPrefix(string(line), "Category=")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rdr.Reset(ini)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func getName(ini *os.File) string {
|
||||||
|
rdr := bufio.NewReader(ini)
|
||||||
|
var ret string
|
||||||
|
for line, _, err := rdr.ReadLine(); err == nil; line, _, err = rdr.ReadLine() {
|
||||||
|
if strings.HasPrefix(string(line), "Name=") {
|
||||||
|
ret = strings.TrimPrefix(string(line), "Name=")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rdr.Reset(ini)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIcon(fold string) *gdk.Pixbuf {
|
||||||
|
var pic string
|
||||||
|
if folder, err := os.Open(fold + "/App/AppInfo"); err == nil {
|
||||||
|
fis, _ := folder.Readdir(-1)
|
||||||
|
var pics []string
|
||||||
|
for _, v := range fis {
|
||||||
|
if !v.IsDir() && strings.HasSuffix(strings.ToLower(v.Name()), ".png") && strings.HasPrefix(strings.ToLower(v.Name()), "appicon_") {
|
||||||
|
pics = append(pics, v.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Strings(pics)
|
||||||
|
if len(pics) > 1 {
|
||||||
|
var ind int
|
||||||
|
if !contains(pics, "appicon_32.png") {
|
||||||
|
ind = len(pics) - 1
|
||||||
|
} else {
|
||||||
|
ind = sort.SearchStrings(pics, "appicon_32.png")
|
||||||
|
}
|
||||||
|
pic = fold + "/App/AppInfo/" + pics[ind]
|
||||||
|
}
|
||||||
|
} else if _, err := os.Open(fold + "/appicon.png"); err == nil {
|
||||||
|
pic = fold + "/appicon.png"
|
||||||
|
} else {
|
||||||
|
img, _ := gtk.ImageNewFromIconName("application-x-executable", gtk.ICON_SIZE_BUTTON)
|
||||||
|
buf, _ := img.GetPixbuf().ScaleSimple(32, 32, gdk.INTERP_BILINEAR)
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
img, _ := gtk.ImageNewFromFile(pic)
|
||||||
|
buf, _ := img.GetPixbuf().ScaleSimple(32, 32, gdk.INTERP_BILINEAR)
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
func findInfo(fold string) *os.File {
|
||||||
|
tmp, err := os.Open(fold + "/App/AppInfo")
|
||||||
|
if err == nil {
|
||||||
|
fis, _ := tmp.Readdirnames(-1)
|
||||||
|
for _, v := range fis {
|
||||||
|
if strings.ToLower(v) == "appinfo.ini" {
|
||||||
|
tmp, _ := os.Open(fold + "/App/AppInfo/" + v)
|
||||||
|
return tmp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if fi, err := os.Open(fold + "/appinfo.ini"); err == nil {
|
||||||
|
return fi
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,57 +1,136 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"fmt"
|
||||||
"os/exec"
|
|
||||||
"path"
|
|
||||||
|
|
||||||
"github.com/nelsam/gxui"
|
"github.com/gotk3/gotk3/glib"
|
||||||
"github.com/nelsam/gxui/themes/dark"
|
"github.com/gotk3/gotk3/gtk"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
func ui(win *gtk.Window) {
|
||||||
dr gxui.Driver
|
ls := getCatRows()
|
||||||
)
|
var treeApps []*gtk.TreeIter
|
||||||
|
header, _ := gtk.HeaderBarNew()
|
||||||
func uiMain(dri gxui.Driver) {
|
header.SetShowCloseButton(true)
|
||||||
dr = dri
|
header.SetTitle("LinuxPA")
|
||||||
catAdap := &StrList{}
|
header.SetSubtitle("PortableApps.com type launcher")
|
||||||
catAdap.SetStrings(cats)
|
settings, _ := gtk.ButtonNewFromIconName("applications-system", gtk.ICON_SIZE_SMALL_TOOLBAR)
|
||||||
appAdap := &prtapAdap{}
|
settings.Connect("clicked", func() {
|
||||||
th := dark.CreateTheme(dr)
|
settingsUI()
|
||||||
win := th.CreateWindow(500, 500, "LinuxPA")
|
|
||||||
top := th.CreateLinearLayout()
|
|
||||||
top.SetDirection(gxui.BottomToTop)
|
|
||||||
top.SetHorizontalAlignment(gxui.AlignRight)
|
|
||||||
spl := th.CreateSplitterLayout()
|
|
||||||
spl.SetOrientation(gxui.Horizontal)
|
|
||||||
catlist := th.CreateList()
|
|
||||||
catlist.SetAdapter(catAdap)
|
|
||||||
catlist.OnItemClicked(func(_ gxui.MouseEvent, it gxui.AdapterItem) {
|
|
||||||
str := it.(string)
|
|
||||||
appAdap.SetApps(appMaster[str])
|
|
||||||
})
|
})
|
||||||
applist := th.CreateList()
|
settings.SetTooltipText("Settings (Coming Soon!)")
|
||||||
applist.SetAdapter(appAdap)
|
header.PackStart(settings)
|
||||||
spl.AddChild(catlist)
|
win.SetTitlebar(header)
|
||||||
spl.AddChild(applist)
|
topLvl, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
|
||||||
but := th.CreateLinearLayout()
|
lrBox, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 5)
|
||||||
but.SetDirection(gxui.RightToLeft)
|
catList, _ := gtk.ListBoxNew()
|
||||||
launch := th.CreateButton()
|
catList.SetActivateOnSingleClick(true)
|
||||||
launch.SetText("Launch!")
|
store, _ := gtk.TreeStoreNew(glib.TYPE_OBJECT, glib.TYPE_STRING)
|
||||||
launch.OnClick(func(gxui.MouseEvent) {
|
appsList, _ := gtk.TreeViewNewWithModel(store)
|
||||||
if appAdap.ItemIndex(applist.Selected()) != -1 {
|
render, _ := gtk.CellRendererPixbufNew()
|
||||||
app := applist.Selected().(prtap)
|
pixColumn, _ := gtk.TreeViewColumnNewWithAttribute("", render, "pixbuf", 0)
|
||||||
dir, fi := path.Split(app.ex)
|
txtRender, _ := gtk.CellRendererTextNew()
|
||||||
cmd := exec.Command("/bin/sh", "-c", "cd \""+dir+"\"; \"./"+fi+"\"")
|
txtColumn, _ := gtk.TreeViewColumnNewWithAttribute("", txtRender, "text", 1)
|
||||||
cmd.Stdin = os.Stdin
|
appsList.AppendColumn(pixColumn)
|
||||||
cmd.Stdout = os.Stdout
|
appsList.AppendColumn(txtColumn)
|
||||||
cmd.Start()
|
appsList.SetHeadersVisible(false)
|
||||||
|
catList.SetHExpand(true)
|
||||||
|
catList.SetVExpand(true)
|
||||||
|
appsList.SetHExpand(true)
|
||||||
|
appsList.SetVExpand(true)
|
||||||
|
vScrollCat, _ := gtk.AdjustmentNew(0, 0, 0, 0, 0, 0)
|
||||||
|
hScrollCat, _ := gtk.AdjustmentNew(0, 0, 0, 0, 0, 0)
|
||||||
|
vScrollApp, _ := gtk.AdjustmentNew(0, 0, 0, 0, 0, 0)
|
||||||
|
hScrollApp, _ := gtk.AdjustmentNew(0, 0, 0, 0, 0, 0)
|
||||||
|
catScrl, _ := gtk.ScrolledWindowNew(hScrollCat, vScrollCat)
|
||||||
|
catScrl.Add(catList)
|
||||||
|
catScrl.SetSizeRequest(170, 500)
|
||||||
|
appScrl, _ := gtk.ScrolledWindowNew(hScrollApp, vScrollApp)
|
||||||
|
appScrl.Add(appsList)
|
||||||
|
appScrl.SetSizeRequest(300, 500)
|
||||||
|
lrBox.Add(catScrl)
|
||||||
|
lrBox.Add(appScrl)
|
||||||
|
botBox, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 2)
|
||||||
|
wineCheck, _ := gtk.CheckButtonNewWithLabel("Show Windows apps (Wine)")
|
||||||
|
if !wineAvail {
|
||||||
|
wineCheck.SetSensitive(false)
|
||||||
|
wineCheck.SetTooltipText("Download wine to run windows apps")
|
||||||
|
}
|
||||||
|
wineCheck.Connect("toggled", func() {
|
||||||
|
wine = wineCheck.GetActive()
|
||||||
|
for i := range ls {
|
||||||
|
catList.Remove(catList.GetRowAtIndex(i))
|
||||||
|
}
|
||||||
|
ls = getCatRows()
|
||||||
|
for _, v := range ls {
|
||||||
|
catList.Add(v)
|
||||||
|
}
|
||||||
|
catList.ShowAll()
|
||||||
|
})
|
||||||
|
botBox.Add(wineCheck)
|
||||||
|
topLvl.Add(lrBox)
|
||||||
|
topLvl.PackEnd(botBox, false, true, 0)
|
||||||
|
win.Add(topLvl)
|
||||||
|
for _, v := range ls {
|
||||||
|
catList.Prepend(v)
|
||||||
|
}
|
||||||
|
catList.Connect("row-selected", func() {
|
||||||
|
store.Clear()
|
||||||
|
if catList.GetSelectedRow().GetIndex() >= 0 {
|
||||||
|
treeApps = make([]*gtk.TreeIter, 0)
|
||||||
|
if wine {
|
||||||
|
apps := master[cats[catList.GetSelectedRow().GetIndex()]]
|
||||||
|
for _, v := range apps {
|
||||||
|
treeApps = append(treeApps, v.getTreeIter(store))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
apps := linmaster[lin[catList.GetSelectedRow().GetIndex()]]
|
||||||
|
for _, v := range apps {
|
||||||
|
treeApps = append(treeApps, v.getTreeIter(store))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
appsList.Connect("row-activated", func() {
|
||||||
|
selec, _ := appsList.GetSelection()
|
||||||
|
_, it, ok := selec.GetSelected()
|
||||||
|
if ok {
|
||||||
|
pth, _ := store.GetPath(it)
|
||||||
|
ind := pth.GetIndices()
|
||||||
|
if len(ind) == 1 {
|
||||||
|
if wine {
|
||||||
|
app := master[cats[catList.GetSelectedRow().GetIndex()]][ind[0]]
|
||||||
|
app.launch()
|
||||||
|
} else {
|
||||||
|
app := linmaster[lin[catList.GetSelectedRow().GetIndex()]][ind[0]]
|
||||||
|
app.launch()
|
||||||
|
}
|
||||||
|
} else if len(ind) == 2 {
|
||||||
|
if wine {
|
||||||
|
app := master[cats[catList.GetSelectedRow().GetIndex()]][ind[0]]
|
||||||
|
app.launchSub(ind[1])
|
||||||
|
} else {
|
||||||
|
app := linmaster[lin[catList.GetSelectedRow().GetIndex()]][ind[0]]
|
||||||
|
app.launchSub(ind[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
but.AddChild(launch)
|
}
|
||||||
top.AddChild(but)
|
|
||||||
top.AddChild(spl)
|
func getCatRows() (out []*gtk.Label) {
|
||||||
win.AddChild(top)
|
if wine {
|
||||||
win.OnClose(dr.Terminate)
|
for _, v := range cats {
|
||||||
|
txt, _ := gtk.LabelNew(v)
|
||||||
|
out = append(out, txt)
|
||||||
|
fmt.Println(v)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for _, v := range lin {
|
||||||
|
txt, _ := gtk.LabelNew(v)
|
||||||
|
out = append(out, txt)
|
||||||
|
fmt.Println(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,156 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gotk3/gotk3/gtk"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
versionURL = "https://www.dropbox.com/s/a0xizzo0a4vsfqt/Version?dl=1"
|
||||||
|
downloadURL = "https://github.com/CalebQ42/LinuxPA/releases/download/vXXX/LinuxPA"
|
||||||
|
)
|
||||||
|
|
||||||
|
//Thanks to https://www.socketloop.com/tutorials/golang-download-file-example
|
||||||
|
//For some of the code
|
||||||
|
|
||||||
|
//Returns if success
|
||||||
|
func versionDL() (bool, error) {
|
||||||
|
versionFile, err := os.Create("PortableApps/LinuxPACom/Version")
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
versionFile.Chmod(0777)
|
||||||
|
check := http.Client{
|
||||||
|
CheckRedirect: func(r *http.Request, via []*http.Request) error {
|
||||||
|
r.URL.Opaque = r.URL.Path
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
response, err := check.Get(versionURL)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
_, err = io.Copy(versionFile, response.Body)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getVersionFileInfo() string {
|
||||||
|
fil, err := os.Open("PortableApps/LinuxPACom/Version")
|
||||||
|
if err != nil {
|
||||||
|
return "Error!"
|
||||||
|
}
|
||||||
|
rdr := bufio.NewReader(fil)
|
||||||
|
out, _, _ := rdr.ReadLine()
|
||||||
|
return string(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkForUpdate(new string) (bool, error) {
|
||||||
|
curSlice := strings.Split(version, ".")
|
||||||
|
newSlice := strings.Split(new, ".")
|
||||||
|
curNums := make([]int, 4)
|
||||||
|
newNums := make([]int, 4)
|
||||||
|
for i, v := range curSlice {
|
||||||
|
num, err := strconv.Atoi(v)
|
||||||
|
if err == nil {
|
||||||
|
curNums[i] = num
|
||||||
|
}
|
||||||
|
num, err = strconv.Atoi(newSlice[i])
|
||||||
|
if err == nil {
|
||||||
|
newNums[i] = num
|
||||||
|
} else {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if newNums[i] > curNums[i] {
|
||||||
|
return true, nil
|
||||||
|
} else if curNums[i] > newNums[i] {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func downloadUpdate(newVersion string) (bool, error) {
|
||||||
|
url := strings.Replace(downloadURL, "XXX", newVersion, -1)
|
||||||
|
err := os.Rename("LinuxPA", ".LinuxPA.old")
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
fil, err := os.Create("LinuxPA")
|
||||||
|
fil.Chmod(0777)
|
||||||
|
defer fil.Close()
|
||||||
|
if err != nil {
|
||||||
|
os.Rename(".LinuxPA.old", "LinuxPA")
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
check := http.Client{
|
||||||
|
CheckRedirect: func(r *http.Request, via []*http.Request) error {
|
||||||
|
r.URL.Opaque = r.URL.Path
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
re, err := check.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
defer re.Body.Close()
|
||||||
|
_, err = io.Copy(fil, re.Body)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(win *gtk.Window) {
|
||||||
|
updateWin, _ := gtk.WindowNew(gtk.WINDOW_POPUP)
|
||||||
|
updateWin.SetTransientFor(win)
|
||||||
|
topLvl, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 5)
|
||||||
|
spin, _ := gtk.SpinnerNew()
|
||||||
|
spin.Start()
|
||||||
|
lbl, _ := gtk.LabelNew("Checking for updates")
|
||||||
|
topLvl.Add(spin)
|
||||||
|
topLvl.Add(lbl)
|
||||||
|
topLvl.SetMarginBottom(10)
|
||||||
|
topLvl.SetMarginEnd(10)
|
||||||
|
topLvl.SetMarginStart(10)
|
||||||
|
topLvl.SetMarginTop(10)
|
||||||
|
updateWin.SetPosition(gtk.WIN_POS_CENTER_ON_PARENT)
|
||||||
|
updateWin.Add(topLvl)
|
||||||
|
updateWin.ShowAll()
|
||||||
|
updateWin.Show()
|
||||||
|
go func(win, updateWin *gtk.Window) {
|
||||||
|
defer updateWin.Close()
|
||||||
|
stat, err := versionDL()
|
||||||
|
if stat {
|
||||||
|
res := getVersionFileInfo()
|
||||||
|
if res != "Error!" {
|
||||||
|
stat, err = checkForUpdate(res)
|
||||||
|
if stat {
|
||||||
|
lbl.SetText("Updating!")
|
||||||
|
downloadUpdate(res)
|
||||||
|
win.Close()
|
||||||
|
cmd := exec.Command("./LinuxPA")
|
||||||
|
cmd.Stdin = os.Stdin
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Start()
|
||||||
|
} else {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Println("Failed Version File Info")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
}(win, updateWin)
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gotk3/gotk3/gtk"
|
||||||
|
"github.com/mholt/archiver"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
wineURL = "https://www.playonlinux.com/wine/binaries/linux-amd64/PlayOnLinux-wine-2.5-linux-amd64.pol"
|
||||||
|
)
|
||||||
|
|
||||||
|
func downloadWine(parent *gtk.Window, cb chan bool) {
|
||||||
|
win, _ := gtk.WindowNew(gtk.WINDOW_TOPLEVEL)
|
||||||
|
win.SetTransientFor(parent)
|
||||||
|
spin, _ := gtk.SpinnerNew()
|
||||||
|
spin.Start()
|
||||||
|
txt, _ := gtk.LabelNew("Downloading Wine")
|
||||||
|
box, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 5)
|
||||||
|
box.SetMarginBottom(10)
|
||||||
|
box.SetMarginEnd(10)
|
||||||
|
box.SetMarginStart(10)
|
||||||
|
box.SetMarginTop(10)
|
||||||
|
box.Add(spin)
|
||||||
|
box.Add(txt)
|
||||||
|
win.Add(box)
|
||||||
|
win.SetPosition(gtk.WIN_POS_CENTER_ON_PARENT)
|
||||||
|
win.ShowAll()
|
||||||
|
win.Show()
|
||||||
|
go func(win *gtk.Window) {
|
||||||
|
defer win.Close()
|
||||||
|
wineTar, err := os.Create("PortableApps/LinuxPACom/wine2.5.tar.bz2")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
cb <- false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
wineTar.Chmod(0777)
|
||||||
|
defer wineTar.Close()
|
||||||
|
check := http.Client{
|
||||||
|
CheckRedirect: func(r *http.Request, via []*http.Request) error {
|
||||||
|
r.URL.Opaque = r.URL.Path
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
resp, err := check.Get(wineURL)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
cb <- false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
_, err = io.Copy(wineTar, resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
cb <- false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
txt.SetText("Extracting Wine")
|
||||||
|
os.RemoveAll("PortableApps/LinuxPACom/Wine")
|
||||||
|
err = archiver.TarBz2.Open("PortableApps/LinuxPACom/wine2.5.tar.bz2", "PortableApps/LinuxPACom/Wine")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
cb <- false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
os.Remove("PortableApps/LinuxPACom/wine2.5.tar.bz2")
|
||||||
|
fil, err := os.Open("PortableApps/LinuxPACom/common.sh")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
cb <- false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tmp, err := ioutil.ReadAll(fil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
cb <- false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println("Hello")
|
||||||
|
if !strings.Contains(string(tmp), "export PATH=$PWD/PortableApps/LinuxPACom/Wine/wineversion/2.5/bin:$PATH") {
|
||||||
|
tmp = append(tmp, []byte("\nexport PATH=$PWD/PortableApps/LinuxPACom/Wine/wineversion/2.5/bin:$PATH")...)
|
||||||
|
ioutil.WriteFile("PortableApps/LinuxPACom/common.sh", tmp, 0777)
|
||||||
|
fmt.Println("Hello2")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
cb <- false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Println("HelloT")
|
||||||
|
cb <- true
|
||||||
|
return
|
||||||
|
}(win)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user