/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
#include <stdio.h>
#include <string.h>
#include <X11/Xmd.h>
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include <gdk/gdkkeysyms.h>
#include <canna/jrkanji.h>
#include <canna/mfdef.h>
#include "SunIM.h"
#include "xaux_common.h"
#include "xaux_ext_common.h"
#include "iiimpAux.h"
#include "cannadef.h"

#define	FLABEL_FORMAT	"%s [%d/%d]"
#define DATA_FATTACHED	"auxmenu-frame-attached"
#define	DATA_ATTACHED	"auxmenu-attached"
#define DATA_ENTRY	"auxmenu-entry"
#define	DATA_TREEVIEW	"auxmenu-treeview"
#define	DATA_FRAME	"auxmenu-frame"
#define	DATA_LABEL	"auxmenu-label"
#define	DATA_FTITLE	"auxmenu-frame-title"
#define	DATA_BEGIN_ITEM	"auxmenu-begin-of-item"
#define	DATA_CUR_ITEM	"auxmenu-current-item"
#define	DATA_END_ITEM	"auxmenu-end-of-item"
#define	DATA_PAGE_MAX	"auxmenu-page-max-item"

#ifdef DEBUG
#define	ENTER	fprintf(stderr, "%s: enter.\n", __FUNCTION__)
#define	LEAVE	fprintf(stderr, "%s: leave.\n", __FUNCTION__)
#else
#define	ENTER
#define	LEAVE
#endif

xaux_class_t xaux_class = {
    .classname = "com.OpenI18N.leif.CannaLE.menu",
    .index = 0,
    .extexec = XAUX_EXT_DIR "auxmenu",
    .sowin = (Window)0,
    .clientwin = (Window)0,
    .extwin = (Window)0,
    .atom_classname = (Atom)0,
    .atom_clientwin = (Atom)0,
    .atom_sowin = (Atom)0,
    .atom_extwin = (Atom)0,
    .atom_sx = { (Atom)0 },
    .atom_sx_num = 32,
    .atom_sx_idx = 0,
    .atom_xs = { (Atom)0 },
    .atom_xs_num = 32,
    .atom_xs_idx = 0,
    .utfname = NULL,
};
GtkWidget *auxwindow;

static GdkEvent *
translate_key_event(int keycode, int modifier, gboolean press)
{
    GdkKeymap *keymap = gdk_keymap_get_for_display(gtk_widget_get_display(auxwindow));
    GdkKeymapKey *keys;
    GdkEvent *event = gdk_event_new((press ? GDK_KEY_PRESS : GDK_KEY_RELEASE));
    gint n_keys;

    event->key.type = (press ? GDK_KEY_PRESS : GDK_KEY_RELEASE);
    event->key.state = 0;
    if (modifier & IM_SHIFT_MASK) {
	event->key.state |= GDK_SHIFT_MASK;
    }
    if (modifier & IM_CTRL_MASK) {
	event->key.state |= GDK_CONTROL_MASK;
    }
    if (modifier & IM_ALT_MASK) {
	event->key.state |= GDK_MOD1_MASK;
    }
    switch (keycode) {
	case IM_VK_BACK_SPACE:
	    event->key.keyval = GDK_BackSpace;
	    break;
	case IM_VK_UP:
	    event->key.keyval = GDK_Up;
	    break;
	case IM_VK_DOWN:
	    event->key.keyval = GDK_Down;
	    break;
	case IM_VK_LEFT:
	    event->key.keyval = GDK_Left;
	    break;
	case IM_VK_RIGHT:
	    event->key.keyval = GDK_Right;
	    break;
	case IM_VK_PAGE_UP:
	    event->key.keyval = GDK_Page_Up;
	    break;
	case IM_VK_PAGE_DOWN:
	    event->key.keyval = GDK_Page_Down;
	    break;
	case IM_VK_CLEAR:
	    event->key.keyval = GDK_Clear;
	    break;
	case IM_VK_SPACE:
	    event->key.keyval = GDK_space;
	    break;
	case IM_VK_END:
	    event->key.keyval = GDK_End;
	    break;
	case IM_VK_HOME:
	    event->key.keyval = GDK_Home;
	    break;
	case IM_VK_COMMA:
	    event->key.keyval = GDK_comma;
	    break;
	case IM_VK_MINUS:
	    event->key.keyval = GDK_minus;
	    break;
	case IM_VK_PERIOD:
	    event->key.keyval = GDK_period;
	    break;
	case IM_VK_SLASH:
	    event->key.keyval = GDK_slash;
	    break;
	case IM_VK_SEMICOLON:
	    event->key.keyval = GDK_semicolon;
	    break;
	case IM_VK_EQUALS:
	    event->key.keyval = GDK_equal;
	    break;
	case IM_VK_OPEN_BRACKET:
	    event->key.keyval = GDK_bracketleft;
	    break;
	case IM_VK_BACK_SLASH:
	    event->key.keyval = GDK_backslash;
	    break;
	case IM_VK_CLOSE_BRACKET:
	    event->key.keyval = GDK_bracketright;
	    break;
	case IM_VK_MULTIPLY:
	    event->key.keyval = GDK_KP_Multiply;
	    break;
	case IM_VK_ADD:
	    event->key.keyval = GDK_KP_Add;
	    break;
	case IM_VK_SEPARATER:
	    event->key.keyval = GDK_KP_Separator;
	    break;
	case IM_VK_SUBTRACT:
	    event->key.keyval = GDK_KP_Subtract;
	    break;
	case IM_VK_DECIMAL:
	    event->key.keyval = GDK_KP_Decimal;
	    break;
	case IM_VK_DIVIDE:
	    event->key.keyval = GDK_KP_Divide;
	    break;
	case IM_VK_DELETE:
	    event->key.keyval = GDK_Delete;
	    break;
	default:
	    if (keycode >= IM_VK_0 && keycode <= IM_VK_9) {
		event->key.keyval = GDK_0 + keycode - IM_VK_0;
	    } else if (keycode >= IM_VK_A && keycode <= IM_VK_Z) {
		event->key.keyval = GDK_a + keycode - IM_VK_A;
	    } else if (keycode >= IM_VK_NUMPAD0 && keycode <= IM_VK_NUMPAD9) {
		event->key.keyval = GDK_KP_0 + keycode - IM_VK_NUMPAD0;
	    }
	    break;
    }
    if (event->key.keyval != 0) {
	gdk_keymap_get_entries_for_keyval(keymap, event->key.keyval, &keys, &n_keys);
	if (n_keys) {
	    event->key.hardware_keycode = keys[0].keycode;

	    g_free(keys);
	}
    }

    return event;
}

