#include <stdio.h>
#include <stdlib.h>

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "gtkiiimwin.h"
#include "gtkflatbutton.h"
#include "imlabel.h"

#define MAX_CANDIDATE_COUNT  32
#define MAX_LABELSTRING_LEN  (8*1024)

#define WINDOW_GAP_VERTICAL    2
#define WINDOW_GAP_HORIZENTAL  2

#define MIN_PREEDIT_WIDTH      160
//for horizental display
#define MIN_CANDIDATES_WIDTH   200
//for vertical display
#define MIN_CANDIDATES_HEIGHT  120

#define _(a)  (a)

typedef struct {
    GtkWidget *             iiimPCWinSysMenu;
    GSList *                RootWindow_group;
    GtkWidget *             RootWindow;
    GtkWidget *             ClassicCompositeFollow;
    GtkWidget *             TraditionalStyle;
    GtkWidget *             ModernCanidatesHorizental;
    GtkWidget *             CustomizeStyle;
    GtkWidget *             _________1;
    GtkWidget *                 ShowSystemMenu;
    GtkWidget *                 FollowCursor;
    GtkWidget *                 GroupPreeditCandidates;
    GtkWidget *             _________2;
    GtkWidget *                 CandidatesDirection;
    GtkWidget *                 CandidatesDirection_menu;
    GSList *                        CandidatesHorizental_group;
    GtkWidget *                     CandidatesHorizental;
    GtkWidget *                     CandidatesVertical;
    GtkWidget *                 CandidateTitlePosition;
    GtkWidget *                 CandidateTitlePosition_menu;
    GSList *                        AboveCandidates_group;
    GtkWidget *                     AboveCandidates;
    GtkWidget *                     BelowCandidates;
    GtkWidget *                 CandidatesPosition;
    GtkWidget *                 CandidatesPosition_menu;
    GSList *                        CandidatesFollowIme_group;
    GtkWidget *                     CandidatesFollowIme;
    GtkWidget *                     CandidatesStickPreedit;
    GtkWidget *                     CandidatesFollowCaret;
    GtkWidget *                     CandidatesRight2Preedit;
    GtkWidget *             _________3;
    GtkWidget *                 ShowPageControl;
    GtkWidget *                 IgnorCandidatesTitle;
} TPCOptionMenu;

typedef struct _tagRealIIimPCWin {
    TPCStyleOption  style;

    TPCOptionMenu*  pMenu;
    int             poping_up;

    GtkWidget*      dragging_area;
    GtkWidget*      preeditWin;
    GtkWidget*      preeditLabel;

    GtkWidget*      candidatesWin;
    GtkWidget*      candidatesTitleLabel;
    GtkWidget*      candidatesLabels[MAX_CANDIDATE_COUNT];

    GtkWidget*      sysMenuButton;
    GtkWidget*      buttonPageRequest[4];

    GtkWidget*      pageButtonBox;
    GtkWidget*      candidatesPackBox;
    GtkWidget*      candidatesTitleBox;

    int             cursor_x, cursor_y, cursor_w, cursor_h;
    int             preeditShow, preeditRealShow;
    int             preeditCaret;
    int             preeditCandidateCaret;
    char            bufPreedit[MAX_LABELSTRING_LEN];

    int             candidatesCount;
    int             candidatesShow, candidatesRealShow;
    char            bufCandidateTitle[MAX_LABELSTRING_LEN];
    char            bufCandidates[MAX_CANDIDATE_COUNT][MAX_LABELSTRING_LEN];

    TIIimPCWinCallback  process_style_change;
    TIIimPCWinCallback  process_page_request;
    TIIimPCWinCallback  process_select_request;
    TIIimPCWinCallback  process_position_change;

    int             firstCandidat, currentCandidateInWindow, totalCandidates;
    int             dragging, drag_x_start, drag_y_start, drag_x_save, drag_y_save;
} TRealIIimPCWin;

static GdkCursor*       s_moving_cursor = NULL;

static GdkCursor*
getMovingCursor(void)
{
    if (s_moving_cursor == NULL) {
        s_moving_cursor = gdk_cursor_new(GDK_FLEUR);
        gdk_cursor_ref(s_moving_cursor);
    }
    return s_moving_cursor;
}

static TPCOptionMenu*   create_iiimPCWinSysMenu (TRealIIimPCWin* pc);
static void             getPreeditBox(TRealIIimPCWin* pc, int idx, int *px, int *py, int *pw, int *ph);
static void             updateMenuStates(TRealIIimPCWin* pc);
static void             updateMenuActivates(TRealIIimPCWin* pc);
static void 	        adjustCandidateWindow(TRealIIimPCWin* pc, int candx, int candy, int candw, int candh);
static int  	        adjustPCWindows(TRealIIimPCWin* pc, int predx, int predy, int predw, int predh);
static void             updatePreeditData(TIIimPCWin vpc, const char* label, int caret, int candidate_start, int nfbs, ImeFeedbackRec* fbs);
static void             updateCandidatesData(TIIimPCWin vpc, int num, const char* candidates[], const char* title,
                                             int firstIdx, int focusIdx, int total,
                                             int *p_fb_counts, ImeFeedbackRec **pfbs);

static gboolean         onMouseButtonPress(GtkWidget *widget, GdkEventButton *event, gpointer user_data);
static gboolean         onWindowPointerMove(GtkWidget *widget, GdkEventMotion *event, gpointer user_data);
static gboolean         onMouseButtonRelease(GtkWidget *widget, GdkEventButton *event, gpointer user_data);

static gboolean         on_SystemMenu_press_event(GtkWidget *widget, GdkEventButton *event, gpointer user_data);

static TPCStyleAtomic predefinedStyles[] = {
  { // PC_STYLE_CUSTOM
      1,      /* showSystemBar */
      1,      /* followCursor */
      0,      /* composite */
      1,      /* showPageControl */
      1,      /* ignoreCandidateTitle */
      0,      /* candidateVertical */
      CANDIDATES_POSITION_IME,      /* candidatePosition */
      TITLE_ABOVE_CANDIDATES
  },

  { //PC_STYLE_ROOT
      1,      /* showSystemBar */
      0,      /* followCursor */
      1,      /* composite */
      1,      /* showPageControl */
      0,      /* ignoreCandidateTitle */
      0,      /* candidateVertical */
      CANDIDATES_POSITION_PREEDIT_STARTS,   /* candidatePosition */
      TITLE_ABOVE_CANDIDATES                /* candidateTitlePosition */
  },

  { //PC_STYLE_CLASSIC
      1,      /* showSystemBar */
      1,      /* followCursor */
      1,      /* composite */
      1,      /* showPageControl */
      1,      /* ignoreCandidateTitle */
      0,      /* candidateVertical */
      CANDIDATES_POSITION_PREEDIT_STARTS,   /* candidatePosition */
      TITLE_ABOVE_CANDIDATES                /* candidateTitlePosition */
  },

  { //PC_STYLE_TRADITIONAL
      1,      /* showSystemBar */
      1,      /* followCursor */
      0,      /* composite */
      1,      /* showPageControl */
      0,      /* ignoreCandidateTitle */
      1,      /* candidateVertical */
      CANDIDATES_POSITION_PREEDIT_RIGHT,    /* candidatePosition */
      TITLE_ABOVE_CANDIDATES                /* candidateTitlePosition */
  },

  { //PC_STYLE_MODERN
      1,      /* showSystemBar */
      1,      /* followCursor */
      0,      /* composite */
      1,      /* showPageControl */
      1,      /* ignoreCandidateTitle */
      0,      /* candidateVertical */
      CANDIDATES_POSITION_IME,              /* candidatePosition */
      TITLE_BELOW_CANDIDATES                /* candidateTitlePosition */
  }
};

static const char *pgop_icon_horizental_names[] = {
  "LeftMostTriangle.png",
  "LeftTriangle.png",
  "RightTriangle.png",
  "RightMostTriangle.png"
};

static const char *pgop_icon_vertical_names[] = {
  "UpMostTriangle.png",
  "UpTriangle.png",
  "DownTriangle.png",
  "DownMostTriangle.png"
};

static const char *pgop_desc[] = {
    "First Page",
    "Prev Page",
    "Next Page",
    "Last Page"
};

static TPCStyleAtomic*
pc_get_atomic_style(TRealIIimPCWin *pc)
{
    TPCStyleAtomic* prs=NULL;

    if (pc->style.type > PC_STYLE_CUSTOM && pc->style.type <= PC_STYLE_MODERN) {
        prs = &predefinedStyles[pc->style.type];
    } else {
        prs = &pc->style.customOption;
    }
    return prs;
}

static int
get_pc_candidates_position(TRealIIimPCWin *pc)
{
    return pc_get_atomic_style(pc)->candidatePosition;
}

static int
is_pc_candidates_vertical(TRealIIimPCWin *pc)
{
    TPCStyleAtomic* prs = pc_get_atomic_style(pc);
    return (!prs->composite && prs->candidateVertical);
}

static int
is_pc_show_page_buttons(TRealIIimPCWin *pc)
{
    return pc_get_atomic_style(pc)->showPageControl;
}

static int
is_pc_show_candidates_title(TRealIIimPCWin *pc)
{
    return !(pc_get_atomic_style(pc)->ignoreCandidateTitle);
}

static
is_pc_show_sys_button(TRealIIimPCWin *pc)
{
  return pc_get_atomic_style(pc)->showSystemBar;
}

static int
is_pc_follow_cursor(TRealIIimPCWin *pc)
{
    return pc_get_atomic_style(pc)->followCursor;
}

static int
is_pc_candidates_title_left_or_upper(TRealIIimPCWin *pc)
{
    TPCStyleAtomic* prs = pc_get_atomic_style(pc);
    return ((!prs->composite) && (prs->candidateTitlePosition == TITLE_LEFT2_CANDIDATES));
}

static int
is_pc_composite(TRealIIimPCWin *pc)
{
    return (pc_get_atomic_style(pc)->composite);
}

static gboolean
on_PageRequest_press_event             (GtkWidget       *widget,
                                        GdkEventButton  *event,
                                        gpointer         user_data)
{
    int pgop;
    TRealIIimPCWin *pc = (TRealIIimPCWin*)user_data;

    if (pc) {
        /*
        if (!is_pc_composite(pc)) {
            gtk_window_set_resizable(GTK_WINDOW(pc->preeditWin), TRUE); //make sure window size do not decrease
        }
        if (!is_pc_composite(pc) && !is_pc_candidates_vertical(pc)) {
            gtk_window_set_resizable(GTK_WINDOW(pc->candidatesWin), TRUE); //make sure window size do not decrease
        }
        */

        for (pgop=PC_PAGE_FIRST; pgop <= PC_PAGE_LAST; ++pgop) {
            if (widget == pc->buttonPageRequest[pgop]) {
                DEBUG_printf("%s Requested\n", pgop_desc[pgop]);
                if (pc->process_page_request != NULL)
                    pc->process_page_request(pc, pgop);
            }
        }
    }
}

