Merge lp:~julien-spautz/switchboard-plug-keyboard/custom-shortcuts into lp:~elementary-pantheon/switchboard-plug-keyboard/trunk

Proposed by Cody Garver
Status: Merged
Merged at revision: 325
Proposed branch: lp:~julien-spautz/switchboard-plug-keyboard/custom-shortcuts
Merge into: lp:~elementary-pantheon/switchboard-plug-keyboard/trunk
Diff against target: 971 lines (+550/-233)
12 files modified
src/CMakeLists.txt (+4/-0)
src/Pages/Shortcuts/conflict_dialog.vala (+19/-0)
src/Pages/Shortcuts/custom_shortcut_settings.vala (+146/-0)
src/Pages/Shortcuts/custom_tree.vala (+178/-0)
src/Pages/Shortcuts/display.vala (+23/-11)
src/Pages/Shortcuts/display_tree.vala (+5/-0)
src/Pages/Shortcuts/list.vala (+1/-26)
src/Pages/Shortcuts/section_switcher.vala (+6/-3)
src/Pages/Shortcuts/settings.vala (+1/-1)
src/Pages/Shortcuts/shortcut.vala (+2/-2)
src/Pages/Shortcuts/tree.vala (+144/-181)
src/Pages/shortcuts.vala (+21/-9)
To merge this branch: bzr merge lp:~julien-spautz/switchboard-plug-keyboard/custom-shortcuts
Reviewer Review Type Date Requested Status
Raphael Isemann (community) code style, functionality Needs Fixing
Artem Anufrij (community) code style Needs Fixing
Danielle Foré Needs Fixing
David Gomes (community) Needs Fixing
Review via email: mp+212343@code.launchpad.net

Description of the change

Now open for review.

Implemented custom shortcuts into the keyboard plug. I'm using dconf/gsettings instead of gconf (unlike elementary tweaks) since the gconf version doesn't seem to work on isis, also it's deprecated.

Custom shortcuts can be added/removed/edited, the UI is a little different from the original ubuntu settings to avoid using unnecessary dialogs. I hope it's still intuitive.

If the schema for custom shortcuts doesn't exist, the section will not be shown at all.

Conflicting shortcuts are handled like before, asking the user whether he wants to reassign the shortcut.

NOTE that changes to custom shortcuts will, for some reason, not be applied immediately but you'll have to log out and in again, reboot or restart gnome-settings-daemon. This is not necessary with ubuntu's system settings, but I haven't yet figured out how to fix this.

To post a comment you must log in.
136. By Julien Spautz

small refactor: using DisplayTree instead of GtkTree to avoid casting

137. By Julien Spautz

remove some unused code

138. By Julien Spautz

fixed failing assertions on clearing shortcuts

Revision history for this message
David Gomes (davidgomes) wrote :

Code style wise this can't really pass for now. You added a new file that is tab-indented (conflict_dialog.vala). (and I realize you did it to keep consistency with existing files in the project, but new code should *usually* be good code).

Also diff lines 727-735 you mixed tabs and spaces.

I can't yet review the code itself, give me a couple of days. I'll test it in the meanwhile too.

review: Needs Fixing
139. By Julien Spautz

fix inconsistent whitespace

Revision history for this message
Julien Spautz (julien-spautz) wrote :

Completely forgot about the whitespace... Following files should now follow elementary's coding guidelines, all other files should still be internally consistent:

src/Pages/Shortcuts/conflict_dialog.vala
src/Pages/Shortcuts/custom_shortcut_settings.vala
src/Pages/Shortcuts/custom_tree.vala
src/Pages/Shortcuts/display_tree.vala
src/Pages/Shortcuts/tree.vala

