Logo Search packages:      
Sourcecode: uim version File versions

pref-gtk-custom-widgets.c

/*

  Copyright (c) 2005 uim Project http://uim.freedesktop.org/

  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 REGENTS 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 REGENTS 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.

*/

#include <pref-gtk-custom-widgets.h>
#include <gdk/gdkkeysyms.h>

#include <string.h>
#include <stdlib.h>
#include <locale.h>

#include "uim/config.h"
#include "uim/uim.h"
#include "uim/uim-custom.h"
#include "uim/gettext.h"

#define OBJECT_DATA_UIM_CUSTOM_SYM    "uim-pref-gtk::uim-custom-sym"

extern gboolean uim_pref_gtk_value_changed;

static GtkSizeGroup *spin_button_sgroup = NULL;

static struct OListPrefWin {
  GtkWidget *window;
  GtkWidget *tree_view[2];
  GtkWidget *up_button;
  GtkWidget *down_button;
  GtkWidget *left_button;
  GtkWidget *right_button;
} olist_pref_win = {
  NULL, {NULL, NULL},
};
static struct KeyPrefWin {
  GtkWidget *window;
  GtkWidget *tree_view;
  GtkWidget *add_button;
  GtkWidget *remove_button;
  GtkWidget *keycode_entry;

  guint           grabbed_key_val;
  GdkModifierType grabbed_key_state;
} key_pref_win = {
  NULL, NULL, NULL, NULL, NULL, 0, 0,
};



static void
custom_check_button_toggled_cb(GtkToggleButton *button, gpointer user_data)
{
  const char *custom_sym;
  struct uim_custom *custom;
  uim_bool rv;
  gboolean active;

  custom_sym = g_object_get_data(G_OBJECT(button), OBJECT_DATA_UIM_CUSTOM_SYM);
  g_return_if_fail(custom_sym);

  custom = uim_custom_get(custom_sym);
  g_return_if_fail(custom && custom->type == UCustom_Bool);

  active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
  custom->value->as_bool = active;
  rv = uim_custom_set(custom);

  if (rv) {
    uim_pref_gtk_value_changed = TRUE;
  } else {
    g_printerr("Failed to set bool value for \"%s\".\n", custom->symbol);
    /* FIXME! reset the widget */
  }

  uim_custom_free(custom);
}

static void
sync_value_bool(GtkCheckButton *button)
{
  const char *custom_sym;
  struct uim_custom *custom;

  g_signal_handlers_block_by_func(G_OBJECT(button),
                          (gpointer) custom_check_button_toggled_cb,
                          NULL);

  custom_sym = g_object_get_data(G_OBJECT(button), OBJECT_DATA_UIM_CUSTOM_SYM);
  g_return_if_fail(custom_sym);

  custom = uim_custom_get(custom_sym);
  g_return_if_fail(custom && custom->type == UCustom_Bool);

  gtk_widget_set_sensitive(GTK_WIDGET(button), custom->is_active);
  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), custom->value->as_bool);

  uim_custom_free(custom);

  g_signal_handlers_unblock_by_func(G_OBJECT(button),
                            (gpointer) custom_check_button_toggled_cb,
                            NULL);
}

static void
update_custom_type_bool_cb(void *ptr, const char *custom_sym)
{
  sync_value_bool(GTK_CHECK_BUTTON(ptr));
}

static void
add_custom_type_bool(GtkWidget *vbox, struct uim_custom *custom)
{
  GtkWidget *hbox;
  GtkWidget *check_button;
  hbox = gtk_hbox_new(FALSE, 8);

  check_button = gtk_check_button_new_with_label(custom->label);
  g_object_set_data_full(G_OBJECT(check_button),
                   OBJECT_DATA_UIM_CUSTOM_SYM, g_strdup(custom->symbol),
                   (GDestroyNotify) g_free);

  sync_value_bool(GTK_CHECK_BUTTON(check_button));

  g_signal_connect(G_OBJECT(check_button), "toggled",
               G_CALLBACK(custom_check_button_toggled_cb), NULL);
  uim_custom_cb_add(custom->symbol, check_button, update_custom_type_bool_cb);

  gtk_box_pack_start (GTK_BOX (hbox), check_button, FALSE, TRUE, 0);
  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);
}



static void
custom_spin_button_value_changed(GtkSpinButton *spin, gpointer user_data)
{
  const char *custom_sym;
  struct uim_custom *custom;
  uim_bool rv;

  custom_sym = g_object_get_data(G_OBJECT(spin), OBJECT_DATA_UIM_CUSTOM_SYM);
  g_return_if_fail(custom_sym);

  custom = uim_custom_get(custom_sym);
  g_return_if_fail(custom && custom->type == UCustom_Int);

  custom->value->as_int = gtk_spin_button_get_value(spin);
  rv = uim_custom_set(custom);

  if (rv) {
    uim_pref_gtk_value_changed = TRUE;
  } else {
    g_printerr("Failed to set int value for \"%s\".\n", custom->symbol);
    /* FIXME! reset the widget */
  }

  uim_custom_free(custom);
}

static void
sync_value_int(GtkSpinButton *spin)
{
  const char *custom_sym;
  struct uim_custom *custom;
  GtkAdjustment *adj;

  g_signal_handlers_block_by_func(G_OBJECT(spin),
                          (gpointer) custom_spin_button_value_changed,
                          NULL);

  adj = gtk_spin_button_get_adjustment(spin);

  custom_sym = g_object_get_data(G_OBJECT(spin), OBJECT_DATA_UIM_CUSTOM_SYM);
  g_return_if_fail(custom_sym);

  custom = uim_custom_get(custom_sym);
  g_return_if_fail(custom && custom->type == UCustom_Int);

  gtk_widget_set_sensitive(GTK_WIDGET(spin)->parent, custom->is_active);
  if (custom->range->as_int.min != (int) adj->lower ||
      custom->range->as_int.max != (int) adj->upper)
    gtk_spin_button_set_range(spin, custom->range->as_int.min, custom->range->as_int.max);
  if (custom->value->as_int != (int) gtk_spin_button_get_value(spin)) {
    gtk_spin_button_set_value(spin, custom->value->as_int);
  }

  uim_custom_free(custom);

  g_signal_handlers_unblock_by_func(G_OBJECT(spin),
                            (gpointer) custom_spin_button_value_changed,
                            NULL);
}

static void
update_custom_type_int_cb(void *ptr, const char *custom_sym)
{
  sync_value_int(GTK_SPIN_BUTTON(ptr));
}

static void
add_custom_type_integer(GtkWidget *vbox, struct uim_custom *custom)
{
  GtkWidget *hbox;
  GtkWidget *label;
  GtkAdjustment *adjustment;
  GtkWidget *spin;

  if (!spin_button_sgroup)
    spin_button_sgroup = gtk_size_group_new(GTK_SIZE_GROUP_VERTICAL);

  hbox = gtk_hbox_new(FALSE, 8);

  label = gtk_label_new(custom->label);
  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);

  adjustment = (GtkAdjustment*)gtk_adjustment_new(custom->value->as_int, /* initial size */
                                      custom->range->as_int.min, /* minimum */
                                      custom->range->as_int.max, /* maximum */
                                      1.0,
                                      10.0,
                                      100.0);
  spin = gtk_spin_button_new(adjustment, 1.0, 0);
  gtk_size_group_add_widget(spin_button_sgroup, spin);
  gtk_box_pack_end (GTK_BOX (hbox), spin, FALSE, TRUE, 0);

  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);

  g_object_set_data_full(G_OBJECT(adjustment),
                   OBJECT_DATA_UIM_CUSTOM_SYM, g_strdup(custom->symbol),
                   (GDestroyNotify) g_free);
  g_object_set_data_full(G_OBJECT(spin),
                   OBJECT_DATA_UIM_CUSTOM_SYM, g_strdup(custom->symbol),
                   (GDestroyNotify) g_free);

  sync_value_int(GTK_SPIN_BUTTON(spin));

  g_signal_connect(G_OBJECT(spin), "value-changed",
               G_CALLBACK(custom_spin_button_value_changed), NULL);
  uim_custom_cb_add(custom->symbol, spin, update_custom_type_int_cb);
}



