add applications utility

This commit is contained in:
fhdk 2018-08-22 18:33:43 +02:00
parent df37f9d032
commit c8d42ffd1f
12 changed files with 2287 additions and 122 deletions

11
.idea/manjaro-hello.iml generated Normal file
View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="TestRunnerService">
<option name="PROJECT_TEST_RUNNER" value="Unittests" />
</component>
</module>

7
.idea/misc.xml generated Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.7" project-jdk-type="Python SDK" />
</project>

8
.idea/modules.xml generated Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/manjaro-hello.iml" filepath="$PROJECT_DIR$/.idea/manjaro-hello.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

284
.idea/workspace.xml generated Normal file
View File

@ -0,0 +1,284 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="1878e107-6f19-4cdd-96ac-cf56d1731299" name="Default Changelist" comment="">
<change afterPath="$PROJECT_DIR$/data/advanced.json" afterDir="false" />
<change afterPath="$PROJECT_DIR$/data/default.json" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/app-install" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/app-utility" afterDir="false" />
<change beforePath="$PROJECT_DIR$/data/preferences.json" beforeDir="false" afterPath="$PROJECT_DIR$/data/preferences.json" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/manjaro_hello.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/manjaro_hello.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/ui/manjaro-hello.glade" beforeDir="false" afterPath="$PROJECT_DIR$/ui/manjaro-hello.glade" afterDir="false" />
</list>
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="DockManager">
<window id="1">
<content type="file-editors">
<state>
<leaf>
<file pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/src/app-utility">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="450">
<caret line="63" column="9" lean-forward="true" selection-start-line="62" selection-end-line="64" selection-end-column="40" />
<folding>
<element signature="e#1167#1185#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
</leaf>
</state>
</content>
</window>
</component>
<component name="FUSProjectUsageTrigger">
<session id="1179412992">
<usages-collector id="statistics.lifecycle.project">
<counts>
<entry key="project.open.time.0" value="1" />
<entry key="project.opened" value="1" />
</counts>
</usages-collector>
<usages-collector id="statistics.file.extensions.open">
<counts>
<entry key="app-utility" value="2" />
<entry key="json" value="1" />
<entry key="py" value="1" />
</counts>
</usages-collector>
<usages-collector id="statistics.file.types.open">
<counts>
<entry key="JSON" value="1" />
<entry key="Python" value="3" />
</counts>
</usages-collector>
<usages-collector id="statistics.file.extensions.edit">
<counts>
<entry key="app-utility" value="29" />
<entry key="dummy" value="41" />
<entry key="json" value="45" />
<entry key="py" value="962" />
</counts>
</usages-collector>
<usages-collector id="statistics.file.types.edit">
<counts>
<entry key="JSON" value="45" />
<entry key="PLAIN_TEXT" value="41" />
<entry key="Python" value="991" />
</counts>
</usages-collector>
<usages-collector id="statistics.js.language.service.starts" />
</session>
</component>
<component name="FileEditorManager">
<leaf>
<file pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/src/manjaro_hello.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-213">
<caret line="194" column="88" selection-start-line="194" selection-start-column="88" selection-end-line="194" selection-end-column="88" />
<folding>
<element signature="e#24#42#0" expanded="true" />
<marker date="1534955350678" expanded="true" signature="7207:7233" ph="..." />
<marker date="1534955350678" expanded="true" signature="17177:17181" ph="..." />
</folding>
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/data/preferences.json">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="180">
<caret line="12" column="27" selection-start-line="12" selection-start-column="27" selection-end-line="12" selection-end-column="27" />
</state>
</provider>
</entry>
</file>
</leaf>
</component>
<component name="FindInProjectRecents">
<findStrings>
<find>share_dir</find>
</findStrings>
<replaceStrings>
<replace>data_path</replace>
</replaceStrings>
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="IdeDocumentHistory">
<option name="CHANGED_PATHS">
<list>
<option value="$PROJECT_DIR$/data/preferences.json" />
<option value="$PROJECT_DIR$/src/app-utility" />
<option value="$PROJECT_DIR$/src/manjaro_hello.py" />
</list>
</option>
</component>
<component name="JsBuildToolGruntFileManager" detection-done="true" sorting="DEFINITION_ORDER" />
<component name="JsBuildToolPackageJson" detection-done="true" sorting="DEFINITION_ORDER" />
<component name="JsGulpfileManager">
<detection-done>true</detection-done>
<sorting>DEFINITION_ORDER</sorting>
</component>
<component name="ProjectFrameBounds">
<option name="x" value="146" />
<option name="y" value="68" />
<option name="width" value="1398" />
<option name="height" value="965" />
</component>
<component name="ProjectView">
<navigator proportions="" version="1">
<foldersAlwaysOnTop value="true" />
</navigator>
<panes>
<pane id="ProjectPane">
<subPane>
<expand>
<path>
<item name="manjaro-hello" type="b2602c69:ProjectViewProjectNode" />
<item name="manjaro-hello" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="manjaro-hello" type="b2602c69:ProjectViewProjectNode" />
<item name="manjaro-hello" type="462c0819:PsiDirectoryNode" />
<item name="data" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="manjaro-hello" type="b2602c69:ProjectViewProjectNode" />
<item name="manjaro-hello" type="462c0819:PsiDirectoryNode" />
<item name="src" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="manjaro-hello" type="b2602c69:ProjectViewProjectNode" />
<item name="manjaro-hello" type="462c0819:PsiDirectoryNode" />
<item name="ui" type="462c0819:PsiDirectoryNode" />
</path>
</expand>
<select />
</subPane>
</pane>
<pane id="Scope" />
</panes>
</component>
<component name="PropertiesComponent">
<property name="WebServerToolWindowFactoryState" value="false" />
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
<property name="nodejs_interpreter_path.stuck_in_default_project" value="undefined stuck path" />
<property name="nodejs_npm_path_reset_for_default_project" value="true" />
</component>
<component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/data" />
<recent name="$PROJECT_DIR$/src" />
</key>
</component>
<component name="RunDashboard">
<option name="ruleStates">
<list>
<RuleState>
<option name="name" value="ConfigurationTypeDashboardGroupingRule" />
</RuleState>
<RuleState>
<option name="name" value="StatusDashboardGroupingRule" />
</RuleState>
</list>
</option>
</component>
<component name="SvnConfiguration">
<configuration />
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="1878e107-6f19-4cdd-96ac-cf56d1731299" name="Default Changelist" comment="" />
<created>1534949710604</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1534949710604</updated>
</task>
<servers />
</component>
<component name="ToolWindowManager">
<frame x="146" y="68" width="1398" height="965" extended-state="0" />
<layout>
<window_info id="Favorites" side_tool="true" />
<window_info content_ui="combo" id="Project" order="0" visible="true" weight="0.25" />
<window_info id="Structure" order="1" side_tool="true" weight="0.25" />
<window_info anchor="bottom" id="Docker" show_stripe_button="false" />
<window_info anchor="bottom" id="Database Changes" show_stripe_button="false" />
<window_info anchor="bottom" id="Version Control" />
<window_info anchor="bottom" id="Python Console" />
<window_info active="true" anchor="bottom" id="Terminal" visible="true" weight="0.32893157" />
<window_info anchor="bottom" id="Event Log" side_tool="true" />
<window_info anchor="bottom" id="Message" order="0" />
<window_info anchor="bottom" id="Find" order="1" />
<window_info anchor="bottom" id="Run" order="2" />
<window_info anchor="bottom" id="Debug" order="3" weight="0.4" />
<window_info anchor="bottom" id="Cvs" order="4" weight="0.25" />
<window_info anchor="bottom" id="Inspection" order="5" weight="0.4" />
<window_info anchor="bottom" id="TODO" order="6" />
<window_info anchor="right" id="SciView" />
<window_info anchor="right" id="Database" />
<window_info anchor="right" id="Commander" internal_type="SLIDING" order="0" type="SLIDING" weight="0.4" />
<window_info anchor="right" id="Ant Build" order="1" weight="0.25" />
<window_info anchor="right" content_ui="combo" id="Hierarchy" order="2" weight="0.25" />
</layout>
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="1" />
</component>
<component name="VcsContentAnnotationSettings">
<option name="myLimit" value="2678400000" />
</component>
<component name="XDebuggerManager">
<breakpoint-manager>
<breakpoints>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/src/app-utility</url>
<line>59</line>
<option name="timeStamp" value="1" />
</line-breakpoint>
</breakpoints>
</breakpoint-manager>
</component>
<component name="editorHistoryManager">
<entry file="file://$PROJECT_DIR$/src/app-utility">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="450">
<caret line="63" column="9" lean-forward="true" selection-start-line="62" selection-end-line="64" selection-end-column="40" />
<folding>
<element signature="e#1167#1185#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/data/preferences.json">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="180">
<caret line="12" column="27" selection-start-line="12" selection-start-column="27" selection-end-line="12" selection-end-column="27" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/manjaro_hello.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-213">
<caret line="194" column="88" selection-start-line="194" selection-start-column="88" selection-end-line="194" selection-end-column="88" />
<folding>
<element signature="e#24#42#0" expanded="true" />
<marker date="1534955350678" expanded="true" signature="7207:7233" ph="..." />
<marker date="1534955350678" expanded="true" signature="17177:17181" ph="..." />
</folding>
</state>
</provider>
</entry>
</component>
</project>

