[gnome-initial-setup/wip/pwithnall/misc-fixes: 20/70] keyboard: introduce detector




commit 23852494409507972551fb6e4d882d83490d8c83
Author: Alessandro Puccetti <alessandro kinvolk io>
Date:   Wed May 10 15:07:18 2017 +0200

    keyboard: introduce detector
    
    This code is coming from the master branch of endless repository
    before rebased on 3.22, and is based on the python code that can be found
    in the Ubuntu installer.
    
    References:
        https://phabricator.endlessm.com/T3425

 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          | 275 +++++++++++++
 .../pages/keyboard/cc-keyboard-detector.h          |  59 +++
 .../pages/keyboard/cc-keyboard-query.c             | 297 ++++++++++++++
 .../pages/keyboard/cc-keyboard-query.h             |  65 +++
 .../pages/keyboard/detector-trees/C/pc105.tree     | 442 +++++++++++++++++++++
 .../pages/keyboard/detector-trees/es/pc105.tree    |  29 ++
 .../pages/keyboard/detector-trees/pt/pc105.tree    |  29 ++
 .../pages/keyboard/gis-keyboard-page.c             |  38 ++
 .../pages/keyboard/gis-keyboard-page.ui            |  11 +
 .../pages/keyboard/keyboard-detector.ui            |  93 +++++
 .../pages/keyboard/keyboard.gresource.xml          |   4 +
 gnome-initial-setup/pages/keyboard/meson.build     |   6 +
 po/POTFILES.in                                     |   2 +
 15 files changed, 1470 insertions(+)