static void
pc_draw_dragging_frame(TRealIIimPCWin *pc, int pos_x, int pos_y, int w, int h)
{
    GdkGC *xor_gc;
    GdkGCValues values;
    GdkWindow *root_window;

    values.foreground = (pc->preeditWin->style->white);

    values.function = GDK_XOR;
    values.subwindow_mode = GDK_INCLUDE_INFERIORS;

    root_window = gtk_widget_get_root_window (pc->preeditWin);

    xor_gc = gdk_gc_new_with_values (root_window, &values,
                                     GDK_GC_FOREGROUND | GDK_GC_FUNCTION | GDK_GC_SUBWINDOW);

    gdk_draw_rectangle (root_window, xor_gc, FALSE, pos_x, pos_y, w, h);
}

static void
pcDraggingMove(TRealIIimPCWin *pc, GdkEventMotion *event)
{
    int predx, predy, predw, predh;

    predx = pc->drag_x_start;
    predy = pc->drag_y_start;
    predw = pc->preeditWin->allocation.width;
    predh = pc->preeditWin->allocation.height;

    /* remove the old dragging frame */
    pc_draw_dragging_frame(pc, predx, predy, predw, predh);

    /* draw the new dragging frame */
    predx += (event->x_root - pc->drag_x_save);
    predy += (event->y_root - pc->drag_y_save);
    pc_draw_dragging_frame(pc, predx, predy, predw, predh);

    /* save the curent dragging pointer position */
    pc->drag_x_start = predx;
    pc->drag_y_start = predy;
    pc->drag_x_save = event->x_root;
    pc->drag_y_save = event->y_root;
}

static void
pcDraggingEnd(TRealIIimPCWin *pc, GdkEventButton* event)
{
    int predx, predy, predw, predh, styleChange = 0;
    TPCStyleOption pcstyle = pc->style;

    gdk_pointer_ungrab(event->time);

    predx = pc->drag_x_start;
    predy = pc->drag_y_start;
    predw = pc->preeditWin->allocation.width;
    predh = pc->preeditWin->allocation.height;

    /* remove the old dragging frame */
    pc_draw_dragging_frame(pc, predx, predy, predw, predh);

    predx += (event->x_root - pc->drag_x_save);
    predy += (event->y_root - pc->drag_y_save);

    movePreedit(pc, predx, predy - WINDOW_GAP_VERTICAL - 10, 1, 10);

    if (pc->style.type != PC_STYLE_CUSTOM ||
            (pc->style.type != PC_STYLE_ROOT && pc->style.customOption.followCursor != 0)) {

        if (pc->style.type > PC_STYLE_CUSTOM && pc->style.type <= PC_STYLE_MODERN) {
            pcstyle.customOption = predefinedStyles[pc->style.type];
        }
        pcstyle.type = PC_STYLE_CUSTOM;
        pcstyle.customOption.followCursor = 0;
        styleChange = 1;
    }

    if (pc->process_position_change)
        pc->process_position_change(pc, 0);
    if (styleChange && pc->process_style_change)
         pc->process_style_change(pc, (int)&pcstyle);
}

static void
pcDraggingBegin(TRealIIimPCWin *pc, GdkEventButton* event)
{
    int predx, predy, predw, predh;

    gtk_window_get_position(GTK_WINDOW(pc->preeditWin), &predx, &predy);
    predw = pc->preeditWin->allocation.width;
    predh = pc->preeditWin->allocation.height;

    /* begin dragging */
    pc->drag_x_start = predx;
    pc->drag_y_start = predy;
    pc->drag_x_save  = event->x_root;
    pc->drag_y_save  = event->y_root;

    /* Grab the cursor to prevent losing events. */
    gdk_pointer_grab (pc->preeditWin->window,
                      FALSE,
                      GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK,
                      NULL,
                      getMovingCursor(),
                      event->time);

    pc_draw_dragging_frame(pc, predx, predy, predw, predh);
}

