[caribou: 5/22] Use new layout format. Added sub-keys. Added group change notification.



commit 048f045f85982579932d147390e6503710a455bc
Author: Eitan Isaacson <eitan monotonous org>
Date:   Fri Apr 22 15:10:38 2011 -0700

    Use new layout format. Added sub-keys. Added group change notification.

 caribou/common/const.py          |    2 +-
 caribou/common/settings.py       |   34 +-
 caribou/ui/keyboard.py           |  339 ++++++----
 caribou/ui/main.py               |    6 +-
 caribou/ui/window.py             |   28 -
 configure.ac                     |    3 +-
 data/Makefile.am                 |    2 +-
 data/keyboards/Makefile.am       |   10 -
 data/keyboards/hebrew.xml        |  272 --------
 data/keyboards/lexic_es.json     |  300 ---------
 data/keyboards/qwerty.xml        |  274 --------
 data/keyboards/qwerty_en.json    |  261 --------
 data/keyboards/qwerty_es.json    | 1318 --------------------------------------
 data/keyboards/qwerty_ja.xml     |  782 ----------------------
 data/layouts/Makefile.am         |    1 +
 data/layouts/natural/Makefile.am |    6 +
 data/layouts/natural/us.json     |  208 ++++++
 17 files changed, 432 insertions(+), 3414 deletions(-)
---
diff --git a/caribou/common/const.py b/caribou/common/const.py
index b3a13b7..0591e5d 100644
--- a/caribou/common/const.py
+++ b/caribou/common/const.py
@@ -30,7 +30,7 @@ APP_SLUG_NAME = 'caribou'
 
 # Paths
 DATA_DIR = data_path
-KEYBOARDS_DIR = join(DATA_DIR, 'keyboards')
+LAYOUTS_DIR = join(DATA_DIR, 'layouts')
 
 # Preferences
 CARIBOU_GCONF = '/org/gnome/caribou/osk'
diff --git a/caribou/common/settings.py b/caribou/common/settings.py
index a3d900d..7837508 100644
--- a/caribou/common/settings.py
+++ b/caribou/common/settings.py
@@ -4,39 +4,21 @@ from gettext import gettext as _
 import caribou.common.const as const
 import caribou.ui.i18n
 import xml.dom.minidom
+import json
 
 GSETTINGS_SCHEMA = "org.gnome.caribou"
 