static void
change_focus(GtkWidget *widget, int focus)
{
    GdkEvent *event;

    if (widget != NULL) {
	g_object_ref(widget);
	event = gdk_event_new(GDK_FOCUS_CHANGE);
	GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
	event->focus_change.type = GDK_FOCUS_CHANGE;
	event->focus_change.window = widget->window;
	if (widget->window)
	    g_object_ref(widget->window);
	if (focus == 0) {
	    /* unfocus */
	    event->focus_change.in = FALSE;
	} else {
	    /* focus */
	    event->focus_change.in = TRUE;
	}
	gtk_widget_event(widget, event);
	g_object_notify(G_OBJECT(widget), "has_focus");
	g_object_unref(widget);
	gdk_event_free(event);
    }
}

static void
send_key_event(int im,
	       int ic,
	       xaux_class_t *xc,
	       int keycode,
	       int modifier,
	       int translated)
{
    aux_ext_data_t data;
    int integer_list[INT_END];

    integer_list[INT_AUXMODE] = AUX_UPDATE_KEY;
    integer_list[INT_KEYCODE] = keycode;
    integer_list[INT_KEYMOD] = modifier;
    integer_list[INT_TRANSLATEDCODE] = translated;

    DEBUG_printf("keycode = %d\n", keycode);
    data.im = im;
    data.ic = ic;
    data.integer_count = INT_END;
    data.integer_list = integer_list;
    data.string_count = 0;
    data.string_list = NULL;
    data.string_ptr = NULL;
    data.point.x = 0;
    data.point.y = 0;

    if (xc != NULL)
	xaux_ext_SetValue(GDK_DISPLAY(), xc, &data);
    XFlush(GDK_DISPLAY());
}

static void
send_aux_event(int im,
	       int ic,
	       xaux_class_t *xc,
	       int auxmode)
{
    aux_ext_data_t data;
    int integer_list[INT_END];

    integer_list[INT_AUXMODE] = auxmode;

    data.im = im;
    data.ic = ic;
    data.integer_count = INT_END;
    data.integer_list = integer_list;
    data.string_count = 0;
    data.string_list = NULL;
    data.string_ptr = NULL;
    data.point.x = 0;
    data.point.y = 0;

    if (xc != NULL)
	xaux_ext_SetValue(GDK_DISPLAY(), xc, &data);
    XFlush(GDK_DISPLAY());
}

static void
set_position(GtkWidget *widget)
{
    GtkRequisition requisition;
    GdkScreen *screen, *pointer_screen;
    gint x, y;

    ENTER;

    screen = gtk_widget_get_screen(widget);
    gdk_display_get_pointer(gdk_screen_get_display(screen),
			    &pointer_screen, &x, &y, NULL);

    gtk_widget_size_request(widget, &requisition);
    if (pointer_screen != screen) {
	/* Pointer is on a different screen; roughly center the
	 * menu on the screen. If someone was using multiscreen
	 * + Xinerama together they'd probably want something
	 * fancier; but that is likely to be vanishingly rare.
	 */
	x = MAX(0, (gdk_screen_get_width(screen) - requisition.width) / 2);
	y = MAX(0, (gdk_screen_get_height(screen) - requisition.height) / 2);
    }

    gtk_window_move(GTK_WINDOW(widget), x, y);

    LEAVE;
}

static gboolean
update_entry_count(guint current)
{
    GtkWidget *frame = g_object_get_data(G_OBJECT(auxwindow), DATA_FRAME);
    GtkWidget *label;
    gchar *str;
    gchar *title = g_object_get_data(G_OBJECT(auxwindow), DATA_FTITLE);
    guint beginitem = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(auxwindow), DATA_BEGIN_ITEM));
    guint enditem = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(auxwindow), DATA_END_ITEM));

    if (current > enditem)
	return FALSE;

    str = g_strdup_printf(FLABEL_FORMAT, title, current + beginitem, enditem);
    label = gtk_frame_get_label_widget(GTK_FRAME(frame));
    gtk_label_set_text(GTK_LABEL(label), str);
    g_free(str);

    return TRUE;
}

static void
update_menu(GtkWidget              *widget,
	    int                     count,
	    const aux_ext_string_t *string_list)
{
    GtkListStore *store;
    GtkTreeIter iter;
    GtkCellRenderer *renderer;
    GtkTreeViewColumn *column;
    GtkTreePath *path;
    GdkRectangle rect;
    GtkRequisition requisition;
    gint width, height;
    int i;
    gchar *item;

    g_object_set_data(G_OBJECT(auxwindow), DATA_PAGE_MAX, GUINT_TO_POINTER(count / 2));

    /* remove the old columns first */
    while (1) {
	column = gtk_tree_view_get_column(GTK_TREE_VIEW(widget), 0);
	if (column != NULL)
	    gtk_tree_view_remove_column(GTK_TREE_VIEW(widget), column);
	else
	    break;
    }

    /* create new columns */
    store = gtk_list_store_new(2, G_TYPE_UINT, G_TYPE_STRING);
    for (i = 1; i < count; i += 2) {
	gtk_list_store_append(store, &iter);
	string_list[i].ptr[string_list[i].length] = 0;
	string_list[i].ptr[string_list[i].length+1] = 0;
	item = g_utf16_to_utf8((gunichar2 *)string_list[i].ptr, -1, NULL, NULL, NULL);

	DEBUG_printf("*** %d: item = %s (%d)\n", i / 2 + 1, item, string_list[i].length);

	gtk_list_store_set(store, &iter, 0, i / 2 + 1, 1, item, -1);
	g_free(item);
    }
    gtk_tree_view_set_model(GTK_TREE_VIEW(widget), GTK_TREE_MODEL(store));

    for (i = 0; i < 2; i++) {
	renderer = gtk_cell_renderer_text_new();
	column = gtk_tree_view_column_new_with_attributes("", renderer, "text", i, NULL);
	gtk_tree_view_column_set_resizable(column, TRUE);
	gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
	gtk_tree_view_append_column(GTK_TREE_VIEW(widget), column);
    }

    g_object_unref(G_OBJECT(store));

    /* enforce to select first item */
    path = gtk_tree_path_new_first();
    column = gtk_tree_view_get_column(GTK_TREE_VIEW(widget), 0);
    gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), path, column, FALSE);
    gtk_tree_path_free(path);

    /* recalculate the window size */
    gdk_window_get_frame_extents(auxwindow->window, &rect);
    gtk_widget_size_request(auxwindow, &requisition);

    if (requisition.width < rect.width || requisition.height < rect.height)
	gtk_window_resize(GTK_WINDOW(auxwindow), requisition.width, requisition.height);
}