static void
custom_entry_changed_cb(GtkEntry *entry, gpointer user_data)
{
  const char *custom_sym;
  struct uim_custom *custom;
  uim_bool rv;
  const char *str;

  custom_sym = g_object_get_data(G_OBJECT(entry), OBJECT_DATA_UIM_CUSTOM_SYM);
  g_return_if_fail(custom_sym);

  custom = uim_custom_get(custom_sym);
  g_return_if_fail(custom &&
               (custom->type == UCustom_Str ||
                custom->type == UCustom_Pathname));

  str = gtk_entry_get_text(GTK_ENTRY(entry));

  if (custom->type == UCustom_Str) {
    free(custom->value->as_str);
    custom->value->as_str = strdup(str);
    rv = uim_custom_set(custom);
  } else if (custom->type == UCustom_Pathname) {
    free(custom->value->as_pathname);
    custom->value->as_pathname = strdup(str);
    rv = uim_custom_set(custom);
  } else {
    rv = UIM_FALSE;
  }

  if (rv) {
    uim_pref_gtk_value_changed = TRUE;
  } else {
    g_printerr("Failed to set str value for \"%s\".\n", custom->symbol);
    /* FIXME! reset the widget */
  }

  uim_custom_free(custom);
}

static void
sync_value_string(GtkEntry *entry)
{
  const char *custom_sym;
  struct uim_custom *custom;

  g_signal_handlers_block_by_func(G_OBJECT(entry),
                          (gpointer) custom_entry_changed_cb,
                          NULL);

  custom_sym = g_object_get_data(G_OBJECT(entry), OBJECT_DATA_UIM_CUSTOM_SYM);
  g_return_if_fail(custom_sym);

  custom = uim_custom_get(custom_sym);
  g_return_if_fail(custom &&
               (custom->type == UCustom_Str ||
                custom->type == UCustom_Pathname));

  gtk_widget_set_sensitive(GTK_WIDGET(entry)->parent, custom->is_active);
  if (custom->type == UCustom_Str) {
    gtk_entry_set_text(GTK_ENTRY(entry), custom->value->as_str);
  } else if (custom->type == UCustom_Pathname) {
    gtk_entry_set_text(GTK_ENTRY(entry), custom->value->as_pathname);
  }

  uim_custom_free(custom);

  g_signal_handlers_unblock_by_func(G_OBJECT(entry),
                            (gpointer) custom_entry_changed_cb,
                            NULL);
}

static void
update_custom_type_string_cb(void *ptr, const char *custom_sym)
{
  sync_value_string(GTK_ENTRY(ptr));
}

static void
add_custom_type_string(GtkWidget *vbox, struct uim_custom *custom)
{
  GtkWidget *hbox;
  GtkWidget *label;
  GtkWidget *entry;

  hbox = gtk_hbox_new(FALSE, 8);

  label = gtk_label_new(custom->label);
  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);

  entry = gtk_entry_new();
  gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);

  g_object_set_data_full(G_OBJECT(entry),
                   OBJECT_DATA_UIM_CUSTOM_SYM, g_strdup(custom->symbol),
                   (GDestroyNotify) g_free);

  sync_value_string(GTK_ENTRY(entry));

  g_signal_connect(G_OBJECT(entry), "changed",
               G_CALLBACK(custom_entry_changed_cb), NULL);
  uim_custom_cb_add(custom->symbol, entry, update_custom_type_string_cb);
}



static void
custom_pathname_button_clicked_cb(GtkWidget *button, GtkWidget *entry)
{
  GtkWidget *dialog;
  dialog = gtk_file_chooser_dialog_new (_("Specify file"),
                              NULL,
                              GTK_FILE_CHOOSER_ACTION_OPEN,
                              GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                              GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
                              NULL);
  
  if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
    char *filename;    
    filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
    if(filename) {
      gtk_entry_set_text(GTK_ENTRY(entry), filename);
      g_free (filename);
    }
  }

  gtk_widget_destroy (dialog);
}

static void
add_custom_type_pathname(GtkWidget *vbox, struct uim_custom *custom)
{
  GtkWidget *hbox;
  GtkWidget *label;
  GtkWidget *entry;
  GtkWidget *button;

  hbox = gtk_hbox_new(FALSE, 8);
 
  label = gtk_label_new(custom->label);
  gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
  gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
  gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);

  entry = gtk_entry_new();
  gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);

  button = gtk_button_new_with_label(_("File..."));
  gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0);

  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);

  g_object_set_data_full(G_OBJECT(entry),
                   OBJECT_DATA_UIM_CUSTOM_SYM, g_strdup(custom->symbol),
                   (GDestroyNotify) g_free);

  sync_value_string(GTK_ENTRY(entry));

  g_signal_connect(G_OBJECT(entry), "changed",
               G_CALLBACK(custom_entry_changed_cb), NULL);

  g_signal_connect(G_OBJECT(button), "clicked",
               G_CALLBACK(custom_pathname_button_clicked_cb), entry);
}



static void
custom_combo_box_changed(GtkComboBox *combo_box, gpointer user_data)
{
  const char *custom_sym;
  struct uim_custom *custom;
  struct uim_custom_choice *choice = NULL;
  gint i, num;
  uim_bool rv;

  custom_sym = g_object_get_data(G_OBJECT(combo_box), OBJECT_DATA_UIM_CUSTOM_SYM);
  g_return_if_fail(custom_sym);

  custom = uim_custom_get(custom_sym);
  g_return_if_fail(custom_sym && custom->type == UCustom_Choice);

  num = gtk_combo_box_get_active(combo_box);

  /* check length of custom->range->as_choice.valid_items */
  for (i = 0;
       custom->range->as_choice.valid_items &&
       custom->range->as_choice.valid_items[i];
       i++)
  {
    if (i == num) {
      choice = custom->range->as_choice.valid_items[i];
    }
  }
  g_return_if_fail(choice);

  free(custom->value->as_choice->symbol);
  free(custom->value->as_choice->label);
  free(custom->value->as_choice->desc);

  custom->value->as_choice->symbol = choice->symbol ? strdup(choice->symbol): NULL;
  custom->value->as_choice->label  = choice->label  ? strdup(choice->label) : NULL;
  custom->value->as_choice->desc   = choice->desc   ? strdup(choice->desc)  : NULL;

  rv = uim_custom_set(custom);

  if (rv) {
    uim_pref_gtk_value_changed = TRUE;
  } else {
    g_printerr("Failed to set str value for \"%s\".\n", custom->symbol);
    /* FIXME! reset the widget */
  }

  uim_custom_free(custom);
}

static void
sync_value_choice(GtkComboBox *combobox)
{
  const char *custom_sym;
  struct uim_custom *custom;
  struct uim_custom_choice **item;
  gint i = 0, default_index = 0;
  gchar *default_symbol;

  custom_sym = g_object_get_data(G_OBJECT(combobox), OBJECT_DATA_UIM_CUSTOM_SYM);
  g_return_if_fail(custom_sym);

  custom = uim_custom_get(custom_sym);
  g_return_if_fail (custom && custom->type == UCustom_Choice);

  item = custom->range->as_choice.valid_items;
  if(item == NULL || *item == NULL) {
    uim_custom_free(custom);
    return;
  }
  
  g_signal_handlers_block_by_func(G_OBJECT(combobox),
                          (gpointer)custom_combo_box_changed, NULL);

  gtk_list_store_clear(GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combobox))));
  
  default_symbol = custom->value->as_choice->symbol;

  while(*item) {
    gtk_combo_box_append_text(GTK_COMBO_BOX(combobox), (*item)->label);
    if(!strcmp(default_symbol, (*item)->symbol))
      default_index = i;    
    i++;
    item++;
  }
  gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), default_index);

  gtk_widget_set_sensitive(GTK_WIDGET(combobox)->parent, custom->is_active);

  g_signal_handlers_unblock_by_func(G_OBJECT(combobox),
                            (gpointer)custom_combo_box_changed, NULL);

  uim_custom_free(custom);
}

static void
update_custom_type_choice_cb(void *ptr, const char *custom_sym)
{
  sync_value_choice(GTK_COMBO_BOX(ptr));
}

