Skip to content
Browse files

Use gtk dialog to add/edit panel launchers, so we can have the

full power of a file picker, copy/paste, and icon previews
  • Loading branch information...
1 parent 8c0a193 commit daa013de8ca8e4d22c04b569319dc4a47fa58392 @mtwebster mtwebster committed
View
228 files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/add-panel-launcher.glade
@@ -0,0 +1,228 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <object class="GtkDialog" id="add-panel-launcher-dialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">5</property>
+ <property name="title" translatable="yes">Add panel launcher...</property>
+ <property name="resizable">False</property>
+ <property name="window_position">center</property>
+ <property name="icon_name">list-add</property>
+ <property name="type_hint">dialog</property>
+ <signal name="close" handler="onDeleteWindow" swapped="no"/>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="add_button">
+ <property name="label" translatable="yes">Add</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ <signal name="clicked" handler="onAdd" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancel_button">
+ <property name="label" translatable="yes">Cancel</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ <signal name="clicked" handler="onDeleteWindow" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLayout" id="layout1">
+ <property name="width_request">549</property>
+ <property name="height_request">110</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkGrid" id="grid1">
+ <property name="width_request">430</property>
+ <property name="height_request">85</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkLabel" id="name_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Name </property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="application_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Application </property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="icon_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Icon</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="app_name">
+ <property name="width_request">300</property>
+ <property name="height_request">35</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <signal name="changed" handler="onNameChanged" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkEntry" id="app_path">
+ <property name="width_request">300</property>
+ <property name="height_request">35</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <signal name="changed" handler="onAppChanged" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="icon_path">
+ <property name="width_request">300</property>
+ <property name="height_request">35</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ <signal name="changed" handler="onIconChanged" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFileChooserButton" id="app_picker">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="orientation">vertical</property>
+ <property name="preview_widget_active">False</property>
+ <property name="use_preview_label">False</property>
+ <signal name="file-set" handler="onAppPicked" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFileChooserButton" id="icon_picker">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="orientation">vertical</property>
+ <property name="preview_widget_active">False</property>
+ <property name="use_preview_label">False</property>
+ <signal name="file-set" handler="onIconPicked" swapped="no"/>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImage" id="icon">
+ <property name="width_request">100</property>
+ <property name="height_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="stock">gtk-execute</property>
+ <property name="icon-size">6</property>
+ </object>
+ <packing>
+ <property name="x">441</property>
+ <property name="y">11</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="0">add_button</action-widget>
+ <action-widget response="0">cancel_button</action-widget>
+ </action-widgets>
+ </object>
+</interface>
View
154 files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/add-panel-launcher.py
@@ -0,0 +1,154 @@
+#! /usr/bin/python -OOt
+
+from gi.repository import Gtk
+from gi.repository import Gdk
+from gi.repository import Gio
+from os.path import expanduser
+from time import sleep as wait
+import os
+import os.path
+import inspect
+import sys
+import gettext
+
+gettext.install("cinnamon", "/usr/share/cinnamon/locale")
+Settings = Gio.Settings.new("org.cinnamon")
+
+_ = gettext.gettext
+
+class Namespace: pass
+
+iface = Namespace()
+
+oldDesktopName = ""
+newDesktopName = ""
+appName = ""
+appPath = ""
+iconPath = ""
+
+editMode = len(sys.argv) > 1
+if editMode:
+ oldDesktopName = sys.argv[1]
+ appName = sys.argv[2]
+ appPath = sys.argv[3]
+ iconPath = sys.argv[4]
+
+def updatePreviewIcon(name):
+ global iface
+ if os.path.exists(name):
+ iface.preview_icon.set_from_file(name)
+ else:
+ iface.preview_icon.set_from_icon_name(name, 6)
+
+def editOrAddLaunchers():
+ makeLauncher()
+ global Settings, desktopName
+ desktopFiles = Settings.get_strv('panel-launchers')
+ if not editMode:
+ desktopFiles.append(newDesktopName)
+ else:
+ i = desktopFiles.index(oldDesktopName)
+ if i >= 0:
+ del desktopFiles[i]
+ desktopFiles.insert(i, newDesktopName)
+ Settings.set_strv('panel-launchers', desktopFiles)
+ Gtk.main_quit(None)
+
+def makeLauncher():
+ global appName, appPath, iconPath, custom_launchers_path, newDesktopName
+ description = _("Custom Launcher")
+ i = 1
+ dir = Gio.file_new_for_path(custom_launchers_path)
+ if not dir.query_exists(None):
+ dir.make_directory_with_parents(None)
+
+ file = Gio.file_parse_name(custom_launchers_path + '/cinnamon-custom-launcher-' + str(i) + '.desktop')
+ while file.query_exists(None):
+ i = i + 1
+ file = Gio.file_parse_name(custom_launchers_path + '/cinnamon-custom-launcher-' + str(i) + '.desktop')
+ file = open(custom_launchers_path+ '/cinnamon-custom-launcher-' + str(i) + '.desktop', "w")
+
+ desktopEntry = "[Desktop Entry]\nName=" + appName + "\nExec=" + appPath + "\nType=Application\n"
+ desktopEntry = desktopEntry + "Description=" + description + "\n"
+ if iconPath == "":
+ iconPath = "application-x-executable"
+ desktopEntry += "Icon=" + iconPath + "\n"
+ print desktopEntry
+ file.write(desktopEntry)
+ file.close()
+ newDesktopName = 'cinnamon-custom-launcher-' + str(i) + '.desktop'
+
+class Handler:
+ def onDeleteWindow(self, *args):
+ Gtk.main_quit(*args)
+
+ def onAdd(self, button):
+ global appPath, appName
+ if appPath == "" or appName == "":
+ return
+ else:
+ editOrAddLaunchers()
+
+ def onIconPicked(self, *args):
+ global iconPath, iface
+ iconPath = iface.icon_picker.get_uri()[7:]
+ iface.icon_path.set_text(iconPath)
+ updatePreviewIcon(iconPath)
+
+ def onAppPicked(self, *args):
+ global appPath, iface
+ appPath = iface.app_picker.get_uri()[7:]
+ iface.file_path.set_text(appPath)
+
+ def onNameChanged(self, *args):
+ global appName, iface
+ appName = iface.app_name.get_text().strip()
+
+ def onAppChanged(self, *args):
+ global appPath, iface
+ appPath = iface.file_path.get_text().strip()
+
+ def onIconChanged(self, *args):
+ global iconPath, iface
+ iconPath = iface.icon_path.get_text().strip()
+ updatePreviewIcon(iconPath)
+
+builder = Gtk.Builder()
+
+userhome = expanduser("~")
+custom_launchers_path = userhome + "/.cinnamon/panel-launchers"
+
+applet_dir = os.path.dirname(inspect.getfile(inspect.currentframe()))
+builder.add_from_file(applet_dir + "/add-panel-launcher.glade")
+
+window = builder.get_object("add-panel-launcher-dialog")
+builder.connect_signals(Handler())
+
+iface.add_button = builder.get_object("add_button")
+iface.cancel_button = builder.get_object("cancel_button")
+iface.preview_icon = builder.get_object("icon")
+iface.app_name = builder.get_object("app_name")
+iface.file_path = builder.get_object("app_path")
+iface.icon_path = builder.get_object("icon_path")
+iface.app_picker = builder.get_object("app_picker")
+iface.icon_picker = builder.get_object("icon_picker")
+
+
+# set static translations (labels, etc..)
+builder.get_object("name_label").set_markup(_("Name"))
+builder.get_object("application_label").set_markup(_("Application"))
+builder.get_object("icon_label").set_markup(_("Icon"))
+builder.get_object("cancel_button").set_label(_("Cancel"))
+builder.get_object("add-panel-launcher-dialog").set_title(_("Add panel launcher..."))
+
+if editMode:
+ iface.app_name.set_text(appName)
+ iface.file_path.set_text(appPath)
+ iface.icon_path.set_text(iconPath)
+ iface.add_button.set_label(_("Update"))
+ updatePreviewIcon(iconPath)
+else:
+ iface.add_button.set_label(_("Add"))
+
+window.show_all()
+Gtk.main()
View
202 files/usr/share/cinnamon/applets/panel-launchers@cinnamon.org/applet.js
@@ -12,6 +12,7 @@ const GLib = imports.gi.GLib;
const Tooltips = imports.ui.tooltips;
const DND = imports.ui.dnd;
const Tweener = imports.ui.tweener;
+const Util = imports.misc.util;
let pressLauncher = null;
@@ -19,6 +20,7 @@ function PanelAppLauncherMenu(launcher, orientation) {
this._init(launcher, orientation);
}
+const APPLET_DIR = imports.ui.appletManager._find_applet('panel-launchers@cinnamon.org');
const CUSTOM_LAUNCHERS_PATH = GLib.get_home_dir() + '/.cinnamon/panel-launchers';
PanelAppLauncherMenu.prototype = {
@@ -244,171 +246,6 @@ PanelAppLauncher.prototype = {
}
}
-function AddLauncherDialog() {
- this._init();
-}
-
-AddLauncherDialog.prototype = {
- __proto__: ModalDialog.ModalDialog.prototype,
-
- _init: function() {
- ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'panel-launcher-add-dialog' });
-
- let box;
- let label;
-
- let box = new St.BoxLayout({ styleClass: 'panel-launcher-add-dialog-content-box' });
- let leftBox = new St.BoxLayout({vertical: true, styleClass: 'panel-launcher-add-dialog-content-box-left'});
- let rightBox = new St.BoxLayout({vertical: true, styleClass: 'panel-launcher-add-dialog-content-box-right'});
-
- label = new St.Label();
- label.set_text(_("Name"));
- leftBox.add(label, { x_align: St.Align.START, x_fill: true, x_expand: true });
- this._nameEntry = new St.Entry({ styleClass: 'panel-launcher-add-dialog-entry', can_focus: true });
- rightBox.add(this._nameEntry, { x_align: St.Align.END, x_fill: false, x_expand: false });
-
- label = new St.Label();
- label.set_text(_("Command"));
- leftBox.add(label, { x_align: St.Align.START, x_fill: true, x_expand: true });
- this._commandEntry = new St.Entry({ styleClass: 'panel-launcher-add-dialog-entry', can_focus: true });
- rightBox.add(this._commandEntry, { x_align: St.Align.END, x_fill: false, x_expand: false });
-
- label = new St.Label();
- label.set_text(_("Icon"));
- leftBox.add(label, { x_align: St.Align.START, x_fill: true, x_expand: true });
- this._iconEntry = new St.Entry({ styleClass: 'panel-launcher-add-dialog-entry', can_focus: true });
- rightBox.add(this._iconEntry, { x_align: St.Align.END, x_fill: false, x_expand: false });
-
- box.add(leftBox);
- box.add(rightBox);
- this.contentLayout.add(box, { y_align: St.Align.START });
-
- this._errorBox = new St.BoxLayout({ style_class: 'run-dialog-error-box' });
- this.contentLayout.add(this._errorBox, { expand: true });
-
- let errorIcon = new St.Icon({ icon_name: 'dialog-error', icon_size: 24, style_class: 'run-dialog-error-icon' });
-
- this._errorBox.add(errorIcon, { y_align: St.Align.MIDDLE });
-
- this._commandError = false;
-
- this._errorMessage = new St.Label({ style_class: 'run-dialog-error-label' });
- this._errorMessage.clutter_text.line_wrap = true;
-
- this._errorBox.add(this._errorMessage, { expand: true,
- y_align: St.Align.MIDDLE,
- y_fill: false });
-
- this._errorBox.hide();
-
- this.connect('opened', Lang.bind(this, this._onOpened));
-
- this._currentLauncher = null;
- },
-
- _onOpened: function() {
- this._nameEntry.grab_key_focus();
- },
-
- _validateAdd: function() {
- if (this._nameEntry.clutter_text.get_text()==""){
- this._errorMessage.clutter_text.set_text(_("Name cannot be empty!"));
- this._errorBox.show();
- return false;
- }
- if (this._commandEntry.clutter_text.get_text()==""){
- this._errorMessage.clutter_text.set_text(_("Command cannot be empty!"));
- this._errorBox.show();
- return false;
- }
-
-
- let appid = this._saveNewLauncher(this._nameEntry.clutter_text.get_text(), this._commandEntry.clutter_text.get_text(), _("Custom Launcher"), this._iconEntry.clutter_text.get_text());
-
- this.close();
-
- if (this._currentLauncher) this.emit("launcher-updated", this._currentLauncher, appid);
- else this.emit("launcher-created", appid);
-
- return true;
- },
-
- _saveNewLauncher: function(name, command, description, icon){
- let file;
- let i;
- if (this._currentLauncher && this._currentLauncher.is_custom()){
- file = Gio.file_parse_name(CUSTOM_LAUNCHERS_PATH+'/'+this._currentLauncher.get_id());
- file.delete(null);
- }else{
- let dir = Gio.file_new_for_path(CUSTOM_LAUNCHERS_PATH);
- if (!dir.query_exists(null)) dir.make_directory_with_parents(null);
- i = 1;
- file = Gio.file_parse_name(CUSTOM_LAUNCHERS_PATH+'/cinnamon-custom-launcher-'+i+'.desktop');
- while (file.query_exists(null)){
- i++;
- file = Gio.file_parse_name(CUSTOM_LAUNCHERS_PATH+'/cinnamon-custom-launcher-'+i+'.desktop');
- }
- }
-
- let desktopEntry = "[Desktop Entry]\nName="+name+"\nExec="+command+"\nType=Application\n";
- if (description) desktopEntry += "Description="+description+"\n";
- if (!icon && this._currentLauncher) icon = this._currentLauncher.get_icon();
- if (!icon) icon = "application-x-executable";
- desktopEntry += "Icon="+icon+"\n";
-
- let fp = file.create(0, null);
- fp.write(desktopEntry, null);
- fp.close(null);
-
- if (this._currentLauncher && this._currentLauncher.is_custom()) return this._currentLauncher.get_id();
- else return 'cinnamon-custom-launcher-'+i+'.desktop';
- },
-
- open: function(timestamp, launcher) {
- this._currentLauncher = launcher;
-
- if (launcher){
- this._commandEntry.clutter_text.set_text(launcher.get_command());
- this._nameEntry.clutter_text.set_text(launcher.get_appname());
- if (launcher.get_icon()) this._iconEntry.clutter_text.set_text(launcher.get_icon());
- this._errorBox.hide();
- this.setButtons([
- {
- label: _("Save"),
- action: Lang.bind(this, this._validateAdd)
- },
- {
- label: _("Cancel"),
- key: Clutter.KEY_Escape,
- action: Lang.bind(this, function(){
- this.close();
- })
- }
- ]);
- }else{
- this._commandEntry.clutter_text.set_text('');
- this._nameEntry.clutter_text.set_text('');
- this._errorBox.hide();
- this.setButtons([
- {
- label: _("Add"),
- action: Lang.bind(this, this._validateAdd)
- },
- {
- label: _("Cancel"),
- key: Clutter.KEY_Escape,
- action: Lang.bind(this, function(){
- this.close();
- })
- }
- ]);
- }
-
- ModalDialog.ModalDialog.prototype.open.call(this, timestamp);
- },
-}
-Signals.addSignalMethods(AddLauncherDialog.prototype);
-
function MyApplet(orientation) {
this._init(orientation);
}
@@ -430,11 +267,7 @@ MyApplet.prototype = {
this._settings = new Gio.Settings({ schema: 'org.cinnamon' });
this._settings.connect('changed::panel-launchers', Lang.bind(this, this._onSettingsChanged));
-
- this._addLauncherDialog = new AddLauncherDialog();
- this._addLauncherDialog.connect("launcher-created", Lang.bind(this, this._onLauncherCreated));
- this._addLauncherDialog.connect("launcher-updated", Lang.bind(this, this._onLauncherUpdated));
-
+
this._launchers = new Array();
this.reload();
@@ -460,24 +293,6 @@ MyApplet.prototype = {
this.reload();
},
- _onLauncherUpdated: function(obj, launcher, appid){
- let desktopFiles = this._settings.get_strv('panel-launchers');
- let i = this._launchers.indexOf(launcher);
- if (i>=0){
- desktopFiles.splice(i, 1);
- desktopFiles.splice(i, 0, appid);
- this._settings.set_strv('panel-launchers', desktopFiles);
- }
- },
-
- _onLauncherCreated: function(obj, appid){
- if (appid){
- let desktopFiles = this._settings.get_strv('panel-launchers');
- desktopFiles.push(appid);
- this._settings.set_strv('panel-launchers', desktopFiles);
- }
- },
-
loadApps: function() {
let desktopFiles = this._settings.get_strv('panel-launchers');
let appSys = Cinnamon.AppSystem.get_default();
@@ -532,7 +347,16 @@ MyApplet.prototype = {
},
showAddLauncherDialog: function(timestamp, launcher){
- this._addLauncherDialog.open(timestamp, launcher);
+ if (launcher) {
+ let cl = APPLET_DIR.get_child('add-panel-launcher.py').get_path() + ' ';
+ cl += '"' + launcher.get_id() + '" ';
+ cl += '"' + launcher.get_appname() + '" ';
+ cl += '"' + launcher.get_command() + '" ';
+ cl += '"' + launcher.get_icon() + '"';
+ Util.spawnCommandLine(cl);
+ } else {
+ Util.spawnCommandLine(APPLET_DIR.get_child('add-panel-launcher.py').get_path());
+ }
},
_clearDragPlaceholder: function() {
View
2 makepot
@@ -1,3 +1,3 @@
#!/bin/bash
-xgettext --language=C --keyword=_ --output=cinnamon.pot src/*.c src/*/*.c js/*/*.js files/usr/share/cinnamon/applets/*/applet.js files/usr/lib/cinnamon-settings/*.py
+xgettext --language=C --keyword=_ --output=cinnamon.pot src/*.c src/*/*.c js/*/*.js files/usr/share/cinnamon/applets/*/applet.js files/usr/lib/cinnamon-settings/*.py files/usr/share/cinnamon/applets/*/*.py

0 comments on commit daa013d

Please sign in to comment.
Something went wrong with that request. Please try again.