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