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 #include <config.h>
35 
36 #include "uim-cand-win-horizontal-gtk.h"
37 #include <string.h>
38 #include <stdlib.h>
39 #include <math.h>
40 
41 #include <uim/uim.h>
42 #include <uim/uim-scm.h>
43 
44 #define DEFAULT_MIN_WINDOW_WIDTH 60
45 
46 enum {
47   TERMINATOR = -1,
48   COLUMN_HEADING,
49   COLUMN_CANDIDATE,
50   COLUMN_ANNOTATION,
51   LISTSTORE_NR_COLUMNS
52 };
53 
54 #define DEFAULT_NR_CELLS 10
55 
56 struct index_button {
57   gint cand_index_in_page;
58   GtkEventBox *button;
59 };
60 
61 static void uim_cand_win_horizontal_gtk_init(UIMCandWinHorizontalGtk *cwin);
62 static void uim_cand_win_horizontal_gtk_class_init(UIMCandWinGtkClass *klass);
63 static void uim_cand_win_horizontal_gtk_dispose(GObject *obj);
64 static void button_clicked(GtkEventBox *button, GdkEventButton *event, gpointer data);
65 #if GTK_CHECK_VERSION(2, 90, 0)
66 static gboolean label_draw(GtkWidget *label, cairo_t *cr, gpointer data);
67 #else
68 static gboolean label_exposed(GtkWidget *label, GdkEventExpose *event, gpointer data);
69 #endif
70 static void clear_button(struct index_button *idxbutton, gint cell_index);
71 #if GTK_CHECK_VERSION(3, 4, 0)
72 static void show_table(GtkGrid *view, GPtrArray *buttons);
73 #else
74 static void show_table(GtkTable *view, GPtrArray *buttons);
75 #endif
76 static void scale_label(GtkEventBox *button, double factor);
77 
78 
79 static GType cand_win_horizontal_type = 0;
80 static GTypeInfo const object_info = {
81   sizeof (UIMCandWinHorizontalGtkClass),
82   (GBaseInitFunc) NULL,
83   (GBaseFinalizeFunc) NULL,
84   (GClassInitFunc) uim_cand_win_horizontal_gtk_class_init,
85   (GClassFinalizeFunc) NULL,
86   NULL,                       /* class_data */
87   sizeof (UIMCandWinHorizontalGtk),
88   0,                          /* n_preallocs */
89   (GInstanceInitFunc) uim_cand_win_horizontal_gtk_init,
90 };
91 
92 static GtkWindowClass *parent_class = NULL;
93 
94 GType
uim_cand_win_horizontal_gtk_get_type(void)95 uim_cand_win_horizontal_gtk_get_type(void)
96 {
97   if (!cand_win_horizontal_type)
98     cand_win_horizontal_type = g_type_register_static(UIM_TYPE_CAND_WIN_GTK, "UIMCandWinHorizontalGtk",
99 					   &object_info, (GTypeFlags)0);
100   return cand_win_horizontal_type;
101 }
102 
103 GType
uim_cand_win_horizontal_gtk_register_type(GTypeModule * module)104 uim_cand_win_horizontal_gtk_register_type(GTypeModule *module)
105 {
106   if (!cand_win_horizontal_type)
107     cand_win_horizontal_type = g_type_module_register_type(module,
108 						UIM_TYPE_CAND_WIN_GTK,
109 						"UIMCandWinHorizontalGtk",
110 						&object_info, 0);
111   return cand_win_horizontal_type;
112 }
113 
114 static void
uim_cand_win_horizontal_gtk_class_init(UIMCandWinGtkClass * klass)115 uim_cand_win_horizontal_gtk_class_init (UIMCandWinGtkClass *klass)
116 {
117   GObjectClass *object_class = (GObjectClass *) klass;
118 
119   parent_class = g_type_class_peek_parent (klass);
120   object_class->dispose = uim_cand_win_horizontal_gtk_dispose;
121 
122   klass->set_index = (void (*)(UIMCandWinGtk *, gint))uim_cand_win_horizontal_gtk_set_index;
123   klass->set_page = (void (*)(UIMCandWinGtk *, gint))uim_cand_win_horizontal_gtk_set_page;
124   klass->create_sub_window = (void (*)(UIMCandWinGtk *))uim_cand_win_horizontal_gtk_create_sub_window;
125   klass->layout_sub_window = (void (*)(UIMCandWinGtk *))uim_cand_win_horizontal_gtk_layout_sub_window;
126 }
127 
128 static void
uim_cand_win_horizontal_gtk_init(UIMCandWinHorizontalGtk * horizontal_cwin)129 uim_cand_win_horizontal_gtk_init (UIMCandWinHorizontalGtk *horizontal_cwin)
130 {
131   gint col;
132   GtkWidget *viewport;
133   UIMCandWinGtk *cwin;
134 
135   cwin = UIM_CAND_WIN_GTK(horizontal_cwin);
136 
137   horizontal_cwin->buttons = g_ptr_array_new();
138   horizontal_cwin->selected = NULL;
139 
140 #if GTK_CHECK_VERSION(3, 4, 0)
141   cwin->view = gtk_grid_new();
142   gtk_grid_set_column_spacing(GTK_GRID(cwin->view), 10);
143 #else
144   cwin->view = gtk_table_new(1, DEFAULT_NR_CELLS, FALSE);
145   gtk_table_set_col_spacings(GTK_TABLE(cwin->view), 10);
146 #endif
147   viewport = gtk_viewport_new(NULL, NULL);
148   gtk_container_add(GTK_CONTAINER(viewport), cwin->view);
149   gtk_container_add(GTK_CONTAINER(cwin->scrolled_window), viewport);
150   gtk_container_set_resize_mode(GTK_CONTAINER(viewport), GTK_RESIZE_PARENT);
151   for (col = 0; col < DEFAULT_NR_CELLS; col++) {
152     GtkWidget *button;
153     GtkWidget *label;
154     struct index_button *idxbutton;
155 
156     button = gtk_event_box_new();
157     gtk_event_box_set_above_child(GTK_EVENT_BOX(button), TRUE);
158     label = gtk_label_new("");
159     gtk_container_add(GTK_CONTAINER(button), label);
160     scale_label(GTK_EVENT_BOX(button), PANGO_SCALE_LARGE);
161     g_signal_connect(button, "button-press-event", G_CALLBACK(button_clicked), horizontal_cwin);
162 #if GTK_CHECK_VERSION(2, 90, 0)
163     g_signal_connect_after(label, "draw", G_CALLBACK(label_draw), horizontal_cwin);
164 #else
165     g_signal_connect_after(label, "expose-event", G_CALLBACK(label_exposed), horizontal_cwin);
166 #endif
167 #if GTK_CHECK_VERSION(3, 4, 0)
168     gtk_widget_set_hexpand(button, TRUE);
169     gtk_widget_set_vexpand(button, TRUE);
170     gtk_grid_attach(GTK_GRID(cwin->view), button, col, 0, 1, 1);
171 #else
172     gtk_table_attach_defaults(GTK_TABLE(cwin->view), button, col, col + 1, 0, 1);
173 #endif
174     idxbutton = g_malloc(sizeof(struct index_button));
175     if (idxbutton) {
176       idxbutton->button = GTK_EVENT_BOX(button);
177       clear_button(idxbutton, col);
178     }
179     g_ptr_array_add(horizontal_cwin->buttons, idxbutton);
180   }
181 
182   gtk_widget_show_all(cwin->view);
183   gtk_widget_show(viewport);
184 
185   gtk_widget_set_size_request(cwin->num_label, DEFAULT_MIN_WINDOW_WIDTH, -1);
186   gtk_window_set_default_size(GTK_WINDOW(cwin), DEFAULT_MIN_WINDOW_WIDTH, -1);
187   gtk_window_set_resizable(GTK_WINDOW(cwin), FALSE);
188 }
189 
190 #if !GTK_CHECK_VERSION(2, 90, 0)
191 static void
get_layout_x(GtkLabel * label,gint * xp)192 get_layout_x(GtkLabel *label, gint *xp)
193 {
194   GtkMisc *misc;
195   GtkWidget *widget;
196   gfloat xalign;
197   gint req_width, x;
198 
199   misc = GTK_MISC(label);
200   widget = GTK_WIDGET(label);
201 
202   if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_LTR)
203     xalign = misc->xalign;
204   else
205     xalign = 1.0 - misc->xalign;
206 
207   req_width = widget->requisition.width;
208 
209   x = floor(widget->allocation.x + (gint)misc->xpad +
210 	     xalign * (widget->allocation.width - req_width));
211 
212   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
213     x = MAX(x, widget->allocation.x + misc->xpad);
214   else
215     x = MIN(x, widget->allocation.x + widget->allocation.width - misc->xpad);
216 
217   if (xp)
218     *xp = x;
219 }
220 #endif
221 
222 #if GTK_CHECK_VERSION(2, 90, 0)
223 static gboolean
label_draw(GtkWidget * label,cairo_t * cr,gpointer data)224 label_draw(GtkWidget *label, cairo_t *cr, gpointer data)
225 {
226   UIMCandWinHorizontalGtk *horizontal_cwin = data;
227   struct index_button *selected;
228   GtkWidget *selected_label = NULL;
229   GdkRGBA *bg_color, *fg_color;
230   GtkStyleContext *context;
231   PangoLayout *layout;
232   gint x, y;
233   GtkStateFlags state;
234 
235   selected = horizontal_cwin->selected;
236   if (selected)
237     selected_label = gtk_bin_get_child(GTK_BIN(selected->button));
238 
239   layout = gtk_label_get_layout(GTK_LABEL(label));
240   gtk_label_get_layout_offsets(GTK_LABEL(label), &x, &y);
241 
242   context = gtk_widget_get_style_context(label);
243 
244   if (label == selected_label)
245     state = GTK_STATE_FLAG_SELECTED;
246   else
247     state = GTK_STATE_FLAG_NORMAL;
248 
249   gtk_style_context_get (context, state, "background-color", &bg_color, "color", &fg_color, NULL);
250 
251   cairo_save(cr);
252   gdk_cairo_set_source_rgba(cr, bg_color);
253   cairo_paint(cr);
254   cairo_restore(cr);
255   gdk_rgba_free(bg_color);
256   gdk_rgba_free(fg_color);
257 
258   gtk_style_context_set_state (context, state);
259   gtk_render_layout (context, cr, x, y, layout);
260 
261   return FALSE;
262 }
263 #else
264 static gboolean
label_exposed(GtkWidget * label,GdkEventExpose * event,gpointer data)265 label_exposed(GtkWidget *label, GdkEventExpose *event, gpointer data)
266 {
267   UIMCandWinHorizontalGtk *horizontal_cwin = data;
268   struct index_button *selected;
269   GtkWidget *selected_label = NULL;
270 
271   selected = horizontal_cwin->selected;
272   if (selected)
273     selected_label = gtk_bin_get_child(GTK_BIN(selected->button));
274 
275   if (label == selected_label) {
276     gint x;
277     get_layout_x(GTK_LABEL(label), &x);
278     gdk_draw_layout_with_colors(label->window,
279 		      label->style->black_gc, x, 0,
280 		      GTK_LABEL(label)->layout,
281 		      &label->style->text[GTK_STATE_SELECTED],
282 		      &label->style->bg[GTK_STATE_SELECTED]);
283   }
284 
285   return FALSE;
286 }
287 #endif
288 
289 static void
button_clicked(GtkEventBox * button,GdkEventButton * event,gpointer data)290 button_clicked(GtkEventBox *button, GdkEventButton *event, gpointer data)
291 {
292   UIMCandWinHorizontalGtk *horizontal_cwin = data;
293   UIMCandWinGtk *cwin = UIM_CAND_WIN_GTK(horizontal_cwin);
294   gint i;
295   gint idx = -1;
296   struct index_button *prev_selected;
297 
298   prev_selected = horizontal_cwin->selected;
299   if (prev_selected) {
300     GtkWidget *label = gtk_bin_get_child(GTK_BIN(prev_selected->button));
301     gtk_widget_queue_draw(label);
302   }
303 
304   for (i = 0; i < (gint)horizontal_cwin->buttons->len; i++) {
305     GtkEventBox *p;
306     struct index_button *idxbutton;
307     idxbutton = g_ptr_array_index(horizontal_cwin->buttons, i);
308     if (!idxbutton) {
309       continue;
310     }
311     p = idxbutton->button;
312     if (p == button) {
313       GtkWidget *label = gtk_bin_get_child(GTK_BIN(button));
314       idx = idxbutton->cand_index_in_page;
315       gtk_widget_queue_draw(label);
316       horizontal_cwin->selected = idxbutton;
317       break;
318     }
319   }
320   if (idx >= 0 && cwin->display_limit) {
321     if (idx >= (gint)cwin->display_limit) {
322       idx %= cwin->display_limit;
323     }
324     cwin->candidate_index = cwin->page_index * cwin->display_limit + idx;
325   } else {
326     cwin->candidate_index = idx;
327   }
328   if (cwin->candidate_index >= (gint)cwin->nr_candidates) {
329     cwin->candidate_index = -1;
330   }
331 
332   g_signal_emit_by_name(G_OBJECT(cwin), "index-changed");
333 }
334 
335 static void
uim_cand_win_horizontal_gtk_dispose(GObject * obj)336 uim_cand_win_horizontal_gtk_dispose (GObject *obj)
337 {
338   UIMCandWinHorizontalGtk *horizontal_cwin;
339 
340   g_return_if_fail(UIM_IS_CAND_WIN_HORIZONTAL_GTK(obj));
341 
342   horizontal_cwin = UIM_CAND_WIN_HORIZONTAL_GTK(obj);
343 
344   if (horizontal_cwin->buttons) {
345     guint i;
346     for (i = 0; i < horizontal_cwin->buttons->len; i++) {
347       g_free(horizontal_cwin->buttons->pdata[i]);
348       /* GtkEventBox is destroyed by container */
349     }
350     g_ptr_array_free(horizontal_cwin->buttons, TRUE);
351     horizontal_cwin->buttons = NULL;
352   }
353   horizontal_cwin->selected = NULL;
354 
355   if (G_OBJECT_CLASS (parent_class)->dispose)
356     G_OBJECT_CLASS (parent_class)->dispose(obj);
357 }
358 
359 UIMCandWinHorizontalGtk *
uim_cand_win_horizontal_gtk_new(void)360 uim_cand_win_horizontal_gtk_new (void)
361 {
362   GObject *obj = g_object_new(UIM_TYPE_CAND_WIN_HORIZONTAL_GTK,
363 			      "type", GTK_WINDOW_POPUP,
364 			      NULL);
365 
366   return UIM_CAND_WIN_HORIZONTAL_GTK(obj);
367 }
368 
369 static GtkEventBox*
assign_cellbutton(UIMCandWinHorizontalGtk * horizontal_cwin,gint cand_index,gint display_limit)370 assign_cellbutton(UIMCandWinHorizontalGtk *horizontal_cwin,
371 		  gint cand_index, gint display_limit)
372 {
373   struct index_button *idxbutton;
374   int len;
375   GPtrArray *buttons;
376 
377   buttons = horizontal_cwin->buttons;
378   len = buttons->len;
379 
380   if (len <= cand_index) {
381     GtkWidget *button;
382     GtkWidget *label;
383 
384     button = gtk_event_box_new();
385     gtk_event_box_set_above_child(GTK_EVENT_BOX(button), TRUE);
386     label = gtk_label_new("");
387     gtk_container_add(GTK_CONTAINER(button), label);
388     scale_label(GTK_EVENT_BOX(button), PANGO_SCALE_LARGE);
389     g_signal_connect(button, "button-press-event", G_CALLBACK(button_clicked), horizontal_cwin);
390 #if GTK_CHECK_VERSION(2, 90, 0)
391     g_signal_connect_after(label, "draw", G_CALLBACK(label_draw), horizontal_cwin);
392 #else
393     g_signal_connect_after(label, "expose-event", G_CALLBACK(label_exposed), horizontal_cwin);
394 #endif
395 #if GTK_CHECK_VERSION(3, 4, 0)
396     gtk_widget_set_hexpand(button, TRUE);
397     gtk_widget_set_vexpand(button, TRUE);
398     gtk_grid_attach(GTK_GRID(UIM_CAND_WIN_GTK(horizontal_cwin)->view), button,
399                     cand_index, 0, 1, 1);
400 #else
401     gtk_table_attach_defaults(GTK_TABLE(UIM_CAND_WIN_GTK(horizontal_cwin)->view), button, cand_index, cand_index + 1, 0, 1);
402 #endif
403     idxbutton = g_malloc(sizeof(struct index_button));
404     if (idxbutton) {
405       idxbutton->button = GTK_EVENT_BOX(button);
406       clear_button(idxbutton, cand_index);
407       idxbutton->cand_index_in_page = cand_index;
408     }
409     g_ptr_array_add(horizontal_cwin->buttons, idxbutton);
410   } else {
411     idxbutton = g_ptr_array_index(buttons, cand_index);
412     idxbutton->cand_index_in_page = cand_index;
413   }
414 
415   return idxbutton->button;
416 }
417 
418 void
uim_cand_win_horizontal_gtk_set_index(UIMCandWinHorizontalGtk * horizontal_cwin,gint index)419 uim_cand_win_horizontal_gtk_set_index(UIMCandWinHorizontalGtk *horizontal_cwin, gint index)
420 {
421   gint new_page, prev_index;
422   UIMCandWinGtk *cwin;
423 
424   g_return_if_fail(UIM_IS_CAND_WIN_HORIZONTAL_GTK(horizontal_cwin));
425   cwin = UIM_CAND_WIN_GTK(horizontal_cwin);
426 
427   prev_index = cwin->candidate_index;
428   if (index >= (gint) cwin->nr_candidates)
429     cwin->candidate_index = 0;
430   else
431     cwin->candidate_index = index;
432 
433   if (cwin->candidate_index >= 0 && cwin->display_limit)
434     new_page = cwin->candidate_index / cwin->display_limit;
435   else
436     new_page = cwin->page_index;
437 
438   if (cwin->page_index != new_page)
439     uim_cand_win_gtk_set_page(cwin, new_page);
440 
441   if (cwin->candidate_index >= 0) {
442     gint pos;
443     struct index_button *idxbutton, *prev_selected;
444     GtkWidget *label;
445 
446     if (cwin->display_limit)
447       pos = cwin->candidate_index % cwin->display_limit;
448     else
449       pos = cwin->candidate_index;
450 
451     idxbutton = g_ptr_array_index(horizontal_cwin->buttons, pos);
452     prev_selected = (gpointer)horizontal_cwin->selected;
453     if (prev_selected && prev_index != cwin->candidate_index) {
454       label = gtk_bin_get_child(GTK_BIN(prev_selected->button));
455       gtk_widget_queue_draw(label);
456     }
457     label = gtk_bin_get_child(GTK_BIN(idxbutton->button));
458     gtk_widget_queue_draw(label);
459     horizontal_cwin->selected = idxbutton;
460 
461     /* show subwin */
462     if (cwin->stores->pdata[new_page]) {
463       char *annotation = NULL;
464       GtkTreeModel *model = GTK_TREE_MODEL(cwin->stores->pdata[new_page]);
465       GtkTreeIter iter;
466 
467       gtk_tree_model_iter_nth_child(model, &iter, NULL, pos);
468       gtk_tree_model_get(model, &iter, COLUMN_ANNOTATION, &annotation, -1);
469 
470       if (annotation && *annotation) {
471 	if (!cwin->sub_window.window)
472           uim_cand_win_gtk_create_sub_window(cwin);
473 	gtk_text_buffer_set_text(gtk_text_view_get_buffer(GTK_TEXT_VIEW(cwin->sub_window.text_view)), annotation, -1);
474 	uim_cand_win_gtk_layout_sub_window(cwin);
475 	gtk_widget_show(cwin->sub_window.window);
476 	cwin->sub_window.active = TRUE;
477       } else {
478         if (cwin->sub_window.window) {
479           gtk_widget_hide(cwin->sub_window.window);
480           cwin->sub_window.active = FALSE;
481 	}
482       }
483       free(annotation);
484     }
485   } else {
486     horizontal_cwin->selected = NULL;
487     if (cwin->sub_window.window) {
488       gtk_widget_hide(cwin->sub_window.window);
489       cwin->sub_window.active = FALSE;
490     }
491   }
492 
493   uim_cand_win_gtk_update_label(cwin);
494 }
495 
496 static void
scale_label(GtkEventBox * button,double scale)497 scale_label(GtkEventBox *button, double scale)
498 {
499   GtkWidget *label;
500   PangoAttrList *attrs = pango_attr_list_new();
501   PangoAttribute *attr = pango_attr_scale_new(scale);
502 
503   pango_attr_list_insert(attrs, attr);
504   label = gtk_bin_get_child(GTK_BIN(button));
505   if (GTK_IS_LABEL(label))
506     gtk_label_set_attributes(GTK_LABEL(label), attrs);
507   pango_attr_list_unref(attrs);
508 }
509 
510 static void
clear_button(struct index_button * idxbutton,gint cell_index)511 clear_button(struct index_button *idxbutton,
512     gint cell_index)
513 {
514   GtkEventBox *button;
515   GtkWidget *label;
516 
517   idxbutton->cand_index_in_page = -1;
518   button = idxbutton->button;
519 
520   label = gtk_bin_get_child(GTK_BIN(button));
521   gtk_label_set_text(GTK_LABEL(label), "");
522   scale_label(button, PANGO_SCALE_LARGE);
523 }
524 
525 static void
clear_all_buttons(GPtrArray * buttons)526 clear_all_buttons(GPtrArray *buttons)
527 {
528   gint i;
529 
530   for (i = 0; i < (gint)buttons->len; i++) {
531     struct index_button *idxbutton;
532 
533     idxbutton = g_ptr_array_index(buttons, i);
534     if (idxbutton && idxbutton->cand_index_in_page != -1) {
535       clear_button(idxbutton, i);
536     }
537   }
538 }
539 
540 static void
update_table_button(UIMCandWinHorizontalGtk * horizontal_cwin,guint new_page)541 update_table_button(UIMCandWinHorizontalGtk *horizontal_cwin, guint new_page)
542 {
543   UIMCandWinGtk *cwin;
544   GtkTreeModel *model;
545   GPtrArray *buttons;
546   GtkTreeIter ti;
547   gboolean has_next;
548   gint display_limit, len, cand_index = 0;
549 
550   cwin = UIM_CAND_WIN_GTK(horizontal_cwin);
551   if (!cwin->stores->pdata[new_page]) {
552     return;
553   }
554   model = GTK_TREE_MODEL(cwin->stores->pdata[new_page]);
555   buttons = horizontal_cwin->buttons;
556   display_limit = cwin->display_limit;
557   len = buttons->len;
558 
559   clear_all_buttons(buttons);
560   has_next = gtk_tree_model_get_iter_first(model, &ti);
561   while (has_next) {
562     gchar *heading;
563     gchar *cand_str;
564     GtkEventBox *button = NULL;
565 
566     gtk_tree_model_get(model, &ti, COLUMN_HEADING, &heading,
567         COLUMN_CANDIDATE, &cand_str, TERMINATOR);
568     if (cand_str != NULL) {
569       button = assign_cellbutton(horizontal_cwin, cand_index, display_limit);
570       if (button != NULL) {
571         GtkWidget *label;
572         label = gtk_bin_get_child(GTK_BIN(button));
573         if (heading && heading[0] != '\0') {
574           gchar *text = g_strdup_printf("%s: %s", heading, cand_str);
575           gtk_label_set_text(GTK_LABEL(label), text);
576           g_free(text);
577 	} else {
578           gtk_label_set_text(GTK_LABEL(label), cand_str);
579 	}
580 	scale_label(button, PANGO_SCALE_LARGE);
581       }
582     }
583 
584     g_free(cand_str);
585     g_free(heading);
586     cand_index++;
587     has_next = gtk_tree_model_iter_next(model, &ti);
588   }
589 
590   if (cand_index < len) {
591     gint i;
592     for (i = len - 1; i >= cand_index; i--) {
593       struct index_button *idxbutton;
594       idxbutton = g_ptr_array_index(buttons, i);
595       if (idxbutton == horizontal_cwin->selected)
596         horizontal_cwin->selected = NULL;
597       gtk_widget_destroy(GTK_WIDGET(idxbutton->button));
598       g_free(idxbutton);
599       g_ptr_array_remove_index(buttons, i);
600     }
601 #if !GTK_CHECK_VERSION(3, 4, 0)
602     gtk_table_resize(GTK_TABLE(cwin->view), 1, cand_index);
603 #endif
604   }
605 }
606 
607 void
uim_cand_win_horizontal_gtk_set_page(UIMCandWinHorizontalGtk * horizontal_cwin,gint page)608 uim_cand_win_horizontal_gtk_set_page(UIMCandWinHorizontalGtk *horizontal_cwin, gint page)
609 {
610   guint len, new_page;
611   gint new_index;
612   UIMCandWinGtk *cwin;
613 
614   g_return_if_fail(UIM_IS_CAND_WIN_HORIZONTAL_GTK(horizontal_cwin));
615   cwin = UIM_CAND_WIN_GTK(horizontal_cwin);
616   g_return_if_fail(cwin->stores);
617 
618   len = cwin->stores->len;
619   g_return_if_fail(len);
620 
621   if (page < 0)
622     new_page = len - 1;
623   else if (page >= (gint) len)
624     new_page = 0;
625   else
626     new_page = page;
627 
628   update_table_button(horizontal_cwin, new_page);
629 #if GTK_CHECK_VERSION(3, 4, 0)
630   show_table(GTK_GRID(cwin->view), horizontal_cwin->buttons);
631 #else
632   show_table(GTK_TABLE(cwin->view), horizontal_cwin->buttons);
633 #endif
634 
635   cwin->page_index = new_page;
636 
637   if (cwin->display_limit) {
638     if (cwin->candidate_index >= 0)
639       new_index
640         = (new_page * cwin->display_limit) + (cwin->candidate_index % cwin->display_limit);
641     else
642       new_index = -1;
643   } else {
644     new_index = cwin->candidate_index;
645   }
646 
647   if (new_index >= (gint) cwin->nr_candidates)
648     new_index = cwin->nr_candidates - 1;
649 
650   /* check if (cwin->candidate_index != new_index) ?? */
651   uim_cand_win_gtk_set_index(cwin, new_index);
652 }
653 
654 static void
655 #if GTK_CHECK_VERSION(3, 4, 0)
show_table(GtkGrid * view,GPtrArray * buttons)656 show_table(GtkGrid *view, GPtrArray *buttons)
657 #else
658 show_table(GtkTable *view, GPtrArray *buttons)
659 #endif
660 {
661   gint col;
662 
663   for (col = 0; col < (gint)buttons->len; col++) {
664     GtkEventBox *button = NULL;
665     struct index_button *idxbutton;
666 
667     idxbutton = g_ptr_array_index(buttons, col);
668     button = idxbutton->button;
669 
670     gtk_widget_show_all(GTK_WIDGET(button));
671   }
672   gtk_widget_show(GTK_WIDGET(view));
673 }
674 
675 #define UIM_ANNOTATION_WIN_WIDTH 280
676 #define UIM_ANNOTATION_WIN_HEIGHT 140
677 
678 void
uim_cand_win_horizontal_gtk_create_sub_window(UIMCandWinHorizontalGtk * horizontal_cwin)679 uim_cand_win_horizontal_gtk_create_sub_window(UIMCandWinHorizontalGtk *horizontal_cwin)
680 {
681   GtkWidget *window, *scrwin, *text_view, *frame;
682   GdkGeometry hints;
683   UIMCandWinGtk *cwin;
684 
685   g_return_if_fail(UIM_IS_CAND_WIN_HORIZONTAL_GTK(horizontal_cwin));
686   cwin = UIM_CAND_WIN_GTK(horizontal_cwin);
687 
688   if (cwin->sub_window.window)
689     return;
690 
691   cwin->sub_window.window = window = gtk_window_new(GTK_WINDOW_POPUP);
692 
693   frame = gtk_frame_new(NULL);
694   gtk_container_set_border_width(GTK_CONTAINER(frame), 0);
695 
696   hints.min_width = UIM_ANNOTATION_WIN_WIDTH;
697   hints.min_height = UIM_ANNOTATION_WIN_HEIGHT;
698   hints.max_width = UIM_ANNOTATION_WIN_WIDTH;
699   hints.max_height = UIM_ANNOTATION_WIN_HEIGHT;
700   gtk_window_set_geometry_hints(GTK_WINDOW(window), frame, &hints, GDK_HINT_MAX_SIZE | GDK_HINT_MIN_SIZE);
701 
702   cwin->sub_window.scrolled_window = scrwin = gtk_scrolled_window_new(NULL, NULL);
703   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrwin),
704                                  GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
705 
706   cwin->sub_window.text_view = text_view = gtk_text_view_new();
707   gtk_text_view_set_editable(GTK_TEXT_VIEW(text_view), FALSE);
708   gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text_view), GTK_WRAP_WORD_CHAR);
709   gtk_widget_show(text_view);
710 
711   gtk_container_add(GTK_CONTAINER(scrwin), text_view);
712   gtk_container_add(GTK_CONTAINER(frame), scrwin);
713   gtk_container_add(GTK_CONTAINER(window), frame);
714   gtk_widget_show(frame);
715   gtk_widget_show(scrwin);
716   gtk_widget_show(text_view);
717 }
718 
719 void
uim_cand_win_horizontal_gtk_layout_sub_window(UIMCandWinHorizontalGtk * horizontal_cwin)720 uim_cand_win_horizontal_gtk_layout_sub_window(UIMCandWinHorizontalGtk *horizontal_cwin)
721 {
722   UIMCandWinGtk *cwin;
723 #if GTK_CHECK_VERSION(2, 90, 0)
724   gint x, y, w, h, x2, y2, w2, h2, x3, y3;
725 #else
726   gint x, y, w, h, d, x2, y2, w2, h2, d2, x3, y3;
727 #endif
728   struct index_button *idxbutton;
729 
730   g_return_if_fail(UIM_IS_CAND_WIN_HORIZONTAL_GTK(horizontal_cwin));
731   cwin = UIM_CAND_WIN_GTK(horizontal_cwin);
732 
733   if (!cwin->sub_window.window)
734     return;
735 
736 #if GTK_CHECK_VERSION(2, 90, 0)
737   gdk_window_get_geometry(gtk_widget_get_window(GTK_WIDGET(cwin)),
738                           &x, &y, &w, &h);
739 #else
740   gdk_window_get_geometry(gtk_widget_get_window(GTK_WIDGET(cwin)),
741                           &x, &y, &w, &h, &d);
742 #endif
743   gdk_window_get_origin(gtk_widget_get_window(GTK_WIDGET(cwin)), &x, &y);
744 
745 #if GTK_CHECK_VERSION(2, 90, 0)
746   gdk_window_get_geometry(gtk_widget_get_window(cwin->sub_window.window),
747                           &x2, &y2, &w2, &h2);
748 #else
749   gdk_window_get_geometry(gtk_widget_get_window(cwin->sub_window.window),
750                           &x2, &y2, &w2, &h2, &d2);
751 #endif
752 
753   if (horizontal_cwin->selected) {
754     GtkWidget *button;
755     idxbutton = horizontal_cwin->selected;
756     button = GTK_WIDGET(idxbutton->button);
757     gdk_window_get_origin(gtk_widget_get_window(button), &x3, &y3);
758 
759 #if GTK_CHECK_VERSION(2, 18, 0)
760     if (!gtk_widget_get_has_window(button)) {
761       GtkAllocation allocation;
762       gtk_widget_get_allocation(button, &allocation);
763       x3 += allocation.x;
764     }
765 #else
766     if (GTK_WIDGET_NO_WINDOW(button))
767       x3 += button->allocation.x;
768 #endif
769   }
770   y = y + h;
771 
772   gtk_window_move(GTK_WINDOW(cwin->sub_window.window), x3, y);
773 }
774