static gboolean
onMouseButtonPress(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
{
    TRealIIimPCWin *pc = (TRealIIimPCWin*)user_data;

    if (pc &&  event->button <= 1) {
        DEBUG_printf("Dragging start from position (%lf, %lf)\n", event->x_root, event->y_root);
        pc->dragging = 1;
        pcDraggingBegin(pc, event);
    } else if (pc) {
        on_SystemMenu_press_event(widget, event, user_data); //poping_up on right click
    }

    return FALSE;
}

static gboolean
onWindowPointerMove(GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
{
    TRealIIimPCWin *pc = (TRealIIimPCWin*)user_data;
    if (pc) {
        if ((event->state & GDK_BUTTON1_MASK) != 0 && pc->dragging) {
            DEBUG_printf("PC Dragging to new position (%lf, %lf)\n", event->x_root, event->y_root);
            pcDraggingMove(pc, event);
        }
        return TRUE;
    }
    return FALSE;
}

static gboolean
onMouseButtonRelease(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
{
    TRealIIimPCWin *pc = (TRealIIimPCWin*)user_data;
    if (pc && event->button <= 1) {
        DEBUG_printf("PC Dragging end at position (%lf, %lf)\n", event->x_root, event->y_root);
        if (pc->dragging != 0) {
            pc->dragging = 0;
            pcDraggingEnd(pc, event);
            return TRUE;
        }
    }
    return FALSE;
}

static gboolean
on_DraggingArea_expose_event           (GtkWidget       *widget,
                                        GdkEventExpose  *event,
                                        gpointer         user_data)
{
  int left, right, top, bottom;
  GdkColor lightcolor = {0, 255*256, 255*256, 255*256};
  GdkColor darkcolor = {0, 32*256, 32*256, 32*256};
  GdkGCValues gcvalues;

  g_return_val_if_fail (widget != NULL, FALSE);
  g_return_val_if_fail (event != NULL, FALSE);

  if (event->count > 0) return FALSE;

  gdk_window_clear_area (widget->window,
                         0,
                         0,
                         widget->allocation.width,
                         widget->allocation.height);

  gdk_gc_get_values  ( widget->style->fg_gc[widget->state], &gcvalues);

  gdk_window_clear_area (widget->window,
                         0,
                         0,
                         widget->allocation.width,
                         widget->allocation.height);


     left = widget->allocation.width/2 - 3;
     right = widget->allocation.width/2;
     for (; left <= right; left += 3) {
         top = 2;
         bottom = widget->allocation.height - 3;
         for (; top <= bottom; top += 3 ) {
             gdk_gc_set_rgb_fg_color ( widget->style->fg_gc[widget->state], &lightcolor );
             gdk_draw_line (widget->window,
                            widget->style->fg_gc[widget->state],
                            left, top, left, top);
             gdk_gc_set_rgb_fg_color ( widget->style->fg_gc[widget->state], &darkcolor );
             gdk_draw_line (widget->window,
                            widget->style->fg_gc[widget->state],
                            left+1, top+1, left+1, top+1);
         }
     }

  gdk_gc_set_values  ( widget->style->fg_gc[widget->state],  &gcvalues, GDK_GC_FOREGROUND);
    /*
     gdk_draw_line (widget->window,
                    widget->style->fg_gc[widget->state],
                    widget->allocation.width/2,
                    2,
                    widget->allocation.width/2,
                    widget->allocation.height-2);
    */

}


static gboolean
on_OuterFrame_expose_event             (GtkWidget       *widget,
                                        GdkEventExpose  *event,
                                        gpointer         user_data)
{
  GdkGCValues gcvalues;
  //GdkColor linecolor = {0, 155*256, 174*256, 185*256};
  GdkColor linecolor = {0, 78*256, 87*256, 93*256};
  GdkColor lightcolor = {0, 245*256, 244*256, 237*256};
  GdkColor darkcolor = {0, 166*256, 166*256, 152*256};
  //GdkColor darkcolor = {0, 196*256, 196*256, 184*256};

  g_return_val_if_fail (widget != NULL, FALSE);
  g_return_val_if_fail (event != NULL, FALSE);

  if (event->count > 0) return FALSE;

  gdk_window_clear_area (widget->window,
                         0,
                         0,
                         widget->allocation.width,
                         widget->allocation.height);

  gdk_gc_get_values  ( widget->style->fg_gc[widget->state], &gcvalues);
  gdk_gc_set_rgb_fg_color ( widget->style->fg_gc[widget->state], &linecolor );

  gdk_draw_rectangle (widget->window,
                 widget->style->fg_gc[widget->state],
                 FALSE,
                 0,
                 0,
                 widget->allocation.width-1,
                 widget->allocation.height-1);

  gdk_gc_set_rgb_fg_color ( widget->style->fg_gc[widget->state], &lightcolor );

  gdk_draw_line (widget->window,
                 widget->style->fg_gc[widget->state],
                 1,
                 1,
                 widget->allocation.width-2,
                 1);

  gdk_draw_line (widget->window,
                 widget->style->fg_gc[widget->state],
                 1,
                 1,
                 1,
                 widget->allocation.height-2);

  gdk_gc_set_rgb_fg_color ( widget->style->fg_gc[widget->state], &darkcolor );

  gdk_draw_line (widget->window,
                 widget->style->fg_gc[widget->state],
                 widget->allocation.width-2,
                 1,
                 widget->allocation.width-2,
                 widget->allocation.height-2);

  gdk_draw_line (widget->window,
                 widget->style->fg_gc[widget->state],
                 1,
                 widget->allocation.height-2,
                 widget->allocation.width-2,
                 widget->allocation.height-2);

  gdk_gc_set_values  ( widget->style->fg_gc[widget->state],  &gcvalues, GDK_GC_FOREGROUND);

  return FALSE;
}

static gboolean
on_SystemMenu_press_event              (GtkWidget       *widget,
                                        GdkEventButton  *event,
                                        gpointer         user_data)
{
    TRealIIimPCWin *pc = (TRealIIimPCWin*)user_data;
    if (pc) {
        if (event->type == GDK_BUTTON_PRESS) {
            if (pc->pMenu == NULL)
                pc->pMenu = create_iiimPCWinSysMenu(pc);

            GdkEventButton *bevent = (GdkEventButton *) event;
            pc->poping_up = 0;
            updateMenuActivates(pc);
            updateMenuStates(pc);
            pc->poping_up = 1;
            gtk_menu_popup (GTK_MENU (pc->pMenu->iiimPCWinSysMenu),
                            NULL, NULL, NULL, NULL,
                            bevent->button, bevent->time);

            return TRUE;
        }
    }

    return FALSE;
}


static gboolean
on_Candidate_press_event               (GtkWidget       *widget,
                                        GdkEventButton  *event,
                                        gpointer         user_data)
{
    int i;
    TRealIIimPCWin *pc = (TRealIIimPCWin*)user_data;

    if (pc) {
        for (i=0; i < pc-> candidatesCount; ++i) {
            if (pc->candidatesLabels[i] == widget) {
                DEBUG_printf("Candidate #%d selected\n", i);
                if (pc->process_select_request != NULL)
                    pc->process_select_request(pc, i);
            }
        }
    }
}

static void
pc_create_sys_button_dragging_area(TRealIIimPCWin* pc, GtkWidget* parent_box)
{
    GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
    gtk_box_pack_start (GTK_BOX(parent_box), vbox, FALSE, FALSE, 0);
    gtk_widget_show(vbox);

        pc->sysMenuButton = gtk_flat_button_new ();
        gtk_box_pack_start (GTK_BOX (vbox), pc->sysMenuButton, FALSE, FALSE, 0);
        gtk_flat_button_set_alignment(GTK_FLAT_BUTTON(pc->sysMenuButton), 0.5, 0.5);
        GTK_WIDGET_UNSET_FLAGS (pc->sysMenuButton, GTK_CAN_FOCUS);
        if (is_pc_show_sys_button(pc)) gtk_widget_show(pc->sysMenuButton);

            GtkWidget* imageSysMenuButton = (GtkWidget *)create_pixmap(pc->preeditWin, "SysBar.png");
            gtk_container_add (GTK_CONTAINER (pc->sysMenuButton), imageSysMenuButton);
            gtk_misc_set_padding(GTK_MISC(imageSysMenuButton), 0, 0);
            gtk_widget_show(imageSysMenuButton);
            g_signal_connect ((gpointer) pc->sysMenuButton, "button_press_event", G_CALLBACK (on_SystemMenu_press_event), pc);

        pc->dragging_area = gtk_drawing_area_new();
        gtk_widget_set_events(pc->dragging_area, GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK);
        gtk_box_pack_start (GTK_BOX (vbox), pc->dragging_area, TRUE, TRUE, 0);
        gtk_widget_set_size_request(pc->dragging_area, 10, 10);
        gtk_widget_show(pc->dragging_area);

        g_signal_connect ((gpointer)pc->dragging_area, "expose_event",  G_CALLBACK(on_DraggingArea_expose_event), pc);
        //g_signal_connect_after ((gpointer)pc->preeditWin, "realize",  G_CALLBACK(after_pc_dragging_area_realized), pc);
}

static void
pc_create_page_buttons(TRealIIimPCWin* pc, GtkWidget* parent_box)
{
    int pgop;
    GtkWidget *inner_page_box, *image_page_button, *vseperator;

    pc->pageButtonBox = gtk_hbox_new(FALSE, 0);
    gtk_box_pack_start (GTK_BOX (parent_box), pc->pageButtonBox, FALSE, FALSE, 0);
    if (is_pc_show_page_buttons(pc))
        gtk_widget_show(pc->pageButtonBox);
    else
        gtk_widget_hide(pc->pageButtonBox);

      vseperator = gtk_vseparator_new ();
      if (is_pc_candidates_vertical(pc))
          gtk_box_pack_end(GTK_BOX(pc->pageButtonBox), vseperator, FALSE, FALSE, 1);
      else
          gtk_box_pack_start(GTK_BOX(pc->pageButtonBox), vseperator, FALSE, FALSE, 1);
      gtk_widget_show(vseperator);


      if (is_pc_candidates_vertical(pc))
          inner_page_box = gtk_vbox_new (FALSE, 0);
      else
          inner_page_box = gtk_hbox_new (FALSE, 0);
      gtk_box_pack_start(GTK_BOX(pc->pageButtonBox), inner_page_box, FALSE, FALSE, 0);
      gtk_widget_show(inner_page_box);

        for (pgop=1; pgop <= 2; ++pgop) { //never show first page button and last page button
            pc->buttonPageRequest[pgop] = gtk_flat_button_new ();
            gtk_box_pack_start(GTK_BOX(inner_page_box), pc->buttonPageRequest[pgop], FALSE, FALSE, 0);
            gtk_flat_button_set_alignment(GTK_FLAT_BUTTON(pc->buttonPageRequest[pgop]), 0.5, 0.5);
            GTK_WIDGET_UNSET_FLAGS (pc->buttonPageRequest[pgop], GTK_CAN_FOCUS);
            gtk_widget_show(pc->buttonPageRequest[pgop]);
            g_signal_connect ((gpointer)pc->buttonPageRequest[pgop], "button_press_event",
                              G_CALLBACK (on_PageRequest_press_event),
                              pc);

                if (is_pc_candidates_vertical(pc))
                    image_page_button = (GtkWidget*)create_pixmap (pc->preeditWin, pgop_icon_vertical_names[pgop]);
                else
                    image_page_button = (GtkWidget*)create_pixmap (pc->preeditWin, pgop_icon_horizental_names[pgop]);
                gtk_container_add (GTK_CONTAINER(pc->buttonPageRequest[pgop]), image_page_button);
                gtk_misc_set_padding(GTK_MISC(image_page_button), 0, 0);
                gtk_widget_show(image_page_button);
        }
}

static GtkWidget*      // return the inner most widget as a container
pc_create_window_frames(TRealIIimPCWin* pc, GtkWidget* window)
{
    GtkWidget *outerFrame;
    GtkWidget *innerFrame;

    outerFrame = gtk_frame_new (NULL);
    gtk_container_add(GTK_CONTAINER(window), outerFrame);
    gtk_frame_set_shadow_type (GTK_FRAME (outerFrame), GTK_SHADOW_NONE);
    gtk_widget_show(outerFrame);
    g_signal_connect ((gpointer) outerFrame, "expose_event", G_CALLBACK (on_OuterFrame_expose_event), pc);

      innerFrame = gtk_frame_new (NULL);
      gtk_container_add (GTK_CONTAINER (outerFrame), innerFrame);
      gtk_frame_set_shadow_type (GTK_FRAME (innerFrame), GTK_SHADOW_NONE);
      gtk_widget_show(innerFrame);

    return innerFrame;
}

static void
pc_create_candidate_title_box(TRealIIimPCWin* pc, GtkWidget* parent_box)
{
    GtkWidget* title_seperator;

    if (is_pc_candidates_vertical(pc)) {
        pc->candidatesTitleBox = gtk_vbox_new (FALSE, 0);
        title_seperator = gtk_vseparator_new();
    } else {
        pc->candidatesTitleBox = gtk_hbox_new (FALSE, 0);
        title_seperator = gtk_vseparator_new();
    }
    if (is_pc_candidates_title_left_or_upper(pc))
        gtk_box_pack_start(GTK_BOX(parent_box), pc->candidatesTitleBox, FALSE, FALSE, 2);
    else
        gtk_box_pack_end(GTK_BOX(parent_box), pc->candidatesTitleBox, FALSE, FALSE, 2);
    gtk_widget_hide(pc->candidatesTitleBox);

      if (is_pc_candidates_title_left_or_upper(pc))
          gtk_box_pack_end(GTK_BOX(pc->candidatesTitleBox), title_seperator, FALSE, FALSE, 0);
      else
          gtk_box_pack_start(GTK_BOX(pc->candidatesTitleBox), title_seperator, FALSE, FALSE, 0);
      gtk_widget_show(title_seperator);

      pc->candidatesTitleLabel = gtk_label_new ("");
      gtk_box_pack_start (GTK_BOX (pc->candidatesTitleBox), pc->candidatesTitleLabel, FALSE, FALSE, 2);
      gtk_misc_set_alignment (GTK_MISC (pc->candidatesTitleLabel), 0.5, 0.5);
      /*gtk_label_set_justify (GTK_LABEL (pc->candidatesTitleLabel), GTK_JUSTIFY_RIGHT);
      gtk_misc_set_padding (GTK_MISC (pc->candidatesTitleLabel), 1, 1);*/
      gtk_widget_show(pc->candidatesTitleLabel);
}

static void
pc_create_preedit_label(TRealIIimPCWin* pc, GtkWidget* parent_box)
{
    pc->preeditLabel = im_label_new ("");
    gtk_box_pack_start (GTK_BOX (parent_box), pc->preeditLabel, TRUE, TRUE, 0);
    gtk_misc_set_alignment (GTK_MISC (pc->preeditLabel), 0, 0.5);
    gtk_misc_set_padding (GTK_MISC (pc->preeditLabel), 5, 5);
    gtk_widget_show(pc->preeditLabel);
}

static void
pc_create_composite_windows(TRealIIimPCWin* pc)
{
    GtkWidget *window_frame;
    GtkWidget *outer_vbox;
    GtkWidget *upper_hbox;
    GtkWidget *preedit_candidate_seperator;
    GtkWidget *lower_hbox;
    GtkWidget *candidates_padding_area;

    pc->preeditWin = gtk_window_new (GTK_WINDOW_POPUP);
    gtk_widget_realize(pc->preeditWin);
    gtk_window_set_type_hint (GTK_WINDOW (pc->preeditWin), GDK_WINDOW_TYPE_HINT_UTILITY);
    gtk_window_set_resizable(GTK_WINDOW(pc->preeditWin), FALSE);

      window_frame = pc_create_window_frames(pc, pc->preeditWin);

        outer_vbox = gtk_vbox_new (FALSE, 0);
        gtk_container_add (GTK_CONTAINER (window_frame), outer_vbox);
        gtk_widget_show(outer_vbox);

          upper_hbox = gtk_hbox_new (FALSE, 0);
          gtk_box_pack_start (GTK_BOX (outer_vbox), upper_hbox, FALSE, FALSE, 0);
          gtk_widget_show(upper_hbox);

            pc_create_sys_button_dragging_area(pc, upper_hbox);
            pc_create_preedit_label(pc, upper_hbox);
            pc_create_candidate_title_box(pc, upper_hbox);
            pc_create_page_buttons(pc, upper_hbox);

          preedit_candidate_seperator = gtk_hseparator_new ();
          gtk_box_pack_start (GTK_BOX (outer_vbox), preedit_candidate_seperator, FALSE, FALSE, 0);
          gtk_widget_show(preedit_candidate_seperator);

          lower_hbox = gtk_hbox_new(FALSE, 0);
          gtk_box_pack_start (GTK_BOX (outer_vbox), lower_hbox, FALSE, FALSE, 0);
          gtk_widget_show(lower_hbox);

            pc->candidatesPackBox = gtk_hbox_new(FALSE, 0);
            gtk_box_pack_start(GTK_BOX (lower_hbox), pc->candidatesPackBox, FALSE, FALSE, 0);
            gtk_widget_show(pc->candidatesPackBox);

            candidates_padding_area = gtk_label_new(" ");
            gtk_box_pack_end (GTK_BOX (lower_hbox), candidates_padding_area, TRUE, TRUE, 1);
            gtk_widget_show(candidates_padding_area);
}

static void
pc_create_seperate_preedit_window(TRealIIimPCWin* pc)
{
    GtkWidget *window_frame;
    GtkWidget *preedit_box;

    pc->preeditWin = gtk_window_new (GTK_WINDOW_POPUP);
    gtk_widget_realize(pc->preeditWin);
    gtk_window_set_type_hint(GTK_WINDOW(pc->preeditWin), GDK_WINDOW_TYPE_HINT_UTILITY);
    gtk_window_set_resizable(GTK_WINDOW(pc->preeditWin), FALSE);

      window_frame = pc_create_window_frames(pc, pc->preeditWin);

        preedit_box = gtk_hbox_new (FALSE, 0);
        gtk_container_add (GTK_CONTAINER (window_frame), preedit_box);
        gtk_widget_show(preedit_box);

          pc_create_sys_button_dragging_area(pc, preedit_box);
          pc_create_preedit_label(pc, preedit_box);
}

static void
pc_create_seperate_candidates_window(TRealIIimPCWin* pc)
{
    GtkWidget *window_frame;
    GtkWidget *candi_win_box;
    GtkWidget *candidates_padding_area;
    GtkWidget *lower_box;

    pc->candidatesWin = gtk_window_new (GTK_WINDOW_POPUP);
    gtk_widget_realize(pc->candidatesWin);
    gtk_window_set_type_hint (GTK_WINDOW (pc->candidatesWin), GDK_WINDOW_TYPE_HINT_UTILITY);
    gtk_window_set_resizable(GTK_WINDOW(pc->candidatesWin), FALSE);

      window_frame = pc_create_window_frames(pc, pc->candidatesWin);

      if (!is_pc_candidates_vertical(pc)) {
        candi_win_box = gtk_hbox_new (FALSE, 0);
        gtk_container_add (GTK_CONTAINER(window_frame), candi_win_box);
        gtk_widget_show(candi_win_box);

          pc_create_candidate_title_box(pc, candi_win_box);

          pc->candidatesPackBox = gtk_hbox_new (FALSE, 0);
          gtk_box_pack_start (GTK_BOX (candi_win_box), pc->candidatesPackBox, FALSE, FALSE, 0);
          gtk_widget_show(pc->candidatesPackBox);

          candidates_padding_area = gtk_label_new(" ");
          gtk_box_pack_start (GTK_BOX(candi_win_box), candidates_padding_area, TRUE, TRUE, 1);
          gtk_widget_show(candidates_padding_area);

          pc_create_page_buttons(pc, candi_win_box);

      } else {
        candi_win_box = gtk_vbox_new (FALSE, 0);
        gtk_container_add (GTK_CONTAINER (window_frame), candi_win_box);
        gtk_widget_show(candi_win_box);

          pc_create_candidate_title_box(pc, candi_win_box);

          lower_box = gtk_hbox_new (FALSE, 0);
          gtk_box_pack_start(GTK_BOX(candi_win_box), lower_box, FALSE, FALSE, 0);
          gtk_widget_show(lower_box);

            pc_create_page_buttons(pc, lower_box);

            pc->candidatesPackBox = gtk_vbox_new (FALSE, 0);
            gtk_box_pack_start(GTK_BOX(lower_box), pc->candidatesPackBox, FALSE, FALSE, 0);
            gtk_widget_show(pc->candidatesPackBox);
    }
}

static void
restoreInternalData(TRealIIimPCWin* pc)
{
    int i;
    const char* cls[MAX_CANDIDATE_COUNT];


    for (i=0; i < MAX_CANDIDATE_COUNT; ++i)
        cls[i] = pc->bufCandidates[i];

    updatePreeditData(pc, pc->bufPreedit, pc->preeditCaret, pc->preeditCandidateCaret, 0, 0);

    updateCandidatesData(pc, pc->candidatesCount, cls, pc->bufCandidateTitle,
                         pc->firstCandidat, pc->currentCandidateInWindow, pc->totalCandidates,
                         NULL, NULL);

    movePreedit(pc, pc->cursor_x, pc->cursor_y, pc->cursor_w, pc->cursor_h);

    if (pc->preeditShow)
        showPreedit(pc);

    if (pc->candidatesShow)
        showCandidates(pc);
}

static void
adjustCandidateWindowAtomic(TRealIIimPCWin* pc,
                            int candx, int candy, int candw, int candh,
                            int predx, int predy, int predw, int predh)
{
    if (is_pc_composite(pc)) return;

    int  newx, newy, sw, sh, cand_idx;

    sw = gdk_screen_get_width  ( gtk_widget_get_screen(pc->preeditWin) );
    sh = gdk_screen_get_height ( gtk_widget_get_screen(pc->preeditWin) );

    if (CANDIDATES_POSITION_PREEDIT_RIGHT == get_pc_candidates_position(pc) ) {
        newx = predx + predw + WINDOW_GAP_VERTICAL;
        newy = predy;

        if (newx + candw > sw) {
            newy = predy + predh + WINDOW_GAP_VERTICAL;
            if (newy + candh > sh)
                newy = predy - WINDOW_GAP_VERTICAL - candh;
            if (newy < 0) newy = 0;

            newx = sw - candw - WINDOW_GAP_VERTICAL;
        }
        if (newx < 0) newx = 0;

        if (newy + candh > sh)
           newy = sh - candh;
        if (newy < 0) newy = 0;

        if (newx != candx || newy != candy)
            gtk_window_move(GTK_WINDOW(pc->candidatesWin), newx, newy);

    } else {
        switch ( get_pc_candidates_position(pc) ) {
            case CANDIDATES_POSITION_PREEDIT_CARET:
                cand_idx = pc->preeditCaret; break;
            case CANDIDATES_POSITION_IME:
                cand_idx = pc->preeditCandidateCaret; break;
            default:
                cand_idx = 0;
        }
        getPreeditBox(pc, cand_idx, &predx, &predy, &predw, &predh);

        gtk_widget_translate_coordinates(pc->candidatesPackBox, pc->candidatesWin, 0, 0, &newx, &newy);

        newx = predx - newx;
        if (newx + candw > sw)
            newx = sw - candw;
        if (newx < 0) newx = 0;

        if (predy < pc->cursor_y) {
            newy = predy - candh - WINDOW_GAP_VERTICAL;
        } else {
            newy = predy + predh + WINDOW_GAP_VERTICAL;
            if (newy + candh > sh)
                newy = pc->cursor_y - candh - WINDOW_GAP_VERTICAL;
        }

        if (newy < 0) newy = 0;

        if (newx != candx || newy != candy)
            gtk_window_move(GTK_WINDOW(pc->candidatesWin), newx, newy);
    }
}

static void
adjustCandidateWindow(TRealIIimPCWin* pc,
                      int candx, int candy, int candw, int candh)
{
    int predx, predy, predw, predh;

    gtk_window_get_position(GTK_WINDOW(pc->preeditWin), &predx, &predy);
    predw = pc->preeditWin->allocation.width;
    predh = pc->preeditWin->allocation.height;
    adjustCandidateWindowAtomic(pc, candx, candy, candw, candh, predx, predy, predw, predh);
}

static int
adjustPCWindows(TRealIIimPCWin* pc, int predx, int predy, int predw, int predh)
{
    int newx, newy, sw, sh, candx, candy, candw, candh;

    newx = pc->cursor_x;
    newy = pc->cursor_y + WINDOW_GAP_VERTICAL + pc->cursor_h;

    sw = gdk_screen_get_width  ( gtk_widget_get_screen(pc->preeditWin) );
    sh = gdk_screen_get_height ( gtk_widget_get_screen(pc->preeditWin) );

    if (newx + predw > sw)
         newx = sw - predw - WINDOW_GAP_HORIZENTAL;
    if (newx < 0)
        newx = 0;
    if (newy + predh > sh)
        newy = pc->cursor_y - predh - WINDOW_GAP_VERTICAL;
    if (newy < 0) newy = 0;

    if (newx != predx || newy != predy) {
        gtk_window_move(GTK_WINDOW(pc->preeditWin), newx, newy);
        DEBUG_printf("++++++++++Move preedit window to new position, waiting for next configuration change!\n");
        return 0; //waiting for next configure event
    }

    if (!is_pc_composite(pc)) {
        DEBUG_printf("++++++++++Try to recalculate new candiates window position!\n");
        gtk_window_get_position(GTK_WINDOW(pc->candidatesWin), &candx, &candy);
        adjustCandidateWindowAtomic(pc,
                                    candx, candy, pc->candidatesWin->allocation.width, pc->candidatesWin->allocation.height,
                                    predx, predy, predw, predh);
    }

    return 1;
}

static gboolean
on_preeditWin_configure_event          (GtkWidget       *widget,
                                        GdkEventConfigure *event,
                                        gpointer         user_data)
{
    DEBUG_printf("+++++++++++++++++++preedit window configuration changed!!! (x,y,w,h)=(%d,%d,%d,%d)\n",
                                     event->x, event->y, event->width, event->height);

    TRealIIimPCWin* pc = (TRealIIimPCWin*)user_data;
    adjustPCWindows(pc, event->x, event->y, event->width, event->height);
    return FALSE;
}

static gboolean
on_candidateswin_configure_event       (GtkWidget       *widget,
                                        GdkEventConfigure *event,
                                        gpointer         user_data)
{
    DEBUG_printf("        ----------candidates window configuration changed!!! (x,y,w,h)=(%d,%d,%d,%d)\n",
                                    event->x, event->y, event->width, event->height);

    TRealIIimPCWin* pc = (TRealIIimPCWin*)user_data;
    adjustCandidateWindow(pc, event->x, event->y, event->width, event->height);
    return FALSE;
}

static void
createWindows(TRealIIimPCWin* pc)
{
    if (is_pc_composite(pc)) {
        pc_create_composite_windows(pc);
    } else {
        pc_create_seperate_preedit_window(pc);
        pc_create_seperate_candidates_window(pc);
    }

    restoreInternalData(pc);

    g_signal_connect((gpointer)pc->preeditWin,
                     "button_press_event",
                     G_CALLBACK (onMouseButtonPress),
                     pc);

    g_signal_connect((gpointer)pc->preeditWin,
                     "button_release_event",
                     G_CALLBACK (onMouseButtonRelease),
                     pc);

    g_signal_connect((gpointer)pc->preeditWin,
                     "motion-notify-event",
                     G_CALLBACK (onWindowPointerMove),
                     pc);

    g_signal_connect ((gpointer)pc->preeditWin,
                      "configure_event",
                      G_CALLBACK (on_preeditWin_configure_event),
                      pc);

    if (pc->candidatesWin) {
        g_signal_connect ((gpointer)pc->candidatesWin ,
                          "configure_event",
                          G_CALLBACK (on_candidateswin_configure_event),
                          pc);
    }
}


static void destroyWindows(TRealIIimPCWin* pc)
{
    int i = 0;

    if (pc) {
        if (pc->preeditWin) gtk_widget_hide_all(pc->preeditWin);
        if (pc->candidatesWin) gtk_widget_hide_all(pc->candidatesWin);

        if (pc->preeditWin) gtk_widget_destroy(pc->preeditWin);
        if (pc->candidatesWin) gtk_widget_destroy(pc->candidatesWin);

        pc->preeditWin = NULL;
        pc->preeditLabel = NULL;

        pc->candidatesWin = NULL;
        pc->candidatesPackBox = NULL;

        for (i=0; i < MAX_CANDIDATE_COUNT; ++i)
            pc->candidatesLabels[i] = NULL;
        for (i=0; i < 4; ++i)
            pc->buttonPageRequest[i] = NULL;

        pc->sysMenuButton = NULL;
        pc->candidatesTitleBox = NULL;
        pc->candidatesTitleLabel = NULL;

        pc->preeditRealShow = 0;
        pc->candidatesRealShow = 0;
    }
}

TIIimPCWin createStockPreeditCandidatesWindows(int stockStyle)
{
    TRealIIimPCWin* pc = (TRealIIimPCWin*)calloc(1, sizeof(TRealIIimPCWin));
    pc->cursor_x = pc->cursor_y = 0x80000000; //move to almost the center bottom

    if (stockStyle < PC_STYLE_CUSTOM || stockStyle > PC_STYLE_MODERN)
        stockStyle = PC_STYLE_MODERN;
    pc->style.type = stockStyle;
    pc->style.customOption = predefinedStyles[stockStyle];

    createWindows(pc);

    return pc;
}

TIIimPCWin createPreeditCandidatesWindows(TPCStyleAtomic* pstyle)
{
    TRealIIimPCWin* pc = (TRealIIimPCWin*)calloc(1, sizeof(TRealIIimPCWin));
    pc->cursor_x = pc->cursor_y = 0x80000000; //move to almost the center bottom

    if (pstyle == NULL) {
        pc->style.type = PC_STYLE_MODERN;
        pc->style.customOption = predefinedStyles[pc->style.type];
    } else {
        pc->style.type = PC_STYLE_CUSTOM;
        pc->style.customOption = *pstyle;
    }

    createWindows(pc);

    return pc;
}

void recreateWindows(TIIimPCWin vpc, TPCStyleOption* pstyle)
{
    DEBUG_printf("recreating windows...");
    TRealIIimPCWin* pc = (TRealIIimPCWin*) vpc;

    if (pstyle == NULL) {
        pc->style.type = PC_STYLE_CUSTOM;
        pc->style.customOption = predefinedStyles[pc->style.type];
    } else {
        pc->style = *pstyle;
    }
    destroyWindows(pc);
    createWindows(pc);
    DEBUG_printf("done!\n");
}

void showPreedit(TIIimPCWin vpc)
{
    TRealIIimPCWin* pc = vpc;

    pc->preeditShow = 1;
    if (im_label_get_text_length(IM_LABEL(pc->preeditLabel)) == 0) {
        gtk_widget_hide (pc->preeditWin);
        pc->preeditRealShow = 0;
        if (is_pc_composite(pc))
            gtk_window_set_resizable(GTK_WINDOW(pc->preeditWin), FALSE);
    } else {
        if (pc->preeditRealShow > 1) {
            if (is_pc_composite(pc))
                gtk_window_set_resizable(GTK_WINDOW(pc->preeditWin), TRUE);
        } else  {
            pc->preeditRealShow = 1;
            movePreedit(pc, pc->cursor_x, pc->cursor_y, pc->cursor_w, pc->cursor_h);
            gtk_widget_show(pc->preeditWin);
        }
    }
}

void hidePreedit(TIIimPCWin vpc)
{
    TRealIIimPCWin* pc = vpc;

    if (pc->poping_up != 1) {
        pc->preeditShow = 0;
        pc->preeditRealShow = 0;

        gtk_widget_hide (pc->preeditWin);
        updatePreeditData(vpc, "", 0, 0, 0, NULL);
        if (is_pc_composite(pc)) {
            gtk_window_set_resizable(GTK_WINDOW(pc->preeditWin), FALSE);
        }
    }
}

static void
getPreeditBox(TRealIIimPCWin* pc, int idx, int *px, int *py, int *pw, int *ph)
{
    int sx, sy;

    im_label_get_caret_rect(IM_LABEL(pc->preeditLabel), idx, px, py, pw, ph);
    DEBUG_printf("Pango rect of label at index %d is (xywh) (%d %d %d %d)", idx, *px, *py, *pw, *ph);

    gtk_widget_translate_coordinates(pc->preeditLabel, pc->preeditWin, 0, 0, &sx, &sy);
    *px += sx;

    gtk_window_get_position(GTK_WINDOW(pc->preeditWin), &sx, &sy);
    *px += sx;
    *py = sy;
    *ph = pc->preeditWin->allocation.height;

    DEBUG_printf("screen (%d %d)\n", *px, *py);
}


void movePreedit(TIIimPCWin vpc, int cursor_x, int cursor_y, int cursor_w, int cursor_h)
{
    TRealIIimPCWin* pc = vpc;
    int predx, predy, predw, predh;

    int sw = gdk_screen_get_width  ( gtk_widget_get_screen(pc->preeditWin) );
    int sh = gdk_screen_get_height ( gtk_widget_get_screen(pc->preeditWin) );

    if (pc && pc->preeditWin) {
        if (cursor_x == 0x80000000)
            cursor_x = sw/2 - 100;
        if (cursor_y == 0x80000000)
            cursor_y = sh - 180;

        pc->cursor_x = cursor_x;
        pc->cursor_y = cursor_y;
        pc->cursor_w = cursor_w;
        pc->cursor_h = cursor_h;

        gtk_window_get_position(GTK_WINDOW(pc->preeditWin), &predx, &predy);
        adjustPCWindows(pc, predx, predy, pc->preeditWin->allocation.width, pc->preeditWin->allocation.height);
    }
}

void movePreeditCaret(TIIimPCWin vpc, int caret)
{
    int candx, candy, candw, candh;

    TRealIIimPCWin* pc = vpc;
    if (pc && pc->preeditLabel) {
        pc->preeditCaret = caret;
        im_label_set_caret_index (IM_LABEL(pc->preeditLabel), caret);

        if (pc->candidatesWin) {
            gtk_window_get_position(GTK_WINDOW(pc->candidatesWin), &candx, &candy);
            adjustCandidateWindow(pc, candx, candy, pc->candidatesWin->allocation.width, pc->candidatesWin->allocation.height);
        }
    }
}

void setPreeditCandidateStart(TIIimPCWin vpc, int candidate_start)
{
    int candx, candy, candw, candh;

    TRealIIimPCWin* pc = vpc;
    if (pc) {
        pc->preeditCandidateCaret = candidate_start;
        if (pc->candidatesWin) {
            gtk_window_get_position(GTK_WINDOW(pc->candidatesWin), &candx, &candy);
            adjustCandidateWindow(pc, candx, candy, pc->candidatesWin->allocation.width, pc->candidatesWin->allocation.height);
        }
    }
}

static const char *s_escape_lt = "&lt;";
static const char *s_escape_gt = "&gt;";
static const char *s_escape_amp = "&amp;";
#define s_escape_lt_len  4
#define s_escape_gt_len  4
#define s_escape_amp_len 5

static char color_span_start[256];
static char*color_span_end="</span>";

//suppose no embeded feedbacks and only foregroundcolor feedbacks
//painful in programming in C, C++ is better, if dealing with embeded feedbacks.
//dst != NULL, src != 0
static void
my_escape_text(char dst[], const char* src, int nfbs, ImeFeedbackRec* fbs, int dst_byte_len)
{
    char* p = dst;
    const char *pchar;
    int idx, current_fb=0, charlen;

    for (idx = current_fb = 0; *src != 0 && dst_byte_len > 1; ++idx) { //leave one for max utf8 len
        if (current_fb < nfbs && idx == fbs[current_fb].start) { //starting tag
            snprintf(color_span_start, 256, "<span color=\"#%06X\">", (fbs[current_fb].value & 0x00FFFFFF));
            charlen = strlen(color_span_start);
            if (dst_byte_len <= charlen)
                break;
            strcpy(p, color_span_start);
            dst_byte_len -= charlen;
            p += charlen;
        }

        //copy the char
        if (*src == '<') {
            charlen = s_escape_lt_len;
            pchar = s_escape_lt;
            ++src;
        } else if (*src == '&') {
            charlen = s_escape_amp_len;
            pchar = s_escape_amp;
            ++src;
        } else {
            pchar = src;
            src = g_utf8_next_char(src);
            charlen = src - pchar;
        }

        if (dst_byte_len > charlen) {
            strncpy(p, pchar, charlen);
            p += charlen;
            dst_byte_len -= charlen;
        } else {
            break;
        }

        if ((current_fb < nfbs) && (idx == fbs[current_fb].start + fbs[current_fb].length - 1)) { //ending tag
            charlen = strlen(color_span_end);
            if (dst_byte_len <= charlen)
                break;
            strcpy(p, color_span_end);
            dst_byte_len -= charlen;
            p += charlen;
            for(++current_fb; current_fb < nfbs &&  fbs[current_fb].start <= idx; ++current_fb)
                ;
        }
    }
    *p = 0;
}

static void
updatePreeditData(TIIimPCWin vpc, const char* label, int caret, int candidate_start, int nfbs, ImeFeedbackRec* fbs)
{
    const char* src;
    char * dst;
    int i, sz;
    TRealIIimPCWin* pc = vpc;

    if (pc && pc->preeditLabel) {
        pc->preeditCaret = caret;
        pc->preeditCandidateCaret = candidate_start;
        if (label != pc->bufPreedit) {
            pc->bufPreedit[0] = 0;
            pc->bufPreedit[MAX_LABELSTRING_LEN-1] = 0;
            my_escape_text(pc->bufPreedit, label, nfbs, fbs, MAX_LABELSTRING_LEN-7);
            DEBUG_printf("+++++++++++++++++|escaped string %s for %s|++++++++++++++++\n", pc->bufPreedit, label);
        }

        pc->preeditCandidateCaret = candidate_start;
        im_label_set_markup(IM_LABEL(pc->preeditLabel), pc->bufPreedit);
        im_label_set_caret_index (IM_LABEL(pc->preeditLabel), caret);
    }
}

void
updatePreedit(TIIimPCWin vpc, const char* label, int caret, int candidate_start, int nfbs, ImeFeedbackRec* fbs)
{
    TRealIIimPCWin* pc = vpc;

    updatePreeditData(vpc, label, caret, candidate_start, nfbs, fbs);

    if (pc->bufPreedit[0] != 0 && pc->preeditShow && pc->preeditRealShow == 1)
        pc->preeditRealShow = 2;

    if (pc->preeditShow)
        showPreedit(pc);
}

void showCandidates(TIIimPCWin vpc)
{
    int i;
    TRealIIimPCWin* pc = vpc;

    pc->candidatesShow = 1;

    if (pc->candidatesCount == 0) {
        if (!is_pc_composite(pc))
            gtk_widget_hide(pc->candidatesWin);
        pc->candidatesRealShow = 0;
        gtk_widget_hide(pc->candidatesPackBox);
    } else {
        if (pc->candidatesRealShow > 1) { 
            if (!is_pc_composite(pc) && !is_pc_candidates_vertical(pc))
                gtk_window_set_resizable(GTK_WINDOW(pc->candidatesWin), TRUE);
        } else {
            pc->candidatesRealShow = 1;
            gtk_widget_show(pc->candidatesPackBox);
            if (!is_pc_composite(pc) && !is_pc_candidates_vertical(pc))
                gtk_window_set_resizable(GTK_WINDOW(pc->candidatesWin), FALSE);
            if (!is_pc_composite(pc))
                gtk_widget_show(pc->candidatesWin);
        }
    }

    if (is_pc_show_candidates_title(pc) && pc->bufCandidateTitle[0] != 0)
        gtk_widget_show(pc->candidatesTitleBox);
    else
        gtk_widget_hide(pc->candidatesTitleBox);

    if (is_pc_show_page_buttons(pc))
        gtk_widget_show(pc->pageButtonBox);
    else
        gtk_widget_hide(pc->pageButtonBox);
}

static void
updateCandidatesData(TIIimPCWin vpc, 
                     int num, const char* candidates[], const char* title, 
                     int firstIdx, int focusIdx, int total, 
                     int *p_fb_counts, ImeFeedbackRec **pfbs)
{
    int i, len;
    TRealIIimPCWin* pc = vpc;

    pc->firstCandidat = firstIdx;
    pc->currentCandidateInWindow = focusIdx;
    pc->totalCandidates = total;

    //gtk_widget_set_sensitive(pc->buttonPageRequest[0], (firstIdx > 0));
    gtk_widget_set_sensitive(pc->buttonPageRequest[1], (firstIdx > 0));
    gtk_widget_set_sensitive(pc->buttonPageRequest[2], (firstIdx+num < total));
    //gtk_widget_set_sensitive(pc->buttonPageRequest[3], (firstIdx+num < total));

    if (title != pc->bufCandidateTitle) {
        pc->bufCandidateTitle[0] = 0;
        if (title) {
            char* escape_str = g_markup_escape_text(title, strlen(title));
            strncpy(pc->bufCandidateTitle, escape_str, MAX_LABELSTRING_LEN-1);
            g_free(escape_str);
        }
    }
    gtk_label_set_markup(GTK_LABEL(pc->candidatesTitleLabel), pc->bufCandidateTitle);

    pc->candidatesCount = (num <= MAX_CANDIDATE_COUNT)?(num):(MAX_CANDIDATE_COUNT);
    for (i=0; i < pc->candidatesCount; ++i) {
        if (pc->bufCandidates[i] != candidates[i]) {
            pc->bufCandidates[i][0] = 0;
            pc->bufCandidates[i][MAX_LABELSTRING_LEN-1] = 0;
            if (candidates[i])
                my_escape_text(pc->bufCandidates[i], candidates[i],
                               (p_fb_counts)?(p_fb_counts[i]):0,
                               (pfbs)?(pfbs[i]):(NULL), MAX_LABELSTRING_LEN-7);
        }

        if (pc->candidatesLabels[i] == NULL) {
            pc->candidatesLabels[i] = gtk_flat_button_new();
            gtk_flat_button_set_alignment(GTK_FLAT_BUTTON(pc->candidatesLabels[i]), 0, 0.5);
            gtk_box_pack_start (GTK_BOX (pc->candidatesPackBox), pc->candidatesLabels[i], TRUE, TRUE, 0);
            GTK_WIDGET_UNSET_FLAGS (pc->candidatesLabels[i], GTK_CAN_FOCUS);
            g_signal_connect ((gpointer)pc->candidatesLabels[i], "button_press_event", G_CALLBACK (on_Candidate_press_event), pc);
        }

        gtk_widget_show(pc->candidatesLabels[i]);
        gtk_flat_button_set_label(GTK_FLAT_BUTTON(pc->candidatesLabels[i]), pc->bufCandidates[i]);
        gtk_widget_show(pc->candidatesLabels[i]);
    }

    for (; i < MAX_CANDIDATE_COUNT; ++i) {
        pc->bufCandidates[i][0] = 0;
        if (pc->candidatesLabels[i] != NULL) {
            gtk_flat_button_set_label(GTK_FLAT_BUTTON(pc->candidatesLabels[i]), pc->bufCandidates[i]);
            gtk_widget_hide(pc->candidatesLabels[i]);
        }
    }
}

void hideCandidates(TIIimPCWin vpc)
{
    int i;
    TRealIIimPCWin* pc = vpc;

    if (pc->poping_up != 1) {
        pc->candidatesShow = 0;
        pc->candidatesRealShow = 0;

        gtk_widget_hide(pc->pageButtonBox);
        gtk_widget_hide(pc->candidatesTitleBox);
        gtk_widget_hide(pc->candidatesPackBox);
        updateCandidatesData(pc, 0, NULL, NULL, 0, 0, 0, 0, NULL);
        if (!is_pc_composite(pc)) {
            gtk_widget_hide(pc->candidatesWin);
            if (!is_pc_candidates_vertical(pc))
                gtk_window_set_resizable(GTK_WINDOW(pc->candidatesWin), FALSE);
        }
    }
}

void
updateCandidates(TIIimPCWin vpc, 
                 int num, const char* candidates[], const char* title, 
                 int firstIdx, int focusIdx, int total, 
                 int *p_fb_counts, ImeFeedbackRec **pfbs)
{
    TRealIIimPCWin* pc = vpc;

    updateCandidatesData(vpc, num, candidates, title, firstIdx, focusIdx, total, p_fb_counts, pfbs);

    if (is_pc_composite(pc) && pc->preeditShow && pc->preeditRealShow == 1) {
        pc->preeditRealShow = 2;
        showPreedit(pc);
        //gtk_window_set_resizable(GTK_WINDOW(pc->preeditWin), TRUE);
    }
   
    if (!is_pc_composite(pc) && num >0 && pc->candidatesRealShow == 1)
        pc->candidatesRealShow = 2;

    if (pc->candidatesShow)
        showCandidates(vpc);
}

static void
updateMenuActivates(TRealIIimPCWin* pc)
{
    TPCOptionMenu* pMenu = pc->pMenu;

    GTK_CHECK_MENU_ITEM (pMenu->RootWindow)->active                  = (pc->style.type == PC_STYLE_ROOT);
    GTK_CHECK_MENU_ITEM (pMenu->ClassicCompositeFollow)->active      = (pc->style.type == PC_STYLE_CLASSIC);
    GTK_CHECK_MENU_ITEM (pMenu->TraditionalStyle)->active        = (pc->style.type == PC_STYLE_TRADITIONAL);
    GTK_CHECK_MENU_ITEM (pMenu->ModernCanidatesHorizental)->active   = (pc->style.type == PC_STYLE_MODERN);
    GTK_CHECK_MENU_ITEM (pMenu->CustomizeStyle)->active              = (pc->style.type == PC_STYLE_CUSTOM);

    GTK_CHECK_MENU_ITEM (pMenu->ShowSystemMenu)->active              = pc->style.customOption.showSystemBar;
    GTK_CHECK_MENU_ITEM (pMenu->FollowCursor)->active                = pc->style.customOption.followCursor;
    GTK_CHECK_MENU_ITEM (pMenu->GroupPreeditCandidates)->active      = pc->style.customOption.composite;

    GTK_CHECK_MENU_ITEM (pMenu->CandidatesHorizental)->active        = !pc->style.customOption.candidateVertical;
    GTK_CHECK_MENU_ITEM (pMenu->CandidatesVertical)->active          = pc->style.customOption.candidateVertical;

    GTK_CHECK_MENU_ITEM (pMenu->AboveCandidates)->active             = (pc->style.customOption.candidateTitlePosition == TITLE_ABOVE_CANDIDATES);
    GTK_CHECK_MENU_ITEM (pMenu->BelowCandidates)->active             = (pc->style.customOption.candidateTitlePosition != TITLE_ABOVE_CANDIDATES);

    GTK_CHECK_MENU_ITEM (pMenu->CandidatesFollowIme)->active         = (pc->style.customOption.candidatePosition == CANDIDATES_POSITION_IME);
    GTK_CHECK_MENU_ITEM (pMenu->CandidatesStickPreedit)->active      = (pc->style.customOption.candidatePosition == CANDIDATES_POSITION_PREEDIT_STARTS);
    GTK_CHECK_MENU_ITEM (pMenu->CandidatesFollowCaret)->active       = (pc->style.customOption.candidatePosition == CANDIDATES_POSITION_PREEDIT_CARET);
    GTK_CHECK_MENU_ITEM (pMenu->CandidatesRight2Preedit)->active     = (pc->style.customOption.candidatePosition == CANDIDATES_POSITION_PREEDIT_RIGHT);

    GTK_CHECK_MENU_ITEM (pMenu->ShowPageControl)->active             = pc->style.customOption.showPageControl;
    GTK_CHECK_MENU_ITEM (pMenu->IgnorCandidatesTitle)->active        = pc->style.customOption.ignoreCandidateTitle;
}

static void
updateMenuStates(TRealIIimPCWin* pc)
{
    TPCOptionMenu* pMenu = pc->pMenu;

    gtk_widget_set_sensitive(pMenu->ShowSystemMenu, (pc->style.type == PC_STYLE_CUSTOM));
    gtk_widget_set_sensitive(pMenu->FollowCursor, (pc->style.type == PC_STYLE_CUSTOM));
    gtk_widget_set_sensitive(pMenu->GroupPreeditCandidates, (pc->style.type == PC_STYLE_CUSTOM));

    gtk_widget_set_sensitive(pMenu->CandidatesDirection, (pc->style.type == PC_STYLE_CUSTOM && !pc->style.customOption.composite));
    gtk_widget_set_sensitive(pMenu->CandidateTitlePosition, (pc->style.type == PC_STYLE_CUSTOM && !pc->style.customOption.composite));
    gtk_widget_set_sensitive(pMenu->CandidatesPosition, (pc->style.type == PC_STYLE_CUSTOM && !pc->style.customOption.composite));

    gtk_widget_set_sensitive(pMenu->ShowPageControl, (pc->style.type == PC_STYLE_CUSTOM));
    gtk_widget_set_sensitive(pMenu->IgnorCandidatesTitle, (pc->style.type == PC_STYLE_CUSTOM));
}

static void
on_PCMenuItem_activate               (GtkMenuItem     *menuitem,
                                      gpointer         user_data)
{
    int changed = 1;
    TRealIIimPCWin* pc = user_data;
    TPCOptionMenu* pMenu = pc->pMenu;

    if (pc == NULL)
        return;

    TPCStyleOption pcstyle = pc->style;

    if (GTK_WIDGET(menuitem) == pMenu->RootWindow) {
        if (pcstyle.type == PC_STYLE_ROOT) changed = 0;
        pcstyle.type = PC_STYLE_ROOT;

    } else if (GTK_WIDGET(menuitem) == pMenu->ClassicCompositeFollow) {
        if (pcstyle.type == PC_STYLE_CLASSIC) changed = 0;
        pcstyle.type = PC_STYLE_CLASSIC;

    } else if (GTK_WIDGET(menuitem) == pMenu->TraditionalStyle) {
        if (pcstyle.type == PC_STYLE_TRADITIONAL) changed = 0;
        pcstyle.type = PC_STYLE_TRADITIONAL;

    } else if (GTK_WIDGET(menuitem) == pMenu->ModernCanidatesHorizental) {
        if (pcstyle.type == PC_STYLE_MODERN) changed = 0;
        pcstyle.type = PC_STYLE_MODERN;

    }  else if (GTK_WIDGET(menuitem) == pMenu->CustomizeStyle) {
        if (pcstyle.type == PC_STYLE_CUSTOM) changed = 0;
        pcstyle.type = PC_STYLE_CUSTOM;

    } else if (GTK_WIDGET(menuitem) == pMenu->ShowSystemMenu) {
        pcstyle.customOption.showSystemBar = !(pcstyle.customOption.showSystemBar);

    } else if (GTK_WIDGET(menuitem) == pMenu->FollowCursor) {
        pcstyle.customOption.followCursor = !(pcstyle.customOption.followCursor);

    } else if (GTK_WIDGET(menuitem) == pMenu->GroupPreeditCandidates) {
        pcstyle.customOption.composite = !(pcstyle.customOption.composite);

    } else if (GTK_WIDGET(menuitem) == pMenu->CandidatesHorizental) {
        if (pcstyle.customOption.candidateVertical == 0) changed = 0;
        pcstyle.customOption.candidateVertical = 0;

    } else if (GTK_WIDGET(menuitem) == pMenu->CandidatesVertical) {
        if (pcstyle.customOption.candidateVertical == 1) changed = 0;
        pcstyle.customOption.candidateVertical = 1;

    } else if (GTK_WIDGET(menuitem) == pMenu->AboveCandidates) {
        if (pcstyle.customOption.candidateTitlePosition == TITLE_ABOVE_CANDIDATES) changed = 0;
        pcstyle.customOption.candidateTitlePosition = TITLE_ABOVE_CANDIDATES;

    } else if (GTK_WIDGET(menuitem) == pMenu->BelowCandidates) {
        if (pcstyle.customOption.candidateTitlePosition != TITLE_ABOVE_CANDIDATES) changed = 0;
        pcstyle.customOption.candidateTitlePosition = TITLE_BELOW_CANDIDATES;

    } else if (GTK_WIDGET(menuitem) == pMenu->CandidatesFollowIme) {
        if (pcstyle.customOption.candidatePosition == CANDIDATES_POSITION_IME) changed = 0;
        pcstyle.customOption.candidatePosition = CANDIDATES_POSITION_IME;

    } else if (GTK_WIDGET(menuitem) == pMenu->CandidatesStickPreedit) {
        if (pcstyle.customOption.candidatePosition == CANDIDATES_POSITION_PREEDIT_STARTS) changed = 0;
        pcstyle.customOption.candidatePosition = CANDIDATES_POSITION_PREEDIT_STARTS;

    } else if (GTK_WIDGET(menuitem) == pMenu->CandidatesFollowCaret) {
        if (pcstyle.customOption.candidatePosition == CANDIDATES_POSITION_PREEDIT_CARET) changed = 0;
        pcstyle.customOption.candidatePosition = CANDIDATES_POSITION_PREEDIT_CARET;

    } else if (GTK_WIDGET(menuitem) == pMenu->CandidatesRight2Preedit) {
        if (pcstyle.customOption.candidatePosition == CANDIDATES_POSITION_PREEDIT_RIGHT) changed = 0;
        pcstyle.customOption.candidatePosition = CANDIDATES_POSITION_PREEDIT_RIGHT;

    } else if (GTK_WIDGET(menuitem) == pMenu->ShowPageControl) {
        pcstyle.customOption.showPageControl = !(pcstyle.customOption.showPageControl);

    } else if (GTK_WIDGET(menuitem) == pMenu->IgnorCandidatesTitle) {
        pcstyle.customOption.ignoreCandidateTitle = !(pcstyle.customOption.ignoreCandidateTitle);

    } else {
        changed = 0;
    }

    if (changed) {
        if (pc->process_style_change)
             pc->process_style_change(pc, (int)&pcstyle);
    }
}

static void
setMenuActivateCallbacks(TRealIIimPCWin* pc, TPCOptionMenu* pMenu)
{
  g_signal_connect ((gpointer) pMenu->RootWindow, "activate",
                    G_CALLBACK (on_PCMenuItem_activate),
                    pc);
  g_signal_connect ((gpointer) pMenu->ClassicCompositeFollow, "activate",
                    G_CALLBACK (on_PCMenuItem_activate),
                    pc);
  g_signal_connect ((gpointer) pMenu->TraditionalStyle, "activate",
                    G_CALLBACK (on_PCMenuItem_activate),
                    pc);
  g_signal_connect ((gpointer) pMenu->ModernCanidatesHorizental, "activate",
                    G_CALLBACK (on_PCMenuItem_activate),
                    pc);
  g_signal_connect ((gpointer) pMenu->CustomizeStyle, "activate",
                    G_CALLBACK (on_PCMenuItem_activate),
                    pc);
  g_signal_connect ((gpointer) pMenu->ShowSystemMenu, "activate",
                    G_CALLBACK (on_PCMenuItem_activate),
                    pc);
  g_signal_connect ((gpointer) pMenu->FollowCursor, "activate",
                    G_CALLBACK (on_PCMenuItem_activate),
                    pc);
  g_signal_connect ((gpointer) pMenu->GroupPreeditCandidates, "activate",
                    G_CALLBACK (on_PCMenuItem_activate),
                    pc);
  g_signal_connect ((gpointer) pMenu->CandidatesDirection, "activate",
                    G_CALLBACK (on_PCMenuItem_activate),
                    pc);
  g_signal_connect ((gpointer) pMenu->CandidatesHorizental, "activate",
                    G_CALLBACK (on_PCMenuItem_activate),
                    pc);
  g_signal_connect ((gpointer) pMenu->CandidatesVertical, "activate",
                    G_CALLBACK (on_PCMenuItem_activate),
                    pc);
  g_signal_connect ((gpointer) pMenu->AboveCandidates, "activate",
                    G_CALLBACK (on_PCMenuItem_activate),
                    pc);
  g_signal_connect ((gpointer) pMenu->BelowCandidates, "activate",
                    G_CALLBACK (on_PCMenuItem_activate),
                    pc);
  g_signal_connect ((gpointer) pMenu->CandidatesFollowIme, "activate",
                    G_CALLBACK (on_PCMenuItem_activate),
                    pc);
  g_signal_connect ((gpointer) pMenu->CandidatesStickPreedit, "activate",
                    G_CALLBACK (on_PCMenuItem_activate),
                    pc);
  g_signal_connect ((gpointer) pMenu->CandidatesFollowCaret, "activate",
                    G_CALLBACK (on_PCMenuItem_activate),
                    pc);
  g_signal_connect ((gpointer) pMenu->CandidatesRight2Preedit, "activate",
                    G_CALLBACK (on_PCMenuItem_activate),
                    pc);
  g_signal_connect ((gpointer) pMenu->ShowPageControl, "activate",
                    G_CALLBACK (on_PCMenuItem_activate),
                    pc);
  g_signal_connect ((gpointer) pMenu->IgnorCandidatesTitle, "activate",
                    G_CALLBACK (on_PCMenuItem_activate),
                    pc);
}

static void
on_PCSysMenu_popdown           (GtkMenuShell *menushell,
                                gpointer     user_data)
{
    TRealIIimPCWin* pc = user_data;

    if (pc) pc->poping_up = 2;
}

#if defined(LE_ZH_TW) || defined(LE_ZH_HK)
static char *pc_msg_menuitem_root_style = "固定輸入視窗 (輸入框不跟隨游標移動)";
static char *pc_msg_menuitem_classic_style = "典型 (輸入框跟隨游標移動)";
static char *pc_msg_menuitem_traditional_style = "傳統 (右側豎排候選)";
static char *pc_msg_menuitem_modern_style = "現代 (下部橫排候選)";
static char *pc_msg_menuitem_custom_style = "使用者自行定義";
static char *pc_msg_menuitem_show_sysbutton = "顯示選單按鈕";
static char *pc_msg_menuitem_follow_cursor = "跟隨游標";
static char *pc_msg_menuitem_composite = "組合";
static char *pc_msg_menu_candidates_direction = "候選排列方式";
static char *pc_msg_menuitem_candidates_vertical = "豎排";
static char *pc_msg_menuitem_candidates_horizental = "橫排";
static char *pc_msg_menu_candidates_title_position = "候選標題位置";
static char *pc_msg_menuitem_candidates_title_left = "候選左側或上方";
static char *pc_msg_menuitem_candidates_title_right = "候選右側或下方";
static char *pc_msg_menu_candidates_position = "候選視窗位置";
static char *pc_msg_menuitem_candidates_ime = "輸入法決定";
static char *pc_msg_menuitem_candidates_stick = "編輯視窗起始位置";
static char *pc_msg_menuitem_candidates_right = "編輯視窗右側";
static char *pc_msg_menuitem_candidates_follow_cursor = "跟隨編輯視窗游標";
static char *pc_msg_menuitem_show_page_controls = "顯示捲頁元件";
static char *pc_msg_menuitem_ignore_candidates_title = "隱藏候選標題";
#else
static char *pc_msg_menuitem_root_style = "固定(组合,不跟随光标)";
static char *pc_msg_menuitem_classic_style = "经典(组合,跟随光标)";
static char *pc_msg_menuitem_traditional_style = "传统(右侧竖排候选)";
static char *pc_msg_menuitem_modern_style = "现代(下部横排候选)";
static char *pc_msg_menuitem_custom_style = "自定义";
static char *pc_msg_menuitem_show_sysbutton = "显示菜单按钮";
static char *pc_msg_menuitem_follow_cursor = "光标跟随";
static char *pc_msg_menuitem_composite = "组合";
static char *pc_msg_menu_candidates_direction = "候选排列方式";
static char *pc_msg_menuitem_candidates_vertical = "竖排";
static char *pc_msg_menuitem_candidates_horizental = "横排";
static char *pc_msg_menu_candidates_title_position = "候选标题位置";
static char *pc_msg_menuitem_candidates_title_left = "候选左侧或上方";
static char *pc_msg_menuitem_candidates_title_right = "候选右侧或下方";
static char *pc_msg_menu_candidates_position = "候选窗口位置";
static char *pc_msg_menuitem_candidates_ime = "输入法决定";
static char *pc_msg_menuitem_candidates_stick = "编辑窗口起始位置";
static char *pc_msg_menuitem_candidates_right = "编辑窗口右侧";
static char *pc_msg_menuitem_candidates_follow_cursor = "跟随编辑窗口光标";
static char *pc_msg_menuitem_show_page_controls = "显示翻页控件";
static char *pc_msg_menuitem_ignore_candidates_title = "隐藏候选标题";
#endif

static TPCOptionMenu*
create_iiimPCWinSysMenu (TRealIIimPCWin* pc)
{
  TPCOptionMenu* pMenu = (TPCOptionMenu* )calloc(1, sizeof(TPCOptionMenu));

  pMenu->iiimPCWinSysMenu = gtk_menu_new ();
  GTK_WIDGET_UNSET_FLAGS(pMenu->iiimPCWinSysMenu, GTK_CAN_FOCUS);
  gtk_container_set_border_width (GTK_CONTAINER (pMenu->iiimPCWinSysMenu), 2);

  pMenu->RootWindow = gtk_radio_menu_item_new_with_mnemonic (pMenu->RootWindow_group, pc_msg_menuitem_root_style);
  pMenu->RootWindow_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (pMenu->RootWindow));
  gtk_container_add (GTK_CONTAINER (pMenu->iiimPCWinSysMenu), pMenu->RootWindow);
  gtk_widget_show(pMenu->RootWindow);

  pMenu->ClassicCompositeFollow = gtk_radio_menu_item_new_with_mnemonic (pMenu->RootWindow_group, pc_msg_menuitem_classic_style);
  pMenu->RootWindow_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (pMenu->ClassicCompositeFollow));
  gtk_container_add (GTK_CONTAINER (pMenu->iiimPCWinSysMenu), pMenu->ClassicCompositeFollow);
  gtk_widget_show(pMenu->ClassicCompositeFollow);

  pMenu->TraditionalStyle = gtk_radio_menu_item_new_with_mnemonic (pMenu->RootWindow_group, pc_msg_menuitem_traditional_style);
  pMenu->RootWindow_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (pMenu->TraditionalStyle));
  gtk_container_add (GTK_CONTAINER (pMenu->iiimPCWinSysMenu), pMenu->TraditionalStyle);
  gtk_widget_show(pMenu->TraditionalStyle);

  pMenu->ModernCanidatesHorizental = gtk_radio_menu_item_new_with_mnemonic (pMenu->RootWindow_group, pc_msg_menuitem_modern_style);
  pMenu->RootWindow_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (pMenu->ModernCanidatesHorizental));
  gtk_container_add (GTK_CONTAINER (pMenu->iiimPCWinSysMenu), pMenu->ModernCanidatesHorizental);
  gtk_widget_show(pMenu->ModernCanidatesHorizental);

  pMenu->CustomizeStyle = gtk_radio_menu_item_new_with_mnemonic (pMenu->RootWindow_group, pc_msg_menuitem_custom_style);
  pMenu->RootWindow_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (pMenu->CustomizeStyle));
  gtk_container_add (GTK_CONTAINER (pMenu->iiimPCWinSysMenu), pMenu->CustomizeStyle);
  gtk_widget_show(pMenu->CustomizeStyle);

  pMenu->_________1 = gtk_separator_menu_item_new ();
  gtk_container_add (GTK_CONTAINER (pMenu->iiimPCWinSysMenu), pMenu->_________1);
  gtk_widget_set_sensitive (pMenu->_________1, FALSE);
  gtk_widget_show(pMenu->_________1);

  pMenu->ShowSystemMenu = gtk_check_menu_item_new_with_mnemonic (pc_msg_menuitem_show_sysbutton);
  gtk_container_add (GTK_CONTAINER (pMenu->iiimPCWinSysMenu), pMenu->ShowSystemMenu);
  gtk_widget_show(pMenu->ShowSystemMenu);

  pMenu->FollowCursor = gtk_check_menu_item_new_with_mnemonic (pc_msg_menuitem_follow_cursor);
  gtk_container_add (GTK_CONTAINER (pMenu->iiimPCWinSysMenu), pMenu->FollowCursor);
  gtk_widget_show(pMenu->FollowCursor);

  pMenu->GroupPreeditCandidates = gtk_check_menu_item_new_with_mnemonic (pc_msg_menuitem_composite);
  gtk_container_add (GTK_CONTAINER (pMenu->iiimPCWinSysMenu), pMenu->GroupPreeditCandidates);
  gtk_widget_show(pMenu->GroupPreeditCandidates);

  pMenu->_________2 = gtk_separator_menu_item_new ();
  gtk_container_add (GTK_CONTAINER (pMenu->iiimPCWinSysMenu), pMenu->_________2);
  gtk_widget_set_sensitive (pMenu->_________2, FALSE);
  gtk_widget_show(pMenu->_________2);

  pMenu->CandidatesDirection = gtk_menu_item_new_with_mnemonic (pc_msg_menu_candidates_direction);
  gtk_container_add (GTK_CONTAINER (pMenu->iiimPCWinSysMenu), pMenu->CandidatesDirection);
  gtk_widget_show(pMenu->CandidatesDirection);

  pMenu->CandidatesDirection_menu = gtk_menu_new ();
  gtk_menu_item_set_submenu (GTK_MENU_ITEM (pMenu->CandidatesDirection), pMenu->CandidatesDirection_menu);
  gtk_widget_show(pMenu->CandidatesDirection_menu);

      pMenu->CandidatesHorizental = gtk_radio_menu_item_new_with_mnemonic (pMenu->CandidatesHorizental_group, pc_msg_menuitem_candidates_horizental);
      pMenu->CandidatesHorizental_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (pMenu->CandidatesHorizental));
      gtk_container_add (GTK_CONTAINER (pMenu->CandidatesDirection_menu), pMenu->CandidatesHorizental);
      gtk_widget_show(pMenu->CandidatesHorizental);

      pMenu->CandidatesVertical = gtk_radio_menu_item_new_with_mnemonic (pMenu->CandidatesHorizental_group, pc_msg_menuitem_candidates_vertical);
      pMenu->CandidatesHorizental_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (pMenu->CandidatesVertical));
      gtk_container_add (GTK_CONTAINER (pMenu->CandidatesDirection_menu), pMenu->CandidatesVertical);
      gtk_widget_show(pMenu->CandidatesVertical);

  pMenu->CandidateTitlePosition = gtk_menu_item_new_with_mnemonic (pc_msg_menu_candidates_title_position);
  gtk_container_add (GTK_CONTAINER (pMenu->iiimPCWinSysMenu), pMenu->CandidateTitlePosition);
  gtk_widget_show(pMenu->CandidateTitlePosition);

  pMenu->CandidateTitlePosition_menu = gtk_menu_new ();
  gtk_menu_item_set_submenu (GTK_MENU_ITEM (pMenu->CandidateTitlePosition), pMenu->CandidateTitlePosition_menu);
  gtk_widget_show(pMenu->CandidateTitlePosition_menu);

      pMenu->AboveCandidates = gtk_radio_menu_item_new_with_mnemonic (pMenu->AboveCandidates_group, pc_msg_menuitem_candidates_title_left);
      pMenu->AboveCandidates_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (pMenu->AboveCandidates));
      gtk_container_add (GTK_CONTAINER (pMenu->CandidateTitlePosition_menu), pMenu->AboveCandidates);
      gtk_widget_show(pMenu->AboveCandidates);

      pMenu->BelowCandidates = gtk_radio_menu_item_new_with_mnemonic (pMenu->AboveCandidates_group, pc_msg_menuitem_candidates_title_right);
      pMenu->AboveCandidates_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (pMenu->BelowCandidates));
      gtk_container_add (GTK_CONTAINER (pMenu->CandidateTitlePosition_menu), pMenu->BelowCandidates);
      gtk_widget_show(pMenu->BelowCandidates);

  pMenu->CandidatesPosition = gtk_menu_item_new_with_mnemonic (pc_msg_menu_candidates_position);
  gtk_container_add (GTK_CONTAINER (pMenu->iiimPCWinSysMenu), pMenu->CandidatesPosition);
  gtk_widget_show(pMenu->CandidatesPosition);

  pMenu->CandidatesPosition_menu = gtk_menu_new ();
  gtk_menu_item_set_submenu (GTK_MENU_ITEM (pMenu->CandidatesPosition), pMenu->CandidatesPosition_menu);
  gtk_widget_show(pMenu->CandidatesPosition_menu);

      pMenu->CandidatesFollowIme = gtk_radio_menu_item_new_with_mnemonic (pMenu->CandidatesFollowIme_group, pc_msg_menuitem_candidates_ime);
      pMenu->CandidatesFollowIme_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (pMenu->CandidatesFollowIme));
      gtk_container_add (GTK_CONTAINER (pMenu->CandidatesPosition_menu), pMenu->CandidatesFollowIme);
      gtk_widget_show(pMenu->CandidatesFollowIme);

      pMenu->CandidatesStickPreedit = gtk_radio_menu_item_new_with_mnemonic (pMenu->CandidatesFollowIme_group, pc_msg_menuitem_candidates_stick);
      pMenu->CandidatesFollowIme_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (pMenu->CandidatesStickPreedit));
      gtk_container_add (GTK_CONTAINER (pMenu->CandidatesPosition_menu), pMenu->CandidatesStickPreedit);
      gtk_widget_show(pMenu->CandidatesStickPreedit);


      pMenu->CandidatesFollowCaret = gtk_radio_menu_item_new_with_mnemonic (pMenu->CandidatesFollowIme_group, pc_msg_menuitem_candidates_follow_cursor);
      pMenu->CandidatesFollowIme_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (pMenu->CandidatesFollowCaret));
      gtk_container_add (GTK_CONTAINER (pMenu->CandidatesPosition_menu), pMenu->CandidatesFollowCaret);
      gtk_widget_show(pMenu->CandidatesFollowCaret);

      pMenu->CandidatesRight2Preedit = gtk_radio_menu_item_new_with_mnemonic (pMenu->CandidatesFollowIme_group, pc_msg_menuitem_candidates_right);
      pMenu->CandidatesFollowIme_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (pMenu->CandidatesRight2Preedit));
      gtk_container_add (GTK_CONTAINER (pMenu->CandidatesPosition_menu), pMenu->CandidatesRight2Preedit);
      gtk_widget_show(pMenu->CandidatesRight2Preedit);

  pMenu->_________3 = gtk_separator_menu_item_new ();
  gtk_container_add (GTK_CONTAINER (pMenu->iiimPCWinSysMenu), pMenu->_________3);
  gtk_widget_set_sensitive (pMenu->_________3, FALSE);
  gtk_widget_show(pMenu->_________3);

  pMenu->ShowPageControl = gtk_check_menu_item_new_with_mnemonic (pc_msg_menuitem_show_page_controls);
  gtk_container_add (GTK_CONTAINER (pMenu->iiimPCWinSysMenu), pMenu->ShowPageControl);
  gtk_widget_show(pMenu->ShowPageControl);

  pMenu->IgnorCandidatesTitle = gtk_check_menu_item_new_with_mnemonic (pc_msg_menuitem_ignore_candidates_title);
  gtk_container_add (GTK_CONTAINER (pMenu->iiimPCWinSysMenu), pMenu->IgnorCandidatesTitle);
  gtk_widget_show(pMenu->IgnorCandidatesTitle);

  pc->pMenu = pMenu;
  updateMenuActivates(pc);
  updateMenuStates(pc);
  setMenuActivateCallbacks(pc, pMenu);

  g_signal_connect ((gpointer) GTK_MENU_SHELL(pMenu->iiimPCWinSysMenu), "deactivate",
                    G_CALLBACK (on_PCSysMenu_popdown),
                    pc);

  return pMenu;
}


