[gnome-flashback/wip/segeiger/gnome-3-18-inputmethods: 1/2] input-sources: implement candidate popup



commit 04f2711b611df5707bf971fdae6ea4e0cd1a4daf
Author: Sebastian Geiger <sbastig gmx net>
Date:   Tue Jan 12 16:33:38 2016 +0100

    input-sources: implement candidate popup

 gnome-flashback/Adwaita.css                        |    4 +
 gnome-flashback/libinput-sources/Makefile.am       |    2 +
 .../libinput-sources/gf-candidate-area.c           |  319 ++++++++++++++++++++
 .../libinput-sources/gf-candidate-area.h           |   49 +++
 .../libinput-sources/gf-candidate-popup.c          |  298 ++++++++++++++++++
 5 files changed, 672 insertions(+), 0 deletions(-)
---
diff --git a/gnome-flashback/Adwaita.css b/gnome-flashback/Adwaita.css
index 794e263..b03f1ae 100644
--- a/gnome-flashback/Adwaita.css
+++ b/gnome-flashback/Adwaita.css
@@ -18,3 +18,7 @@ FlashbackLabelWindow
 {
   font-size: 40px;
 }
+
+.candidate-box.selected {
+    background: #7b7b7b;
+}
diff --git a/gnome-flashback/libinput-sources/Makefile.am b/gnome-flashback/libinput-sources/Makefile.am
index e7ce173..80fb88d 100644
--- a/gnome-flashback/libinput-sources/Makefile.am
+++ b/gnome-flashback/libinput-sources/Makefile.am
@@ -15,6 +15,8 @@ libinput_sources_la_CFLAGS = \
        $(NULL)
 
 libinput_sources_la_SOURCES = \
+       gf-candidate-area.c \
+       gf-candidate-area.h \
        gf-candidate-popup.c \
        gf-candidate-popup.h \
        gf-ibus-manager.c \