static void
update_dictmenu(GtkWidget              *widget,
		int                     count,
		const aux_ext_string_t *string_list)
{
    GtkListStore *store;
    GtkTreeIter iter;
    GtkCellRenderer *renderer;
    GtkTreeViewColumn *column;
    GtkTreePath *path;
    GdkRectangle rect;
    GtkRequisition requisition;
    gint width, height;
    int i;
    guchar *item, *fitem;
    gboolean flag;

    g_object_set_data(G_OBJECT(auxwindow), DATA_PAGE_MAX, GUINT_TO_POINTER(count / 2));

    /* remove the old columns first */
    while (1) {
	column = gtk_tree_view_get_column(GTK_TREE_VIEW(widget), 0);
	if (column != NULL)
	    gtk_tree_view_remove_column(GTK_TREE_VIEW(widget), column);
	else
	    break;
    }

    /* create new columns */
    store = gtk_list_store_new(3, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_BOOLEAN);
    for (i = 0; i < count; i += 2) {
	gtk_list_store_append(store, &iter);
	string_list[i].ptr[string_list[i].length] = 0;
	string_list[i].ptr[string_list[i].length+1] = 0;
	string_list[i+1].ptr[string_list[i+1].length] = 0;
	string_list[i+1].ptr[string_list[i+1].length+1] = 0;
	fitem = g_utf16_to_utf8((gunichar2 *)string_list[i].ptr, -1, NULL, NULL, NULL);
	item = g_utf16_to_utf8((gunichar2 *)string_list[i+1].ptr, -1, NULL, NULL, NULL);
	if (fitem[0] == 0xe2 && fitem[1] == 0x97 && fitem[2] == 0x8e)
	    flag = TRUE;
	else
	    flag = FALSE;
	gtk_list_store_set(store, &iter, 0, flag, 1, item, 2, FALSE, -1);
	g_free(item);
    }
    gtk_tree_view_set_model(GTK_TREE_VIEW(widget), GTK_TREE_MODEL(store));

    /* column 0 */
    renderer = gtk_cell_renderer_toggle_new();
    column = gtk_tree_view_column_new_with_attributes("", renderer, "active", 0, NULL);
    gtk_tree_view_column_set_resizable(column, TRUE);
    gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
    gtk_tree_view_append_column(GTK_TREE_VIEW(widget), column);
    /* column 1 */
    renderer = gtk_cell_renderer_text_new();
    column = gtk_tree_view_column_new_with_attributes("", renderer, "text", 1, NULL);
    gtk_tree_view_column_set_resizable(column, TRUE);
    gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
    gtk_tree_view_append_column(GTK_TREE_VIEW(widget), column);

    g_object_unref(G_OBJECT(store));

    /* enforce to select first item */
    path = gtk_tree_path_new_first();
    column = gtk_tree_view_get_column(GTK_TREE_VIEW(widget), 0);
    gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), path, column, FALSE);
    gtk_tree_path_free(path);

    /* recalculate the window size */
    gdk_window_get_frame_extents(auxwindow->window, &rect);
    gtk_widget_size_request(auxwindow, &requisition);

    if (requisition.width < rect.width || requisition.height < rect.height)
	gtk_window_resize(GTK_WINDOW(auxwindow), requisition.width, requisition.height);
}

static void
move_menu_position(gint pos)
{
    GtkWidget *widget = g_object_get_data(G_OBJECT(auxwindow), DATA_TREEVIEW);
    GtkTreePath *path;
    GtkTreeViewColumn *column;
    guint offset = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(auxwindow), DATA_BEGIN_ITEM));

    g_return_if_fail((pos - offset) >= 0);

    path = gtk_tree_path_new_from_indices(pos - offset, -1);
    column = gtk_tree_view_get_column(GTK_TREE_VIEW(widget), 0);
    gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), path, column, FALSE);
    gtk_tree_path_free(path);
}

static void
update_menu_position(int           keycode,
		     int           modifier,
		     int           translated,
		     int           im,
		     int           ic,
		     xaux_class_t *xc,
		     gboolean      numericalkey)
{
    GtkWidget *widget = g_object_get_data(G_OBJECT(auxwindow), DATA_TREEVIEW);
    GtkTreeViewColumn *column;
    GtkTreePath *path = NULL;
    GtkTreeModel *model;
    GtkTreeIter iter;
    GtkTreeSelection *sel;
    gint num = 0, beginitem, enditem, pagemax, idx, i;

    pagemax = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(auxwindow), DATA_PAGE_MAX));
    beginitem = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(auxwindow), DATA_BEGIN_ITEM));
    enditem = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(auxwindow), DATA_END_ITEM));

    model = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
    sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
    switch (keycode) {
	case IM_VK_UP:
	    if(gtk_tree_selection_get_selected(sel, NULL, &iter)) {
		path = gtk_tree_model_get_path(model, &iter);
		idx = gtk_tree_path_get_indices(path)[0];
		if (idx == 0) {
		    if (beginitem > 1) {
			/* back to the previous page. */
			send_key_event(im, ic, xc, IM_VK_LEFT, 0, CANNA_KEY_Left);
			return;
		    }
		}
		if (gtk_tree_path_prev(path)) {
		    column = gtk_tree_view_get_column(GTK_TREE_VIEW(widget), 0);
		    gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), path, column, FALSE);
		}
	    } else {
		path = gtk_tree_path_new_first();
		column = gtk_tree_view_get_column(GTK_TREE_VIEW(widget), 0);
		gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), path, column, FALSE);
	    }
	    idx = gtk_tree_path_get_indices(path)[0];
	    update_entry_count(idx);
	    gtk_tree_path_free(path);
	    break;
	case IM_VK_DOWN:
	    if (gtk_tree_selection_get_selected(sel, NULL, &iter)) {
		path = gtk_tree_model_get_path(model, &iter);
		idx = gtk_tree_path_get_indices(path)[0];
		if ((idx + 1) >= pagemax) {
		    if (enditem >= (beginitem + pagemax)) {
			/*
			 * current position in the server should be the begin of item.
			 * so just sending Rollup key should works.
			 */
			send_key_event(im, ic, xc, IM_VK_PAGE_DOWN, 0, CANNA_KEY_Rollup);
		    }
		    return;
		}
		gtk_tree_path_next(path);
	    } else {
		path = gtk_tree_path_new_first();
	    }
	    column = gtk_tree_view_get_column(GTK_TREE_VIEW(widget), 0);
	    gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), path, column, FALSE);
	    idx = gtk_tree_path_get_indices(path)[0];
	    update_entry_count(idx);
	    gtk_tree_path_free(path);
	    break;
	case IM_VK_ENTER:
	    if (gtk_tree_selection_get_selected(sel, NULL, &iter)) {
		model = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
		gtk_tree_model_get(model, &iter, 0, &num, -1);
		if (num >= 0 && num <= 9) {
		    if (numericalkey) {
			send_key_event(im, ic, xc, IM_VK_0 + num, 0, IM_VK_0 + num);
		    } else {
			/* use cursor key and enter key instead of numerical shortcut key */
			for (i = 0; i < num - 1; i++) {
			    send_aux_event(im, ic, xc, AUX_FORCE_NOT_UPDATE_STATE);
			    send_key_event(im, ic, xc, IM_VK_RIGHT, 0, CANNA_KEY_Right);
			}
			send_key_event(im, ic, xc, keycode, 0, translated);
		    }
		}
	    }
	    break;
	case IM_VK_BACK_SPACE:
	    send_key_event(im, ic, xc, keycode, 0, translated);
	    break;
	default:
	    if (modifier & IM_CTRL_MASK && keycode == IM_VK_G) {
		/* cancel */
		send_key_event(im, ic, xc, IM_VK_G, modifier, '');
	    } else if (keycode >= IM_VK_0 && keycode <= IM_VK_9) {
		if (numericalkey) {
		    send_key_event(im, ic, xc, keycode, 0, '0' + keycode - IM_VK_0);
		} else {
		    /* use cursor key and enter key instead of numerical shortcut key */
		    for (i = 0; i < (keycode - IM_VK_0) - 1; i++) {
			send_key_event(im, ic, xc, IM_VK_RIGHT, 0, CANNA_KEY_Right);
		    }
		    send_key_event(im, ic, xc, IM_VK_ENTER, 0, 0x0D);
		}
	    }
	    break;
    }
}

