1 /*
2 
3   Copyright (c) 2003-2013 uim Project https://github.com/uim/uim
4 
5   All rights reserved.
6 
7   Redistribution and use in source and binary forms, with or without
8   modification, are permitted provided that the following conditions
9   are met:
10 
11   1. Redistributions of source code must retain the above copyright
12      notice, this list of conditions and the following disclaimer.
13   2. Redistributions in binary form must reproduce the above copyright
14      notice, this list of conditions and the following disclaimer in the
15      documentation and/or other materials provided with the distribution.
16   3. Neither the name of authors nor the names of its contributors
17      may be used to endorse or promote products derived from this software
18      without specific prior written permission.
19 
20   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
21   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
24   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26   OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30   SUCH DAMAGE.
31 
32 */
33 
34 #ifdef HAVE_CONFIG_H
35 #  include <config.h>
36 #endif
37 #include <uim/uim.h>
38 #include <uim/uim-helper.h>
39 #include <uim/uim-scm.h>
40 #include <gtk/gtk.h>
41 #include <gdk/gdkx.h>
42 #include <glib.h>
43 #include <stdlib.h>
44 #include <stdio.h>
45 #include <string.h>
46 #include <sys/types.h>
47 #include <unistd.h>
48 #include <math.h>
49 
50 #include "../gtk2/immodule/caret-state-indicator.h"
51 
52 #define UIM_TYPE_CANDIDATE_WINDOW	(candidate_window_get_type())
53 #define UIM_CANDIDATE_WINDOW(obj)	(G_TYPE_CHECK_INSTANCE_CAST ((obj), candidate_window_get_type(), UIMCandidateWindow))
54 #define UIM_IS_CANDIDATE_WINDOW(obj)	(G_TYPE_CHECK_INSTANCE_TYPE ((obj), UIM_TYPE_CANDIDATE_WINDOW))
55 #define UIM_IS_CANDIDATE_WINDOW_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass), UIM_TYPE_CANDIDATE_WINDOW))
56 #define UIM_CANDIDATE_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), UIM_TYPE_CANDIDATE_WINDOW, UIMCandidateWindowClass))
57 
58 #define UIM_ANNOTATION_WIN_WIDTH 280
59 #define UIM_ANNOTATION_WIN_HEIGHT 140
60 
61 typedef struct _UIMCandidateWindow	UIMCandidateWindow;
62 typedef struct _UIMCandidateWindowClass	UIMCandidateWindowClass;
63 
64 struct _UIMCandidateWindow {
65   GtkWindow parent;
66 
67   GtkWidget *view;
68   GtkWidget *num_label;
69   GtkWidget *scrolled_window;
70   GtkWidget *viewport;
71   GtkWidget *vbox;
72   GtkWidget *frame;
73   GtkWidget *hbox;
74   GtkWidget *prev_page_button;
75   GtkWidget *next_page_button;
76 
77   GPtrArray *stores;
78   GPtrArray *buttons;
79   gpointer selected;
80 
81   guint nr_candidates;
82   guint display_limit;
83   gint candidate_index;
84   gint page_index;
85 
86   gint pos_x;
87   gint pos_y;
88   gint width;
89   gint height;
90 
91   GtkWidget *caret_state_indicator;
92 
93   gboolean is_active;
94   gboolean need_hilite;
95   gboolean need_page_update;
96 
97   /* sub window */
98   struct sub_window {
99     GtkWidget *window;
100     GtkWidget *scrolled_window;
101     GtkWidget *text_view;
102     gboolean active;
103   } sub_window;
104 };
105 
106 struct _UIMCandidateWindowClass {
107   GtkWindowClass parent_class;
108 
109   /* signals */
110   void (*index_changed) (UIMCandidateWindowClass *candwin);
111 };
112 
113 static UIMCandidateWindow *cwin; /* use single candwin */
114 
115 GType candidate_window_get_type(void);
116 UIMCandidateWindow *candidate_window_new(void);
117 
118 /* copied from uim-cand-win-gtk.c */
119 static gint uim_cand_win_gtk_get_index(UIMCandidateWindow *cwin);
120 static void uim_cand_win_gtk_set_index(UIMCandidateWindow *cwin, gint index);
121 static void uim_cand_win_gtk_set_page(UIMCandidateWindow *cwin, gint page);
122 static void uim_cand_win_gtk_set_page_candidates(UIMCandidateWindow *cwin, guint page, GSList *candidates);
123 static void uim_cand_win_gtk_create_sub_window(UIMCandidateWindow *cwin);
124 static void uim_cand_win_gtk_layout_sub_window(UIMCandidateWindow *cwin);
125 
126 static void uim_cand_win_gtk_layout(void);
127 static void uim_cand_win_gtk_show(UIMCandidateWindow *cwin);
128 
129 #define CANDWIN_DEFAULT_WIDTH	60
130 
131 enum {
132   INDEX_CHANGED_SIGNAL,
133   NR_SIGNALS
134 };
135 
136 enum {
137   TERMINATOR = -1,
138   COLUMN_HEADING,
139   COLUMN_CANDIDATE,
140   COLUMN_ANNOTATION,
141   LISTSTORE_NR_COLUMNS
142 };
143 
144 #define DEFAULT_NR_CELLS 10
145 
146 struct index_button {
147   gint cand_index_in_page;
148   GtkEventBox *button;
149 };
150 
151 static void candidate_window_init(UIMCandidateWindow *cwin);
152 static void candidate_window_class_init(UIMCandidateWindowClass *klass);
153 
154 static gboolean configure_event_cb(GtkWidget *widget, GdkEventConfigure *event, gpointer data);
155 
156 static GType candidate_window_type = 0;
157 static GTypeInfo const object_info = {
158   sizeof (UIMCandidateWindowClass),
159   NULL,
160   NULL,
161   (GClassInitFunc) candidate_window_class_init,
162   NULL,
163   NULL,
164   sizeof(UIMCandidateWindow),
165   0,
166   (GInstanceInitFunc) candidate_window_init,
167 };
168 
169 static gint cand_win_gtk_signals[NR_SIGNALS] = {0};
170 
171 static unsigned int read_tag;
172 
173 static void init_candidate_win(void);
174 static void candwin_activate(gchar **str);
175 static void candwin_update(gchar **str);
176 static void candwin_move(char **str);
177 static void candwin_show(void);
178 static void candwin_deactivate(void);
179 static void candwin_set_nr_candidates(gchar **str);
180 static void candwin_set_page_candidates(gchar **str);
181 static void candwin_show_page(gchar **str);
182 static void str_parse(char *str);
183 static void clear_button(struct index_button *idxbutton, gint cell_index);
184 #if GTK_CHECK_VERSION(3, 4, 0)
185 static void show_table(GtkGrid *view, GPtrArray *buttons);
186 #else
187 static void show_table(GtkTable *view, GPtrArray *buttons);
188 #endif
189 static void scale_label(GtkEventBox *button, double factor);
190 
index_changed_cb(UIMCandidateWindow * cwin)191 static void index_changed_cb(UIMCandidateWindow *cwin)
192 {
193   fprintf(stdout, "index\n");
194   fprintf(stdout, "%d\n\n", uim_cand_win_gtk_get_index(cwin));
195   fflush(stdout);
196 }
197 
198 GType
candidate_window_get_type(void)199 candidate_window_get_type(void)
200 {
201   if (!candidate_window_type)
202     candidate_window_type = g_type_register_static(GTK_TYPE_WINDOW,
203 		    "UIMCandWinHorizontalGtk", &object_info, (GTypeFlags)0);
204   return candidate_window_type;
205 }
206 
candidate_window_class_init(UIMCandidateWindowClass * klass)207 static void candidate_window_class_init(UIMCandidateWindowClass *klass)
208 {
209   cand_win_gtk_signals[INDEX_CHANGED_SIGNAL]
210     = g_signal_new("index-changed",
211 		   G_TYPE_FROM_CLASS(klass),
212 		   G_SIGNAL_RUN_FIRST,
213 		   G_STRUCT_OFFSET(UIMCandidateWindowClass, index_changed),
214 		   NULL, NULL,
215 		   g_cclosure_marshal_VOID__VOID,
216 		   G_TYPE_NONE, 0);
217 }
218 
219 UIMCandidateWindow *
candidate_window_new(void)220 candidate_window_new(void)
221 {
222   GObject *obj = g_object_new(UIM_TYPE_CANDIDATE_WINDOW, "type",
223 		  GTK_WINDOW_POPUP, NULL);
224   return UIM_CANDIDATE_WINDOW(obj);
225 }
226 
227 /* copied from uim-cand-win-gtk.c */
228 static void
update_label(UIMCandidateWindow * cwin)229 update_label(UIMCandidateWindow *cwin)
230 {
231   char label_str[20];
232 
233   if (cwin->candidate_index >= 0)
234     g_snprintf(label_str, sizeof(label_str), "%d / %d",
235 	       cwin->candidate_index + 1 , cwin->nr_candidates);
236   else
237     g_snprintf(label_str, sizeof(label_str), "- / %d",
238 	       cwin->nr_candidates);
239 
240   gtk_label_set_text(GTK_LABEL(cwin->num_label), label_str);
241 }
242 
243 #if !GTK_CHECK_VERSION(2, 90, 0)
244 static void
get_layout_x(GtkLabel * label,gint * xp)245 get_layout_x(GtkLabel *label, gint *xp)
246 {
247   GtkMisc *misc;
248   GtkWidget *widget;
249   gfloat xalign;
250   gint req_width, x;
251 
252   misc = GTK_MISC(label);
253   widget = GTK_WIDGET(label);
254 
255   if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_LTR)
256     xalign = misc->xalign;
257   else
258     xalign = 1.0 - misc->xalign;
259 
260   req_width = widget->requisition.width;
261 
262   x = floor(widget->allocation.x + (gint)misc->xpad +
263              xalign * (widget->allocation.width - req_width));
264 
265   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
266     x = MAX(x, widget->allocation.x + misc->xpad);
267   else
268     x = MIN(x, widget->allocation.x + widget->allocation.width - misc->xpad);
269 
270   if (xp)
271     *xp = x;
272 }
273 #endif
274 #if GTK_CHECK_VERSION(2, 90, 0)
275 static gboolean
label_draw(GtkWidget * label,cairo_t * cr,gpointer data)276 label_draw(GtkWidget *label, cairo_t *cr, gpointer data)
277 {
278   UIMCandidateWindow *cwin = UIM_CANDIDATE_WINDOW(data);
279   struct index_button *selected;
280   GtkWidget *selected_label = NULL;
281   GdkRGBA *bg_color, *fg_color;
282   GtkStyleContext *context;
283   PangoLayout *layout;
284   gint x, y;
285   GtkStateFlags state;
286 
287   selected = cwin->selected;
288   if (selected)
289     selected_label = gtk_bin_get_child(GTK_BIN(selected->button));
290 
291   layout = gtk_label_get_layout(GTK_LABEL(label));
292   gtk_label_get_layout_offsets(GTK_LABEL(label), &x, &y);
293 
294   context = gtk_widget_get_style_context(label);
295 
296   if (label == selected_label)
297     state = GTK_STATE_FLAG_SELECTED;
298   else
299     state = GTK_STATE_FLAG_NORMAL;
300 
301   gtk_style_context_get (context, state, "background-color", &bg_color, "color", &fg_color, NULL);
302 
303   cairo_save(cr);
304   gdk_cairo_set_source_rgba(cr, bg_color);
305   cairo_paint(cr);
306   cairo_restore(cr);
307   gdk_rgba_free(bg_color);
308   gdk_rgba_free(fg_color);
309 
310   gtk_style_context_set_state (context, state);
311   gtk_render_layout (context, cr, x, y, layout);
312 
313   return FALSE;
314 }
315 #else
316 static gboolean
label_exposed(GtkWidget * label,GdkEventExpose * event,gpointer data)317 label_exposed(GtkWidget *label, GdkEventExpose *event, gpointer data)
318 {
319   UIMCandidateWindow *cwin = UIM_CANDIDATE_WINDOW(data);
320   struct index_button *selected;
321   GtkWidget *selected_label = NULL;
322 
323   selected = cwin->selected;
324   if (selected)
325     selected_label = gtk_bin_get_child(GTK_BIN(selected->button));
326 
327   if (label == selected_label) {
328     gint x;
329     get_layout_x(GTK_LABEL(label), &x);
330     gdk_draw_layout_with_colors(label->window,
331                       label->style->black_gc, x, 0,
332                       GTK_LABEL(label)->layout,
333                       &label->style->text[GTK_STATE_SELECTED],
334                       &label->style->bg[GTK_STATE_SELECTED]);
335   }
336 
337   return FALSE;
338 }
339 #endif
340 
341 static void
button_clicked(GtkEventBox * button,GdkEventButton * event,gpointer data)342 button_clicked(GtkEventBox *button, GdkEventButton *event, gpointer data)
343 {
344   UIMCandidateWindow *cwin = UIM_CANDIDATE_WINDOW(data);
345   gint i;
346   gint idx = -1;
347   struct index_button *prev_selected;
348 
349   prev_selected = cwin->selected;
350   if (prev_selected) {
351     GtkWidget *label = gtk_bin_get_child(GTK_BIN(prev_selected->button));
352     gtk_widget_queue_draw(label);
353   }
354 
355   for (i = 0; i < (gint)cwin->buttons->len; i++) {
356     GtkEventBox *p;
357     struct index_button *idxbutton;
358     idxbutton = g_ptr_array_index(cwin->buttons, i);
359     if (!idxbutton) {
360       continue;
361     }
362     p = idxbutton->button;
363     if (p == button) {
364       GtkWidget *label = gtk_bin_get_child(GTK_BIN(button));
365       idx = idxbutton->cand_index_in_page;
366       gtk_widget_queue_draw(label);
367       cwin->selected = idxbutton;
368       break;
369     }
370   }
371   if (idx >= 0 && cwin->display_limit) {
372     if (idx >= (gint)cwin->display_limit) {
373       idx %= cwin->display_limit;
374     }
375     cwin->candidate_index = cwin->page_index * cwin->display_limit + idx;
376   } else {
377     cwin->candidate_index = idx;
378   }
379   if (cwin->candidate_index >= (gint)cwin->nr_candidates) {
380     cwin->candidate_index = -1;
381   }
382   g_signal_emit_by_name(G_OBJECT(cwin), "index-changed");
383 }
384 
385 static void
pagebutton_clicked(GtkButton * button,gpointer data)386 pagebutton_clicked(GtkButton *button, gpointer data)
387 {
388   UIMCandidateWindow *cwin = UIM_CANDIDATE_WINDOW(data);
389 
390   if (cwin->candidate_index < 0) {
391     /* if candidate_index < 0, "index-changed" signal is not emitted
392      * and candidates for new page is not set.
393      */
394     cwin->candidate_index = cwin->page_index * cwin->display_limit;
395   }
396   if (button == GTK_BUTTON(cwin->prev_page_button)) {
397     uim_cand_win_gtk_set_page(cwin, cwin->page_index - 1);
398   } else if (button == GTK_BUTTON(cwin->next_page_button)) {
399     uim_cand_win_gtk_set_page(cwin, cwin->page_index + 1);
400   } else {
401     return;
402   }
403   if (cwin->candidate_index >= 0) {
404     g_signal_emit(G_OBJECT(cwin),
405                   cand_win_gtk_signals[INDEX_CHANGED_SIGNAL], 0);
406   }
407   if (!cwin->stores->pdata[cwin->page_index]) {
408     /*       candwin                         uim-xim
409      * pagebutton_clicked()
410      *            ---------"index"------------>
411      *                                      InputContext::candidate_select()
412      *            <---"set_page_candidates"----
413      *                                        Canddisp::select()
414      *            <--------"select"------------
415      * candwin_update()
416      *   uim_cand_win_gtk_set_index()
417      *     uim_cand_win_gtk_set_page()
418      */
419     cwin->need_page_update = TRUE;
420   }
421 }
422 
423 static void
cb_table_view_destroy(GtkWidget * widget,GPtrArray * stores)424 cb_table_view_destroy(GtkWidget *widget, GPtrArray *stores)
425 {
426   gint i;
427 
428   g_return_if_fail(GTK_IS_TABLE(widget));
429 
430   for (i = cwin->stores->len - 1; i >= 0; i--) {
431     GtkListStore *store = g_ptr_array_remove_index(cwin->stores, i);
432     if (store) {
433       gtk_list_store_clear(store);
434       g_object_unref(G_OBJECT(store));
435     }
436   }
437   g_ptr_array_free(cwin->stores, TRUE);
438 
439   for (i = 0; i < (gint)cwin->buttons->len; i++) {
440     if (cwin->buttons->pdata[i]) {
441       g_free(cwin->buttons->pdata[i]);
442       /* GtkEventBox is destroyed by container */
443     }
444   }
445   g_ptr_array_free(cwin->buttons, TRUE);
446   cwin->buttons = NULL;
447   cwin->selected = NULL;
448 }
449 
450 static void
init_candidate_win(void)451 init_candidate_win(void) {
452   cwin = candidate_window_new();
453   g_signal_connect(G_OBJECT(cwin), "index-changed",
454 		   G_CALLBACK(index_changed_cb), NULL);
455   g_signal_connect(G_OBJECT(cwin), "configure_event",
456 		   G_CALLBACK(configure_event_cb), NULL);
457 }
458 
459 static void
candidate_window_init(UIMCandidateWindow * cwin)460 candidate_window_init(UIMCandidateWindow *cwin)
461 {
462   GdkRectangle cursor_location;
463   gint col;
464 
465 #if GTK_CHECK_VERSION(3, 2, 0)
466   cwin->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
467 #else
468   cwin->vbox = gtk_vbox_new(FALSE, 0);
469 #endif
470   cwin->frame = gtk_frame_new(NULL);
471 
472   cwin->stores = g_ptr_array_new();
473   cwin->buttons = g_ptr_array_new();
474   cwin->selected = NULL;
475 
476   gtk_window_set_default_size(GTK_WINDOW(cwin),
477 		  CANDWIN_DEFAULT_WIDTH, -1);
478 
479 
480   cwin->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
481   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(cwin->scrolled_window),
482 				 GTK_POLICY_NEVER,
483 				 GTK_POLICY_NEVER);
484   gtk_box_pack_start(GTK_BOX(cwin->vbox), cwin->scrolled_window, TRUE, TRUE, 0);
485 
486 #if GTK_CHECK_VERSION(3, 4, 0)
487   cwin->view = gtk_grid_new();
488   gtk_grid_set_column_spacing(GTK_GRID(cwin->view), 10);
489 #else
490   cwin->view = gtk_table_new(1, DEFAULT_NR_CELLS, FALSE);
491   gtk_table_set_col_spacings(GTK_TABLE(cwin->view), 10);
492 #endif
493   g_signal_connect(G_OBJECT(cwin->view), "destroy",
494   		   G_CALLBACK(cb_table_view_destroy), cwin->stores);
495   cwin->viewport = gtk_viewport_new(NULL, NULL);
496   gtk_container_add(GTK_CONTAINER(cwin->viewport), cwin->view);
497   gtk_container_add(GTK_CONTAINER(cwin->scrolled_window), cwin->viewport);
498   gtk_container_set_resize_mode(GTK_CONTAINER(cwin->viewport), GTK_RESIZE_PARENT);
499   for (col = 0; col < DEFAULT_NR_CELLS; col++) {
500     GtkWidget *button;
501     GtkWidget *label;
502     struct index_button *idxbutton;
503 
504     button = gtk_event_box_new();
505     gtk_event_box_set_above_child(GTK_EVENT_BOX(button), TRUE);
506     label = gtk_label_new("");
507     gtk_container_add(GTK_CONTAINER(button), label);
508     scale_label(GTK_EVENT_BOX(button), PANGO_SCALE_LARGE);
509     g_signal_connect(button, "button-press-event", G_CALLBACK(button_clicked), cwin);
510 #if GTK_CHECK_VERSION(2, 90, 0)
511     g_signal_connect_after(label, "draw", G_CALLBACK(label_draw), cwin);
512 #else
513     g_signal_connect_after(label, "expose-event", G_CALLBACK(label_exposed), cwin);
514 #endif
515 #if GTK_CHECK_VERSION(3, 4, 0)
516     gtk_widget_set_hexpand(button, TRUE);
517     gtk_widget_set_vexpand(button, TRUE);
518     gtk_grid_attach(GTK_GRID(cwin->view), button, col, 0, 1, 1);
519 #else
520     gtk_table_attach_defaults(GTK_TABLE(cwin->view), button, col, col + 1, 0, 1);
521 #endif
522     idxbutton = g_malloc(sizeof(struct index_button));
523     if (idxbutton) {
524       idxbutton->button = GTK_EVENT_BOX(button);
525       clear_button(idxbutton, col);
526     }
527     g_ptr_array_add(cwin->buttons, idxbutton);
528   }
529 
530   gtk_container_add(GTK_CONTAINER(cwin->frame), cwin->vbox);
531   gtk_container_add(GTK_CONTAINER(cwin), cwin->frame);
532   gtk_container_set_border_width(GTK_CONTAINER(cwin->frame), 0);
533 
534   cwin->num_label = gtk_label_new("");
535 
536   /* hbox with prev and next page button: [[<] num_label [>]] */
537 #if GTK_CHECK_VERSION(3, 2, 0)
538   cwin->hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
539 #else
540   cwin->hbox = gtk_hbox_new(FALSE, 0);
541 #endif
542   cwin->prev_page_button = gtk_button_new_with_label("<");
543   cwin->next_page_button = gtk_button_new_with_label(">");
544   gtk_box_pack_start(GTK_BOX(cwin->hbox), GTK_WIDGET(cwin->prev_page_button),
545       TRUE, TRUE, 0);
546   gtk_box_pack_start(GTK_BOX(cwin->hbox), cwin->num_label, FALSE, FALSE, 0);
547   gtk_box_pack_end(GTK_BOX(cwin->hbox), GTK_WIDGET(cwin->next_page_button),
548       TRUE, TRUE, 0);
549   gtk_box_pack_start(GTK_BOX(cwin->vbox), cwin->hbox, FALSE, FALSE, 0);
550   g_signal_connect(cwin->prev_page_button, "clicked",
551       G_CALLBACK(pagebutton_clicked), cwin);
552   g_signal_connect(cwin->next_page_button, "clicked",
553       G_CALLBACK(pagebutton_clicked), cwin);
554 
555   cwin->pos_x = 0;
556   cwin->pos_y = 0;
557   cwin->is_active = FALSE;
558   cwin->need_hilite = FALSE;
559   cwin->need_page_update = FALSE;
560   cwin->caret_state_indicator = caret_state_indicator_new();
561 
562   cursor_location.x = 0;
563   cursor_location.y = 0;
564   cursor_location.height = 0;
565   caret_state_indicator_set_cursor_location(cwin->caret_state_indicator, &cursor_location);
566 
567   cwin->sub_window.window = NULL;
568   cwin->sub_window.scrolled_window = NULL;
569   cwin->sub_window.text_view = NULL;
570   cwin->sub_window.active = FALSE;
571 }
572 
573 static GtkEventBox*
assign_cellbutton(GPtrArray * buttons,gint cand_index,gint display_limit)574 assign_cellbutton(GPtrArray *buttons, gint cand_index, gint display_limit)
575 {
576   gint len;
577   struct index_button *idxbutton;
578   len = buttons->len;
579 
580   if (len <= cand_index) {
581     GtkWidget *button;
582     GtkWidget *label;
583 
584     button = gtk_event_box_new();
585     gtk_event_box_set_above_child(GTK_EVENT_BOX(button), TRUE);
586     label = gtk_label_new("");
587     gtk_container_add(GTK_CONTAINER(button), label);
588     scale_label(GTK_EVENT_BOX(button), PANGO_SCALE_LARGE);
589     g_signal_connect(button, "button-press-event", G_CALLBACK(button_clicked), cwin);
590 #if GTK_CHECK_VERSION(2, 90, 0)
591     g_signal_connect_after(label, "draw", G_CALLBACK(label_draw), cwin);
592 #else
593     g_signal_connect_after(label, "expose-event", G_CALLBACK(label_exposed), cwin);
594 #endif
595 #if GTK_CHECK_VERSION(3, 4, 0)
596     gtk_widget_set_hexpand(button, TRUE);
597     gtk_widget_set_vexpand(button, TRUE);
598     gtk_grid_attach(GTK_GRID(cwin->view), button, cand_index, 0, 1, 1);
599 #else
600     gtk_table_attach_defaults(GTK_TABLE(cwin->view), button, cand_index, cand_index + 1, 0, 1);
601 #endif
602     idxbutton = g_malloc(sizeof(struct index_button));
603     if (idxbutton) {
604       idxbutton->button = GTK_EVENT_BOX(button);
605       clear_button(idxbutton, cand_index);
606       idxbutton->cand_index_in_page = cand_index;
607     }
608     g_ptr_array_add(cwin->buttons, idxbutton);
609   } else {
610     idxbutton = g_ptr_array_index(buttons, cand_index);
611     idxbutton->cand_index_in_page = cand_index;
612   }
613 
614   return idxbutton->button;
615 }
616 
617 static void
candwin_activate(gchar ** str)618 candwin_activate(gchar **str)
619 {
620   gsize rbytes, wbytes;
621   gint i, nr_stores = 1;
622   guint j = 1;
623   gchar *utf8_str;
624   const gchar *charset;
625   guint display_limit;
626   GSList *candidates = NULL;
627 
628   if (cwin->stores == NULL)
629     cwin->stores = g_ptr_array_new();
630 
631   /* remove old data */
632   for (i = cwin->stores->len - 1; i >= 0; i--) {
633     GtkListStore *store = g_ptr_array_remove_index(cwin->stores, i);
634     if (store) {
635       gtk_list_store_clear(store);
636       g_object_unref(G_OBJECT(store));
637     }
638   }
639 
640   if (!strncmp(str[1], "charset=", 8))
641     charset = str[1] + 8;
642   else
643     charset = "UTF-8";
644 
645   if (!strncmp(str[2], "display_limit=", 14)) {
646     display_limit = atoi(str[2] + 14);
647     i = 3;
648   } else {
649     display_limit = 0;
650     i = 2;
651   }
652 
653   for ( ; str[i]; i++) {
654     if (strcmp(str[i], "") == 0) {
655       break;
656     }
657     utf8_str = g_convert(str[i],
658 			 -1,
659 			 "UTF-8",
660 			 charset,
661 			 &rbytes, &wbytes, NULL);
662 
663     candidates = g_slist_prepend(candidates, utf8_str);
664     j++;
665   }
666   candidates = g_slist_reverse(candidates);
667 
668   cwin->candidate_index = -1;
669   cwin->nr_candidates = j - 1;
670   cwin->display_limit = display_limit;
671   cwin->need_hilite = FALSE;
672   cwin->need_page_update = FALSE;
673 
674   if (candidates == NULL)
675     return;
676 
677   /* calculate number of GtkListStores to create */
678   if (display_limit) {
679     nr_stores = cwin->nr_candidates / display_limit;
680     if (cwin->nr_candidates > display_limit * nr_stores)
681       nr_stores++;
682   }
683 
684   /* create GtkListStores, and set candidates */
685   for (i = 0; i < nr_stores; i++) {
686     GtkListStore *store = gtk_list_store_new(LISTSTORE_NR_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
687     GSList *node;
688 
689     g_ptr_array_add(cwin->stores, store);
690 
691     /* set candidates */
692     for (j = i * display_limit, node = g_slist_nth(candidates, j);
693 	 display_limit ? j < display_limit * (i + 1) : j < cwin->nr_candidates;
694 	 j++, node = g_slist_next(node))
695     {
696       GtkTreeIter ti;
697       if (node) {
698 	gchar *str = node->data;
699 	gchar **column = g_strsplit(str, "\a", 3);
700 	gtk_list_store_append(store, &ti);
701 	gtk_list_store_set(store, &ti,
702 			   COLUMN_HEADING, column[0],
703 			   COLUMN_CANDIDATE, column[1],
704 			   COLUMN_ANNOTATION, column[2],
705 			   TERMINATOR);
706 	g_strfreev(column);
707 	g_free(str);
708       } else {
709 	/* No need to set any data for empty row. */
710       }
711     }
712   }
713   g_slist_free(candidates);
714 
715   if (cwin->nr_candidates <= cwin->display_limit) {
716     gtk_widget_set_sensitive(GTK_WIDGET(cwin->prev_page_button), FALSE);
717     gtk_widget_set_sensitive(GTK_WIDGET(cwin->next_page_button), FALSE);
718   } else {
719     gtk_widget_set_sensitive(GTK_WIDGET(cwin->prev_page_button), TRUE);
720     gtk_widget_set_sensitive(GTK_WIDGET(cwin->next_page_button), TRUE);
721   }
722 
723   uim_cand_win_gtk_set_page(cwin, 0);
724   update_label(cwin);
725 
726   uim_cand_win_gtk_show(cwin);
727   cwin->is_active = TRUE;
728 }
729 
730 static void
candwin_update(gchar ** str)731 candwin_update(gchar **str)
732 {
733   int index, need_hilite;
734   sscanf(str[1], "%d", &index);
735   sscanf(str[2], "%d", &need_hilite);
736   cwin->need_hilite = (need_hilite == 1) ? TRUE : FALSE;
737 
738   uim_cand_win_gtk_set_index(cwin, index);
739 }
740 
741 static void
candwin_move(char ** str)742 candwin_move(char **str)
743 {
744   sscanf(str[1], "%d", &cwin->pos_x);
745   sscanf(str[2], "%d", &cwin->pos_y);
746 
747   uim_cand_win_gtk_layout();
748 }
749 
750 static void
candwin_show(void)751 candwin_show(void)
752 {
753   if (cwin->is_active) {
754     uim_cand_win_gtk_show(cwin);
755     if (cwin->sub_window.active)
756       gtk_widget_show(cwin->sub_window.window);
757   }
758 }
759 
760 static void
candwin_deactivate(void)761 candwin_deactivate(void)
762 {
763   gtk_widget_hide(GTK_WIDGET(cwin));
764   cwin->is_active = FALSE;
765   if (cwin->sub_window.window)
766     gtk_widget_hide(cwin->sub_window.window);
767 }
768 
769 static void
caret_state_show(gchar ** str)770 caret_state_show(gchar **str)
771 {
772   int timeout;
773 
774   sscanf(str[1], "%d", &timeout);
775   caret_state_indicator_update(cwin->caret_state_indicator, cwin->pos_x, cwin->pos_y, str[2]);
776   if (timeout != 0)
777     caret_state_indicator_set_timeout(cwin->caret_state_indicator, timeout * 1000);
778   gtk_widget_show_all(GTK_WIDGET(cwin->caret_state_indicator));
779 }
780 
781 static void
caret_state_update()782 caret_state_update()
783 {
784   caret_state_indicator_update(cwin->caret_state_indicator, cwin->pos_x, cwin->pos_y, NULL);
785 }
786 
787 static void
caret_state_hide()788 caret_state_hide()
789 {
790   gtk_widget_hide(cwin->caret_state_indicator);
791 }
792 
793 static void
candwin_set_nr_candidates(gchar ** str)794 candwin_set_nr_candidates(gchar **str)
795 {
796   guint nr, display_limit;
797   gint i, nr_stores = 1;
798 
799   sscanf(str[1], "%ud", &nr);
800   sscanf(str[2], "%ud", &display_limit);
801 
802   cwin->candidate_index = -1;
803   cwin->nr_candidates = nr;
804   cwin->display_limit = display_limit;
805   cwin->need_hilite = FALSE;
806   cwin->need_page_update = FALSE;
807   cwin->is_active = TRUE;
808 
809   if (cwin->nr_candidates <= cwin->display_limit) {
810     gtk_widget_set_sensitive(GTK_WIDGET(cwin->prev_page_button), FALSE);
811     gtk_widget_set_sensitive(GTK_WIDGET(cwin->next_page_button), FALSE);
812   } else {
813     gtk_widget_set_sensitive(GTK_WIDGET(cwin->prev_page_button), TRUE);
814     gtk_widget_set_sensitive(GTK_WIDGET(cwin->next_page_button), TRUE);
815   }
816 
817   if (cwin->stores == NULL)
818     cwin->stores = g_ptr_array_new();
819 
820   /* remove old data */
821   for (i = cwin->stores->len - 1; i >= 0; i--) {
822     GtkListStore *store = g_ptr_array_remove_index(cwin->stores, i);
823     if (store) {
824       gtk_list_store_clear(store);
825       g_object_unref(G_OBJECT(store));
826     }
827   }
828 
829   /* calculate number of GtkListStores to create */
830   if (display_limit) {
831     nr_stores = nr / display_limit;
832     if (nr > display_limit * nr_stores)
833       nr_stores++;
834   }
835 
836   /* setup dummy array */
837   for (i = 0; i < nr_stores; i++)
838     g_ptr_array_add(cwin->stores, NULL);
839 }
840 
841 static void
candwin_set_page_candidates(gchar ** str)842 candwin_set_page_candidates(gchar **str)
843 {
844   gsize rbytes, wbytes;
845   gint i;
846   guint j = 1;
847   gchar *utf8_str;
848   const gchar *charset;
849   GSList *candidates = NULL;
850   int page;
851 
852   if (!strncmp(str[1], "charset=", 8))
853     charset = str[1] + 8;
854   else
855     charset = "UTF-8";
856 
857   if (!strncmp(str[2], "page=", 5)) {
858     page = atoi(str[2] + 5);
859     i = 3;
860   } else {
861     /* shouldn't happen */
862     page = 0;
863     i = 2;
864   }
865 
866   for ( ; str[i]; i++) {
867     if (strcmp(str[i], "") == 0) {
868       break;
869     }
870     utf8_str = g_convert(str[i],
871 			 -1,
872 			 "UTF-8",
873 			 charset,
874 			 &rbytes, &wbytes, NULL);
875 
876     candidates = g_slist_prepend(candidates, utf8_str);
877     j++;
878   }
879   candidates = g_slist_reverse(candidates);
880 
881   uim_cand_win_gtk_set_page_candidates(cwin, page, candidates);
882   g_slist_free(candidates);
883 }
884 
885 static void
candwin_show_page(gchar ** str)886 candwin_show_page(gchar **str)
887 {
888   int page;
889 
890   sscanf(str[1], "%d", &page);
891 
892   uim_cand_win_gtk_set_page(cwin, page);
893   uim_cand_win_gtk_show(cwin);
894 }
895 
str_parse(gchar * str)896 static void str_parse(gchar *str)
897 {
898   gchar **tmp;
899   gchar *command;
900 
901   tmp = g_strsplit(str, "\f", 0);
902   command = tmp[0];
903 
904   if (command) {
905     if (strcmp("activate", command) == 0) {
906       candwin_activate(tmp);
907     } else if (strcmp("select", command) == 0) {
908       candwin_update(tmp);
909     } else if (strcmp("show", command) == 0) {
910       candwin_show();
911     } else if (strcmp("hide", command) == 0) {
912       gtk_widget_hide(GTK_WIDGET(cwin));
913       if (cwin->sub_window.window)
914         gtk_widget_hide(cwin->sub_window.window);
915     } else if (strcmp("move", command) == 0) {
916       candwin_move(tmp);
917     } else if (strcmp("deactivate", command) == 0) {
918       candwin_deactivate();
919     } else if (strcmp("show_caret_state", command) == 0) {
920       caret_state_show(tmp);
921     } else if (strcmp("update_caret_state", command) == 0) {
922       caret_state_update();
923     } else if (strcmp("hide_caret_state", command) == 0) {
924       caret_state_hide();
925     } else if (strcmp("set_nr_candidates", command) == 0) {
926       candwin_set_nr_candidates(tmp);
927     } else if (strcmp("set_page_candidates", command) == 0) {
928       candwin_set_page_candidates(tmp);
929     } else if (strcmp("show_page", command) == 0) {
930       candwin_show_page(tmp);
931     }
932   }
933   g_strfreev(tmp);
934 }
935 
936 #define CANDIDATE_BUFFER_SIZE	4096
937 static gboolean
read_cb(GIOChannel * channel,GIOCondition c,gpointer p)938 read_cb(GIOChannel *channel, GIOCondition c, gpointer p)
939 {
940   char buf[CANDIDATE_BUFFER_SIZE];
941   char *read_buf = strdup("");
942   int i = 0;
943   int n;
944   gchar **tmp;
945   int fd = g_io_channel_unix_get_fd(channel);
946 
947   while (uim_helper_fd_readable(fd) > 0) {
948     n = read(fd, buf, CANDIDATE_BUFFER_SIZE - 1);
949     if (n == 0) {
950       close(fd);
951       exit(EXIT_FAILURE);
952     }
953     if (n == -1)
954       return TRUE;
955     buf[n] = '\0';
956     read_buf = realloc(read_buf, strlen(read_buf) + n + 1);
957     strcat(read_buf, buf);
958   }
959 
960   tmp = g_strsplit(read_buf, "\f\f", 0);
961 
962   while (tmp[i]) {
963     str_parse(tmp[i]);
964     i++;
965   }
966   g_strfreev(tmp);
967   free(read_buf);
968   return TRUE;
969 }
970 
971 int
main(int argc,char * argv[])972 main(int argc, char *argv[])
973 {
974   GIOChannel *channel;
975 
976   /* disable uim context in annotation window */
977   setenv("GTK_IM_MODULE", "gtk-im-context-simple", 1);
978 
979   gtk_init(&argc, &argv);
980   if (uim_init() < 0)
981     return 0;
982 
983   init_candidate_win();
984 
985   channel = g_io_channel_unix_new(0);
986   read_tag = g_io_add_watch(channel, G_IO_IN | G_IO_HUP | G_IO_ERR,
987 			    read_cb, 0);
988   g_io_channel_unref(channel);
989 
990   gtk_main();
991   uim_quit();
992 
993   return 0;
994 }
995 
996 /* copied from uim-cand-win-gtk.c */
997 static gint
uim_cand_win_gtk_get_index(UIMCandidateWindow * cwin)998 uim_cand_win_gtk_get_index(UIMCandidateWindow *cwin)
999 {
1000   g_return_val_if_fail(UIM_IS_CANDIDATE_WINDOW(cwin), -1);
1001 
1002   return cwin->candidate_index;
1003 }
1004 
1005 /* copied from uim-cand-win-gtk.c */
1006 static void
uim_cand_win_gtk_set_index(UIMCandidateWindow * cwin,gint index)1007 uim_cand_win_gtk_set_index(UIMCandidateWindow *cwin, gint index)
1008 {
1009   gint new_page, prev_index;
1010 
1011   g_return_if_fail(UIM_IS_CANDIDATE_WINDOW(cwin));
1012 
1013   prev_index = cwin->candidate_index;
1014   if (index >= (gint) cwin->nr_candidates)
1015     cwin->candidate_index = 0;
1016   else
1017     cwin->candidate_index = index;
1018 
1019   if (cwin->candidate_index >= 0 && cwin->display_limit)
1020     new_page = cwin->candidate_index / cwin->display_limit;
1021   else
1022     new_page = cwin->page_index;
1023 
1024   if (cwin->page_index != new_page || cwin->need_page_update)
1025     uim_cand_win_gtk_set_page(cwin, new_page);
1026 
1027   if (cwin->candidate_index >= 0 && cwin->need_hilite) {
1028     gint pos;
1029     struct index_button *idxbutton, *prev_selected;
1030     GtkWidget *label;
1031 
1032     if (cwin->display_limit)
1033       pos = cwin->candidate_index % cwin->display_limit;
1034     else
1035       pos = cwin->candidate_index;
1036 
1037     idxbutton = g_ptr_array_index(cwin->buttons, pos);
1038     prev_selected = (gpointer)cwin->selected;
1039     if (prev_selected && prev_index != cwin->candidate_index) {
1040       label = gtk_bin_get_child(GTK_BIN(prev_selected->button));
1041       gtk_widget_queue_draw(label);
1042     }
1043     label = gtk_bin_get_child(GTK_BIN(idxbutton->button));
1044     gtk_widget_queue_draw(label);
1045     cwin->selected = idxbutton;
1046 
1047     /* show subwin */
1048     if (cwin->stores->pdata[new_page]) {
1049       char *annotation = NULL;
1050       GtkTreeModel *model = GTK_TREE_MODEL(cwin->stores->pdata[new_page]);
1051       GtkTreeIter iter;
1052 
1053       gtk_tree_model_iter_nth_child(model, &iter, NULL, pos);
1054       gtk_tree_model_get(model, &iter, COLUMN_ANNOTATION, &annotation, -1);
1055 
1056       if (annotation && *annotation) {
1057         if (!cwin->sub_window.window)
1058           uim_cand_win_gtk_create_sub_window(cwin);
1059         gtk_text_buffer_set_text(gtk_text_view_get_buffer(GTK_TEXT_VIEW(cwin->sub_window.text_view)), annotation, -1);
1060         uim_cand_win_gtk_layout_sub_window(cwin);
1061         gtk_widget_show(cwin->sub_window.window);
1062         cwin->sub_window.active = TRUE;
1063       } else {
1064         if (cwin->sub_window.window) {
1065           gtk_widget_hide(cwin->sub_window.window);
1066           cwin->sub_window.active = FALSE;
1067         }
1068       }
1069       free(annotation);
1070     }
1071   } else {
1072     cwin->selected = NULL;
1073     if (cwin->sub_window.window) {
1074       gtk_widget_hide(cwin->sub_window.window);
1075       cwin->sub_window.active = FALSE;
1076     }
1077   }
1078 
1079   update_label(cwin);
1080 }
1081 
1082 static void
scale_label(GtkEventBox * button,double scale)1083 scale_label(GtkEventBox *button, double scale)
1084 {
1085   GtkWidget *label;
1086   PangoAttrList *attrs = pango_attr_list_new();
1087   PangoAttribute *attr = pango_attr_scale_new(scale);
1088 
1089   pango_attr_list_insert(attrs, attr);
1090   label = gtk_bin_get_child(GTK_BIN(button));
1091   if (GTK_IS_LABEL(label))
1092     gtk_label_set_attributes(GTK_LABEL(label), attrs);
1093   pango_attr_list_unref(attrs);
1094 }
1095 
1096 static void
clear_button(struct index_button * idxbutton,gint cell_index)1097 clear_button(struct index_button *idxbutton, gint cell_index)
1098 {
1099   GtkEventBox *button;
1100   GtkWidget *label;
1101 
1102   idxbutton->cand_index_in_page = -1;
1103   button = idxbutton->button;
1104 
1105   label = gtk_bin_get_child(GTK_BIN(button));
1106   gtk_label_set_text(GTK_LABEL(label), "");
1107   scale_label(button, PANGO_SCALE_LARGE);
1108 }
1109 
1110 static void
clear_all_buttons(GPtrArray * buttons)1111 clear_all_buttons(GPtrArray *buttons)
1112 {
1113   gint i;
1114 
1115   for (i = 0; i < (gint)buttons->len; i++) {
1116     struct index_button *idxbutton;
1117 
1118     idxbutton = g_ptr_array_index(buttons, i);
1119     if (idxbutton && idxbutton->cand_index_in_page != -1) {
1120       clear_button(idxbutton, i);
1121     }
1122   }
1123 }
1124 
1125 static void
update_table_button(GtkTreeModel * model,GPtrArray * buttons,gint display_limit)1126 update_table_button(GtkTreeModel *model, GPtrArray *buttons, gint display_limit)
1127 {
1128   GtkTreeIter ti;
1129   gboolean has_next;
1130   gint cand_index = 0;
1131   gint len;
1132 
1133   len = buttons->len;
1134 
1135   clear_all_buttons(buttons);
1136   has_next = gtk_tree_model_get_iter_first(model, &ti);
1137   while (has_next) {
1138     gchar *heading;
1139     gchar *cand_str;
1140     GtkEventBox *button = NULL;
1141 
1142     gtk_tree_model_get(model, &ti, COLUMN_HEADING, &heading,
1143         COLUMN_CANDIDATE, &cand_str, TERMINATOR);
1144     if (cand_str != NULL) {
1145       button = assign_cellbutton(buttons, cand_index, display_limit);
1146       if (button != NULL) {
1147         GtkWidget *label;
1148 	label = gtk_bin_get_child(GTK_BIN(button));
1149 	if (heading && heading[0] != '\0') {
1150 	  gchar *text = g_strdup_printf("%s: %s", heading, cand_str);
1151 	  gtk_label_set_text(GTK_LABEL(label), text);
1152 	  g_free(text);
1153 	} else {
1154 	  gtk_label_set_text(GTK_LABEL(label), cand_str);
1155 	}
1156 	scale_label(button, PANGO_SCALE_LARGE);
1157       }
1158     }
1159 
1160     g_free(cand_str);
1161     g_free(heading);
1162     cand_index++;
1163     has_next = gtk_tree_model_iter_next(model, &ti);
1164   }
1165 
1166   if (cand_index < len) {
1167     gint i;
1168     for (i = len - 1; i >= cand_index; i--) {
1169       struct index_button *idxbutton;
1170       idxbutton = g_ptr_array_index(buttons, i);
1171       if (idxbutton == cwin->selected)
1172 	cwin->selected = NULL;
1173       gtk_widget_destroy(GTK_WIDGET(idxbutton->button));
1174       g_free(idxbutton);
1175       g_ptr_array_remove_index(buttons, i);
1176     }
1177 #if !GTK_CHECK_VERSION(3, 4, 0)
1178     gtk_table_resize(GTK_TABLE(cwin->view), 1, cand_index);
1179 #endif
1180   }
1181 }
1182 
1183 /* copied from uim-cand-win-gtk.c */
1184 static void
uim_cand_win_gtk_set_page(UIMCandidateWindow * cwin,gint page)1185 uim_cand_win_gtk_set_page(UIMCandidateWindow *cwin, gint page)
1186 {
1187   guint len, new_page;
1188   gint new_index;
1189 
1190   g_return_if_fail(UIM_IS_CANDIDATE_WINDOW(cwin));
1191   g_return_if_fail(cwin->stores);
1192 
1193   len = cwin->stores->len;
1194   g_return_if_fail(len);
1195 
1196   if (page < 0)
1197     new_page = len - 1;
1198   else if (page >= (gint) len)
1199     new_page = 0;
1200   else
1201     new_page = page;
1202 
1203   if (cwin->stores->pdata[new_page]) {
1204     update_table_button(GTK_TREE_MODEL(cwin->stores->pdata[new_page]),
1205                         cwin->buttons, cwin->display_limit);
1206 #if GTK_CHECK_VERSION(3, 4, 0)
1207     show_table(GTK_GRID(cwin->view), cwin->buttons);
1208 #else
1209     show_table(GTK_TABLE(cwin->view), cwin->buttons);
1210 #endif
1211   }
1212 
1213   cwin->page_index = new_page;
1214 
1215   if (cwin->display_limit) {
1216     if (cwin->candidate_index >= 0)
1217       new_index
1218 	= (new_page * cwin->display_limit) + (cwin->candidate_index % cwin->display_limit);
1219     else
1220       new_index = -1;
1221   } else {
1222     new_index = cwin->candidate_index;
1223   }
1224 
1225   if (new_index >= (gint) cwin->nr_candidates)
1226     new_index = cwin->nr_candidates - 1;
1227 
1228   /* shrink the window */
1229   gtk_window_resize(GTK_WINDOW(cwin), CANDWIN_DEFAULT_WIDTH, 1);
1230 
1231   cwin->need_page_update = FALSE; /* avoid infinite loop with set_index() */
1232   uim_cand_win_gtk_set_index(cwin, new_index);
1233 }
1234 
1235 /* copied from uim-cand-win-gtk.c and adjusted */
1236 static void
uim_cand_win_gtk_set_page_candidates(UIMCandidateWindow * cwin,guint page,GSList * candidates)1237 uim_cand_win_gtk_set_page_candidates(UIMCandidateWindow *cwin,
1238 				     guint page,
1239 				     GSList *candidates)
1240 {
1241   GtkListStore *store;
1242   GSList *node;
1243   gint j, len;
1244 
1245   g_return_if_fail(UIM_IS_CANDIDATE_WINDOW(cwin));
1246 
1247   if (candidates == NULL)
1248     return;
1249 
1250   cwin->sub_window.active = FALSE;
1251   len = g_slist_length(candidates);
1252 
1253   /* create GtkListStores, and set candidates */
1254   store = gtk_list_store_new(LISTSTORE_NR_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
1255 
1256   cwin->stores->pdata[page] = store;
1257   /* set candidates */
1258   for (j = 0, node = g_slist_nth(candidates, j);
1259        j < len;
1260        j++, node = g_slist_next(node))
1261   {
1262     GtkTreeIter ti;
1263 
1264     if (node) {
1265       gchar *str = node->data;
1266       gchar **column = g_strsplit(str, "\a", 3);
1267       gtk_list_store_append(store, &ti);
1268       gtk_list_store_set(store, &ti,
1269 			 COLUMN_HEADING, column[0],
1270 			 COLUMN_CANDIDATE, column[1],
1271 			 COLUMN_ANNOTATION, column[2],
1272 			 TERMINATOR);
1273 
1274       g_strfreev(column);
1275       g_free(str);
1276     }
1277   }
1278 }
1279 
1280 static void
uim_cand_win_gtk_layout()1281 uim_cand_win_gtk_layout()
1282 {
1283   int x, y;
1284   int screen_width, screen_height;
1285 
1286   screen_width = gdk_screen_get_width(gdk_screen_get_default());
1287   screen_height = gdk_screen_get_height(gdk_screen_get_default());
1288 
1289   if (screen_width < cwin->pos_x + cwin->width)
1290     /* x = cwin->pos_x - cwin->width; */
1291     x = screen_width - cwin->width;
1292   else
1293     x = cwin->pos_x;
1294 
1295   if (screen_height < cwin->pos_y + cwin->height)
1296     y = cwin->pos_y - cwin->height - 20; /* FIXME: Preedit height is needed to
1297 					    be sent by uim-xim */
1298   else
1299     y = cwin->pos_y;
1300 
1301   gtk_window_move(GTK_WINDOW(cwin), x, y);
1302 
1303   uim_cand_win_gtk_layout_sub_window(cwin);
1304 }
1305 
1306 /* copied from uim-cand-win-gtk.c */
1307 static void
uim_cand_win_gtk_create_sub_window(UIMCandidateWindow * cwin)1308 uim_cand_win_gtk_create_sub_window(UIMCandidateWindow *cwin)
1309 {
1310   GtkWidget *window, *scrwin, *text_view, *frame;
1311   GdkGeometry hints;
1312 
1313   if (cwin->sub_window.window)
1314     return;
1315 
1316   cwin->sub_window.window = window = gtk_window_new(GTK_WINDOW_POPUP);
1317 
1318   frame = gtk_frame_new(NULL);
1319   gtk_container_set_border_width(GTK_CONTAINER(frame), 0);
1320 
1321   hints.min_width = UIM_ANNOTATION_WIN_WIDTH;
1322   hints.min_height = UIM_ANNOTATION_WIN_HEIGHT;
1323   hints.max_width = UIM_ANNOTATION_WIN_WIDTH;
1324   hints.max_height = UIM_ANNOTATION_WIN_HEIGHT;
1325   gtk_window_set_geometry_hints(GTK_WINDOW(window), frame, &hints, GDK_HINT_MAX_SIZE | GDK_HINT_MIN_SIZE);
1326 
1327   cwin->sub_window.scrolled_window = scrwin = gtk_scrolled_window_new(NULL, NULL);
1328   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrwin),
1329                                  GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1330 
1331   cwin->sub_window.text_view = text_view = gtk_text_view_new();
1332   gtk_text_view_set_editable(GTK_TEXT_VIEW(text_view), FALSE);
1333   gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text_view), GTK_WRAP_WORD_CHAR);
1334   gtk_widget_show(text_view);
1335 
1336   gtk_container_add(GTK_CONTAINER(scrwin), text_view);
1337   gtk_container_add(GTK_CONTAINER(frame), scrwin);
1338   gtk_container_add(GTK_CONTAINER(window), frame);
1339   gtk_widget_show(frame);
1340   gtk_widget_show(scrwin);
1341   gtk_widget_show(text_view);
1342 }
1343 
1344 /* copied from uim-cand-win-horizontal-gtk.c */
1345 static void
uim_cand_win_gtk_layout_sub_window(UIMCandidateWindow * cwin)1346 uim_cand_win_gtk_layout_sub_window(UIMCandidateWindow *cwin)
1347 {
1348 #if GTK_CHECK_VERSION(2, 90, 0)
1349   gint x, y, w, h, x2, y2, w2, h2, x3, y3;
1350 #else
1351   gint x, y, w, h, x2, y2, w2, h2, d, d2, x3, y3;
1352 #endif
1353   struct index_button *idxbutton;
1354 
1355   if (!cwin->sub_window.window)
1356     return;
1357 
1358 #if GTK_CHECK_VERSION(2, 90, 0)
1359   gdk_window_get_geometry(gtk_widget_get_window(GTK_WIDGET(cwin)),
1360                           &x, &y, &w, &h);
1361 #else
1362   gdk_window_get_geometry(gtk_widget_get_window(GTK_WIDGET(cwin)),
1363                           &x, &y, &w, &h, &d);
1364 #endif
1365   gdk_window_get_origin(gtk_widget_get_window(GTK_WIDGET(cwin)), &x, &y);
1366 
1367 #if GTK_CHECK_VERSION(2, 90, 0)
1368   gdk_window_get_geometry(gtk_widget_get_window(cwin->sub_window.window),
1369                           &x2, &y2, &w2, &h2);
1370 #else
1371   gdk_window_get_geometry(gtk_widget_get_window(cwin->sub_window.window),
1372                           &x2, &y2, &w2, &h2, &d2);
1373 #endif
1374 
1375   if (cwin->selected) {
1376     GtkWidget *button;
1377     idxbutton = cwin->selected;
1378     button = GTK_WIDGET(idxbutton->button);
1379     gdk_window_get_origin(gtk_widget_get_window(button), &x3, &y3);
1380 
1381 #if GTK_CHECK_VERSION(2, 18, 0)
1382     if (!gtk_widget_get_has_window(button)) {
1383       GtkAllocation allocation;
1384       gtk_widget_get_allocation(button, &allocation);
1385       x3 += allocation.x;
1386     }
1387 #else
1388     if (GTK_WIDGET_NO_WINDOW(button))
1389       x3 += button->allocation.x;
1390 #endif
1391   }
1392   y = y + h;
1393 
1394   gtk_window_move(GTK_WINDOW(cwin->sub_window.window), x3, y);
1395 }
1396 static void
1397 #if GTK_CHECK_VERSION(3, 4, 0)
show_table(GtkGrid * view,GPtrArray * buttons)1398 show_table(GtkGrid *view, GPtrArray *buttons)
1399 #else
1400 show_table(GtkTable *view, GPtrArray *buttons)
1401 #endif
1402 {
1403   gint col;
1404 
1405   for (col = 0; col < (gint)buttons->len; col++) {
1406     GtkEventBox *button = NULL;
1407     struct index_button *idxbutton;
1408 
1409     idxbutton = g_ptr_array_index(buttons, col);
1410     button = idxbutton->button;
1411 
1412     gtk_widget_show_all(GTK_WIDGET(button));
1413   }
1414   gtk_widget_show(GTK_WIDGET(view));
1415 }
1416 
1417 
1418 static void
uim_cand_win_gtk_show(UIMCandidateWindow * cwin)1419 uim_cand_win_gtk_show(UIMCandidateWindow *cwin)
1420 {
1421   gtk_widget_show(GTK_WIDGET(cwin->viewport));
1422   gtk_widget_show(GTK_WIDGET(cwin->scrolled_window));
1423   gtk_widget_show_all(GTK_WIDGET(cwin->hbox));
1424   gtk_widget_show(GTK_WIDGET(cwin->vbox));
1425   gtk_widget_show(GTK_WIDGET(cwin->frame));
1426   gtk_widget_show(GTK_WIDGET(cwin));
1427 }
1428 
1429 static gboolean
configure_event_cb(GtkWidget * widget,GdkEventConfigure * event,gpointer data)1430 configure_event_cb(GtkWidget *widget, GdkEventConfigure *event, gpointer data)
1431 {
1432   cwin->width = event->width;
1433   cwin->height = event->height;
1434 
1435   uim_cand_win_gtk_layout();
1436 
1437   return FALSE;
1438 }
1439