static void
add_custom_type_choice(GtkWidget *vbox, struct uim_custom *custom)
{
  GtkWidget *hbox;
  GtkWidget *label;
  GtkWidget *combobox;

  hbox = gtk_hbox_new(FALSE, 8);
  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);

  label = gtk_label_new(custom->label);
  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);

  combobox = gtk_combo_box_new_text();
  gtk_box_pack_start (GTK_BOX (hbox), combobox, FALSE, TRUE, 0);

  g_object_set_data_full(G_OBJECT(combobox),
                   OBJECT_DATA_UIM_CUSTOM_SYM, g_strdup(custom->symbol),
                   (GDestroyNotify) g_free);
  
  sync_value_choice(GTK_COMBO_BOX(combobox));

  g_signal_connect(G_OBJECT(combobox), "changed",
               G_CALLBACK(custom_combo_box_changed), NULL);
  uim_custom_cb_add(custom->symbol, combobox, update_custom_type_choice_cb);
}



static void
olist_pref_dialog_response_cb(GtkDialog *dialog, gint action, GtkEntry *key_entry)
{
  gtk_widget_destroy(GTK_WIDGET(dialog));
}

static void
sync_value_olist(GtkEntry *entry)
{
  const char *custom_sym;
  struct uim_custom *custom;
  struct uim_custom_choice *item;
  GString *str;
  gint i;

  custom_sym = g_object_get_data(G_OBJECT(entry), OBJECT_DATA_UIM_CUSTOM_SYM);
  g_return_if_fail(custom_sym);

  custom = uim_custom_get(custom_sym);
  g_return_if_fail(custom && custom->type == UCustom_OrderedList);

  str = g_string_new("");

  if (custom->value->as_olist) {
    for (item = custom->value->as_olist[0], i = 0;
       item;
       item = custom->value->as_olist[++i])
    {
      if (i != 0)
      g_string_append(str, ", ");
      g_string_append_printf(str, "\"%s\"", item->label);
    }
  } else {
    /* error message */
  }

  gtk_entry_set_text(GTK_ENTRY(entry), str->str);
  gtk_widget_set_sensitive(GTK_WIDGET(entry)->parent, custom->is_active);

  g_string_free(str, TRUE);
  uim_custom_free(custom);
}

static void
olist_pref_tree_view_set_value(GtkEntry *olist_entry,
                         gboolean left_view,
                         gboolean right_view)
{
  const char *custom_sym;
  struct uim_custom *custom;
  struct uim_custom_choice *item;
  GtkTreeView *view;
  GtkListStore *store;
  GtkTreeIter iter;
  gint i;

  custom_sym = g_object_get_data(G_OBJECT(olist_entry), OBJECT_DATA_UIM_CUSTOM_SYM);
  g_return_if_fail(custom_sym);

  custom = uim_custom_get(custom_sym);
  g_return_if_fail(custom && custom->type == UCustom_OrderedList);

  /* left tree view */
  if (left_view) {
    view = GTK_TREE_VIEW(olist_pref_win.tree_view[0]);
    store = GTK_LIST_STORE(gtk_tree_view_get_model(view));
    gtk_list_store_clear(store);

    for (item = custom->value->as_olist[0], i = 0;
       item;
       item = custom->value->as_olist[++i])
    {
      gtk_list_store_append(store, &iter);
      gtk_list_store_set(store, &iter,
                   0, item->label,
                   1, item->symbol,
                   -1);
    }
  }

  /* right tree view */
  if (right_view) {
    view = GTK_TREE_VIEW(olist_pref_win.tree_view[1]);
    store = GTK_LIST_STORE(gtk_tree_view_get_model(view));
    gtk_list_store_clear(store);

    for (item = custom->range->as_olist.valid_items[0], i = 0;
       item;
       item = custom->range->as_olist.valid_items[++i])
    {
      struct uim_custom_choice *item2;
      gint j;
      gboolean enabled = FALSE;

      for (item2 = custom->value->as_olist[0], j = 0;
         item2;
         item2 = custom->value->as_olist[++j])
      {
      if (!strcmp(item->symbol, item2->symbol)) {
        enabled = TRUE;
        break;
      }
      }

      if (!enabled) {
      gtk_list_store_append(store, &iter);
      gtk_list_store_set(store, &iter,
                     0, item->label,
                     1, item->symbol,
                     -1);
      }
    }
  }

  uim_custom_free(custom);
}

static void
set_olist_buttons_sensitive(GtkEntry *olist_entry)
{
  const char *custom_sym;
  struct uim_custom *custom;
  GtkWidget *view;
  GtkTreeSelection *selection;
  gint num;
  gboolean is_selected1 = FALSE, is_selected2 = FALSE;
  gboolean is_multiple = FALSE, is_first = FALSE, is_end = FALSE;

  custom_sym = g_object_get_data(G_OBJECT(olist_entry), OBJECT_DATA_UIM_CUSTOM_SYM);
  g_return_if_fail(custom_sym);

  custom = uim_custom_get(custom_sym);
  g_return_if_fail(custom && custom->type == UCustom_OrderedList);

  view = olist_pref_win.tree_view[0];
  selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
  num = gtk_tree_selection_count_selected_rows(selection);
  if (num > 0) {
    is_selected1 = TRUE;

    if (num > 1) {
      is_multiple = TRUE;
    } else {
      GtkTreeModel *model;
      GList *rows = gtk_tree_selection_get_selected_rows(selection, &model);
      GtkTreePath *path = rows->data;
      GtkTreeIter iter;

      if (path && !gtk_tree_path_prev(path))
        is_first = TRUE;

      if (gtk_tree_model_get_iter(model, &iter, path)) {
      if (!gtk_tree_model_iter_next(model, &iter)) {
        is_end = TRUE;
      }
      }

      g_list_foreach (rows, (GFunc)gtk_tree_path_free, NULL);
      g_list_free (rows);
    }
  }

  view = olist_pref_win.tree_view[1];
  selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
  num = gtk_tree_selection_count_selected_rows(selection);
  if (num > 0)
    is_selected2 = TRUE;

  gtk_widget_set_sensitive(olist_pref_win.up_button,
                     is_selected1 && !is_first && !is_multiple);
  gtk_widget_set_sensitive(olist_pref_win.down_button,
                     is_selected1 && !is_end && !is_multiple);
  gtk_widget_set_sensitive(olist_pref_win.right_button, is_selected1);
  gtk_widget_set_sensitive(olist_pref_win.left_button,  is_selected2);

  uim_custom_free(custom);
}

static void
olist_pref_up_button_clicked_cb(GtkWidget *widget, GtkEntry *olist_entry)
{
  const char *custom_sym;
  struct uim_custom *custom;
  struct uim_custom_choice *choice;
  GtkWidget *view;
  GtkTreeSelection *selection;
  GtkTreeModel *model;
  GtkTreeIter iter1, iter2;
  GtkTreePath *path;
  GList *rows = NULL;
  gint num;
  gboolean rv;
  uim_bool urv;
  gint *indices, idx;

  custom_sym = g_object_get_data(G_OBJECT(olist_entry), OBJECT_DATA_UIM_CUSTOM_SYM);
  g_return_if_fail(custom_sym);

  custom = uim_custom_get(custom_sym);;
  g_return_if_fail(custom && custom->type == UCustom_OrderedList);

  view = olist_pref_win.tree_view[0];

  selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
  num = gtk_tree_selection_count_selected_rows(selection);
  if (num < 1) {
    uim_custom_free(custom);
    return;
  }
  if (num > 1) {
    uim_custom_free(custom);
    return;
  }

  rows = gtk_tree_selection_get_selected_rows(selection, &model);
  path = rows->data;

  rv = gtk_tree_model_get_iter(model, &iter1, path);
  if (!rv)
    goto ERROR;

  indices = gtk_tree_path_get_indices(path);
  if (!indices)
    goto ERROR;
  idx = *indices;

  rv = gtk_tree_path_prev(path);
  if (!rv)
    goto ERROR;

  /* real move */
  choice = custom->value->as_olist[idx];
  custom->value->as_olist[idx] = custom->value->as_olist[idx - 1];
  custom->value->as_olist[idx - 1] = choice;
  urv = uim_custom_set(custom);
  if (urv == UIM_FALSE)
    return;

  uim_pref_gtk_value_changed = TRUE;

  /* sync the view */
  rv = gtk_tree_model_get_iter(model, &iter2, path);
  if (!rv)
    goto ERROR;

  gtk_list_store_swap(GTK_LIST_STORE(model), &iter1, &iter2);
  path = gtk_tree_model_get_path(model, &iter1);
  gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(view), path, NULL,
                         FALSE, 0.0, 0.0);
  gtk_tree_path_free(path);
  set_olist_buttons_sensitive(olist_entry);
  sync_value_olist(GTK_ENTRY(olist_entry));

