10 Commits

Author SHA1 Message Date
Belac Darkstorm 3b639f53fd Merge pull request #6 from CalebQ42/gtkUI
Gtk ui
2017-04-04 18:36:18 -05:00
Belac Darkstorm 3199ad88fd Finishing up gui 2017-04-04 18:35:28 -05:00
Belac Darkstorm a221d18d33 Starting UI remake work 2017-04-04 07:54:04 -05:00
Belac Darkstorm a9eeb3bb1c Last push before GUI redo 2017-04-04 01:32:13 -05:00
Belac Darkstorm 844a282fd7 Wine download finished for later on.
Going to start switching to another GUI library
2017-04-04 01:23:17 -05:00
Belac Darkstorm 9da352a315 Updating is working!! 2017-04-03 10:43:22 -05:00
Belac Darkstorm 34b8f7c926 Forgot to implement updating 2017-04-03 09:23:41 -05:00
Belac Darkstorm 5d5cedec58 Ready to test then bug fix :)
Also, I'm ALIVE!!!!
2017-04-03 09:17:49 -05:00
Belac Darkstorm 8441a8b752 Working on an auto-update mechanism 2017-04-03 08:23:25 -05:00
Belac Darkstorm d64d88e0a5 Tries to make PortableApps folder before failing 2016-09-23 14:32:30 -05:00
11 changed files with 976 additions and 276 deletions
+2 -3
View File
@@ -7,7 +7,6 @@ Just double click on an app to launch it! If there are multiple executables, you
# Apps: # Apps:
Both of the below places provide linux executables that don't need libs installed on the host system: Both of the below places provide linux executables that don't need libs installed on the host system:
[AppImage](https://bintray.com/probono/AppImages) [AppImage](https://bintray.com/probono/AppImages)
<s>[Orbital-Apps](https://www.orbital-apps.com/blog/2016/introducing-universal-orbs)</s> (Only works if the Orb-Launcher is installed on the system, for some reason. [Issue](https://github.com/CalebQ42/LinuxPA/issues/3))
# PortableApps.com Compatibility # PortableApps.com Compatibility
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. 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.
@@ -24,7 +23,7 @@ Because apps aren't natively formated in the PortableApps.com format, if LinuxPA
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. 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.
# USB mount # USB mount
Unfortunately Linux, by default, doesn't support running executables off of flash drives, requiring you to mount your drive with special mount arguments, I personally use the arguments `exec,noauto,nodev,nosuid,umask=0000` 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 # Screenshots
Photos are found [Here](https://goo.gl/photos/VtBUL6DyZTMidj5n6) Photos are found [Here](https://goo.gl/photos/VtBUL6DyZTMidj5n6)
@@ -35,4 +34,4 @@ Photos are found [Here](https://goo.gl/photos/VtBUL6DyZTMidj5n6)
1. Add updater for .AppImage files 1. Add updater for .AppImage files
1. Download .AppImage files (maybe) 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. Check if all apps are closed when it closes and ask if you want to force stop the apps.
1. Portable wine??? 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)
+220 -184
View File
@@ -5,8 +5,8 @@ import (
"os/exec" "os/exec"
"strings" "strings"
"github.com/nelsam/gxui" "github.com/gotk3/gotk3/gdk"
"github.com/nelsam/gxui/math" "github.com/gotk3/gotk3/gtk"
) )
type app struct { type app struct {
@@ -15,94 +15,35 @@ type app struct {
appimg []string appimg []string
lin []string lin []string
ex []string ex []string
icon gxui.Texture icon *gdk.Pixbuf
dir string dir string
ini *os.File ini *os.File
} }
type appExNode struct { func (a *app) getTreeIter(store *gtk.TreeStore) *gtk.TreeIter {
ap app it := store.Append(nil)
exInd int store.SetValue(it, 0, a.icon)
} store.SetValue(it, 1, a.name)
if len(a.ex) > 1 {
func (a *appExNode) launch() { for _, v := range a.ex {
if wine { i := store.Append(it)
var cmd *exec.Cmd store.SetValue(i, 1, v)
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 return it
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 { func (a *app) launch() {
return 0 if len(a.ex) == 1 {
}
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
}
type appNode struct {
ap app
}
func (a *appNode) launch() {
if len(a.ap.ex) == 1 {
if wine { if wine {
var cmd *exec.Cmd var cmd *exec.Cmd
if !contains(a.ap.lin, a.ap.ex[0]) { if !contains(a.lin, a.ex[0]) {
cmd = exec.Command("/bin/sh", "-c", "cd \""+a.ap.dir+"\"; wine \""+a.ap.ex[0]+"\"") cmd = exec.Command("/bin/sh", "-c", "cd \""+a.dir+"\"; wine \""+a.ex[0]+"\"")
} else { } else {
if comEnbld { if comEnbld {
cmd = exec.Command("/bin/sh", "-c", ". PortableApps/LinuxPACom/common.sh || exit 1;cd \""+a.ap.dir+"\"; \"./"+a.ap.ex[0]+"\"") cmd = exec.Command("/bin/sh", "-c", ". PortableApps/LinuxPACom/common.sh || exit 1;cd \""+a.dir+"\"; \"./"+a.ex[0]+"\"")
} else { } else {
cmd = exec.Command("/bin/sh", "-c", "cd \""+a.ap.dir+"\"; \"./"+a.ap.ex[0]+"\"") cmd = exec.Command("/bin/sh", "-c", "cd \""+a.dir+"\"; \"./"+a.ex[0]+"\"")
} }
} }
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
@@ -111,9 +52,9 @@ func (a *appNode) launch() {
} else { } else {
var cmd *exec.Cmd var cmd *exec.Cmd
if comEnbld { if comEnbld {
cmd = exec.Command("/bin/sh", "-c", ". PortableApps/LinuxPACom/common.sh || exit 1;cd \""+a.ap.dir+"\"; \"./"+a.ap.ex[0]+"\"") cmd = exec.Command("/bin/sh", "-c", ". PortableApps/LinuxPACom/common.sh || exit 1;cd \""+a.dir+"\"; \"./"+a.ex[0]+"\"")
} else { } else {
cmd = exec.Command("/bin/sh", "-c", "cd \""+a.ap.dir+"\"; \"./"+a.ap.ex[0]+"\"") cmd = exec.Command("/bin/sh", "-c", "cd \""+a.dir+"\"; \"./"+a.ex[0]+"\"")
} }
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
@@ -122,29 +63,29 @@ func (a *appNode) launch() {
} else { } else {
if wine { if wine {
var cmd *exec.Cmd var cmd *exec.Cmd
if len(a.ap.lin) == 0 { if len(a.lin) == 0 {
cmd = exec.Command("/bin/sh", "-c", "cd \""+a.ap.dir+"\"; wine \""+a.ap.ex[0]+"\"") cmd = exec.Command("/bin/sh", "-c", "cd \""+a.dir+"\"; wine \""+a.ex[0]+"\"")
} else { } else {
var ind int var ind int
for i, v := range a.ap.lin { for i, v := range a.lin {
if strings.HasSuffix(v, ".sh") { if strings.HasSuffix(v, ".sh") {
ind = i ind = i
break break
} }
} }
if comEnbld { if comEnbld {
cmd = exec.Command("/bin/sh", "-c", ". PortableApps/LinuxPACom/common.sh || exit 1;cd \""+a.ap.dir+"\"; \"./"+a.ap.lin[ind]+"\"") cmd = exec.Command("/bin/sh", "-c", ". PortableApps/LinuxPACom/common.sh || exit 1;cd \""+a.dir+"\"; \"./"+a.lin[ind]+"\"")
} else { } else {
cmd = exec.Command("/bin/sh", "-c", "cd \""+a.ap.dir+"\"; \"./"+a.ap.lin[ind]+"\"") cmd = exec.Command("/bin/sh", "-c", "cd \""+a.dir+"\"; \"./"+a.lin[ind]+"\"")
} }
} }
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
cmd.Start() cmd.Start()
} else { } else {
if len(a.ap.lin) != 0 { if len(a.lin) != 0 {
var ind int var ind int
for i, v := range a.ap.lin { for i, v := range a.lin {
if strings.HasSuffix(v, ".sh") { if strings.HasSuffix(v, ".sh") {
ind = i ind = i
break break
@@ -152,9 +93,9 @@ func (a *appNode) launch() {
} }
var cmd *exec.Cmd var cmd *exec.Cmd
if comEnbld { if comEnbld {
cmd = exec.Command("/bin/sh", "-c", ". PortableApps/LinuxPACom/common.sh || exit 1;cd \""+a.ap.dir+"\"; \"./"+a.ap.lin[ind]+"\"") cmd = exec.Command("/bin/sh", "-c", ". PortableApps/LinuxPACom/common.sh || exit 1;cd \""+a.dir+"\"; \"./"+a.lin[ind]+"\"")
} else { } else {
cmd = exec.Command("/bin/sh", "-c", "cd \""+a.ap.dir+"\"; \"./"+a.ap.lin[ind]+"\"") cmd = exec.Command("/bin/sh", "-c", "cd \""+a.dir+"\"; \"./"+a.lin[ind]+"\"")
} }
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
@@ -164,109 +105,204 @@ func (a *appNode) launch() {
} }
} }
func (a *appNode) Count() int { func (a *app) launchSub(sub int) {
if wine { if wine {
if len(a.ap.ex) > 1 { var cmd *exec.Cmd
return len(a.ap.ex) if !contains(a.lin, a.ex[sub]) {
} cmd = exec.Command("/bin/sh", "-c", "cd \""+a.dir+"\"; wine \""+a.ex[sub]+"\"")
return 0 } else {
} if comEnbld {
if len(a.ap.lin) > 1 { cmd = exec.Command("/bin/sh", "-c", ". PortableApps/LinuxPACom/common.sh || exit 1;cd \""+a.dir+"\"; \"./"+a.ex[sub]+"\"")
return len(a.ap.lin) } else {
} cmd = exec.Command("/bin/sh", "-c", "cd \""+a.dir+"\"; \"./"+a.ex[sub]+"\"")
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
} }
} }
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 { } else {
for i, v := range a.ap.lin { cmd = exec.Command("/bin/sh", "-c", "cd \""+a.dir+"\"; \"./"+a.ex[sub]+"\"")
if v == item {
return i
}
}
} }
return -1 cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Start()
} }
func (a *appNode) Item() gxui.AdapterItem { // type appExNode struct {
return a.ap.name // 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 *appNode) Create(the gxui.Theme) gxui.Control { // func (a *appExNode) Count() int {
box := the.CreateLinearLayout() // return 0
box.SetDirection(gxui.LeftToRight) // }
box.SetPadding(math.CreateSpacing(2)) //
box.SetVerticalAlignment(gxui.AlignMiddle) // func (a *appExNode) NodeAt(int) gxui.TreeNode {
img := the.CreateImage() // return nil
if a.ap.icon != nil { // }
img.SetTexture(a.ap.icon) //
} // func (a *appExNode) ItemIndex(gxui.AdapterItem) int {
img.SetExplicitSize(math.Size{H: 32, W: 32}) // return -1
lbl := the.CreateLabel() // }
lbl.SetText(a.ap.name) //
box.AddChild(img) // func (a *appExNode) Item() gxui.AdapterItem {
box.AddChild(lbl) // if wine {
box.OnDoubleClick(func(gxui.MouseEvent) { // return a.ap.ex[a.exInd]
a.launch() // }
}) // return a.ap.lin[a.exInd]
return box // }
} //
// 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
// }
type catAdap struct { // func (a *appNode) Count() int {
gxui.AdapterBase // if wine {
cat string // 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
// }
func (a *catAdap) setCat(cat string) { // type catAdap struct {
a.cat = cat // gxui.AdapterBase
a.DataChanged(false) // cat string
} // }
//
func (a *catAdap) refresh() { // func (a *catAdap) setCat(cat string) {
a.DataChanged(false) // a.cat = cat
} // a.DataChanged(false)
// }
func (a *catAdap) Count() int { //
if wine { // func (a *catAdap) refresh() {
return len(master[a.cat]) // a.DataChanged(false)
} // }
return len(linmaster[a.cat]) //
} // func (a *catAdap) Count() int {
// if wine {
func (a *catAdap) NodeAt(i int) gxui.TreeNode { // return len(master[a.cat])
if wine { // }
return &appNode{ap: master[a.cat][i]} // return len(linmaster[a.cat])
} // }
return &appNode{ap: linmaster[a.cat][i]} //
} // func (a *catAdap) NodeAt(i int) gxui.TreeNode {
// if wine {
func (a *catAdap) Size(gxui.Theme) math.Size { // return &appNode{ap: master[a.cat][i]}
return math.Size{H: 34, W: math.MaxSize.W} // }
} // return &appNode{ap: linmaster[a.cat][i]}
// }
func (a *catAdap) ItemIndex(item gxui.AdapterItem) int { //
if wine { // func (a *catAdap) Size(gxui.Theme) math.Size {
for i, v := range master[a.cat] { // return math.Size{H: 34, W: math.MaxSize.W}
if v.name == item { // }
return i //
} // func (a *catAdap) ItemIndex(item gxui.AdapterItem) int {
} // if wine {
} else { // for i, v := range master[a.cat] {
for i, v := range linmaster[a.cat] { // if v.name == item {
if v.name == item { // return i
return i // }
} // }
} // } else {
} // for i, v := range linmaster[a.cat] {
return -1 // if v.name == item {
} // return i
// }
// }
// }
// return -1
// }
+82
View File
@@ -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
View File
@@ -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
}
View File
+66
View File
@@ -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)
}
+27 -14
View File
@@ -1,39 +1,52 @@
package main package main
import ( import (
"fmt"
"os" "os"
"github.com/nelsam/gxui" "github.com/gotk3/gotk3/gtk"
"github.com/nelsam/gxui/drivers/gl" )
"github.com/nelsam/gxui/themes/dark"
const (
version = "2.0.0.1"
defIni = ""
) )
var ( var (
dr gxui.Driver
th gxui.Theme
master map[string][]app master map[string][]app
linmaster map[string][]app linmaster map[string][]app
cats []string cats []string
lin []string lin []string
wine bool wine bool
comEnbld bool comEnbld bool
darkTheme = true
) )
func main() { func main() {
_, err := os.Open("PortableApps/LinuxPACom/common.sh") os.MkdirAll("PortableApps/LinuxPACom", 0777)
if err == nil {
comEnbld = true
}
master = make(map[string][]app) master = make(map[string][]app)
linmaster = make(map[string][]app) linmaster = make(map[string][]app)
gl.StartDriver(appMain) uiStart()
} }
func appMain(dri gxui.Driver) { func uiStart() {
dr = dri gtk.Init(nil)
th = dark.CreateTheme(dr)
setup() setup()
ui() win, err := gtk.WindowNew(gtk.WINDOW_TOPLEVEL)
if err != nil {
fmt.Println("Window not created", err)
}
win.SetTitle("LinuxPA")
win.Connect("destroy", func() {
gtk.MainQuit()
})
win.SetDefaultSize(500, 500)
win.SetPosition(gtk.WIN_POS_CENTER)
ui(win)
win.ShowAll()
win.Show()
update(win)
gtk.Main()
} }
func contains(arr []string, str string) bool { func contains(arr []string, str string) bool {
+51 -19
View File
@@ -2,21 +2,56 @@ package main
import ( import (
"bufio" "bufio"
"image" "fmt"
"image/draw"
_ "image/png" _ "image/png"
"os" "os"
"reflect" "reflect"
"sort" "sort"
"strings" "strings"
"github.com/nelsam/gxui" "github.com/gotk3/gotk3/gdk"
"github.com/gotk3/gotk3/gtk"
) )
func setup() { func setup() {
PortableAppsFold, err := os.Open("PortableApps") PortableAppsFold, err := os.Open("PortableApps")
if PAStat, _ := PortableAppsFold.Stat(); err != nil || !PAStat.IsDir() { if PAStat, _ := PortableAppsFold.Stat(); err != nil || !PAStat.IsDir() {
panic("PortableApps folder not found!!") 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) PAFolds, _ := PortableAppsFold.Readdirnames(-1)
sort.Strings(PAFolds) sort.Strings(PAFolds)
@@ -78,7 +113,7 @@ func processApp(fold string) (out app) {
btys := make([]byte, 4) btys := make([]byte, 4)
rdr := bufio.NewReader(tmp) rdr := bufio.NewReader(tmp)
rdr.Read(btys) rdr.Read(btys)
if (strings.Contains(strings.ToLower(string(btys)), "elf") && !strings.HasSuffix(strings.ToLower(v), ".so")) || strings.HasPrefix(strings.ToLower(string(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.ex = append(out.ex, v)
out.lin = append(out.lin, v) out.lin = append(out.lin, v)
} }
@@ -119,8 +154,8 @@ func getName(ini *os.File) string {
return ret return ret
} }
func getIcon(fold string) gxui.Texture { func getIcon(fold string) *gdk.Pixbuf {
var pic *os.File var pic string
if folder, err := os.Open(fold + "/App/AppInfo"); err == nil { if folder, err := os.Open(fold + "/App/AppInfo"); err == nil {
fis, _ := folder.Readdir(-1) fis, _ := folder.Readdir(-1)
var pics []string var pics []string
@@ -137,21 +172,18 @@ func getIcon(fold string) gxui.Texture {
} else { } else {
ind = sort.SearchStrings(pics, "appicon_32.png") ind = sort.SearchStrings(pics, "appicon_32.png")
} }
pic, _ = os.Open(fold + "/App/AppInfo/" + pics[ind]) pic = fold + "/App/AppInfo/" + pics[ind]
} }
} else if fi, err := os.Open(fold + "/appicon.png"); err == nil { } else if _, err := os.Open(fold + "/appicon.png"); err == nil {
pic = fi pic = fold + "/appicon.png"
} else { } else {
return nil img, _ := gtk.ImageNewFromIconName("application-x-executable", gtk.ICON_SIZE_BUTTON)
buf, _ := img.GetPixbuf().ScaleSimple(32, 32, gdk.INTERP_BILINEAR)
return buf
} }
img, _, err := image.Decode(pic) img, _ := gtk.ImageNewFromFile(pic)
if err != nil { buf, _ := img.GetPixbuf().ScaleSimple(32, 32, gdk.INTERP_BILINEAR)
return nil return buf
}
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 { func findInfo(fold string) *os.File {
+119 -56
View File
@@ -1,66 +1,129 @@
package main package main
import ( import (
"os" "fmt"
"os/exec"
"github.com/nelsam/gxui" "github.com/gotk3/gotk3/glib"
"github.com/gotk3/gotk3/gtk"
) )
func ui() { func ui(win *gtk.Window) {
catListAdap := &StrList{} ls := getCatRows()
appListAdap := &catAdap{} var treeApps []*gtk.TreeIter
catListAdap.SetStrings(lin) header, _ := gtk.HeaderBarNew()
win := th.CreateWindow(500, 500, "LinuxPA") header.SetShowCloseButton(true)
top := th.CreateLinearLayout() header.SetTitle("LinuxPA")
top.SetDirection(gxui.BottomToTop) header.SetSubtitle("PortableApps.com type launcher")
splBox := th.CreateLinearLayout() settings, _ := gtk.ButtonNewFromIconName("applications-system", gtk.ICON_SIZE_SMALL_TOOLBAR)
spl := th.CreateSplitterLayout() settings.Connect("clicked", func() {
spl.SetOrientation(gxui.Horizontal) //Open Settings window!
catList := th.CreateList()
catList.SetAdapter(catListAdap)
catList.OnSelectionChanged(func(it gxui.AdapterItem) {
appListAdap.setCat(it.(string))
}) })
appList := th.CreateTree() settings.SetTooltipText("Settings (Coming Soon!)")
appList.SetAdapter(appListAdap) header.PackStart(settings)
spl.AddChild(catList) win.SetTitlebar(header)
spl.AddChild(appList) topLvl, _ := gtk.BoxNew(gtk.ORIENTATION_VERTICAL, 0)
splBox.AddChild(spl) lrBox, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 5)
butBox := th.CreateLinearLayout() catList, _ := gtk.ListBoxNew()
butBox.SetDirection(gxui.LeftToRight) catList.SetActivateOnSingleClick(true)
if _, err := exec.LookPath("wine"); err == nil { store, _ := gtk.TreeStoreNew(glib.TYPE_OBJECT, glib.TYPE_STRING)
wineBut := th.CreateButton() appsList, _ := gtk.TreeViewNewWithModel(store)
wineBut.SetType(gxui.ToggleButton) render, _ := gtk.CellRendererPixbufNew()
wineBut.SetChecked(wine) pixColumn, _ := gtk.TreeViewColumnNewWithAttribute("", render, "pixbuf", 0)
wineBut.SetText("Show Windows Apps") txtRender, _ := gtk.CellRendererTextNew()
wineBut.OnClick(func(gxui.MouseEvent) { txtColumn, _ := gtk.TreeViewColumnNewWithAttribute("", txtRender, "text", 1)
wine = wineBut.IsChecked() appsList.AppendColumn(pixColumn)
appListAdap.refresh() appsList.AppendColumn(txtColumn)
if wineBut.IsChecked() { catList.SetHExpand(true)
catListAdap.SetStrings(cats) catList.SetVExpand(true)
wineBut.SetText("Hide Windows Apps") appsList.SetHExpand(true)
} else { appsList.SetVExpand(true)
catListAdap.SetStrings(lin) vScrollCat, _ := gtk.AdjustmentNew(0, 0, 0, 0, 0, 0)
wineBut.SetText("Show Windows Apps") 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)
_, err := os.Open("Start.exe") catScrl, _ := gtk.ScrolledWindowNew(hScrollCat, vScrollCat)
if err == nil { catScrl.Add(catList)
pa := th.CreateButton() appScrl, _ := gtk.ScrolledWindowNew(hScrollApp, vScrollApp)
pa.SetText("Open PortableApps Launcher") appScrl.Add(appsList)
pa.OnClick(func(gxui.MouseEvent) { lrBox.Add(catScrl)
cmd := exec.Command("wine", "Start.exe") lrBox.Add(appScrl)
cmd.Stdout = os.Stdout botBox, _ := gtk.BoxNew(gtk.ORIENTATION_HORIZONTAL, 2)
cmd.Stderr = os.Stderr wineCheck, _ := gtk.CheckButtonNewWithLabel("Show Windows apps (Wine)")
cmd.Start() wineCheck.Connect("toggled", func() {
}) wine = wineCheck.GetActive()
butBox.AddChild(pa) for i := range ls {
catList.Remove(catList.GetRowAtIndex(i))
} }
butBox.AddChild(wineBut) 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)
} }
top.AddChild(butBox) catList.Connect("row-selected", func() {
top.AddChild(splBox) store.Clear()
win.AddChild(top) if catList.GetSelectedRow().GetIndex() >= 0 {
win.OnClose(dr.Terminate) 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])
}
}
}
})
}
func getCatRows() (out []*gtk.Label) {
if wine {
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
} }
+159
View File
@@ -0,0 +1,159 @@
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_TOPLEVEL)
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) {
stat, err := versionDL()
if stat {
res := getVersionFileInfo()
if res != "Error!" {
stat, err = checkForUpdate(res)
if stat {
lbl.SetText("Updating!")
downloadUpdate(res)
updateWin.Close()
win.Close()
cmd := exec.Command("./LinuxPA")
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Start()
} else {
fmt.Println(err)
updateWin.Close()
}
} else {
fmt.Println("Failed Version File Info")
updateWin.Close()
}
} else {
fmt.Println(err)
updateWin.Close()
}
}(win, updateWin)
}
+42
View File
@@ -0,0 +1,42 @@
package main
import (
"io"
"net/http"
"os"
"github.com/mholt/archiver"
)
const (
wineURL = "https://www.playonlinux.com/wine/binaries/linux-amd64/PlayOnLinux-wine-2.5-linux-amd64.pol"
)
func downloadWine() (bool, error) {
wineTar, err := os.Create("PortableApps/LinuxPACom/wine2.5.tar.bz2")
if err != nil {
return false, err
}
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 {
return false, err
}
defer resp.Body.Close()
_, err = io.Copy(wineTar, resp.Body)
if err != nil {
return false, err
}
err = archiver.TarBz2.Open("PortableApps/LinuxPACom/wine2.5.tar.bz2", "PortableApps/LinuxPACom/Wine")
if err != nil {
return false, err
}
return true, nil
}