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