ERROR:
  uim_custom_free(custom);
  g_list_foreach(rows, (GFunc)gtk_tree_path_free, NULL);
  g_list_free(rows);
}

static void
olist_pref_down_button_clicked_cb(GtkWidget *widget, GtkEntry *olist_entry)
{
  const char *custom_sym;
  struct uim_custom *custom;
  struct uim_custom_choice *choice;
  GtkWidget *view;
  GtkTreeSelection *selection;
  GtkTreeModel *model;
  GtkTreeIter iter1, iter2;
  GtkTreePath *path;
  GList *rows = NULL;
  gint *indices, idx, num;
  gboolean rv;
  uim_bool urv;

  custom_sym = g_object_get_data(G_OBJECT(olist_entry), OBJECT_DATA_UIM_CUSTOM_SYM);
  g_return_if_fail(custom_sym);

  custom = uim_custom_get(custom_sym);
  g_return_if_fail(custom && custom->type == UCustom_OrderedList);

  view = olist_pref_win.tree_view[0];

  selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
  num = gtk_tree_selection_count_selected_rows(selection);
  if (num < 1) {
    uim_custom_free(custom);
    return;
  }
  if (num > 1) {
    uim_custom_free(custom);
    return;
  }

  rows = gtk_tree_selection_get_selected_rows(selection, &model);
  path = rows->data;
  if (!path)
    goto ERROR;
  indices = gtk_tree_path_get_indices(path);
  if (!indices)
    goto ERROR;
  idx = *indices;

  rv = gtk_tree_model_get_iter(model, &iter1, path);
  if (!rv)
    goto ERROR;

  iter2 = iter1;

  rv = gtk_tree_model_iter_next(model, &iter2);
  if (!rv)
    goto ERROR;

  /* real move */
  choice = custom->value->as_olist[idx];
  custom->value->as_olist[idx] = custom->value->as_olist[idx + 1];
  custom->value->as_olist[idx + 1] = choice;
  urv = uim_custom_set(custom);
  if (urv == UIM_FALSE)
    goto ERROR;

  uim_pref_gtk_value_changed = TRUE;

  /* sync the view */
  gtk_list_store_swap(GTK_LIST_STORE(model), &iter1, &iter2);
  path = gtk_tree_model_get_path(model, &iter1);
  gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(view), path, NULL,
                         FALSE, 0.0, 0.0);
  gtk_tree_path_free(path);
  set_olist_buttons_sensitive(olist_entry);
  sync_value_olist(GTK_ENTRY(olist_entry));

ERROR:
  uim_custom_free(custom);
  g_list_foreach(rows, (GFunc)gtk_tree_path_free, NULL);
  g_list_free(rows);
}

static void
olist_pref_left_button_clicked_cb(GtkWidget *widget, GtkEntry *olist_entry)
{
  const char *custom_sym;
  struct uim_custom *custom;
  GtkWidget *view;
  GtkTreeSelection *selection;
  GtkTreeModel *model;
  GtkTreeIter iter;
  GtkTreePath *path;
  gint num;
  GList *rows = NULL, *node;
  uim_bool urv;

  custom_sym = g_object_get_data(G_OBJECT(olist_entry), OBJECT_DATA_UIM_CUSTOM_SYM);
  g_return_if_fail(custom_sym);

  custom = uim_custom_get(custom_sym);
  g_return_if_fail(custom && custom->type == UCustom_OrderedList);

  view = olist_pref_win.tree_view[1];

  selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
  rows = gtk_tree_selection_get_selected_rows(selection, &model);
  if (!rows) {
    uim_custom_free(custom);
    return;
  }

  for (num = 0; custom->value->as_olist[num]; num++);

  for (node = rows; node; node = g_list_next(node)) {
    char *symbol = NULL;
    struct uim_custom_choice *choice = NULL;
    gint i;

    path = node->data;

    gtk_tree_model_get_iter(model, &iter, path);
    gtk_tree_model_get(model, &iter,
                   1, &symbol,
                   -1);

    if (!symbol)
      continue;

    for (i = 0; custom->range->as_olist.valid_items[i]->symbol; i++) {
      if (!strcmp(custom->range->as_olist.valid_items[i]->symbol, symbol)) {
      choice = custom->range->as_olist.valid_items[i];
      break;
      }
    }

    g_free(symbol);

    if (!choice)
      continue;

    num++;
    custom->value->as_olist = realloc(custom->value->as_olist,
                              sizeof(struct uim_custom_choice *) * (num + 1));
    custom->value->as_olist[num - 1] = malloc(sizeof(struct uim_custom_choice));
    custom->value->as_olist[num - 1]->symbol = choice->symbol ? strdup(choice->symbol) : NULL;
    custom->value->as_olist[num - 1]->label  = choice->label  ? strdup(choice->label)  : NULL;
    custom->value->as_olist[num - 1]->desc   = choice->desc   ? strdup(choice->desc)   : NULL;
    custom->value->as_olist[num] = NULL;
  }
  
  urv = uim_custom_set(custom);

  if (urv != UIM_FALSE) {
    sync_value_olist(GTK_ENTRY(olist_entry));
    olist_pref_tree_view_set_value(GTK_ENTRY(olist_entry), TRUE, TRUE);
    uim_pref_gtk_value_changed = TRUE;
  } else {
    /* error message */
  }

  uim_custom_free(custom);
  g_list_foreach(rows, (GFunc)gtk_tree_path_free, NULL);
  g_list_free(rows);
}

static void
olist_pref_right_button_clicked_cb(GtkWidget *widget, GtkEntry *olist_entry)
{
  const char *custom_sym;
  struct uim_custom *custom;
  struct uim_custom_choice *choice;
  GtkWidget *view;
  GtkTreeSelection *selection;
  GtkTreeModel *model;
  GtkTreePath *path;
  gint *indices, idx, num;
  GList *rows = NULL, *node;
  uim_bool urv;

  custom_sym = g_object_get_data(G_OBJECT(olist_entry), OBJECT_DATA_UIM_CUSTOM_SYM);
  g_return_if_fail(custom_sym);

  custom = uim_custom_get(custom_sym);
  g_return_if_fail(custom && custom->type == UCustom_OrderedList);

  view = olist_pref_win.tree_view[0];

  selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
  rows = gtk_tree_selection_get_selected_rows(selection, &model);
  if (!rows) {
    uim_custom_free(custom);
    return;
  }

  for (num = 0; custom->value->as_olist[num]; num++);

  for (node = g_list_last(rows); node; node = g_list_previous(node)) {
    path = node->data;
    indices = gtk_tree_path_get_indices(path);
    if (!indices)
      continue;
    idx = *indices;

    choice = custom->value->as_olist[idx];
    free(choice->symbol);
    free(choice->label);
    free(choice->desc);
    free(choice);

    memmove(custom->value->as_olist + idx,
          custom->value->as_olist + idx + 1,
          sizeof(struct uim_custom_choice *) * (num - idx));
    custom->value->as_olist = realloc(custom->value->as_olist,
                              sizeof(struct uim_custom_choice *) * num);
    num--;
  }

  urv = uim_custom_set(custom);

  if (urv != UIM_FALSE) {
    sync_value_olist(GTK_ENTRY(olist_entry));
    olist_pref_tree_view_set_value(GTK_ENTRY(olist_entry), TRUE, TRUE);
    uim_pref_gtk_value_changed = TRUE;
    /* FIXME! reset the selection */
  } else {
    /* error message */
  }

  uim_custom_free(custom);
  g_list_foreach(rows, (GFunc)gtk_tree_path_free, NULL);
  g_list_free(rows);
}

