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