-try:
-    import json
-except ImportError:
-    HAS_JSON = False
-else:
-    HAS_JSON = True
-
-def fetch_keyboards():
-    try:
-        files = os.listdir(const.KEYBOARDS_DIR)
-    except:
-        files = []
-    kbds = []
-    for f in files:
-        if (HAS_JSON and f.endswith('.json')) or f.endswith('.xml'):
-            module = f.rsplit('.', 1)[0]
-            # TODO: verify keyboard before adding it to the list
-            kbds.append(module)
-    return kbds
-
 settings = SettingsGroup("_top", "", [
         SettingsGroup("keyboard", _("Keyboard"), [
                 SettingsGroup("general", _("General"), [
                         StringSetting(
-                            "layout", _("Keyboard layout"), "qwerty",
-                            _("The layout Caribou should use."),
-                            _("The layout should be in the data directory of "
-                              "Caribou (usually /usr/share/caribou/keyboards) "
-                              "and should be a .xml or .json file."),
-                            allowed=[(a,a) for a in fetch_keyboards()])]),
+                            "geometry", _("Keyboard geometry"), "natural",
+                            _("The keyboard geometery Caribou should use"),
+                            _("The keyboard geometery determines the shape "
+                              "and complexity of the keyboard, it could range from "
+                              "a 'natural' look and feel good for composing simple "
+                              "text, to a fullscale keyboard."),
+                            allowed=[(('natural'), _('Natural'))])]),
                 SettingsGroup("color", _("Color"), [
                         BooleanSetting(
                             "default_colors", _("Use system theme"), True,
diff --git a/caribou/ui/keyboard.py b/caribou/ui/keyboard.py
index c390d49..9546935 100644
--- a/caribou/ui/keyboard.py
+++ b/caribou/ui/keyboard.py
@@ -25,6 +25,7 @@
 import caribou.common.const as const
 from caribou.common.settings_manager import SettingsManager
 from preferences_window import PreferencesWindow
+from caribou import data_path
 from gi.repository import GConf
 import gobject
 from gi.repository import Gdk
@@ -48,33 +49,40 @@ from xml.dom import minidom
 import gettext
 import i18n
 
+PRETTY_LABELS = {
+    "BackSpace" : u'\u232b',
+    "space" : u' ',
+    "Return" : u'\u23ce',
+    'Caribou_Prefs' : u'\u2328',
+    'Caribou_ShiftUp' : u'\u2b06',
+    'Caribou_ShiftDown' : u'\u2b07',
+    'Caribou_Emoticons' : u'\u263a',
+    'Caribou_Symbols' : u'123',
+    'Caribou_Symbols_More' : u'{#*',
+    'Caribou_Alpha' : u'Abc'
+}
+
 class BaseKey(object):
     '''An abstract class the represents a key on the keyboard.
     Inheriting classes also need to inherit from Gtk.Button or any
     of it's subclasses.'''
 
-    def __init__(self, label = '', value = '', key_type = 'normal',
-                 width = 1, fill = False):
-        self.key_type = key_type
-        self.value = value
-        self.width = float(width)
-        self.fill = False
-        self.label = label or value
-        if self.key_type == const.DUMMY_KEY_TYPE:
-            self.set_relief(Gtk.ReliefStyle.NONE)
-            self.set_sensitive(False)
-        elif self.key_type == const.PREFERENCES_KEY_TYPE:
-            image = Gtk.Image()
-            image.set_from_stock(Gtk.STOCK_PREFERENCES,
-                                 Gtk.IconSize.BUTTON)
-            self.set_image(image)
+    def __init__(self, **kwargs):
+        if not kwargs.has_key("name"):
+            raise TypeError, "%r requires a 'name' parameter" % self.__class__
+        self.margin_left = 0
+        self.width = 1
+
+        for k, v in kwargs.items():
+            setattr(self, k, v)
+        if hasattr(self, "extended_names"):
+            self.extended_keys = \
+                [self.__class__(name=n) for n in self.extended_names]
         else:
-            if label:
-                label_markup = Gtk.Label()
-                label_markup.set_markup(self.label)
-                self.add(label_markup)
-            else:
-                self.set_label(self.label)
+            self.extended_keys = []
+
+        self.keyval, self.key_label = self._get_keyval_and_label(self.name)
+        self.set_label(self.key_label)
 
         ctx = self.get_style_context()
         ctx.add_class("caribou-keyboard-button")
@@ -86,6 +94,21 @@ class BaseKey(object):
         if not SettingsManager.default_font.value:
             self._key_font_changed(None, None)
 
+    def _get_keyval_and_label(self, name):
+        keyval = Gdk.keyval_from_name(name)
+        if PRETTY_LABELS.has_key(name):
+            label = PRETTY_LABELS[name]
+        elif name.startswith('Caribou_'):
+            label = name.replace('Caribou_', '')
+        else:
+            unichar = unichr(Gdk.keyval_to_unicode(keyval))
+            if unichar.isspace() or unichar == u'\x00':
+                label = name
+            else:
+                label = unichar
+
+        return keyval, label
+
     def _key_font_changed(self, setting, value):
         if SettingsManager.default_font.value:
             self.reset_font()
@@ -107,54 +130,55 @@ class BaseKey(object):
     def scan_highlight_clear(self):
         raise NotImplemented
 
-    def _on_image_key_mapped(self, key):
-        key_width = key.get_allocated_height()
-        icon_size = Gtk.IconSize.MENU
-        image = Gtk.Image()
-        for size in [Gtk.IconSize.MENU,
-                     Gtk.IconSize.SMALL_TOOLBAR,
-                     Gtk.IconSize.LARGE_TOOLBAR,
-                     Gtk.IconSize.BUTTON,
-                     Gtk.IconSize.DND,
-                     Gtk.IconSize.DIALOG]:
-            pixbuf = image.render_icon_pixbuf(Gtk.STOCK_PREFERENCES, size)
-            pixel_size = pixbuf.get_width()
-            if pixel_size > key_width:
-                break
-            icon_size = size
-        image.set_from_stock(Gtk.STOCK_PREFERENCES, icon_size)
-        self.set_image(image)
-
     def set_font(self, font):
         raise NotImplemented
 
     def reset_font(self):
         raise NotImplemented
 
-    def _get_value(self):
-        return self._value
-
-    def _set_value(self, value):
-        if self.key_type in (const.NORMAL_KEY_TYPE, const.MASK_KEY_TYPE):
-            if type(value) == str:
-                value = value.decode('utf-8')
-            if type(value) == unicode:
-                if len(value) == 1:
-                    self._value = Gdk.unicode_to_keyval(ord(value))
-                else:
-                    key_value = Gdk.keyval_from_name(value)
-                    if key_value:
-                        self._value = key_value
-        else:
-            self._value = value
+class CaribouSubKeys(Gtk.Window):
+    def __init__(self, keys):
+        gobject.GObject.__init__(self, type=Gtk.WindowType.POPUP)
+        self.set_decorated(False)
+        self.set_resizable(False)
+        self.set_accept_focus(False)
+        self.set_position(Gtk.WindowPosition.MOUSE)
+        self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
 
-    value = property(_get_value, _set_value)
+        for key in keys:
+            key.connect("clicked", self._on_key_clicked)
+
+        layout = KeyboardLayout("")
+        layout.add_row(keys)
+        self.add(layout)
+
+    def show_subkeys(self, parent):
+        self.set_transient_for(parent)
+        self._parent = parent
+        self._parent.set_sensitive(False)
+        self.show_all()
+
+    def _on_key_clicked(self, key):
+        self._parent.set_sensitive(True)
+        self.hide()
 
 class Key(Gtk.Button, BaseKey):
-    def __init__(self, label = '', value = '', key_type = 'normal',
-                 width = 1, fill = False):
+    def __init__(self, **kwargs):
         gobject.GObject.__init__(self)
-        BaseKey.__init__(self, label, value, key_type, width, fill)
+        BaseKey.__init__(self, **kwargs)
+        if self.extended_keys:
+            self.sub_keys = CaribouSubKeys(self.extended_keys)
+        else:
+            self.sub_keys = None
+
+        child = self.get_child()
+        child.set_padding(4, 4)
+
+    def do_get_preferred_width_for_height(self, w):
+        return (w, w)
+
+    def do_get_request_mode(self):
+        return Gtk.SizeRequestMode.HEIGHT_FOR_WIDTH
 
     def set_font(self, font):
         child = self.get_child()
@@ -196,27 +220,30 @@ class Key(Gtk.Button, BaseKey):
 class ModifierKey(Gtk.ToggleButton, Key):
     pass
 
-class KeyboardLayout(Gtk.Table):
+class KeyboardLayout(Gtk.Grid):
     KEY_SPAN = 4
+
     def __init__(self, name):
         gobject.GObject.__init__(self)
         self.layout_name = name
         self.rows = []
-        self.set_homogeneous(True)
+        self.set_column_homogeneous(True)
+        self.set_row_homogeneous(True)
+        self.set_row_spacing(4)
+        self.set_column_spacing(4)
 
     def add_row(self, row):
         row_num = len(self.rows)
         self.rows.append(row)
-        last_col = 0
+        col_num = 0
         for i, key in enumerate(row):
-            next_col = (last_col + (key.width * self.KEY_SPAN))
-            self.attach(key, last_col, next_col,
-                        row_num * self.KEY_SPAN, (row_num + 1) * self.KEY_SPAN,
-                        Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL, 
-                        Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL,
-                        0, 0)
-            last_col = next_col
-    
+            self.attach(key,
+                        col_num + int(key.margin_left * self.KEY_SPAN),
+                        row_num * self.KEY_SPAN,
+                        int(self.KEY_SPAN * key.width),
+                        self.KEY_SPAN)
+            col_num += int((key.width + key.margin_left ) * self.KEY_SPAN)
+
     def get_scan_rows(self):
         return [filter(lambda x: x.is_sensitive(), row) for row in self.rows]
 
@@ -247,16 +274,21 @@ class KeyboardLayout(Gtk.Table):
                     group.append(block)
 
         return reduce(lambda a, b: a + b, blocks)
-        
-                                    
 
 class KbLayoutDeserializer(object):
-    def __init__(self):
-        pass
-
-    def deserialize(self, kb_layout_file):
-        kb_file = os.path.abspath(kb_layout_file)
-        if not os.path.isfile(kb_file):
+    def _get_layout_file(self, group, variant):
+        layout_path = os.path.join(data_path, "layouts",
+                                   SettingsManager.geometry.value)
+        for fn in ('%s_%s.json' % (group, variant),
+                   '%s.json' % group):
+            layout_file = os.path.join(layout_path, fn)
+            if os.path.exists(layout_file):
+                return layout_file
+        return None
+
+    def deserialize(self, group, variant):
+        kb_file = self._get_layout_file(group, variant)
+        if not kb_file:
             return []
         kb_file_obj = open(kb_file)
         contents = kb_file_obj.read()
@@ -304,37 +336,24 @@ class KbLayoutDeserializer(object):
     def _create_kb_layout_from_dict(self, dictionary):
         if not isinstance(dictionary, dict):
             return None
-        layouts = self._get_dict_value_as_list(dictionary, 'layout')
         layouts_encoded = []
-        for layout in layouts:
-            name = layout.get('name')
-            if not name:
-                continue
+        for name, level in dictionary.items():
             kb_layout = KeyboardLayout(name)
-            rows_list = self._get_dict_value_as_list(layout, 'rows')
-            for rows in rows_list:
-                for row_encoded in self._get_rows_from_dict(rows):
-                    kb_layout.add_row(row_encoded)
-                layouts_encoded.append(kb_layout)
+            rows_list = self._get_dict_value_as_list(level, 'rows')
+            for row in rows_list:
+                keys = self._get_keys_from_list(row)
+                kb_layout.add_row(keys)
+            layouts_encoded.append(kb_layout)
         return layouts_encoded
 
-    def _get_rows_from_dict(self, rows):
-        rows_encoded = []
-        row_list = self._get_dict_value_as_list(rows, 'row')
-        for row in row_list:
-            keys = self._get_dict_value_as_list(row, 'key')
-            if keys:
-                rows_encoded.append(self._get_keys_from_list(keys))
-        return rows_encoded
-
     def _get_keys_from_list(self, keys_list):
         keys = []
         for key_vars in keys_list:
             vars = {}
             for key, value in key_vars.items():
                 vars[str(key)] = value
-            if vars.get('key_type', '') == const.MASK_KEY_TYPE:
-                key = ModifierKey(**vars)
+            if vars.get('modifier', False) == const.MASK_KEY_TYPE:
+                key = ModifierKey(self.vk, **vars)
             else:
                 key = Key(**vars)
             keys.append(key)
@@ -360,45 +379,82 @@ class CaribouKeyboard(Gtk.Notebook):
         self.key_size = 30
         self.current_page = 0
         self.depressed_mods = []
-
-        self.row_height = -1
-
-    def load_kb(self, kb_location):
+        self.layouts = {}
+        self._load_kb()
+        self.vk.connect('group-changed', self._on_group_changed)
+        grpid, group, variant = self.vk.get_current_group()
+        self._on_group_changed(self.vk, grpid, group, variant)
+        self._key_hold_tid = 0
+
+    def _load_kb(self):
         kb_deserializer = KbLayoutDeserializer()
-        layouts = kb_deserializer.deserialize(kb_location)
-        self._set_layouts(layouts)
-
-    def _set_layouts(self, layout_list):
-        self._clear()            
-        for layout in layout_list:
-            self.append_page(layout, None)
-            for row in layout.rows:
+        groups, variants = self.vk.get_groups()
+        for group, variant in zip(groups, variants):
+            levels = kb_deserializer.deserialize(group, variant)
+            self._add_levels('%s_%s' % (group, variant), levels)
+
+    def _connect_key_signals(self, key):
+        if hasattr(key, "toggle"):
+            key.connect('clicked',
+                        self._pressed_layout_switcher_key)
+        elif key.name == "Caribou_Prefs":
+            key.connect('clicked', self._pressed_preferences_key)
+        elif key.keyval != 0:
+            if False: # We should enable this for hardware emulation
+                key.connect('pressed',
+                            self._pressed_normal_key)
+                key.connect('released',
+                            self._released_normal_key)
+            else:
+                key.connect('clicked',
+                            self._clicked_normal_key)
+                key.connect('pressed',
+                            self._key_hold_start)
+                key.connect('released',
+                            self._key_hold_end)
+        
+
+    def _add_levels(self, group, level_list):
+        self.layouts[group] = {}
+        for level in level_list:
+            level.show()
+            self.layouts[group][level.layout_name] = self.append_page(level, None)
+            for row in level.rows:
                 for key in row:
-                    if key.key_type == const.LAYOUT_SWITCHER_KEY_TYPE:
-                        key.connect('clicked',
-                                    self._pressed_layout_switcher_key)
-                    elif key.key_type == const.MASK_KEY_TYPE:
-                        key.connect('toggled',
-                                    self._toggled_mask_key)
-                    elif key.key_type == const.PREFERENCES_KEY_TYPE:
-                        key.connect('clicked',
-                                    self._pressed_preferences_key)
-                    else:
-                        key.connect('pressed',
-                                    self._pressed_normal_key)
-                        key.connect('released',
-                                    self._released_normal_key)
+                    self._connect_key_signals(key)
+                    for k in key.extended_keys:
+                        self._connect_key_signals(k)
 
     def _clear(self):
         n_pages = self.get_n_pages()
         for i in range(n_pages):
             self.remove_page(i)
 
+    def _key_hold_start(self, key):
+        self._key_hold_tid = gobject.timeout_add(1000, self._on_key_held, key)
+
+    def _key_hold_end(self, key):
+        if self._key_hold_tid != 0:
+            gobject.source_remove(self._key_hold_tid)
+            self._key_hold_tid = 0
+
+    def _on_key_held(self, key):
+        self._key_hold_tid = 0
+        if key.sub_keys:
+            key.sub_keys.show_subkeys(self.get_toplevel())
+        return False
+
+    def _clicked_normal_key(self, key):
+        if self._key_hold_tid == 0:
+            return
+        self._pressed_normal_key(key)
+        self._released_normal_key(key)
+
     def _pressed_normal_key(self, key):
-        self.vk.keyval_press(key.value)
+        self.vk.keyval_press(key.keyval)
 
     def _released_normal_key(self, key):
-        self.vk.keyval_release(key.value)
+        self.vk.keyval_release(key.keyval)
         while True:
             try:
                 mod = self.depressed_mods.pop()
@@ -407,7 +463,11 @@ class CaribouKeyboard(Gtk.Notebook):
             mod.set_active (False)
 
     def _pressed_layout_switcher_key(self, key):
-        self._switch_to_layout(key.value)
+        _, group, variant = self.vk.get_current_group()
+        self._switch_to_layout('%s_%s' % (group, variant), key.toggle)
+
+    def _on_group_changed(self, vk, groupid, group, variant):
+        self._switch_to_default('%s_%s' % (group, variant))
 
     def _toggled_mask_key(self, key):
         if key.get_active():
@@ -430,13 +490,19 @@ class CaribouKeyboard(Gtk.Notebook):
         p.run()
         p.destroy()
 
-    def _switch_to_layout(self, name):
-        n_pages = self.get_n_pages()
-        for i in range(n_pages):
-            if self.get_nth_page(i).layout_name == name:
-                self.set_current_page(i)
-                self.current_page = i
-                break
+    def _switch_to_default(self, group):
+        try:
+            i = min(self.layouts[group].values())
+        except KeyError:
+            i = 0
+        self.set_current_page(i)
+
+    def _switch_to_layout(self, group, level):
+        if self.layouts.has_key(group):
+            if self.layouts[group].has_key(level):
+                self.set_current_page(self.layouts[group][level])
+                return
+        self._switch_to_default(group)
 
     def get_current_layout(self):
         i = self.get_current_page()
@@ -450,10 +516,9 @@ if __name__ == "__main__":
     signal.signal(signal.SIGINT, signal.SIG_DFL)
 
     w = Gtk.Window()
+    w.set_accept_focus(False)
 
     kb = CaribouKeyboard()
-    kb.load_kb('data/keyboards/qwerty.xml')
-
     w.add(kb)
 
     w.show_all()
diff --git a/caribou/ui/main.py b/caribou/ui/main.py
index 7e861b8..de2302c 100644
--- a/caribou/ui/main.py
+++ b/caribou/ui/main.py
@@ -83,8 +83,8 @@ class Caribou:
         kb = kb_factory()
         self.window = window_factory(kb)
         self._register_event_listeners()
-        SettingsManager.layout.connect("value-changed",
-                                       self._on_layout_changed)
+        SettingsManager.geometry.connect("value-changed",
+                                         self._on_geometry_changed)
 
         # Scanning
         self.scan_master = ScanMaster(self.window, kb)
@@ -157,7 +157,7 @@ class Caribou:
         else:
             self.scan_master.stop()
 
-    def _on_layout_changed(self, setting, val):
+    def _on_geometry_changed(self, setting, val):
         self._deregister_event_listeners()
         self.window.destroy()
         self._update_window()
diff --git a/caribou/ui/window.py b/caribou/ui/window.py
index 471ee1c..2dc7423 100644
--- a/caribou/ui/window.py
+++ b/caribou/ui/window.py
@@ -22,7 +22,6 @@
 
 from caribou import data_path
 from opacity import ProximityWindowBase
-from caribou.common.settings_manager import SettingsManager
 
 from gi.repository import Gtk
 from gi.repository import Gdk
@@ -31,8 +30,6 @@ import os
 import sys
 import gobject
 
-CARIBOU_LAYOUT_DIR = 'keyboards'
-
 Clutter.init("caribou")
 
 class CaribouWindow(Gtk.Window, Clutter.Animatable, ProximityWindowBase):
@@ -66,10 +63,6 @@ class CaribouWindow(Gtk.Window, Clutter.Animatable, ProximityWindowBase):
         self._default_placement = default_placement or \
             CaribouWindowPlacement()
 
-        conf_file_path = self._get_keyboard_conf()
-        if conf_file_path:
-            text_entry_mech.load_kb(conf_file_path)
-
         self.connect('show', self._on_window_show)
 
         # animation
@@ -118,7 +111,6 @@ class CaribouWindow(Gtk.Window, Clutter.Animatable, ProximityWindowBase):
         self.keyboard.destroy()
         super(Gtk.Window, self).destroy()
 
-
     def set_cursor_location(self, cursor_location):
         self._cursor_location = cursor_location
         self._update_position()
@@ -205,26 +197,6 @@ class CaribouWindow(Gtk.Window, Clutter.Animatable, ProximityWindowBase):
 
         return offset
 
-    def _get_keyboard_conf(self):
-        layout = SettingsManager.layout.value
-        conf_file_path = os.path.join(data_path, CARIBOU_LAYOUT_DIR, layout)
-
-        if os.path.exists(conf_file_path):
-            return conf_file_path
-        else:
-            json_path = '%s.json' % conf_file_path
-
-            if os.path.exists(json_path):
-                return json_path
-
-            xml_path = '%s.xml' % conf_file_path
-
-            if os.path.exists(xml_path):
-                return xml_path
-
-	raise Exception("Could not load keyboard %s" % conf_file_path)
-
-
     def show_all(self):
         Gtk.Window.show_all(self)
         self.keyboard.show_all()
diff --git a/configure.ac b/configure.ac
index 40cac78..d41d3e3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -74,6 +74,7 @@ caribou/ui/Makefile
 bin/Makefile
 bin/caribou
 data/Makefile
-data/keyboards/Makefile
+data/layouts/Makefile
+data/layouts/natural/Makefile
 libcaribou/Makefile
 ])
diff --git a/data/Makefile.am b/data/Makefile.am
index 581ca65..98da3b2 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = keyboards
+SUBDIRS = layouts
 
 @GSETTINGS_RULES@
 @INTLTOOL_XML_NOMERGE_RULE@
diff --git a/data/layouts/Makefile.am b/data/layouts/Makefile.am
new file mode 100644
index 0000000..40457f3
--- /dev/null
+++ b/data/layouts/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = natural
diff --git a/data/layouts/natural/Makefile.am b/data/layouts/natural/Makefile.am
new file mode 100644
index 0000000..6bf87b3
--- /dev/null
+++ b/data/layouts/natural/Makefile.am
@@ -0,0 +1,6 @@
+naturallayoutsdir = $(datadir)/caribou/layouts/natural
+
+naturallayouts_DATA = \
+    us.json
+
+EXTRA_DIST = $(naturallayouts_DATA)
diff --git a/data/layouts/natural/us.json b/data/layouts/natural/us.json
new file mode 100644
index 0000000..c153468
--- /dev/null
+++ b/data/layouts/natural/us.json
@@ -0,0 +1,208 @@
+{
+   "level1" : {
+      "rows" : [
+         [{"name" : "q"},
+          {"name" : "w"},
+          {"name" : "e", "extended_names" : ["egrave",
+                                             "eacute",
+                                             "ecircumflex",
+                                             "ediaeresis",
+                                             "emacron"]},
+          {"name" : "r"},
+          {"name" : "t"},
+          {"name" : "y"},
+          {"name" : "u", "extended_names" : ["ugrave",
+                                             "uacute",
+                                             "ucircumflex",
+                                             "udiaeresis",
+                                             "umacron"]},
+          {"name" : "i", "extended_names" : ["igrave",
+                                             "iacute",
+                                             "icircumflex",
+                                             "idiaeresis",
+                                             "imacron"]},
+          {"name" : "o", "extended_names" : ["ograve",
+                                             "oacute",
+                                             "ocircumflex",
+                                             "odiaeresis",
+                                             "omacron"]},
+          {"name" : "p"},
+          {"name" : "BackSpace"}],
+         [{"name" : "a", "extended_names" : ["agrave",
+                                             "aacute",
+                                             "acircumflex",
+                                             "adiaeresis",
+                                             "aring",
+                                             "atilde",
+                                             "ae",
+                                             "amacron"],
+           "margin_left" : 0.5},
+          {"name" : "s"},
+          {"name" : "d"},
+          {"name" : "f"},
+          {"name" : "g"},
+          {"name" : "h"},
+          {"name" : "j"},
+          {"name" : "k"},
+          {"name" : "l"},
+          {"name" : "Return", "width" : 1.5}],
+         [{"name" : "Caribou_ShiftUp", "toggle" : "level2"},
+          {"name" : "z"},
+          {"name" : "x"},
+          {"name" : "c", "extended_names" : ["ccedilla"]},
+          {"name" : "v"},
+          {"name" : "b"},
+          {"name" : "n"},
+          {"name" : "m"},
+          {"name" : "comma"},
+          {"name" : "period"},
+          {"name" : "question"}],
+         [{"name" : "Caribou_Symbols", "width" : 2, "toggle" : "symbols1"},
+          {"name" : "Caribou_Emoticons"},
+          {"name" : "space", "width" : 5},
+          {"name" : "Caribou_Prefs", "margin_left" : 2}]
+      ]
+   },
+   "level2" : {
+      "rows" : [
+         [{"name" : "Q"},
+          {"name" : "W"},
+          {"name" : "E", "extended_names" : ["Egrave",
+                                             "Eacute",
+                                             "Ecircumflex",
+                                             "Ediaeresis",
+                                             "Emacron"]},
+          {"name" : "R"},
+          {"name" : "T"},
+          {"name" : "Y"},
+          {"name" : "U", "extended_names" : ["Ugrave",
+                                             "Uacute",
+                                             "Ucircumflex",
+                                             "Udiaeresis",
+                                             "Umacron"]},
+          {"name" : "I", "extended_names" : ["Igrave",
+                                             "Iacute",
+                                             "Icircumflex",
+                                             "Idiaeresis",
+                                             "Imacron"]},
+          {"name" : "O", "extended_names" : ["Ograve",
+                                             "Oacute",
+                                             "Ocircumflex",
+                                             "Odiaeresis",
+                                             "Omacron"]},
+          {"name" : "P"},
+          {"name" : "BackSpace"}],
+         [{"name" : "A", "extended_names" : ["Agrave",
+                                             "Aacute",
+                                             "Acircumflex",
+                                             "Adiaeresis",
+                                             "Aring",
+                                             "Atilde",
+                                             "Ae",
+                                             "Amacron"],
+           "margin_left" : 0.5},
+          {"name" : "S"},
+          {"name" : "D"},
+          {"name" : "F"},
+          {"name" : "G"},
+          {"name" : "H"},
+          {"name" : "J"},
+          {"name" : "K"},
+          {"name" : "L"},
+          {"name" : "Return", "width" : 1.5}],
+         [{"name" : "Caribou_ShiftDown", "toggle" : "level1"},
+          {"name" : "Z"},
+          {"name" : "X"},
+          {"name" : "C", "extended_names" : ["Ccedilla"]},
+          {"name" : "V"},
+          {"name" : "B"},
+          {"name" : "N"},
+          {"name" : "M"},
+          {"name" : "comma"},
+          {"name" : "period"},
+          {"name" : "question"}],
+         [{"name" : "Caribou_Symbols", "width" : 2, "toggle" : "symbols1"},
+          {"name" : "Caribou_Emoticons"},
+          {"name" : "space", "width" : 5},
+          {"name" : "Caribou_Prefs", "margin_left" : 2}]
+      ]
+   },
+   "symbols1" : {
+      "rows" : [
+         [{"name" : "1"},
+          {"name" : "2"},
+          {"name" : "3"},
+          {"name" : "4"},
+          {"name" : "5"},
+          {"name" : "6"},
+          {"name" : "7"},
+          {"name" : "8"},
+          {"name" : "9"},
+          {"name" : "0"},
+          {"name" : "BackSpace"}],
+         [{"name" : "minus", "margin_left" : 0.5},
+          {"name" : "slash"},
+          {"name" : "colon"},
+          {"name" : "semicolon"},
+          {"name" : "parenleft"},
+          {"name" : "parenright"},
+          {"name" : "dollar"},
+          {"name" : "ampersand"},
+          {"name" : "at"},
+          {"name" : "Return", "width" : 1.5}],
+         [{"name" : "Caribou_Symbols_More", "width" : 2, "toggle" : "symbols2"},
+          {"name" : "period"},
+          {"name" : "comma"},
+          {"name" : "question"},
+          {"name" : "slash"},
+          {"name" : "exclam"},
+          {"name" : "apostrophe"},
+          {"name" : "quotedbl"},
+          {"name" : "bar"},
+          {"name" : "numbersign"}],
+         [{"name" : "Caribou_Alpha", "width" : 2, "toggle" : "level1"},
+          {"name" : "Caribou_Emoticons"},
+          {"name" : "space", "width" : 5},
+          {"name" : "Caribou_Prefs", "margin_left" : 2}]
+      ]
+   },
+   "symbols2" : {
+      "rows" : [
+         [{"name" : "percent"},
+          {"name" : "bracketleft"},
+          {"name" : "bracketright"},
+          {"name" : "braceleft"},
+          {"name" : "braceright"},
+          {"name" : "backslash"},
+          {"name" : "plus"},
+          {"name" : "equal"},
+          {"name" : "grave"},
+          {"name" : "underscore"},
+          {"name" : "BackSpace"}],
+         [{"name" : "less", "margin_left" : 0.5},
+          {"name" : "greater"},
+          {"name" : "asterisk"},
+          {"name" : "semicolon"},
+          {"name" : "parenleft"},
+          {"name" : "parenright"},
+          {"name" : "dollar"},
+          {"name" : "ampersand"},
+          {"name" : "at"},
+          {"name" : "Return", "width" : 1.5}],
+         [{"name" : "Caribou_Symbols", "width" : 2, "toggle" : "symbols1"},
+          {"name" : "period"},
+          {"name" : "comma"},
+          {"name" : "question"},
+          {"name" : "slash"},
+          {"name" : "exclam"},
+          {"name" : "apostrophe"},
+          {"name" : "quotedbl"},
+          {"name" : "bar"},
+          {"name" : "numbersign"}],
+         [{"name" : "Caribou_Alpha", "width" : 2, "toggle" : "level1"},
+          {"name" : "Caribou_Emoticons"},
+          {"name" : "space", "width" : 5},
+          {"name" : "Caribou_Prefs", "margin_left" : 2}]
+      ]
+   }
+}
\ No newline at end of file



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]