static void
update_codeinput_position(int           keycode,
			  int           modifier,
			  int           translated,
			  int           im,
			  int           ic,
			  xaux_class_t *xc)
{
    GtkWidget *widget = g_object_get_data(G_OBJECT(auxwindow), DATA_ENTRY);
    GdkEvent *eventp, *eventr;
    GString *str;
    int i;

    eventp = translate_key_event(keycode, modifier, TRUE);
    eventr = translate_key_event(keycode, modifier, FALSE);

    str = g_string_new(gtk_entry_get_text(GTK_ENTRY(widget)));
    if ((keycode == IM_VK_BACK_SPACE && str->len == 0) ||
	keycode == IM_VK_ESCAPE ||
	(keycode == IM_VK_G && modifier & IM_CTRL_MASK)) {
	/* cancel */
	send_key_event(im, ic, xc, IM_VK_G, IM_CTRL_MASK, '');
    } else if (keycode == IM_VK_ENTER) {
	for (i = 0; i < str->len; i++) {
	    send_key_event(im, ic, xc, str->str[i], 0, str->str[i]);
	}
	send_key_event(im, ic, xc, keycode, 0, translated);
    } else if (eventp->key.keyval == 0) {
	/* nothing here */
    } else if (keycode >= IM_VK_G && keycode <= IM_VK_Z && ((modifier | IM_SHIFT_MASK) ^ IM_SHIFT_MASK) == 0) {
	/* nothing here */
    } else {
	/* send pseudo key event to the widget */
	eventp->key.window = eventr->key.window = widget->window;
	if (widget->window) {
	    g_object_ref(widget->window);
	    g_object_ref(widget->window);
	}
	/* prefers capital letter for code input */
	if (keycode >= IM_VK_A && keycode <= IM_VK_F) {
	    eventp->key.keyval = eventr->key.keyval = GDK_A + eventp->key.keyval - GDK_a;
	}
	gtk_widget_event(widget, eventp);
	gtk_widget_event(widget, eventr);
    }

    gdk_event_free(eventp);
    gdk_event_free(eventr);
    g_string_free(str, TRUE);
}

static void
update_dictmenu_state(int           im,
		      int           ic,
		      xaux_class_t *xc,
		      int           direction)
{
    GtkWidget *widget = g_object_get_data(G_OBJECT(auxwindow), DATA_TREEVIEW);
    GtkTreePath *path;
    GtkTreeModel *model;
    GtkTreeIter iter;
    guint pagemax = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(auxwindow), DATA_PAGE_MAX));
    gboolean changed = FALSE;
    int i;

    path = gtk_tree_path_new_first();
    model = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
    while (gtk_tree_path_get_indices(path)[0] < pagemax) {
	if (gtk_tree_model_get_iter(model, &iter, path)) {
	    gtk_tree_model_get(model, &iter, 2, &changed, -1);
	    if (changed) {
		send_key_event(im, ic, xc, IM_VK_SPACE, 0, IM_VK_SPACE);
	    }
	    send_key_event(im, ic, xc, IM_VK_RIGHT, 0, CANNA_KEY_Right);
	}
	gtk_tree_path_next(path);
    }
    gtk_tree_path_free(path);

    if (direction < 0) {
	for (i = 0; i <= pagemax; i++)
	    send_key_event(im, ic, xc, IM_VK_LEFT, 0, CANNA_KEY_Left);
    }
    send_aux_event(im, ic, xc, AUX_FORCE_UPDATE_STATE);
}

