[gnome-initial-setup/shell/4765: 292/362] Port Python keyboard detector to C



commit 98c0b8662d8a82e2ca02227f2e1b7b3a21005704
Author: Philip Chimento <philip endlessm com>
Date:   Thu Dec 4 14:35:09 2014 -0800

    Port Python keyboard detector to C
    
    This is a straightforward port to C of the keyboard detector code
    from Ubuntu's installer; with the exception of "Cancel" and "Add" buttons
    in the dialog box. When the detection heuristic has finished, the "Add"
    button becomes active.
    
    (Roddy, Philip)
    
    [endlessm/eos-shell#3425]

 gnome-initial-setup/pages/keyboard/Makefile.am     |    3 +
 gnome-initial-setup/pages/keyboard/cc-key-row.c    |   62 ++++
 gnome-initial-setup/pages/keyboard/cc-key-row.h    |   58 ++++
 .../pages/keyboard/cc-keyboard-detector.c          |  230 +++++++++++++
 .../pages/keyboard/cc-keyboard-detector.h          |   58 ++++
 .../pages/keyboard/cc-keyboard-query.c             |  343 ++++++++++++++++++++
 .../pages/keyboard/cc-keyboard-query.h             |   66 ++++
 .../pages/keyboard/keyboard-detector.ui            |   98 ++++++
 .../pages/keyboard/keyboard.gresource.xml          |    1 +
 9 files changed, 919 insertions(+), 0 deletions(-)
---
diff --git a/gnome-initial-setup/pages/keyboard/Makefile.am b/gnome-initial-setup/pages/keyboard/Makefile.am
index 1d8831d..28b3fbd 100644
--- a/gnome-initial-setup/pages/keyboard/Makefile.am
+++ b/gnome-initial-setup/pages/keyboard/Makefile.am
@@ -18,6 +18,9 @@ BUILT_SOURCES += keyboard-resources.c keyboard-resources.h
 libgiskeyboard_la_SOURCES =                            \
        cc-input-chooser.c cc-input-chooser.h           \
        cc-ibus-utils.c cc-ibus-utils.h                 \
+       cc-keyboard-detector.c cc-keyboard-detector.h   \
+       cc-keyboard-query.c cc-keyboard-query.h         \
+       cc-key-row.c cc-key-row.h                       \
        gis-keyboard-page.c gis-keyboard-page.h         \
        $(BUILT_SOURCES)
 
diff --git a/gnome-initial-setup/pages/keyboard/cc-key-row.c b/gnome-initial-setup/pages/keyboard/cc-key-row.c
new file mode 100644
index 0000000..e36b7de
--- /dev/null
+++ b/gnome-initial-setup/pages/keyboard/cc-key-row.c
@@ -0,0 +1,62 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/*
+ * Copyright (C) 2014 Endless Mobile, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "cc-key-row.h"
+
+G_DEFINE_TYPE (CcKeyRow, cc_key_row, GTK_TYPE_BOX);
+
+static void
+cc_key_row_class_init (CcKeyRowClass *klass)
+{
+}
+
+static void
+cc_key_row_init (CcKeyRow *self)
+{
+  gtk_box_set_spacing (GTK_BOX (self), 24);
+}
+
+GtkWidget *
+cc_key_row_new (void)
+{
+  return g_object_new (CC_TYPE_KEY_ROW, NULL);
+}
+
+void
+cc_key_row_add_character (CcKeyRow   *self,
+                          const char *key_label)
+{
+  char *label_string = g_strdup_printf ("<big>%s</big>", key_label);
+  GtkWidget *label = gtk_label_new (label_string);
+  g_free (label_string);
+
+  gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
+  gtk_box_pack_start (GTK_BOX (self), label, TRUE, TRUE, 0);
+  gtk_widget_show (label);
+}
+
+void
+cc_key_row_clear (CcKeyRow *self)
+{
+  GList *children = gtk_container_get_children (GTK_CONTAINER (self));
+  g_list_foreach (children, (GFunc) gtk_widget_destroy, NULL);
+  g_list_free (children);
+}
diff --git a/gnome-initial-setup/pages/keyboard/cc-key-row.h b/gnome-initial-setup/pages/keyboard/cc-key-row.h
new file mode 100644
index 0000000..c08f0c1
--- /dev/null
+++ b/gnome-initial-setup/pages/keyboard/cc-key-row.h
@@ -0,0 +1,58 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/*
+ * Copyright (C) 2014 Endless Mobile, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef CC_KEY_ROW_H
+#define CC_KEY_ROW_H
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define CC_TYPE_KEY_ROW cc_key_row_get_type()
+#define CC_KEY_ROW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CC_TYPE_KEY_ROW, CcKeyRow))
+#define CC_KEY_ROW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CC_TYPE_KEY_ROW, CcKeyRowClass))
+#define CC_IS_KEY_ROW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CC_TYPE_KEY_ROW))
+#define CC_IS_KEY_ROW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CC_TYPE_KEY_ROW))
+#define CC_KEY_ROW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CC_TYPE_KEY_ROW, CcKeyRowClass))
+
+typedef struct _CcKeyRow CcKeyRow;
+typedef struct _CcKeyRowClass CcKeyRowClass;
+
+struct _CcKeyRow
+{
+  GtkBox parent;
+};
+
+struct _CcKeyRowClass
+{
+  GtkBoxClass parent_class;
+};
+
+GType      cc_key_row_get_type      (void) G_GNUC_CONST;
+GtkWidget *cc_key_row_new           (void);
+void       cc_key_row_add_character (CcKeyRow   *self,
+                                     const char *label);
+void       cc_key_row_clear         (CcKeyRow   *self);
+
+G_END_DECLS
+
+#endif /* CC_KEY_ROW_H */
diff --git a/gnome-initial-setup/pages/keyboard/cc-keyboard-detector.c 
b/gnome-initial-setup/pages/keyboard/cc-keyboard-detector.c
new file mode 100644
index 0000000..7993899
--- /dev/null
+++ b/gnome-initial-setup/pages/keyboard/cc-keyboard-detector.c
@@ -0,0 +1,230 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/*
+ * Copyright (C) 2014 Endless Mobile, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <string.h>
+
+#include "cc-keyboard-detector.h"
+
+KeyboardDetector *
+keyboard_detector_new (void)
+{
+  GError *error = NULL;
+  GInputStream *istream;
+  KeyboardDetector *det = g_new0 (KeyboardDetector, 1);
+
+  det->current_step = -1;
+  istream = g_resources_open_stream ("/org/gnome/initial-setup/pc105.tree",
+                                     G_RESOURCE_LOOKUP_FLAGS_NONE, &error);
+  if (istream == NULL)
+    g_error ("Error loading keyboard detector tree: %s", error->message);
+  det->fp = g_data_input_stream_new (istream);
+  g_object_unref (istream);
+
+  det->keycodes = g_hash_table_new (NULL, NULL);
+  det->symbols = NULL;
+  det->present = -1;
+  det->not_present = -1;
+  det->result = NULL;
+
+  return det;
+}
+
+static void
+keyboard_detector_clear (KeyboardDetector *det)
+{
+  g_hash_table_remove_all (det->keycodes);
+  g_list_foreach (det->symbols, (GFunc) g_free, NULL);
+  g_clear_pointer (&det->symbols, g_list_free);
+  det->present = -1;
+  det->not_present = -1;
+  g_clear_pointer (&det->result, g_free);
+}
+
+void
+keyboard_detector_free (KeyboardDetector *det)
+{
+  keyboard_detector_clear (det);
+  g_object_unref (det->fp);
+  g_hash_table_destroy (det->keycodes);
+  g_free (det);
+}
+
+KeyboardDetectorStepType
+keyboard_detector_read_step (KeyboardDetector *det,
+                             int               step)
+{
+  char *line;
+  size_t len;
+
+  if (det->current_step != -1)
+    {
+      GList *valid_steps = g_hash_table_get_values (det->keycodes);
+      gboolean found = (g_list_find (valid_steps, GINT_TO_POINTER (step)) != NULL);
+      g_list_free (valid_steps);
+      if (!(found || step == det->present || step == det->not_present))
+        /* Invalid argument */
+        return ERROR;
+      if (det->result)
+        /* Already done */
+        return ERROR;
+    }
+
+  KeyboardDetectorStepType step_type = UNKNOWN;
+  keyboard_detector_clear (det);
+
+  while ((line = g_data_input_stream_read_line_utf8 (det->fp, &len, NULL, NULL)) != NULL)
+    {
+      if (g_str_has_prefix (line, "STEP "))
+        {
+          /* This line starts a new step. */
+          int new_step = atoi (line + 5);
+          g_free (line);
+          if (det->current_step == step)
+            {
+              det->current_step = new_step;
+              return step_type;
+            }
+          else
+            {
+              det->current_step = new_step;
+            }
+        }
+      else if (det->current_step != step)
+        {
+          g_free (line);
+          continue;
+        }
+      else if (g_str_has_prefix (line, "PRESS "))
+        {
+          /* Ask the user to press a character on the keyboard. */
+          if (step_type == UNKNOWN)
+            step_type = PRESS_KEY;
+          if (step_type != PRESS_KEY)
+            {
+              g_free (line);
+              return ERROR;
+            }
+          char *key_symbol = g_strdup (g_strstrip (line + 6));
+          g_free (line);
+          det->symbols = g_list_append (det->symbols, key_symbol);
+        }
+      else if (g_str_has_prefix (line, "CODE "))
+        {
+          /* Direct the evaluating code to process step ## next if the
+           * user has pressed a key which returned that keycode.
+           */
+          if (step_type != PRESS_KEY)
+            {
+              g_free (line);
+              return ERROR;
+            }
+          char *keycode = strtok (line + 5, " ");
+          char *s = strtok (NULL, " ");
+          int code = atoi (keycode);
+          int next_step = atoi (s);
+          g_free (line);
+          g_hash_table_insert (det->keycodes, GINT_TO_POINTER (code), GINT_TO_POINTER (next_step));
+        }
+      else if (g_str_has_prefix (line, "FIND "))
+        {
+          /* Ask the user whether that character is present on their
+           * keyboard.
+           */
+          if (step_type == UNKNOWN)
+            {
+              step_type = KEY_PRESENT;
+            }
+          else
+            {
+              g_free (line);
+              return ERROR;
+            }
+          char *key_symbol = g_strdup (g_strstrip (line + 5));
+          g_free (line);
+          det->symbols = g_list_prepend (det->symbols, key_symbol);
+        }
+      else if (g_str_has_prefix (line, "FINDP "))
+        {
+          /* Equivalent to FIND, except that the user is asked to consider
+           * only the primary symbols (i.e. Plain and Shift).
+           */
+          if (step_type == UNKNOWN)
+            {
+              step_type = KEY_PRESENT_P;
+            }
+          else
+            {
+              g_free (line);
+              return ERROR;
+            }
+          char *key_symbol = g_strdup (g_strstrip (line + 6));
+          g_free (line);
+          det->symbols = g_list_prepend (det->symbols, key_symbol);
+        }
+      else if (g_str_has_prefix (line, "YES "))
+        {
+          /* Direct the evaluating code to process step ## next if the
+           * user does have this key.
+           */
+          int next_step = atoi (g_strstrip (line + 4));
+          g_free (line);
+          if (step_type != KEY_PRESENT_P &&
+              step_type != KEY_PRESENT)
+            return ERROR;
+          det->present = next_step;
+        }
+      else if (g_str_has_prefix (line, "NO "))
+        {
+          /* Direct the evaluating code to process step ## next if the
+           * user does not have this key.
+           */
+          int next_step = atoi (g_strstrip (line + 3));
+          g_free (line);
+          if (step_type != KEY_PRESENT_P &&
+              step_type != KEY_PRESENT)
+            return ERROR;
+          det->not_present = next_step;
+        }
+      else if (g_str_has_prefix (line, "MAP "))
+        {
+          /* This step uniquely identifies a keymap. */
+          if (step_type == UNKNOWN)
+            step_type = RESULT;
+          det->result = g_strdup (g_strstrip (line + 4));
+          g_free (line);
+          /* The Ubuntu file uses colons to separate country codes from layout
+           * variants, and GnomeXkb requires plus signs.
+           */
+          char *colon_pointer = strchr (det->result, ':');
+          if (colon_pointer != NULL)
+            *colon_pointer = '+';
+          return step_type;
+        }
+      else {
+        g_free (line);
+        return ERROR;
+      }
+    }
+
+  /* The requested step was not found. */
+  return ERROR;
+}
diff --git a/gnome-initial-setup/pages/keyboard/cc-keyboard-detector.h 
b/gnome-initial-setup/pages/keyboard/cc-keyboard-detector.h
new file mode 100644
index 0000000..9f490a5
--- /dev/null
+++ b/gnome-initial-setup/pages/keyboard/cc-keyboard-detector.h
@@ -0,0 +1,58 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/*
+ * Copyright (C) 2014 Endless Mobile, Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef CC_KEYBOARD_DETECTOR_H
+#define CC_KEYBOARD_DETECTOR_H
+
+#include <glib.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+  UNKNOWN,
+  PRESS_KEY,
+  KEY_PRESENT,
+  KEY_PRESENT_P,
+  RESULT,
+  ERROR,
+} KeyboardDetectorStepType;
+
+typedef struct {
+  GHashTable *keycodes; /* GHashTable<int, int> */
+  GList *symbols;       /* GList<char *>, strings owned by KeyboardDetector */
+  int present;
+  int not_present;
+  char *result;
+
+  /* Private */
+  int current_step;
+  GDataInputStream *fp;
+} KeyboardDetector;
+
+KeyboardDetector        *keyboard_detector_new       (void);
+void                     keyboard_detector_free      (KeyboardDetector *det);
+KeyboardDetectorStepType keyboard_detector_read_step (KeyboardDetector *det,
+                                                      int               step);
+
+G_END_DECLS
+
+#endif /* CC_KEYBOARD_DETECTOR_H */
diff --git a/gnome-initial-setup/pages/keyboard/cc-keyboard-query.c 
b/gnome-initial-setup/pages/keyboard/cc-keyboard-query.c
new file mode 100644
index 0000000..4bdfccb
--- /dev/null
+++ b/gnome-initial-setup/pages/keyboard/cc-keyboard-query.c
@@ -0,0 +1,343 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/*
+ * Copyright (C) 2014 Endless Mobile, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <glib/gi18n.h>
+
+#include "cc-keyboard-detector.h"
+#include "cc-keyboard-query.h"
+#include "cc-key-row.h"
+
+typedef struct
+{
+  KeyboardDetector *det;
+  char *detected_id;
+  char *detected_display_name;
+
+  const char *press_string;
+  const char *present_string;
+
+  GtkWidget *vbox;
+  GtkWidget *heading;
+  GtkWidget *keyrow;
+  GtkWidget *buttons;
+  GtkWidget *add_button;
+
+  GnomeXkbInfo *xkb_data;
+} CcKeyboardQueryPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (CcKeyboardQuery, cc_keyboard_query, GTK_TYPE_DIALOG);
+
+enum {
+  LAYOUT_RESULT,
+  N_SIGNALS,
+};
+
+enum {
+  PROP_0,
+  XKB_DATA,
+  N_PROPS,
+};
+
+static guint cc_keyboard_query_signals[N_SIGNALS] = { 0, };
+static GParamSpec *cc_keyboard_query_props[N_PROPS] = { NULL, };
+
+static void
+process (CcKeyboardQuery         *self,
+         KeyboardDetectorStepType result)
+{
+  CcKeyboardQueryPrivate *priv = cc_keyboard_query_get_instance_private (self);
+  GList *iter;
+
+  cc_key_row_clear (CC_KEY_ROW (priv->keyrow));
+  for (iter = priv->det->symbols; iter != NULL; iter = iter->next)
+    cc_key_row_add_character (CC_KEY_ROW (priv->keyrow), iter->data);
+
+  switch (result)
+    {
+    case PRESS_KEY:
+      gtk_label_set_label (GTK_LABEL (priv->heading), priv->press_string);
+      gtk_widget_hide (GTK_WIDGET (priv->buttons));
+      break;
+    case KEY_PRESENT:
+    case KEY_PRESENT_P:
+      gtk_label_set_label (GTK_LABEL (priv->heading), priv->present_string);
+      gtk_widget_show (GTK_WIDGET (priv->buttons));
+      break;
+    case RESULT:
+      g_signal_emit (self, cc_keyboard_query_signals[LAYOUT_RESULT], 0,
+                     priv->det->result);
+      break;
+    case ERROR:
+      gtk_widget_hide (GTK_WIDGET (self));
+      break;
+    default:
+      g_assert_not_reached ();
+    }
+}
+
+static void
+have_key (GtkButton       *button,
+          CcKeyboardQuery *self)
+{
+  CcKeyboardQueryPrivate *priv = cc_keyboard_query_get_instance_private (self);
+  KeyboardDetectorStepType result;
+
+  result = keyboard_detector_read_step (priv->det, priv->det->present);
+  process (self, result);
+}
+
+static void
+no_have_key (GtkButton       *button,
+             CcKeyboardQuery *self)
+{
+  CcKeyboardQueryPrivate *priv = cc_keyboard_query_get_instance_private (self);
+  KeyboardDetectorStepType result;
+
+  result = keyboard_detector_read_step (priv->det, priv->det->not_present);
+  process (self, result);
+}
+
+static gboolean
+key_press_event (GtkWidget       *widget,
+                 GdkEventKey     *event,
+                 CcKeyboardQuery *self)
+{
+  CcKeyboardQueryPrivate *priv = cc_keyboard_query_get_instance_private (self);
+  KeyboardDetectorStepType result;
+  int code, new_step;
+  gpointer c;
+
+  /* FIXME need to account for possible remapping.  Find the API to translate
+   * kernel keycodes to X keycodes (xkb).
+   * MIN_KEYCODE = 8
+   */
+
+  /* FIXME escape should close the window. */
+
+  code = event->hardware_keycode - 8;
+  if (code > 255)
+    return GDK_EVENT_PROPAGATE;
+  /* XKB doesn't support keycodes > 255. */
+
+  c = g_hash_table_lookup (priv->det->keycodes, GINT_TO_POINTER (code));
+  if (c == NULL)
+    return GDK_EVENT_PROPAGATE;
+
+  new_step = GPOINTER_TO_INT (c);
+  result = keyboard_detector_read_step (priv->det, new_step);
+  process (self, result);
+  return GDK_EVENT_STOP;
+}
+
+static void
+cc_keyboard_query_constructed (GObject *object)
+{
+  CcKeyboardQuery *self = CC_KEYBOARD_QUERY (object);
+  CcKeyboardQueryPrivate *priv = cc_keyboard_query_get_instance_private (self);
+
+  priv->keyrow = cc_key_row_new ();
+  gtk_box_pack_start (GTK_BOX (priv->vbox), priv->keyrow, FALSE, TRUE, 0);
+  gtk_box_reorder_child (GTK_BOX (priv->vbox), priv->keyrow, 1);
+
+  gtk_widget_hide (priv->buttons);
+
+  G_OBJECT_CLASS (cc_keyboard_query_parent_class)->constructed (object);
+}
+
+static void
+cc_keyboard_query_get_property (GObject    *object,
+                                guint       id,
+                                GValue     *value,
+                                GParamSpec *pspec)
+{
+  CcKeyboardQuery *self = CC_KEYBOARD_QUERY (object);
+  CcKeyboardQueryPrivate *priv = cc_keyboard_query_get_instance_private (self);
+
+  switch (id)
+    {
+    case XKB_DATA:
+      g_value_set_object (value, priv->xkb_data);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, id, pspec);
+    }
+}
+
+static void
+cc_keyboard_query_set_property (GObject      *object,
+                                guint         id,
+                                const GValue *value,
+                                GParamSpec   *pspec)
+{
+  CcKeyboardQuery *self = CC_KEYBOARD_QUERY (object);
+  CcKeyboardQueryPrivate *priv = cc_keyboard_query_get_instance_private (self);
+
+  switch (id)
+    {
+    case XKB_DATA:
+      priv->xkb_data = g_value_dup_object (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, id, pspec);
+    }
+}
+
+static void
+cc_keyboard_query_dispose (GObject *object)
+{
+  CcKeyboardQuery *self = CC_KEYBOARD_QUERY (object);
+  CcKeyboardQueryPrivate *priv = cc_keyboard_query_get_instance_private (self);
+
+  g_clear_object (&priv->xkb_data);
+
+  G_OBJECT_CLASS (cc_keyboard_query_parent_class)->dispose (object);
+}
+
+static void
+cc_keyboard_query_finalize (GObject *object)
+{
+  CcKeyboardQuery *self = CC_KEYBOARD_QUERY (object);
+  CcKeyboardQueryPrivate *priv = cc_keyboard_query_get_instance_private (self);
+
+  g_clear_pointer (&priv->det, keyboard_detector_free);
+  g_clear_pointer (&priv->detected_id, g_free);
+  g_clear_pointer (&priv->detected_display_name, g_free);
+
+  G_OBJECT_CLASS (cc_keyboard_query_parent_class)->finalize (object);
+}
+
+/* Default handler for CcKeyboardQuery::layout-result */
+static void
+cc_keyboard_query_layout_result (CcKeyboardQuery *self,
+                                 const char      *result)
+{
+  CcKeyboardQueryPrivate *priv = cc_keyboard_query_get_instance_private (self);
+  char *result_message;
+  const char *display_name = NULL;
+
+  priv->detected_id = g_strdup (result);
+  if (priv->xkb_data != NULL)
+    gnome_xkb_info_get_layout_info (priv->xkb_data, result, &display_name, NULL,
+                                    NULL, NULL);
+
+  priv->detected_display_name = g_strdup (display_name);
+  result_message = g_strdup_printf ("%s\n%s",
+                                    _("Your keyboard layout seems to be:"),
+                                    display_name ? display_name : result);
+  gtk_label_set_label (GTK_LABEL (priv->heading), result_message);
+  gtk_widget_hide (priv->buttons);
+  gtk_widget_hide (priv->keyrow);
+  gtk_widget_set_sensitive (priv->add_button, TRUE);
+
+  g_free (result_message);
+}
+
+static void
+cc_keyboard_query_class_init (CcKeyboardQueryClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  cc_keyboard_query_signals[LAYOUT_RESULT] =
+    g_signal_new ("layout-result", CC_TYPE_KEYBOARD_QUERY, G_SIGNAL_RUN_FIRST,
+                  G_STRUCT_OFFSET (CcKeyboardQueryClass, layout_result),
+                  NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE,
+                  1, G_TYPE_STRING);
+
+  cc_keyboard_query_props[XKB_DATA] =
+    g_param_spec_object ("xkb-data", "XKB data",
+                         "X Keyboard info for looking up display names of keyboard layouts",
+                         GNOME_TYPE_XKB_INFO,
+                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+  object_class->constructed = cc_keyboard_query_constructed;
+  object_class->get_property = cc_keyboard_query_get_property;
+  object_class->set_property = cc_keyboard_query_set_property;
+  object_class->dispose = cc_keyboard_query_dispose;
+  object_class->finalize = cc_keyboard_query_finalize;
+  g_object_class_install_properties (object_class, N_PROPS,
+                                     cc_keyboard_query_props);
+
+  gtk_widget_class_set_template_from_resource (widget_class,
+                                               "/org/gnome/initial-setup/keyboard-detector.ui");
+  gtk_widget_class_bind_template_child_private (widget_class, CcKeyboardQuery, vbox);
+  gtk_widget_class_bind_template_child_private (widget_class, CcKeyboardQuery, heading);
+  gtk_widget_class_bind_template_child_private (widget_class, CcKeyboardQuery, buttons);
+  gtk_widget_class_bind_template_child_private (widget_class, CcKeyboardQuery, add_button);
+  gtk_widget_class_bind_template_callback (widget_class, have_key);
+  gtk_widget_class_bind_template_callback (widget_class, no_have_key);
+  gtk_widget_class_bind_template_callback (widget_class, key_press_event);
+
+  klass->layout_result = cc_keyboard_query_layout_result;
+}
+
+static void
+cc_keyboard_query_init (CcKeyboardQuery *self)
+{
+  CcKeyboardQueryPrivate *priv = cc_keyboard_query_get_instance_private (self);
+
+  gtk_widget_init_template (GTK_WIDGET (self));
+
+  priv->press_string = _("Please press one of the following keys:");
+  priv->present_string = _("Is the following key present on your keyboard?");
+
+  priv->det = keyboard_detector_new ();
+}
+
+GtkWidget *
+cc_keyboard_query_new (GtkWindow    *main_window,
+                       GnomeXkbInfo *xkb_data)
+{
+  return g_object_new (CC_TYPE_KEYBOARD_QUERY,
+                       "transient-for", main_window,
+                       "xkb-data", xkb_data,
+                       NULL);
+}
+
+void
+cc_keyboard_query_run (CcKeyboardQuery *self)
+{
+  CcKeyboardQueryPrivate *priv = cc_keyboard_query_get_instance_private (self);
+  KeyboardDetectorStepType result;
+
+  gtk_widget_show_all (GTK_WIDGET (self));
+  result = keyboard_detector_read_step (priv->det, 0);
+  process (self, result);
+}
+
+gboolean
+cc_keyboard_query_get_selected (CcKeyboardQuery *self,
+                                gchar          **id,
+                                gchar          **name)
+{
+  CcKeyboardQueryPrivate *priv = cc_keyboard_query_get_instance_private (self);
+
+  if (!priv->detected_id)
+    return FALSE;
+
+  if (id != NULL)
+    *id = g_strdup (priv->detected_id);
+  if (name != NULL)
+    *name = g_strdup (priv->detected_display_name);
+
+  return TRUE;
+}
diff --git a/gnome-initial-setup/pages/keyboard/cc-keyboard-query.h 
b/gnome-initial-setup/pages/keyboard/cc-keyboard-query.h
new file mode 100644
index 0000000..1329af1
--- /dev/null
+++ b/gnome-initial-setup/pages/keyboard/cc-keyboard-query.h
@@ -0,0 +1,66 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/*
+ * Copyright (C) 2014 Endless Mobile, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef CC_KEYBOARD_QUERY_H
+#define CC_KEYBOARD_QUERY_H
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#define GNOME_DESKTOP_USE_UNSTABLE_API
+#include <libgnome-desktop/gnome-xkb-info.h>
+
+G_BEGIN_DECLS
+
+#define CC_TYPE_KEYBOARD_QUERY cc_keyboard_query_get_type()
+#define CC_KEYBOARD_QUERY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CC_TYPE_KEYBOARD_QUERY, CcKeyboardQuery))
+#define CC_KEYBOARD_QUERY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CC_TYPE_KEYBOARD_QUERY, 
CcKeyboardQueryClass))
+#define CC_IS_KEYBOARD_QUERY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CC_TYPE_KEYBOARD_QUERY))
+#define CC_IS_KEYBOARD_QUERY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CC_TYPE_KEYBOARD_QUERY))
+#define CC_KEYBOARD_QUERY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CC_TYPE_KEYBOARD_QUERY, 
CcKeyboardQueryClass))
+
+typedef struct _CcKeyboardQuery CcKeyboardQuery;
+typedef struct _CcKeyboardQueryClass CcKeyboardQueryClass;
+
+struct _CcKeyboardQuery
+{
+  GtkDialog parent;
+};
+
+struct _CcKeyboardQueryClass
+{
+  GtkDialogClass parent_class;
+
+  /* signals */
+  void (*layout_result)(CcKeyboardQuery *, const char *);
+};
+
+GType      cc_keyboard_query_get_type     (void) G_GNUC_CONST;
+GtkWidget *cc_keyboard_query_new          (GtkWindow       *main_window,
+                                           GnomeXkbInfo    *xkb_info);
+void       cc_keyboard_query_run          (CcKeyboardQuery *self);
+gboolean   cc_keyboard_query_get_selected (CcKeyboardQuery *self,
+                                           char           **id,
+                                           char           **name);
+
+G_END_DECLS
+
+#endif /* CC_KEYBOARD_QUERY_H */
diff --git a/gnome-initial-setup/pages/keyboard/keyboard-detector.ui 
b/gnome-initial-setup/pages/keyboard/keyboard-detector.ui
new file mode 100644
index 0000000..4276729
--- /dev/null
+++ b/gnome-initial-setup/pages/keyboard/keyboard-detector.ui
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.0 -->
+  <template class="CcKeyboardQuery" parent="GtkDialog">
+    <property name="title" translatable="yes">Detect Keyboard Layout...</property>
+    <property name="default_height">-1</property>
+    <property name="modal">True</property>
+    <property name="border-width">20</property>
+    <property name="destroy_with_parent">True</property>
+    <property name="resizable">False</property>
+    <signal name="key-press-event" handler="key_press_event"/>
+    <child internal-child="vbox">
+      <object class="GtkBox" id="vbox">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">10</property>
+        <child>
+          <object class="GtkLabel" id="heading">
+            <property name="visible">True</property>
+            <property name="xalign">0</property>
+            <property name="label" translatable="yes">Please press one of the following keys:</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkButtonBox" id="buttons">
+            <property name="visible">False</property>
+            <property name="spacing">12</property>
+            <property name="layout_style">start</property>
+            <property name="orientation">horizontal</property>
+            <child>
+              <object class="GtkButton" id="no_button">
+                <property name="visible">True</property>
+                <property name="label">gtk-no</property>
+                <property name="use_stock">True</property>
+                <signal name="clicked" handler="no_have_key"/>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="yes_button">
+                <property name="visible">True</property>
+                <property name="label">gtk-yes</property>
+                <property name="use_stock">True</property>
+                <signal name="clicked" handler="have_key"/>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">2</property>
+          </packing>
+        </child>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox" id="action_area">
+            <property name="visible">True</property>
+            <property name="orientation">horizontal</property>
+            <child>
+              <object class="GtkButton" id="cancel_button">
+                <property name="visible">True</property>
+                <property name="label">gtk-cancel</property>
+                <property name="use_stock">True</property>
+                <property name="use_underline" >True</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkButton" id="add_button">
+                <property name="visible">True</property>
+                <property name="sensitive">False</property>
+                <property name="label">gtk-add</property>
+                <property name="use_stock">True</property>
+                <property name="use_underline" >True</property>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="-5">add_button</action-widget>
+      <action-widget response="-6">cancel_button</action-widget>
+    </action-widgets>
+  </template>
+</interface>
diff --git a/gnome-initial-setup/pages/keyboard/keyboard.gresource.xml 
b/gnome-initial-setup/pages/keyboard/keyboard.gresource.xml
index b3aaad6..db34d78 100644
--- a/gnome-initial-setup/pages/keyboard/keyboard.gresource.xml
+++ b/gnome-initial-setup/pages/keyboard/keyboard.gresource.xml
@@ -3,6 +3,7 @@
   <gresource prefix="/org/gnome/initial-setup">
     <file preprocess="xml-stripblanks">gis-keyboard-page.ui</file>
     <file preprocess="xml-stripblanks">input-chooser.ui</file>
+    <file preprocess="xml-stripblanks">keyboard-detector.ui</file>
     <file>pc105.tree</file>
   </gresource>
 </gresources>


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