diff --git a/gnome-flashback/libinput-sources/gf-candidate-area.c 
b/gnome-flashback/libinput-sources/gf-candidate-area.c
new file mode 100644
index 0000000..c7ee744
--- /dev/null
+++ b/gnome-flashback/libinput-sources/gf-candidate-area.c
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2016 Sebastian Geiger
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gf-candidate-area.h"
+
+#define MAX_CANDIDATES_PER_PAGE 16
+
+const gchar* DEFAULT_INDEX_LABELS[] =
+  {
+    "1", "2", "3", "4", "5", "6", "7", "8",
+    "9", "0", "a", "b", "c", "d", "e", "f"
+  };
+
+struct _GfCandidateArea
+{
+  GtkBox           parent;
+
+  GtkWidget       *button_box;
+  GtkWidget       *prev_button;
+  GtkWidget       *next_button;
+
+  GList           *candidate_boxes;
+
+  IBusOrientation  orientation;
+  guint            cursor_position;
+};
+
+enum
+{
+  SIGNAL_CANDIDATE_CLICKED,
+  SIGNAL_PREV_PAGE,
+  SIGNAL_NEXT_PAGE,
+
+  SIGNAL_LAST
+};
+
+static guint signals[SIGNAL_LAST] = { 0 };
+
+G_DEFINE_TYPE (GfCandidateArea, gf_candidate_area, GTK_TYPE_BOX)
+
+static gboolean
+button_clicked_cb (GtkWidget *widget,
+                   GdkEvent  *event,
+                   gpointer   user_data)
+{
+  int index;
+  GfCandidateArea *area;
+
+  area = GF_CANDIDATE_AREA (user_data);
+  index = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), "index"));
+
+  g_signal_emit (area,  signals[SIGNAL_CANDIDATE_CLICKED], 0, index, event);
+
+  return GDK_EVENT_PROPAGATE;
+}
+
+static gboolean
+prev_button_clicked_cb (GtkButton *button,
+                        gpointer   user_data)
+{
+  GfCandidateArea *area;
+
+  area = GF_CANDIDATE_AREA (user_data);
+
+  g_signal_emit (area, signals[SIGNAL_PREV_PAGE], 0);
+
+  return GDK_EVENT_PROPAGATE;
+}
+
+static gboolean
+next_button_clicked_cb (GtkButton *button,
+                        gpointer   user_data)
+{
+  GfCandidateArea *area;
+
+  area = GF_CANDIDATE_AREA (user_data);
+
+  g_signal_emit (area, signals[SIGNAL_NEXT_PAGE], 0);
+
+  return GDK_EVENT_PROPAGATE;
+}
+
+void
+gf_candidate_area_set_orientation (GfCandidateArea *area,
+                                   IBusOrientation  orientation)
+{
+  GtkStyleContext *context;
+  GtkWidget *image;
+
+  if (area->orientation == orientation)
+    return;
+
+  area->orientation = orientation;
+
+  if (area->orientation == IBUS_ORIENTATION_HORIZONTAL)
+    {
+      gtk_orientable_set_orientation (GTK_ORIENTABLE (area->button_box),
+                                      GTK_ORIENTATION_HORIZONTAL);
+
+      context = gtk_widget_get_style_context (GTK_WIDGET (area));
+      gtk_style_context_add_class(context, "horizontal");
+      gtk_style_context_remove_class(context, "vertical");
+
+      image = gtk_button_get_image(GTK_BUTTON (area->prev_button));
+      gtk_image_set_from_icon_name(GTK_IMAGE (image), "go-previous-symbolic",
+                                   GTK_ICON_SIZE_BUTTON);
+
+      image = gtk_button_get_image(GTK_BUTTON (area->next_button));
+      gtk_image_set_from_icon_name(GTK_IMAGE (image), "go-next-symbolic",
+                                   GTK_ICON_SIZE_BUTTON);
+    }
+  else
+    {
+      gtk_orientable_set_orientation (GTK_ORIENTABLE (area->button_box),
+                                      GTK_ORIENTATION_VERTICAL);
+
+      context = gtk_widget_get_style_context (GTK_WIDGET (area));
+      gtk_style_context_add_class (context, "vertical");
+      gtk_style_context_remove_class(context, "horizontal");
+
+      image = gtk_button_get_image(GTK_BUTTON (area->prev_button));
+      gtk_image_set_from_icon_name(GTK_IMAGE (image), "go-up-symbolic",
+                                   GTK_ICON_SIZE_BUTTON);
+
+      image = gtk_button_get_image(GTK_BUTTON (area->next_button));
+      gtk_image_set_from_icon_name(GTK_IMAGE (image), "go-down-symbolic",
+                                   GTK_ICON_SIZE_BUTTON);
+    }
+}
+
+void
+gf_candidate_area_set_candidates (GfCandidateArea *area,
+                                  GList           *indexes,
+                                  GList           *candidates,
+                                  guint            cursor_position,
+                                  gboolean         cursor_visible)
+{
+  guint i;
+  GtkWidget *current_candidate;
+  GtkStyleContext *context;
+
+  for (i = 0; i < MAX_CANDIDATES_PER_PAGE; i++)
+    {
+      gboolean visible;
+      GtkWidget *box;
+      GList *children;
+      GtkWidget *index_label;
+      GtkWidget *candidate_label;
+      const gchar *index_text;
+
+      visible = i < g_list_length (candidates);
+      box = g_list_nth (area->candidate_boxes, i)->data;
+      gtk_widget_set_visible (box, visible);
+
+      if (!visible)
+        continue;
+
+      children = gtk_container_get_children (GTK_CONTAINER (box));
+      index_label = children->data;
+      candidate_label = children->next->data;
+
+      if (indexes && g_list_nth (indexes, i)->data)
+          index_text = g_list_nth (indexes, i)->data;
+      else
+          index_text = DEFAULT_INDEX_LABELS [i];
+
+      gtk_label_set_text (GTK_LABEL(index_label), index_text);
+      gtk_label_set_text (GTK_LABEL (candidate_label),
+                          g_list_nth (candidates, i)->data);
+    }
+
+  current_candidate = g_list_nth (area->candidate_boxes,
+                                  area->cursor_position)->data;
+  context = gtk_widget_get_style_context (current_candidate);
+  gtk_style_context_remove_class (context, "selected");
+
+  area->cursor_position = cursor_position;
+
+  if (cursor_visible)
+    {
+      current_candidate = g_list_nth (area->candidate_boxes, cursor_position)->data;
+      context = gtk_widget_get_style_context (current_candidate);
+      gtk_style_context_add_class (context, "selected");
+    }
+}
+
+void
+gf_candidate_area_update_buttons (GfCandidateArea *area,
+                                  gboolean         wraps_around,
+                                  gint             page,
+                                  gint             n_pages)
+{
+  gtk_widget_set_visible (area->button_box, n_pages > 1);
+
+  if (n_pages < 2)
+    return;
+
+  gtk_widget_set_sensitive (area->prev_button, wraps_around || page > 0);
+  gtk_widget_set_sensitive (area->next_button,
+                            wraps_around || page < n_pages - 1);
+}
+
+static void
+gf_candidate_area_class_init (GfCandidateAreaClass *area_class)
+{
+  signals[SIGNAL_CANDIDATE_CLICKED] =
+    g_signal_new ("candidate-clicked", G_OBJECT_CLASS_TYPE (area_class),
+                  G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE,
+                  2, G_TYPE_UINT, GDK_TYPE_EVENT);
+
+  signals[SIGNAL_PREV_PAGE] =
+    g_signal_new ("previous-page", G_OBJECT_CLASS_TYPE (area_class),
+                  G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
+
+  signals[SIGNAL_NEXT_PAGE] =
+    g_signal_new ("next-page", G_OBJECT_CLASS_TYPE (area_class),
+                  G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
+}
+
+static void
+gf_candidate_area_init (GfCandidateArea *area)
+{
+  GtkStyleContext *context;
+  GtkWidget *prev_image, *next_image;
+  int i;
+
+  for (i = 0; i < MAX_CANDIDATES_PER_PAGE; i++)
+    {
+      GtkWidget *event_box,
+                *box,
+                *index_label,
+                *candidate_label;
+
+      event_box = gtk_event_box_new();
+      box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+      context = gtk_widget_get_style_context (box);
+      gtk_style_context_add_class (context, "candidate-box");
+      gtk_container_add (GTK_CONTAINER (area), event_box);
+      gtk_container_add (GTK_CONTAINER (event_box), box);
+      g_object_set_data (G_OBJECT (box), "index,", GINT_TO_POINTER(i));
+
+      index_label = gtk_label_new (NULL);
+      context = gtk_widget_get_style_context (index_label);
+      gtk_style_context_add_class (context, "candidate-index");
+
+      candidate_label = gtk_label_new (NULL);
+      context = gtk_widget_get_style_context (candidate_label);
+      gtk_style_context_add_class (context, "candidate-label");
+
+      gtk_container_add (GTK_CONTAINER (box), index_label);
+      gtk_container_add (GTK_CONTAINER (box), candidate_label);
+
+      gtk_widget_show_all (box);
+
+      area->candidate_boxes = g_list_append (area->candidate_boxes, box);
+
+      g_signal_connect (event_box, "button-release-event",
+                        G_CALLBACK (button_clicked_cb), area);
+    }
+
+  area->button_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+  context = gtk_widget_get_style_context (area->button_box);
+  gtk_style_context_add_class (context, "candidate-page-button-box");
+
+  gtk_container_add (GTK_CONTAINER (area), area->button_box);
+
+  prev_image = gtk_image_new_from_icon_name ("go-previous-symbolic",
+                                             GTK_ICON_SIZE_BUTTON);
+  area->prev_button = gtk_button_new_with_label ("Prev");
+  gtk_button_set_image (GTK_BUTTON (area->prev_button), prev_image);
+  context = gtk_widget_get_style_context (area->prev_button);
+  gtk_style_context_add_class (context, "candidate-page-button");
+  gtk_style_context_add_class (context, "candidate-page-button-previous");
+
+  next_image = gtk_image_new_from_icon_name ("go-next-symbolic",
+                                             GTK_ICON_SIZE_BUTTON);
+  area->next_button = gtk_button_new_with_label ("Next");
+  gtk_button_set_image (GTK_BUTTON (area->next_button), next_image);
+  context = gtk_widget_get_style_context (area->next_button);
+  gtk_style_context_add_class (context, "candidate-page-button");
+  gtk_style_context_add_class (context, "candidate-page-button-next");
+
+  gtk_container_add (GTK_CONTAINER (area->button_box), area->prev_button);
+  gtk_container_add (GTK_CONTAINER (area->button_box), area->next_button);
+
+  gtk_widget_show_all (GTK_WIDGET (area));
+
+  g_signal_connect (area->prev_button, "clicked",
+                    G_CALLBACK (prev_button_clicked_cb), area);
+  g_signal_connect (area->next_button, "clicked",
+                    G_CALLBACK (next_button_clicked_cb), area);
+
+  area->orientation = IBUS_ORIENTATION_HORIZONTAL;
+  area->cursor_position = 0;
+}
+
+GtkWidget*
+gf_candidate_area_new (void)
+{
+  return g_object_new (GF_TYPE_CANDIDATE_AREA,
+                       "orientation", GTK_ORIENTATION_VERTICAL,
+                       NULL);
+}
diff --git a/gnome-flashback/libinput-sources/gf-candidate-area.h 
b/gnome-flashback/libinput-sources/gf-candidate-area.h
new file mode 100644
index 0000000..40aa683
--- /dev/null
+++ b/gnome-flashback/libinput-sources/gf-candidate-area.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2016 Sebastian Geiger
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GF_CANDIDATE_AREA_H
+#define GF_CANDIDATE_AREA_H
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <ibus-1.0/ibus.h>
+
+G_BEGIN_DECLS
+
+#define GF_TYPE_CANDIDATE_AREA gf_candidate_area_get_type ()
+G_DECLARE_FINAL_TYPE (GfCandidateArea, gf_candidate_area,
+                      GF, CANDIDATE_AREA, GtkBox)
+
+GtkWidget* gf_candidate_area_new             (void);
+
+void       gf_candidate_area_set_orientation (GfCandidateArea *area,
+                                              IBusOrientation  orientation);
+
+void       gf_candidate_area_set_candidates  (GfCandidateArea *area,
+                                              GList           *indexes,
+                                              GList           *candidates,
+                                              guint            cursor_position,
+                                              gboolean         cursor_visible);
+
+void       gf_candidate_area_update_buttons  (GfCandidateArea *area,
+                                              gboolean         wraps_around,
+                                              gint             page,
+                                              gint             n_pages);
+
+G_END_DECLS
+
+#endif
diff --git a/gnome-flashback/libinput-sources/gf-candidate-popup.c 
b/gnome-flashback/libinput-sources/gf-candidate-popup.c
index 32d6a63..c3c3d75 100644
--- a/gnome-flashback/libinput-sources/gf-candidate-popup.c
+++ b/gnome-flashback/libinput-sources/gf-candidate-popup.c
@@ -17,6 +17,12 @@
 
 #include "config.h"
 
+/* _XOPEN_SOURCE macro needed for floor function from math library */
+#define _XOPEN_SOURCE 600
+
+#include <math.h>
+
+#include "gf-candidate-area.h"
 #include "gf-candidate-popup.h"
 
 struct _GfCandidatePopup
@@ -24,11 +30,41 @@ struct _GfCandidatePopup
   GObject           parent;
 
   IBusPanelService *service;
+
+  GtkWidget        *window;
+
+  GtkWidget        *box_layout;
+
+  GtkWidget        *pre_edit_text;
+  GtkWidget        *aux_text;
+  GtkWidget        *candidate_area;
+
+  gint              cursor_x_pos;
+  gint              cursor_y_pos;
+  gint              cursor_height;
+  gint              cursor_width;
 };
 
 G_DEFINE_TYPE (GfCandidatePopup, gf_candidate_popup, G_TYPE_OBJECT)
 
 static void
+set_text_attributes (GfCandidatePopup *popup, IBusAttrList *attributes)
+{
+  IBusAttribute *attribute;
+  guint i, start, end;
+
+  for (i = 0; (attribute = ibus_attr_list_get (attributes, i)) != NULL; i++)
+    {
+      if (ibus_attribute_get_type () == IBUS_ATTR_TYPE_BACKGROUND)
+        {
+          start = ibus_attribute_get_start_index (attribute);
+          end = ibus_attribute_get_end_index (attribute);
+          gtk_label_select_region (GTK_LABEL (popup->pre_edit_text), start, end);
+        }
+    }
+}
+
+static void
 set_cursor_location_cb (IBusPanelService *service,
                         gint              x,
                         gint              y,
@@ -36,6 +72,65 @@ set_cursor_location_cb (IBusPanelService *service,
                         gint              h,
                         gpointer          user_data)
 {
+  GfCandidatePopup *popup;
+
+  /* We want to position the candidate window as close as possible to the
+   * cursor. But we do not want it to extend outside the screen area.
+   * If placing the candidate area below the cursor would cause parts of the
+   * window to extend outside the screen, then we will place it above the
+   * cursor instead.
+   *
+   * The x, y parameters refer to the top, left corner of the character under
+   * the cursor.
+   **/
+
+  popup = GF_CANDIDATE_POPUP (user_data);
+
+  popup->cursor_x_pos = x;
+  popup->cursor_y_pos = y;
+  popup->cursor_height = h;
+  popup->cursor_width = w;
+}
+
+static void
+update_window_location (GfCandidatePopup *popup)
+{
+  gint popup_height, popup_width;
+  GdkScreen *screen;
+  GdkWindow *window;
+  GdkRectangle rectangle;
+  gint monitor;
+  gint x, y;
+  gint box_min_height, box_natural_height;
+  gint box_min_width, box_natural_width;
+
+  gtk_window_get_size (GTK_WINDOW (popup->window), &popup_height, &popup_width);
+
+  window = gtk_widget_get_window (popup->window);
+
+  if (window == NULL)
+    return;
+
+  screen = gdk_window_get_screen (window);
+  monitor = gdk_screen_get_monitor_at_window (screen, window);
+  gdk_screen_get_monitor_geometry (screen, monitor, &rectangle);
+
+  x = popup->cursor_x_pos;
+  y = popup->cursor_y_pos;
+
+  if (x + popup_width > rectangle.width)
+    x = x - popup_width + popup->cursor_width;
+
+  if (y + popup_height > rectangle.height)
+    y = y - popup_height;
+  else
+      y = y + popup->cursor_height;
+
+  gtk_widget_get_preferred_height (popup->window, &box_min_height, &box_natural_height);
+  gtk_widget_get_preferred_width (popup->window, &box_min_width, &box_natural_width);
+
+  gtk_window_move (GTK_WINDOW (popup->window), x, y);
+  gtk_window_resize (GTK_WINDOW (popup->window), box_natural_width, box_natural_height);
 }
 
 static void
@@ -45,18 +140,50 @@ update_preedit_text_cb (IBusPanelService *service,
                         gboolean          visible,
                         gpointer          user_data)
 {
+  GfCandidatePopup *popup;
+  IBusAttrList *attributes;
+
+  g_message ("update_preedit_text_cb");
+
+  popup = GF_CANDIDATE_POPUP (user_data);
+
+  gtk_widget_set_visible (popup->pre_edit_text, visible);
+
+  gtk_label_set_text (GTK_LABEL (popup->pre_edit_text),
+                      ibus_text_get_text (text));
+
+  attributes = ibus_text_get_attributes (text);
+
+  if (attributes)
+  {
+    set_text_attributes (popup, attributes);
+  }
 }
 
 static void
 show_preedit_text_cb (IBusPanelService *service,
                       gpointer          user_data)
 {
+  GfCandidatePopup *popup;
+
+  g_message ("show_preedit_text_cb");
+
+  popup = GF_CANDIDATE_POPUP (user_data);
+
+  gtk_widget_show (popup->pre_edit_text);
 }
 
 static void
 hide_preedit_text_cb (IBusPanelService *service,
                       gpointer          user_data)
 {
+  GfCandidatePopup *popup;
+
+  g_message ("hide_preedit_text_cb");
+
+  popup = GF_CANDIDATE_POPUP (user_data);
+
+  gtk_widget_hide (popup->pre_edit_text);
 }
 
 static void
@@ -65,18 +192,41 @@ update_auxiliary_text_cb (IBusPanelService *service,
                           gboolean          visible,
                           gpointer          user_data)
 {
+  GfCandidatePopup *popup;
+
+  g_message ("update_auxiliary_text_cb");
+
+  popup = GF_CANDIDATE_POPUP (user_data);
+
+  gtk_widget_set_visible (popup->aux_text, visible);
+
+  gtk_label_set_text (GTK_LABEL (popup->aux_text), ibus_text_get_text (text));
 }
 
 static void
 show_auxiliary_text_cb (IBusPanelService *service,
                         gpointer          user_data)
 {
+  GfCandidatePopup *popup;
+
+  g_message ("show_auxiliary_text_cb");
+
+  popup = GF_CANDIDATE_POPUP (user_data);
+
+  gtk_widget_show (popup->aux_text);
 }
 
 static void
 hide_auxiliary_text_cb (IBusPanelService *service,
                         gpointer          user_data)
 {
+  GfCandidatePopup *popup;
+
+  g_message ("hide_auxiliary_text_cb");
+
+  popup = GF_CANDIDATE_POPUP (user_data);
+
+  gtk_widget_hide (popup->aux_text);
 }
 
 static void
@@ -85,18 +235,92 @@ update_lookup_table_cb (IBusPanelService *service,
                         gboolean          visible,
                         gpointer          user_data)
 {
+  GfCandidatePopup *popup;
+  guint n_candidates,
+        cursor_position,
+        page_size, n_pages, page,
+        start_index, end_index,
+        i;
+  GList *indexes, *candidates;
+  IBusText *index_label;
+  gint orientation;
+
+  popup = GF_CANDIDATE_POPUP (user_data);
+  indexes = NULL;
+  candidates = NULL;
+
+  gtk_widget_set_visible (popup->window, visible);
+  update_window_location (popup);
+  n_candidates = ibus_lookup_table_get_number_of_candidates (lookup_table);
+  cursor_position = ibus_lookup_table_get_cursor_pos (lookup_table);
+  page_size = ibus_lookup_table_get_page_size (lookup_table);
+  n_pages = (guint) ceil (n_candidates / ((gdouble) page_size));
+  page = ((cursor_position == 0) ? 0 :
+          (guint) floor (cursor_position / ((gdouble) page_size)));
+  start_index = page * page_size;
+  end_index = MIN ((page + 1) * page_size, n_candidates);
+
+  for (i = 0; (index_label = ibus_lookup_table_get_label (lookup_table, i)) != NULL; i++)
+    {
+      gchar *text;
+
+      text = g_strdup (ibus_text_get_text (index_label));
+      indexes = g_list_append (indexes, text);
+    }
+
+  for (i = start_index; i < end_index; i++)
+    {
+      IBusText *ibus_text;
+      const gchar* text;
+
+      ibus_text = ibus_lookup_table_get_candidate(lookup_table, i);
+      text = ibus_text_get_text(ibus_text);
+      candidates = g_list_append (candidates, (gpointer) text);
+    }
+
+  gf_candidate_area_set_candidates (GF_CANDIDATE_AREA (popup->candidate_area),
+                                    indexes, candidates, cursor_position,
+                                    visible);
+
+  orientation = ibus_lookup_table_get_orientation (lookup_table);
+
+  gf_candidate_area_set_orientation (GF_CANDIDATE_AREA (popup->candidate_area),
+                                     (IBusOrientation) orientation);
+
+  gf_candidate_area_update_buttons (GF_CANDIDATE_AREA (popup->candidate_area),
+                                    ibus_lookup_table_is_round (lookup_table),
+                                    page, n_pages);
+
+  g_list_free (indexes);
+  g_list_free (candidates);
 }
 
 static void
 show_lookup_table_cb (IBusPanelService *service,
                       gpointer          user_data)
 {
+  GfCandidatePopup *popup;
+
+  g_message ("show_lookup_table_cb");
+
+  popup = GF_CANDIDATE_POPUP (user_data);
+
+  gtk_widget_show (popup->window);
+  update_window_location (popup);
 }
 
 static void
 hide_lookup_table_cb (IBusPanelService *service,
                       gpointer          user_data)
 {
+  GfCandidatePopup *popup;
+
+  g_message ("hide_lookup_table_cb");
+
+  popup = GF_CANDIDATE_POPUP (user_data);
+
+  gtk_widget_hide (popup->window);
+  update_window_location (popup);
 }
 
 static void
@@ -104,6 +328,50 @@ focus_out_cb (IBusPanelService *service,
               const gchar      *input_context_path,
               gpointer          user_data)
 {
+  g_message ("focus_out_cb");
+}
+
+static void
+area_page_prev_cb (GfCandidateArea *area,
+                   gpointer         user_data)
+{
+  GfCandidatePopup *popup;
+
+  popup = GF_CANDIDATE_POPUP (user_data);
+
+  ibus_panel_service_page_up (popup->service);
+}
+
+static void
+area_page_next_cb (GfCandidateArea *area,
+                   gpointer         user_data)
+{
+  GfCandidatePopup *popup;
+
+  popup = GF_CANDIDATE_POPUP (user_data);
+
+  ibus_panel_service_page_down (popup->service);
+}
+
+static void
+area_candidate_clicked_cb (GfCandidateArea *area,
+                           guint             index,
+                           GdkEvent        *event,
+                           gpointer         user_data)
+{
+  GfCandidatePopup *popup;
+  guint button;
+  GdkModifierType state;
+
+  popup = GF_CANDIDATE_POPUP (user_data);
+
+  gdk_event_get_button (event, &button);
+  gdk_event_get_state (event, &state);
+
+  ibus_panel_service_candidate_clicked (popup->service,
+                                        index,
+                                        button,
+                                        state);
 }
 
 static void
@@ -131,6 +399,36 @@ gf_candidate_popup_class_init (GfCandidatePopupClass *popup_class)
 static void
 gf_candidate_popup_init (GfCandidatePopup *popup)
 {
+  GtkStyleContext *context;
+
+  popup->candidate_area = gf_candidate_area_new();
+
+  popup->window = gtk_window_new (GTK_WINDOW_POPUP);
+  gtk_window_set_default_size (GTK_WINDOW (popup->window), 1, 1);
+  context = gtk_widget_get_style_context (popup->window);
+  gtk_style_context_add_class (context, "candidate-popup");
+
+  popup->box_layout = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+  gtk_container_add (GTK_CONTAINER (popup->window), popup->box_layout);
+
+  popup->pre_edit_text = gtk_label_new ("Pre_Edit_Text");
+  popup->aux_text = gtk_label_new ("Aux_Text");
+
+  gtk_container_add (GTK_CONTAINER (popup->box_layout), popup->pre_edit_text);
+  gtk_container_add (GTK_CONTAINER (popup->box_layout), popup->aux_text);
+
+  gtk_container_add (GTK_CONTAINER (popup->box_layout),
+                     popup->candidate_area);
+
+  gtk_widget_show (popup->box_layout);
+  gtk_widget_show (popup->candidate_area);
+
+  g_signal_connect (popup->candidate_area, "previous-page",
+                    G_CALLBACK (area_page_prev_cb), popup);
+  g_signal_connect (popup->candidate_area, "next-page",
+                    G_CALLBACK (area_page_next_cb), popup);
+  g_signal_connect (popup->candidate_area, "candidate-clicked",
+                    G_CALLBACK (area_candidate_clicked_cb), popup);
 }
 
 GfCandidatePopup *


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