static void
update_dictmenu_position(int           keycode,
			 int           modifier,
			 int           translated,
			 int           im,
			 int           ic,
			 xaux_class_t *xc)
{
    GtkWidget *widget = g_object_get_data(G_OBJECT(auxwindow), DATA_TREEVIEW);
    GtkTreeViewColumn *column;
    GtkTreePath *path;
    GtkTreeModel *model;
    GtkTreeIter iter;
    GtkTreeSelection *sel;
    int beginitem, enditem, pagemax, idx;
    gboolean use = FALSE, changed = FALSE;

    pagemax = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(auxwindow), DATA_PAGE_MAX));
    beginitem = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(auxwindow), DATA_BEGIN_ITEM));
    enditem = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(auxwindow), DATA_END_ITEM));

    model = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
    sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
    switch (keycode) {
	case IM_VK_UP:
	    if(gtk_tree_selection_get_selected(sel, NULL, &iter)) {
		path = gtk_tree_model_get_path(model, &iter);
		idx = gtk_tree_path_get_indices(path)[0];
		if (idx == 0) {
		    if (beginitem > 1) {
			/* back to the previous page. */
			update_dictmenu_state(im, ic, xc, -1);
			return;
		    }
		}
		if (gtk_tree_path_prev(path)) {
		    column = gtk_tree_view_get_column(GTK_TREE_VIEW(widget), 0);
		    gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), path, column, FALSE);
		}
	    } else {
		path = gtk_tree_path_new_first();
		column = gtk_tree_view_get_column(GTK_TREE_VIEW(widget), 0);
		gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), path, column, FALSE);
	    }
	    idx = gtk_tree_path_get_indices(path)[0];
	    update_entry_count(idx);
	    gtk_tree_path_free(path);
	    break;
	case IM_VK_DOWN:
	    if (gtk_tree_selection_get_selected(sel, NULL, &iter)) {
		path = gtk_tree_model_get_path(model, &iter);
		idx = gtk_tree_path_get_indices(path)[0];
		if ((idx + 1) >= pagemax) {
		    if (enditem >= (beginitem + pagemax)) {
			/*
			 * current position in the server should be the begin of item.
			 */
			update_dictmenu_state(im, ic, xc, 1);
		    }
		    return;
		}
		gtk_tree_path_next(path);
	    } else {
		path = gtk_tree_path_new_first();
	    }
	    column = gtk_tree_view_get_column(GTK_TREE_VIEW(widget), 0);
	    gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), path, column, FALSE);
	    idx = gtk_tree_path_get_indices(path)[0];
	    update_entry_count(idx);
	    gtk_tree_path_free(path);
	    break;
	case IM_VK_ENTER:
	    update_dictmenu_state(im, ic, xc, 1);
	    send_key_event(im, ic, xc, IM_VK_ENTER, 0, 0x0D);
	    break;
	case IM_VK_SPACE:
	    gtk_tree_view_get_cursor(GTK_TREE_VIEW(widget), &path, &column);
	    model = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
	    if (gtk_tree_model_get_iter(model, &iter, path)) {
		gtk_tree_model_get(model, &iter, 0, &use, 2, &changed, -1);
		use ^= 1;
		changed ^= 1;
		gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, use, 2, changed, -1);
	    }
	    gtk_tree_path_free(path);
	    break;
	case IM_VK_BACK_SPACE:
	    send_key_event(im, ic, xc, keycode, 0, translated);
	    break;
	default:
	    if (modifier & IM_CTRL_MASK && keycode == IM_VK_G) {
		/* cancel */
		send_key_event(im, ic, xc, IM_VK_BACK_SPACE, 0, 0x08);
	    }
	    break;
    }
}

static void
update_server_position(int           keycode,
		       int           modifier,
		       int           translated,
		       int           im,
		       int           ic,
		       xaux_class_t *xc)
{
    GtkWidget *widget = g_object_get_data(G_OBJECT(auxwindow), DATA_ENTRY);
    GdkEvent *eventp, *eventr;
    GString *str;
    int i;

    eventp = translate_key_event(keycode, modifier, TRUE);
    eventr = translate_key_event(keycode, modifier, FALSE);

    str = g_string_new(gtk_entry_get_text(GTK_ENTRY(widget)));
    if ((keycode == IM_VK_BACK_SPACE && str->len == 0) ||
	keycode == IM_VK_ESCAPE ||
	(keycode == IM_VK_G && modifier & IM_CTRL_MASK)) {
	/* cancel */
	send_key_event(im, ic, xc, IM_VK_BACK_SPACE, 0, 0x08);
    } else if (keycode == IM_VK_ENTER) {
	for (i = 0; i < str->len; i++) {
	    send_key_event(im, ic, xc, str->str[i], 0, str->str[i]);
	}
	send_key_event(im, ic, xc, keycode, 0, translated);
    } else if (eventp->key.keyval == 0) {
	/* nothing here */
    } else {
	/* send pseudo key event to the widget */
	eventp->key.window = eventr->key.window = widget->window;
	if (widget->window) {
	    g_object_ref(widget->window);
	    g_object_ref(widget->window);
	}
	gtk_widget_event(widget, eventp);
	gtk_widget_event(widget, eventr);
    }

    gdk_event_free(eventp);
    gdk_event_free(eventr);
    g_string_free(str, TRUE);
}

static void
update_wordregist_position(int           keycode,
			   int           modifier,
			   int           translated,
			   int           im,
			   int           ic,
			   xaux_class_t *xc)
{
    /*
     * basically all of key event should be passed through cannaserver -
     * some keys needs to be translated, though
     */
    switch(keycode) {
	case IM_VK_BACK_SPACE:
	    translated = 0x08;
	    break;
	default:
	    if (keycode == IM_VK_G && (modifier & IM_CTRL_MASK)) {
		translated = '';
	    }
	    break;
    }
    send_key_event(im, ic, xc, keycode, modifier, translated);
}

static void
label_keyevent(int           keycode,
	       int           modifier,
	       int           translated,
	       int           im,
	       int           ic,
	       xaux_class_t *xc)
{
    /*
     * basically all of key event should be passed through cannaserver -
     * some keys needs to be translated, though
     */
    switch(keycode) {
	case IM_VK_BACK_SPACE:
	    translated = 0x08;
	    break;
	default:
	    if (keycode == IM_VK_G && (modifier & IM_CTRL_MASK)) {
		translated = '';
	    }
	    break;
    }
    send_key_event(im, ic, xc, keycode, modifier, translated);
}

static void
unknown_keyevent(int           keycode,
		 int           modifier,
		 int           translated,
		 int           im,
		 int           ic,
		 xaux_class_t *xc)
{
    /* always cancel */
    send_key_event(im, ic, xc, IM_VK_G, IM_CTRL_MASK, '');
}

static GdkFilterReturn
event_handler(GdkXEvent *gdk_xevent,
	       GdkEvent *event,
	       gpointer data)
{
    XEvent *xevent = (XEvent *)gdk_xevent;

    if (xevent->type == ClientMessage) {
	xaux_ext_process_client_message(GDK_DISPLAY(), (XClientMessageEvent *)xevent);
    }

    return GDK_FILTER_CONTINUE;
}