---
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 00000000..e36b7de0
--- /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 00000000..c08f0c1a
--- /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 00000000..b6fe8e73
--- /dev/null
+++ b/gnome-initial-setup/pages/keyboard/cc-keyboard-detector.c
@@ -0,0 +1,275 @@
+/* -*- 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 <stdio.h>
+#include <stdlib.h>
+
+#include "cc-keyboard-detector.h"
+
+KeyboardDetector *
+keyboard_detector_new (void)
+{
+  GError *error = NULL;
+  GInputStream *istream;
+  KeyboardDetector *det;
+  const gchar * const *language_names = g_get_language_names ();
+  const gchar *language_name;
+  int idx;
+
+  /* Find the detector tree that is the best match for the user's language. */
+  for (idx = 0; (language_name = language_names[idx]) != NULL; idx++)
+    {
+      gchar *path = g_strdup_printf (
+          "/org/gnome/initial-setup/detector-trees/%s/pc105.tree",
+          language_name);
+      g_clear_error (&error);
+      istream = g_resources_open_stream (path,
+                                         G_RESOURCE_LOOKUP_FLAGS_NONE, &error);
+      g_free (path);
+
+      if (istream == NULL)
+        {
+          g_debug ("Unable to load keyboard detector tree for %s: %s",
+                   language_name, error->message);
+          /* Don't clear the error here, as we need the message
+           * for the last error outside the loop.  Instead, we will
+           * clear the error at the start of the next iteration. */
+        }
+      else
+        {
+          g_debug ("Successfully loaded keyboard detector tree for %s",
+                   language_name);
+          break;
+        }
+    }
+
+  if (istream == NULL)
+    g_error ("Error loading keyboard detector tree: %s", error->message);
+
+  det = g_new0 (KeyboardDetector, 1);
+
+  det->current_step = -1;
+  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;
+  det->step_type = UNKNOWN;
+
+  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;
+  det->step_type = UNKNOWN;
+  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);
+}
+
+/* Return value: TRUE if should read another line */
+static gboolean
+process_line (KeyboardDetector         *det,
+              KeyboardDetectorStepType  step,
+              char                     *line,
+              KeyboardDetectorStepType *result)
+{
+  if (g_str_has_prefix (line, "STEP "))
+    {
+      /* This line starts a new step. */
+      int new_step = atoi (line + 5);
+      if (det->current_step == step)
+        {
+          det->current_step = new_step;
+          *result = det->step_type;
+          return FALSE;
+        }
+      else
+        {
+          det->current_step = new_step;
+        }
+    }
+  else if (det->current_step != step)
+    return TRUE;
+  else if (g_str_has_prefix (line, "PRESS "))
+    {
+      /* Ask the user to press a character on the keyboard. */
+      if (det->step_type == UNKNOWN)
+        det->step_type = PRESS_KEY;
+      if (det->step_type != PRESS_KEY)
+        {
+          *result = ERROR;
+          return FALSE;
+        }
+      char *key_symbol = g_strdup (g_strstrip (line + 6));
+      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 (det->step_type != PRESS_KEY)
+        {
+          *result = ERROR;
+          return FALSE;
+        }
+      char *keycode = strtok (line + 5, " ");
+      char *s = strtok (NULL, " ");
+      int code = atoi (keycode);
+      int next_step = atoi (s);
+      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 (det->step_type == UNKNOWN)
+        {
+          det->step_type = KEY_PRESENT;
+        }
+      else
+        {
+          *result = ERROR;
+          return FALSE;
+        }
+      char *key_symbol = g_strdup (g_strstrip (line + 5));
+      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 (det->step_type == UNKNOWN)
+        {
+          det->step_type = KEY_PRESENT_P;
+        }
+      else
+        {
+          *result = ERROR;
+          return FALSE;
+        }
+      char *key_symbol = g_strdup (g_strstrip (line + 6));
+      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.
+       */
+      if (det->step_type != KEY_PRESENT_P &&
+          det->step_type != KEY_PRESENT)
+        {
+          *result = ERROR;
+          return FALSE;
+        }
+      det->present = atoi (g_strstrip (line + 4));
+    }
+  else if (g_str_has_prefix (line, "NO "))
+    {
+      /* Direct the evaluating code to process step ## next if the
+       * user does not have this key.
+       */
+      if (det->step_type != KEY_PRESENT_P &&
+          det->step_type != KEY_PRESENT)
+        {
+          *result = ERROR;
+          return FALSE;
+        }
+      det->not_present = atoi (g_strstrip (line + 3));
+    }
+  else if (g_str_has_prefix (line, "MAP "))
+    {
+      /* This step uniquely identifies a keymap. */
+      if (det->step_type == UNKNOWN)
+        det->step_type = RESULT;
+      det->result = g_strdup (g_strstrip (line + 4));
+      /* 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 = '+';
+      *result = det->step_type;
+      return FALSE;
+    }
+  else
+    {
+      *result = ERROR;
+      return FALSE;
+    }
+  return TRUE;
+}
+
+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;
+    }
+
+  keyboard_detector_clear (det);
+
+  while ((line = g_data_input_stream_read_line_utf8 (det->fp, &len, NULL, NULL)) != NULL)
+    {
+      KeyboardDetectorStepType retval;
+      gboolean should_continue = process_line (det, step, line, &retval);
+      g_free (line);
+      if (!should_continue)
+        return retval;
+    }
+
+  /* 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 00000000..a08ef806
--- /dev/null
+++ b/gnome-initial-setup/pages/keyboard/cc-keyboard-detector.h
@@ -0,0 +1,59 @@
+/* -*- 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;
+  KeyboardDetectorStepType step_type;
+  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 00000000..ec7dab69
--- /dev/null
+++ b/gnome-initial-setup/pages/keyboard/cc-keyboard-query.c
@@ -0,0 +1,297 @@
+/* -*- 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 *select_button;
+
+  GnomeXkbInfo *xkb_data;
+} CcKeyboardQueryPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (CcKeyboardQuery, cc_keyboard_query, GTK_TYPE_DIALOG);
+
+enum {
+  LAYOUT_RESULT,
+  N_SIGNALS,
+};
+
+static guint cc_keyboard_query_signals[N_SIGNALS] = { 0, };
+
+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_finalize (GObject *object)
+{
+  CcKeyboardQuery *self = CC_KEYBOARD_QUERY (object);
+  CcKeyboardQueryPrivate *priv = cc_keyboard_query_get_instance_private (self);
+
+  g_clear_object (&priv->xkb_data);
+
+  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);
+
+  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->select_button, TRUE);
+
+  g_free (result_message);
+}
+
+static void
+cc_keyboard_query_realize (GtkWidget *widget)
+{
+  GdkWindow *window;
+
+  GTK_WIDGET_CLASS (cc_keyboard_query_parent_class)->realize (widget);
+
+  window = gtk_widget_get_window (widget);
+  /* disable all the WM functions */
+  gdk_window_set_functions (window, GDK_FUNC_ALL
+                            | GDK_FUNC_RESIZE
+                            | GDK_FUNC_MOVE
+                            | GDK_FUNC_MINIMIZE
+                            | GDK_FUNC_MAXIMIZE
+                            | GDK_FUNC_CLOSE);
+}
+
+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);
+
+  object_class->constructed = cc_keyboard_query_constructed;
+  object_class->finalize = cc_keyboard_query_finalize;
+
+  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, select_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);
+
+  widget_class->realize = cc_keyboard_query_realize;
+
+  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 ();
+  priv->xkb_data = gnome_xkb_info_new ();
+}
+
+GtkWidget *
+cc_keyboard_query_new (GtkWindow    *main_window)
+{
+  return g_object_new (CC_TYPE_KEYBOARD_QUERY,
+                       "transient-for", main_window,
+                       "use-header-bar", TRUE,
+                       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 00000000..acf1d865
--- /dev/null
+++ b/gnome-initial-setup/pages/keyboard/cc-keyboard-query.h
@@ -0,0 +1,65 @@
+/* -*- 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);
+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/detector-trees/C/pc105.tree 
b/gnome-initial-setup/pages/keyboard/detector-trees/C/pc105.tree
new file mode 100644
index 00000000..76ff3cb9
--- /dev/null
+++ b/gnome-initial-setup/pages/keyboard/detector-trees/C/pc105.tree
@@ -0,0 +1,442 @@
+STEP 0
+PRESS )
+PRESS у
+PRESS υ
+PRESS г
+PRESS n
+PRESS γ
+PRESS u
+PRESS ה
+PRESS v
+PRESS y
+PRESS ν
+CODE 10 1
+CODE 11 39
+CODE 12 76
+CODE 17 44
+CODE 18 63
+CODE 20 40
+CODE 21 83
+CODE 22 104
+CODE 23 2
+CODE 27 108
+CODE 30 2
+CODE 33 40
+CODE 34 41
+CODE 35 44
+CODE 38 40
+CODE 39 2
+CODE 41 44
+CODE 43 91
+CODE 44 106
+CODE 45 68
+CODE 46 2
+CODE 47 104
+CODE 49 104
+CODE 52 40
+STEP 1
+PRESS b
+PRESS ß
+PRESS y
+PRESS v
+PRESS u
+PRESS ה
+PRESS n
+CODE 39 2
+CODE 40 3
+CODE 44 6
+CODE 12 9
+CODE 46 2
+CODE 47 11
+CODE 48 11
+CODE 49 11
+CODE 51 2
+CODE 21 12
+CODE 22 11
+CODE 23 2
+CODE 30 2
+CODE 31 34
+STEP 3
+FINDP ö
+YES 5
+NO 4
+STEP 11
+PRESS z
+CODE 44 12
+CODE 21 6
+STEP 12
+FINDP ö
+YES 26
+NO 13
+STEP 13
+FINDP ç
+YES 21
+NO 14
+STEP 14
+FINDP æ
+YES 18
+NO 15
+STEP 15
+FINDP ñ
+YES 17
+NO 16
+STEP 6
+FINDP ö
+YES 7
+NO 4
+STEP 34
+PRESS z
+CODE 44 35
+CODE 21 8
+STEP 35
+FINDP ö
+YES 38
+NO 36
+STEP 36
+FINDP ç
+YES 21
+NO 37
+STEP 37
+PRESS æ
+CODE 40 19
+CODE 30 17
+CODE 39 20
+STEP 38
+PRESS ö
+CODE 12 28
+CODE 39 31
+STEP 39
+PRESS r
+PRESS у
+PRESS υ
+PRESS г
+PRESS n
+PRESS γ
+PRESS u
+PRESS ה
+PRESS v
+PRESS y
+PRESS ν
+CODE 33 40
+CODE 34 41
+CODE 35 44
+CODE 38 40
+CODE 17 44
+CODE 47 45
+CODE 49 45
+CODE 18 63
+CODE 19 45
+CODE 20 40
+CODE 21 45
+CODE 22 45
+CODE 24 40
+CODE 52 40
+STEP 45
+FINDP ș
+YES 75
+NO 46
+STEP 46
+FINDP é
+YES 74
+NO 47
+STEP 47
+FINDP ç
+YES 73
+NO 48
+STEP 48
+FINDP š
+YES 72
+NO 49
+STEP 83
+PRESS ω
+PRESS w
+CODE 16 84
+CODE 17 94
+CODE 44 76
+CODE 30 80
+CODE 47 42
+STEP 84
+PRESS q
+CODE 16 85
+CODE 30 81
+STEP 41
+FINDP ч
+YES 43
+NO 42
+STEP 2
+MAP tr:f
+STEP 104
+PRESS ω
+PRESS w
+CODE 16 105
+CODE 17 109
+CODE 44 76
+CODE 30 80
+CODE 47 42
+STEP 105
+PRESS z
+CODE 17 81
+CODE 44 85
+CODE 21 106
+STEP 85
+FINDP ö
+YES 92
+NO 86
+STEP 86
+FINDP ç
+YES 21
+NO 87
+STEP 87
+FINDP å
+YES 18
+NO 88
+STEP 88
+FINDP ä
+YES 91
+NO 89
+STEP 89
+FINDP ñ
+YES 17
+NO 90
+STEP 90
+PRESS @
+CODE 16 71
+CODE 40 71
+CODE 3 62
+STEP 21
+PRESS ç
+CODE 43 22
+CODE 39 23
+STEP 23
+FINDP è
+YES 25
+NO 24
+STEP 92
+PRESS ö
+CODE 51 27
+CODE 12 28
+CODE 39 93
+STEP 93
+FINDP ü
+YES 30
+NO 32
+STEP 109
+PRESS z
+PRESS ζ
+CODE 44 94
+CODE 21 106
+STEP 94
+FINDP ö
+YES 26
+NO 95
+STEP 95
+FINDP é
+YES 103
+NO 96
+STEP 96
+FINDP ç
+YES 101
+NO 97
+STEP 97
+FINDP æ
+YES 18
+NO 98
+STEP 98
+FINDP ș
+YES 75
+NO 99
+STEP 99
+FINDP š
+YES 72
+NO 100
+STEP 100
+FINDP ñ
+YES 17
+NO 49
+STEP 49
+FINDP £
+YES 71
+NO 50
+STEP 50
+FINDP ѝ
+YES 44
+NO 52
+STEP 52
+FINDP ș
+YES 69
+NO 53
+STEP 53
+FINDP ψ
+YES 42
+NO 54
+STEP 54
+FINDP ב
+YES 16
+NO 55
+STEP 55
+FINDP љ
+YES 43
+NO 56
+STEP 56
+FINDP ภ
+YES 68
+NO 57
+STEP 57
+FINDP ч
+YES 63
+NO 58
+STEP 58
+FINDP ə
+YES 62
+NO 59
+STEP 59
+FINDP š
+YES 61
+NO 60
+STEP 60
+MAP us
+STEP 61
+MAP lv
+STEP 62
+MAP pl
+STEP 63
+FINDP ы
+YES 65
+NO 64
+STEP 64
+MAP ua
+STEP 65
+FINDP и
+YES 67
+NO 66
+STEP 66
+MAP by
+STEP 67
+MAP ru
+STEP 68
+MAP th:tis
+STEP 43
+MAP mk
+STEP 16
+MAP il
+STEP 69
+MAP ro
+STEP 44
+MAP bg
+STEP 71
+MAP gb
+STEP 17
+MAP latam
+STEP 72
+MAP lt
+STEP 75
+MAP ro:std
+STEP 18
+PRESS æ
+CODE 40 19
+CODE 39 20
+STEP 19
+MAP no
+STEP 20
+MAP dk
+STEP 101
+PRESS º
+CODE 40 24
+CODE 41 22
+CODE 50 102
+CODE 43 73
+CODE 86 73
+STEP 102
+PRESS ç
+CODE 43 22
+CODE 39 24
+STEP 22
+MAP es
+STEP 24
+MAP pt
+STEP 73
+MAP br
+STEP 103
+PRESS é
+CODE 26 25
+CODE 11 91
+CODE 53 74
+STEP 25
+MAP it
+STEP 91
+MAP sk:qwerty
+STEP 74
+MAP ca
+STEP 26
+PRESS ö
+CODE 51 27
+CODE 12 28
+CODE 39 29
+STEP 27
+MAP tr
+STEP 28
+MAP is
+STEP 29
+FINDP å
+YES 31
+NO 30
+STEP 30
+MAP ee
+STEP 31
+FINDP ə
+YES 33
+NO 32
+STEP 32
+MAP se
+STEP 33
+MAP fi
+STEP 106
+FINDP ö
+YES 7
+NO 107
+STEP 107
+PRESS š
+CODE 26 4
+CODE 4 108
+STEP 4
+MAP hr
+STEP 108
+MAP cz
+STEP 7
+PRESS ö
+CODE 11 5
+CODE 39 8
+STEP 5
+MAP hu
+STEP 8
+FINDP é
+YES 10
+NO 9
+STEP 9
+MAP de:nodeadkeys
+STEP 10
+MAP ch
+STEP 76
+FINDP œ
+YES 82
+NO 77
+STEP 77
+FINDP º
+YES 79
+NO 78
+STEP 78
+MAP fr:oss
+STEP 79
+FINDP œ
+YES 81
+NO 80
+STEP 81
+MAP be
+STEP 82
+MAP fr:latin9
+STEP 80
+MAP fr
+STEP 42
+MAP gr
+STEP 40
+MAP us:dvorak
diff --git a/gnome-initial-setup/pages/keyboard/detector-trees/es/pc105.tree 
b/gnome-initial-setup/pages/keyboard/detector-trees/es/pc105.tree
new file mode 100644
index 00000000..15912015
--- /dev/null
+++ b/gnome-initial-setup/pages/keyboard/detector-trees/es/pc105.tree
@@ -0,0 +1,29 @@
+STEP 0
+FINDP ç
+YES 4
+NO 1
+STEP 1
+FINDP ñ
+YES 3
+NO 2
+STEP 2
+MAP us
+STEP 3
+MAP latam
+STEP 4
+PRESS º
+CODE 40 5
+CODE 41 6
+CODE 50 7
+CODE 43 8
+CODE 86 8
+STEP 7
+PRESS ç
+CODE 43 6
+CODE 39 5
+STEP 6
+MAP es
+STEP 5
+MAP pt
+STEP 8
+MAP br
diff --git a/gnome-initial-setup/pages/keyboard/detector-trees/pt/pc105.tree 
b/gnome-initial-setup/pages/keyboard/detector-trees/pt/pc105.tree
new file mode 100644
index 00000000..15912015
--- /dev/null
+++ b/gnome-initial-setup/pages/keyboard/detector-trees/pt/pc105.tree
@@ -0,0 +1,29 @@
+STEP 0
+FINDP ç
+YES 4
+NO 1
+STEP 1
+FINDP ñ
+YES 3
+NO 2
+STEP 2
+MAP us
+STEP 3
+MAP latam
+STEP 4
+PRESS º
+CODE 40 5
+CODE 41 6
+CODE 50 7
+CODE 43 8
+CODE 86 8
+STEP 7
+PRESS ç
+CODE 43 6
+CODE 39 5
+STEP 6
+MAP es
+STEP 5
+MAP pt
+STEP 8
+MAP br
diff --git a/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c 
b/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c
index 3adfd663..193611db 100644
--- a/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c
+++ b/gnome-initial-setup/pages/keyboard/gis-keyboard-page.c
@@ -36,6 +36,7 @@
 #include "gis-keyboard-page.h"
 #include "keyboard-resources.h"
 #include "cc-input-chooser.h"
+#include "cc-keyboard-query.h"
 
 #include "cc-common-language.h"
 
@@ -47,6 +48,7 @@
 
 struct _GisKeyboardPagePrivate {
         GtkWidget *input_chooser;
+       GtkWidget *input_auto_detect;
 
        GDBusProxy *localed;
        GCancellable *cancellable;
@@ -459,6 +461,38 @@ input_changed (CcInputChooser  *chooser,
         update_page_complete (self);
 }
 
+static void
+detector_response (GtkDialog *detector,
+                   gint       response_id,
+                   gpointer   data)
+{
+        GisKeyboardPage *self = data;
+        GisKeyboardPagePrivate *priv = gis_keyboard_page_get_instance_private (self);
+        char *id;
+
+        if (response_id == GTK_RESPONSE_OK) {
+                if (cc_keyboard_query_get_selected (CC_KEYBOARD_QUERY (detector), &id, NULL)) {
+                        cc_input_chooser_set_input (CC_INPUT_CHOOSER (priv->input_chooser), id, "xkb");
+                        update_input (self);
+                        g_free (id);
+                }
+        }
+        gtk_widget_destroy (GTK_WIDGET (detector));
+}
+
+static void
+show_keyboard_detector (CcInputChooser  *chooser,
+                        GisKeyboardPage *self)
+{
+        GtkWidget *detector;
+        GtkWidget *toplevel;
+
+        toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self));
+        detector = cc_keyboard_query_new (GTK_WINDOW (toplevel));
+        g_signal_connect (detector, "response", G_CALLBACK (detector_response), self);
+        cc_keyboard_query_run (CC_KEYBOARD_QUERY (detector));
+}
+
 static void
 gis_keyboard_page_constructed (GObject *object)
 {
@@ -474,6 +508,9 @@ gis_keyboard_page_constructed (GObject *object)
         g_signal_connect (priv->input_chooser, "changed",
                           G_CALLBACK (input_changed), self);
 
+        g_signal_connect (priv->input_auto_detect, "clicked",
+                          G_CALLBACK (show_keyboard_detector), self);
+
        priv->input_settings = g_settings_new (GNOME_DESKTOP_INPUT_SOURCES_DIR);
        g_settings_delay (priv->input_settings);
 
@@ -513,6 +550,7 @@ gis_keyboard_page_class_init (GisKeyboardPageClass * klass)
         gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (klass), 
"/org/gnome/initial-setup/gis-keyboard-page.ui");
 
         gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisKeyboardPage, 
input_chooser);
+        gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisKeyboardPage, 
input_auto_detect);
 
         page_class->page_id = PAGE_ID;
         page_class->apply = gis_keyboard_page_apply;
diff --git a/gnome-initial-setup/pages/keyboard/gis-keyboard-page.ui 
b/gnome-initial-setup/pages/keyboard/gis-keyboard-page.ui
index 8d022fbc..001afdeb 100644
--- a/gnome-initial-setup/pages/keyboard/gis-keyboard-page.ui
+++ b/gnome-initial-setup/pages/keyboard/gis-keyboard-page.ui
@@ -25,6 +25,17 @@
             <property name="visible">True</property>
             <property name="halign">center</property>
             <property name="valign">start</property>
+            <property name="vexpand_set">True</property>
+          </object>
+        </child>
+        <child>
+          <object class="GtkButton" id="input_auto_detect">
+            <property name="label" translatable="yes">Help Detect My Keyboard Layout</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="halign">center</property>
+            <property name="valign">start</property>
           </object>
         </child>
       </object>
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 00000000..4e2a9638
--- /dev/null
+++ b/gnome-initial-setup/pages/keyboard/keyboard-detector.ui
@@ -0,0 +1,93 @@
+<?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>
+      </object>
+    </child>
+    <child type="action">
+      <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 type="action">
+      <object class="GtkButton" id="select_button">
+        <property name="visible">True</property>
+        <property name="sensitive">False</property>
+        <property name="label" translatable="yes">_Select</property>
+        <property name="use_underline">True</property>
+        <property name="can_default">True</property>
+        <property name="has_default">True</property>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="-5">select_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 103b3f1b..5f6d47a6 100644
--- a/gnome-initial-setup/pages/keyboard/keyboard.gresource.xml
+++ b/gnome-initial-setup/pages/keyboard/keyboard.gresource.xml
@@ -3,5 +3,9 @@
   <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>detector-trees/C/pc105.tree</file>
+    <file>detector-trees/es/pc105.tree</file>
+    <file>detector-trees/pt/pc105.tree</file>
   </gresource>
 </gresources>
diff --git a/gnome-initial-setup/pages/keyboard/meson.build b/gnome-initial-setup/pages/keyboard/meson.build
index 69d6de85..502da30e 100644
--- a/gnome-initial-setup/pages/keyboard/meson.build
+++ b/gnome-initial-setup/pages/keyboard/meson.build
@@ -9,6 +9,12 @@ sources += files(
     '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'
 )
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 3e036645..b40ed056 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -21,9 +21,11 @@ gnome-initial-setup/pages/endless-eula/gis-endless-eula-viewer.c
 gnome-initial-setup/pages/goa/gis-goa-page.c
 gnome-initial-setup/pages/goa/gis-goa-page.ui
 gnome-initial-setup/pages/keyboard/cc-input-chooser.c
+gnome-initial-setup/pages/keyboard/cc-keyboard-query.c
 gnome-initial-setup/pages/keyboard/gis-keyboard-page.c
 gnome-initial-setup/pages/keyboard/gis-keyboard-page.ui
 gnome-initial-setup/pages/keyboard/input-chooser.ui
+gnome-initial-setup/pages/keyboard/keyboard-detector.ui
 gnome-initial-setup/pages/language/cc-language-chooser.c
 gnome-initial-setup/pages/language/gis-language-page.c
 gnome-initial-setup/pages/language/gis-language-page.ui



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