diff options
-rw-r--r-- | data/main.vala | 17 | ||||
-rwxr-xr-x | debian/rules | 2 | ||||
-rw-r--r-- | lib/Makefile.am | 1 | ||||
-rw-r--r-- | lib/common.vala | 16 | ||||
-rw-r--r-- | lib/main.vala | 617 | ||||
-rw-r--r-- | lib/source.vala | 441 | ||||
-rw-r--r-- | po/indicator-keyboard.pot | 8 | ||||
-rw-r--r-- | tests/main.vala | 27 |
8 files changed, 629 insertions, 500 deletions
diff --git a/data/main.vala b/data/main.vala index 6457c61f..8077f093 100644 --- a/data/main.vala +++ b/data/main.vala @@ -91,15 +91,14 @@ int main (string[] args) { info.get_layout_info (name, null, out short_name, null, null); - if (short_name != null) { - var abbreviation = get_abbreviation ((!) short_name); - - if (abbreviation.get_char () != '\0') { - if (!occurrences.has_key (abbreviation)) { - occurrences[abbreviation] = 1; - } else { - occurrences[abbreviation] = occurrences[abbreviation] + 1; - } + var abbreviation = abbreviate (short_name); + var has_abbreviation = abbreviation != null && ((!) abbreviation).get_char () != '\0'; + + if (has_abbreviation) { + if (!occurrences.has_key ((!) abbreviation)) { + occurrences[(!) abbreviation] = 1; + } else { + occurrences[(!) abbreviation] = occurrences[(!) abbreviation] + 1; } } }); diff --git a/debian/rules b/debian/rules index d2bfba7b..a69660c0 100755 --- a/debian/rules +++ b/debian/rules @@ -17,3 +17,5 @@ override_dh_install: find debian/indicator-keyboard/usr/lib -name *.a -delete dh_install --fail-missing +override_dh_auto_test: + dh_auto_test || ( cat tests/test-suite.log ) diff --git a/lib/Makefile.am b/lib/Makefile.am index 169a2de0..cdf41ba2 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -7,6 +7,7 @@ AM_VALAFLAGS = --enable-experimental-non-null \ --vapidir $(top_srcdir)/deps indicator_keyboard_service_SOURCES = main.vala \ + source.vala \ common.vala indicator_keyboard_service_VALAFLAGS = $(AM_VALAFLAGS) \ --pkg gee-1.0 \ diff --git a/lib/common.vala b/lib/common.vala index 4d8b9add..9824bc26 100644 --- a/lib/common.vala +++ b/lib/common.vala @@ -16,18 +16,22 @@ * Authors: William Hua <william.hua@canonical.com> */ -string get_abbreviation (string name) { +string? abbreviate (string? name) { var index = 0; unichar first; unichar second; - if (name.get_next_char (ref index, out first)) { - if (name.get_next_char (ref index, out second)) { - return @"$((!) first.toupper ().to_string ())$((!) second.to_string ())"; + if (name != null) { + if (((!) name).get_next_char (ref index, out first)) { + if (((!) name).get_next_char (ref index, out second)) { + return @"$((!) first.toupper ().to_string ())$((!) second.to_string ())"; + } else { + return first.toupper ().to_string (); + } } else { - return (!) first.toupper ().to_string (); + return ""; } } else { - return ""; + return null; } } diff --git a/lib/main.vala b/lib/main.vala index 5fb251dd..91466f8c 100644 --- a/lib/main.vala +++ b/lib/main.vala @@ -19,78 +19,79 @@ [DBus (name = "com.canonical.indicator.keyboard")] public class Indicator.Keyboard.Service : Object { + private static IBus.Bus? ibus; + + private bool force; private bool use_gtk; private bool use_bamf; + private MainLoop? loop; private Settings indicator_settings; private Settings source_settings; private Settings per_window_settings; - private Gnome.XkbInfo xkb_info; - private IBus.Bus? ibus; private Bamf.Matcher? matcher; private Gee.HashMap<string, uint>? window_sources; + private Source[]? sources; + private SimpleActionGroup? action_group; private SimpleAction? indicator_action; private MenuModel? menu_model; private Menu? sources_menu; - private Icon?[]? icons; - private string[]? icon_strings; - private int[]? icon_string_uniques; - private uint[]? icon_string_subscripts; - [DBus (visible = false)] public Service (ref unowned string[] args) { - Bus.own_name (BusType.SESSION, - "com.canonical.indicator.keyboard", - BusNameOwnerFlags.ALLOW_REPLACEMENT | ("--force" in args ? BusNameOwnerFlags.REPLACE : 0), - this.handle_bus_acquired, - null, - this.handle_name_lost); - - this.use_gtk = "--use-gtk" in args; - this.use_bamf = "--use-bamf" in args; + force = "--force" in args; + use_gtk = "--use-gtk" in args; + use_bamf = "--use-bamf" in args; - if (this.use_gtk) { - this.use_gtk = Gtk.init_check (ref args); + if (use_gtk) { + use_gtk = Gtk.init_check (ref args); } else { Gdk.init (ref args); } - this.indicator_settings = new Settings ("com.canonical.indicator.keyboard"); - this.indicator_settings.changed["visible"].connect (this.handle_changed_visible); - - this.source_settings = new Settings ("org.gnome.desktop.input-sources"); - this.source_settings.changed["current"].connect (this.handle_changed_current); - this.source_settings.changed["sources"].connect (this.handle_changed_sources); + indicator_settings = new Settings ("com.canonical.indicator.keyboard"); + indicator_settings.changed["visible"].connect (handle_changed_visible); - this.per_window_settings = new Settings ("org.gnome.libgnomekbd.desktop"); - this.per_window_settings.changed["group-per-window"].connect (this.handle_changed_group_per_window); + source_settings = new Settings ("org.gnome.desktop.input-sources"); + source_settings.changed["current"].connect (handle_changed_current); + source_settings.changed["sources"].connect (handle_changed_sources); - this.xkb_info = new Gnome.XkbInfo (); + per_window_settings = new Settings ("org.gnome.libgnomekbd.desktop"); + per_window_settings.changed["group-per-window"].connect (handle_changed_group_per_window); migrate_keyboard_layouts (); - update_window_sources (); - - this.loop = new MainLoop (); - ((!) this.loop).run (); + acquire_bus_name (); } [DBus (visible = false)] - private IBus.Bus get_ibus () { - if (this.ibus == null) { + private static IBus.Bus get_ibus () { + if (ibus == null) { IBus.init (); - this.ibus = new IBus.Bus (); + ibus = new IBus.Bus (); } - return (!) this.ibus; + return (!) ibus; + } + + [DBus (visible = false)] + private void acquire_bus_name () { + Bus.own_name (BusType.SESSION, + "com.canonical.indicator.keyboard", + BusNameOwnerFlags.ALLOW_REPLACEMENT | (force ? BusNameOwnerFlags.REPLACE : 0), + handle_bus_acquired, + null, + handle_name_lost); + + loop = new MainLoop (); + ((!) loop).run (); } [DBus (visible = false)] private void migrate_keyboard_layouts () { - if (!this.indicator_settings.get_boolean ("migrated")) { + if (!indicator_settings.get_boolean ("migrated")) { var builder = new VariantBuilder (new VariantType ("a(ss)")); var length = 0; @@ -139,26 +140,26 @@ public class Indicator.Keyboard.Service : Object { } } - this.source_settings.set_value ("sources", builder.end ()); + source_settings.set_value ("sources", builder.end ()); - this.indicator_settings.set_boolean ("migrated", true); + indicator_settings.set_boolean ("migrated", true); } } [DBus (visible = false)] private void update_window_sources () { - if (this.use_bamf) { - var group_per_window = this.per_window_settings.get_boolean ("group-per-window"); + if (use_bamf) { + var group_per_window = per_window_settings.get_boolean ("group-per-window"); - if (group_per_window != (this.window_sources != null)) { + if (group_per_window != (window_sources != null)) { if (group_per_window) { - this.window_sources = new Gee.HashMap<string, uint> (); - this.matcher = Bamf.Matcher.get_default (); - ((!) this.matcher).active_window_changed.connect (this.handle_active_window_changed); + window_sources = new Gee.HashMap<string, uint> (); + matcher = Bamf.Matcher.get_default (); + ((!) matcher).active_window_changed.connect (handle_active_window_changed); } else { - ((!) this.matcher).active_window_changed.disconnect (this.handle_active_window_changed); - this.matcher = null; - this.window_sources = null; + ((!) matcher).active_window_changed.disconnect (handle_active_window_changed); + matcher = null; + window_sources = null; } } } @@ -172,312 +173,74 @@ public class Indicator.Keyboard.Service : Object { [DBus (visible = false)] private void handle_active_window_changed (Bamf.View? old_view, Bamf.View? new_view) { if (old_view != null) { - ((!) this.window_sources)[((!) old_view).path] = this.source_settings.get_uint ("current"); + ((!) window_sources)[((!) old_view).path] = source_settings.get_uint ("current"); } if (new_view != null) { - if (!((!) this.window_sources).has_key (((!) new_view).path)) { - var default_group = this.per_window_settings.get_int ("default-group"); + if (!((!) window_sources).has_key (((!) new_view).path)) { + var default_group = per_window_settings.get_int ("default-group"); if (default_group >= 0) { - this.source_settings.set_uint ("current", (uint) default_group); + source_settings.set_uint ("current", (uint) default_group); } } else { - this.source_settings.set_uint ("current", ((!) this.window_sources)[((!) new_view).path]); - } - } - } - - [DBus (visible = false)] - private Gtk.StyleContext? get_style_context () { - Gtk.StyleContext? context = null; - - if (this.use_gtk) { - Gdk.Screen? screen = Gdk.Screen.get_default (); - - if (screen != null) { - context = new Gtk.StyleContext (); - ((!) context).set_screen ((!) screen); - - var path = new Gtk.WidgetPath (); - path.append_type (typeof (Gtk.MenuItem)); - ((!) context).set_path (path); - } - } - - return context; - } - - [DBus (visible = false)] - protected virtual Icon? create_icon (string? text, uint subscript) { - Icon? icon = null; - - var style = get_style_context (); - - if (style != null) { - const int W = 22; - const int H = 22; - const int w = 20; - const int h = 20; - const double R = 2.0; - const double TEXT_SIZE = 12.0; - const double SUBSCRIPT_SIZE = 8.0; - - Pango.FontDescription description; - var colour = ((!) style).get_color (Gtk.StateFlags.NORMAL); - colour = { 0.5, 0.5, 0.5, 1.0 }; - ((!) style).get (Gtk.StateFlags.NORMAL, Gtk.STYLE_PROPERTY_FONT, out description); - - var surface = new Cairo.ImageSurface (Cairo.Format.ARGB32, W, H); - var context = new Cairo.Context (surface); - - context.translate (0.5 * (W - w), 0.5 * (H - h)); - - context.new_sub_path (); - context.arc (R, R, R, Math.PI, -0.5 * Math.PI); - context.arc (w - R, R, R, -0.5 * Math.PI, 0); - context.arc (w - R, h - R, R, 0, 0.5 * Math.PI); - context.arc (R, h - R, R, 0.5 * Math.PI, Math.PI); - context.close_path (); - - context.set_source_rgba (colour.red, colour.green, colour.blue, colour.alpha); - context.fill (); - context.set_operator (Cairo.Operator.CLEAR); - - if (text != null) { - var text_layout = Pango.cairo_create_layout (context); - text_layout.set_alignment (Pango.Alignment.CENTER); - description.set_absolute_size (Pango.units_from_double (TEXT_SIZE)); - text_layout.set_font_description (description); - text_layout.set_text ((!) text, -1); - Pango.cairo_update_layout (context, text_layout); - int text_width; - int text_height; - text_layout.get_pixel_size (out text_width, out text_height); - - if (subscript > 0) { - var subscript_layout = Pango.cairo_create_layout (context); - subscript_layout.set_alignment (Pango.Alignment.CENTER); - description.set_absolute_size (Pango.units_from_double (SUBSCRIPT_SIZE)); - subscript_layout.set_font_description (description); - subscript_layout.set_text (@"$subscript", -1); - Pango.cairo_update_layout (context, subscript_layout); - int subscript_width; - int subscript_height; - subscript_layout.get_pixel_size (out subscript_width, out subscript_height); - - context.save (); - context.translate ((w - (text_width + subscript_width)) / 2, (h - text_height) / 2); - Pango.cairo_layout_path (context, text_layout); - context.fill (); - context.restore (); - - context.save (); - context.translate ((w + (text_width - subscript_width)) / 2, (h + text_height) / 2 - subscript_height); - Pango.cairo_layout_path (context, subscript_layout); - context.fill (); - context.restore (); - } else { - context.save (); - context.translate ((w - text_width) / 2, (h - text_height) / 2); - Pango.cairo_layout_path (context, text_layout); - context.fill (); - context.restore (); - } - } - - var buffer = new ByteArray (); - - surface.write_to_png_stream ((data) => { - buffer.append (data); - return Cairo.Status.SUCCESS; - }); - - icon = new BytesIcon (ByteArray.free_to_bytes ((owned) buffer)); - } - - return icon; - } - - [DBus (visible = false)] - private string get_icon_string (uint index) { - string? icon_string = null; - - if (this.icon_strings == null) { - var array = this.source_settings.get_value ("sources"); - this.icon_strings = new string[array.n_children ()]; - } - - if (index < ((!) this.icon_strings).length) { - icon_string = this.icon_strings[index]; - - if (icon_string == null) { - var array = this.source_settings.get_value ("sources"); - - string type; - string name; - - array.get_child (index, "(ss)", out type, out name); - - if (type == "xkb") { - string? short_name; - - this.xkb_info.get_layout_info (name, null, out short_name, null, null); - - if (short_name != null) { - this.icon_strings[index] = get_abbreviation ((!) short_name); - icon_string = this.icon_strings[index]; - } - } + source_settings.set_uint ("current", ((!) window_sources)[((!) new_view).path]); } } - - if (icon_string == null) { - icon_string = ""; - } - - return (!) icon_string; } [DBus (visible = false)] - private bool is_icon_string_unique (uint index) { - bool icon_string_unique = true; - - if (this.icon_string_uniques == null) { - var array = this.source_settings.get_value ("sources"); - this.icon_string_uniques = new int[array.n_children ()]; + private Source[] get_sources () { + if (sources == null) { + var array = source_settings.get_value ("sources"); - for (var i = 0; i < ((!) this.icon_string_uniques).length; i++) { - this.icon_string_uniques[i] = -1; - } - } + sources = new Source[array.n_children ()]; - if (index < ((!) this.icon_string_uniques).length) { - if (this.icon_string_uniques[index] == -1) { - this.icon_string_uniques[index] = 1; + for (var i = 0; i < ((!) sources).length; i++) { + sources[i] = new Source(array.get_child_value (i), use_gtk); + sources[i].show_subscript = false; + sources[i].subscript = 1; - var icon_string = get_icon_string (index); + for (var j = (int) i - 1; j >= 0; j--) { + if ((!) sources[j].short_name == (!) sources[i].short_name) { + sources[i].subscript = sources[j].subscript + 1; + sources[i].show_subscript = true; + sources[j].show_subscript = true; - for (var i = 0; i < ((!) this.icon_string_uniques).length && this.icon_string_uniques[index] == 1; i++) { - if (i != index && get_icon_string (i) == icon_string) { - this.icon_string_uniques[index] = 0; + break; } } } - - icon_string_unique = this.icon_string_uniques[index] != 0; } - return icon_string_unique; + return (!) sources; } [DBus (visible = false)] - private uint get_icon_string_subscript (uint index) { - uint icon_string_subscript = 0; - - if (this.icon_string_subscripts == null) { - var array = this.source_settings.get_value ("sources"); - this.icon_string_subscripts = new uint[array.n_children ()]; - } - - if (index < ((!) this.icon_string_subscripts).length) { - icon_string_subscript = this.icon_string_subscripts[index]; - - if (icon_string_subscript == 0) { - this.icon_string_subscripts[index] = 1; - - for (var i = (int) index - 1; i >= 0 && this.icon_string_subscripts[index] == 1; i--) { - if (get_icon_string (i) == get_icon_string (index)) { - this.icon_string_subscripts[index] = get_icon_string_subscript (i) + 1; - } - } + private void update_indicator_action () { + var visible = indicator_settings.get_boolean ("visible"); + var current = source_settings.get_uint ("current"); + var icon = get_sources ()[current].icon; + Variant state; - icon_string_subscript = this.icon_string_subscripts[index]; - } + if (icon != null) { + state = new Variant.parsed ("{ 'visible' : <%b>, 'icon' : %v }", visible, ((!) icon).serialize ()); + } else { + state = new Variant.parsed ("{ 'visible' : <%b> }", visible); } - return icon_string_subscript; + get_indicator_action ().set_state (state); } [DBus (visible = false)] - private Icon? get_icon (uint index) { - Icon? icon = null; - - if (this.icons == null) { - var array = this.source_settings.get_value ("sources"); - this.icons = new Icon?[array.n_children ()]; - } - - if (index < ((!) this.icons).length) { - icon = this.icons[index]; - - if (icon == null) { - var array = this.source_settings.get_value ("sources"); - - string type; - string name; - - array.get_child (index, "(ss)", out type, out name); - - if (type == "xkb") { - var icon_string = get_icon_string (index); - var icon_unique = is_icon_string_unique (index); - var icon_subscript = get_icon_string_subscript (index); - - if (icon_string.get_char () != '\0') { - string icon_name; - - if (icon_unique) { - icon_name = @"indicator-keyboard-$((!) icon_string)"; - } else { - icon_name = @"indicator-keyboard-$((!) icon_string)-$icon_subscript"; - } - - if (this.use_gtk) { - var icon_theme = Gtk.IconTheme.get_default (); - Gtk.IconInfo? icon_info = icon_theme.lookup_icon (icon_name, 22, 0); - - if (icon_info != null) { - try { - this.icons[index] = Icon.new_for_string (((!) icon_info).get_filename ()); - } catch (Error error) { - this.icons[index] = null; - } - } - } else { - this.icons[index] = new ThemedIcon (icon_name); - } - } - - if (this.icons[index] == null) { - if (icon_unique) { - this.icons[index] = create_icon (icon_string, 0); - } else { - this.icons[index] = create_icon (icon_string, icon_subscript); - } - } - } else if (type == "ibus") { - var names = new string[2]; - names[0] = name; - - var engines = get_ibus ().get_engines_by_names (names); - - if (engines.length > 0) { - var engine = engines[0]; - - try { - this.icons[index] = Icon.new_for_string (engine.get_icon ()); - } catch (Error error) { - warning ("error: %s", error.message); - } - } - } - - icon = this.icons[index]; - } + private SimpleAction get_indicator_action () { + if (indicator_action == null) { + var state = new Variant.parsed ("{ 'visible' : <false> }"); + indicator_action = new SimpleAction.stateful ("indicator", null, state); + update_indicator_action (); } - return icon; + return (!) indicator_action; } [DBus (visible = false)] @@ -485,57 +248,66 @@ public class Indicator.Keyboard.Service : Object { var group = new SimpleActionGroup (); group.insert (root_action); - group.insert (this.source_settings.create_action ("current")); + group.insert (source_settings.create_action ("current")); var action = new SimpleAction ("map", null); - action.activate.connect (this.handle_activate_map); + action.activate.connect (handle_activate_map); group.insert (action); action = new SimpleAction ("chart", null); - action.activate.connect (this.handle_activate_chart); + action.activate.connect (handle_activate_chart); group.insert (action); action = new SimpleAction ("settings", null); - action.activate.connect (this.handle_activate_settings); + action.activate.connect (handle_activate_settings); group.insert (action); return group; } [DBus (visible = false)] - private void update_indicator_action () { - var visible = this.indicator_settings.get_boolean ("visible"); - var current = this.source_settings.get_uint ("current"); - var icon = get_icon (current); - Variant state; - - if (icon != null) { - state = new Variant.parsed ("{ 'visible' : <%b>, 'icon' : %v }", visible, ((!) icon).serialize ()); - } else { - state = new Variant.parsed ("{ 'visible' : <%b> }", visible); + public SimpleActionGroup get_action_group () { + if (action_group == null) { + action_group = create_action_group (get_indicator_action ()); } - get_indicator_action ().set_state (state); + return (!) action_group; } [DBus (visible = false)] - private SimpleAction get_indicator_action () { - if (this.indicator_action == null) { - var state = new Variant.parsed ("{ 'visible' : <false> }"); - this.indicator_action = new SimpleAction.stateful ("indicator", null, state); - update_indicator_action (); - } + private void update_sources_menu () { + if (sources_menu != null) { + var menu = get_sources_menu (); + + while (menu.get_n_items () > 0) + menu.remove (0); + + var sources = get_sources (); - return (!) this.indicator_action; + for (var i = 0; i < sources.length; i++) { + var item = new MenuItem (sources[i].name, "indicator.current"); + item.set_attribute (Menu.ATTRIBUTE_TARGET, "u", i); + + var icon = sources[i].icon; + if (icon != null) { + item.set_icon ((!) icon); + } + + menu.append_item (item); + } + } else { + get_sources_menu (); + } } [DBus (visible = false)] - public SimpleActionGroup get_action_group () { - if (this.action_group == null) { - this.action_group = create_action_group (get_indicator_action ()); + private Menu get_sources_menu () { + if (sources_menu == null) { + sources_menu = new Menu (); + update_sources_menu (); } - return (!) this.action_group; + return (!) sources_menu; } [DBus (visible = false)] @@ -561,107 +333,12 @@ public class Indicator.Keyboard.Service : Object { } [DBus (visible = false)] - private string get_display_name (string layout) { - string? language = Xkl.get_language_name (layout); - string? country = Xkl.get_country_name (layout); - var has_language = language != null && ((!) language).get_char () != '\0'; - var has_country = country != null && ((!) country).get_char () != '\0'; - - if (has_language && has_country) { - return @"$((!) language) ($((!) country))"; - } else if (has_language) { - return (!) language; - } else if (has_country) { - return (!) country; - } else { - return ""; - } - } - - [DBus (visible = false)] - private void update_sources_menu () { - if (this.sources_menu != null) { - var menu = get_sources_menu (); - - while (menu.get_n_items () > 0) - menu.remove (0); - - VariantIter iter; - string type; - string name; - - this.source_settings.get ("sources", "a(ss)", out iter); - - for (var i = 0; iter.next ("(ss)", out type, out name); i++) { - if (type == "xkb") { - string? display_name; - string? layout_name; - - this.xkb_info.get_layout_info (name, out display_name, null, out layout_name, null); - - if (display_name != null) { - name = (!) display_name; - } else if (layout_name != null) { - name = get_display_name ((!) layout_name); - } - } - else if (type == "ibus") { - var names = new string[2]; - names[0] = name; - - var engines = get_ibus ().get_engines_by_names (names); - - if (engines.length > 0) { - var engine = engines[0]; - string? language = engine.get_language (); - string? display_name = engine.get_longname (); - - if (language != null) { - language = Xkl.get_language_name ((!) language); - } - - if (language != null && display_name != null) { - name = @"$((!) language) ($((!) display_name))"; - } else if (language != null) { - name = (!) language; - } else if (display_name != null) { - name = (!) display_name; - } - } - } - - var menu_item = new MenuItem (name, "indicator.current"); - menu_item.set_attribute (Menu.ATTRIBUTE_TARGET, "u", i); - - var icon = get_icon (i); - if (icon != null) { - menu_item.set_icon ((!) icon); - } - - menu.append_item (menu_item); - } - } else { - get_sources_menu (); - } - } - - [DBus (visible = false)] - private Menu get_sources_menu () { - if (this.sources_menu == null) { - this.sources_menu = new Menu (); - update_sources_menu (); - } - - return (!) this.sources_menu; - } - - [DBus (visible = false)] public MenuModel get_menu_model () { - if (this.menu_model == null) { - this.menu_model = create_menu_model (get_sources_menu ()); + if (menu_model == null) { + menu_model = create_menu_model (get_sources_menu ()); } - return (!) this.menu_model; + return (!) menu_model; } [DBus (visible = false)] @@ -676,10 +353,7 @@ public class Indicator.Keyboard.Service : Object { [DBus (visible = false)] private void handle_changed_sources (string key) { - this.icon_string_subscripts = null; - this.icon_string_uniques = null; - this.icon_strings = null; - this.icons = null; + sources = null; update_sources_menu (); update_indicator_action (); @@ -696,42 +370,29 @@ public class Indicator.Keyboard.Service : Object { [DBus (visible = false)] private void handle_activate_chart (Variant? parameter) { - var layout = "us"; + string? layout = "us"; string? variant = null; - var current = this.source_settings.get_uint ("current"); - var array = this.source_settings.get_value ("sources"); - - if (current < array.n_children ()) { - string type; - string name; - - array.get_child (current, "(ss)", out type, out name); - - if (type == "xkb") { - this.xkb_info.get_layout_info (name, null, null, out layout, out variant); - } else if (type == "ibus") { - var names = new string[2]; - names[0] = name; + var sources = get_sources (); + var current = source_settings.get_uint ("current"); - var engines = get_ibus ().get_engines_by_names (names); - - if (engines.length > 0) { - var engine = engines[0]; - - layout = engine.get_layout (); - variant = engine.get_layout_variant (); - } - } + if (current < sources.length) { + layout = sources[current].layout; + variant = sources[current].variant; } + var has_layout = layout != null && ((!) layout).get_char () != '\0'; + var has_variant = variant != null && ((!) variant).get_char () != '\0'; + try { string command; - if (variant != null && ((!) variant).get_char () != '\0') { - command = @"gkbd-keyboard-display -l \"$layout\t$((!) variant)\""; + if (has_layout && has_variant) { + command = @"gkbd-keyboard-display -l \"$((!) layout)\t$((!) variant)\""; + } else if (has_layout) { + command = @"gkbd-keyboard-display -l $((!) layout)"; } else { - command = @"gkbd-keyboard-display -l $layout"; + command = @"gkbd-keyboard-display -l us"; } Process.spawn_command_line_async (command); @@ -761,8 +422,8 @@ public class Indicator.Keyboard.Service : Object { [DBus (visible = false)] private void handle_name_lost (DBusConnection? connection, string name) { - ((!) this.loop).quit (); - this.loop = null; + ((!) loop).quit (); + loop = null; } [DBus (visible = false)] diff --git a/lib/source.vala b/lib/source.vala new file mode 100644 index 00000000..3e38a86e --- /dev/null +++ b/lib/source.vala @@ -0,0 +1,441 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * 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, version 3 of the License. + * + * 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/>. + * + * Authors: William Hua <william.hua@canonical.com> + */ + +public class Indicator.Keyboard.Source : Object { + + private static Gnome.XkbInfo? xkb_info; + private static IBus.Bus? bus; + + private string? xkb; + private string? ibus; + + private string? _name; + private string? _short_name; + private string? _layout; + private string? _variant; + private Icon? _icon; + private uint _subscript; + private bool _show_subscript; + private bool _use_gtk; + + public string? name { + get { if (_name == null) { _name = _get_name (); } return _name; } + } + + public string? short_name { + get { if (_short_name == null) { _short_name = _get_short_name (); } return _short_name; } + } + + public string? layout { + get { if (_layout == null) { _layout = _get_layout (); } return _layout; } + } + + public string? variant { + get { if (_variant == null) { _variant = _get_variant (); } return _variant; } + } + + public Icon? icon { + get { if (_icon == null) { _icon = _get_icon (); } return _icon; } + private set { _icon = value; } + } + + public uint subscript { + get { return _subscript; } + set { _subscript = value; icon = null; } + } + + public bool show_subscript { + get { return _show_subscript; } + set { _show_subscript = value; icon = null; } + } + + public bool use_gtk { + get { return _use_gtk; } + construct set { _use_gtk = value; icon = null; } + } + + public Source (Variant variant, bool use_gtk = false) { + Object (use_gtk: use_gtk); + + if (variant.is_of_type (new VariantType ("(ss)"))) { + unowned string type; + unowned string name; + + variant.get ("(&s&s)", out type, out name); + + if (type == "xkb") { + xkb = name; + } else if (type == "ibus") { + ibus = name; + } + } else if (variant.is_of_type (new VariantType ("a{ss}"))) { + VariantIter iter; + unowned string key; + unowned string value; + + variant.get ("a{ss}", out iter); + + while (iter.next ("{&s&s}", out key, out value)) { + if (key == "xkb") { + xkb = value; + } else if (key == "ibus") { + ibus = value; + } + } + } + } + + private static Gnome.XkbInfo get_xkb_info () { + if (xkb_info == null) { + xkb_info = new Gnome.XkbInfo (); + } + + return (!) xkb_info; + } + + private static IBus.Bus get_bus () { + if (bus == null) { + IBus.init (); + bus = new IBus.Bus (); + } + + return (!) bus; + } + + private IBus.EngineDesc? get_engine () { + IBus.EngineDesc? engine = null; + + if (ibus != null) { + var names = new string[2]; + names[0] = (!) ibus; + + var engines = get_bus ().get_engines_by_names (names); + + if (engines.length > 0) { + engine = engines[0]; + } + } + + return engine; + } + + protected virtual string? _get_name () { + string? name = null; + + var engine = get_engine (); + + if (engine != null) { + string? language = ((!) engine).get_language (); + string? display_name = ((!) engine).get_longname (); + var has_language = language != null && ((!) language).get_char () != '\0'; + var has_display_name = display_name != null && ((!) display_name).get_char () != '\0'; + + if (has_language) { + language = Xkl.get_language_name ((!) language); + has_language = language != null && ((!) language).get_char () != '\0'; + } + + if (has_language && has_display_name) { + name = @"$((!) language) ($((!) display_name))"; + } else if (has_language) { + name = language; + } else if (has_display_name) { + name = display_name; + } + } + + var has_name = name != null && ((!) name).get_char () != '\0'; + + if (!has_name && xkb != null) { + string? display_name = null; + string? layout = null; + + get_xkb_info ().get_layout_info ((!) xkb, out display_name, null, out layout, null); + + var has_display_name = display_name != null && ((!) display_name).get_char () != '\0'; + var has_layout = layout != null && ((!) layout).get_char () != '\0'; + + if (has_display_name) { + name = display_name; + } else if (has_layout) { + string? language = Xkl.get_language_name ((!) layout); + string? country = Xkl.get_country_name ((!) layout); + var has_language = language != null && ((!) language).get_char () != '\0'; + var has_country = country != null && ((!) country).get_char () != '\0'; + + if (has_language && has_country) { + name = @"$((!) language) ($((!) country))"; + } else if (has_language) { + name = language; + } else if (has_country) { + name = country; + } + } + } + + if (name == null || ((!) name).get_char () == '\0') { + if (ibus != null) { + name = ibus; + } else if (xkb != null) { + name = xkb; + } + } + + return name; + } + + protected virtual string? _get_short_name () { + string? short_name = null; + + if (xkb != null) { + get_xkb_info ().get_layout_info ((!) xkb, null, out short_name, null, null); + } + + var has_short_name = short_name != null && ((!) short_name).get_char () != '\0'; + + if (!has_short_name) { + var engine = get_engine (); + + if (engine != null) { + short_name = ((!) engine).get_name (); + } + } + + if (short_name == null || ((!) short_name).get_char () == '\0') { + if (ibus != null) { + short_name = ibus; + } else if (xkb != null) { + short_name = xkb; + } + } + + return abbreviate (short_name); + } + + protected virtual string? _get_layout () { + string? layout = null; + + if (xkb != null) { + get_xkb_info ().get_layout_info ((!) xkb, null, null, out layout, null); + } + + var has_layout = layout != null && ((!) layout).get_char () != '\0'; + + if (!has_layout) { + var engine = get_engine (); + + if (engine != null) { + layout = ((!) engine).get_layout (); + } + } + + if (layout == null || ((!) layout).get_char () == '\0') { + layout = xkb; + } + + return layout; + } + + protected virtual string? _get_variant () { + string? variant = null; + + if (xkb != null) { + get_xkb_info ().get_layout_info ((!) xkb, null, null, null, out variant); + } + + var has_variant = variant != null && ((!) variant).get_char () != '\0'; + + if (!has_variant) { + var engine = get_engine (); + + if (engine != null) { + variant = ((!) engine).get_layout_variant (); + } + } + + if (variant == null || ((!) variant).get_char () == '\0') { + variant = null; + } + + return variant; + } + + private Gtk.StyleContext? get_style_context () { + Gtk.StyleContext? context = null; + + if (_use_gtk) { + Gdk.Screen? screen = Gdk.Screen.get_default (); + + if (screen != null) { + context = new Gtk.StyleContext (); + ((!) context).set_screen ((!) screen); + + var path = new Gtk.WidgetPath (); + path.append_type (typeof (Gtk.MenuItem)); + ((!) context).set_path (path); + } + } + + return context; + } + + protected virtual Icon? create_icon () { + Icon? icon = null; + + var style = get_style_context (); + + if (style != null) { + const int W = 22; + const int H = 22; + const int w = 20; + const int h = 20; + const double R = 2.0; + const double TEXT_SIZE = 12.0; + const double SUBSCRIPT_SIZE = 8.0; + + Pango.FontDescription description; + var colour = ((!) style).get_color (Gtk.StateFlags.NORMAL); + colour = { 0.5, 0.5, 0.5, 1.0 }; + ((!) style).get (Gtk.StateFlags.NORMAL, Gtk.STYLE_PROPERTY_FONT, out description); + + var surface = new Cairo.ImageSurface (Cairo.Format.ARGB32, W, H); + var context = new Cairo.Context (surface); + + context.translate (0.5 * (W - w), 0.5 * (H - h)); + + context.new_sub_path (); + context.arc (R, R, R, Math.PI, -0.5 * Math.PI); + context.arc (w - R, R, R, -0.5 * Math.PI, 0); + context.arc (w - R, h - R, R, 0, 0.5 * Math.PI); + context.arc (R, h - R, R, 0.5 * Math.PI, Math.PI); + context.close_path (); + + context.set_source_rgba (colour.red, colour.green, colour.blue, colour.alpha); + context.fill (); + context.set_operator (Cairo.Operator.CLEAR); + + if (short_name != null) { + var text_layout = Pango.cairo_create_layout (context); + text_layout.set_alignment (Pango.Alignment.CENTER); + description.set_absolute_size (Pango.units_from_double (TEXT_SIZE)); + text_layout.set_font_description (description); + text_layout.set_text ((!) short_name, -1); + Pango.cairo_update_layout (context, text_layout); + int text_width; + int text_height; + text_layout.get_pixel_size (out text_width, out text_height); + + if (_show_subscript) { + var subscript_layout = Pango.cairo_create_layout (context); + subscript_layout.set_alignment (Pango.Alignment.CENTER); + description.set_absolute_size (Pango.units_from_double (SUBSCRIPT_SIZE)); + subscript_layout.set_font_description (description); + subscript_layout.set_text (@"$_subscript", -1); + Pango.cairo_update_layout (context, subscript_layout); + int subscript_width; + int subscript_height; + subscript_layout.get_pixel_size (out subscript_width, out subscript_height); + + context.save (); + context.translate ((w - (text_width + subscript_width)) / 2, (h - text_height) / 2); + Pango.cairo_layout_path (context, text_layout); + context.fill (); + context.restore (); + + context.save (); + context.translate ((w + (text_width - subscript_width)) / 2, (h + text_height) / 2 - subscript_height); + Pango.cairo_layout_path (context, subscript_layout); + context.fill (); + context.restore (); + } else { + context.save (); + context.translate ((w - text_width) / 2, (h - text_height) / 2); + Pango.cairo_layout_path (context, text_layout); + context.fill (); + context.restore (); + } + } + + var buffer = new ByteArray (); + + surface.write_to_png_stream ((data) => { + buffer.append (data); + return Cairo.Status.SUCCESS; + }); + + icon = new BytesIcon (ByteArray.free_to_bytes ((owned) buffer)); + } + + return icon; + } + + private Icon? _get_icon () { + Icon? icon = null; + + var engine = get_engine (); + + if (engine != null) { + string? icon_name = ((!) engine).get_icon (); + var has_icon_name = icon_name != null && ((!) icon_name).get_char () != '\0'; + + if (has_icon_name) { + try { + icon = Icon.new_for_string ((!) icon_name); + } catch (Error error) { + warning ("error: %s", error.message); + } + } + } + + if (icon == null && short_name != null) { + string icon_name; + + if (_show_subscript) { + icon_name = @"indicator-keyboard-$((!) short_name)-$_subscript"; + } else { + icon_name = @"indicator-keyboard-$((!) short_name)"; + } + + if (_use_gtk) { + var icon_theme = Gtk.IconTheme.get_default (); + Gtk.IconInfo? icon_info = icon_theme.lookup_icon (icon_name, 22, 0); + + if (icon_info != null) { + string? file_name = ((!) icon_info).get_filename (); + var has_file_name = file_name != null && ((!) file_name).get_char () != '\0'; + + if (has_file_name) { + try { + icon = Icon.new_for_string ((!) file_name); + } catch (Error error) { + warning ("error: %s", error.message); + } + } + } + } else { + icon = new ThemedIcon (icon_name); + } + } + + if (icon == null) { + icon = create_icon (); + } + + return icon; + } +} diff --git a/po/indicator-keyboard.pot b/po/indicator-keyboard.pot index a96fdd39..171c3021 100644 --- a/po/indicator-keyboard.pot +++ b/po/indicator-keyboard.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-07-18 18:00-0400\n" +"POT-Creation-Date: 2013-08-07 15:03-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -17,14 +17,14 @@ msgstr "" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" -#: ../lib/main.c:2297 ../lib/main.vala:532 +#: ../lib/main.c:1405 ../lib/main.vala:322 msgid "Character Map" msgstr "" -#: ../lib/main.c:2299 ../lib/main.vala:533 +#: ../lib/main.c:1407 ../lib/main.vala:323 msgid "Keyboard Layout Chart" msgstr "" -#: ../lib/main.c:2301 ../lib/main.vala:534 +#: ../lib/main.c:1409 ../lib/main.vala:324 msgid "Text Entry Settings..." msgstr "" diff --git a/tests/main.vala b/tests/main.vala index 36a915c7..d9d52733 100644 --- a/tests/main.vala +++ b/tests/main.vala @@ -31,12 +31,12 @@ public class Service : Object { } public void execute (string command) { - this._command = command; + _command = command; - var pspec = this.get_class ().find_property ("command"); + var pspec = get_class ().find_property ("command"); if (pspec != null) { - this.notify["command"] ((!) pspec); + notify["command"] ((!) pspec); } } } @@ -165,11 +165,13 @@ static void test_activate_input_source (void *data) { var state = action_group.get_action_state ("current"); var current = state.get_uint32 (); + stderr.printf ("current = %u\n", current); assert (current == 2); try { string output; Process.spawn_command_line_sync ("gsettings get org.gnome.desktop.input-sources current", out output); + stderr.printf ("output = \"%s\"\n", output); assert (strcmp (output, "uint32 2\n") == 0); } catch (SpawnError error) { Test.message ("error: %s", error.message); @@ -202,6 +204,7 @@ static void test_activate_character_map (void *data) { Source.remove (source); ((!) fixture.service).disconnect (signal_name); + stderr.printf ("fixture.service.command = \"%s\"\n", (!) ((!) fixture.service).command); assert (strcmp ((!) ((!) fixture.service).command, "'gucharmap '") == 0); } @@ -240,6 +243,7 @@ static void test_activate_keyboard_layout_chart (void *data) { Source.remove (source); ((!) fixture.service).disconnect (signal_name); + stderr.printf ("fixture.service.command = \"%s\"\n", (!) ((!) fixture.service).command); assert (strcmp ((!) ((!) fixture.service).command, "'gkbd-keyboard-display -l ca\teng'") == 0); } @@ -267,6 +271,7 @@ static void test_activate_text_entry_settings (void *data) { Source.remove (source); ((!) fixture.service).disconnect (signal_name); + stderr.printf ("fixture.service.command = \"%s\"\n", (!) ((!) fixture.service).command); assert (strcmp ((!) ((!) fixture.service).command, "'gnome-control-center region layouts'") == 0); } @@ -323,6 +328,7 @@ static void test_migration (void *data) { try { string sources; Process.spawn_command_line_sync ("gsettings get org.gnome.desktop.input-sources sources", out sources); + stderr.printf ("sources = \"%s\"\n", sources); assert (strcmp (sources, "[('xkb', 'us'), ('xkb', 'ca+eng'), ('xkb', 'epo')]\n") == 0); } catch (SpawnError error) { Test.message ("error: %s", error.message); @@ -384,6 +390,7 @@ static void test_no_migration (void *data) { try { string sources; Process.spawn_command_line_sync ("gsettings get org.gnome.desktop.input-sources sources", out sources); + stderr.printf ("sources = \"%s\"\n", sources); assert (strcmp (sources, "[('xkb', 'us')]\n") == 0); } catch (SpawnError error) { Test.message ("error: %s", error.message); @@ -429,6 +436,7 @@ static void test_update_visible (void *data) { var state = action_group.get_action_state ("indicator"); assert (state.lookup ("visible", "b", out visible)); + stderr.printf ("visible = %s\n", visible ? "true" : "false"); assert (visible); loop = new MainLoop (null, false); @@ -452,6 +460,7 @@ static void test_update_visible (void *data) { state = action_group.get_action_state ("indicator"); assert (state.lookup ("visible", "b", out visible)); + stderr.printf ("visible = %s\n", visible ? "true" : "false"); assert (!visible); loop = new MainLoop (null, false); @@ -475,6 +484,7 @@ static void test_update_visible (void *data) { state = action_group.get_action_state ("indicator"); assert (state.lookup ("visible", "b", out visible)); + stderr.printf ("visible = %s\n", visible ? "true" : "false"); assert (visible); } @@ -524,11 +534,13 @@ static void test_update_input_source (void *data) { var state = action_group.get_action_state ("current"); var current = state.get_uint32 (); + stderr.printf ("current = %u\n", current); assert (current == 1); try { string output; Process.spawn_command_line_sync ("gsettings get org.gnome.desktop.input-sources current", out output); + stderr.printf ("output = \"%s\"\n", output); assert (strcmp (output, "uint32 1\n") == 0); } catch (SpawnError error) { Test.message ("error: %s", error.message); @@ -557,11 +569,13 @@ static void test_update_input_source (void *data) { state = action_group.get_action_state ("current"); current = state.get_uint32 (); + stderr.printf ("current = %u\n", current); assert (current == 0); try { string output; Process.spawn_command_line_sync ("gsettings get org.gnome.desktop.input-sources current", out output); + stderr.printf ("output = \"%s\"\n", output); assert (strcmp (output, "uint32 0\n") == 0); } catch (SpawnError error) { Test.message ("error: %s", error.message); @@ -633,8 +647,10 @@ static void test_update_input_sources (void *data) { string label; + stderr.printf ("section.get_n_items () = %d\n", section.get_n_items ()); assert (section.get_n_items () == 1); section.get_item_attribute (0, Menu.ATTRIBUTE_LABEL, "s", out label); + stderr.printf ("label = \"%s\"\n", label); assert (strcmp (label, "English (US)") == 0); loop = new MainLoop (null, false); @@ -658,14 +674,19 @@ static void test_update_input_sources (void *data) { Source.remove (source); section.disconnect (signal_name); + stderr.printf ("section.get_n_items () = %d\n", section.get_n_items ()); assert (section.get_n_items () == 4); section.get_item_attribute (0, Menu.ATTRIBUTE_LABEL, "s", out label); + stderr.printf ("label = \"%s\"\n", label); assert (strcmp (label, "English (US)") == 0); section.get_item_attribute (1, Menu.ATTRIBUTE_LABEL, "s", out label); + stderr.printf ("label = \"%s\"\n", label); assert (strcmp (label, "English (Canada)") == 0); section.get_item_attribute (2, Menu.ATTRIBUTE_LABEL, "s", out label); + stderr.printf ("label = \"%s\"\n", label); assert (strcmp (label, "Esperanto") == 0); section.get_item_attribute (3, Menu.ATTRIBUTE_LABEL, "s", out label); + stderr.printf ("label = \"%s\"\n", label); assert (label.ascii_casecmp ("Pinyin") == 0); } |