Bool
xaux_ext_Draw(xaux_class_t   *xc,
	      aux_ext_data_t *aux_ext_data)
{
    int mode, i, len;
    GtkWidget *widget, *fwidget, *frame, *flabel;
    gchar *item, *p, *pp, *value;
    static gboolean update_win_pos = FALSE;

    ENTER;

    if (aux_ext_data->integer_count > 0) {
	mode = aux_ext_data->integer_list[INT_STATE];
	switch (aux_ext_data->integer_list[INT_AUXMODE]) {
	    case AUX_UPDATE_STATE:
		frame = g_object_get_data(G_OBJECT(auxwindow), DATA_FRAME);
		flabel = gtk_frame_get_label_widget(GTK_FRAME(frame));
		fwidget = g_object_get_data(G_OBJECT(auxwindow), DATA_FATTACHED);
		widget = g_object_get_data(G_OBJECT(auxwindow), DATA_ATTACHED);

		/* unfocus before removing the widget */
		change_focus(fwidget, 0);

		if (widget != NULL) {
		    gtk_container_remove(GTK_CONTAINER(auxwindow), widget);
		}
		if (fwidget != NULL) {
		    gtk_container_remove(GTK_CONTAINER(frame), fwidget);
		}
		widget = NULL;
		fwidget = NULL;

		DEBUG_printf("*** mode = %d\n", mode);
		DEBUG_printf("*** items = %d/(%d-%d)\n",
			     aux_ext_data->integer_list[INT_CURPOS],
			     aux_ext_data->integer_list[INT_BEGINPOS],
			     aux_ext_data->integer_list[INT_CANDCOUNT]);

		switch (mode) {
		    case CANNA_MODE_HexMode:
			if (aux_ext_data->string_count > 0) {
			    gchar *item;

			    fwidget = g_object_get_data(G_OBJECT(auxwindow), DATA_ENTRY);
			    aux_ext_data->string_list[0].ptr[aux_ext_data->string_list[0].length] = 0;
			    aux_ext_data->string_list[0].ptr[aux_ext_data->string_list[0].length+1] = 0;
			    item = g_utf16_to_utf8((gunichar2 *)aux_ext_data->string_list[0].ptr, -1, NULL, NULL, NULL);
			    gtk_entry_set_text(GTK_ENTRY(fwidget), "");
			    gtk_entry_set_max_length(GTK_ENTRY(fwidget), 4);
			    gtk_label_set_text(GTK_LABEL(flabel), item);
			    g_free(item);
			    widget = frame;
			}
			break;
		    case CANNA_MODE_TourokuHinshiMode:
		    case CANNA_MODE_TourokuDicMode:
			if (aux_ext_data->integer_list[INT_CANDCOUNT] == 0)
			    goto label;
		    case CANNA_MODE_ExtendMode:
		    menu:
			if (aux_ext_data->string_count > 0) {
			    fwidget = g_object_get_data(G_OBJECT(auxwindow), DATA_TREEVIEW);
			    g_object_set_data(G_OBJECT(auxwindow), DATA_FTITLE, "Menu");
			    g_object_set_data(G_OBJECT(auxwindow),
					      DATA_BEGIN_ITEM,
					      GUINT_TO_POINTER(aux_ext_data->integer_list[INT_BEGINPOS]));
			    g_object_set_data(G_OBJECT(auxwindow),
					      DATA_CUR_ITEM,
					      GUINT_TO_POINTER(aux_ext_data->integer_list[INT_CURPOS]));
			    g_object_set_data(G_OBJECT(auxwindow),
					      DATA_END_ITEM,
					      GUINT_TO_POINTER(aux_ext_data->integer_list[INT_CANDCOUNT]));
			    update_menu(fwidget, aux_ext_data->string_count, aux_ext_data->string_list);
			    move_menu_position(aux_ext_data->integer_list[INT_CURPOS]);
			    update_entry_count(aux_ext_data->integer_list[INT_CURPOS] - aux_ext_data->integer_list[INT_BEGINPOS]);
			    widget = frame;
			}
			break;
		    case CANNA_MODE_MountDicMode:
			if (aux_ext_data->string_count > 0) {
			    fwidget = g_object_get_data(G_OBJECT(auxwindow), DATA_TREEVIEW);
			    g_object_set_data(G_OBJECT(auxwindow), DATA_FTITLE, "Menu");
			    g_object_set_data(G_OBJECT(auxwindow),
					      DATA_BEGIN_ITEM,
					      GUINT_TO_POINTER(aux_ext_data->integer_list[INT_BEGINPOS]));
			    g_object_set_data(G_OBJECT(auxwindow),
					      DATA_CUR_ITEM,
					      GUINT_TO_POINTER(aux_ext_data->integer_list[INT_CURPOS]));
			    g_object_set_data(G_OBJECT(auxwindow),
					      DATA_END_ITEM,
					      GUINT_TO_POINTER(aux_ext_data->integer_list[INT_CANDCOUNT]));
			    update_dictmenu(fwidget, aux_ext_data->string_count, aux_ext_data->string_list);
			    move_menu_position(aux_ext_data->integer_list[INT_CURPOS]);
			    update_entry_count(aux_ext_data->integer_list[INT_CURPOS] - aux_ext_data->integer_list[INT_BEGINPOS]);
			    widget = frame;
			}
			break;
		    case CANNA_MODE_ChangingServerMode:
			if (aux_ext_data->string_count > 0) {
			    fwidget = g_object_get_data(G_OBJECT(auxwindow), DATA_ENTRY);
			    aux_ext_data->string_list[0].ptr[aux_ext_data->string_list[0].length] = 0;
			    aux_ext_data->string_list[0].ptr[aux_ext_data->string_list[0].length+1] = 0;
			    item = g_utf16_to_utf8((gunichar2 *)aux_ext_data->string_list[0].ptr, -1, NULL, NULL, NULL);
			    /* find the value */
			    p = rindex(item, '[');
			    pp = rindex(item, ']');
			    if (p != NULL && pp != NULL) {
				value = g_strndup(p + 1, pp - p - 1);
				*p = 0;
				gtk_entry_set_text(GTK_ENTRY(fwidget), value);
				len = strlen(value);
			    } else {
				len = 0;
			    }
			    gtk_entry_set_max_length(GTK_ENTRY(fwidget), 0);
			    gtk_frame_set_label(GTK_FRAME(frame), item);
			    /*
			     * this is bad hack. but no way to wrap it on here.
			     * send a request to remove the current value to cannaserver first
			     */
			    for (i = 0; i < len; i++) {
				send_key_event(aux_ext_data->im,
					       aux_ext_data->ic,
					       xc, IM_VK_RIGHT, 0, CANNA_KEY_Right);
				send_key_event(aux_ext_data->im,
					       aux_ext_data->ic,
					       xc, IM_VK_BACK_SPACE, 0, 0x08);
			    }
			    if (item != NULL)
				g_free(item);
			    if (value != NULL)
				g_free(value);
			    widget = frame;
			}
			break;
		    case CANNA_MODE_TourokuMode:
			if (aux_ext_data->string_count > 0) {
			    aux_ext_data->string_list[0].ptr[aux_ext_data->string_list[0].length] = 0;
			    aux_ext_data->string_list[0].ptr[aux_ext_data->string_list[0].length+1] = 0;
			    item = g_utf16_to_utf8((gunichar2 *)aux_ext_data->string_list[0].ptr, -1, NULL, NULL, NULL);
			    p = rindex(item, '(');
			    if (p != NULL && strncmp(p, "(y/n)", 5) == 0) {
				widget = g_object_get_data(G_OBJECT(auxwindow), DATA_LABEL);
				gtk_label_set_label(GTK_LABEL(widget), item);
			    } else {
				p = rindex(item, '[');
				pp = rindex(item, ']');
				if (p != NULL && pp != NULL) {
				    value = g_strndup(p + 1, pp - p - 1);
				    *p = 0;
				    fwidget = g_object_get_data(G_OBJECT(auxwindow), DATA_ENTRY);
				    widget = frame;
				    if (value != NULL)
					gtk_entry_set_text(GTK_ENTRY(fwidget), value);
				    gtk_entry_set_max_length(GTK_ENTRY(fwidget), 0);
				    gtk_frame_set_label(GTK_FRAME(frame), item);
				} else {
				    widget = g_object_get_data(G_OBJECT(auxwindow), DATA_LABEL);
				    gtk_label_set_label(GTK_LABEL(widget), item);
				}
			    }
			    if (value != NULL)
				g_free(value);
			    if (item != NULL)
				g_free(item);
			}
			break;
		    case CANNA_MODE_DeleteDicMode:
			if (aux_ext_data->string_count > 0) {
			    aux_ext_data->string_list[0].ptr[aux_ext_data->string_list[0].length] = 0;
			    aux_ext_data->string_list[0].ptr[aux_ext_data->string_list[0].length+1] = 0;
			    item = g_utf16_to_utf8((gunichar2 *)aux_ext_data->string_list[0].ptr, -1, NULL, NULL, NULL);
			    p = rindex(item, '[');
			    pp = rindex(item, ']');
			    if (p != NULL && pp != NULL) {
				value = g_strndup(p + 1, pp - p - 1);
				*p = 0;
				fwidget = g_object_get_data(G_OBJECT(auxwindow), DATA_ENTRY);
				widget = frame;
				if (value != NULL)
				    gtk_entry_set_text(GTK_ENTRY(fwidget), value);
				gtk_entry_set_max_length(GTK_ENTRY(fwidget), 0);
				gtk_frame_set_label(GTK_FRAME(frame), item);
			    } else if (aux_ext_data->integer_list[INT_CANDCOUNT] > 0) {
				g_free(item);
				goto menu;
			    } else {
				widget = g_object_get_data(G_OBJECT(auxwindow), DATA_LABEL);
				gtk_label_set_label(GTK_LABEL(widget), item);
			    }
			    if (value != NULL)
				g_free(value);
			    if (item != NULL)
				g_free(item);
			}
			break;
		    case CANNA_MODE_HenkanMethodMode:
			widget = g_object_get_data(G_OBJECT(auxwindow), DATA_LABEL);
			gtk_label_set_label(GTK_LABEL(widget), "not implemented yet. please hit any key to be back");
			break;
		    default:
		    label:
			if (aux_ext_data->string_count > 0) {
			    widget = g_object_get_data(G_OBJECT(auxwindow), DATA_LABEL);
			    aux_ext_data->string_list[0].ptr[aux_ext_data->string_list[0].length] = 0;
			    aux_ext_data->string_list[0].ptr[aux_ext_data->string_list[0].length+1] = 0;
			    item = g_utf16_to_utf8((gunichar2 *)aux_ext_data->string_list[0].ptr, -1, NULL, NULL, NULL);
			    gtk_label_set_label(GTK_LABEL(widget), item);
			    g_free(item);
			}
			break;
		}
		if (fwidget != NULL) {
		    gtk_container_add(GTK_CONTAINER(frame), fwidget);
		    change_focus(fwidget, 1);
		}
		if (widget != NULL) {
		    gtk_container_add(GTK_CONTAINER(auxwindow), widget);
		    if (!update_win_pos) {
			set_position(auxwindow);
			update_win_pos = TRUE;
		    }
		    gtk_widget_show_all(auxwindow);
		} else {
		    gtk_widget_hide_all(auxwindow);
		    update_win_pos = FALSE;
		}
		g_object_set_data(G_OBJECT(auxwindow), DATA_FATTACHED, fwidget);
		g_object_set_data(G_OBJECT(auxwindow), DATA_ATTACHED, widget);
		break;
	    case AUX_UPDATE_KEY:
		switch (mode) {
		    case CANNA_MODE_HexMode:
			update_codeinput_position(aux_ext_data->integer_list[INT_KEYCODE],
						  aux_ext_data->integer_list[INT_KEYMOD],
						  aux_ext_data->integer_list[INT_TRANSLATEDCODE],
						  aux_ext_data->im,
						  aux_ext_data->ic,
						  xc);
			break;
		    case CANNA_MODE_ExtendMode:
		    case CANNA_MODE_TourokuHinshiMode:
			if (aux_ext_data->integer_list[INT_CANDCOUNT] == 0) {
			    label_keyevent(aux_ext_data->integer_list[INT_KEYCODE],
					   aux_ext_data->integer_list[INT_KEYMOD],
					   aux_ext_data->integer_list[INT_TRANSLATEDCODE],
					   aux_ext_data->im,
					   aux_ext_data->ic,
					   xc);
			    break;
			}
		    case CANNA_MODE_TourokuDicMode:
			update_menu_position(aux_ext_data->integer_list[INT_KEYCODE],
					     aux_ext_data->integer_list[INT_KEYMOD],
					     aux_ext_data->integer_list[INT_TRANSLATEDCODE],
					     aux_ext_data->im,
					     aux_ext_data->ic,
					     xc, (mode == CANNA_MODE_TourokuHinshiMode ? FALSE : TRUE));
			break;
		    case CANNA_MODE_MountDicMode:
			update_dictmenu_position(aux_ext_data->integer_list[INT_KEYCODE],
						 aux_ext_data->integer_list[INT_KEYMOD],
						 aux_ext_data->integer_list[INT_TRANSLATEDCODE],
						 aux_ext_data->im,
						 aux_ext_data->ic,
						 xc);
			break;
		    case CANNA_MODE_ChangingServerMode:
			update_server_position(aux_ext_data->integer_list[INT_KEYCODE],
					       aux_ext_data->integer_list[INT_KEYMOD],
					       aux_ext_data->integer_list[INT_TRANSLATEDCODE],
					       aux_ext_data->im,
					       aux_ext_data->ic,
					       xc);
			break;
		    case CANNA_MODE_TourokuMode:
			update_wordregist_position(aux_ext_data->integer_list[INT_KEYCODE],
						   aux_ext_data->integer_list[INT_KEYMOD],
						   aux_ext_data->integer_list[INT_TRANSLATEDCODE],
						   aux_ext_data->im,
						   aux_ext_data->ic,
						   xc);
			break;
		    case CANNA_MODE_DeleteDicMode:
			if (aux_ext_data->integer_list[INT_CANDCOUNT] == 0) {
			    update_wordregist_position(aux_ext_data->integer_list[INT_KEYCODE],
						       aux_ext_data->integer_list[INT_KEYMOD],
						       aux_ext_data->integer_list[INT_TRANSLATEDCODE],
						       aux_ext_data->im,
						       aux_ext_data->ic,
						       xc);
			} else {
			    update_menu_position(aux_ext_data->integer_list[INT_KEYCODE],
						 aux_ext_data->integer_list[INT_KEYMOD],
						 aux_ext_data->integer_list[INT_TRANSLATEDCODE],
						 aux_ext_data->im,
						 aux_ext_data->ic,
						 xc, TRUE);
			}
			break;
		    case CANNA_MODE_HenkanMethodMode:
			unknown_keyevent(aux_ext_data->integer_list[INT_KEYCODE],
					 aux_ext_data->integer_list[INT_KEYMOD],
					 aux_ext_data->integer_list[INT_TRANSLATEDCODE],
					 aux_ext_data->im,
					 aux_ext_data->ic,
					 xc);
			break;
		    default:
			break;
		}
		break;
	    case AUX_FOCUS_CHANGE:
		fwidget = g_object_get_data(G_OBJECT(auxwindow), DATA_FATTACHED);
		change_focus(fwidget, aux_ext_data->integer_list[INT_STATE]);
		if (aux_ext_data->integer_list[INT_STATE] == 0) {
		    gtk_widget_hide_all(auxwindow);
		}
		break;
	    case AUX_FORCE_SAVE_STATE:
		switch (aux_ext_data->integer_list[INT_STATE]) {
		    case CANNA_MODE_HexMode:
		    case CANNA_MODE_ExtendMode:
		    case CANNA_MODE_MountDicMode:
		    case CANNA_MODE_ChangingServerMode:
		    case CANNA_MODE_HenkanMethodMode:
		    case CANNA_MODE_DeleteDicMode:
		    case CANNA_MODE_TourokuHinshiMode:
		    case CANNA_MODE_TourokuDicMode:
			/* FIXME: imlement me */
			break;
		    case CANNA_MODE_TourokuMode:
			break;
		    default:
			break;
		}
		break;
	}
    } else {
	gtk_widget_hide_all(auxwindow);
    }

    LEAVE;

    return True;
}