static gboolean
olist_pref_selection_changed(GtkTreeSelection *selection,
                       GtkEntry *olist_entry)
{
  set_olist_buttons_sensitive(olist_entry);

  return TRUE;
}

static void
choose_olist_clicked_cb(GtkWidget *widget, GtkEntry *olist_entry)
{
  const char *custom_sym;
  struct uim_custom *custom;
  GtkWidget *dialog, *hbox, *vbox, *scrwin, *table;
  GtkWidget *tree_view, *button, *arrow, *label;
  GtkListStore *store;
  GtkCellRenderer *renderer;
  GtkTreeViewColumn *column;
  GtkTreeSelection *selection;

  custom_sym = g_object_get_data(G_OBJECT(olist_entry), OBJECT_DATA_UIM_CUSTOM_SYM);;
  g_return_if_fail(custom_sym);
  custom = uim_custom_get(custom_sym);
  g_return_if_fail(custom);

  dialog = gtk_dialog_new_with_buttons(
    custom->label, GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(olist_entry))),
    GTK_DIALOG_MODAL,
    GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
    NULL);

  uim_custom_free(custom);

  olist_pref_win.window = dialog;
  gtk_window_set_default_size(GTK_WINDOW(dialog), 400, 250);
  g_signal_connect(G_OBJECT(dialog), "response",
               G_CALLBACK(olist_pref_dialog_response_cb), olist_entry);

  hbox = gtk_hbox_new(FALSE, 0);
  gtk_container_set_border_width(GTK_CONTAINER(hbox), 4);
  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox,
                 TRUE, TRUE, 0);
  gtk_widget_show(hbox);

  /* left tree view */
  vbox = gtk_vbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(hbox), vbox,
                 TRUE, TRUE, 0);
  gtk_widget_show(vbox);

  label = gtk_label_new(_("Enabled"));
  gtk_box_pack_start(GTK_BOX(vbox), label,
                 FALSE, FALSE, 4);
  gtk_widget_show(label);

  scrwin = gtk_scrolled_window_new(NULL, NULL);
  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrwin),
                         GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
  gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrwin),
                              GTK_SHADOW_IN);
  gtk_box_pack_start(GTK_BOX(vbox), scrwin,
                 TRUE, TRUE, 0);
  gtk_widget_show(scrwin);

  store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
  tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
  olist_pref_win.tree_view[0] = tree_view;
  gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view), FALSE);
  selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
  gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
  g_signal_connect (G_OBJECT(selection), "changed",
                G_CALLBACK(olist_pref_selection_changed), olist_entry);
  gtk_container_add(GTK_CONTAINER(scrwin), tree_view);
  gtk_widget_show(tree_view);

  renderer = gtk_cell_renderer_text_new();
  column = gtk_tree_view_column_new_with_attributes(_("Enabled items"),
                                        renderer,
                                        "text", 0,
                                        NULL);
  gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);

  g_object_unref(store);

  /* button area */
  vbox = gtk_vbox_new(TRUE, 0);
  gtk_box_pack_start(GTK_BOX(hbox), vbox,
                 FALSE, FALSE, 4);
  gtk_widget_show(vbox);

  table = gtk_table_new(3, 6, FALSE);
  gtk_table_set_row_spacings(GTK_TABLE(table), 3);
  gtk_table_set_col_spacings(GTK_TABLE(table), 3);
  gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
  gtk_widget_show(table);

  /* up button */
  button = gtk_button_new();
  olist_pref_win.up_button = button;
  gtk_table_attach_defaults(GTK_TABLE(table), button,
                      1, 2,
                      0, 1);
  gtk_widget_show(button);
  arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
  gtk_container_add(GTK_CONTAINER(button), arrow);
  gtk_widget_show(arrow);
  g_signal_connect(G_OBJECT(button), "clicked",
               G_CALLBACK(olist_pref_up_button_clicked_cb), olist_entry);

  /* down button */
  button = gtk_button_new();
  olist_pref_win.down_button = button;
  gtk_table_attach_defaults(GTK_TABLE(table), button,
                      1, 2,
                      2, 3);
  gtk_widget_show(button);
  arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_NONE);
  gtk_container_add(GTK_CONTAINER(button), arrow);
  gtk_widget_show(arrow);
  g_signal_connect(G_OBJECT(button), "clicked",
               G_CALLBACK(olist_pref_down_button_clicked_cb), olist_entry);

  /* left button */
  button = gtk_button_new();
  olist_pref_win.left_button = button;
  gtk_table_attach_defaults(GTK_TABLE(table), button,
                      0, 1,
                      1, 2);
  gtk_widget_show(button);
  arrow = gtk_arrow_new(GTK_ARROW_LEFT, GTK_SHADOW_NONE);
  gtk_container_add(GTK_CONTAINER(button), arrow);
  gtk_widget_show(arrow);
  g_signal_connect(G_OBJECT(button), "clicked",
               G_CALLBACK(olist_pref_left_button_clicked_cb), olist_entry);

  /* right button */
  button = gtk_button_new();
  olist_pref_win.right_button = button;
  gtk_table_attach_defaults(GTK_TABLE(table), button,
                      2, 3,
                      1, 2);
  gtk_widget_show(button);
  arrow = gtk_arrow_new(GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
  gtk_container_add(GTK_CONTAINER(button), arrow);
  gtk_widget_show(arrow);
  g_signal_connect(G_OBJECT(button), "clicked",
               G_CALLBACK(olist_pref_right_button_clicked_cb), olist_entry);

  /* right tree view */
  vbox = gtk_vbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(hbox), vbox,
                 TRUE, TRUE, 0);
  gtk_widget_show(vbox);

  label = gtk_label_new(_("Disabled"));
  gtk_box_pack_start(GTK_BOX(vbox), label,
                 FALSE, FALSE, 4);
  gtk_widget_show(label);

  scrwin = gtk_scrolled_window_new(NULL, NULL);
  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrwin),
                         GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
  gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrwin),
                              GTK_SHADOW_IN);
  gtk_box_pack_start(GTK_BOX(vbox), scrwin,
                 TRUE, TRUE, 0);
  gtk_widget_show(scrwin);

  store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
  tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
  olist_pref_win.tree_view[1] = tree_view;
  gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view), FALSE);
  selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
  gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
  g_signal_connect (G_OBJECT(selection), "changed",
                G_CALLBACK(olist_pref_selection_changed), olist_entry);
  gtk_container_add(GTK_CONTAINER(scrwin), tree_view);
  gtk_widget_show(tree_view);

  renderer = gtk_cell_renderer_text_new();
  column = gtk_tree_view_column_new_with_attributes(_("Disabled items"),
                                        renderer,
                                        "text", 0,
                                        NULL);
  gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);

  g_object_unref(store);

  /* set value */
  olist_pref_tree_view_set_value(olist_entry, TRUE, TRUE);
  set_olist_buttons_sensitive(olist_entry);

  /* show dialog */
  gtk_widget_show(dialog);
}

static void
update_custom_type_olist_cb(void *ptr, const char *custom_sym)
{
  sync_value_olist(GTK_ENTRY(ptr));
}

static void
add_custom_type_orderedlist(GtkWidget *vbox, struct uim_custom *custom)
{
  GtkWidget *hbox;
  GtkWidget *label, *entry, *button;

  hbox = gtk_hbox_new(FALSE, 8);
  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);

  label = gtk_label_new(custom->label);
  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);

  entry = gtk_entry_new();
  gtk_entry_set_editable(GTK_ENTRY(entry), FALSE);
  gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);

  button = gtk_button_new_with_label(_("Edit..."));
  gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);

  g_object_set_data_full(G_OBJECT(entry),
                   OBJECT_DATA_UIM_CUSTOM_SYM, g_strdup(custom->symbol),
                   (GDestroyNotify) g_free);

  sync_value_olist(GTK_ENTRY(entry));

  g_signal_connect(G_OBJECT(button), "clicked",
               G_CALLBACK(choose_olist_clicked_cb), entry);
  uim_custom_cb_add(custom->symbol, entry, update_custom_type_olist_cb);
}



