Revision: 6441
Author: deton.kih
Date: Sun Jun 13 00:29:13 2010
Log: * Add new table style candidate window for uim-xim [uim-ja 234]
* helper/candwin-tbl-gtk.c
  - New file
* helper/Makefile.am
  - (libexec_PROGRAMS): Add uim-candwin-tbl-gtk

http://code.google.com/p/uim/source/detail?r=6441

Added:
 /trunk/helper/candwin-tbl-gtk.c
Modified:
 /trunk/helper/Makefile.am

=======================================
--- /dev/null
+++ /trunk/helper/candwin-tbl-gtk.c     Sun Jun 13 00:29:13 2010
@@ -0,0 +1,1104 @@
+/*
+
+  Copyright (c) 2003-2010 uim Project http://code.google.com/p/uim/
+
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+  3. Neither the name of authors nor the names of its contributors
+     may be used to endorse or promote products derived from this software
+     without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
+  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+  SUCH DAMAGE.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+#include <uim/uim.h>
+#include <uim/uim-helper.h>
+#include <uim/uim-scm.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+#include <glib.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "../gtk/caret-state-indicator.h"
+
+#define UIM_TYPE_CANDIDATE_WINDOW      (candidate_window_get_type())
+#define UIM_CANDIDATE_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), candidate_window_get_type(), UIMCandidateWindow)) +#define UIM_IS_CANDIDATE_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), UIM_TYPE_CANDIDATE_WINDOW)) +#define UIM_IS_CANDIDATE_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), UIM_TYPE_CANDIDATE_WINDOW)) +#define UIM_CANDIDATE_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), UIM_TYPE_CANDIDATE_WINDOW, UIMCandidateWindowClass))
+
+typedef struct _UIMCandidateWindow     UIMCandidateWindow;
+typedef struct _UIMCandidateWindowClass        UIMCandidateWindowClass;
+
+struct _UIMCandidateWindow {
+  GtkWindow parent;
+
+  GtkWidget *view;
+  GtkWidget *num_label;
+  GtkWidget *scrolled_window;
+  GtkWidget *viewport;
+  GtkWidget *vbox;
+  GtkWidget *frame;
+
+  GPtrArray *stores;
+  GPtrArray *buttons;
+  gchar *labelchar_table;
+
+  guint nr_candidates;
+  guint display_limit;
+  gint candidate_index;
+  gint page_index;
+
+  gint pos_x;
+  gint pos_y;
+  gint width;
+  gint height;
+
+  GtkWidget *caret_state_indicator;
+
+  gboolean is_active;
+  gboolean need_hilite;
+};
+
+struct _UIMCandidateWindowClass {
+  GtkWindowClass parent_class;
+
+  /* signals */
+  void (*index_changed) (UIMCandidateWindowClass *candwin);
+};
+
+static UIMCandidateWindow *cwin; /* use single candwin */
+
+GType candidate_window_get_type(void);
+UIMCandidateWindow *candidate_window_new(void);
+
+/* copied from uim-cand-win-gtk.c */
+static gint uim_cand_win_gtk_get_index(UIMCandidateWindow *cwin);
+static void uim_cand_win_gtk_set_index(UIMCandidateWindow *cwin, gint index);
+static void uim_cand_win_gtk_set_page(UIMCandidateWindow *cwin, gint page);
+static void uim_cand_win_gtk_set_page_candidates(UIMCandidateWindow *cwin, guint page, GSList *candidates);
+
+static void uim_cand_win_gtk_layout(void);
+static void uim_cand_win_gtk_show(UIMCandidateWindow *cwin);
+
+#define CANDWIN_DEFAULT_WIDTH  80
+
+enum {
+  INDEX_CHANGED_SIGNAL,
+  NR_SIGNALS
+};
+
+enum {
+  TERMINATOR = -1,
+  COLUMN_CANDIDATE1,
+  COLUMN_CANDIDATE2,
+  COLUMN_CANDIDATE3,
+  COLUMN_CANDIDATE4,
+  COLUMN_CANDIDATE5,
+  COLUMN_CANDIDATE6,
+  COLUMN_CANDIDATE7,
+  COLUMN_CANDIDATE8,
+  COLUMN_CANDIDATE9,
+  COLUMN_CANDIDATE10,
+  COLUMN_CANDIDATE11,
+  COLUMN_CANDIDATE12,
+  COLUMN_CANDIDATE13
+};
+
+#define LABELCHAR_NR_COLUMNS 13
+#define LABELCHAR_NR_ROWS 8
+#define LABELCHAR_NR_CELLS (LABELCHAR_NR_COLUMNS * LABELCHAR_NR_ROWS)
+#define INDEX(row,col) ((row) * LABELCHAR_NR_COLUMNS + (col))
+/* 106 keyboard */
+static gchar default_labelchar_table[LABELCHAR_NR_CELLS] = {
+  '1','2','3','4','5', '6','7','8','9','0',   '-','^','\\',
+  'q','w','e','r','t', 'y','u','i','o','p',   '@','[','\0',
+  'a','s','d','f','g', 'h','j','k','l',';',   ':',']','\0',
+  'z','x','c','v','b', 'n','m',',','.','/',   '\0','\0',' ',
+  '!','"','#','$','%', '&','\'','(',')','\0', '=','~','|',
+  'Q','W','E','R','T', 'Y','U','I','O','P',   '`','{','\0',
+  'A','S','D','F','G', 'H','J','K','L','+',   '*','}','\0',
+  'Z','X','C','V','B', 'N','M','<','>','?',   '_','\0','\0',
+};
+/* labelchar_table consists of four blocks
+ *   blockLR  blockA
+ *   blockLRS blockAS
+ */
+#define BLOCK_A_ROW_START 0
+#define BLOCK_A_ROW_END 4
+#define BLOCK_A_COLUMN_START 10
+#define BLOCK_A_COLUMN_END LABELCHAR_NR_COLUMNS
+#define BLOCK_LRS_ROW_START BLOCK_A_ROW_END
+#define BLOCK_LRS_ROW_END LABELCHAR_NR_ROWS
+#define BLOCK_LRS_COLUMN_START 0
+#define BLOCK_LRS_COLUMN_END BLOCK_A_COLUMN_START
+#define BLOCK_AS_ROW_START BLOCK_LRS_ROW_START
+#define BLOCK_AS_ROW_END BLOCK_LRS_ROW_END
+#define BLOCK_AS_COLUMN_START BLOCK_LRS_COLUMN_END
+#define BLOCK_AS_COLUMN_END LABELCHAR_NR_COLUMNS
+
+#define BLOCK_SPACING 20
+#define HOMEPOSITION_SPACING 2
+#define BLOCK_SPACING 20
+#define HOMEPOSITION_SPACING 2
+#define SPACING_LEFT_BLOCK_COLUMN 4
+#define SPACING_RIGHT_BLOCK_COLUMN (BLOCK_A_COLUMN_START - 1)
+#define SPACING_UP_BLOCK_ROW (BLOCK_A_ROW_END - 1)
+#define SPACING_LEFTHAND_FAR_COLUMN 3
+#define SPACING_RIGHTHAND_FAR_COLUMN 5
+#define SPACING_UPPER_FAR_ROW 0
+#define SPACING_SHIFT_UPPER_FAR_ROW 4
+
+static void candidate_window_init(UIMCandidateWindow *cwin);
+static void candidate_window_class_init(UIMCandidateWindowClass *klass);
+
+static gboolean configure_event_cb(GtkWidget *widget, GdkEventConfigure *event, gpointer data);
+
+static GType candidate_window_type = 0;
+static GTypeInfo const object_info = {
+  sizeof (UIMCandidateWindowClass),
+  NULL,
+  NULL,
+  (GClassInitFunc) candidate_window_class_init,
+  NULL,
+  NULL,
+  sizeof(UIMCandidateWindow),
+  0,
+  (GInstanceInitFunc) candidate_window_init,
+};
+
+static gint cand_win_gtk_signals[NR_SIGNALS] = {0};
+
+static unsigned int read_tag;
+
+static void init_candidate_win(void);
+static void candwin_activate(gchar **str);
+static void candwin_update(gchar **str);
+static void candwin_move(char **str);
+static void candwin_show(void);
+static void candwin_deactivate(void);
+static void candwin_set_nr_candidates(gchar **str);
+static void candwin_set_page_candidates(gchar **str);
+static void candwin_show_page(gchar **str);
+static void str_parse(char *str);
+static gchar *init_labelchar_table(void);
+
+static void index_changed_cb(UIMCandidateWindow *cwin)
+{
+  fprintf(stdout, "index\n");
+  fprintf(stdout, "%d\n\n", uim_cand_win_gtk_get_index(cwin));
+  fflush(stdout);
+}
+
+GType
+candidate_window_get_type(void)
+{
+  if (!candidate_window_type)
+    candidate_window_type = g_type_register_static(GTK_TYPE_WINDOW,
+                   "UIMCandidateWindow", &object_info, (GTypeFlags)0);
+  return candidate_window_type;
+}
+
+static void candidate_window_class_init(UIMCandidateWindowClass *klass)
+{
+  cand_win_gtk_signals[INDEX_CHANGED_SIGNAL]
+    = g_signal_new("index-changed",
+                  G_TYPE_FROM_CLASS(klass),
+                  G_SIGNAL_RUN_FIRST,
+                  G_STRUCT_OFFSET(UIMCandidateWindowClass, index_changed),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE, 0);
+}
+
+UIMCandidateWindow *
+candidate_window_new(void)
+{
+  GObject *obj = g_object_new(UIM_TYPE_CANDIDATE_WINDOW, "type",
+                 GTK_WINDOW_POPUP, NULL);
+  return UIM_CANDIDATE_WINDOW(obj);
+}
+
+/* copied from uim-cand-win-gtk.c */
+static void
+update_label(UIMCandidateWindow *cwin)
+{
+  char label_str[20];
+
+  if (cwin->candidate_index >= 0)
+    g_snprintf(label_str, sizeof(label_str), "%d / %d",
+              cwin->candidate_index + 1 , cwin->nr_candidates);
+  else
+    g_snprintf(label_str, sizeof(label_str), "- / %d",
+              cwin->nr_candidates);
+
+  gtk_label_set_text(GTK_LABEL(cwin->num_label), label_str);
+}
+
+static void
+button_clicked(GtkButton *button, gpointer cwin)
+{
+}
+
+static void
+cb_table_view_destroy(GtkWidget *widget, GPtrArray *stores)
+{
+  gint i;
+
+  g_return_if_fail(GTK_IS_TABLE(widget));
+
+  for (i = cwin->stores->len - 1; i >= 0; i--) {
+    GtkListStore *store = g_ptr_array_remove_index(cwin->stores, i);
+    if (store) {
+      gtk_list_store_clear(store);
+      g_object_unref(G_OBJECT(store));
+    }
+  }
+  g_ptr_array_free(cwin->stores, TRUE);
+
+  g_ptr_array_free(cwin->buttons, TRUE);
+
+  if (cwin->labelchar_table != default_labelchar_table) {
+    g_free(cwin->labelchar_table);
+  }
+}
+
+static void
+init_candidate_win(void) {
+  cwin = candidate_window_new();
+  g_signal_connect(G_OBJECT(cwin), "index-changed",
+                  G_CALLBACK(index_changed_cb), NULL);
+  g_signal_connect(G_OBJECT(cwin), "configure_event",
+                  G_CALLBACK(configure_event_cb), NULL);
+}
+
+static void
+candidate_window_init(UIMCandidateWindow *cwin)
+{
+  GdkRectangle cursor_location;
+  gint row, col;
+
+  cwin->vbox = gtk_vbox_new(FALSE, 0);
+  cwin->frame = gtk_frame_new(NULL);
+
+  cwin->stores = g_ptr_array_new();
+  cwin->buttons = g_ptr_array_new();
+  cwin->labelchar_table = init_labelchar_table();
+
+  gtk_window_set_default_size(GTK_WINDOW(cwin),
+                 CANDWIN_DEFAULT_WIDTH, -1);
+
+
+  cwin->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(cwin->scrolled_window),
+                                GTK_POLICY_NEVER,
+                                GTK_POLICY_NEVER);
+ gtk_box_pack_start(GTK_BOX(cwin->vbox), cwin->scrolled_window, TRUE, TRUE, 0);
+
+ cwin->view = gtk_table_new(LABELCHAR_NR_ROWS, LABELCHAR_NR_COLUMNS, FALSE);
+  g_signal_connect(G_OBJECT(cwin->view), "destroy",
+                  G_CALLBACK(cb_table_view_destroy), cwin->stores);
+  cwin->viewport = gtk_viewport_new(NULL, NULL);
+  gtk_container_add(GTK_CONTAINER(cwin->viewport), cwin->view);
+  gtk_container_add(GTK_CONTAINER(cwin->scrolled_window), cwin->viewport);
+ gtk_container_set_resize_mode(GTK_CONTAINER(cwin->viewport), GTK_RESIZE_PARENT);
+  for (row = 0; row < LABELCHAR_NR_ROWS; row++) {
+    for (col = 0; col < LABELCHAR_NR_COLUMNS; col++) {
+      GtkWidget *button;
+      button = gtk_button_new_with_label("  ");
+ g_signal_connect(button, "clicked", G_CALLBACK(button_clicked), cwin);
+      gtk_table_attach_defaults(GTK_TABLE(cwin->view), button,
+                                col, col + 1, row, row + 1);
+      g_ptr_array_add(cwin->buttons, button);
+    }
+  }
+ gtk_table_set_col_spacing(GTK_TABLE(cwin->view), SPACING_LEFT_BLOCK_COLUMN,
+      BLOCK_SPACING);
+ gtk_table_set_col_spacing(GTK_TABLE(cwin->view), SPACING_RIGHT_BLOCK_COLUMN,
+      BLOCK_SPACING);
+  gtk_table_set_row_spacing(GTK_TABLE(cwin->view), SPACING_UP_BLOCK_ROW,
+      BLOCK_SPACING);
+ gtk_table_set_col_spacing(GTK_TABLE(cwin->view), SPACING_LEFTHAND_FAR_COLUMN,
+      HOMEPOSITION_SPACING);
+ gtk_table_set_col_spacing(GTK_TABLE(cwin->view), SPACING_RIGHTHAND_FAR_COLUMN,
+      HOMEPOSITION_SPACING);
+  gtk_table_set_row_spacing(GTK_TABLE(cwin->view), SPACING_UPPER_FAR_ROW,
+      HOMEPOSITION_SPACING);
+ gtk_table_set_row_spacing(GTK_TABLE(cwin->view), SPACING_SHIFT_UPPER_FAR_ROW,
+      HOMEPOSITION_SPACING);
+
+  gtk_container_add(GTK_CONTAINER(cwin->frame), cwin->vbox);
+  gtk_container_add(GTK_CONTAINER(cwin), cwin->frame);
+  gtk_container_set_border_width(GTK_CONTAINER(cwin->frame), 0);
+
+  cwin->num_label = gtk_label_new("");
+
+ gtk_box_pack_start(GTK_BOX(cwin->vbox), cwin->num_label, FALSE, FALSE, 0);
+
+  cwin->pos_x = 0;
+  cwin->pos_y = 0;
+  cwin->is_active = FALSE;
+  cwin->need_hilite = FALSE;
+  cwin->caret_state_indicator = caret_state_indicator_new();
+
+  cursor_location.x = 0;
+  cursor_location.y = 0;
+  cursor_location.height = 0;
+ caret_state_indicator_set_cursor_location(cwin->caret_state_indicator, &cursor_location);
+}
+
+static gchar *
+init_labelchar_table(void)
+{
+  gchar *table;
+  uim_lisp list;
+  size_t len = 0;
+  uim_lisp *ary0, *ary;
+  guint i;
+
+  list = uim_scm_symbol_value("uim-candwin-prog-layout");
+  if (list == NULL || !uim_scm_listp(list)) {
+    return default_labelchar_table;
+  }
+  ary0 = ary = (uim_lisp *)uim_scm_list2array(list, &len, NULL);
+  if (ary == NULL || len <= 0) {
+    if (ary0) {
+      free(ary0);
+    }
+    return default_labelchar_table;
+  }
+  table = (gchar *)g_malloc(LABELCHAR_NR_CELLS);
+  if (table == NULL) {
+    free(ary0);
+    return default_labelchar_table;
+  }
+  for (i = 0; i < LABELCHAR_NR_CELLS; i++, ary++) {
+    table[i] = '\0';
+    if (i < len) {
+      char *str = uim_scm_c_str(*ary);
+      if (str) {
+        table[i] = *str;
+        free(str);
+      }
+    }
+  }
+  free(ary0);
+  return table;
+}
+
+static void
+get_row_column(gchar *labelchar_table, const gchar labelchar, gint *row, gint *col)
+{
+  gint i;
+  for (i = 0; i < LABELCHAR_NR_CELLS; i++) {
+    if (labelchar_table[i] == labelchar) {
+      *row = i / LABELCHAR_NR_COLUMNS;
+      *col = i % LABELCHAR_NR_COLUMNS;
+      return;
+    }
+  }
+  *row = 0;
+  *col = 0;
+}
+
+static void
+set_candidate(UIMCandidateWindow *cwin, GSList *node, GtkListStore *store)
+{
+  if (node) {
+    GtkTreeIter ti;
+    gint row = 0;
+    gint col = 0;
+    gint i;
+    gchar *str = node->data;
+    /* "heading label char\acandidate\aannotation" */
+    gchar **column = g_strsplit(str, "\a", 3);
+
+    get_row_column(cwin->labelchar_table, column[0][0], &row, &col);
+    gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &ti);
+    for (i = 0; i < row; i++) {
+      gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &ti);
+    }
+    gtk_list_store_set(store, &ti, col, column[1], TERMINATOR);
+
+    g_strfreev(column);
+    g_free(str);
+  } else {
+    /* No need to set any data for empty row. */
+  }
+}
+
+static void
+candwin_activate(gchar **str)
+{
+  gsize rbytes, wbytes;
+  gint i, nr_stores = 1;
+  guint j = 1;
+  gchar *utf8_str;
+  const gchar *charset;
+  guint display_limit;
+  GSList *candidates = NULL;
+
+  if (cwin->stores == NULL)
+    cwin->stores = g_ptr_array_new();
+
+  /* remove old data */
+  for (i = cwin->stores->len - 1; i >= 0; i--) {
+    GtkListStore *store = g_ptr_array_remove_index(cwin->stores, i);
+    if (store) {
+      gtk_list_store_clear(store);
+      g_object_unref(G_OBJECT(store));
+    }
+  }
+
+  if (!strncmp(str[1], "charset=", 8))
+    charset = str[1] + 8;
+  else
+    charset = "UTF-8";
+
+  if (!strncmp(str[2], "display_limit=", 14)) {
+    display_limit = atoi(str[2] + 14);
+    i = 3;
+  } else {
+    display_limit = 0;
+    i = 2;
+  }
+
+  for ( ; str[i]; i++) {
+    if (strcmp(str[i], "") == 0) {
+      break;
+    }
+    utf8_str = g_convert(str[i],
+                        -1,
+                        "UTF-8",
+                        charset,
+                        &rbytes, &wbytes, NULL);
+
+    candidates = g_slist_prepend(candidates, utf8_str);
+    j++;
+  }
+  candidates = g_slist_reverse(candidates);
+
+  cwin->candidate_index = -1;
+  cwin->nr_candidates = j - 1;
+  cwin->display_limit = display_limit;
+  cwin->need_hilite = FALSE;
+
+  if (candidates == NULL)
+    return;
+
+  /* calculate number of GtkListStores to create */
+  if (display_limit) {
+    nr_stores = cwin->nr_candidates / display_limit;
+    if (cwin->nr_candidates > display_limit * nr_stores)
+      nr_stores++;
+  }
+
+  /* create GtkListStores, and set candidates */
+  for (i = 0; i < nr_stores; i++) {
+    GtkListStore *store = gtk_list_store_new(LABELCHAR_NR_COLUMNS,
+        G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+        G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+        G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+       G_TYPE_STRING);
+    GSList *node;
+
+    g_ptr_array_add(cwin->stores, store);
+
+    for (j = 0; j < LABELCHAR_NR_ROWS; j++) {
+      GtkTreeIter ti;
+      gtk_list_store_append(store, &ti);
+    }
+
+    /* set candidates */
+    for (j = i * display_limit, node = g_slist_nth(candidates, j);
+        display_limit ? j < display_limit * (i + 1) : j < cwin->nr_candidates;
+        j++, node = g_slist_next(node))
+    {
+      set_candidate(cwin, node, store);
+    }
+  }
+  g_slist_free(candidates);
+
+  uim_cand_win_gtk_set_page(cwin, 0);
+  update_label(cwin);
+
+  uim_cand_win_gtk_show(cwin);
+  cwin->is_active = TRUE;
+}
+
+static void
+candwin_update(gchar **str)
+{
+  int index, need_hilite;
+  sscanf(str[1], "%d", &index);
+  sscanf(str[2], "%d", &need_hilite);
+  cwin->need_hilite = (need_hilite == 1) ? TRUE : FALSE;
+
+  uim_cand_win_gtk_set_index(cwin, index);
+}
+
+static void
+candwin_move(char **str)
+{
+  sscanf(str[1], "%d", &cwin->pos_x);
+  sscanf(str[2], "%d", &cwin->pos_y);
+
+  uim_cand_win_gtk_layout();
+}
+
+static void
+candwin_show(void)
+{
+  if (cwin->is_active)
+    uim_cand_win_gtk_show(cwin);
+}
+
+static void
+candwin_deactivate(void)
+{
+  gtk_widget_hide(GTK_WIDGET(cwin));
+  cwin->is_active = FALSE;
+}
+
+static void
+caret_state_show(gchar **str)
+{
+  int timeout;
+
+  sscanf(str[1], "%d", &timeout);
+ caret_state_indicator_update(cwin->caret_state_indicator, cwin->pos_x, cwin->pos_y, str[2]);
+  if (timeout != 0)
+ caret_state_indicator_set_timeout(cwin->caret_state_indicator, timeout * 1000);
+  gtk_widget_show_all(GTK_WIDGET(cwin->caret_state_indicator));
+}
+
+static void
+caret_state_update()
+{
+ caret_state_indicator_update(cwin->caret_state_indicator, cwin->pos_x, cwin->pos_y, NULL);
+}
+
+static void
+caret_state_hide()
+{
+  gtk_widget_hide(cwin->caret_state_indicator);
+}
+
+static void
+candwin_set_nr_candidates(gchar **str)
+{
+  guint nr, display_limit;
+  gint i, nr_stores = 1;
+
+  sscanf(str[1], "%ud", &nr);
+  sscanf(str[2], "%ud", &display_limit);
+
+  cwin->candidate_index = -1;
+  cwin->nr_candidates = nr;
+  cwin->display_limit = display_limit;
+  cwin->need_hilite = FALSE;
+  cwin->is_active = TRUE;
+
+  if (cwin->stores == NULL)
+    cwin->stores = g_ptr_array_new();
+
+  /* remove old data */
+  for (i = cwin->stores->len - 1; i >= 0; i--) {
+    GtkListStore *store = g_ptr_array_remove_index(cwin->stores, i);
+    if (store) {
+      gtk_list_store_clear(store);
+      g_object_unref(G_OBJECT(store));
+    }
+  }
+
+  /* calculate number of GtkListStores to create */
+  if (display_limit) {
+    nr_stores = nr / display_limit;
+    if (nr > display_limit * nr_stores)
+      nr_stores++;
+  }
+
+  /* setup dummy array */
+  for (i = 0; i < nr_stores; i++)
+    g_ptr_array_add(cwin->stores, NULL);
+}
+
+static void
+candwin_set_page_candidates(gchar **str)
+{
+  gsize rbytes, wbytes;
+  gint i;
+  guint j = 1;
+  gchar *utf8_str;
+  const gchar *charset;
+  GSList *candidates = NULL;
+  int page;
+
+  if (!strncmp(str[1], "charset=", 8))
+    charset = str[1] + 8;
+  else
+    charset = "UTF-8";
+
+  if (!strncmp(str[2], "page=", 5)) {
+    page = atoi(str[2] + 5);
+    i = 3;
+  } else {
+    /* shouldn't happen */
+    page = 0;
+    i = 2;
+  }
+
+  for ( ; str[i]; i++) {
+    if (strcmp(str[i], "") == 0) {
+      break;
+    }
+    utf8_str = g_convert(str[i],
+                        -1,
+                        "UTF-8",
+                        charset,
+                        &rbytes, &wbytes, NULL);
+
+    candidates = g_slist_prepend(candidates, utf8_str);
+    j++;
+  }
+  candidates = g_slist_reverse(candidates);
+
+  uim_cand_win_gtk_set_page_candidates(cwin, page, candidates);
+  g_slist_free(candidates);
+}
+
+static void
+candwin_show_page(gchar **str)
+{
+  int page;
+
+  sscanf(str[1], "%d", &page);
+
+  uim_cand_win_gtk_set_page(cwin, page);
+  uim_cand_win_gtk_show(cwin);
+}
+
+static void str_parse(gchar *str)
+{
+  gchar **tmp;
+  gchar *command;
+
+  tmp = g_strsplit(str, "\f", 0);
+  command = tmp[0];
+
+  if (command) {
+    if (strcmp("activate", command) == 0) {
+      candwin_activate(tmp);
+    } else if (strcmp("select", command) == 0) {
+      candwin_update(tmp);
+    } else if (strcmp("show", command) == 0) {
+      candwin_show();
+    } else if (strcmp("hide", command) == 0) {
+      gtk_widget_hide_all(GTK_WIDGET(cwin));
+    } else if (strcmp("move", command) == 0) {
+      candwin_move(tmp);
+    } else if (strcmp("deactivate", command) == 0) {
+      candwin_deactivate();
+    } else if (strcmp("show_caret_state", command) == 0) {
+      caret_state_show(tmp);
+    } else if (strcmp("update_caret_state", command) == 0) {
+      caret_state_update();
+    } else if (strcmp("hide_caret_state", command) == 0) {
+      caret_state_hide();
+    } else if (strcmp("set_nr_candidates", command) == 0) {
+      candwin_set_nr_candidates(tmp);
+    } else if (strcmp("set_page_candidates", command) == 0) {
+      candwin_set_page_candidates(tmp);
+    } else if (strcmp("show_page", command) == 0) {
+      candwin_show_page(tmp);
+    }
+  }
+  g_strfreev(tmp);
+}
+
+#define CANDIDATE_BUFFER_SIZE  4096
+static gboolean
+read_cb(GIOChannel *channel, GIOCondition c, gpointer p)
+{
+  char buf[CANDIDATE_BUFFER_SIZE];
+  char *read_buf = strdup("");
+  int i = 0;
+  int n;
+  gchar **tmp;
+  int fd = g_io_channel_unix_get_fd(channel);
+
+  while (uim_helper_fd_readable(fd) > 0) {
+    n = read(fd, buf, CANDIDATE_BUFFER_SIZE - 1);
+    if (n == 0) {
+      close(fd);
+      exit(EXIT_FAILURE);
+    }
+    if (n == -1)
+      return TRUE;
+    buf[n] = '\0';
+    read_buf = realloc(read_buf, strlen(read_buf) + n + 1);
+    strcat(read_buf, buf);
+  }
+
+  tmp = g_strsplit(read_buf, "\f\f", 0);
+
+  while (tmp[i]) {
+    str_parse(tmp[i]);
+    i++;
+  }
+  g_strfreev(tmp);
+  free(read_buf);
+  return TRUE;
+}
+
+int
+main(int argc, char *argv[])
+{
+  GIOChannel *channel;
+
+  /* disable uim context in annotation window */
+  setenv("GTK_IM_MODULE", "gtk-im-context-simple", 1);
+
+  gtk_set_locale();
+  gtk_init(&argc, &argv);
+  if (uim_init() < 0)
+    return 0;
+
+  init_candidate_win();
+
+  channel = g_io_channel_unix_new(0);
+  read_tag = g_io_add_watch(channel, G_IO_IN | G_IO_HUP | G_IO_ERR,
+                           read_cb, 0);
+  g_io_channel_unref(channel);
+
+  gtk_main();
+  uim_quit();
+
+  return 0;
+}
+
+/* copied from uim-cand-win-gtk.c */
+static gint
+uim_cand_win_gtk_get_index(UIMCandidateWindow *cwin)
+{
+  g_return_val_if_fail(UIM_IS_CANDIDATE_WINDOW(cwin), -1);
+
+  return cwin->candidate_index;
+}
+
+/* copied from uim-cand-win-gtk.c */
+static void
+uim_cand_win_gtk_set_index(UIMCandidateWindow *cwin, gint index)
+{
+  gint new_page;
+
+  g_return_if_fail(UIM_IS_CANDIDATE_WINDOW(cwin));
+
+  if (index >= (gint) cwin->nr_candidates)
+    cwin->candidate_index = 0;
+  else
+    cwin->candidate_index = index;
+
+  if (cwin->candidate_index >= 0 && cwin->display_limit)
+    new_page = cwin->candidate_index / cwin->display_limit;
+  else
+    new_page = cwin->page_index;
+
+  if (cwin->page_index != new_page)
+    uim_cand_win_gtk_set_page(cwin, new_page);
+
+  update_label(cwin);
+}
+
+static void
+update_table_button(GtkTreeModel *model, GPtrArray *buttons, gchar *labelchar_table)
+{
+  GtkTreeIter ti;
+  gint row, col;
+  gboolean hasValue = TRUE;
+  gtk_tree_model_get_iter_first(model, &ti);
+  for (row = 0; row < LABELCHAR_NR_ROWS; row++) {
+    for (col = 0; col < LABELCHAR_NR_COLUMNS; col++) {
+      GValue value = {0};
+      const gchar *str = NULL;
+      GtkButton *button;
+      button = g_ptr_array_index(buttons, INDEX(row, col));
+      if (hasValue) {
+        gtk_tree_model_get_value(model, &ti, col, &value);
+        str = g_value_get_string(&value);
+      }
+      if (str == NULL) {
+        str = "  ";
+       if (labelchar_table[INDEX(row, col)] == '\0') {
+          gtk_button_set_relief(button, GTK_RELIEF_NONE);
+       } else {
+          gtk_button_set_relief(button, GTK_RELIEF_HALF);
+       }
+        gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE);
+      } else {
+        gtk_button_set_relief(button, GTK_RELIEF_NORMAL);
+        gtk_widget_set_sensitive(GTK_WIDGET(button), TRUE);
+      }
+      gtk_button_set_label(button, str);
+      if (hasValue) {
+        g_value_unset(&value);
+      }
+    }
+    if (hasValue) {
+      hasValue = gtk_tree_model_iter_next(model, &ti);
+    }
+  }
+}
+
+/* copied from uim-cand-win-gtk.c */
+static void
+uim_cand_win_gtk_set_page(UIMCandidateWindow *cwin, gint page)
+{
+  guint len, new_page;
+  gint new_index;
+
+  g_return_if_fail(UIM_IS_CANDIDATE_WINDOW(cwin));
+  g_return_if_fail(cwin->stores);
+
+  len = cwin->stores->len;
+  g_return_if_fail(len);
+
+  if (page < 0)
+    new_page = len - 1;
+  else if (page >= (gint) len)
+    new_page = 0;
+  else
+    new_page = page;
+
+  update_table_button(GTK_TREE_MODEL(cwin->stores->pdata[new_page]),
+                      cwin->buttons, cwin->labelchar_table);
+
+  cwin->page_index = new_page;
+
+  if (cwin->display_limit) {
+    if (cwin->candidate_index >= 0)
+      new_index
+ = (new_page * cwin->display_limit) + (cwin->candidate_index % cwin->display_limit);
+    else
+      new_index = -1;
+  } else {
+    new_index = cwin->candidate_index;
+  }
+
+  if (new_index >= (gint) cwin->nr_candidates)
+    new_index = cwin->nr_candidates - 1;
+
+ /* shrink the window */
+  gtk_window_resize(GTK_WINDOW(cwin), CANDWIN_DEFAULT_WIDTH, 1);
+
+  uim_cand_win_gtk_set_index(cwin, new_index);
+}
+
+/* copied from uim-cand-win-gtk.c and adjusted */
+static void
+uim_cand_win_gtk_set_page_candidates(UIMCandidateWindow *cwin,
+                                    guint page,
+                                    GSList *candidates)
+{
+  GtkListStore *store;
+  GSList *node;
+  gint j, len;
+
+  g_return_if_fail(UIM_IS_CANDIDATE_WINDOW(cwin));
+
+  if (candidates == NULL)
+    return;
+
+  len = g_slist_length(candidates);
+
+  /* create GtkListStores, and set candidates */
+  store = gtk_list_store_new(LABELCHAR_NR_COLUMNS, G_TYPE_STRING,
+      G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+      G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+      G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
+
+  cwin->stores->pdata[page] = store;
+  for (j = 0; j < LABELCHAR_NR_ROWS; j++) {
+    GtkTreeIter ti;
+    gtk_list_store_append(store, &ti);
+  }
+  /* set candidates */
+  for (j = 0, node = g_slist_nth(candidates, j);
+       j < len;
+       j++, node = g_slist_next(node))
+  {
+    set_candidate(cwin, node, store);
+  }
+}
+
+static void
+uim_cand_win_gtk_layout()
+{
+  int x, y;
+  int screen_width, screen_height;
+
+  screen_width = gdk_screen_get_width(gdk_screen_get_default());
+  screen_height = gdk_screen_get_height(gdk_screen_get_default());
+
+  if (screen_width < cwin->pos_x + cwin->width)
+    x = cwin->pos_x - cwin->width;
+  else
+    x = cwin->pos_x;
+
+  if (screen_height < cwin->pos_y + cwin->height)
+ y = cwin->pos_y - cwin->height - 20; /* FIXME: Preedit height is needed to
+                                           be sent by uim-xim */
+  else
+    y = cwin->pos_y;
+
+  gtk_window_move(GTK_WINDOW(cwin), x, y);
+}
+
+static gboolean
+is_empty_block(GPtrArray *buttons, gint rowstart, gint rowend, gint colstart, gint colend)
+{
+  gint row, col;
+  for (row = rowstart; row < rowend; row++) {
+    for (col = colstart; col < colend; col++) {
+      GtkButton *button;
+      GtkReliefStyle relief;
+      button = g_ptr_array_index(buttons, INDEX(row, col));
+      relief = gtk_button_get_relief(button);
+      if (relief == GTK_RELIEF_NORMAL) {
+        return FALSE;
+      }
+    }
+  }
***The diff for this file has been truncated for email.***
=======================================
--- /trunk/helper/Makefile.am   Mon Apr 26 18:53:41 2010
+++ /trunk/helper/Makefile.am   Sun Jun 13 00:29:13 2010
@@ -40,9 +40,9 @@
bin_PROGRAMS = uim-toolbar-gtk uim-toolbar-gtk-systray uim-im-switcher-gtk \
               uim-input-pad-ja
 if APPLET_GNOME
-libexec_PROGRAMS += uim-candwin-gtk
+libexec_PROGRAMS += uim-candwin-gtk uim-candwin-tbl-gtk
 else
-libexec_PROGRAMS = uim-candwin-gtk
+libexec_PROGRAMS = uim-candwin-gtk uim-candwin-tbl-gtk
 endif

 if DICT
@@ -63,6 +63,14 @@
 uim_candwin_gtk_CPPFLAGS = -I$(top_srcdir)
 uim_candwin_gtk_CFLAGS   = @GTK2_CFLAGS@

+uim_candwin_tbl_gtk_SOURCES  = candwin-tbl-gtk.c \
+                          ../gtk/caret-state-indicator.c \
+                          ../gtk/caret-state-indicator.h
+uim_candwin_tbl_gtk_LDADD = @GTK2_LIBS@ $(top_builddir)/uim/libuim-scm.la \
+                          $(top_builddir)/uim/libuim.la
+uim_candwin_tbl_gtk_CPPFLAGS = -I$(top_srcdir)
+uim_candwin_tbl_gtk_CFLAGS   = @GTK2_CFLAGS@
+
 uim_toolbar_gtk_SOURCES  = toolbar-standalone-gtk.c toolbar-common-gtk.c
 uim_toolbar_gtk_LDADD    = @GTK2_LIBS@ $(top_builddir)/uim/libuim-scm.la \
                           $(top_builddir)/uim/libuim.la

Reply via email to