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-gtk.h"
37 #include <string.h>
38 #include <stdlib.h>
39 #include <uim/uim.h>
40 #include <uim/uim-scm.h>
41
42 #define NR_CANDIDATES 20 /* FIXME! not used yet */
43 #define DEFAULT_MIN_WINDOW_WIDTH 80
44
45 enum {
46 INDEX_CHANGED_SIGNAL,
47 NR_SIGNALS
48 };
49
50 enum {
51 TERMINATOR = -1,
52 COLUMN_HEADING,
53 COLUMN_CANDIDATE,
54 COLUMN_ANNOTATION,
55 NR_COLUMNS
56 };
57
58 static void uim_cand_win_gtk_init (UIMCandWinGtk *cwin);
59 static void uim_cand_win_gtk_class_init (UIMCandWinGtkClass *klass);
60 static void uim_cand_win_gtk_dispose (GObject *obj);
61 static void uim_cand_win_gtk_map (GtkWidget *widget);
62 static void uim_cand_win_gtk_unmap (GtkWidget *widget);
63 static void uim_cand_win_gtk_real_set_index (UIMCandWinGtk *cwin,
64 gint index);
65 static void uim_cand_win_gtk_real_set_page (UIMCandWinGtk *cwin,
66 gint page);
67 static void uim_cand_win_gtk_real_create_sub_window(UIMCandWinGtk *cwin);
68 static void uim_cand_win_gtk_real_layout_sub_window (UIMCandWinGtk *cwin);
69
70 static void pagebutton_clicked(GtkButton *button, gpointer data);
71
72 static GType cand_win_type = 0;
73 static GTypeInfo const object_info = {
74 sizeof (UIMCandWinGtkClass),
75 (GBaseInitFunc) NULL,
76 (GBaseFinalizeFunc) NULL,
77 (GClassInitFunc) uim_cand_win_gtk_class_init,
78 (GClassFinalizeFunc) NULL,
79 NULL, /* class_data */
80 sizeof (UIMCandWinGtk),
81 0, /* n_preallocs */
82 (GInstanceInitFunc) uim_cand_win_gtk_init,
83 };
84
85 static GtkWindowClass *parent_class = NULL;
86 static gint cand_win_gtk_signals[NR_SIGNALS] = {0};
87
88 GType
uim_cand_win_gtk_get_type(void)89 uim_cand_win_gtk_get_type(void)
90 {
91 if (!cand_win_type)
92 cand_win_type = g_type_register_static(GTK_TYPE_WINDOW, "UIMCandWinGtk",
93 &object_info, (GTypeFlags)0);
94 return cand_win_type;
95 }
96
97 GType
uim_cand_win_gtk_register_type(GTypeModule * module)98 uim_cand_win_gtk_register_type(GTypeModule *module)
99 {
100 if (!cand_win_type)
101 cand_win_type = g_type_module_register_type(module,
102 GTK_TYPE_WINDOW,
103 "UIMCandWinGtk",
104 &object_info, 0);
105 return cand_win_type;
106 }
107
108 static void
uim_cand_win_gtk_class_init(UIMCandWinGtkClass * klass)109 uim_cand_win_gtk_class_init (UIMCandWinGtkClass *klass)
110 {
111 GObjectClass *object_class = (GObjectClass *) klass;
112 GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
113
114 parent_class = g_type_class_peek_parent (klass);
115 object_class->dispose = uim_cand_win_gtk_dispose;
116
117 cand_win_gtk_signals[INDEX_CHANGED_SIGNAL]
118 = g_signal_new("index-changed",
119 G_TYPE_FROM_CLASS(klass),
120 G_SIGNAL_RUN_FIRST,
121 G_STRUCT_OFFSET(UIMCandWinGtkClass, index_changed),
122 NULL, NULL,
123 g_cclosure_marshal_VOID__VOID,
124 G_TYPE_NONE, 0);
125
126 widget_class->map = uim_cand_win_gtk_map;
127 widget_class->unmap = uim_cand_win_gtk_unmap;
128
129 klass->set_index = uim_cand_win_gtk_real_set_index;
130 klass->set_page = uim_cand_win_gtk_real_set_page;
131 klass->create_sub_window = uim_cand_win_gtk_real_create_sub_window;
132 klass->layout_sub_window = uim_cand_win_gtk_real_layout_sub_window;
133 }
134
135 void
uim_cand_win_gtk_get_window_pos_type(UIMCandWinGtk * cwin)136 uim_cand_win_gtk_get_window_pos_type(UIMCandWinGtk *cwin)
137 {
138 char *win_pos;
139
140 win_pos = uim_scm_symbol_value_str("candidate-window-position");
141 if (win_pos && !strcmp(win_pos, "left")) {
142 cwin->position = UIM_CAND_WIN_POS_LEFT;
143 } else if (win_pos && !strcmp(win_pos, "right")) {
144 cwin->position = UIM_CAND_WIN_POS_RIGHT;
145 } else {
146 cwin->position = UIM_CAND_WIN_POS_CARET;
147 }
148 free(win_pos);
149 }
150
151 static void
uim_cand_win_gtk_init(UIMCandWinGtk * cwin)152 uim_cand_win_gtk_init (UIMCandWinGtk *cwin)
153 {
154 GtkWidget *frame;
155 GtkWidget *vbox;
156 GtkWidget *hbox;
157
158 /* init struct */
159 cwin->scrolled_window = gtk_scrolled_window_new(NULL, NULL);
160 cwin->num_label = gtk_label_new("");
161
162 cwin->stores = g_ptr_array_new();
163
164 cwin->nr_candidates = 0;
165 cwin->display_limit = 0;
166 cwin->candidate_index = -1;
167 cwin->page_index = 0;
168
169 uim_cand_win_gtk_get_window_pos_type(cwin);
170
171 cwin->block_index_selection = FALSE;
172 cwin->index_changed = FALSE;
173
174 cwin->cursor.x = cwin->cursor.y = 0;
175 cwin->cursor.width = cwin->cursor.height = 0;
176
177 cwin->sub_window.window = NULL;
178 cwin->sub_window.scrolled_window = NULL;
179 cwin->sub_window.text_view = NULL;
180 cwin->sub_window.active = FALSE;
181
182 /* build window */
183 #if GTK_CHECK_VERSION(3, 2, 0)
184 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
185 #else
186 vbox = gtk_vbox_new(FALSE, 0);
187 #endif
188
189 gtk_box_pack_start(GTK_BOX(vbox), cwin->scrolled_window, TRUE, TRUE, 0);
190 uim_cand_win_gtk_set_scrollable(cwin, FALSE);
191
192 /* hbox with prev and next page button: [[<] num_label [>]] */
193 #if GTK_CHECK_VERSION(3, 2, 0)
194 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
195 #else
196 hbox = gtk_hbox_new(FALSE, 0);
197 #endif
198 cwin->prev_page_button = gtk_button_new_with_label("<");
199 cwin->next_page_button = gtk_button_new_with_label(">");
200 gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(cwin->prev_page_button),
201 TRUE, TRUE, 0);
202 gtk_box_pack_start(GTK_BOX(hbox), cwin->num_label, FALSE, FALSE, 0);
203 gtk_box_pack_end(GTK_BOX(hbox), GTK_WIDGET(cwin->next_page_button),
204 TRUE, TRUE, 0);
205 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
206 g_signal_connect(cwin->prev_page_button, "clicked",
207 G_CALLBACK(pagebutton_clicked), cwin);
208 g_signal_connect(cwin->next_page_button, "clicked",
209 G_CALLBACK(pagebutton_clicked), cwin);
210
211 frame = gtk_frame_new(NULL);
212
213 gtk_container_add(GTK_CONTAINER(frame), vbox);
214 gtk_container_add(GTK_CONTAINER(cwin), frame);
215 gtk_container_set_border_width(GTK_CONTAINER(cwin), 0);
216
217 /* set size */
218 /* gtk_widget_set_size_request(cwin->view, -1, -1); */
219
220 /* show children */
221 gtk_widget_show(cwin->scrolled_window);
222 gtk_widget_show_all(hbox);
223 gtk_widget_show(vbox);
224 gtk_widget_show(frame);
225
226 gtk_widget_set_size_request(cwin->num_label, DEFAULT_MIN_WINDOW_WIDTH, -1);
227 gtk_window_set_default_size(GTK_WINDOW(cwin), DEFAULT_MIN_WINDOW_WIDTH, -1);
228 gtk_window_set_resizable(GTK_WINDOW(cwin), TRUE);
229 }
230
231 static void
uim_cand_win_gtk_dispose(GObject * obj)232 uim_cand_win_gtk_dispose (GObject *obj)
233 {
234 UIMCandWinGtk *cwin;
235
236 g_return_if_fail(UIM_IS_CAND_WIN_GTK(obj));
237
238 cwin = UIM_CAND_WIN_GTK(obj);
239
240 if (cwin->stores) {
241 guint i;
242
243 for (i = 0; i < cwin->stores->len; i++) {
244 if (cwin->stores->pdata[i])
245 g_object_unref(G_OBJECT(cwin->stores->pdata[i]));
246 }
247 g_ptr_array_free(cwin->stores, TRUE);
248 cwin->stores = NULL;
249 }
250
251 if (cwin->sub_window.window) {
252 gtk_widget_destroy(cwin->sub_window.window);
253 cwin->sub_window.window = NULL;
254 cwin->sub_window.scrolled_window = NULL;
255 cwin->sub_window.text_view = NULL;
256 }
257
258 if (G_OBJECT_CLASS (parent_class)->dispose)
259 G_OBJECT_CLASS (parent_class)->dispose(obj);
260 }
261
262 static void
uim_cand_win_gtk_map(GtkWidget * widget)263 uim_cand_win_gtk_map (GtkWidget *widget)
264 {
265 UIMCandWinGtk *cwin = UIM_CAND_WIN_GTK(widget);
266
267 if (cwin->sub_window.active)
268 gtk_widget_show(cwin->sub_window.window);
269
270 if (GTK_WIDGET_CLASS (parent_class)->map)
271 GTK_WIDGET_CLASS (parent_class)->map(widget);
272 }
273
274
275 static void
uim_cand_win_gtk_unmap(GtkWidget * widget)276 uim_cand_win_gtk_unmap (GtkWidget *widget)
277 {
278 UIMCandWinGtk *cwin = UIM_CAND_WIN_GTK(widget);
279
280 if (cwin->sub_window.window)
281 gtk_widget_hide(cwin->sub_window.window);
282
283 if (GTK_WIDGET_CLASS (parent_class)->unmap)
284 GTK_WIDGET_CLASS (parent_class)->unmap(widget);
285 }
286
287 UIMCandWinGtk *
uim_cand_win_gtk_new(void)288 uim_cand_win_gtk_new (void)
289 {
290 GObject *obj = g_object_new(UIM_TYPE_CAND_WIN_GTK,
291 "type", GTK_WINDOW_POPUP,
292 NULL);
293 return UIM_CAND_WIN_GTK(obj);
294 }
295
296 void
uim_cand_win_gtk_update_label(UIMCandWinGtk * cwin)297 uim_cand_win_gtk_update_label(UIMCandWinGtk *cwin)
298 {
299 char label_str[20];
300
301 if (cwin->candidate_index >= 0)
302 g_snprintf(label_str, sizeof(label_str), "%d / %d",
303 cwin->candidate_index + 1 , cwin->nr_candidates);
304 else
305 g_snprintf(label_str, sizeof(label_str), "- / %d",
306 cwin->nr_candidates);
307
308 gtk_label_set_text(GTK_LABEL(cwin->num_label), label_str);
309 }
310
311 void
uim_cand_win_gtk_set_nr_candidates(UIMCandWinGtk * cwin,guint nr,guint display_limit)312 uim_cand_win_gtk_set_nr_candidates(UIMCandWinGtk *cwin,
313 guint nr,
314 guint display_limit)
315 {
316 gint i, nr_stores = 1;
317
318 g_return_if_fail(UIM_IS_CAND_WIN_GTK(cwin));
319
320 cwin->nr_candidates = nr;
321 cwin->display_limit = display_limit;
322
323 if (nr <= display_limit) {
324 gtk_widget_set_sensitive(GTK_WIDGET(cwin->prev_page_button), FALSE);
325 gtk_widget_set_sensitive(GTK_WIDGET(cwin->next_page_button), FALSE);
326 } else {
327 gtk_widget_set_sensitive(GTK_WIDGET(cwin->prev_page_button), TRUE);
328 gtk_widget_set_sensitive(GTK_WIDGET(cwin->next_page_button), TRUE);
329 }
330
331 if (cwin->stores == NULL)
332 cwin->stores = g_ptr_array_new();
333
334 /* remove old data */
335 if (cwin->page_index >= 0 && cwin->page_index < (int) cwin->stores->len) {
336 /* Remove data from current page to shrink the window */
337 if (cwin->stores->pdata[cwin->page_index]) {
338 cwin->block_index_selection = TRUE;
339 gtk_list_store_clear(cwin->stores->pdata[cwin->page_index]);
340 cwin->block_index_selection = FALSE;
341 }
342 }
343 for (i = cwin->stores->len - 1; i >= 0; i--) {
344 GtkListStore *store = g_ptr_array_remove_index(cwin->stores, i);
345 if (G_OBJECT(store))
346 g_object_unref(G_OBJECT(store));
347 }
348 /* calculate number of GtkListStores to create */
349 if (display_limit) {
350 nr_stores = nr / display_limit;
351 if (cwin->nr_candidates > display_limit * nr_stores)
352 nr_stores++;
353 }
354
355 /* setup dummy array */
356 for (i = 0; i < nr_stores; i++)
357 g_ptr_array_add(cwin->stores, NULL);
358 }
359
360 void
uim_cand_win_gtk_set_candidates(UIMCandWinGtk * cwin,guint display_limit,GSList * candidates)361 uim_cand_win_gtk_set_candidates(UIMCandWinGtk *cwin,
362 guint display_limit,
363 GSList *candidates)
364 {
365 gint i, nr_stores = 1;
366
367 g_return_if_fail(UIM_IS_CAND_WIN_GTK(cwin));
368
369 if (cwin->stores == NULL)
370 cwin->stores = g_ptr_array_new();
371
372 /* remove old data */
373 if (cwin->page_index >= 0 && cwin->page_index < (int) cwin->stores->len) {
374 /* Remove data from current page to shrink the window */
375 if (cwin->stores->pdata[cwin->page_index])
376 gtk_list_store_clear(cwin->stores->pdata[cwin->page_index]);
377 }
378 for (i = cwin->stores->len - 1; i >= 0; i--) {
379 GtkListStore *store = g_ptr_array_remove_index(cwin->stores, i);
380 if (store)
381 g_object_unref(G_OBJECT(store));
382 }
383
384 cwin->candidate_index = -1;
385 cwin->nr_candidates = g_slist_length(candidates);
386 cwin->display_limit = display_limit;
387
388 cwin->sub_window.active = FALSE;
389
390 if (candidates == NULL)
391 return;
392
393 /* calculate number of GtkListStores to create */
394 if (display_limit) {
395 nr_stores = cwin->nr_candidates / display_limit;
396 if (cwin->nr_candidates > display_limit * nr_stores)
397 nr_stores++;
398 }
399
400 /* create GtkListStores, and set candidates */
401 for (i = 0; i < nr_stores; i++) {
402 GtkListStore *store = gtk_list_store_new(NR_COLUMNS,
403 G_TYPE_STRING,
404 G_TYPE_STRING,
405 G_TYPE_STRING);
406 GSList *node;
407 guint j;
408
409 g_ptr_array_add(cwin->stores, store);
410
411 /* set candidates */
412 for (j = i * display_limit, node = g_slist_nth(candidates, j);
413 display_limit ? j < display_limit * (i + 1) : j < cwin->nr_candidates;
414 j++, node = g_slist_next(node))
415 {
416 GtkTreeIter ti;
417
418 if (node) {
419 uim_candidate cand = node->data;
420 gtk_list_store_append(store, &ti);
421 gtk_list_store_set(store, &ti,
422 COLUMN_HEADING, uim_candidate_get_heading_label(cand),
423 COLUMN_CANDIDATE, uim_candidate_get_cand_str(cand),
424 COLUMN_ANNOTATION, uim_candidate_get_annotation_str(cand),
425 TERMINATOR);
426 } else {
427 #if 0
428 /*
429 * 2004-07-22 Takuro Ashie <ashie@good-day.co.jp>
430 *
431 * FIXME!:
432 * I think we shoudn't set any data for empty row.
433 * It may cause incorrect action.
434 */
435 gtk_list_store_append(store, &ti);
436 gtk_list_store_set(store, &ti,
437 COLUMN_HEADING, "",
438 COLUMN_CANDIDATE, "",
439 COLUMN_ANNOTATION, NULL,
440 TERMINATOR);
441 #endif
442 }
443 }
444 }
445
446 if (cwin->nr_candidates <= cwin->display_limit) {
447 gtk_widget_set_sensitive(GTK_WIDGET(cwin->prev_page_button), FALSE);
448 gtk_widget_set_sensitive(GTK_WIDGET(cwin->next_page_button), FALSE);
449 } else {
450 gtk_widget_set_sensitive(GTK_WIDGET(cwin->prev_page_button), TRUE);
451 gtk_widget_set_sensitive(GTK_WIDGET(cwin->next_page_button), TRUE);
452 }
453
454 uim_cand_win_gtk_set_page(cwin, 0);
455
456 uim_cand_win_gtk_update_label(cwin);
457 }
458
459 void
uim_cand_win_gtk_set_page_candidates(UIMCandWinGtk * cwin,guint page,GSList * candidates)460 uim_cand_win_gtk_set_page_candidates(UIMCandWinGtk *cwin,
461 guint page,
462 GSList *candidates)
463 {
464 GtkListStore *store;
465 GSList *node;
466 gint j, len;
467
468 g_return_if_fail(UIM_IS_CAND_WIN_GTK(cwin));
469
470 if (candidates == NULL)
471 return;
472
473 cwin->sub_window.active = FALSE;
474 len = g_slist_length(candidates);
475
476 /* create GtkListStores, and set candidates */
477 store = gtk_list_store_new(NR_COLUMNS,
478 G_TYPE_STRING,
479 G_TYPE_STRING,
480 G_TYPE_STRING);
481
482 cwin->stores->pdata[page] = store;
483
484 /* set candidates */
485 for (j = 0, node = g_slist_nth(candidates, j);
486 j < len;
487 j++, node = g_slist_next(node))
488 {
489 GtkTreeIter ti;
490
491 if (node) {
492 uim_candidate cand = node->data;
493 gtk_list_store_append(store, &ti);
494 gtk_list_store_set(store, &ti,
495 COLUMN_HEADING, uim_candidate_get_heading_label(cand),
496 COLUMN_CANDIDATE, uim_candidate_get_cand_str(cand),
497 COLUMN_ANNOTATION, uim_candidate_get_annotation_str(cand),
498 TERMINATOR);
499 }
500 }
501 }
502
503 void
uim_cand_win_gtk_clear_candidates(UIMCandWinGtk * cwin)504 uim_cand_win_gtk_clear_candidates(UIMCandWinGtk *cwin)
505 {
506 uim_cand_win_gtk_set_candidates(cwin, 0, NULL);
507 }
508
509 guint
uim_cand_win_gtk_get_nr_candidates(UIMCandWinGtk * cwin)510 uim_cand_win_gtk_get_nr_candidates(UIMCandWinGtk *cwin)
511 {
512 g_return_val_if_fail(UIM_IS_CAND_WIN_GTK(cwin), 0);
513
514 return cwin->nr_candidates;
515 }
516
517 gint
uim_cand_win_gtk_get_index(UIMCandWinGtk * cwin)518 uim_cand_win_gtk_get_index(UIMCandWinGtk *cwin)
519 {
520 g_return_val_if_fail(UIM_IS_CAND_WIN_GTK(cwin), -1);
521
522 return cwin->candidate_index;
523 }
524
525 void
uim_cand_win_gtk_set_index(UIMCandWinGtk * cwin,gint index)526 uim_cand_win_gtk_set_index(UIMCandWinGtk *cwin, gint index)
527 {
528 UIM_CAND_WIN_GTK_GET_CLASS (cwin)->set_index(cwin, index);
529 }
530
531 static void
uim_cand_win_gtk_real_set_index(UIMCandWinGtk * cwin,gint index)532 uim_cand_win_gtk_real_set_index(UIMCandWinGtk *cwin, gint index)
533 {
534 gint new_page;
535
536 g_return_if_fail(UIM_IS_CAND_WIN_GTK(cwin));
537
538 if (index >= (gint) cwin->nr_candidates)
539 cwin->candidate_index = 0;
540 else
541 cwin->candidate_index = index;
542
543 if (cwin->candidate_index >= 0 && cwin->display_limit)
544 new_page = cwin->candidate_index / cwin->display_limit;
545 else
546 new_page = cwin->page_index;
547
548 if (cwin->page_index != new_page)
549 uim_cand_win_gtk_set_page(cwin, new_page);
550 }
551
552 guint
uim_cand_win_gtk_get_nr_pages(UIMCandWinGtk * cwin)553 uim_cand_win_gtk_get_nr_pages(UIMCandWinGtk *cwin)
554 {
555 g_return_val_if_fail(UIM_IS_CAND_WIN_GTK(cwin), 0);
556 g_return_val_if_fail(UIM_IS_CAND_WIN_GTK(cwin->stores), 0);
557
558 return cwin->stores->len;
559 }
560
561 gint
uim_cand_win_gtk_get_page(UIMCandWinGtk * cwin)562 uim_cand_win_gtk_get_page(UIMCandWinGtk *cwin)
563 {
564 g_return_val_if_fail(UIM_IS_CAND_WIN_GTK(cwin), -1);
565
566 return cwin->page_index;
567 }
568
569 void
uim_cand_win_gtk_set_page(UIMCandWinGtk * cwin,gint page)570 uim_cand_win_gtk_set_page(UIMCandWinGtk *cwin, gint page)
571 {
572 UIM_CAND_WIN_GTK_GET_CLASS (cwin)->set_page(cwin, page);
573 }
574
575 static void
uim_cand_win_gtk_real_set_page(UIMCandWinGtk * cwin,gint page)576 uim_cand_win_gtk_real_set_page(UIMCandWinGtk *cwin, gint page)
577 {
578 guint len, new_page;
579 gint new_index;
580
581 g_return_if_fail(UIM_IS_CAND_WIN_GTK(cwin));
582 g_return_if_fail(cwin->stores);
583
584 len = cwin->stores->len;
585 g_return_if_fail(len);
586
587 if (page < 0)
588 new_page = len - 1;
589 else if (page >= (gint) len)
590 new_page = 0;
591 else
592 new_page = page;
593
594 /* XXX: change to call virtual update_view()
595 gtk_tree_view_set_model(GTK_TREE_VIEW(cwin->view),
596 GTK_TREE_MODEL(cwin->stores->pdata[new_page]));
597 */
598
599 cwin->page_index = new_page;
600
601 if (cwin->display_limit) {
602 if (cwin->candidate_index >= 0)
603 new_index
604 = (new_page * cwin->display_limit) + (cwin->candidate_index % cwin->display_limit);
605 else
606 new_index = -1;
607 } else {
608 new_index = cwin->candidate_index;
609 }
610
611 if (new_index >= (gint) cwin->nr_candidates)
612 new_index = cwin->nr_candidates - 1;
613
614 uim_cand_win_gtk_set_index(cwin, new_index);
615 }
616
617 void
uim_cand_win_gtk_set_scrollable(UIMCandWinGtk * cwin,gboolean scrollable)618 uim_cand_win_gtk_set_scrollable(UIMCandWinGtk *cwin, gboolean scrollable)
619 {
620 GtkPolicyType policy = scrollable ? GTK_POLICY_AUTOMATIC : GTK_POLICY_NEVER;
621
622 g_return_if_fail(UIM_IS_CAND_WIN_GTK(cwin));
623
624 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(cwin->scrolled_window),
625 GTK_POLICY_NEVER, policy);
626 }
627
628 void
uim_cand_win_gtk_shift_page(UIMCandWinGtk * cwin,gboolean forward)629 uim_cand_win_gtk_shift_page(UIMCandWinGtk *cwin, gboolean forward)
630 {
631 g_return_if_fail(UIM_IS_CAND_WIN_GTK(cwin));
632
633 if (forward) {
634 uim_cand_win_gtk_set_page(cwin, cwin->page_index + 1);
635 } else {
636 uim_cand_win_gtk_set_page(cwin, cwin->page_index - 1);
637 }
638 }
639
640 guint
uim_cand_win_gtk_query_new_page_by_cand_select(UIMCandWinGtk * cwin,gint index)641 uim_cand_win_gtk_query_new_page_by_cand_select(UIMCandWinGtk *cwin, gint index)
642 {
643 guint new_page;
644
645 g_return_val_if_fail(UIM_IS_CAND_WIN_GTK(cwin), 0);
646
647 if (index >= (gint)cwin->nr_candidates)
648 index = 0;
649
650 if (index >= 0 && cwin->display_limit)
651 new_page = index / cwin->display_limit;
652 else
653 new_page = cwin->page_index;
654
655 return new_page;
656 }
657
658 guint
uim_cand_win_gtk_query_new_page_by_shift_page(UIMCandWinGtk * cwin,gboolean forward)659 uim_cand_win_gtk_query_new_page_by_shift_page(UIMCandWinGtk *cwin,
660 gboolean forward)
661 {
662 gint index;
663 guint new_page, len;
664
665 g_return_val_if_fail(UIM_IS_CAND_WIN_GTK(cwin), 0);
666
667 len = cwin->stores->len;
668
669 if (forward)
670 index = cwin->page_index + 1;
671 else
672 index = cwin->page_index - 1;
673
674 if (index < 0)
675 new_page = len - 1;
676 else if (index >= (gint)len)
677 new_page = 0;
678 else
679 new_page = index;
680
681 return new_page;
682 }
683
684 static void
pagebutton_clicked(GtkButton * button,gpointer data)685 pagebutton_clicked(GtkButton *button, gpointer data)
686 {
687 UIMCandWinGtk *cwin = UIM_CAND_WIN_GTK(data);
688 gboolean has_candidates = FALSE;
689
690 if (cwin->candidate_index < 0) {
691 /* if candidate_index < 0, "index-changed" signal is not emitted
692 * and candidates for new page is not set.
693 */
694 cwin->candidate_index = cwin->page_index * cwin->display_limit;
695 }
696 if (button == GTK_BUTTON(cwin->prev_page_button)) {
697 uim_cand_win_gtk_shift_page(cwin, FALSE);
698 } else if (button == GTK_BUTTON(cwin->next_page_button)) {
699 uim_cand_win_gtk_shift_page(cwin, TRUE);
700 } else {
701 return;
702 }
703 if (cwin->stores->pdata[cwin->page_index]) {
704 has_candidates = TRUE;
705 }
706 if (cwin->candidate_index >= 0) {
707 g_signal_emit(G_OBJECT(cwin),
708 cand_win_gtk_signals[INDEX_CHANGED_SIGNAL], 0);
709 }
710 /* if got candidates, update view */
711 if (!has_candidates && cwin->stores->pdata[cwin->page_index]) {
712 uim_cand_win_gtk_set_page(cwin, cwin->page_index);
713 }
714 }
715
716 void
uim_cand_win_gtk_layout(UIMCandWinGtk * cwin,gint topwin_x,gint topwin_y,gint topwin_width,gint topwin_height)717 uim_cand_win_gtk_layout(UIMCandWinGtk *cwin,
718 gint topwin_x, gint topwin_y,
719 gint topwin_width, gint topwin_height)
720 {
721 GtkRequisition req;
722 int x, y;
723 int cursor_x, cursor_y;
724 int sc_he, cw_he; /*screen height, candidate window height*/
725 int sc_wi, cw_wi;
726
727 g_return_if_fail(UIM_IS_CAND_WIN_GTK(cwin));
728
729 #if GTK_CHECK_VERSION(3, 0, 0)
730 gtk_widget_get_preferred_size(GTK_WIDGET(cwin), &req, NULL);
731 #else
732 gtk_widget_size_request(GTK_WIDGET(cwin), &req);
733 #endif
734 cw_wi = req.width;
735 cw_he = req.height;
736
737 sc_he = gdk_screen_get_height(gdk_screen_get_default ());
738 sc_wi = gdk_screen_get_width (gdk_screen_get_default ());
739
740 /* FIXME */
741 switch (cwin->position) {
742 case UIM_CAND_WIN_POS_LEFT:
743 cursor_x = 0;
744 break;
745 case UIM_CAND_WIN_POS_RIGHT:
746 cursor_x = topwin_width - cw_wi;
747 break;
748 default:
749 cursor_x = cwin->cursor.x;
750 break;
751 }
752 cursor_y = cwin->cursor.y;
753
754 if (sc_wi < topwin_x + cursor_x + cw_wi) {
755 /* x = topwin_x + cursor_x - cw_wi; */
756 x = sc_wi - cw_wi;
757 } else {
758 x = topwin_x + cursor_x;
759 }
760
761 if (sc_he < topwin_y + cursor_y + cwin->cursor.height + cw_he) {
762 y = topwin_y + cursor_y - cw_he;
763 } else {
764 y = topwin_y + cursor_y + cwin->cursor.height;
765 }
766
767 gtk_window_move(GTK_WINDOW(cwin), x, y);
768 #if GTK_CHECK_VERSION(3, 7, 8)
769 if (gtk_widget_get_mapped(cwin->view) && GTK_IS_TREE_VIEW(cwin->view))
770 gtk_widget_queue_resize_no_redraw(cwin->view);
771 #endif
772
773 uim_cand_win_gtk_layout_sub_window(cwin);
774 }
775
776 void
uim_cand_win_gtk_set_cursor_location(UIMCandWinGtk * cwin,GdkRectangle * area)777 uim_cand_win_gtk_set_cursor_location(UIMCandWinGtk *cwin, GdkRectangle *area)
778 {
779 g_return_if_fail(UIM_CAND_WIN_GTK(cwin));
780 g_return_if_fail(area);
781
782 cwin->cursor = *area;
783 }
784
785 void
uim_cand_win_gtk_create_sub_window(UIMCandWinGtk * cwin)786 uim_cand_win_gtk_create_sub_window(UIMCandWinGtk *cwin)
787 {
788 UIM_CAND_WIN_GTK_GET_CLASS (cwin)->create_sub_window(cwin);
789 }
790
791 #define UIM_ANNOTATION_WIN_WIDTH 200
792 #define UIM_ANNOTATION_WIN_HEIGHT 200
793
794 void
uim_cand_win_gtk_real_create_sub_window(UIMCandWinGtk * cwin)795 uim_cand_win_gtk_real_create_sub_window(UIMCandWinGtk *cwin)
796 {
797 GtkWidget *window, *scrwin, *text_view, *frame;
798 GdkGeometry hints;
799
800 if (cwin->sub_window.window)
801 return;
802
803 cwin->sub_window.window = window = gtk_window_new(GTK_WINDOW_POPUP);
804
805 frame = gtk_frame_new(NULL);
806 gtk_container_set_border_width(GTK_CONTAINER(frame), 0);
807
808 hints.min_width = UIM_ANNOTATION_WIN_WIDTH;
809 hints.min_height = UIM_ANNOTATION_WIN_HEIGHT;
810 hints.max_width = UIM_ANNOTATION_WIN_WIDTH;
811 hints.max_height = UIM_ANNOTATION_WIN_HEIGHT;
812 gtk_window_set_geometry_hints(GTK_WINDOW(window), frame, &hints, GDK_HINT_MAX_SIZE | GDK_HINT_MIN_SIZE);
813
814 cwin->sub_window.scrolled_window = scrwin = gtk_scrolled_window_new(NULL, NULL);
815 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrwin),
816 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
817
818 cwin->sub_window.text_view = text_view = gtk_text_view_new();
819 gtk_text_view_set_editable(GTK_TEXT_VIEW(text_view), FALSE);
820 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text_view), GTK_WRAP_WORD_CHAR);
821 gtk_widget_show(text_view);
822
823 gtk_container_add(GTK_CONTAINER(scrwin), text_view);
824 gtk_container_add(GTK_CONTAINER(frame), scrwin);
825 gtk_container_add(GTK_CONTAINER(window), frame);
826 gtk_widget_show(frame);
827 gtk_widget_show(scrwin);
828 gtk_widget_show(text_view);
829 }
830
831
832 void
uim_cand_win_gtk_layout_sub_window(UIMCandWinGtk * cwin)833 uim_cand_win_gtk_layout_sub_window(UIMCandWinGtk *cwin)
834 {
835 UIM_CAND_WIN_GTK_GET_CLASS (cwin)->layout_sub_window(cwin);
836 }
837
838 void
uim_cand_win_gtk_real_layout_sub_window(UIMCandWinGtk * cwin)839 uim_cand_win_gtk_real_layout_sub_window(UIMCandWinGtk *cwin)
840 {
841 #if GTK_CHECK_VERSION(2, 90, 0)
842 gint x, y, w, h, sw, sh, x2, y2, w2, h2;
843 #else
844 gint x, y, w, h, d, sw, sh, x2, y2, w2, h2, d2;
845 #endif
846 GdkRectangle rect;
847 GtkTreePath *path;
848 GtkTreeViewColumn *focus_column;
849
850 if (!cwin->sub_window.window)
851 return;
852
853 gtk_tree_view_get_cursor(GTK_TREE_VIEW(cwin->view), &path, &focus_column);
854 gtk_tree_view_get_cell_area(GTK_TREE_VIEW(cwin->view), path, NULL, &rect);
855 gtk_tree_path_free(path);
856
857 #if GTK_CHECK_VERSION(2, 90, 0)
858 gdk_window_get_geometry(gtk_widget_get_window(GTK_WIDGET(cwin)),
859 &x, &y, &w, &h);
860 #else
861 gdk_window_get_geometry(gtk_widget_get_window(GTK_WIDGET(cwin)),
862 &x, &y, &w, &h, &d);
863 #endif
864 gdk_window_get_origin(gtk_widget_get_window(GTK_WIDGET(cwin)), &x, &y);
865
866 sw = gdk_screen_get_width (gdk_screen_get_default ());
867 sh = gdk_screen_get_height (gdk_screen_get_default ());
868 #if GTK_CHECK_VERSION(2, 90, 0)
869 gdk_window_get_geometry(gtk_widget_get_window(cwin->sub_window.window),
870 &x2, &y2, &w2, &h2);
871 #else
872 gdk_window_get_geometry(gtk_widget_get_window(cwin->sub_window.window),
873 &x2, &y2, &w2, &h2, &d2);
874 #endif
875 if (x + w + w2 > sw)
876 x = x - w2;
877 else
878 x = x + w;
879
880 if ((y + rect.y + h2) > sh)
881 y = sh - h2;
882 else
883 y = y + rect.y;
884
885 gtk_window_move(GTK_WINDOW(cwin->sub_window.window), x, y);
886 }
887