static gboolean
key_pref_selection_changed(GtkTreeSelection *selection,
                     gpointer data)
{
  GtkTreeModel *model;
  GtkTreeIter iter;
  gboolean selected;

  selected = gtk_tree_selection_get_selected(selection, &model, &iter);

  gtk_widget_set_sensitive(key_pref_win.remove_button, selected);

  return TRUE;
}

static void
key_pref_set_value(guint keyval, GdkModifierType mod)
{
  GString *keystr;

  keystr = g_string_new("");
  /*
   * Ignore Shift modifier for printable char keys for
   * easy-to-recognize key configuration.  uim-custom performs
   * implicit shift key encoding/decoding appropriately.
   */
  if (((keyval >= 256) || !g_ascii_isgraph(keyval)) &&
              (mod & GDK_SHIFT_MASK))
    g_string_append(keystr, "<Shift>");
  if (mod & GDK_CONTROL_MASK)
    g_string_append(keystr, "<Control>");
  if (mod & GDK_MOD1_MASK)
    g_string_append(keystr, "<Alt>");

  switch (keyval) {
  case GDK_space:
    /*
     * "space" is not proper uim keysym and only exists for user
     * convenience. It is converted to " " by uim-custom
     */
    g_string_append(keystr, "space");
    break;
  case GDK_BackSpace:
    g_string_append(keystr, "backspace");
    break;
  case GDK_Delete:
    g_string_append(keystr, "delete");
    break;
  case GDK_Insert:
    g_string_append(keystr, "insert");
    break;
  case GDK_Escape:
    g_string_append(keystr, "escape");
    break;
  case GDK_Tab:
    g_string_append(keystr, "tab");
    break;
  case GDK_Return:
    g_string_append(keystr, "return");
    break;
  case GDK_Left:
    g_string_append(keystr, "left");
    break;
  case GDK_Up:
    g_string_append(keystr, "up");
    break;
  case GDK_Right:
    g_string_append(keystr, "right");
    break;
  case GDK_Down:
    g_string_append(keystr, "down");
    break;
  case GDK_Prior:
    g_string_append(keystr, "prior");
    break;
  case GDK_Next:
    g_string_append(keystr, "next");
    break;
  case GDK_Home:
    g_string_append(keystr, "home");
    break;
  case GDK_End:
    g_string_append(keystr, "end");
    break;
  case GDK_Kanji:
  case GDK_Zenkaku_Hankaku:
    g_string_append(keystr, "zenkaku-hankaku");
    break;
  case GDK_Multi_key:
    g_string_append(keystr, "Multi_key");
    break;
  case GDK_Mode_switch:
    g_string_append(keystr, "Mode_switch");
    break;
  case GDK_Henkan_Mode:
    g_string_append(keystr, "Henkan_Mode");
    break;
  case GDK_Muhenkan:
    g_string_append(keystr, "Muhenkan");
    break;
  case GDK_Shift_L:
  case GDK_Shift_R:
    g_string_append(keystr, "Shift_key");
    break;
  case GDK_Control_L:
  case GDK_Control_R:
    g_string_append(keystr, "Control_key");
    break;
  case GDK_Alt_L:
  case GDK_Alt_R:
    g_string_append(keystr, "Alt_key");
    break;
  case GDK_Meta_L:
  case GDK_Meta_R:
    g_string_append(keystr, "Meta_key");
    break;
  case GDK_Super_L:
  case GDK_Super_R:
    g_string_append(keystr, "Super_key");
    break;
  case GDK_Hyper_L:
  case GDK_Hyper_R:
    g_string_append(keystr, "Hyper_key");
    break;
  default:
    if (keyval >= GDK_F1 && keyval <= GDK_F35) {
      g_string_append_printf(keystr, "F%d", keyval - GDK_F1 + 1);
    } else if (keyval >= GDK_F1 && keyval <= GDK_F35) {
      g_string_append_printf(keystr, "%d", keyval - GDK_KP_0 + UKey_0);
    } else if (keyval < 256) {
      g_string_append_printf(keystr, "%c", keyval);
    } else {
      /* UKey_Other */
    }
    break;
  }

  gtk_entry_set_text(GTK_ENTRY(key_pref_win.keycode_entry), keystr->str);

  key_pref_win.grabbed_key_val   = 0;
  key_pref_win.grabbed_key_state = 0;

  g_string_free(keystr, TRUE);
}

static gboolean
grab_win_key_press_cb (GtkWidget *widget, GdkEventKey *event,
                   GtkEntry *key_entry)
{
  key_pref_win.grabbed_key_val   = event->keyval;
  key_pref_win.grabbed_key_state = event->state;

  return TRUE;
}

static gboolean
grab_win_key_release_cb (GtkWidget *widget, GdkEventKey *event,
                   GtkEntry *key_entry)
{
  key_pref_set_value(key_pref_win.grabbed_key_val,
                 key_pref_win.grabbed_key_state);

  g_signal_handlers_disconnect_by_func(G_OBJECT(widget),
                               (gpointer) grab_win_key_press_cb,
                               key_entry);
  g_signal_handlers_disconnect_by_func(G_OBJECT(widget),
                               (gpointer) grab_win_key_release_cb,
                               key_entry);

  gtk_dialog_response(GTK_DIALOG(widget), 0);

  return TRUE;
}

static gboolean
key_choose_entry_key_press_cb (GtkWidget *widget, GdkEventKey *event,
                         GtkEntry *key_entry)
{
  key_pref_set_value(event->keyval, event->state);
  return TRUE;
}

static void
choose_key_button_clicked_cb(GtkWidget *widget, GtkEntry *key_entry)
{
  GtkWidget *dialog;
  gint rv;

  dialog = gtk_message_dialog_new(GTK_WINDOW(gtk_widget_get_toplevel(widget)),
                          GTK_DIALOG_MODAL,
                          GTK_MESSAGE_INFO,
                          GTK_BUTTONS_CANCEL,
                          _("Press keys to grab (e.g. <Control>a)"));
  gtk_window_set_title(GTK_WINDOW(dialog), _("Grabbing keys"));
  g_signal_connect(G_OBJECT(dialog), "key-press-event",
               G_CALLBACK(grab_win_key_press_cb), key_entry);
  g_signal_connect(G_OBJECT(dialog), "key-release-event",
               G_CALLBACK(grab_win_key_release_cb), key_entry);

  gtk_widget_realize(dialog);
  gdk_keyboard_grab(GTK_WIDGET(dialog)->window,
                TRUE, GDK_CURRENT_TIME);


  rv = gtk_dialog_run(GTK_DIALOG(dialog));

  gtk_widget_destroy(dialog);
}

static void
sync_value_key(GtkEntry *entry)
{
  GString *str;
  const char *custom_sym;
  struct uim_custom *custom;
  struct uim_custom_key *key;
  gint i;

  custom_sym = g_object_get_data(G_OBJECT(entry), OBJECT_DATA_UIM_CUSTOM_SYM);
  g_return_if_fail(custom_sym);

  custom = uim_custom_get(custom_sym);
  g_return_if_fail(custom && custom->type == UCustom_Key);

  str = g_string_new("");

  if (custom->value->as_key) {
    for (key = custom->value->as_key[0], i = 0;
       key;
       key = custom->value->as_key[++i])
    {
      if (i != 0)
      g_string_append(str, ", ");
      g_string_append_printf(str, "\"%s\"", key->literal);
    }
  } else {
    /* error message */
  }

  gtk_entry_set_text(GTK_ENTRY(entry), str->str);
  gtk_widget_set_sensitive(GTK_WIDGET(entry)->parent, custom->is_active);

  g_string_free(str, TRUE);
  uim_custom_free(custom);
}