(The four first files are new, the last one isn't, but I had to change a lot of code in that one so I converted it to elementary code)

140. By Julien Spautz

fixed small inconsistency in whitespace

Revision history for this message
David Gomes (davidgomes) wrote :

I can't seem to build this. First I got:

`
/usr/bin/msgfmt: error while opening "/home/david/src/switchboard-plug-keyboard/switchboard-plug-keyboard/build/po/el.mo" for writing: Permission denied
`

So I ran it with root privileges and got: http://paste.debian.net/91497/

Revision history for this message
Julien Spautz (julien-spautz) wrote :

built it on freya beta 1 and it works for me, can someone test it again?

Revision history for this message
Danielle Foré (danrabbit) wrote :

Shortcuts don't seem to apply until I log out and log back in. Is there any way to make these apply immediately? (restart/update whatever service is responsible for watching for shortcuts?)

If not, we should at least show an infobar stating that you'll need to log out or restart for the changes to take effect.

review: Needs Fixing
Revision history for this message
Danielle Foré (danrabbit) wrote :

The "-" button shouldn't be sensitive if there is no line selected

Revision history for this message
Artem Anufrij (artem-anufrij) wrote :

see line comments

review: Needs Fixing (code style)
Revision history for this message
Raphael Isemann (teemperor) wrote :

As Daniel said, you shouln't need to relog/reboot for the keybindings to take effect, so just set "org.gnome.settings-daemon.plugins.media-keys" to false and then to true again and GSD will reload the media-keys plugin.

Interestingly on my system if i do this noise seems to loose the binding to the next/previous keys on my keyboard until i restart noise. But i think that's rather a bug in noise itself?

Otherwise:
* Commented-out code in line 780-781.
* Line 417: tbar = new Gtk.Toolbar(); <- needs a blank before the bracket

review: Needs Fixing (code style, functionality)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/CMakeLists.txt'
2--- src/CMakeLists.txt 2014-01-10 08:17:09 +0000
3+++ src/CMakeLists.txt 2014-03-30 21:21:15 +0000
4@@ -29,6 +29,10 @@
5 Pages/Shortcuts/shortcut.vala
6 Pages/Shortcuts/list.vala
7 Pages/Shortcuts/section_switcher.vala
8+ Pages/Shortcuts/custom_tree.vala
9+ Pages/Shortcuts/custom_shortcut_settings.vala
10+ Pages/Shortcuts/conflict_dialog.vala
11+ Pages/Shortcuts/display_tree.vala
12
13 Pages/layout.vala
14 Pages/Layout/display.vala
15
16=== added file 'src/Pages/Shortcuts/conflict_dialog.vala'
17--- src/Pages/Shortcuts/conflict_dialog.vala 1970-01-01 00:00:00 +0000
18+++ src/Pages/Shortcuts/conflict_dialog.vala 2014-03-30 21:21:15 +0000
19@@ -0,0 +1,19 @@
20+class ConflictDialog : Gtk.MessageDialog {
21+
22+ public signal void reassign ();
23+
24+ public ConflictDialog (string shortcut, string conflict_action, string this_action) {
25+ modal = true;
26+ message_type = Gtk.MessageType.WARNING;
27+ text = @"\"$shortcut\" is already used for \"$conflict_action\"!";
28+ secondary_text = _(@"If you reassign the shortcut to \"$this_action\", \"$conflict_action\" will be disabled");
29+ add_button (_("Cancel"), 0);
30+ add_button (_("Reassign"), 1);
31+
32+ response.connect ((response_id) => {
33+ if (response_id == 1)
34+ reassign ();
35+ destroy();
36+ });
37+ }
38+}
39\ No newline at end of file
40
41=== added file 'src/Pages/Shortcuts/custom_shortcut_settings.vala'
42--- src/Pages/Shortcuts/custom_shortcut_settings.vala 1970-01-01 00:00:00 +0000
43+++ src/Pages/Shortcuts/custom_shortcut_settings.vala 2014-03-30 21:21:15 +0000
44@@ -0,0 +1,146 @@
45+class Pantheon.Keyboard.Shortcuts.CustomShortcutSettings : Object {
46+
47+ const string SCHEMA = "org.gnome.settings-daemon.plugins.media-keys";
48+ const string KEY = "custom-keybinding";
49+
50+ const string RELOCATABLE_SCHEMA_PATH_TEMLPATE = "/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom%d/";
51+
52+ const int MAX_SHORTCUTS = 100;
53+
54+ static GLib.Settings settings;
55+
56+ public static bool available = false;
57+
58+ public struct CustomShortcut {
59+ string shortcut;
60+ string command;
61+ string relocatable_schema;
62+ }
63+
64+ public static void init () {
65+ var schema_source = GLib.SettingsSchemaSource.get_default ();
66+
67+ var schema = schema_source.lookup (SCHEMA, true);
68+
69+ if (schema == null) {
70+ warning ("Schema \"%s\" is not installed on your system.", SCHEMA);
71+ return;
72+ }
73+
74+ settings = new GLib.Settings.full (schema, null, null);
75+ available = true;
76+ }
77+
78+ static string[] get_relocatable_schemas () {
79+ return settings.get_strv (KEY + "s");
80+ }
81+
82+ static string get_relocatable_schema_path (int i) {
83+ return RELOCATABLE_SCHEMA_PATH_TEMLPATE.printf (i);
84+ }
85+
86+ static GLib.Settings? get_relocatable_schema_settings (string relocatable_schema) {
87+ return new GLib.Settings.with_path (SCHEMA + "." + KEY, relocatable_schema);
88+ }
89+
90+ public static string? create_shortcut () requires (available) {
91+ for (int i = 0; i < MAX_SHORTCUTS; i++) {
92+ var new_relocatable_schema = get_relocatable_schema_path (i);
93+
94+ if (relocatable_schema_is_used (new_relocatable_schema) == false) {
95+ add_relocatable_schema (new_relocatable_schema);
96+ reset_relocatable_schema (new_relocatable_schema);
97+ return new_relocatable_schema;
98+ }
99+ }
100+
101+ return (string) null;
102+ }
103+
104+ static bool relocatable_schema_is_used (string new_relocatable_schema) {
105+ var relocatable_schemas = get_relocatable_schemas ();
106+
107+ foreach (var relocatable_schema in relocatable_schemas)
108+ if (relocatable_schema == new_relocatable_schema)
109+ return true;
110+
111+ return false;
112+ }
113+
114+ static void add_relocatable_schema (string new_relocatable_schema) {
115+ var relocatable_schemas = get_relocatable_schemas ();
116+ relocatable_schemas += new_relocatable_schema;
117+ settings.set_strv (KEY + "s", relocatable_schemas);
118+ }
119+
120+ static void reset_relocatable_schema (string relocatable_schema) {
121+ var relocatable_settings = get_relocatable_schema_settings (relocatable_schema);
122+ relocatable_settings.reset ("name");
123+ relocatable_settings.reset ("command");
124+ relocatable_settings.reset ("binding");
125+ }
126+
127+ public static void remove_shortcut (string relocatable_schema)
128+ requires (available) {
129+
130+ string []relocatable_schemas = {};
131+
132+ foreach (var schema in get_relocatable_schemas ())
133+ if (schema != relocatable_schema)
134+ relocatable_schemas += schema;
135+
136+ reset_relocatable_schema (relocatable_schema);
137+ settings.set_strv (KEY + "s", relocatable_schemas);
138+ }
139+
140+ public static bool edit_shortcut (string relocatable_schema, string shortcut)
141+ requires (available) {
142+
143+ var relocatable_settings = get_relocatable_schema_settings (relocatable_schema);
144+ relocatable_settings.set_string ("binding", shortcut);
145+ return true;
146+ }
147+
148+ public static bool edit_command (string relocatable_schema, string command)
149+ requires (available) {
150+
151+ var relocatable_settings = get_relocatable_schema_settings (relocatable_schema);
152+ relocatable_settings.set_string ("command", command);
153+ return true;
154+ }
155+
156+ public static GLib.List <CustomShortcut?> list_custom_shortcuts ()
157+ requires (available) {
158+
159+ var list = new GLib.List <CustomShortcut?> ();
160+ foreach (var relocatable_schema in get_relocatable_schemas ())
161+ list.append (create_custom_shortcut_object (relocatable_schema));
162+ return list;
163+ }
164+
165+ static CustomShortcut? create_custom_shortcut_object (string relocatable_schema) {
166+ var relocatable_settings = get_relocatable_schema_settings (relocatable_schema);
167+
168+ return {
169+ relocatable_settings.get_string ("binding"),
170+ relocatable_settings.get_string ("command"),
171+ relocatable_schema
172+ };
173+ }
174+
175+ public static bool shortcut_conflicts (Shortcut new_shortcut, out string command,
176+ out string relocatable_schema) {
177+ var custom_shortcuts = list_custom_shortcuts ();
178+
179+ foreach (var custom_shortcut in custom_shortcuts) {
180+ var shortcut = new Shortcut.parse (custom_shortcut.shortcut);
181+ if (shortcut.is_equal (new_shortcut)) {
182+ command = custom_shortcut.command;
183+ relocatable_schema = custom_shortcut.relocatable_schema;
184+ return true;
185+ }
186+ }
187+
188+ return false;
189+ }
190+}
191\ No newline at end of file
192
193=== added file 'src/Pages/Shortcuts/custom_tree.vala'
194--- src/Pages/Shortcuts/custom_tree.vala 1970-01-01 00:00:00 +0000
195+++ src/Pages/Shortcuts/custom_tree.vala 2014-03-30 21:21:15 +0000
196@@ -0,0 +1,178 @@
197+private class Pantheon.Keyboard.Shortcuts.CustomTree : Gtk.TreeView, DisplayTree {
198+
199+ Gtk.CellRendererText cell_desc;
200+ Gtk.CellRendererAccel cell_edit;
201+
202+ enum Column {
203+ COMMAND,
204+ SHORTCUT,
205+ SCHEMA,
206+ COUNT
207+ }
208+
209+ public CustomTree () {
210+ setup_gui ();
211+ load_and_display_custom_shortcuts ();
212+ connect_signals ();
213+ }
214+
215+ Gtk.ListStore list_store {
216+ get { return model as Gtk.ListStore; }
217+ }
218+
219+ void setup_gui () {
220+ var store = new Gtk.ListStore (Column.COUNT , typeof (string),
221+ typeof (string),
222+ typeof (string));
223+
224+ cell_desc = new Gtk.CellRendererText ();
225+ cell_edit = new Gtk.CellRendererAccel ();
226+
227+ cell_desc.editable = true;
228+ cell_edit.editable = true;
229+ cell_edit.accel_mode = Gtk.CellRendererAccelMode.OTHER;
230+
231+ this.set_model (store);
232+
233+ this.insert_column_with_attributes (-1, _("Command"), cell_desc, "markup", Column.COMMAND);
234+ this.insert_column_with_attributes (-1, _("Shortcut"), cell_edit, "text", Column.SHORTCUT);
235+
236+ this.expand = true;
237+ this.get_column (0).expand = true;
238+ }
239+
240+ public void load_and_display_custom_shortcuts () {
241+ Gtk.TreeIter iter;
242+ var store = new Gtk.ListStore (Column.COUNT , typeof (string),
243+ typeof (string),
244+ typeof (string));
245+
246+ foreach (var custom_shortcut in CustomShortcutSettings.list_custom_shortcuts ()) {
247+ var shortcut = new Shortcut.parse (custom_shortcut.shortcut);
248+
249+ store.append (out iter);
250+ store.set (iter,
251+ Column.COMMAND, command_to_display (custom_shortcut.command),
252+ Column.SHORTCUT, shortcut.to_readable (),
253+ Column.SCHEMA, custom_shortcut.relocatable_schema
254+ );
255+ }
256+
257+ model = store;
258+ }
259+
260+ void connect_signals () {
261+ this.button_press_event.connect ((event) => {
262+ if (event.window != this.get_bin_window ())
263+ return false;
264+
265+ Gtk.TreePath path;
266+ Gtk.TreeViewColumn col;
267+
268+ if (this.get_path_at_pos ((int) event.x, (int) event.y,
269+ out path, out col, null, null)) {
270+ this.grab_focus ();
271+ this.set_cursor (path, col, true);
272+ }
273+
274+ return true;
275+ });
276+
277+ cell_edit.accel_edited.connect ((path, key, mods) => {
278+ var shortcut = new Shortcut (key, mods);
279+ change_shortcut (path, shortcut);
280+ });
281+
282+ cell_edit.accel_cleared.connect ((path) => {
283+ change_shortcut (path, (Shortcut) null);
284+ });
285+
286+ cell_desc.edited.connect (change_command);
287+ }
288+
289+ string command_to_display (string? command) {
290+ if (command == null || command.strip () == "")
291+ return "<i>" +_("Enter Command") + "</i>";
292+ return GLib.Markup.escape_text (command);
293+ }
294+
295+ public void on_add_clicked () {
296+ var store = model as Gtk.ListStore;
297+ Gtk.TreeIter iter;
298+
299+ var relocatable_schema = CustomShortcutSettings.create_shortcut ();
300+
301+ store.append (out iter);
302+ store.set (iter, Column.COMMAND, command_to_display (null));
303+ store.set (iter, Column.SHORTCUT, (new Shortcut.parse ("")).to_readable ());
304+ store.set (iter, Column.SCHEMA, relocatable_schema);
305+ }
306+
307+ public void on_remove_clicked () {
308+ Gtk.TreeIter iter;
309+ Gtk.TreePath path;
310+ GLib.Value relocatable_schema;
311+
312+ get_cursor (out path, null);
313+ model.get_iter (out iter, path);
314+ model.get_value (iter, Column.SCHEMA, out relocatable_schema);
315+
316+ CustomShortcutSettings.remove_shortcut ((string) relocatable_schema);
317+ list_store.remove (iter);
318+ }
319+
320+ void change_command (string path, string new_text) {
321+ Gtk.TreeIter iter;
322+ GLib.Value relocatable_schema;
323+
324+ model.get_iter (out iter, new Gtk.TreePath.from_string (path));
325+ model.get_value (iter, Column.SCHEMA, out relocatable_schema);
326+
327+ CustomShortcutSettings.edit_command ((string) relocatable_schema, new_text);
328+ load_and_display_custom_shortcuts ();
329+ }
330+
331+ public bool shortcut_conflicts (Shortcut shortcut, out string name) {
332+ return CustomShortcutSettings.shortcut_conflicts (shortcut, out name, null);
333+ }
334+
335+ public void reset_shortcut (Shortcut shortcut) {
336+ string relocatable_schema;
337+ CustomShortcutSettings.shortcut_conflicts (shortcut, null, out relocatable_schema);
338+ CustomShortcutSettings.edit_shortcut (relocatable_schema, "");
339+ load_and_display_custom_shortcuts ();
340+ }
341+
342+ bool change_shortcut (string path, Shortcut? shortcut) {
343+ Gtk.TreeIter iter;
344+ GLib.Value command, relocatable_schema;
345+
346+ model.get_iter (out iter, new Gtk.TreePath.from_string (path));
347+ model.get_value (iter, Column.SCHEMA, out relocatable_schema);
348+ model.get_value (iter, Column.COMMAND, out command);
349+
350+ var not_null_shortcut = shortcut ?? new Shortcut ();
351+
352+ string conflict_name;
353+
354+ if (shortcut != null) {
355+ foreach (var tree in trees) {
356+ if (tree.shortcut_conflicts (shortcut, out conflict_name) == false)
357+ continue;
358+
359+ var dialog = new ConflictDialog (shortcut.to_readable (), conflict_name, (string) command);
360+ dialog.reassign.connect (() => {
361+ tree.reset_shortcut (shortcut);
362+ CustomShortcutSettings.edit_shortcut ((string) relocatable_schema, not_null_shortcut.to_gsettings ());
363+ load_and_display_custom_shortcuts ();
364+ });
365+ dialog.show ();
366+ return false;
367+ }
368+ }
369+
370+ CustomShortcutSettings.edit_shortcut ((string) relocatable_schema, not_null_shortcut.to_gsettings ());
371+ load_and_display_custom_shortcuts ();
372+ return true;
373+ }
374+}
375\ No newline at end of file
376
377=== modified file 'src/Pages/Shortcuts/display.vala'
378--- src/Pages/Shortcuts/display.vala 2014-01-06 17:12:27 +0000
379+++ src/Pages/Shortcuts/display.vala 2014-03-30 21:21:15 +0000
380@@ -1,3 +1,4 @@
381+// TODO use new Gtk.Stack widget here
382 namespace Pantheon.Keyboard.Shortcuts
383 {
384 // creates a grid containing a tree view and an inline toolbar
385@@ -6,16 +7,17 @@
386 int selected;
387
388 Gtk.ScrolledWindow scroll;
389- Tree[] trees;
390-
391- public ShortcutDisplay (Tree[] t)
392+ DisplayTree[] trees;
393+
394+ Gtk.Toolbar tbar;
395+
396+ public ShortcutDisplay (DisplayTree[] t)
397 {
398 selected = 0;
399
400- trees = t;
401-
402- foreach (var tree in trees) {
403+ foreach (var tree in t) {
404 tree.set_rules_hint (true);
405+ trees += tree;
406 }
407
408 scroll = new Gtk.ScrolledWindow(null, null);
409@@ -23,14 +25,15 @@
410 scroll.vscrollbar_policy = Gtk.PolicyType.AUTOMATIC;
411 scroll.shadow_type = Gtk.ShadowType.IN;
412 scroll.expand = true;
413- scroll.add (trees[selected]);
414+ scroll.add (t[selected]);
415
416- var tbar = new Gtk.Toolbar();
417+ tbar = new Gtk.Toolbar();
418 tbar.set_style(Gtk.ToolbarStyle.ICONS);
419 tbar.set_icon_size(Gtk.IconSize.SMALL_TOOLBAR);
420 tbar.set_show_arrow(false);
421 tbar.hexpand = true;
422-
423+ tbar.no_show_all = true;
424+
425 scroll.get_style_context().set_junction_sides(Gtk.JunctionSides.BOTTOM);
426 tbar.get_style_context().add_class(Gtk.STYLE_CLASS_INLINE_TOOLBAR);
427 tbar.get_style_context().set_junction_sides(Gtk.JunctionSides.TOP);
428@@ -48,7 +51,12 @@
429 tbar.insert (remove_button, -1);
430
431 this.attach (scroll, 0, 0, 1, 1);
432- //this.attach (tbar, 0, 1, 1, 1);
433+ this.attach (tbar, 0, 1, 1, 1);
434+
435+ add_button.clicked.connect (() =>
436+ (trees[selected] as CustomTree).on_add_clicked ());
437+ remove_button.clicked.connect (() =>
438+ (trees[selected] as CustomTree).on_remove_clicked ());
439 }
440
441 // replace old tree view with new one
442@@ -58,7 +66,11 @@
443 scroll.add (trees[new_selection]);
444
445 selected = new_selection;
446- scroll.show_all ();
447+
448+ tbar.no_show_all = new_selection != SectionID.CUSTOM;
449+ tbar.visible = new_selection == SectionID.CUSTOM;
450+
451+ show_all ();
452
453 return true;
454 }
455
456=== added file 'src/Pages/Shortcuts/display_tree.vala'
457--- src/Pages/Shortcuts/display_tree.vala 1970-01-01 00:00:00 +0000
458+++ src/Pages/Shortcuts/display_tree.vala 2014-03-30 21:21:15 +0000
459@@ -0,0 +1,5 @@
460+interface Pantheon.Keyboard.Shortcuts.DisplayTree : Gtk.TreeView {
461+
462+ public abstract bool shortcut_conflicts (Shortcut shortcut, out string name);
463+ public abstract void reset_shortcut (Shortcut shortcut);
464+}
465\ No newline at end of file
466
467=== modified file 'src/Pages/Shortcuts/list.vala'
468--- src/Pages/Shortcuts/list.vala 2014-03-12 08:11:41 +0000
469+++ src/Pages/Shortcuts/list.vala 2014-03-30 21:21:15 +0000
470@@ -21,31 +21,6 @@
471 return;
472 }
473
474- public bool conflicts (Shortcut s, out string key, out int group, out int path)
475- {
476- key = (string) null;
477- group = (SectionID) 0;
478- path = -1;
479-
480- if (s.accel_key == Gdk.Key.BackSpace)
481- return false;
482-
483- for (int g = 0; g < groups.length; g++)
484- {
485- for (int i = 0; i < groups[g].actions.length; i++)
486- {
487- if (s.is_equal (settings.get_val(groups[g].schemas[i], groups[g].keys[i])))
488- {
489- key = groups[g].keys[i];
490- group = g;
491- path = i;
492- return true;
493- }
494- }
495- }
496- return false;
497- }
498-
499 public List ()
500 {
501 groups =
502@@ -70,7 +45,7 @@
503 _("Expose Windows"),
504 _("Expose all Windows")
505 },
506- schemas = {
507+ schemas = {
508 Schema.WM,
509 Schema.WM,
510 Schema.WM,
511
512=== modified file 'src/Pages/Shortcuts/section_switcher.vala'
513--- src/Pages/Shortcuts/section_switcher.vala 2014-01-06 17:12:27 +0000
514+++ src/Pages/Shortcuts/section_switcher.vala 2014-03-30 21:21:15 +0000
515@@ -12,8 +12,11 @@
516
517 Gtk.TreeIter iter;
518
519- // add the sections
520- for (int id = 0; id < SectionID.COUNT; id++) {
521+ var max_section_id = CustomShortcutSettings.available
522+ ? SectionID.COUNT
523+ : SectionID.CUSTOM;
524+
525+ for (int id = 0; id < max_section_id; id++) {
526 store.append (out iter);
527 store.set (iter, 0, section_names[id]);
528 }
529@@ -39,6 +42,6 @@
530 });
531 }
532
533- public signal void changed (int i);
534+ public signal bool changed (int i);
535 }
536 }
537\ No newline at end of file
538
539=== modified file 'src/Pages/Shortcuts/settings.vala'
540--- src/Pages/Shortcuts/settings.vala 2014-01-06 17:12:27 +0000
541+++ src/Pages/Shortcuts/settings.vala 2014-03-30 21:21:15 +0000
542@@ -26,7 +26,7 @@
543 var schema = schema_source.lookup (name, true);
544
545 if (schema == null) {
546- warning ("Schema \"%s\" is not installed on you system.", name);
547+ warning ("Schema \"%s\" is not installed on your system.", name);
548 schemas += (GLib.Settings) null;
549 } else {
550 schemas += new GLib.Settings.full (schema, null, null);
551
552=== modified file 'src/Pages/Shortcuts/shortcut.vala'
553--- src/Pages/Shortcuts/shortcut.vala 2014-01-06 17:12:27 +0000
554+++ src/Pages/Shortcuts/shortcut.vala 2014-03-30 21:21:15 +0000
555@@ -10,7 +10,7 @@
556 string SEPARATOR = " · ";
557
558 // constructors
559- public Shortcut ( uint key, Gdk.ModifierType mod )
560+ public Shortcut (uint key = 0, Gdk.ModifierType mod = (Gdk.ModifierType) 0)
561 {
562 accel_key = key;
563 modifiers = mod;
564@@ -107,4 +107,4 @@
565 }
566
567 }
568-}
569\ No newline at end of file
570+}
571
572=== modified file 'src/Pages/Shortcuts/tree.vala'
573--- src/Pages/Shortcuts/tree.vala 2014-01-06 17:12:27 +0000
574+++ src/Pages/Shortcuts/tree.vala 2014-03-30 21:21:15 +0000
575@@ -1,182 +1,145 @@
576-namespace Pantheon.Keyboard.Shortcuts
577-{
578- // contains the shortcuts and handels all changes in gsettings
579- private class Tree : Gtk.TreeView
580- {
581- private string[] actions;
582- private Schema[] schemas;
583- private string[] keys;
584-
585- // quick access to one item in the tree view
586- public bool get_item (uint i, out string action, out Schema schema, out string key)
587- {
588- action = null;
589- schema = (Schema) null;
590- key = null;
591-
592- if (i < actions.length)
593- {
594- action = actions[i];
595- schema = schemas[i];
596- key = keys[i];
597- return true;
598- }
599- return false;
600- }
601-
602- public Tree (SectionID group)
603- {
604- list.get_group (group, out actions, out schemas, out keys);
605-
606- // create list store
607- var store = new Gtk.ListStore (4, typeof (string),
608- typeof (string),
609- typeof (Schema),
610- typeof (string));
611-
612- Gtk.TreeIter iter;
613-
614- for (int i = 0; i < actions.length; i++)
615- {
616- var shortcut = settings.get_val(schemas[i], keys[i]);
617-
618- // simply ignore missing keys/schemas
619- if (shortcut == null)
620- continue;
621-
622- store.append (out iter);
623- store.set (iter, 0, actions[i],
624- 1, shortcut.to_readable(),
625- 2, schemas[i], // hidden
626- 3, keys[i], -1); // hidden
627- }
628-
629- // create tree view
630- var cell_desc = new Gtk.CellRendererText ();
631- var cell_edit = new Gtk.CellRendererAccel ();
632-
633- cell_edit.editable = true;
634- cell_edit.accel_mode = Gtk.CellRendererAccelMode.OTHER;
635-
636- this.set_model (store);
637-
638- this.insert_column_with_attributes (-1, null, cell_desc, "text", 0);
639- this.insert_column_with_attributes (-1, null, cell_edit, "text", 1);
640- // debug
641- //this.insert_column_with_attributes (-1, null, cell_desc, "text", 2);
642- //this.insert_column_with_attributes (-1, null, cell_edit, "text", 3);
643-
644- this.headers_visible = false;
645- this.expand = true;
646-
647- this.get_column (0).expand = true;
648-
649- this.button_press_event.connect ((event) =>
650- {
651- if (event.window != this.get_bin_window ())
652- return false;
653-
654- Gtk.TreePath path;
655-
656- if (this.get_path_at_pos ((int) event.x,
657- (int) event.y,
658- out path, null,
659- null, null))
660- {
661- Gtk.TreeViewColumn col = this.get_column (1);
662- this.grab_focus ();
663- this.set_cursor (path, col, true);
664- }
665-
666- return true;
667- } );
668-
669- // signals
670- cell_edit.accel_edited.connect ((path, key, mods) =>
671- {
672- var shortcut = new Shortcut (key, mods);
673- change_shortcut (path, shortcut);
674- } );
675-
676- cell_edit.accel_cleared.connect ((path) =>
677- {
678- change_shortcut (path, (Shortcut) null);
679- } );
680- }
681-
682- // change a shortcut in the list store and gsettings
683- private bool change_shortcut (string path, Shortcut? shortcut)
684- {
685- Gtk.TreeIter iter;
686- GLib.Value key, schema, name;
687-
688- model.get_iter (out iter, new Gtk.TreePath.from_string (path));
689-
690- model.get_value (iter, 0, out name);
691- model.get_value (iter, 2, out schema);
692- model.get_value (iter, 3, out key);
693-
694-
695- if (shortcut != null)
696- {
697- // new shortcut is old shortcut?
698- if (shortcut.is_equal (settings.get_val ((Schema)schema, (string)key)))
699- return true;
700-
701- string conflict_accel;
702- int conflict_group;
703- int conflict_path;
704-
705- // check if shortcut is already used
706- if (list.conflicts (shortcut, out conflict_accel, out conflict_group, out conflict_path))
707- {
708- string conflict_action;
709- Schema conflict_schema;
710- string conflict_key;
711-
712- // get some info about the conflicting item
713- trees[conflict_group].get_item (conflict_path, out conflict_action, out conflict_schema, out conflict_key);
714-
715- // ask user what to do
716- var msg = new Gtk.MessageDialog (null, Gtk.DialogFlags.MODAL,
717- Gtk.MessageType.WARNING,
718- Gtk.ButtonsType.NONE,
719- "\"%s\" is already used for \"%s\"!", shortcut.to_readable (), conflict_action);
720-
721- msg.secondary_text = _("If you reassign the shortcut to \"%s\", \"%s\" will be disabled").printf ((string)name, conflict_action);
722- msg.add_button (_("Cancel"), 0);
723- msg.add_button (_("Reassign"), 1);
724-
725- msg.response.connect ((response_id) =>
726- {
727- if (response_id == 1)
728- {
729- trees[conflict_group].change_shortcut (conflict_path.to_string (), (Shortcut) null);
730- change_shortcut (path, shortcut);
731- }
732-
733- msg.destroy();
734- });
735- msg.show ();
736-
737- return false;
738- }
739-
740- if (!shortcut.valid ())
741- return false;
742- }
743-
744- // unset/disable shortcut
745- if (shortcut == null)
746- {
747- (model as Gtk.ListStore).set (iter, 1, _("Disabled"));
748- settings.set_val((Schema)schema, (string)key, new Shortcut(0, (Gdk.ModifierType)0));
749- return true;
750- }
751-
752- (model as Gtk.ListStore).set (iter, 1, shortcut.to_readable ());
753- settings.set_val((Schema)schema, (string)key, shortcut);
754- return true;
755- }
756- }
757+namespace Pantheon.Keyboard.Shortcuts {
758+
759+ private class Tree : Gtk.TreeView, DisplayTree {
760+
761+ public SectionID group { private get; construct; }
762+
763+ private string[] actions;
764+ private Schema[] schemas;
765+ private string[] keys;
766+
767+ public Tree (SectionID group) {
768+ Object (group: group);
769+
770+ load_and_display_shortcuts ();
771+
772+ var cell_desc = new Gtk.CellRendererText ();
773+ var cell_edit = new Gtk.CellRendererAccel ();
774+
775+ cell_edit.editable = true;
776+ cell_edit.accel_mode = Gtk.CellRendererAccelMode.OTHER;
777+
778+ this.insert_column_with_attributes (-1, null, cell_desc, "text", 0);
779+ this.insert_column_with_attributes (-1, null, cell_edit, "text", 1);
780+ //this.insert_column_with_attributes (-1, null, cell_desc, "text", 2);
781+ //this.insert_column_with_attributes (-1, null, cell_edit, "text", 3);
782+
783+ this.headers_visible = false;
784+ this.expand = true;
785+
786+ this.get_column (0).expand = true;
787+
788+ this.button_press_event.connect ((event) => {
789+ if (event.window != this.get_bin_window ())
790+ return false;
791+
792+ Gtk.TreePath path;
793+
794+ if (this.get_path_at_pos ((int) event.x, (int) event.y,
795+ out path, null, null, null)) {
796+ Gtk.TreeViewColumn col = this.get_column (1);
797+ this.grab_focus ();
798+ this.set_cursor (path, col, true);
799+ }
800+
801+ return true;
802+ });
803+
804+ cell_edit.accel_edited.connect ((path, key, mods) => {
805+ var shortcut = new Shortcut (key, mods);
806+ change_shortcut (path, shortcut);
807+ });
808+
809+ cell_edit.accel_cleared.connect ((path) => {
810+ change_shortcut (path, (Shortcut) null);
811+ });
812+ }
813+
814+ void load_and_display_shortcuts () {
815+ list.get_group (group, out actions, out schemas, out keys);
816+
817+ var store = new Gtk.ListStore (4, typeof (string), typeof (string),
818+ typeof (Schema), typeof (string));
819+
820+ Gtk.TreeIter iter;
821+
822+ for (int i = 0; i < actions.length; i++) {
823+ var shortcut = settings.get_val(schemas[i], keys[i]);
824+
825+ if (shortcut == null)
826+ continue;
827+
828+ store.append (out iter);
829+ store.set (iter, 0, actions[i],
830+ 1, shortcut.to_readable(),
831+ 2, schemas[i], // hidden
832+ 3, keys[i], -1); // hidden
833+ }
834+
835+ model = store;
836+ }
837+
838+ public bool shortcut_conflicts (Shortcut shortcut, out string name) {
839+ string[] actions, keys;
840+ Schema[] schemas;
841+
842+ list.get_group (group, out actions, out schemas, out keys);
843+
844+ for (int i = 0; i < actions.length; i++) {
845+ if (shortcut.is_equal (settings.get_val (schemas[i], keys[i]))) {
846+ name = actions[i];
847+ return true;
848+ }
849+ }
850+
851+ return false;
852+ }
853+
854+ public void reset_shortcut (Shortcut shortcut) {
855+ string[] actions, keys;
856+ Schema[] schemas;
857+ var empty_shortcut = new Shortcut ();
858+
859+ list.get_group (group, out actions, out schemas, out keys);
860+
861+ for (int i = 0; i < actions.length; i++)
862+ if (shortcut.is_equal (settings.get_val (schemas[i], keys[i])))
863+ settings.set_val (schemas[i], keys[i], empty_shortcut);
864+
865+ load_and_display_shortcuts ();
866+ }
867+
868+ public bool change_shortcut (string path, Shortcut? shortcut) {
869+ Gtk.TreeIter iter;
870+ GLib.Value key, schema, name;
871+
872+ model.get_iter (out iter, new Gtk.TreePath.from_string (path));
873+
874+ model.get_value (iter, 0, out name);
875+ model.get_value (iter, 2, out schema);
876+ model.get_value (iter, 3, out key);
877+
878+ string conflict_name;
879+
880+ if (shortcut != null) {
881+ foreach (var tree in trees) {
882+ if (tree.shortcut_conflicts (shortcut, out conflict_name) == false)
883+ continue;
884+
885+ var dialog = new ConflictDialog (shortcut.to_readable (), conflict_name, (string) name);
886+ dialog.reassign.connect (() => {
887+ tree.reset_shortcut (shortcut);
888+ settings.set_val ((Schema) schema, (string) key, shortcut);
889+ load_and_display_shortcuts ();
890+ });
891+ dialog.show ();
892+ return false;
893+ }
894+ }
895+
896+ settings.set_val ((Schema) schema, (string) key, shortcut ?? new Shortcut ());
897+ load_and_display_shortcuts ();
898+ return true;
899+ }
900+ }
901 }
902\ No newline at end of file
903
904=== modified file 'src/Pages/shortcuts.vala'
905--- src/Pages/shortcuts.vala 2014-01-06 17:12:27 +0000
906+++ src/Pages/shortcuts.vala 2014-03-30 21:21:15 +0000
907@@ -5,9 +5,18 @@
908 // class to interact with gsettings
909 private Shortcuts.Settings settings;
910 // array of tree views, one for each section
911- private Tree[] trees;
912+ private DisplayTree[] trees;
913
914- private enum SectionID { WINDOWS, WORKSPACES, SCREENSHOTS, APPS, MEDIA, A11Y, COUNT }
915+ private enum SectionID {
916+ WINDOWS,
917+ WORKSPACES,
918+ SCREENSHOTS,
919+ APPS,
920+ MEDIA,
921+ A11Y,
922+ CUSTOM,
923+ COUNT
924+ }
925
926 private string[] section_names;
927
928@@ -27,6 +36,8 @@
929
930 public Page ()
931 {
932+ CustomShortcutSettings.init ();
933+
934 // init public elements
935 section_names = {
936 _("Windows"),
937@@ -34,15 +45,18 @@
938 _("Screenshots"),
939 _("Applications"),
940 _("Media"),
941- _("Universal Access")
942+ _("Universal Access"),
943+ _("Custom")
944 };
945
946 list = new List ();
947 settings = new Shortcuts.Settings ();
948
949- for (int id = 0; id < SectionID.COUNT; id++) {
950+ for (int id = 0; id < SectionID.CUSTOM; id++)
951 trees += new Tree ((SectionID) id);
952- }
953+
954+ if (CustomShortcutSettings.available)
955+ trees += new CustomTree ();
956
957 // private elements
958 var shortcut_display = new ShortcutDisplay (trees);
959@@ -51,9 +65,7 @@
960 this.attach (section_switcher, 0, 0, 1, 1);
961 this.attach (shortcut_display, 1, 0, 2, 1);
962
963- section_switcher.changed.connect ((i) => {
964- shortcut_display.change_selection (i);
965- });
966+ section_switcher.changed.connect (shortcut_display.change_selection);
967 }
968 }
969-}
970\ No newline at end of file
971+}

Subscribers

People subscribed via source and target branches

to all changes: