diff --git a/.idea/manjaro-hello.iml b/.idea/manjaro-hello.iml
new file mode 100644
index 0000000..6711606
--- /dev/null
+++ b/.idea/manjaro-hello.iml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..8656114
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..75718e6
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
new file mode 100644
index 0000000..3599221
--- /dev/null
+++ b/.idea/workspace.xml
@@ -0,0 +1,284 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ share_dir
+
+
+ data_path
+
+
+
+
+
+
+
+
+
+
+
+ true
+ DEFINITION_ORDER
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1534949710604
+
+
+ 1534949710604
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ file://$PROJECT_DIR$/src/app-utility
+ 59
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/data/advanced.json b/data/advanced.json
new file mode 100644
index 0000000..3e2705e
--- /dev/null
+++ b/data/advanced.json
@@ -0,0 +1,646 @@
+[
+ {
+ "name": "Browsers",
+ "icon": "browser",
+ "description": "Web browsing and communication",
+ "apps": [
+ {
+ "name": "Chromium",
+ "icon": "chromium",
+ "description": "Open Sourced Chrome Browser",
+ "pkg": "chromium",
+ "extra": []
+ },
+ {
+ "name": "Falkon",
+ "icon": "falkon",
+ "description": "Qt based Web Browser",
+ "pkg": "falkon",
+ "extra": []
+ },
+ {
+ "name": "Firefox",
+ "icon": "mozilla-firefox",
+ "description": "Mozilla Web Browser",
+ "pkg": "firefox",
+ "extra": []
+ },
+ {
+ "name": "Midori",
+ "icon": "midori",
+ "description": "Lightweight Webbrowser",
+ "pkg": "midori",
+ "extra": []
+ },
+ {
+ "name": "Netsurf",
+ "icon": "netsurf",
+ "description": "Light and Fast Web Browser",
+ "pkg": "netsurf",
+ "extra": []
+ },
+ {
+ "name": "Opera",
+ "icon": "opera",
+ "description": "Fast and secure webbrowser",
+ "pkg": "opera",
+ "extra": []
+ }
+ ]
+ },
+ {
+ "name": "E-mail",
+ "icon": "mail-client",
+ "description": "E-mail, Calendar, Tasks",
+ "apps": [
+ {
+ "name": "Claws Mail",
+ "icon": "claws-mail",
+ "description": "Lightweight and fast GTK+ based Mail Client",
+ "pkg": "claws-mail",
+ "extra": []
+ },
+ {
+ "name": "Evolution",
+ "icon": "evolution",
+ "description": "Manage your email, contacts and schedule",
+ "pkg": "evolution",
+ "extra": []
+ },
+ {
+ "name": "Geary",
+ "icon": "geary",
+ "description": "Send and receive mail",
+ "pkg": "geary",
+ "extra": []
+ },
+ {
+ "name": "Sylpheed",
+ "icon": "sylpheed",
+ "description": "E-mail client",
+ "pkg": "sylpheed",
+ "extra": []
+ },
+ {
+ "name": "Thunderbird",
+ "icon": "thunderbird",
+ "description": "Send and receive mail, contacts and schedule",
+ "pkg": "thunderbird",
+ "extra": []
+ }
+ ]
+ },
+ {
+ "name": "Backup",
+ "icon": "deja-dup",
+ "description": "Backup utilites",
+ "apps": [
+ {
+ "name": "Deja Dup",
+ "icon": "deja-dup",
+ "description": "Keep your important documents safe from disater",
+ "pkg": "deja-dup",
+ "extra": []
+ },
+ {
+ "name": "Grsync",
+ "icon": "grsync",
+ "description": "Synchronize files and folders",
+ "pkg": "grsync",
+ "extra": []
+ },
+ {
+ "name": "Timeshift",
+ "icon": "timeshift",
+ "description": "A system restore utility for Linux",
+ "pkg": "timeshift",
+ "extra": []
+ }
+ ]
+ },
+ {
+ "name": "Text Editors",
+ "icon": "text-editor",
+ "description": "Various editors for text or code",
+ "apps": [
+ {
+ "name": "Atom",
+ "icon": "atom",
+ "description": "A hackable text editor for the 21st Century",
+ "pkg": "atom",
+ "extra": []
+ },
+ {
+ "name": "Geany",
+ "icon": "geany",
+ "description": "A fast and lightweight IDE using GTK+",
+ "pkg": "geany",
+ "extra": []
+ },
+ {
+ "name": "Mousepad",
+ "icon": "mousepad",
+ "description": "Simple Text Editor",
+ "pkg": "mousepad",
+ "extra": []
+ },
+ {
+ "name": "Xed",
+ "icon": "xed",
+ "description": "A small and lightweight text editor. X Apps Project",
+ "pkg": "xed",
+ "extra": []
+ }
+ ]
+ },
+ {
+ "name": "System Tools",
+ "icon": "disk-utility",
+ "description": "System utilities",
+ "apps": [
+ {
+ "name": "Gnome Disks",
+ "icon": "gnome-disks",
+ "description": "Disk management system for Gnome",
+ "pkg": "gnome-disk-utility",
+ "extra": []
+ },
+ {
+ "name": "Gparted",
+ "icon": "gparted",
+ "description": "Create, reorganize, and delete partitions",
+ "pkg": "gparted",
+ "extra": []
+ },
+ {
+ "name": "IsoUSB",
+ "icon": "usb-creator",
+ "description": "A graphical tool to copy a hybrid ISO onto a USB key.",
+ "pkg": "isousb",
+ "extra": []
+ },
+ {
+ "name": "Mintstick",
+ "icon": "mintstick",
+ "description": "Format or wirte imges to usb sticks (Linux Mint tool).",
+ "pkg": "mintstick",
+ "extra": []
+ },
+ {
+ "name": "Pamac",
+ "icon": "pamac-updater",
+ "description": "Update your System, Add/Remove Software from repo and AUR",
+ "pkg": "pamac",
+ "extra": []
+ },
+ {
+ "name": "Yay",
+ "icon": "terminal",
+ "description": "CLI AUR helper",
+ "pkg": "yay",
+ "extra": []
+ }
+ ]
+ },
+ {
+ "name": "Security",
+ "icon": "security-high",
+ "description": "Security oriented utilities",
+ "apps": [
+ {
+ "name": "KeePassX",
+ "icon": "keepassx",
+ "description": "Cross Platform Password Manager",
+ "pkg": "keepassx",
+ "extra": []
+ },
+ {
+ "name": "SeaHorse",
+ "icon": "seahorse",
+ "description": "Manage your passwords and encryption keys",
+ "pkg": "seahorse",
+ "extra": []
+ },
+ {
+ "name": "VeraCrypt",
+ "icon": "veracrypt",
+ "description": "Disk encryption with strong security based on TrueCrypt",
+ "pkg": "veracrypt",
+ "extra": []
+ }
+ ]
+ },
+ {
+ "name": "Virtual Computing",
+ "icon": "virt-manager",
+ "description": "Virtual Machine applications",
+ "apps": [
+ {
+ "name": "VirtualBox",
+ "icon": "virtualbox",
+ "description": "Run several virtual systems on a single host computer",
+ "pkg": "calibre",
+ "extra": []
+ },
+ {
+ "name": "Gnome Boxes",
+ "icon": "gnome-boxes",
+ "description": "Simple remote and virtual machines",
+ "pkg": "gnome-boxes",
+ "extra": []
+ }
+ ]
+ },
+ {
+ "name": "Chat",
+ "icon": "internet-chat",
+ "description": "Online messaging and chat",
+ "apps": [
+ {
+ "name": "HexChat",
+ "icon": "hexchat",
+ "description": "Graphic IRC Client",
+ "pkg": "hexchat",
+ "extra": []
+ },
+ {
+ "name": "Pidgin Messenger",
+ "icon": "pidgin",
+ "description": "Instant messaging Client",
+ "pkg": "pidgin",
+ "extra": []
+ }
+ ]
+ },
+ {
+ "name": "File Sharing",
+ "icon": "transmission",
+ "description": "FTP and Torrent apps",
+ "apps": [
+ {
+ "name": "Filezilla",
+ "icon": "filezilla",
+ "description": "Graphical FTP/FTPS/SFTP browser",
+ "pkg": "filezilla",
+ "extra": []
+ },
+ {
+ "name": "qBittorrent",
+ "icon": "qbittorrent",
+ "description": "A Qt based Bittorrent Client",
+ "pkg": "qbittorrent",
+ "extra": []
+ },
+ {
+ "name": "Transmission GTK",
+ "icon": "transmission",
+ "description": "GTK based Bittorrent Client",
+ "pkg": "transmission-gtk",
+ "extra": []
+ },
+ {
+ "name": "QTransmission",
+ "icon": "transmission",
+ "description": "QT based Bittorrent Client",
+ "pkg": "transmission-qt",
+ "extra": []
+ }
+ ]
+ },
+ {
+ "name": "Graphics Creating",
+ "icon": "applications-accessories",
+ "description": "Creating and editing graphics",
+ "apps": [
+ {
+ "name": "Blender",
+ "icon": "blender",
+ "description": "3D modeling and animation",
+ "pkg": "blender",
+ "extra": []
+ },
+ {
+ "name": "GIMP",
+ "icon": "gimp",
+ "description": "Create images and edit photographs",
+ "pkg": "gimp",
+ "extra": []
+ },
+ {
+ "name": "Inkscape",
+ "icon": "inkscape",
+ "description": "Vector Graphics Editor",
+ "pkg": "inkscape",
+ "extra": []
+ },
+ {
+ "name": "Krita",
+ "icon": "krita",
+ "description": "Digital Painting Creative Freedom",
+ "pkg": "krita",
+ "extra": []
+ },
+ {
+ "name": "Pinta",
+ "icon": "pinta",
+ "description": "Easy create and edit images",
+ "pkg": "pinta",
+ "extra": []
+ },
+ {
+ "name": "Tux Paint",
+ "icon": "tuxpaint",
+ "description": "Drawing program for children",
+ "pkg": "tuxpaint",
+ "extra": []
+ }
+ ]
+ },
+ {
+ "name": "Graphics Organizing",
+ "icon": "applications-graphics",
+ "description": "Viewers and organizers",
+ "apps": [
+ {
+ "name": "Gpicview",
+ "icon": "gpicview",
+ "description": "Lightweight Image Viewer",
+ "pkg": "gpicview",
+ "extra": []
+ },
+ {
+ "name": "gThumb",
+ "icon": "gthumb",
+ "description": "View and organize your images",
+ "pkg": "gthumb",
+ "extra": []
+ },
+ {
+ "name": "Gwenview",
+ "icon": "gwenview",
+ "description": "Image Viewer",
+ "pkg": "gwenview",
+ "extra": []
+ },
+ {
+ "name": "Ristretto",
+ "icon": "ristretto",
+ "description": "Free and lightweight image viewer",
+ "pkg": "ristretto",
+ "extra": []
+ },
+ {
+ "name": "Shotwell",
+ "icon": "shotwell",
+ "description": "Popular Photo Manager",
+ "pkg": "shotwell",
+ "extra": []
+ },
+ {
+ "name": "Viewnior",
+ "icon": "viewnior",
+ "description": "GTK based Elegant Image Viewer",
+ "pkg": "viewnior",
+ "extra": []
+ }
+ ]
+ },
+ {
+ "name": "Video/Movie",
+ "icon": "video-player",
+ "description": "Organize and play videos and movies",
+ "apps": [
+ {
+ "name": "Kodi",
+ "icon": "kodi",
+ "description": "Manage and view your media",
+ "pkg": "kodi",
+ "extra": []
+ },
+ {
+ "name": "Parole",
+ "icon": "parole",
+ "description": "Modern and simple media player",
+ "pkg": "parole",
+ "extra": []
+ },
+ {
+ "name": "SM Player",
+ "icon": "smplayer",
+ "description": "A great MPlayer front end",
+ "pkg": "smplayer",
+ "extra": []
+ },
+ {
+ "name": "Totem",
+ "icon": "totem",
+ "description": "Play movies",
+ "pkg": "totem",
+ "extra": []
+ },
+ {
+ "name": "VLC",
+ "icon": "vlc",
+ "description": "VLC media player, the openxource multimedia player",
+ "pkg": "vlc",
+ "extra": []
+ }
+ ]
+ },
+ {
+ "name": "Audio",
+ "icon": "musicbrainz",
+ "description": "Audio players",
+ "apps": [
+ {
+ "name": "Audacious",
+ "icon": "audacious",
+ "description": "Listen to music",
+ "pkg": "audacious",
+ "extra": []
+ },
+ {
+ "name": "Clementine",
+ "icon": "clementine",
+ "description": "Play music files and internet radio",
+ "pkg": "clementine",
+ "extra": []
+ },
+ {
+ "name": "DeadBeeF",
+ "icon": "deadbeef",
+ "description": "Listen to music",
+ "pkg": "deadbeef",
+ "extra": []
+ },
+ {
+ "name": "Lollypop",
+ "icon": "lollypop",
+ "description": "Play and organize your music collection",
+ "pkg": "deadbeef",
+ "extra": []
+ },
+ {
+ "name": "Rhythmbox",
+ "icon": "rhythmbox",
+ "description": "Gnome music playing application",
+ "pkg": "rhythmbox",
+ "extra": []
+ }
+ ]
+ },
+ {
+ "name": "Media recording/editing",
+ "icon": "kdenlive",
+ "description": "Audio and Video editing",
+ "apps": [
+ {
+ "name": "Audacity",
+ "icon": "audacity",
+ "description": "Record and Edit Audio files",
+ "pkg": "audacity",
+ "extra": []
+ },
+ {
+ "name": "Kdenlive",
+ "icon": "kdenlive",
+ "description": "Video Editor",
+ "pkg": "kdenlive",
+ "extra": []
+ },
+ {
+ "name": "OBS Studio",
+ "icon": "obs",
+ "description": "Open Source Streaming/Recording Application",
+ "pkg": "obs-studio",
+ "extra": []
+ },
+ {
+ "name": "Simple Screen Recorder",
+ "icon": "simple-ccsm",
+ "description": "Screen Capturing Application",
+ "pkg": "simplescreenrecorder",
+ "extra": []
+ }
+ ]
+ },
+ {
+ "name": "Office Suites",
+ "icon": "applications-office",
+ "description": "Office suites like MS Office",
+ "apps": [
+ {
+ "name": "Calligra Office",
+ "icon": "calligrawords",
+ "description": "Qt Based Office Suite",
+ "pkg": "calligra",
+ "extra": []
+ },
+ {
+ "name": "Libre Office (Fresh)",
+ "icon": "libreoffice-main",
+ "description": "Open Source Office Application (Lastest)",
+ "pkg": "libreoffice-fresh",
+ "extra": []
+ },
+ {
+ "name": "Libre Office (Still)",
+ "icon": "libreoffice-main",
+ "description": "Open Source Office Application (Stable)",
+ "pkg": "libreoffice-still",
+ "extra": []
+ },
+ {
+ "name": "MS Office Online",
+ "icon": "ms-word",
+ "description": "Microsoft Office Online",
+ "pkg": "ms-office-online",
+ "extra": []
+ }
+ ]
+ },
+ {
+ "name": "Office Apps",
+ "icon": "gnumeric",
+ "description": "Stand alone applications",
+ "apps": [
+ {
+ "name": "Abiword",
+ "icon": "abiword",
+ "description": "Compose, Edit and view documents",
+ "pkg": "abiword",
+ "extra": []
+ },
+ {
+ "name": "Gnumeric",
+ "icon": "gnumeric",
+ "description": "A High Precision Spreadsheet Program",
+ "pkg": "gnumeric",
+ "extra": []
+ }
+ ]
+ },
+ {
+ "name": "PDF",
+ "icon": "pdfeditor",
+ "description": "PDF applications applications",
+ "apps": [
+ {
+ "name": "Epdfview",
+ "icon": "qpdfview",
+ "description": "Lightweight PDF document viewer",
+ "pkg": "epdfview",
+ "extra": []
+ },
+ {
+ "name": "Evince",
+ "icon": "evince",
+ "description": "View multi page documents",
+ "pkg": "evince",
+ "extra": []
+ },
+ {
+ "name": "Okular",
+ "icon": "okular",
+ "description": "Document Viewer",
+ "pkg": "Okular",
+ "extra": []
+ },
+ {
+ "name": "PDFMod",
+ "icon": "pdfmod",
+ "description": "Remove, extract and rotate pages in PDF Documents",
+ "pkg": "pdfmod",
+ "extra": []
+ },
+ {
+ "name": "Qpdfview",
+ "icon": "qpdfview",
+ "description": "Tabbed document viewer",
+ "pkg": "qpdfview",
+ "extra": []
+ }
+ ]
+ },
+ {
+ "name": "E-Book",
+ "icon": "calibre",
+ "description": "E-book library apps",
+ "apps": [
+ {
+ "name": "Calibre",
+ "icon": "calibre-viewer",
+ "description": "The one stop solution to your e-book needs",
+ "pkg": "calibre",
+ "extra": []
+ },
+ {
+ "name": "FBReader",
+ "icon": "fbreader",
+ "description": "FBReader E-Book Reader",
+ "pkg": "fbreader",
+ "extra": []
+ }
+ ]
+ }
+]
\ No newline at end of file
diff --git a/data/default.json b/data/default.json
new file mode 100644
index 0000000..eaa2d43
--- /dev/null
+++ b/data/default.json
@@ -0,0 +1,352 @@
+[
+ {
+ "name": "Browsers",
+ "icon": "browser",
+ "description": "Web browsning and communication",
+ "apps": [
+ {
+ "name": "Chromium",
+ "icon": "chromium",
+ "description": "Open Sourced Chrome Browser",
+ "pkg": "chromium",
+ "extra": []
+ },
+ {
+ "name": "Firefox",
+ "icon": "mozilla-firefox",
+ "description": "Mozilla Web Browser",
+ "pkg": "firefox",
+ "extra": []
+ },
+ {
+ "name": "Opera",
+ "icon": "opera",
+ "description": "Fast and secure webbrowser",
+ "pkg": "opera",
+ "extra": []
+ }
+ ]
+ },
+ {
+ "name": "E-mail",
+ "icon": "mail-client",
+ "description": "E-mail, Calendar, Tasks",
+ "apps": [
+ {
+ "name": "Evolution",
+ "icon": "evolution",
+ "description": "Manage your email, contacts and schedule",
+ "pkg": "evolution",
+ "extra": []
+ },
+ {
+ "name": "Thunderbird",
+ "icon": "thunderbird",
+ "description": "Send and receive mail, contacts and schedule",
+ "pkg": "thunderbird",
+ "extra": []
+ }
+ ]
+ },
+ {
+ "name": "Backup",
+ "icon": "deja-dup",
+ "description": "Backup utilites",
+ "apps": [
+ {
+ "name": "Deja Dup",
+ "icon": "deja-dup",
+ "description": "Keep your important documents safe from disater",
+ "pkg": "deja-dup",
+ "extra": []
+ }
+ ]
+ },
+ {
+ "name": "Text Editors",
+ "icon": "text-editor",
+ "description": "Simple editors for text or code",
+ "apps": [
+ {
+ "name": "Mousepad",
+ "icon": "mousepad",
+ "description": "Simple Text Editor",
+ "pkg": "mousepad",
+ "extra": []
+ },
+ {
+ "name": "Xed",
+ "icon": "xed",
+ "description": "A small and lightweight text editor. X Apps Project",
+ "pkg": "xed",
+ "extra": []
+ }
+ ]
+ },
+ {
+ "name": "System Tools",
+ "icon": "disk-utility",
+ "description": "System Utilities",
+ "apps": [
+ {
+ "name": "Gnome Disks",
+ "icon": "gnome-disks",
+ "description": "Disk management system for Gnome",
+ "pkg": "gnome-disk-utility",
+ "extra": []
+ },
+ {
+ "name": "Gparted",
+ "icon": "gparted",
+ "description": "Create, reorganize, and delete partitions",
+ "pkg": "gparted",
+ "extra": []
+ },
+ {
+ "name": "Mintstick",
+ "icon": "mintstick",
+ "description": "Format or wirte imges to usb sticks (Linux Mint tool).",
+ "pkg": "mintstick",
+ "extra": []
+ },
+ {
+ "name": "Pamac",
+ "icon": "pamac-updater",
+ "description": "Update your System, Add/Remove Software from repo and AUR",
+ "pkg": "pamac",
+ "extra": []
+ }
+ ]
+ },
+ {
+ "name": "Security",
+ "icon": "security-high",
+ "description": "Security oriented utilities",
+ "apps": [
+ {
+ "name": "KeePassX",
+ "icon": "keepassx",
+ "description": "Cross Platform Password Manager",
+ "pkg": "keepassx",
+ "extra": []
+ },
+ {
+ "name": "SeaHorse",
+ "icon": "seahorse",
+ "description": "Manage your passwords and encryption keys",
+ "pkg": "seahorse",
+ "extra": []
+ },
+ {
+ "name": "VeraCrypt",
+ "icon": "veracrypt",
+ "description": "Disk encryption with strong security based on TrueCrypt",
+ "pkg": "veracrypt",
+ "extra": []
+ }
+ ]
+ },
+ {
+ "name": "Chat",
+ "icon": "internet-chat",
+ "description": "Online messaging and chat",
+ "apps": [
+ {
+ "name": "HexChat",
+ "icon": "hexchat",
+ "description": "Graphic IRC Client",
+ "pkg": "hexchat",
+ "extra": []
+ },
+ {
+ "name": "Pidgin Messenger",
+ "icon": "pidgin",
+ "description": "Instant messaging Client",
+ "pkg": "pidgin",
+ "extra": []
+ }
+ ]
+ },
+ {
+ "name": "File Sharing",
+ "icon": "transmission",
+ "description": "FTP and Torrent apps",
+ "apps": [
+ {
+ "name": "Filezilla",
+ "icon": "filezilla",
+ "description": "Graphical FTP/FTPS/SFTP browser",
+ "pkg": "filezilla",
+ "extra": []
+ },
+ {
+ "name": "qBittorrent",
+ "icon": "qbittorrent",
+ "description": "Bittorrent Client",
+ "pkg": "qbittorrent",
+ "extra": []
+ }
+ ]
+ },
+ {
+ "name": "Graphics Creating",
+ "icon": "applications-accessories",
+ "description": "Creating and editing graphics",
+ "apps": [
+ {
+ "name": "Krita",
+ "icon": "krita",
+ "description": "Create images and edit photographs",
+ "pkg": "krita",
+ "extra": []
+ },
+ {
+ "name": "Pinta",
+ "icon": "pinta",
+ "description": "Easy create and edit images",
+ "pkg": "pinta",
+ "extra": []
+ },
+ {
+ "name": "Tux Paint",
+ "icon": "tuxpaint",
+ "description": "Drawing program for children",
+ "pkg": "tuxpaint",
+ "extra": []
+ }
+ ]
+ },
+ {
+ "name": "Graphics Organizing",
+ "icon": "applications-graphics",
+ "description": "Viewers and organizers",
+ "apps": [
+ {
+ "name": "gThumb",
+ "icon": "gthumb",
+ "description": "View and organize your images",
+ "pkg": "viewnior",
+ "extra": []
+ } ,
+ {
+ "name": "Gwenview",
+ "icon": "gwenview",
+ "description": "Image Viewer",
+ "pkg": "gwenview",
+ "extra": []
+ },
+ {
+ "name": "Shotwell",
+ "icon": "shotwell",
+ "description": "Popular Photo Manager",
+ "pkg": "shotwell",
+ "extra": []
+ }
+ ]
+ },
+ {
+ "name": "Video/Movie",
+ "icon": "video-player",
+ "description": "Organize and play videos and movies",
+ "apps": [
+ {
+ "name": "SM Player",
+ "icon": "smplayer",
+ "description": "A great MPlayer front end",
+ "pkg": "smplayer",
+ "extra": []
+ },
+ {
+ "name": "VLC",
+ "icon": "vlc",
+ "description": "VLC media player, the openxource multimedia player",
+ "pkg": "vlc",
+ "extra": []
+ }
+ ]
+ },
+ {
+ "name": "Audio",
+ "icon": "musicbrainz",
+ "description": "Audio players",
+ "apps": [
+ {
+ "name": "Clementine",
+ "icon": "clementine",
+ "description": "Play music files and internet radio",
+ "pkg": "clementine",
+ "extra": []
+ },
+ {
+ "name": "Rhythmbox",
+ "icon": "rhythmbox",
+ "description": "Gnome music playing application",
+ "pkg": "rhythmbox",
+ "extra": []
+ }
+ ]
+ },
+ {
+ "name": "Office Suites",
+ "icon": "applications-office",
+ "description": "Office suites like MS Office",
+ "apps": [
+ {
+ "name": "Libre Office (Fresh)",
+ "icon": "libreoffice-writer",
+ "description": "Open Source Office Application (Lastest)",
+ "pkg": "libreoffice-fresh",
+ "extra": []
+ },
+ {
+ "name": "MS Office Online",
+ "icon": "ms-word",
+ "description": "Microsoft Office Online",
+ "pkg": "ms-office-online",
+ "extra": []
+ }
+ ]
+ },
+ {
+ "name": "PDF",
+ "icon": "pdfeditor",
+ "description": "PDF applications applications",
+ "apps": [
+ {
+ "name": "Evince",
+ "icon": "evince",
+ "description": "View multi page documents",
+ "pkg": "evince",
+ "extra": []
+ },
+ {
+ "name": "Okular",
+ "icon": "okular",
+ "description": "Document Viewer",
+ "pkg": "okular",
+ "extra": []
+ }
+ ]
+ },
+ {
+ "name": "E-Book",
+ "icon": "calibre",
+ "description": "E-book library apps",
+ "apps": [
+ {
+ "name": "Calibre",
+ "icon": "calibre-viewer",
+ "description": "The one stop solution to your e-book needs",
+ "pkg": "calibre",
+ "extra": []
+ },
+ {
+ "name": "FBReader",
+ "icon": "fbreader",
+ "description": "FBReader E-Book Reader",
+ "pkg": "fbreader",
+ "extra": []
+ }
+ ]
+ }
+]
\ No newline at end of file
diff --git a/data/preferences.json b/data/preferences.json
index 03491c2..6e4519d 100644
--- a/data/preferences.json
+++ b/data/preferences.json
@@ -1,24 +1,31 @@
{
- "default_locale": "en",
- "autostart_path": "~/.config/autostart/manjaro-hello.desktop",
- "data_path": "/usr/share/manjaro-hello/data/",
- "desktop_path": "/usr/share/applications/manjaro-hello.desktop",
- "installer_path": "/usr/bin/calamares",
- "live_path": "/run/miso/bootmnt/manjaro",
- "locale_path": "/usr/share/locale/",
- "logo_path": "/usr/share/icons/hicolor/64x64/apps/manjaro.png",
- "save_path": "~/.config/manjaro-hello.json",
- "ui_path": "/usr/share/manjaro-hello/ui/manjaro-hello.glade",
- "urls": {
- "development": "https://gitlab.manjaro.org",
- "chat": "https://kiwiirc.com/client/irc.freenode.net/?nick=manjaro-web|?#manjaro",
- "donate": "https://manjaro.org/donate",
- "facebook": "https://www.facebook.com/ManjaroLinux",
- "forum": "https://forum.manjaro.org",
- "google+": "https://plus.google.com/118244873957924966264",
- "mailling": "https://lists.manjaro.org/cgi-bin/mailman/listinfo",
- "reddit": "https://www.reddit.com/r/ManjaroLinux",
- "twitter": "https://twitter.com/ManjaroLinux",
- "wiki": "https://wiki.manjaro.org"
- }
+ "default_locale": "en",
+ "autostart_path": "~/.config/autostart/manjaro-hello.desktop",
+ "data_path": "/usr/share/manjaro-hello/data/",
+ "desktop_path": "/usr/share/applications/manjaro-hello.desktop",
+ "installer_path": "/usr/bin/calamares",
+ "live_path": "/run/miso/bootmnt/manjaro",
+ "locale_path": "/usr/share/locale/",
+ "logo_path": "/usr/share/icons/hicolor/64x64/apps/manjaro.png",
+ "save_path": "~/.config/manjaro-hello.json",
+ "ui_path": "/usr/share/manjaro-hello/ui/manjaro-hello.glade",
+ "data_set": "default",
+ "user_path": "~/.config",
+ "data_sets": [
+ "default",
+ "advanced"
+ ],
+ "url": "https://gitlab.manjaro.org/fhdk/application-utility/raw/master",
+ "urls": {
+ "development": "https://gitlab.manjaro.org",
+ "chat": "https://kiwiirc.com/client/irc.freenode.net/?nick=manjaro-web|?#manjaro",
+ "donate": "https://manjaro.org/donate",
+ "facebook": "https://www.facebook.com/ManjaroLinux",
+ "forum": "https://forum.manjaro.org",
+ "google+": "https://plus.google.com/118244873957924966264",
+ "mailling": "https://lists.manjaro.org/cgi-bin/mailman/listinfo",
+ "reddit": "https://www.reddit.com/r/ManjaroLinux",
+ "twitter": "https://twitter.com/ManjaroLinux",
+ "wiki": "https://wiki.manjaro.org"
+ }
}
diff --git a/src/app-install b/src/app-install
new file mode 100755
index 0000000..c7dc46b
--- /dev/null
+++ b/src/app-install
@@ -0,0 +1,118 @@
+#!/bin/bash
+
+# MIT License
+#
+# Copyright (c) 2018 Fredes Computer Service
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+#set -x
+if [[ -n ${APP_UTILITY} ]]; then
+ echo "This script is part of Manjaro Application Utility\nIt is not meant to be run from command line\n exit()"
+fi
+
+# files
+IN_PKG_FILE="/tmp/.install-packages.txt"
+RM_PKG_FILE="/tmp/.remove-packages.txt"
+PM_STATUS_FILE="/tmp/.pm-status.txt"
+PM_LOCK_FILE="/var/lib/pacman/db.lck"
+
+echo ${IN_PKG_FILE}
+echo ${RM_PKG_FILE}
+
+# messages
+TITLE="Manjaro Application Utility"
+UPD_DB_MSG="Updating Package Database..."
+DB_LOCKED_MSG="Another package manager is running.\n
+If you are updating the system\n
+you MUST finish before continuing.\n
+If you are not, it is safe to kill the other process.\n
+What would you like to do?"
+KILL_PROCESS="Kill other process and continue"
+WAIT_PROCESS="Wait to finish updating"
+IN_WORKING_MSG="Installing Packages..."
+RM_WORKING_MSG="Uninstalling Packages..."
+DONE="Your System has Been Successfully Updated"
+
+if [[ -f ${IN_PKG_FILE} ]]; then
+ IN_PKG=` cat ${IN_PKG_FILE} `
+ echo ${IN_PKG}
+fi
+
+if [[ -f ${RM_PKG_FILE} ]]; then
+ RM_PKG=` cat ${RM_PKG_FILE} `
+ echo ${RM_PKG}
+fi
+
+# Make sure only root can run our script
+if [[ $EUID -ne 0 ]]; then
+ echo "Superuser required ..." 1>&2
+ exit 1
+fi
+
+# updating databases and writing results to a file
+pacman -Syy | tee ${PM_STATUS_FILE} | zenity --progress --title="${TITLE}" --no-cancel --pulsate --text "${UPD_DB_MSG}" --width=500 --auto-close
+
+PM_STATUS=` cat ${PM_STATUS_FILE} `
+
+# checking for other running package managers
+if [[ $(cat ${PM_STATUS_FILE} | grep -i 'core.db') = "" ]]
+ then # giving choice to user to kill other process or wait
+ ans=$(zenity --list --title="${TITLE}" --radiolist --text "${DB_LOCKED_MSG}" --column Select --column Choice TRUE "${KILL_PROCESS}" FALSE "${WAIT_PROCESS}" --width=500 --height=300)
+ # killing pamac and unlocking db
+ if [ "$ans" = "${KILL_PROCESS}" ]
+ then
+ killall pamac-updater
+ killall pamac-manager
+ rm ${PM_LOCK_FILE}
+ pacman -Syy | zenity --progress --title="${TITLE}" --no-cancel --pulsate --text "${UPD_DB_MSG}" --width=500 --auto-close
+ else # exiting according to user choice
+ exit
+ fi
+fi
+
+# Installing packages
+if [[ -n ${IN_PKG} ]]; then
+ pacman -Syu --noconfirm ${IN_PKG} | zenity --progress --title="${TITLE}" --no-cancel --pulsate --text "${IN_WORKING_MSG}" --width=500 --auto-close
+fi
+
+# Uninstalling packages
+if [[ -n ${RM_PKG} ]]; then
+ pacman -R --noconfirm ${RM_PKG} | zenity --progress --title="${TITLE}" --no-cancel --pulsate --text "${RM_WORKING_MSG}" --width=500 --auto-close
+fi
+
+# Letting user know that packages have been installed
+zenity --info --text="${DONE}" --width=500 --height=300
+
+# removing files no longer needed
+if [[ -f ${IN_PKG_FILE} ]]; then
+ rm ${IN_PKG_FILE}
+fi
+
+if [[ -f ${RM_PKG_FILE} ]]; then
+ rm ${RM_PKG_FILE}
+fi
+
+if [[ -f ${PM_STATUS_FILE} ]]; then
+ rm ${PM_STATUS_FILE}
+fi
+
+if [[ -f ${PM_LOCK_FILE} ]]; then
+ rm ${PM_LOCK_FILE}
+fi
diff --git a/src/app-utility b/src/app-utility
new file mode 100755
index 0000000..651e999
--- /dev/null
+++ b/src/app-utility
@@ -0,0 +1,371 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+# MIT License
+#
+# Copyright (c) 2018 Fredes Computer Service
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+import collections
+import json
+import glob
+import os
+import subprocess
+import sys
+import urllib.request
+
+import gi
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk, GLib
+
+VERSION = "0.8"
+TITLE = "Manjaro Application Utility {}".format(VERSION)
+DEBUG = True
+
+GROUP = 0
+ICON = 1
+APPLICATION = 2
+DESCRIPTION = 3
+ACTIVE = 4
+PACKAGE = 5
+INSTALLED = 6
+
+
+class AppWindow(Gtk.Window):
+ def __init__(self):
+ Gtk.Window.__init__(self, title=TITLE, border_width=6)
+ self.app = "app-utility"
+ self.pref = {
+ "data_set": "default",
+ "conf_dir": "~/.config",
+ "share_dir": "/usr/share/{}".format(self.app),
+ "data_sets": ["default", "advanced"],
+ "url": "https://gitlab.manjaro.org/fhdk/application-utility/raw/master"
+ }
+
+ self.dev = "--dev" in sys.argv
+ if self.dev:
+ self.pref["share_dir"] = "."
+
+ self.set_position(Gtk.WindowPosition.CENTER_ALWAYS)
+ GLib.set_prgname("{}".format(self.app))
+ icon="system-software-install"
+ pixbuf24 = Gtk.IconTheme.get_default().load_icon(icon, 24, 0)
+ pixbuf32 = Gtk.IconTheme.get_default().load_icon(icon, 32, 0)
+ pixbuf48 = Gtk.IconTheme.get_default().load_icon(icon, 48, 0)
+ pixbuf64 = Gtk.IconTheme.get_default().load_icon(icon, 64, 0)
+ pixbuf96 = Gtk.IconTheme.get_default().load_icon(icon, 96, 0)
+ self.set_icon_list([pixbuf24, pixbuf32, pixbuf48, pixbuf64, pixbuf96])
+
+ # set data
+ self.app_store = None
+ self.pkg_selected = None
+ self.pkg_installed = None
+ self.pkg_list_install = []
+ self.pkg_list_removal = []
+
+ # setup main box
+ self.set_default_size(800, 650)
+ self.application_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
+ self.add(self.application_box)
+
+ # create title box
+ self.title_box = Gtk.Box()
+ self.title_image = Gtk.Image()
+ self.title_image.set_size_request(100, 100)
+ self.title_image.set_from_file("/usr/share/icons/manjaro/maia/96x96.png")
+ self.title_label = Gtk.Label()
+ self.title_label.set_markup("Manjaro Application Maintenance\n"
+ "Select/Deselect apps you want to install/remove.\n"
+ "Click UPDATE SYSTEM button when ready.")
+ self.title_box.pack_start(self.title_image, expand=False, fill=False, padding=0)
+ self.title_box.pack_start(self.title_label, expand=True, fill=True, padding=0)
+
+ # pack title box to main box
+ self.application_box.pack_start(self.title_box, expand=False, fill=False, padding=0)
+
+ # setup grid
+ self.grid = Gtk.Grid()
+ self.grid.set_column_homogeneous(True)
+ self.grid.set_row_homogeneous(True)
+ self.application_box.add(self.grid)
+
+ # setup list store model
+ self.app_store = self.load_app_data(self.pref["data_set"])
+
+ # create a tree view with the model store
+ self.tree_view = Gtk.TreeView.new_with_model(self.app_store)
+ self.tree_view.set_activate_on_single_click(True)
+
+ # column model: icon
+ icon = Gtk.CellRendererPixbuf()
+ column = Gtk.TreeViewColumn("", icon, icon_name=ICON)
+ self.tree_view.append_column(column)
+
+ # column model: group name column
+ renderer = Gtk.CellRendererText()
+ column = Gtk.TreeViewColumn("Group", renderer, text=GROUP)
+ self.tree_view.append_column(column)
+
+ # column model: app name column
+ renderer = Gtk.CellRendererText()
+ column = Gtk.TreeViewColumn("Application", renderer, text=APPLICATION)
+ self.tree_view.append_column(column)
+
+ # column model: description column
+ renderer = Gtk.CellRendererText()
+ column = Gtk.TreeViewColumn("Description", renderer, text=DESCRIPTION)
+ self.tree_view.append_column(column)
+
+ # column model: install column
+ toggle = Gtk.CellRendererToggle()
+ toggle.connect("toggled", self.on_app_toggle)
+ column = Gtk.TreeViewColumn("Installed", toggle, active=ACTIVE)
+ self.tree_view.append_column(column)
+
+ # button box
+ self.button_box = Gtk.Box(spacing=10)
+ self.advanced = Gtk.ToggleButton(label="Advanced")
+ self.advanced.connect("clicked", self.on_expert_clicked)
+ self.download = Gtk.Button(label="download")
+ self.download.connect("clicked", self.on_download_clicked)
+ self.reload_button = Gtk.Button(label="reload")
+ self.reload_button.connect("clicked", self.on_reload_clicked)
+ self.update_system_button = Gtk.Button(label="UPDATE SYSTEM")
+ self.update_system_button.connect("clicked", self.on_update_system_clicked)
+ self.close_button = Gtk.Button(label="close")
+ self.close_button.connect("clicked", Gtk.main_quit)
+ self.button_box.pack_start(self.advanced, expand=False, fill=False, padding=10)
+ self.button_box.pack_end(self.update_system_button, expand=False, fill=False, padding=10)
+ self.button_box.pack_end(self.close_button, expand=False, fill=False, padding=10)
+ self.button_box.pack_end(self.reload_button, expand=False, fill=False, padding=10)
+ self.button_box.pack_end(self.download, expand=False, fill=False, padding=10)
+ self.application_box.pack_end(self.button_box, expand=False, fill=False, padding=10)
+
+ # create a scrollable window
+ self.app_window = Gtk.ScrolledWindow()
+ self.app_window.set_vexpand(True)
+ self.app_window.add(self.tree_view)
+ self.grid.attach(self.app_window, 0, 0, 5, len(self.app_store))
+
+ # show start
+ self.show_all()
+
+ def load_app_data(self, data_set):
+ if os.path.isfile("{}/{}.json".format(self.pref["conf_dir"], data_set)):
+ app_data = self.read_json_file("{}/{}.json".format(self.pref["conf_dir"], data_set))
+ else:
+ app_data = self.read_json_file("{}/{}.json".format(self.pref["share_dir"], data_set))
+
+ store = Gtk.TreeStore(str, str, str, str, bool, str, bool)
+ for group in app_data:
+ index = store.append(None,
+ [group["name"],
+ group["icon"],
+ None, group["description"], None, None, None])
+ for app in group["apps"]:
+ status = self.app_installed(app["pkg"])
+ tree_item = (None,
+ app["icon"],
+ app["name"],
+ app["description"],
+ status,
+ app["pkg"],
+ status)
+ store.append(index, tree_item)
+ return store
+
+ def reload_app_data(self, dataset):
+ self.pkg_selected = None
+ self.pkg_installed = None
+ self.pkg_list_install = []
+ self.pkg_list_removal = []
+ self.app_store.clear()
+ self.app_store = self.load_app_data(dataset)
+ self.tree_view.set_model(self.app_store)
+
+ def on_reload_clicked(self, widget):
+ self.reload_app_data(self.pref["data_set"])
+
+ def on_expert_clicked(self, widget):
+ if widget.get_active():
+ self.pref["data_set"] = "advanced"
+ else:
+ self.pref["data_set"] = "default"
+ self.reload_app_data(self.pref["data_set"])
+
+ def on_download_clicked(self, widget):
+ if self.net_check():
+ # noinspection PyBroadException
+ try:
+ for download in self.pref["data_sets"]:
+ url = "{}/{}.json".format(self.pref["url"], download)
+ file = self.fix_path("{}/{}.json".format(self.pref["conf_dir"], download))
+ req = urllib.request.Request(url=url)
+ with urllib.request.urlopen(req, timeout=2) as response:
+ data = json.loads(response.read().decode("utf8"))
+ self.write_json_file(data, file)
+
+ except Exception as e:
+ print(e)
+
+ else:
+ dialog = Gtk.MessageDialog(self, 0,
+ Gtk.MessageType.ERROR,
+ Gtk.ButtonsType.CANCEL,
+ "Download not available")
+ dialog.format_secondary.text("The server 'gitlab.manjaro.org' could not be reached")
+ dialog.run()
+
+ def on_app_toggle(self, cell, path):
+ # a group has no package attached and we don't install groups
+ if self.app_store[path][PACKAGE] is not None:
+ self.app_store[path][ACTIVE] = not self.app_store[path][ACTIVE]
+ self.pkg_selected = self.app_store[path][PACKAGE]
+ self.pkg_installed = self.app_store[path][INSTALLED]
+
+ if self.app_store[path][ACTIVE] is False:
+ if self.pkg_installed is True:
+ # to uninstall
+ self.pkg_list_removal.append(self.pkg_selected)
+ if self.dev:
+ print("for removal : {}".format(self.pkg_selected))
+ if self.pkg_selected in self.pkg_list_install:
+ # cancel install
+ self.pkg_list_install.remove(self.pkg_selected)
+ if self.dev:
+ print("cancel install: {}".format(self.pkg_selected))
+ else:
+ # don't reinstall
+ if self.pkg_installed is False:
+ # only install
+ if self.pkg_selected not in self.pkg_list_install:
+ self.pkg_list_install.append(self.pkg_selected)
+ if self.dev:
+ print("to install : {}".format(self.pkg_selected))
+ if self.pkg_selected in self.pkg_list_removal:
+ # cancel uninstall
+ self.pkg_list_removal.remove(self.pkg_selected)
+ if self.dev:
+ print("pkg list install: {}".format(self.pkg_list_install))
+ print("pkg list removal: {}".format(self.pkg_list_removal))
+
+ def on_update_system_clicked(self, widget):
+ file_install = "/tmp/.install-packages.txt"
+ file_uninstall = "/tmp/.remove-packages.txt"
+
+ os.environ["APP_UTILITY"] = "PACKAGES"
+ shell_fallback = False
+
+ if self.pkg_list_install:
+ if os.path.isfile("/usr/bin/pamac-installer"):
+ subprocess.run(["pamac-installer"] + self.pkg_list_install)
+ else:
+ shell_fallback = True
+ with open(file_install, "w") as outfile:
+ for p in self.pkg_list_install:
+ outfile.write("{} ".format(p))
+
+ if self.pkg_list_removal:
+ shell_fallback = True
+ with open(file_uninstall, "w") as outfile:
+ for p in self.pkg_list_removal:
+ outfile.write("{} ".format(p))
+
+ if shell_fallback:
+ if self.dev:
+ os.system('gksu-polkit ./app-install')
+ else:
+ os.system('gksu-polkit app-install')
+
+ self.reload_app_data(self.pref["data_set"])
+
+ @staticmethod
+ def app_installed(package):
+ if glob.glob("/var/lib/pacman/local/{}-[0-9]*".format(package)):
+ return True
+ return False
+
+ @staticmethod
+ def fix_path(path):
+ """Make good paths.
+ :param path: path to fix
+ :type path: str
+ :return: fixed path
+ :rtype: str
+ """
+ if "~" in path:
+ path = path.replace("~", os.path.expanduser("~"))
+ return path
+
+ @staticmethod
+ def net_check():
+ """Check for internet connection"""
+ resp = None
+ host = "https://gitlab.manjaro.org"
+ # noinspection PyBroadException
+ try:
+ resp = urllib.request.urlopen(host, timeout=2)
+ except Exception:
+ pass
+ return bool(resp)
+
+ @staticmethod
+ def read_json_file(filename, dictionary=True):
+ """Read json data from file"""
+ result = list()
+ try:
+ if dictionary:
+ with open(filename, "rb") as infile:
+ result = json.loads(
+ infile.read().decode("utf8"),
+ object_pairs_hook=collections.OrderedDict)
+ else:
+ with open(filename, "r") as infile:
+ result = json.load(infile)
+ except OSError:
+ pass
+ return result
+
+ @staticmethod
+ def write_json_file(data, filename, dictionary=False):
+ """Writes data to file as json
+ :param data
+ :param filename:
+ :param dictionary:
+ """
+ try:
+ if dictionary:
+ with open(filename, "wb") as outfile:
+ json.dump(data, outfile)
+ else:
+ with open(filename, "w") as outfile:
+ json.dump(data, outfile, indent=2)
+ return True
+ except OSError:
+ return False
+
+
+if __name__ == "__main__":
+ win = AppWindow()
+ win.connect("delete-event", Gtk.main_quit)
+ win.connect("destroy", Gtk.main_quit)
+ win.show_all()
+ Gtk.main()
diff --git a/src/manjaro_hello.py b/src/manjaro_hello.py
index 3040cca..16f7c12 100644
--- a/src/manjaro_hello.py
+++ b/src/manjaro_hello.py
@@ -1,5 +1,8 @@
#!/usr/bin/env python3
+import collections
+import glob
+import urllib.request
import gettext
import json
import locale
@@ -8,17 +11,348 @@ import subprocess
import sys
import webbrowser
import gi
+
gi.require_version("Gtk", "3.0")
-from gi.repository import Gtk, GdkPixbuf
+from gi.repository import Gtk, GdkPixbuf, GLib
+
+# Applications class constants
+VERSION = "0.8"
+TITLE = "Manjaro Application Utility {}".format(VERSION)
+GROUP = 0
+ICON = 1
+APPLICATION = 2
+DESCRIPTION = 3
+ACTIVE = 4
+PACKAGE = 5
+INSTALLED = 6
-class Hello():
+class Applications(Gtk.Window):
+ def __init__(self):
+ Gtk.Window.__init__(self, title=TITLE, border_width=6)
+ self.app = "manjaro-hello"
+ self.dev = "--dev" in sys.argv
+ if self.dev:
+ self.preferences = self.read_json_file("data/preferences.json".format(self.app))
+ else:
+ self.preferences = self.read_json_file("/usr/share/{}/data/preferences.json".format(self.app))
+ self.preferences["data_path"] = "./data"
+ self.set_position(Gtk.WindowPosition.CENTER_ALWAYS)
+ icon="system-software-install"
+ pixbuf24 = Gtk.IconTheme.get_default().load_icon(icon, 24, 0)
+ pixbuf32 = Gtk.IconTheme.get_default().load_icon(icon, 32, 0)
+ pixbuf48 = Gtk.IconTheme.get_default().load_icon(icon, 48, 0)
+ pixbuf64 = Gtk.IconTheme.get_default().load_icon(icon, 64, 0)
+ pixbuf96 = Gtk.IconTheme.get_default().load_icon(icon, 96, 0)
+ self.set_icon_list([pixbuf24, pixbuf32, pixbuf48, pixbuf64, pixbuf96])
+
+ # set data
+ self.app_store = None
+ self.pkg_selected = None
+ self.pkg_installed = None
+ self.pkg_list_install = []
+ self.pkg_list_removal = []
+
+ # setup main box
+ self.set_default_size(800, 650)
+ self.application_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
+ self.add(self.application_box)
+
+ # create title box
+ self.title_box = Gtk.Box()
+ self.title_image = Gtk.Image()
+ self.title_image.set_size_request(100, 100)
+ self.title_image.set_from_file("/usr/share/icons/manjaro/maia/96x96.png")
+ self.title_label = Gtk.Label()
+ self.title_label.set_markup("Manjaro Application Maintenance\n"
+ "Select/Deselect apps you want to install/remove.\n"
+ "Click UPDATE SYSTEM button when ready.")
+ self.title_box.pack_start(self.title_image, expand=False, fill=False, padding=0)
+ self.title_box.pack_start(self.title_label, expand=True, fill=True, padding=0)
+
+ # pack title box to main box
+ self.application_box.pack_start(self.title_box, expand=False, fill=False, padding=0)
+
+ # setup grid
+ self.grid = Gtk.Grid()
+ self.grid.set_column_homogeneous(True)
+ self.grid.set_row_homogeneous(True)
+ self.application_box.add(self.grid)
+
+ # setup list store model
+ self.app_store = self.load_app_data(self.preferences["data_set"])
+
+ # create a tree view with the model store
+ self.tree_view = Gtk.TreeView.new_with_model(self.app_store)
+ self.tree_view.set_activate_on_single_click(True)
+
+ # column model: icon
+ icon = Gtk.CellRendererPixbuf()
+ column = Gtk.TreeViewColumn("", icon, icon_name=ICON)
+ self.tree_view.append_column(column)
+
+ # column model: group name column
+ renderer = Gtk.CellRendererText()
+ column = Gtk.TreeViewColumn("Group", renderer, text=GROUP)
+ self.tree_view.append_column(column)
+
+ # column model: app name column
+ renderer = Gtk.CellRendererText()
+ column = Gtk.TreeViewColumn("Application", renderer, text=APPLICATION)
+ self.tree_view.append_column(column)
+
+ # column model: description column
+ renderer = Gtk.CellRendererText()
+ column = Gtk.TreeViewColumn("Description", renderer, text=DESCRIPTION)
+ self.tree_view.append_column(column)
+
+ # column model: install column
+ toggle = Gtk.CellRendererToggle()
+ toggle.connect("toggled", self.on_app_toggle)
+ column = Gtk.TreeViewColumn("Installed", toggle, active=ACTIVE)
+ self.tree_view.append_column(column)
+
+ # button box
+ self.button_box = Gtk.Box(spacing=10)
+ self.advanced = Gtk.ToggleButton(label="Advanced")
+ self.advanced.connect("clicked", self.on_expert_clicked)
+ self.download = Gtk.Button(label="download")
+ self.download.connect("clicked", self.on_download_clicked)
+ self.reload_button = Gtk.Button(label="reload")
+ self.reload_button.connect("clicked", self.on_reload_clicked)
+ self.update_system_button = Gtk.Button(label="UPDATE SYSTEM")
+ self.update_system_button.connect("clicked", self.on_update_system_clicked)
+ self.close_button = Gtk.Button(label="close")
+ self.close_button.connect("clicked", self.on_close_clicked)
+ self.button_box.pack_start(self.advanced, expand=False, fill=False, padding=10)
+ self.button_box.pack_end(self.update_system_button, expand=False, fill=False, padding=10)
+ self.button_box.pack_end(self.close_button, expand=False, fill=False, padding=10)
+ self.button_box.pack_end(self.reload_button, expand=False, fill=False, padding=10)
+ self.button_box.pack_end(self.download, expand=False, fill=False, padding=10)
+ self.application_box.pack_end(self.button_box, expand=False, fill=False, padding=10)
+
+ # create a scrollable window
+ self.app_window = Gtk.ScrolledWindow()
+ self.app_window.set_vexpand(True)
+ self.app_window.add(self.tree_view)
+ self.grid.attach(self.app_window, 0, 0, 5, len(self.app_store))
+
+ # show start
+ self.show_all()
+
+ def load_app_data(self, data_set):
+ if os.path.isfile("{}/{}.json".format(self.preferences["user_path"], data_set)):
+ app_data = self.read_json_file("{}/{}.json".format(self.preferences["user_path"], data_set))
+ else:
+ app_data = self.read_json_file("{}/{}.json".format(self.preferences["data_path"], data_set))
+
+ store = Gtk.TreeStore(str, str, str, str, bool, str, bool)
+ for group in app_data:
+ index = store.append(None,
+ [group["name"],
+ group["icon"],
+ None, group["description"], None, None, None])
+ for app in group["apps"]:
+ status = self.app_installed(app["pkg"])
+ tree_item = (None,
+ app["icon"],
+ app["name"],
+ app["description"],
+ status,
+ app["pkg"],
+ status)
+ store.append(index, tree_item)
+ return store
+
+ def reload_app_data(self, dataset):
+ self.pkg_selected = None
+ self.pkg_installed = None
+ self.pkg_list_install = []
+ self.pkg_list_removal = []
+ self.app_store.clear()
+ self.app_store = self.load_app_data(dataset)
+ self.tree_view.set_model(self.app_store)
+
+ def on_close_clicked(self, widget):
+ self.hide()
+
+ def on_reload_clicked(self, widget):
+ self.reload_app_data(self.preferences["data_set"])
+
+ def on_expert_clicked(self, widget):
+ if widget.get_active():
+ self.preferences["data_set"] = "advanced"
+ else:
+ self.preferences["data_set"] = "default"
+ self.reload_app_data(self.preferences["data_set"])
+
+ def on_download_clicked(self, widget):
+ if self.net_check():
+ # noinspection PyBroadException
+ try:
+ for download in self.preferences["data_sets"]:
+ url = "{}/{}.json".format(self.preferences["url"], download)
+ file = self.fix_path("{}/{}.json".format(self.preferences["user_path"], download))
+ req = urllib.request.Request(url=url)
+ with urllib.request.urlopen(req, timeout=2) as response:
+ data = json.loads(response.read().decode("utf8"))
+ self.write_json_file(data, file)
+
+ except Exception as e:
+ print(e)
+
+ else:
+ dialog = Gtk.MessageDialog(self, 0,
+ Gtk.MessageType.ERROR,
+ Gtk.ButtonsType.CANCEL,
+ "Download not available")
+ dialog.format_secondary.text("The server 'gitlab.manjaro.org' could not be reached")
+ dialog.run()
+
+ def on_app_toggle(self, cell, path):
+ # a group has no package attached and we don't install groups
+ if self.app_store[path][PACKAGE] is not None:
+ self.app_store[path][ACTIVE] = not self.app_store[path][ACTIVE]
+ self.pkg_selected = self.app_store[path][PACKAGE]
+ self.pkg_installed = self.app_store[path][INSTALLED]
+
+ if self.app_store[path][ACTIVE] is False:
+ if self.pkg_installed is True:
+ # to uninstall
+ self.pkg_list_removal.append(self.pkg_selected)
+ if self.dev:
+ print("for removal : {}".format(self.pkg_selected))
+ if self.pkg_selected in self.pkg_list_install:
+ # cancel install
+ self.pkg_list_install.remove(self.pkg_selected)
+ if self.dev:
+ print("cancel install: {}".format(self.pkg_selected))
+ else:
+ # don't reinstall
+ if self.pkg_installed is False:
+ # only install
+ if self.pkg_selected not in self.pkg_list_install:
+ self.pkg_list_install.append(self.pkg_selected)
+ if self.dev:
+ print("to install : {}".format(self.pkg_selected))
+ if self.pkg_selected in self.pkg_list_removal:
+ # cancel uninstall
+ self.pkg_list_removal.remove(self.pkg_selected)
+ if self.dev:
+ print("pkg list install: {}".format(self.pkg_list_install))
+ print("pkg list removal: {}".format(self.pkg_list_removal))
+
+ def on_update_system_clicked(self, widget):
+ file_install = "/tmp/.install-packages.txt"
+ file_uninstall = "/tmp/.remove-packages.txt"
+
+ os.environ["APP_UTILITY"] = "PACKAGES"
+ shell_fallback = False
+
+ if self.pkg_list_install:
+ if os.path.isfile("/usr/bin/pamac-installer"):
+ subprocess.run(["pamac-installer"] + self.pkg_list_install)
+ else:
+ shell_fallback = True
+ with open(file_install, "w") as outfile:
+ for p in self.pkg_list_install:
+ outfile.write("{} ".format(p))
+
+ if self.pkg_list_removal:
+ shell_fallback = True
+ with open(file_uninstall, "w") as outfile:
+ for p in self.pkg_list_removal:
+ outfile.write("{} ".format(p))
+
+ if shell_fallback:
+ if self.dev:
+ os.system('gksu-polkit ./app-install')
+ else:
+ os.system('gksu-polkit app-install')
+
+ self.reload_app_data(self.preferences["data_set"])
+
+ @staticmethod
+ def app_installed(package):
+ if glob.glob("/var/lib/pacman/local/{}-[0-9]*".format(package)):
+ return True
+ return False
+
+ @staticmethod
+ def fix_path(path):
+ """Make good paths.
+ :param path: path to fix
+ :type path: str
+ :return: fixed path
+ :rtype: str
+ """
+ if "~" in path:
+ path = path.replace("~", os.path.expanduser("~"))
+ return path
+
+ @staticmethod
+ def net_check():
+ """Check for internet connection"""
+ resp = None
+ host = "https://gitlab.manjaro.org"
+ # noinspection PyBroadException
+ try:
+ resp = urllib.request.urlopen(host, timeout=2)
+ except Exception:
+ pass
+ return bool(resp)
+
+ @staticmethod
+ def read_json_file(filename, dictionary=True):
+ """Read json data from file"""
+ result = list()
+ try:
+ if dictionary:
+ with open(filename, "rb") as infile:
+ result = json.loads(
+ infile.read().decode("utf8"),
+ object_pairs_hook=collections.OrderedDict)
+ else:
+ with open(filename, "r") as infile:
+ result = json.load(infile)
+ except OSError:
+ pass
+ return result
+
+ @staticmethod
+ def write_json_file(data, filename, dictionary=False):
+ """Writes data to file as json
+ :param data
+ :param filename:
+ :param dictionary:
+ """
+ try:
+ if dictionary:
+ with open(filename, "wb") as outfile:
+ json.dump(data, outfile)
+ else:
+ with open(filename, "w") as outfile:
+ json.dump(data, outfile, indent=2)
+ return True
+ except OSError:
+ return False
+
+
+class Hello(Gtk.Window):
"""Hello"""
def __init__(self):
+ Gtk.Window.__init__(self, title="Manjaro Hello", border_width=6)
self.app = "manjaro-hello"
self.dev = "--dev" in sys.argv # Dev mode activated ?
+ # used by application utility
+ self.app_store = None
+ self.pkg_selected = None
+ self.pkg_installed = None
+ self.pkg_list_install = []
+ self.pkg_list_removal = []
+
# Load preferences
if self.dev:
self.preferences = read_json("data/preferences.json")
@@ -88,6 +422,8 @@ class Hello():
os.path.isfile(self.preferences["installer_path"]):
self.builder.get_object("installlabel").set_visible(True)
self.builder.get_object("install").set_visible(True)
+ else:
+ self.builder.get_object("applications").set_visible(True)
self.window.show()
@@ -238,10 +574,14 @@ class Hello():
dialog = self.builder.get_object("aboutdialog")
dialog.run()
dialog.hide()
+ elif name == "applications":
+ win = Applications()
+ win.show_all()
def on_btn_clicked(self, btn):
"""Event for clicked button."""
name = btn.get_name()
+ print(name)
self.builder.get_object("home").set_sensitive(not name == "home")
self.builder.get_object("stack").set_visible_child_name(name + "page")
@@ -296,6 +636,7 @@ def write_json(path, content):
except OSError as error:
print(error)
+
def get_lsb_infos():
"""Read informations from the lsb-release file.
:return: args from lsb-release file
@@ -318,5 +659,6 @@ def get_lsb_infos():
if __name__ == "__main__":
- Hello()
+ hello = Hello()
+ hello.connect("destroy", Gtk.main_quit)
Gtk.main()
diff --git a/ui/manjaro-hello.glade b/ui/manjaro-hello.glade
index 3c38124..6788948 100644
--- a/ui/manjaro-hello.glade
+++ b/ui/manjaro-hello.glade
@@ -1,5 +1,5 @@
-
+
-
-
-
False
@@ -586,6 +599,9 @@ Stéphane
Андрей Раугас
gpl-3-0
+
+
+
True
@@ -608,8 +624,5 @@ Stéphane
-
-
-