static void
key_pref_add_button_clicked_cb(GtkWidget *widget, GtkEntry *key_entry)
{
  const char *custom_sym;
  struct uim_custom *custom;
  const char *key_code;
  GString *str;
  GtkTreeModel *model;
  GtkTreeIter iter;
  gint num;
  uim_bool rv;

  g_return_if_fail(GTK_IS_ENTRY(key_entry));

  custom_sym = g_object_get_data(G_OBJECT(key_entry), OBJECT_DATA_UIM_CUSTOM_SYM);
  g_return_if_fail(custom_sym);

  custom = uim_custom_get(custom_sym);
  g_return_if_fail(custom && custom->type == UCustom_Key);

  key_code = gtk_entry_get_text(GTK_ENTRY(key_pref_win.keycode_entry));
  if (!key_code || !*key_code) {
    uim_custom_free(custom);
    return;
  }

  str = g_string_new("");

  g_string_append(str, key_code);

  for (num = 0; custom->value->as_key[num]; num++);
  num++;

  custom->value->as_key = realloc(custom->value->as_key,
                          sizeof(struct uim_custom_key *) * (num + 1));

  custom->value->as_key[num - 1] = malloc(sizeof(struct uim_custom_key));
  custom->value->as_key[num - 1]->type = UCustomKey_Regular;
  custom->value->as_key[num - 1]->editor_type = UCustomKeyEditor_Basic;
  custom->value->as_key[num - 1]->literal = strdup(str->str);
  custom->value->as_key[num - 1]->label = strdup("");
  custom->value->as_key[num - 1]->desc = strdup("");
  custom->value->as_key[num] = NULL;

  rv = uim_custom_set(custom);

  if (rv != UIM_FALSE) {
    uim_pref_gtk_value_changed = TRUE;
    model = gtk_tree_view_get_model(GTK_TREE_VIEW(key_pref_win.tree_view));
    gtk_list_store_append(GTK_LIST_STORE(model), &iter);
    gtk_list_store_set(GTK_LIST_STORE(model), &iter,
                   0, str->str,
                   -1);
    sync_value_key(key_entry);
  } else {
    g_printerr("Failed to set key value for \"%s\".\n", custom->symbol);
  }

  g_string_free(str, TRUE);
  uim_custom_free(custom);
}

static void
key_pref_remove_button_clicked_cb(GtkWidget *widget, GtkEntry *key_entry)
{
  const char *custom_sym;
  struct uim_custom *custom;
  GtkTreeSelection *selection;
  GtkTreeModel *model;
  GtkTreeIter iter;
  GtkTreePath *path;
  gboolean selected;
  gint num, *indices;
  uim_bool rv;

  g_return_if_fail(GTK_IS_ENTRY(key_entry));

  custom_sym = g_object_get_data(G_OBJECT(key_entry), OBJECT_DATA_UIM_CUSTOM_SYM);
  g_return_if_fail(custom_sym);

  custom = uim_custom_get(custom_sym);
  g_return_if_fail(custom && custom->type == UCustom_Key);

  selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(key_pref_win.tree_view));

  selected = gtk_tree_selection_get_selected(selection, &model, &iter);
  if (!selected) {
    uim_custom_free(custom);
    return;
  }

  for (num = 0; custom->value->as_key[num]; num++);

  path = gtk_tree_model_get_path(model, &iter);
  indices = gtk_tree_path_get_indices(path);

  if (num > 0 && *indices < num) {
    free(custom->value->as_key[*indices]->literal);
    free(custom->value->as_key[*indices]->label);
    free(custom->value->as_key[*indices]->desc);
    free(custom->value->as_key[*indices]);

    memmove(custom->value->as_key + *indices,
          custom->value->as_key + *indices + 1,
          sizeof(struct uim_custom_key *) * (num - *indices));
    custom->value->as_key = realloc(custom->value->as_key,
                            sizeof(struct uim_custom_key *) * num);

    rv = uim_custom_set(custom);

    if (rv != UIM_FALSE) {
      uim_pref_gtk_value_changed = TRUE;
      gtk_list_store_remove(GTK_LIST_STORE(model), &iter);

      sync_value_key(key_entry);
    } else {
      g_printerr("Failed to set key value for \"%s\".\n", custom->symbol);
    }
  } else {
    /* error */
  }

  gtk_tree_path_free(path);

  selected = gtk_tree_selection_get_selected(selection, &model, &iter);
  gtk_widget_set_sensitive(key_pref_win.remove_button, selected);

  uim_custom_free(custom);
}

static void
key_val_entry_changed_cb(GtkEntry *entry, GtkEntry *key_entry)
{
  const char *str = gtk_entry_get_text(entry);

  /* FIXME! validate key value */
  gtk_widget_set_sensitive(key_pref_win.add_button, str && *str);
}

static void
key_pref_dialog_response_cb(GtkDialog *dialog, gint action, GtkEntry *key_entry)
{
  gtk_widget_destroy(GTK_WIDGET(dialog));
}

static void
choose_key_clicked_cb(GtkWidget *widget, GtkEntry *key_entry)
{
  GtkWidget *dialog, *scrwin, *view, *hbox, *vbox, *label, *button, *entry;
  GtkTreeSelection *selection;
  GtkListStore *store;
  GtkCellRenderer *renderer;
  GtkTreeViewColumn *column;
  const char *custom_sym;
  struct uim_custom *custom;
  struct uim_custom_key *key;
  gint i;
  GtkIMContext *im_context;
  gchar title[256];

  g_return_if_fail(GTK_IS_ENTRY(key_entry));

  custom_sym = g_object_get_data(G_OBJECT(key_entry), OBJECT_DATA_UIM_CUSTOM_SYM);
  g_return_if_fail(custom_sym);

  custom = uim_custom_get(custom_sym);
  g_return_if_fail(custom && custom->type == UCustom_Key);

  g_snprintf(title, sizeof(title), _("%s - key configuration"), custom->label);

  /* setup key pref dialog */
  dialog = gtk_dialog_new_with_buttons(
    title,
    GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(key_entry))),
    GTK_DIALOG_MODAL,
    GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
    NULL);
  key_pref_win.window = dialog;
  gtk_window_set_default_size(GTK_WINDOW(dialog), 280, 220);
  g_signal_connect(G_OBJECT(dialog), "response",
               G_CALLBACK(key_pref_dialog_response_cb), key_entry);

  hbox = gtk_hbox_new(FALSE, 0);
  gtk_container_set_border_width(GTK_CONTAINER(hbox), 4);
  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox,
                 TRUE, TRUE, 0);
  gtk_widget_show(hbox);

  scrwin = gtk_scrolled_window_new(NULL, NULL);
  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrwin),
                         GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
  gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrwin), GTK_SHADOW_IN);
  gtk_box_pack_start(GTK_BOX(hbox), scrwin,
                 TRUE, TRUE, 0);
  gtk_widget_show(scrwin);

  store = gtk_list_store_new(1, G_TYPE_STRING);
  view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
  key_pref_win.tree_view = view;
  gtk_container_add(GTK_CONTAINER(scrwin), view);
  gtk_widget_show(view);

  gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE);

  renderer = gtk_cell_renderer_text_new();
  column = gtk_tree_view_column_new_with_attributes(_("Key preference"),
                                        renderer,
                                        "text", 0,
                                        NULL);
  gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);

  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
  gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
  g_signal_connect (G_OBJECT(selection), "changed",
                G_CALLBACK(key_pref_selection_changed), key_entry);

  vbox = gtk_vbox_new(TRUE, 0);
  gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 4);
  gtk_widget_show(vbox);

#if 0
  button = gtk_button_new_from_stock(GTK_STOCK_GO_UP);
  gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 2);
  gtk_widget_show(button);

  button = gtk_button_new_from_stock(GTK_STOCK_GO_DOWN);
  gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 2);
  gtk_widget_show(button);
