Merge lp:~teemperor/switchboard-plug-keyboard/fix-layouts into lp:~elementary-pantheon/switchboard-plug-keyboard/trunk
- fix-layouts
- Merge into trunk
Proposed by
Raphael Isemann
Status: | Merged | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Approved by: | Corentin Noël | ||||||||||||||||
Approved revision: | 207 | ||||||||||||||||
Merged at revision: | 206 | ||||||||||||||||
Proposed branch: | lp:~teemperor/switchboard-plug-keyboard/fix-layouts | ||||||||||||||||
Merge into: | lp:~elementary-pantheon/switchboard-plug-keyboard/trunk | ||||||||||||||||
Diff against target: |
1315 lines (+635/-485) 6 files modified
src/Pages/Layout/add_layout.vala (+2/-2) src/Pages/Layout/display.vala (+205/-224) src/Pages/Layout/handler.vala (+33/-34) src/Pages/Layout/settings.vala (+370/-154) src/Pages/layout.vala (+15/-51) src/keyboard.vala (+10/-20) |
||||||||||||||||
To merge this branch: | bzr merge lp:~teemperor/switchboard-plug-keyboard/fix-layouts | ||||||||||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
ilian (community) | Approve | ||
Corentin Noël | Approve | ||
Review via email: mp+230324@code.launchpad.net |
Commit message
Fix the layout panel
Description of the change
Ok, we merge that or we get just flooded with bug-reports as the plug currently doesn't work at all.
To post a comment you must log in.
Revision history for this message
ilian (9-ilian) wrote : | # |
I`ve tryed to disable or fully remove Ibus, changing stuff in 'desktop.
Also edited "org.gnome.
Same with "org.gnome.
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'src/Pages/Layout/add_layout.vala' |
2 | --- src/Pages/Layout/add_layout.vala 2014-01-06 17:12:27 +0000 |
3 | +++ src/Pages/Layout/add_layout.vala 2014-08-11 14:54:46 +0000 |
4 | @@ -1,7 +1,7 @@ |
5 | -namespace Pantheon.Keyboard.Layout |
6 | +namespace Pantheon.Keyboard.LayoutPage |
7 | { |
8 | // pop over widget to add a new keyboard layout |
9 | - class AddLayout : Gtk.Dialog//Granite.Widgets.PopOver |
10 | + class AddLayout : Gtk.Dialog |
11 | { |
12 | public signal void layout_added (int language, int layout = 0); |
13 | |
14 | |
15 | === modified file 'src/Pages/Layout/display.vala' |
16 | --- src/Pages/Layout/display.vala 2014-01-06 17:12:27 +0000 |
17 | +++ src/Pages/Layout/display.vala 2014-08-11 14:54:46 +0000 |
18 | @@ -1,226 +1,207 @@ |
19 | -namespace Pantheon.Keyboard.Layout |
20 | +namespace Pantheon.Keyboard.LayoutPage |
21 | { |
22 | - // widget to display/add/remove/move keyboard layouts |
23 | - // interacts with class SettingsLayout |
24 | - class Display : Gtk.Grid |
25 | - { |
26 | - private signal void update_buttons (); |
27 | - |
28 | - private SettingsLayouts settings; |
29 | - private Gtk.TreeView tree; |
30 | - |
31 | - public Display () |
32 | - { |
33 | - settings = new SettingsLayouts (); |
34 | - var list = make_list_store (); |
35 | - tree = new Gtk.TreeView.with_model (list); |
36 | - var cell = new Gtk.CellRendererText (); |
37 | - |
38 | - tree.insert_column_with_attributes (-1, null, cell, "text", 0); |
39 | - tree.headers_visible = false; |
40 | - tree.expand = true; |
41 | - |
42 | - var scroll = new Gtk.ScrolledWindow(null, null); |
43 | - scroll.hscrollbar_policy = Gtk.PolicyType.AUTOMATIC; |
44 | - scroll.vscrollbar_policy = Gtk.PolicyType.AUTOMATIC; |
45 | - scroll.shadow_type = Gtk.ShadowType.IN; |
46 | - scroll.add(tree); |
47 | - scroll.expand = true; |
48 | - |
49 | - var tbar = new Gtk.Toolbar(); |
50 | - tbar.set_style(Gtk.ToolbarStyle.ICONS); |
51 | - tbar.set_icon_size(Gtk.IconSize.SMALL_TOOLBAR); |
52 | - tbar.set_show_arrow(false); |
53 | - tbar.hexpand = true; |
54 | - |
55 | - scroll.get_style_context().set_junction_sides(Gtk.JunctionSides.BOTTOM); |
56 | - tbar.get_style_context().add_class(Gtk.STYLE_CLASS_INLINE_TOOLBAR); |
57 | - tbar.get_style_context().set_junction_sides(Gtk.JunctionSides.TOP); |
58 | - |
59 | - var add_button = new Gtk.ToolButton (null, _("Add…")); |
60 | - var remove_button = new Gtk.ToolButton (null, _("Remove")); |
61 | - var up_button = new Gtk.ToolButton (null, _("Move up")); |
62 | - var down_button = new Gtk.ToolButton (null, _("Move down")); |
63 | - |
64 | - add_button.set_tooltip_text (_("Add…")); |
65 | - remove_button.set_tooltip_text (_("Remove")); |
66 | - up_button.set_tooltip_text (_("Move up")); |
67 | - down_button.set_tooltip_text (_("Move down")); |
68 | - |
69 | - add_button.set_icon_name ("list-add-symbolic"); |
70 | - remove_button.set_icon_name ("list-remove-symbolic"); |
71 | - up_button.set_icon_name ("go-up-symbolic"); |
72 | - down_button.set_icon_name ("go-down-symbolic"); |
73 | - |
74 | - remove_button.sensitive = false; |
75 | - up_button.sensitive = false; |
76 | - down_button.sensitive = false; |
77 | - |
78 | - tbar.insert (add_button, -1); |
79 | - tbar.insert (remove_button, -1); |
80 | - tbar.insert (up_button, -1); |
81 | - tbar.insert (down_button, -1); |
82 | - |
83 | - this.attach (scroll, 0, 0, 1, 1); |
84 | - this.attach (tbar, 0, 1, 1, 1); |
85 | - |
86 | - var pop = new AddLayout (); |
87 | - |
88 | - add_button.clicked.connect( () => { |
89 | - // uncomment when reverting to popover |
90 | - //pop.move_to_widget (add_button); |
91 | - // and remove this line |
92 | - pop.show_all (); |
93 | - add_item (tree, pop); |
94 | - } ); |
95 | - |
96 | - remove_button.clicked.connect( () => { |
97 | - remove_item (tree); |
98 | - update_buttons (); |
99 | - } ); |
100 | - |
101 | - up_button.clicked.connect (() => { |
102 | - move_item (tree, 0); |
103 | - update_buttons (); |
104 | - } ); |
105 | - |
106 | - down_button.clicked.connect (() => { |
107 | - move_item (tree, 1); |
108 | - update_buttons (); |
109 | - } ); |
110 | - |
111 | - tree.cursor_changed.connect (() => { |
112 | - update_buttons (); |
113 | - } ); |
114 | - |
115 | - this.update_buttons.connect (() => |
116 | - { |
117 | - Gtk.TreePath path; |
118 | - |
119 | - tree.get_cursor (out path, null); |
120 | - |
121 | - if (path == null) |
122 | - { |
123 | - up_button.sensitive = false; |
124 | - down_button.sensitive = false; |
125 | - remove_button.sensitive = false; |
126 | - return; |
127 | - } |
128 | - |
129 | - int index = (path.get_indices ())[0]; |
130 | - int count = settings.layouts.length - 1; |
131 | - |
132 | - up_button.sensitive = (index != 0); |
133 | - down_button.sensitive = (index != count); |
134 | - remove_button.sensitive = (count > 0); |
135 | - } ); |
136 | - } |
137 | - |
138 | - private Gtk.ListStore make_list_store () |
139 | - { |
140 | - Gtk.ListStore list_store = new Gtk.ListStore (3, typeof (string), typeof(uint), typeof(uint)); |
141 | - Gtk.TreeIter iter; |
142 | - |
143 | - uint layout = 0, variant = 0; |
144 | - |
145 | - foreach (string item in settings.layouts) |
146 | - { |
147 | - handler.from_code (item, out layout, out variant); |
148 | - item = handler.get_name (layout, variant); |
149 | - |
150 | - list_store.append (out iter); |
151 | - list_store.set (iter, 0, item); |
152 | - list_store.set (iter, 1, layout); |
153 | - list_store.set (iter, 2, variant); |
154 | - } |
155 | - |
156 | - return list_store; |
157 | - } |
158 | - |
159 | - public void reset_all () |
160 | - { |
161 | - settings.reset_all (); |
162 | - tree.model = make_list_store (); |
163 | - update_buttons (); |
164 | - } |
165 | - |
166 | - void add_item (Gtk.TreeView tree, Layout.AddLayout pop) |
167 | - { |
168 | - pop.layout_added.connect ((layout, variant) => |
169 | - { |
170 | - Gtk.TreeIter iter; |
171 | - |
172 | - var name = handler.get_name (layout, variant); |
173 | - var code = handler.get_code (layout, variant); |
174 | - var list = tree.model as Gtk.ListStore; |
175 | - |
176 | - if (settings.add_layout (code)) |
177 | - { |
178 | - list.append (out iter); |
179 | - list.set (iter, 0, name); |
180 | - list.set (iter, 1, layout); |
181 | - list.set (iter, 2, variant); |
182 | - |
183 | - tree.set_cursor (list.get_path(iter), null, false); |
184 | - update_buttons (); |
185 | - } |
186 | - } ); |
187 | - } |
188 | - |
189 | - void remove_item (Gtk.TreeView tree) |
190 | - { |
191 | - Gtk.TreeModel model; |
192 | - Gtk.TreeIter iter; |
193 | - |
194 | - var select = tree.get_selection(); |
195 | - select.get_selected (out model, out iter); |
196 | - |
197 | - GLib.Value layout, variant; |
198 | - model.get_value (iter, 1, out layout); |
199 | - model.get_value (iter, 2, out variant); |
200 | - |
201 | - settings.remove_layout (handler.get_code ((uint)layout, (uint)variant)); |
202 | - stdout.printf ("%s\n", handler.get_code ((uint)layout, (uint)variant)); |
203 | - (model as Gtk.ListStore).remove(iter); |
204 | - } |
205 | - |
206 | - void move_item (Gtk.TreeView tree, int dir) |
207 | - { |
208 | - Gtk.TreeModel model; |
209 | - Gtk.TreeIter iter_current, iter_new; |
210 | - Gtk.TreePath path_current; |
211 | - |
212 | - var select = tree.get_selection(); |
213 | - select.get_selected (out model, out iter_current); |
214 | - path_current = model.get_path (iter_current); |
215 | - |
216 | - iter_new = iter_current; |
217 | - |
218 | - var store = model as Gtk.ListStore; |
219 | - |
220 | - switch (dir) |
221 | - { |
222 | - case 1: if (model.iter_next (ref iter_new) == false) |
223 | - return; |
224 | - break; |
225 | - case 0: if (model.iter_previous (ref iter_new) == false) |
226 | - return; |
227 | - break; |
228 | - } |
229 | - |
230 | - store.swap (iter_current, iter_new); |
231 | - |
232 | - tree.set_cursor (model.get_path (iter_current), null, false); |
233 | - |
234 | - switch (dir) |
235 | - { |
236 | - case 1: |
237 | - settings.layout_down ((path_current.get_indices()) [0]); |
238 | - break; |
239 | - case 0: |
240 | - settings.layout_up ((path_current.get_indices()) [0]); |
241 | - break; |
242 | - } |
243 | - } |
244 | - } |
245 | + // widget to display/add/remove/move keyboard layouts |
246 | + class Display : Gtk.Grid |
247 | + { |
248 | + |
249 | + LayoutSettings settings; |
250 | + Gtk.TreeView tree; |
251 | + Gtk.ToolButton up_button; |
252 | + Gtk.ToolButton down_button; |
253 | + Gtk.ToolButton add_button; |
254 | + Gtk.ToolButton remove_button; |
255 | + |
256 | + /* |
257 | + * Set to true when the user has just clicked on the list to prevent |
258 | + * that settings.layouts.active_changed triggers update_cursor |
259 | + */ |
260 | + bool cursor_changing = false; |
261 | + |
262 | + public Display () |
263 | + { |
264 | + settings = LayoutSettings.get_instance (); |
265 | + |
266 | + tree = new Gtk.TreeView (); |
267 | + var cell = new Gtk.CellRendererText (); |
268 | + |
269 | + tree.insert_column_with_attributes (-1, null, cell, "text", 0); |
270 | + tree.headers_visible = false; |
271 | + tree.expand = true; |
272 | + rebuild_list (); |
273 | + |
274 | + var scroll = new Gtk.ScrolledWindow(null, null); |
275 | + scroll.hscrollbar_policy = Gtk.PolicyType.AUTOMATIC; |
276 | + scroll.vscrollbar_policy = Gtk.PolicyType.AUTOMATIC; |
277 | + scroll.shadow_type = Gtk.ShadowType.IN; |
278 | + scroll.add (tree); |
279 | + scroll.expand = true; |
280 | + |
281 | + var tbar = new Gtk.Toolbar(); |
282 | + tbar.set_style(Gtk.ToolbarStyle.ICONS); |
283 | + tbar.set_icon_size(Gtk.IconSize.SMALL_TOOLBAR); |
284 | + tbar.set_show_arrow(false); |
285 | + tbar.hexpand = true; |
286 | + |
287 | + scroll.get_style_context().set_junction_sides(Gtk.JunctionSides.BOTTOM); |
288 | + tbar.get_style_context().add_class(Gtk.STYLE_CLASS_INLINE_TOOLBAR); |
289 | + tbar.get_style_context().set_junction_sides(Gtk.JunctionSides.TOP); |
290 | + |
291 | + add_button = new Gtk.ToolButton (null, _("Add…")); |
292 | + remove_button = new Gtk.ToolButton (null, _("Remove")); |
293 | + up_button = new Gtk.ToolButton (null, _("Move up")); |
294 | + down_button = new Gtk.ToolButton (null, _("Move down")); |
295 | + |
296 | + add_button.set_tooltip_text (_("Add…")); |
297 | + remove_button.set_tooltip_text (_("Remove")); |
298 | + up_button.set_tooltip_text (_("Move up")); |
299 | + down_button.set_tooltip_text (_("Move down")); |
300 | + |
301 | + add_button.set_icon_name ("list-add-symbolic"); |
302 | + remove_button.set_icon_name ("list-remove-symbolic"); |
303 | + up_button.set_icon_name ("go-up-symbolic"); |
304 | + down_button.set_icon_name ("go-down-symbolic"); |
305 | + |
306 | + remove_button.sensitive = false; |
307 | + up_button.sensitive = false; |
308 | + down_button.sensitive = false; |
309 | + |
310 | + tbar.insert (add_button, -1); |
311 | + tbar.insert (remove_button, -1); |
312 | + tbar.insert (up_button, -1); |
313 | + tbar.insert (down_button, -1); |
314 | + |
315 | + this.attach (scroll, 0, 0, 1, 1); |
316 | + this.attach (tbar, 0, 1, 1, 1); |
317 | + |
318 | + var pop = new AddLayout (); |
319 | + |
320 | + add_button.clicked.connect( () => { |
321 | + // uncomment when reverting to popover |
322 | + //pop.move_to_widget (add_button); |
323 | + // and remove this line |
324 | + pop.show_all (); |
325 | + add_item (pop); |
326 | + }); |
327 | + |
328 | + remove_button.clicked.connect( () => { |
329 | + remove_item (); |
330 | + }); |
331 | + |
332 | + up_button.clicked.connect (() => { |
333 | + settings.layouts.move_active_layout_up (); |
334 | + rebuild_list (); |
335 | + }); |
336 | + |
337 | + down_button.clicked.connect (() => { |
338 | + settings.layouts.move_active_layout_down (); |
339 | + rebuild_list (); |
340 | + }); |
341 | + |
342 | + tree.cursor_changed.connect (() => { |
343 | + cursor_changing = true; |
344 | + int new_index = get_cursor_index (); |
345 | + if (new_index != -1) { |
346 | + settings.layouts.active = new_index; |
347 | + } |
348 | + update_buttons (); |
349 | + |
350 | + cursor_changing = false; |
351 | + }); |
352 | + |
353 | + settings.layouts.active_changed.connect (() => { |
354 | + if (cursor_changing) |
355 | + return; |
356 | + update_cursor (); |
357 | + }); |
358 | + update_cursor (); |
359 | + } |
360 | + |
361 | + public void reset_all () |
362 | + { |
363 | + settings.reset_all (); |
364 | + rebuild_list (); |
365 | + } |
366 | + |
367 | + void update_buttons () { |
368 | + int index = get_cursor_index (); |
369 | + |
370 | + // if empty list |
371 | + if (index == -1) |
372 | + { |
373 | + up_button.sensitive = false; |
374 | + down_button.sensitive = false; |
375 | + remove_button.sensitive = false; |
376 | + } else { |
377 | + up_button.sensitive = (index != 0); |
378 | + down_button.sensitive = (index != settings.layouts.length - 1); |
379 | + remove_button.sensitive = (settings.layouts.length > 0); |
380 | + } |
381 | + } |
382 | + |
383 | + /** |
384 | + * Returns the index of the selected layout in the UI. |
385 | + * In case the list contains no layouts, it returns -1. |
386 | + */ |
387 | + int get_cursor_index () { |
388 | + Gtk.TreePath path; |
389 | + |
390 | + tree.get_cursor (out path, null); |
391 | + |
392 | + if (path == null) |
393 | + { |
394 | + return -1; |
395 | + } |
396 | + |
397 | + return (path.get_indices ())[0]; |
398 | + } |
399 | + |
400 | + void update_cursor () { |
401 | + Gtk.TreePath path = new Gtk.TreePath.from_indices (settings.layouts.active); |
402 | + tree.set_cursor (path, null, false); |
403 | + } |
404 | + |
405 | + Gtk.ListStore build_store () { |
406 | + Gtk.ListStore list_store = new Gtk.ListStore (3, typeof (string), typeof(uint), typeof(uint)); |
407 | + Gtk.TreeIter iter; |
408 | + |
409 | + uint layout = 0, variant = 0; |
410 | + |
411 | + for (uint i = 0; i < settings.layouts.length; i++) { |
412 | + string item = settings.layouts.get_layout (i).name; |
413 | + handler.from_code (item, out layout, out variant); |
414 | + item = handler.get_name (layout, variant); |
415 | + |
416 | + list_store.append (out iter); |
417 | + list_store.set (iter, 0, item); |
418 | + list_store.set (iter, 1, layout); |
419 | + list_store.set (iter, 2, variant); |
420 | + } |
421 | + return list_store; |
422 | + } |
423 | + |
424 | + void rebuild_list () { |
425 | + tree.model = build_store (); |
426 | + update_cursor (); |
427 | + update_buttons (); |
428 | + } |
429 | + |
430 | + void remove_item () |
431 | + { |
432 | + settings.layouts.remove_active_layout (); |
433 | + rebuild_list (); |
434 | + } |
435 | + |
436 | + void add_item (LayoutPage.AddLayout pop) |
437 | + { |
438 | + pop.layout_added.connect ((layout, variant) => { |
439 | + |
440 | + var name = handler.get_name (layout, variant); |
441 | + var code = handler.get_code (layout, variant); |
442 | + |
443 | + // TODO variant |
444 | + settings.layouts.add_layout (new Layout.XKB (code, "")); |
445 | + rebuild_list (); |
446 | + }); |
447 | + } |
448 | + } |
449 | } |
450 | \ No newline at end of file |
451 | |
452 | === modified file 'src/Pages/Layout/handler.vala' |
453 | --- src/Pages/Layout/handler.vala 2014-01-06 17:12:27 +0000 |
454 | +++ src/Pages/Layout/handler.vala 2014-08-11 14:54:46 +0000 |
455 | @@ -1,85 +1,84 @@ |
456 | -namespace Pantheon.Keyboard.Layout |
457 | +namespace Pantheon.Keyboard.LayoutPage |
458 | { |
459 | // class that parses the layout file, provides lists of languages and |
460 | - // variants, and converts between layout names and their gsettings codes |
461 | + // variants, and converts between layout names and their gsettings codes |
462 | class LayoutHandler : GLib.Object |
463 | { |
464 | - Layout[] layouts; |
465 | - |
466 | + InternalLayout[] layouts; |
467 | + |
468 | private string[] names; |
469 | private string[] codes; |
470 | - |
471 | + |
472 | public LayoutHandler () |
473 | { |
474 | foreach (var l in parse_layouts ()) |
475 | { |
476 | var parts = l.split(":", 2); |
477 | - |
478 | + |
479 | names += parts[0]; |
480 | codes += parts[1]; |
481 | - |
482 | - layouts += new Layout (parse_variants (l)); |
483 | + |
484 | + layouts += new InternalLayout (parse_variants (l)); |
485 | } |
486 | } |
487 | - |
488 | + |
489 | public string[] get_layouts () { |
490 | return names; |
491 | } |
492 | - |
493 | + |
494 | public string[] get_variants (uint index) { |
495 | return layouts[index].names; |
496 | } |
497 | - |
498 | + |
499 | public string get_code (uint l, uint v) |
500 | { |
501 | if (v != 0) |
502 | - return codes[l] + "\t" + layouts[l].codes[v]; |
503 | + return codes[l] + "+" + layouts[l].codes[v]; |
504 | return codes[l]; |
505 | } |
506 | - |
507 | + |
508 | public string get_name (uint l, uint v) |
509 | { |
510 | if (v != 0) |
511 | return layouts[l].names[v]; |
512 | return names[l]; |
513 | } |
514 | - |
515 | + |
516 | public bool from_code (string code, out uint l, out uint v) |
517 | { |
518 | - var parts = code.split("\t", 2); |
519 | - |
520 | + var parts = code.split("+", 2); |
521 | + |
522 | l = v = 0; |
523 | - |
524 | + |
525 | if (parts[0] == null) return false; |
526 | - |
527 | + |
528 | while (codes[l] != parts[0]) |
529 | if (l++ > codes.length) |
530 | return false; |
531 | - |
532 | + |
533 | if (parts[1] == null) return true; |
534 | - |
535 | + |
536 | while (layouts[l].codes[v] != parts[1]) |
537 | if (v++ > layouts[l].codes.length) |
538 | return false; |
539 | - |
540 | + |
541 | return true; |
542 | } |
543 | |
544 | // private class that contains the variants of one language |
545 | - private class Layout : GLib.Object |
546 | - { |
547 | + private class InternalLayout : GLib.Object { |
548 | public string[] names; |
549 | public string[] codes; |
550 | |
551 | - public Layout( string[] variants ) |
552 | + public InternalLayout (string[] variants ) |
553 | { |
554 | names += _("Default"); |
555 | codes += ""; |
556 | - |
557 | + |
558 | foreach (var v in variants) |
559 | { |
560 | var parts = v.split(":", 2); |
561 | - |
562 | + |
563 | names += parts[0]; |
564 | codes += parts[1]; |
565 | } |
566 | @@ -90,7 +89,7 @@ |
567 | private string[]? parse_layouts () |
568 | { |
569 | string[] return_val = null; |
570 | - |
571 | + |
572 | var file = File.new_for_path (Build.PKGDATADIR + "/layouts.txt"); |
573 | |
574 | if (!file.query_exists ()) { |
575 | @@ -100,23 +99,23 @@ |
576 | |
577 | try { |
578 | var dis = new DataInputStream (file.read ()); |
579 | - |
580 | + |
581 | string line; |
582 | - |
583 | + |
584 | while ((line = dis.read_line (null)) != null) |
585 | if( "#" in line ) |
586 | return_val += line.replace ("#", ""); |
587 | } catch (Error e) { |
588 | error ("%s", e.message); |
589 | } |
590 | - |
591 | + |
592 | return return_val; |
593 | } |
594 | |
595 | private string[]? parse_variants (string language) |
596 | { |
597 | string[] return_val = null; |
598 | - |
599 | + |
600 | var file = File.new_for_path (Build.PKGDATADIR + "/layouts.txt"); |
601 | |
602 | if (!file.query_exists ()) { |
603 | @@ -126,11 +125,11 @@ |
604 | |
605 | try { |
606 | var dis = new DataInputStream (file.read ()); |
607 | - |
608 | + |
609 | string line; |
610 | - |
611 | + |
612 | while ((line = dis.read_line (null)) != null) { |
613 | - if (line == "#"+language) { |
614 | + if (line == "#" + language) { |
615 | while ((line = dis.read_line (null)) != null) { |
616 | if( "#" in line ) break; |
617 | return_val += line; |
618 | |
619 | === modified file 'src/Pages/Layout/settings.vala' |
620 | --- src/Pages/Layout/settings.vala 2014-01-06 17:12:27 +0000 |
621 | +++ src/Pages/Layout/settings.vala 2014-08-11 14:54:46 +0000 |
622 | @@ -1,156 +1,372 @@ |
623 | -namespace Pantheon.Keyboard.Layout |
624 | +namespace Pantheon.Keyboard.LayoutPage |
625 | { |
626 | - // class to interact with gsettings and add/remove keyboard layouts |
627 | - class SettingsLayouts : Granite.Services.Settings |
628 | - { |
629 | - public string[] layouts { get; set; } |
630 | - |
631 | - public void validate () |
632 | - { |
633 | - string[] copy = {}; |
634 | - foreach (string str in layouts) |
635 | - { |
636 | - //if (handler.valid_code(str)) |
637 | - copy += str; |
638 | - } |
639 | - layouts = copy; |
640 | - } |
641 | - |
642 | - public bool add_layout (string layout) |
643 | - { |
644 | - foreach (string str in layouts) |
645 | - { |
646 | - if (str == layout) |
647 | - return false; |
648 | - } |
649 | - string[] val = layouts; |
650 | - val += layout; |
651 | - layouts = val; |
652 | - return true; |
653 | - } |
654 | - |
655 | - public void remove_layout (string layout) |
656 | - { |
657 | - string[] val = {}; |
658 | - |
659 | - foreach (string str in layouts) |
660 | - { |
661 | - if (str != layout) |
662 | - val += str; |
663 | - } |
664 | - layouts = val; |
665 | - } |
666 | - |
667 | - public void layout_up (int i) |
668 | - { |
669 | - var l = layouts; |
670 | - var tmp = l[i]; |
671 | - l[i] = l[i-1]; |
672 | - l[i-1] = tmp; |
673 | - layouts = l; |
674 | - } |
675 | - |
676 | - public void layout_down (int i) |
677 | - { |
678 | - var l = layouts; |
679 | - var tmp = l[i]; |
680 | - l[i] = l[i+1]; |
681 | - l[i+1] = tmp; |
682 | - layouts = l; |
683 | - } |
684 | - |
685 | - public string[]? parse_default () |
686 | - { |
687 | - string[] return_val = null; |
688 | - |
689 | - var file = File.new_for_path ("/etc/default/keyboard"); |
690 | - |
691 | - if (!file.query_exists ()) |
692 | - { |
693 | - warning ("File '%s' doesn't exist.\n", file.get_path ()); |
694 | - return null; |
695 | - } |
696 | - |
697 | - string xkb_layout = ""; |
698 | - string xkb_variant = ""; |
699 | - |
700 | - try |
701 | - { |
702 | - var dis = new DataInputStream (file.read ()); |
703 | - |
704 | - string line; |
705 | - |
706 | - while ((line = dis.read_line (null)) != null) |
707 | - { |
708 | - if (line.contains ("XKBLAYOUT=")) |
709 | - { |
710 | - xkb_layout = line.replace ("XKBLAYOUT=", "").replace ("\"", ""); |
711 | - |
712 | - while ((line = dis.read_line (null)) != null) { |
713 | - if (line.contains ("XKBVARIANT=")) { |
714 | - xkb_variant = line.replace ("XKBVARIANT=", "").replace ("\"", ""); |
715 | - } |
716 | - } |
717 | - |
718 | - break; |
719 | - } |
720 | - } |
721 | - } |
722 | - |
723 | - catch (Error e) |
724 | - { |
725 | - error ("%s", e.message); |
726 | - } |
727 | - |
728 | - var variants = xkb_variant.split (","); |
729 | - var layouts = xkb_layout.split (","); |
730 | - |
731 | - for (int i = 0; i < layouts.length; i++) |
732 | - { |
733 | - if (variants[i] != null && variants[i] != "") |
734 | - return_val += layouts[i] + "\t" + variants[i]; |
735 | - else |
736 | - return_val += layouts[i]; |
737 | - } |
738 | - |
739 | - return return_val; |
740 | - } |
741 | - |
742 | - public void reset_all () |
743 | - { |
744 | - layouts = parse_default (); |
745 | - validate (); |
746 | - reverted (); |
747 | - } |
748 | - |
749 | - public signal void reverted (); |
750 | - |
751 | - public SettingsLayouts() |
752 | - { |
753 | - base ("org.gnome.libgnomekbd.keyboard"); |
754 | - |
755 | - if (layouts == null || layouts.length == 0) |
756 | - layouts = parse_default (); |
757 | - |
758 | - validate (); |
759 | - } |
760 | - } |
761 | - |
762 | - // allows for multiple kayboard layouts at a time |
763 | - class SettingsGroups : Granite.Services.Settings |
764 | - { |
765 | - public int default_group { get; set; } |
766 | - public bool group_per_window { get; set; } |
767 | - |
768 | - public void reset_all () |
769 | - { |
770 | - schema.reset ("default-group"); |
771 | - schema.reset ("group-per-window"); |
772 | - } |
773 | - |
774 | - public SettingsGroups() |
775 | - { |
776 | - base ("org.gnome.libgnomekbd.desktop"); |
777 | - } |
778 | - } |
779 | + |
780 | + /** |
781 | + * Type of a keyboard-layout as described in the description of |
782 | + * "org.gnome.desktop.input-sources sources". |
783 | + */ |
784 | + enum LayoutType { IBUS, XKB } |
785 | + |
786 | + /** |
787 | + * Immutable class that respresents a keyboard-layout according to |
788 | + * "org.gnome.desktop.input-sources sources". |
789 | + * This means that the enum parameter @layout_type equals the first string in the |
790 | + * tupel of strings, and the @name parameter equals the second string. |
791 | + */ |
792 | + class Layout { |
793 | + |
794 | + public LayoutType layout_type { get; private set; } |
795 | + public string name { get; private set; } |
796 | + |
797 | + public Layout (LayoutType layout_type, string name) { |
798 | + this.layout_type = layout_type; |
799 | + this.name = name; |
800 | + } |
801 | + |
802 | + public Layout.XKB (string layout, string? variant) { |
803 | + string full_name = layout; |
804 | + if (variant != null && variant != "") |
805 | + full_name += "+" + variant; |
806 | + this (LayoutType.XKB, full_name); |
807 | + } |
808 | + |
809 | + public Layout.from_variant (GLib.Variant variant) { |
810 | + if (variant.is_of_type (new VariantType ("(ss)"))) { |
811 | + unowned string type; |
812 | + unowned string name; |
813 | + |
814 | + variant.get ("(&s&s)", out type, out name); |
815 | + |
816 | + if (type == "xkb") { |
817 | + layout_type = LayoutType.XKB; |
818 | + } else if (type == "ibus") { |
819 | + layout_type = LayoutType.IBUS; |
820 | + } else { |
821 | + warning ("Unkown type %s", type); |
822 | + } |
823 | + this.name = name; |
824 | + |
825 | + } else { |
826 | + warning ("Variant has invalid type"); |
827 | + } |
828 | + } |
829 | + |
830 | + public bool equal (Layout other) { |
831 | + return this.layout_type == other.layout_type && this.name == other.name; |
832 | + } |
833 | + |
834 | + /** |
835 | + * GSettings saves values in the form of GLib.Variant and this |
836 | + * function creates a Variant representing this object. |
837 | + */ |
838 | + public GLib.Variant to_variant () { |
839 | + string type_name = ""; |
840 | + switch (layout_type) { |
841 | + case LayoutType.IBUS: |
842 | + type_name = "ibus"; |
843 | + break; |
844 | + case LayoutType.XKB: |
845 | + type_name = "xkb"; |
846 | + break; |
847 | + default: |
848 | + error ("You need to implemnt this for all possible values of" |
849 | + + "the LayoutType-enum"); |
850 | + } |
851 | + GLib.Variant first = new GLib.Variant.string (type_name); |
852 | + GLib.Variant second = new GLib.Variant.string (name); |
853 | + GLib.Variant result = new GLib.Variant.tuple ({first, second}); |
854 | + |
855 | + return result; |
856 | + } |
857 | + |
858 | + } |
859 | + |
860 | + /** |
861 | + * Represents a list of layouts. |
862 | + */ |
863 | + class LayoutList : Object { |
864 | + |
865 | + GLib.List<Layout> layouts = new GLib.List<Layout> (); |
866 | + |
867 | + // signals |
868 | + public signal void layouts_changed (); |
869 | + public signal void active_changed (); |
870 | + |
871 | + public uint length { |
872 | + get { |
873 | + return layouts.length (); |
874 | + } |
875 | + } |
876 | + |
877 | + uint _active; |
878 | + public uint active { |
879 | + get { |
880 | + return _active; |
881 | + } |
882 | + set { |
883 | + if (length == 0) |
884 | + return; |
885 | + |
886 | + if (_active == value) |
887 | + return; |
888 | + |
889 | + _active = value; |
890 | + if (_active >= length) |
891 | + _active = length - 1; |
892 | + active_changed (); |
893 | + } |
894 | + |
895 | + } |
896 | + |
897 | + public bool contains_layout (Layout given_layout) { |
898 | + return get_layout_index (given_layout) != -1; |
899 | + } |
900 | + |
901 | + public int get_layout_index (Layout given_layout) { |
902 | + int i = 0; |
903 | + foreach (Layout l in layouts) { |
904 | + if (l.equal (given_layout)) |
905 | + return i; |
906 | + i++; |
907 | + } |
908 | + return -1; |
909 | + } |
910 | + |
911 | + private void switch_items (uint pos1, uint pos2) { |
912 | + unowned List<Layout> container1 = layouts.nth (pos1); |
913 | + unowned List<Layout> container2 = layouts.nth (pos2); |
914 | + Layout tmp = container1.data; |
915 | + container1.data = container2.data; |
916 | + container2.data = tmp; |
917 | + |
918 | + if (active == pos1) |
919 | + active = pos2; |
920 | + else if (active == pos2) |
921 | + active = pos1; |
922 | + |
923 | + layouts_changed (); |
924 | + } |
925 | + |
926 | + public void move_active_layout_up () { |
927 | + if (length == 0) |
928 | + return; |
929 | + |
930 | + // check that the active item is not the first one |
931 | + if (active > 0) { |
932 | + switch_items (active, active - 1); |
933 | + } |
934 | + } |
935 | + |
936 | + public void move_active_layout_down () { |
937 | + if (length == 0) |
938 | + return; |
939 | + |
940 | + // check that the active item is not the last one |
941 | + if (active < length - 1) { |
942 | + switch_items (active, active + 1); |
943 | + } |
944 | + } |
945 | + |
946 | + public bool add_layout (Layout new_layout) { |
947 | + if (! contains_layout (new_layout)) { |
948 | + layouts.append (new_layout); |
949 | + layouts_changed (); |
950 | + return true; |
951 | + } |
952 | + return false; |
953 | + } |
954 | + |
955 | + public void remove_active_layout () { |
956 | + layouts.remove (get_layout (active)); |
957 | + |
958 | + if (active >= length) |
959 | + active = length - 1; |
960 | + layouts_changed (); |
961 | + } |
962 | + |
963 | + public void remove_all () { |
964 | + layouts = new GLib.List<Layout> (); |
965 | + layouts_changed (); |
966 | + } |
967 | + |
968 | + /** |
969 | + * This method does not need call layouts_changed in any situation |
970 | + * as a Layout-Object is immutable. |
971 | + */ |
972 | + public Layout? get_layout (uint index) { |
973 | + if (index >= length) |
974 | + return null; |
975 | + |
976 | + return layouts.nth_data (index); |
977 | + } |
978 | + |
979 | + } |
980 | + |
981 | + class LayoutSettings |
982 | + { |
983 | + |
984 | + public LayoutList layouts { get; private set; } |
985 | + |
986 | + GLib.Settings settings; |
987 | + |
988 | + /** |
989 | + * True if and only if we are currently writing to gsettings |
990 | + * by ourselves. |
991 | + */ |
992 | + bool currently_writing; |
993 | + |
994 | + void write_list_to_gsettings () { |
995 | + currently_writing = true; |
996 | + try { |
997 | + Variant[] elements = {}; |
998 | + for (uint i = 0; i < layouts.length; i++) { |
999 | + elements += layouts.get_layout (i).to_variant (); |
1000 | + } |
1001 | + GLib.Variant list = new GLib.Variant.array (new VariantType ("(ss)"), elements); |
1002 | + settings.set_value ("sources", list); |
1003 | + } finally { |
1004 | + currently_writing = false; |
1005 | + } |
1006 | + } |
1007 | + |
1008 | + void write_active_to_gsettings () { |
1009 | + uint active = layouts.active; |
1010 | + settings.set_uint ("current", active); |
1011 | + } |
1012 | + |
1013 | + void update_list_from_gsettings () { |
1014 | + // We currently write to gsettings, so we caused this signal |
1015 | + // and therefore don't need to read the list again from dconf |
1016 | + if (currently_writing) |
1017 | + return; |
1018 | + |
1019 | + GLib.Variant sources = settings.get_value ("sources"); |
1020 | + if (sources.is_of_type (VariantType.ARRAY)) { |
1021 | + for(size_t i = 0; i < sources.n_children (); i++) { |
1022 | + GLib.Variant child = sources.get_child_value (i); |
1023 | + layouts.add_layout (new Layout.from_variant (child)); |
1024 | + } |
1025 | + } else { |
1026 | + warning ("Unkown type"); |
1027 | + } |
1028 | + } |
1029 | + |
1030 | + void update_active_from_gsettings () { |
1031 | + layouts.active = settings.get_uint ("current"); |
1032 | + } |
1033 | + |
1034 | + bool _per_window; |
1035 | + public bool per_window { |
1036 | + get { |
1037 | + return _per_window; |
1038 | + } |
1039 | + set { |
1040 | + if (value != _per_window) { |
1041 | + settings.set_boolean ("per-window", value); |
1042 | + _per_window = value; |
1043 | + } |
1044 | + } |
1045 | + } |
1046 | + // signal when the variable per_window is changed by gsettings |
1047 | + public signal void per_window_changed (); |
1048 | + |
1049 | + public void parse_default () { |
1050 | + var file = File.new_for_path ("/etc/default/keyboard"); |
1051 | + |
1052 | + if (!file.query_exists ()) { |
1053 | + warning ("File '%s' doesn't exist.\n", file.get_path ()); |
1054 | + return; |
1055 | + } |
1056 | + |
1057 | + string xkb_layout = ""; |
1058 | + string xkb_variant = ""; |
1059 | + |
1060 | + try { |
1061 | + var dis = new DataInputStream (file.read ()); |
1062 | + |
1063 | + string line; |
1064 | + |
1065 | + while ((line = dis.read_line (null)) != null) |
1066 | + { |
1067 | + if (line.contains ("XKBLAYOUT=")) |
1068 | + { |
1069 | + xkb_layout = line.replace ("XKBLAYOUT=", "").replace ("\"", ""); |
1070 | + |
1071 | + while ((line = dis.read_line (null)) != null) { |
1072 | + if (line.contains ("XKBVARIANT=")) { |
1073 | + xkb_variant = line.replace ("XKBVARIANT=", "").replace ("\"", ""); |
1074 | + } |
1075 | + } |
1076 | + |
1077 | + break; |
1078 | + } |
1079 | + } |
1080 | + } |
1081 | + catch (Error e) { |
1082 | + warning ("%s", e.message); |
1083 | + return; |
1084 | + } |
1085 | + |
1086 | + var variants = xkb_variant.split (","); |
1087 | + var xkb_layouts = xkb_layout.split (","); |
1088 | + |
1089 | + for (int i = 0; i < layouts.length; i++) { |
1090 | + if (variants[i] != null && variants[i] != "") |
1091 | + layouts.add_layout (new Layout (LayoutType.XKB, xkb_layouts[i] + "+" + variants[i])); |
1092 | + else |
1093 | + layouts.add_layout (new Layout (LayoutType.XKB, xkb_layouts[i])); |
1094 | + } |
1095 | + } |
1096 | + |
1097 | + public void reset_all () |
1098 | + { |
1099 | + layouts.remove_all (); |
1100 | + parse_default (); |
1101 | + } |
1102 | + |
1103 | + // singleton pattern |
1104 | + static LayoutSettings? instance; |
1105 | + public static LayoutSettings get_instance () { |
1106 | + if (instance == null) { |
1107 | + instance = new LayoutSettings (); |
1108 | + } |
1109 | + return instance; |
1110 | + } |
1111 | + |
1112 | + private LayoutSettings () |
1113 | + { |
1114 | + settings = new Settings ("org.gnome.desktop.input-sources"); |
1115 | + layouts = new LayoutList (); |
1116 | + |
1117 | + _per_window = settings.get_boolean ("per-window"); |
1118 | + settings.changed["per-window"].connect (() => { |
1119 | + _per_window = settings.get_boolean ("per-window"); |
1120 | + per_window_changed (); |
1121 | + }); |
1122 | + |
1123 | + update_list_from_gsettings (); |
1124 | + update_active_from_gsettings (); |
1125 | + |
1126 | + layouts.layouts_changed.connect (() => { |
1127 | + write_list_to_gsettings (); |
1128 | + }); |
1129 | + |
1130 | + layouts.active_changed.connect (() => { |
1131 | + write_active_to_gsettings (); |
1132 | + }); |
1133 | + |
1134 | + settings.changed["sources"].connect (() => { |
1135 | + update_list_from_gsettings (); |
1136 | + }); |
1137 | + |
1138 | + settings.changed["current"].connect (() => { |
1139 | + update_active_from_gsettings (); |
1140 | + }); |
1141 | + |
1142 | + if (layouts.length == 0) |
1143 | + parse_default (); |
1144 | + |
1145 | + } |
1146 | + } |
1147 | + |
1148 | } |
1149 | \ No newline at end of file |
1150 | |
1151 | === modified file 'src/Pages/layout.vala' |
1152 | --- src/Pages/layout.vala 2014-01-06 17:12:27 +0000 |
1153 | +++ src/Pages/layout.vala 2014-08-11 14:54:46 +0000 |
1154 | @@ -1,12 +1,12 @@ |
1155 | -namespace Pantheon.Keyboard.Layout |
1156 | +namespace Pantheon.Keyboard.LayoutPage |
1157 | { |
1158 | // global handler |
1159 | LayoutHandler handler; |
1160 | |
1161 | class Page : Pantheon.Keyboard.AbstractPage |
1162 | { |
1163 | - private Layout.Display display; |
1164 | - private SettingsGroups settings; |
1165 | + private LayoutPage.Display display; |
1166 | + private LayoutSettings settings; |
1167 | |
1168 | public override void reset () |
1169 | { |
1170 | @@ -18,18 +18,14 @@ |
1171 | public Page () |
1172 | { |
1173 | handler = new LayoutHandler (); |
1174 | + settings = LayoutSettings.get_instance (); |
1175 | |
1176 | // first some labels |
1177 | var label_1 = new Gtk.Label (_("Allow different layouts for individual windows:")); |
1178 | - var label_2 = new Gtk.Label (_("New windows use:")); |
1179 | |
1180 | label_1.valign = Gtk.Align.CENTER; |
1181 | label_1.halign = Gtk.Align.END; |
1182 | - label_2.valign = Gtk.Align.CENTER; |
1183 | - label_2.halign = Gtk.Align.END; |
1184 | - |
1185 | this.attach (label_1, 4, 0, 1, 1); |
1186 | - this.attach (label_2, 4, 1, 1, 1); |
1187 | |
1188 | // widgets to change settings |
1189 | var switch_main = new Gtk.Switch(); |
1190 | @@ -37,52 +33,20 @@ |
1191 | switch_main.halign = Gtk.Align.START; |
1192 | switch_main.valign = Gtk.Align.CENTER; |
1193 | |
1194 | - var button1 = new Gtk.RadioButton.with_label(null, _("Default layout")); |
1195 | - var button2 = new Gtk.RadioButton.with_label_from_widget (button1, _("Previous window's layout")); |
1196 | - |
1197 | this.attach (switch_main, 5, 0, 1, 1); |
1198 | - this.attach (button1, 5, 1, 1, 1); |
1199 | - this.attach (button2, 5, 2, 1, 1); |
1200 | - |
1201 | - settings = new Layout.SettingsGroups(); |
1202 | - |
1203 | - // connect switch signals |
1204 | - switch_main.active = settings.group_per_window; |
1205 | - |
1206 | - button1.sensitive = button2.sensitive = switch_main.active; |
1207 | - label_2.sensitive = switch_main.active; |
1208 | - |
1209 | - switch_main.notify["active"].connect( () => { |
1210 | - settings.group_per_window = switch_main.active; |
1211 | - button1.sensitive = button2.sensitive = switch_main.active; |
1212 | - label_2.sensitive = switch_main.active; |
1213 | - } ); |
1214 | - |
1215 | - settings.changed["group-per-window"].connect (() => { |
1216 | - switch_main.active = settings.group_per_window; |
1217 | - button1.sensitive = button2.sensitive = switch_main.active; |
1218 | - label_2.sensitive = switch_main.active; |
1219 | - } ); |
1220 | - |
1221 | - // connect radio button signals |
1222 | - if( settings.default_group >= 0 ) |
1223 | - button1.active = true; |
1224 | - else |
1225 | - button2.active = true; |
1226 | - |
1227 | - button1.toggled.connect (() => { settings.default_group = 0; } ); |
1228 | - button2.toggled.connect (() => { settings.default_group = -1; } ); |
1229 | - |
1230 | - settings.changed["default-group"].connect (() => |
1231 | - { |
1232 | - if( settings.default_group >= 0 ) |
1233 | - button1.active = true; |
1234 | - else |
1235 | - button2.active = true; |
1236 | - } ); |
1237 | + |
1238 | + switch_main.active = settings.per_window; |
1239 | + |
1240 | + switch_main.notify["active"].connect(() => { |
1241 | + settings.per_window = switch_main.active; |
1242 | + }); |
1243 | + settings.per_window_changed.connect (() => { |
1244 | + switch_main.active = settings.per_window; |
1245 | + }); |
1246 | + |
1247 | |
1248 | // tree view to display the current layouts |
1249 | - display = new Layout.Display (); |
1250 | + display = new LayoutPage.Display (); |
1251 | |
1252 | this.attach (display, 0, 0, 4, 4); |
1253 | |
1254 | |
1255 | === modified file 'src/keyboard.vala' |
1256 | --- src/keyboard.vala 2014-08-03 01:16:31 +0000 |
1257 | +++ src/keyboard.vala 2014-08-11 14:54:46 +0000 |
1258 | @@ -17,41 +17,31 @@ |
1259 | var stack_switcher = new Gtk.StackSwitcher (); |
1260 | stack_switcher.set_stack (stack); |
1261 | stack_switcher.halign = Gtk.Align.CENTER; |
1262 | - |
1263 | + |
1264 | stack.add_titled (new Keyboard.Shortcuts.Page (), "shortcuts", _("Shortcuts")); |
1265 | stack.add_titled (new Keyboard.Behaviour.Page (), "behavior", _("Behavior")); |
1266 | - stack.add_titled (new Keyboard.Layout.Page (), "layout", _("Layout")); |
1267 | + stack.add_titled (new Keyboard.LayoutPage.Page (), "layout", _("Layout")); |
1268 | stack.add_titled (new Keyboard.Options.Page (), "options", _("Options")); |
1269 | |
1270 | - // button to reset the current page |
1271 | - /*var button = new Gtk.Button.with_label (_("Reset to defaults")); |
1272 | - |
1273 | - button.expand = false; |
1274 | - button.halign = Gtk.Align.END; |
1275 | - |
1276 | - button.clicked.connect (() => { |
1277 | - pages[notebook.page].reset (); |
1278 | - });*/ |
1279 | - |
1280 | grid.attach (stack_switcher, 0, 0, 1, 1); |
1281 | grid.attach (stack, 0, 1, 1, 1); |
1282 | } |
1283 | grid.show_all (); |
1284 | return grid; |
1285 | } |
1286 | - |
1287 | + |
1288 | public override void shown () { |
1289 | - |
1290 | + |
1291 | } |
1292 | - |
1293 | + |
1294 | public override void hidden () { |
1295 | - |
1296 | + |
1297 | } |
1298 | - |
1299 | + |
1300 | public override void search_callback (string location) { |
1301 | - |
1302 | + |
1303 | } |
1304 | - |
1305 | + |
1306 | // 'search' returns results like ("Keyboard → Behavior → Duration", "keyboard<sep>behavior") |
1307 | public override async Gee.TreeMap<string, string> search (string search) { |
1308 | return new Gee.TreeMap<string, string> (null, null); |
1309 | @@ -62,4 +52,4 @@ |
1310 | debug ("Activating Keyboard plug"); |
1311 | var plug = new Pantheon.Keyboard.Plug (); |
1312 | return plug; |
1313 | -} |
1314 | +} |
1315 | \ No newline at end of file |
Even if it doesn't respect ANY elementary codestyle rule, I approve the bug fix