646
data/advanced.json Normal file
View File

@ -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": []
}
]
}
]

352
data/default.json Normal file
View File

@ -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": []
}
]
}
]

View File

@ -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"
}
}

118
src/app-install Executable file
View File

@ -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

371
src/app-utility Executable file
View File

@ -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("<big>Manjaro Application Maintenance</big>\n"
"Select/Deselect apps you want to install/remove.\n"
"Click <b>UPDATE SYSTEM</b> 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()

View File

@ -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("<big>Manjaro Application Maintenance</big>\n"
"Select/Deselect apps you want to install/remove.\n"
"Click <b>UPDATE SYSTEM</b> 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()

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.1 -->
<!-- Generated with glade 3.22.1 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkImage" id="homeicon">
@ -19,6 +19,99 @@
<property name="default_height">500</property>
<property name="has_resize_grip">True</property>
<signal name="delete-event" handler="on_delete_window" swapped="no"/>
<child type="titlebar">
<object class="GtkHeaderBar" id="headerbar">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="title">Manjaro Hello</property>
<property name="show_close_button">True</property>
<child>
<object class="GtkButton" id="home">
<property name="name">home</property>
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">False</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Home</property>
<property name="image">homeicon</property>
<signal name="clicked" handler="on_btn_clicked" swapped="no"/>
</object>
</child>
<child>
<object class="GtkComboBoxText" id="languages">
<property name="visible">True</property>
<property name="can_focus">False</property>
<items>
<item id="sq">Albanian</item>
<item id="ar">Arabic</item>
<item id="ast-ES">Asturian (Spain)</item>
<item id="be">Belarusian</item>
<item id="bg">Bulgarian</item>
<item id="ca">Catalan</item>
<item id="zh-CN">Chinese (China)</item>
<item id="zh-TW">Chinese (Taiwan)</item>
<item id="hr">Croatian</item>
<item id="cs">Czech</item>
<item id="da">Danish</item>
<item id="nl">Dutch</item>
<item id="en">English</item>
<item id="fi">Finnish</item>
<item id="fr">French</item>
<item id="de">German</item>
<item id="ka">Georgian</item>
<item id="el-GR">Greek (Greece)</item>
<item id="he">Hebrew</item>
<item id="hi-IN">Hindi (India)</item>
<item id="hu">Hungarian</item>
<item id="is">Icelandic</item>
<item id="id-ID">Indonesian (Indonesia)</item>
<item id="it">Italian</item>
<item id="ja">Japanese</item>
<item id="ko-KR">Korean (Korea)</item>
<item id="lt">Lithuanian</item>
<item id="nb">Norwegian Bokmål</item>
<item id="fa-IR">Persian (Iran)</item>
<item id="pl">Polish</item>
<item id="pt-BR">Portuguese (Brazil)</item>
<item id="pt-PT">Portuguese (Portugal)</item>
<item id="ro-RO">Romanian (Romania)</item>
<item id="ru">Russian</item>
<item id="sk">Slovak</item>
<item id="sl-SI">Slovenian (Slovenia)</item>
<item id="sl">Slovenian</item>
<item id="es">Spanish</item>
<item id="sr-RS">Serbian (Serbia)</item>
<item id="sr">Serbian</item>
<item id="sv">Swedish</item>
<item id="th">Thai</item>
<item id="tr-TR">Turkish (Turkey)</item>
<item id="tr">Turkish</item>
<item id="uk">Ukrainian</item>
<item id="vi-VN">Vietnamese (Viet Nam)</item>
</items>
<signal name="changed" handler="on_languages_changed" swapped="no"/>
</object>
<packing>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkButton" id="about">
<property name="name">about</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">About</property>
<property name="image">iconabout</property>
<signal name="clicked" handler="on_action_clicked" swapped="no"/>
</object>
<packing>
<property name="pack_type">end</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<child>
<object class="GtkStack" id="stack">
<property name="visible">True</property>
@ -427,6 +520,19 @@ We, the Manjaro Developers, hope that you will enjoy using Manjaro as much as we
<property name="top_attach">8</property>
</packing>
</child>
<child>
<object class="GtkButton" id="applications">
<property name="label" translatable="yes">Applications</property>
<property name="name">applications</property>
<property name="can_focus">False</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_action_clicked" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">8</property>
</packing>
</child>
<child>
<placeholder/>
</child>
@ -446,99 +552,6 @@ We, the Manjaro Developers, hope that you will enjoy using Manjaro as much as we
</child>
</object>
</child>
<child type="titlebar">
<object class="GtkHeaderBar" id="headerbar">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="title">Manjaro Hello</property>
<property name="show_close_button">True</property>
<child>
<object class="GtkButton" id="home">
<property name="name">home</property>
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">False</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Home</property>
<property name="image">homeicon</property>
<signal name="clicked" handler="on_btn_clicked" swapped="no"/>
</object>
</child>
<child>
<object class="GtkComboBoxText" id="languages">
<property name="visible">True</property>
<property name="can_focus">False</property>
<items>
<item id="sq">Albanian</item>
<item id="ar">Arabic</item>
<item id="ast-ES">Asturian (Spain)</item>
<item id="be">Belarusian</item>
<item id="bg">Bulgarian</item>
<item id="ca">Catalan</item>
<item id="zh-CN">Chinese (China)</item>
<item id="zh-TW">Chinese (Taiwan)</item>
<item id="hr">Croatian</item>
<item id="cs">Czech</item>
<item id="da">Danish</item>
<item id="nl">Dutch</item>
<item id="en">English</item>
<item id="fi">Finnish</item>
<item id="fr">French</item>
<item id="de">German</item>
<item id="ka">Georgian</item>
<item id="el-GR">Greek (Greece)</item>
<item id="he">Hebrew</item>
<item id="hi-IN">Hindi (India)</item>
<item id="hu">Hungarian</item>
<item id="is">Icelandic</item>
<item id="id-ID">Indonesian (Indonesia)</item>
<item id="it">Italian</item>
<item id="ja">Japanese</item>
<item id="ko-KR">Korean (Korea)</item>
<item id="lt">Lithuanian</item>
<item id="nb">Norwegian Bokmål</item>
<item id="fa-IR">Persian (Iran)</item>
<item id="pl">Polish</item>
<item id="pt-BR">Portuguese (Brazil)</item>
<item id="pt-PT">Portuguese (Portugal)</item>
<item id="ro-RO">Romanian (Romania)</item>
<item id="ru">Russian</item>
<item id="sk">Slovak</item>
<item id="sl-SI">Slovenian (Slovenia)</item>
<item id="sl">Slovenian</item>
<item id="es">Spanish</item>
<item id="sr-RS">Serbian (Serbia)</item>
<item id="sr">Serbian</item>
<item id="sv">Swedish</item>
<item id="th">Thai</item>
<item id="tr-TR">Turkish (Turkey)</item>
<item id="tr">Turkish</item>
<item id="uk">Ukrainian</item>
<item id="vi-VN">Vietnamese (Viet Nam)</item>
</items>
<signal name="changed" handler="on_languages_changed" swapped="no"/>
</object>
<packing>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkButton" id="about">
<property name="name">about</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">About</property>
<property name="image">iconabout</property>
<signal name="clicked" handler="on_action_clicked" swapped="no"/>
</object>
<packing>
<property name="pack_type">end</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
<object class="GtkAboutDialog" id="aboutdialog">
<property name="can_focus">False</property>
@ -586,6 +599,9 @@ Stéphane
Андрей Раугас</property>
<property name="logo_icon_name"/>
<property name="license_type">gpl-3-0</property>
<child>
<placeholder/>
</child>
<child internal-child="vbox">
<object class="GtkBox">
<property name="can_focus">True</property>
@ -608,8 +624,5 @@ Stéphane
</child>
</object>
</child>
<child>
<placeholder/>
</child>
</object>
</interface>