Bool
xaux_ext_Done(xaux_class_t   *xc,
	      aux_ext_data_t *aux_ext_data)
{
#if 0
    gtk_main_quit();
#endif

    return True;
}

int
main(int argc, char **argv)
{
    GtkWidget *frame;
    GtkWidget *treeview;
    GtkWidget *label;
    GtkWidget *entry;
    GtkWidget *flabel;

    ENTER;

    gtk_init(&argc, &argv);

    auxwindow = gtk_window_new(GTK_WINDOW_POPUP);
    gtk_window_set_policy(GTK_WINDOW(auxwindow), TRUE, TRUE, FALSE);
    gtk_window_set_resizable(GTK_WINDOW(auxwindow), FALSE);
    gtk_window_set_modal(GTK_WINDOW(auxwindow), TRUE);
    gtk_widget_add_events(auxwindow, GDK_BUTTON_PRESS_MASK);
    gtk_widget_add_events(auxwindow, GDK_BUTTON_RELEASE_MASK);
    gtk_widget_add_events(auxwindow, GDK_KEY_PRESS_MASK);
    gtk_widget_add_events(auxwindow, GDK_KEY_RELEASE_MASK);
    gtk_widget_add_events(auxwindow, GDK_POINTER_MOTION_MASK);
    gtk_container_set_border_width(GTK_CONTAINER(auxwindow), 2);

    gtk_window_set_accept_focus(GTK_WINDOW(auxwindow), FALSE);
    gtk_widget_realize(auxwindow);
    gtk_window_set_accept_focus(GTK_WINDOW(auxwindow), FALSE);

    /* for debug */
    label = gtk_label_new(NULL);

    /* for frame */
    frame = gtk_frame_new(NULL);
    flabel = gtk_label_new(NULL);
    gtk_frame_set_label_widget(GTK_FRAME(frame), flabel);
    gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);

    /* for menu */
    treeview = gtk_tree_view_new();
    gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE);
    gtk_tree_view_columns_autosize(GTK_TREE_VIEW(treeview));
    gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE);

    /* for entry */
    entry = gtk_entry_new();

    /* do g_object_ref() to avoid destroying the object after gtk_container_remove() */
    g_object_ref(G_OBJECT(label));
    g_object_ref(G_OBJECT(frame));
    g_object_ref(G_OBJECT(treeview));
    g_object_ref(G_OBJECT(entry));

    /* store the widgets into auxwindow's data */
    g_object_set_data(G_OBJECT(auxwindow), DATA_LABEL, (gpointer)label);
    g_object_set_data(G_OBJECT(auxwindow), DATA_FRAME, (gpointer)frame);
    g_object_set_data(G_OBJECT(auxwindow), DATA_TREEVIEW, (gpointer)treeview);
    g_object_set_data(G_OBJECT(auxwindow), DATA_ENTRY, (gpointer)entry);
    g_object_set_data(G_OBJECT(auxwindow), DATA_ATTACHED, (gpointer)NULL);
    g_object_set_data(G_OBJECT(auxwindow), DATA_FATTACHED, (gpointer)NULL);

    /* register aux class */
    xaux_ext_init_classes(GDK_DISPLAY(), &xaux_class, GDK_WINDOW_XWINDOW(auxwindow->window));

    gdk_window_add_filter(auxwindow->window, event_handler, NULL);

    g_signal_connect(G_OBJECT(auxwindow),
		     "destroy", gtk_main_quit, NULL);
    g_signal_connect(G_OBJECT(auxwindow),
		     "delete-event", gtk_main_quit, NULL);

    gtk_main();

    LEAVE;

    return 0;
}