#endif

  button = gtk_button_new_from_stock(GTK_STOCK_ADD);
  key_pref_win.add_button = button;
  gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 2);
  g_signal_connect(G_OBJECT(button), "clicked",
               G_CALLBACK(key_pref_add_button_clicked_cb), key_entry);
  gtk_widget_set_sensitive(button, FALSE);
  gtk_widget_show(button);

  button = gtk_button_new_from_stock(GTK_STOCK_REMOVE);
  key_pref_win.remove_button = button;
  gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 2);
  g_signal_connect(G_OBJECT(button), "clicked",
               G_CALLBACK(key_pref_remove_button_clicked_cb), key_entry);
  gtk_widget_set_sensitive(button, FALSE);
  gtk_widget_show(button);

  hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, FALSE, FALSE, 0);
  gtk_container_set_border_width(GTK_CONTAINER(hbox), 4);
  gtk_widget_show(hbox);

  label = gtk_label_new(_("Key:"));
  gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 4);
  gtk_widget_show(label);

  entry = gtk_entry_new();

  /* XXX hack alert!  This modifies private part of gtk_entry */
  im_context = gtk_im_context_simple_new();
  g_object_unref(GTK_ENTRY(entry)->im_context);
  GTK_ENTRY(entry)->im_context = im_context;

  gtk_entry_set_editable(GTK_ENTRY(entry), FALSE);
  key_pref_win.keycode_entry = entry;
  gtk_widget_set_size_request(GTK_WIDGET(entry), 100, -1);
  gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 4);
  g_signal_connect(G_OBJECT(entry), "changed",
               G_CALLBACK(key_val_entry_changed_cb), key_entry);
  g_signal_connect(G_OBJECT(entry), "key-press-event",
               G_CALLBACK(key_choose_entry_key_press_cb), key_entry);
  gtk_widget_show(entry);

  button = gtk_button_new_with_label(_("Grab..."));
  gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 4);
  g_signal_connect(G_OBJECT(button), "clicked",
               G_CALLBACK(choose_key_button_clicked_cb), key_entry);
  gtk_widget_show(button);

  /* set value */
  if (custom->value->as_key) {
    for (key = custom->value->as_key[0], i = 0;
       key;
       key = custom->value->as_key[++i])
    {
      GtkTreeIter iter;
      gtk_list_store_append(store, &iter);
      gtk_list_store_set(store, &iter,
                   0, key->literal,
                   -1);
    }
  } else {
    /* error message */
  }

  /* show dialog */
  gtk_widget_show(dialog);

  /* clean */
  g_object_unref(G_OBJECT(store));
  uim_custom_free(custom);
}

static void
update_custom_type_key_cb(void *ptr, const char *custom_sym)
{
  sync_value_key(GTK_ENTRY(ptr));
}

static void
add_custom_type_key(GtkWidget *vbox, struct uim_custom *custom)
{
  GtkWidget *hbox;
  GtkWidget *label, *entry, *button;

  hbox = gtk_hbox_new(FALSE, 8);
  label = gtk_label_new(custom->label);
  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);

  entry = gtk_entry_new();
  gtk_entry_set_editable(GTK_ENTRY(entry), FALSE);
  g_object_set_data_full(G_OBJECT(entry),
                   OBJECT_DATA_UIM_CUSTOM_SYM, g_strdup(custom->symbol),
                   (GDestroyNotify) g_free);
  gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);

  sync_value_key(GTK_ENTRY(entry));

  button = gtk_button_new_with_label(_("Edit..."));
  gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
  g_signal_connect(G_OBJECT(button), "clicked",
               G_CALLBACK(choose_key_clicked_cb), entry);
  uim_custom_cb_add(custom->symbol, entry, update_custom_type_key_cb);
  
  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
}



void
uim_pref_gtk_add_custom(GtkWidget *vbox, const char *custom_sym)
{
  struct uim_custom *custom;
  custom = uim_custom_get(custom_sym);

  if (custom) {
    /* Should be rewritten with table. */
    switch (custom->type) {
    case UCustom_Bool:
      add_custom_type_bool(vbox, custom);
      break;
    case UCustom_Int:
      add_custom_type_integer(vbox, custom);
      break;
    case UCustom_Str:
      add_custom_type_string(vbox, custom);
      break;
    case UCustom_Pathname:
      add_custom_type_pathname(vbox, custom);
      break;
    case UCustom_Choice:
      add_custom_type_choice(vbox, custom);
      break;
    case UCustom_OrderedList:
      add_custom_type_orderedlist(vbox, custom);
      break;
    case UCustom_Key:
      add_custom_type_key(vbox, custom);
      break;
    default:
      g_printerr("Invalid custom type: %d\n", custom->type);
      break;
    }
  } else {
    g_printerr("Failed to get uim_custom object for %s.\n", custom_sym);
  }

  uim_custom_free(custom);
}

void
uim_pref_gtk_set_default_value(GtkWidget *widget)
{
  const char *custom_sym;
  struct uim_custom *custom;
  union uim_custom_value *value, *defval;
  uim_bool rv;
  gint i, num;
  int type;

  custom_sym = g_object_get_data(G_OBJECT(widget), OBJECT_DATA_UIM_CUSTOM_SYM);
  if (!custom_sym) return;

  custom = uim_custom_get(custom_sym);
  g_return_if_fail(custom);

  value = custom->value;
  defval = custom->default_value;
  type = custom->type;

  switch (type) {
  case UCustom_Bool:
    value->as_bool = defval->as_bool;
    break;
  case UCustom_Int:
    value->as_int = defval->as_int;
    break;
  case UCustom_Str:
    free(value->as_str);
    value->as_str = strdup(defval->as_str);
    break;
  case UCustom_Pathname:
    free(value->as_pathname);
    value->as_pathname = strdup(defval->as_pathname);
    break;
  case UCustom_Choice:
    free(value->as_choice->symbol);
    free(value->as_choice->label);
    free(value->as_choice->desc);
    value->as_choice->symbol = strdup(defval->as_choice->symbol);
    value->as_choice->label  = strdup(defval->as_choice->label);
    value->as_choice->desc   = strdup(defval->as_choice->desc);
    break;
  case UCustom_OrderedList:
    for (num = 0; defval->as_olist[num]; num++);
    for (i = 0; value->as_olist[i]; i++) {
      free(value->as_olist[i]->symbol);
      free(value->as_olist[i]->label);
      free(value->as_olist[i]->desc);
      free(value->as_olist[i]);
    }
    value->as_olist = realloc(value->as_olist,
                        sizeof(struct uim_custom_choice *) * (num + 1));
    for (i = 0; i < num; i++) {
      value->as_olist[i] = malloc(sizeof(struct uim_custom_choice));
      value->as_olist[i]->symbol = strdup(defval->as_olist[i]->symbol);
      value->as_olist[i]->label  = strdup(defval->as_olist[i]->label);
      value->as_olist[i]->desc   = strdup(defval->as_olist[i]->desc);
    }
    value->as_olist[num] = NULL;
    break;
  case UCustom_Key:
    for (num = 0; defval->as_key[num]; num++);
    for (i = 0; value->as_key[i]; i++) {
      free(value->as_key[i]->literal);
      free(value->as_key[i]->label);
      free(value->as_key[i]->desc);
      free(value->as_key[i]);
    }
    value->as_key = realloc(value->as_key,
                      sizeof(struct uim_custom_key *) * (num + 1));
    for (i = 0; i < num; i++) {
      value->as_key[i] = malloc(sizeof(struct uim_custom_key));
      *value->as_key[i] = *defval->as_key[i];
      value->as_key[i]->literal = strdup(defval->as_key[i]->literal);
      value->as_key[i]->label   = strdup(defval->as_key[i]->label);
      value->as_key[i]->desc    = strdup(defval->as_key[i]->desc);
    }
    value->as_key[num] = NULL;
    break;
  default:
    uim_custom_free(custom);
    return;
  }

  rv = uim_custom_set(custom);
  if (rv == UIM_FALSE) {
    g_printerr("Failed to set value for \"%s\".\n", custom->symbol);
    uim_custom_free(custom);
    return;
  }
  uim_custom_free(custom);

  switch (type) {
  case UCustom_Bool:
    sync_value_bool(GTK_CHECK_BUTTON(widget));
    break;
  case UCustom_Int:
    sync_value_int(GTK_SPIN_BUTTON(widget));
    break;
  case UCustom_Str:
    sync_value_string(GTK_ENTRY(widget));
    break;
  case UCustom_Pathname:
    sync_value_string(GTK_ENTRY(widget));
    break;
  case UCustom_Choice:
    sync_value_choice(GTK_COMBO_BOX(widget));
    break;
  case UCustom_OrderedList:
    sync_value_olist(GTK_ENTRY(widget));
    break;
  case UCustom_Key:
    sync_value_key(GTK_ENTRY(widget));
    break;
  default:
    break;
  }
  return;
}

Generated by  Doxygen 1.6.0   Back to index