gtg team mailing list archive
-
gtg team
-
Mailing list archive
-
Message #03812
[Merge] lp:~parinporecha/gtg/final_global_shortcut into lp:gtg
Parin Porecha has proposed merging lp:~parinporecha/gtg/final_global_shortcut into lp:gtg.
Requested reviews:
Gtg developers (gtg)
For more details, see:
https://code.launchpad.net/~parinporecha/gtg/final_global_shortcut/+merge/151566
This is a fix for the Bug #967607 - System level global shortcut key for quick adding task.
Plz suggest any improvements or problems with the patch.
--
https://code.launchpad.net/~parinporecha/gtg/final_global_shortcut/+merge/151566
Your team Gtg developers is requested to review the proposed merge of lp:~parinporecha/gtg/final_global_shortcut into lp:gtg.
=== modified file 'GTG/gtk/preferences.glade'
--- GTG/gtk/preferences.glade 2012-08-08 14:56:18 +0000
+++ GTG/gtk/preferences.glade 2013-03-04 17:25:24 +0000
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk+" version="2.16"/>
+ <!-- interface-naming-policy toplevel-contextual -->
<object class="GtkDialog" id="PreferencesDialog">
<property name="can_focus">False</property>
<property name="border_width">3</property>
@@ -8,7 +9,7 @@
<property name="type_hint">dialog</property>
<signal name="delete-event" handler="on_PreferencesDialog_delete_event" swapped="no"/>
<child internal-child="vbox">
- <object class="GtkBox" id="prefs-vbox1">
+ <object class="GtkVBox" id="prefs-vbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
@@ -53,6 +54,40 @@
<property name="position">0</property>
</packing>
</child>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="height_request">21</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">2</property>
+ <child>
+ <object class="GtkCheckButton" id="shortcut_button">
+ <property name="label" translatable="yes">New Task Shortcut :</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="relief">none</property>
+ <property name="draw_indicator">True</property>
+ <signal name="toggled" handler="on_shortcut_button_toggled" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">3</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
</object>
</child>
</object>
@@ -62,7 +97,7 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="ypad">5</property>
- <property name="label" translatable="yes"><b>Startup</b></property>
+ <property name="label" translatable="yes"><b>System</b></property>
<property name="use_markup">True</property>
</object>
</child>
@@ -255,7 +290,7 @@
</packing>
</child>
<child internal-child="action_area">
- <object class="GtkButtonBox" id="prefs-action_area">
+ <object class="GtkHButtonBox" id="prefs-action_area">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="layout_style">end</property>
=== modified file 'GTG/gtk/preferences.py'
--- GTG/gtk/preferences.py 2013-02-25 08:29:31 +0000
+++ GTG/gtk/preferences.py 2013-03-04 17:25:24 +0000
@@ -28,6 +28,8 @@
from GTG import _
from GTG import info
from GTG.gtk import ViewConfig
+from GTG.tools.shortcut import *
+
AUTOSTART_DIRECTORY = os.path.join(xdg_config_home, "autostart")
AUTOSTART_FILE = "gtg.desktop"
@@ -42,7 +44,7 @@
desktop_file_path = None
this_directory = os.path.dirname(os.path.abspath(__file__))
for path in ["../..", "../../../applications",
- "../../../../../share/applications"]:
+ "../../../../../share/applications"]:
fullpath = os.path.join(this_directory, path, AUTOSTART_FILE)
fullpath = os.path.normpath(fullpath)
if os.path.isfile(fullpath):
@@ -55,11 +57,11 @@
# If the path is a symlink and is broken, remove it
if os.path.islink(AUTOSTART_PATH) and \
- not os.path.exists(os.path.realpath(AUTOSTART_PATH)):
+ not os.path.exists(os.path.realpath(AUTOSTART_PATH)):
os.unlink(AUTOSTART_PATH)
if os.path.isdir(AUTOSTART_DIRECTORY) and \
- not os.path.exists(AUTOSTART_PATH):
+ not os.path.exists(AUTOSTART_PATH):
if hasattr(os, "symlink"):
os.symlink(desktop_file_path, AUTOSTART_PATH)
else:
@@ -86,6 +88,11 @@
self.pref_autostart = builder.get_object("pref_autostart")
self.pref_show_preview = builder.get_object("pref_show_preview")
self.bg_color_enable = builder.get_object("bg_color_enable")
+ self.hbox1 = builder.get_object("hbox1")
+ self.shortcut_button = builder.get_object("shortcut_button")
+
+ self.shortcut = ShortcutWidget(self.dialog, self.hbox1,
+ self.shortcut_button)
self.fontbutton = builder.get_object("fontbutton")
editor_font = self.config.get("font_name")
@@ -95,27 +102,31 @@
self.fontbutton.set_font_name(editor_font)
builder.connect_signals({
- 'on_pref_autostart_toggled':
- self.on_autostart_toggled,
- 'on_pref_show_preview_toggled':
- self.toggle_preview,
- 'on_bg_color_toggled':
- self.on_bg_color_toggled,
- 'on_prefs_help':
- self.on_help,
- 'on_prefs_close':
- self.on_close,
- 'on_PreferencesDialog_delete_event':
- self.on_close,
- 'on_fontbutton_font_set':
- self.on_font_change,
- })
+ 'on_pref_autostart_toggled':
+ self.on_autostart_toggled,
+ 'on_pref_show_preview_toggled':
+ self.toggle_preview,
+ 'on_bg_color_toggled':
+ self.on_bg_color_toggled,
+ 'on_prefs_help':
+ self.on_help,
+ 'on_prefs_close':
+ self.on_close,
+ 'on_PreferencesDialog_delete_event':
+ self.on_close,
+ 'on_fontbutton_font_set':
+ self.on_font_change,
+ 'on_shortcut_button_toggled':
+ self.shortcut.on_shortcut_toggled,
+ })
- def _refresh_preferences_store(self):
+ def _refresh_preferences_store(self):
""" Sets the correct value in the preferences checkboxes """
has_autostart = os.path.isfile(AUTOSTART_PATH)
self.pref_autostart.set_active(has_autostart)
+ self.shortcut.refresh_accel()
+
show_preview = self.config.get("contents_preview_enable")
self.pref_show_preview.set_active(show_preview)
@@ -132,13 +143,13 @@
self._refresh_preferences_store()
self.dialog.show_all()
- def on_close(self, widget, data=None):
+ def on_close(self, widget, data=None): # pylint: disable-msg=W0613
""" Close the preferences dialog."""
self.dialog.hide()
return True
@classmethod
- def on_help(cls, widget):
+ def on_help(cls, widget): # pylint: disable-msg=W0613
""" In future, this will open help for preferences """
return True
@@ -167,3 +178,91 @@
def on_font_change(self, widget):
""" Set a new font for editor """
self.config.set("font_name", self.fontbutton.get_font_name())
+
+
+class ShortcutWidget:
+ """ Show Shortcut Accelerator Widget """
+
+ def __init__(self, dialog, hbox1, button1):
+ self.dialog = dialog
+ self.hbox1 = hbox1
+ self.button = button1
+ self.new_task_default_binding = "<Primary>F12"
+
+ self.liststore = gtk.ListStore(str, str)
+ self.liststore.append(["", ""])
+ treeview = gtk.TreeView(self.liststore)
+ column_accel = gtk.TreeViewColumn()
+ treeview.append_column(column_accel)
+ treeview.set_headers_visible(False)
+
+ cell = gtk.CellRendererAccel()
+ cell.set_alignment(0.0, 1.0)
+ cell.set_fixed_size(-1, 18)
+ cell.set_property("accel-mode", gtk.CELL_RENDERER_ACCEL_MODE_OTHER)
+ cell.set_property("editable", True)
+ cell.connect("accel-edited", self._cellAccelEdit, self.liststore)
+ cell.connect("accel-cleared", self._accel_cleared, self.liststore)
+ column_accel.pack_start(cell, True)
+ column_accel.add_attribute(cell, "text", 1)
+ self.hbox1.add(treeview)
+
+ def refresh_accel(self):
+ """ Refreshes the accelerator """
+ iter1 = self.liststore.get_iter_first()
+ self.new_task_binding = get_saved_binding()
+ self.binding_backup = self.new_task_binding
+ if self.new_task_binding == "":
+ # User had set a shortcut, but has now disabled it
+ self.button.set_active(False)
+ self.liststore.set_value(iter1, 1, "Disabled")
+ return
+ elif self.new_task_binding == None:
+ # User hasn't set a shortcut ever
+ self.button.set_active(False)
+ self.new_task_binding = self.new_task_default_binding
+ self.binding_backup = self.new_task_binding
+ else:
+ # There exists a shortcut
+ self.button.set_active(True)
+ (accel_key, accel_mods) = gtk.accelerator_parse(self.new_task_binding)
+ self.show_input = gtk.accelerator_get_label(accel_key, accel_mods)
+ self.liststore.set_value(iter1, 1, self.show_input)
+
+ def on_shortcut_toggled(self, widget):
+ """ New task shortcut checkbox is toggled """
+ if widget.get_active() == True:
+ self.new_task_binding = self.binding_backup
+ on_shortcut_change(self.new_task_binding, True)
+ else:
+ self.new_task_binding = ""
+ on_shortcut_change(self.new_task_binding, True)
+
+ def _cellAccelEdit(self, cell, path, accel_key, accel_mods, code, model):
+ """ Accelerator is modified """
+ self.show_input = gtk.accelerator_get_label(accel_key, accel_mods)
+ self.new_task_binding = gtk.accelerator_name(accel_key, accel_mods)
+ if check_invalidity(self.new_task_binding, accel_key, accel_mods):
+ self._show_warning(gtk.Button("Warning"), self.show_input)
+ return
+ self.binding_backup = self.new_task_binding
+ iter = model.get_iter(path)
+ model.set_value(iter, 1, self.show_input)
+ on_shortcut_change(self.new_task_binding, self.button.get_active())
+
+ def _accel_cleared(self, widget, path, model):
+ """ Clear the accelerator """
+ iter = model.get_iter(path)
+ model.set_value(iter, 1, None)
+
+ def _show_warning(self, widget, input_str):
+ """ Show warning when user enters inappropriate accelerator """
+ str1 = "The shortcut \"" + input_str + "\" cannot be used because "
+ str1 = str1 + "it will become impossible to type using this key.\n"
+ str1 = str1 + "Please try with a key such as "
+ show = str1 + "Control, Alt or Shift at the same time."
+ dialog = gtk.MessageDialog(self.dialog, gtk.DIALOG_DESTROY_WITH_PARENT,
+ gtk.MESSAGE_WARNING, gtk.BUTTONS_CANCEL,
+ show)
+ response = dialog.run()
+ dialog.hide()
=== added file 'GTG/tools/shortcut.py'
--- GTG/tools/shortcut.py 1970-01-01 00:00:00 +0000
+++ GTG/tools/shortcut.py 2013-03-04 17:25:24 +0000
@@ -0,0 +1,196 @@
+# -*- coding: utf-8 -*-
+# -----------------------------------------------------------------------------
+# Getting Things GNOME! - a personal organizer for the GNOME desktop
+# Copyright (c) 2008-2012 - Lionel Dricot & Bertrand Rousseau
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program. If not, see <http://www.gnu.org/licenses/>.
+# -----------------------------------------------------------------------------
+
+import subprocess
+import re
+
+list_prefix = "gsettings list-keys "
+CHECK_VERSION = list_prefix + "org.gnome.settings-daemon.plugins.media-keys"
+NEW_TASK_ACTION = "gtg_new_task"
+NEW_TASK_NAME = "GTG New Task"
+get_temp = "gsettings get "
+path = "org.gnome.settings-daemon.plugins.media-keys "
+GSETTINGS_GET_LIST = get_temp + path + "custom-keybindings"
+set_temp = "gsettings set "
+GSETTINGS_SET_LIST = set_temp + path + "custom-keybindings"
+key_path1 = "org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:"
+path2 = "/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/"
+GSETTINGS_GET = get_temp + key_path1 + path2 + "custom"
+GSETTINGS_SET = set_temp + key_path1 + path2 + "custom"
+GCONF_GET = "gconftool-2 --get /desktop/gnome/keybindings/custom"
+GCONF_SET = "gconftool-2 --type string --set /desktop/gnome/keybindings/custom"
+
+
+def get_saved_binding():
+ """ Get the current shortcut if the task exists """
+ list_keys = call_subprocess(cmd = CHECK_VERSION)
+ list_keys = list_keys.splitlines()
+ if "custom-keybindings" in list_keys:
+ binding = get_shortcut_from_dconf()
+ else:
+ binding = get_shortcut_from_gconf()
+ return binding
+
+
+def get_shortcut_from_dconf():
+ """ If system uses dconf, then get the shortcut via gsettings """
+ dconf_out = call_subprocess(cmd = GSETTINGS_GET_LIST)
+ custom_shortcuts_list = re.findall(r'custom[0-9]+', dconf_out)
+
+ for entry in custom_shortcuts_list:
+ to_access = entry[-1]
+ get_cmd = call_subprocess(cmd = GSETTINGS_GET, i = str(to_access),
+ key = "/ command")
+
+ if NEW_TASK_ACTION in get_cmd:
+ get_bind = call_subprocess(cmd = GSETTINGS_GET, i = str(to_access),
+ key = "/ binding")
+ return get_bind.rstrip("\n")
+ return None
+
+
+def get_shortcut_from_gconf():
+ """ If system uses gconf, then get the shortcut via gconftool-2 """
+ item=0
+ while(1):
+ get_action = call_subprocess(cmd = GCONF_GET, i = str(item),
+ key = "/action")
+
+ if NEW_TASK_ACTION in get_action:
+ get_bind = call_subprocess(cmd = GCONF_GET, i = str(item),
+ key = "/binding")
+ return get_bind.rstrip("\n")
+
+ elif get_action == "":
+ get_name = call_subprocess(cmd = GCONF_GET, i = str(item),
+ key = "/name")
+ if get_name == "":
+ return None
+ item += 1
+
+
+def on_shortcut_change(binding, button_state):
+ """ When user has entered a new shortcut """
+ if button_state == True:
+ list_keys = call_subprocess(cmd = CHECK_VERSION)
+ list_keys = list_keys.splitlines()
+ if "custom-keybindings" in list_keys:
+ add_shortcut_to_dconf(binding)
+ else:
+ add_shortcut_to_gconf(binding)
+
+
+def add_shortcut_to_dconf(binding):
+ """ If system uses dconf, then set the new shortcut via gsettings """
+ dconf_out = call_subprocess(cmd = GSETTINGS_GET_LIST)
+ custom_keys = re.findall(r'custom[0-9]+', dconf_out)
+
+ for entry in custom_keys:
+ to_access = entry[-1]
+ get_cmd = call_subprocess(cmd = GSETTINGS_GET, i = str(to_access),
+ key = "/ command")
+
+ if NEW_TASK_ACTION in get_cmd:
+ call_subprocess(cmd = GSETTINGS_SET, to_append = binding,
+ i = str(to_access), key = "/ binding")
+ return
+
+ a=[]
+ for item in custom_keys:
+ a.append(int(re.findall(r'[0-9]+', item)[0]))
+ if a == []:
+ index = 0
+ else:
+ a.sort()
+ index = a[-1] + 1
+ prefix = "['/org/gnome/settings-daemon/plugins/media-keys/"
+ prefix = prefix + "custom-keybindings/custom"
+ append_this = prefix + str(index) + "/']"
+ call_subprocess(cmd = GSETTINGS_SET, to_append = NEW_TASK_ACTION,
+ i = str(index), key = "/ command")
+ call_subprocess(cmd = GSETTINGS_SET, to_append = binding,
+ i = str(index), key = "/ binding")
+ call_subprocess(cmd = GSETTINGS_SET, to_append = NEW_TASK_NAME,
+ i = str(index), key = "/ name")
+
+ if index == 0:
+ call_subprocess(cmd = GSETTINGS_SET_LIST, key = ' ' + append_this)
+
+ else:
+ result_list = dconf_out[:-2] + ", "
+ result_list = result_list + append_this[1:-1] + "]"
+ call_subprocess(cmd = GSETTINGS_SET_LIST,
+ to_append = result_list)
+
+
+def add_shortcut_to_gconf(binding):
+ """ If system uses gconf, then set the new shortcut via gconftool-2 """
+ item=0
+ while(1):
+ get_action = call_subprocess(cmd = GCONF_GET, i = str(item),
+ key = "/action")
+
+ if NEW_TASK_ACTION in get_action:
+ binding_ans = call_subprocess(cmd = GCONF_SET,
+ to_append = binding, i = str(item),
+ key = "/binding")
+ break
+
+ if get_action == "":
+ call_subprocess(cmd = GCONF_SET, to_append = NEW_TASK_ACTION,
+ i = str(item), key = "/action")
+ call_subprocess(cmd = GCONF_SET, to_append = binding,
+ i = str(item), key = "/binding")
+ call_subprocess(cmd = GCONF_SET, to_append = NEW_TASK_NAME,
+ i = str(item), key = "/name")
+ break
+ item += 1
+
+
+def call_subprocess(cmd, i = "", key = "", to_append = None):
+ """ Sets the values in either dconf or gconf.
+ 'cmd' holds the command to access the configuration database,
+ 'i' accesses a custom shortcut,
+ 'key' accesses values inside the shortcut,
+ 'to_append' contains the new value to replace if any """
+ cmd = cmd + i + key
+ cmd = cmd.split(" ")
+ if to_append != None:
+ cmd.append(to_append)
+ out = subprocess.Popen(cmd, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ return out.communicate()[0]
+
+
+def check_invalidity(binding, key, mods):
+ """ Checks if the user has entered inappropriate shortcut """
+ if mods == 0:
+ if (key >= 97 and key <= 122):
+ # key is an alphabet
+ return 1
+ elif (key >= 48 and key <= 57):
+ # key is a number
+ return 1
+ elif (key >= 65361 and key <= 65364):
+ # key is one of the arrow keys
+ return 1
+ elif key == 32:
+ # key is 'space'
+ return 1
+ return 0