void
getPCCursorPosition(TIIimPCWin vpc, int *pcursor_x, int *pcursor_y, int *pcursor_w, int* pcursor_h)
{
    TRealIIimPCWin *pc = (TRealIIimPCWin *)vpc;
    *pcursor_x = pc->cursor_x;
    *pcursor_y = pc->cursor_y;
    *pcursor_w = pc->cursor_w;
    *pcursor_h = pc->cursor_h;
    return;
    //gtk_window_get_position(GTK_WINDOW(pc->preeditWin), px, py);
}

void registerStyleChangeCallback(TIIimPCWin vpc, TIIimPCWinCallback callback)
{
    TRealIIimPCWin *pc = (TRealIIimPCWin *)vpc;
    pc->process_style_change = callback;
}

void registerPageCallback(TIIimPCWin vpc, TIIimPCWinCallback callback)
{
    TRealIIimPCWin *pc = (TRealIIimPCWin *)vpc;
    pc->process_page_request = callback;
}

void registerSelectionCallback(TIIimPCWin vpc, TIIimPCWinCallback callback)
{
    TRealIIimPCWin *pc = (TRealIIimPCWin *)vpc;
    pc->process_select_request = callback;
}

void registerMoveCallback(TIIimPCWin vpc, TIIimPCWinCallback callback)
{
    TRealIIimPCWin *pc = (TRealIIimPCWin *)vpc;
    pc->process_position_change = callback;
}

TPCStyleOption* getStyles(TIIimPCWin vpc)
{
    return  &((TRealIIimPCWin *)vpc)->style;
}

int isPCFollowCursor(TIIimPCWin vpc)
{
    return is_pc_follow_cursor((TRealIIimPCWin *)vpc);
}

int is_pc_preedit_show(TIIimPCWin vpc)
{
    return  ((TRealIIimPCWin *)vpc)->preeditShow;
}

int is_pc_candidates_show(TIIimPCWin vpc)
{
    return  ((TRealIIimPCWin *)vpc)->candidatesShow;
}



