1 /*
2 
3   copyright (c) 2011-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-vertical-gtk.h"
37 #include <string.h>
38 #include <stdlib.h>
39 #include <math.h>
40 
41 #include <uim/uim.h>
42 #include <uim/uim-scm.h>
43 
44 enum {
45   INDEX_CHANGED_SIGNAL,
46   NR_SIGNALS
47 };
48 
49 enum {
50   TERMINATOR = -1,
51   COLUMN_HEADING,
52   COLUMN_CANDIDATE,
53   COLUMN_ANNOTATION,
54   LISTSTORE_NR_COLUMNS
55 };
56 
57 static void uim_cand_win_vertical_gtk_init(UIMCandWinVerticalGtk *cwin);
58 static void uim_cand_win_vertical_gtk_class_init(UIMCandWinGtkClass *klass);
59 static void uim_cand_win_vertical_gtk_dispose(GObject *obj);
60 
61 static gboolean	tree_selection_change		(GtkTreeSelection *selection,
62 						 GtkTreeModel *model,
63 						 GtkTreePath *path,
64 						 gboolean path_currently_selected,
65 						 gpointer data);
66 static gboolean tree_selection_changed		(GtkTreeSelection *selection,
67 						 gpointer data);
68 static gboolean tree_view_button_press		(GtkWidget *widget,
69 						 GdkEventButton *event,
70 						 gpointer data);
71 
72 
73 static GType cand_win_vertical_type = 0;
74 static GTypeInfo const object_info = {
75   sizeof (UIMCandWinVerticalGtkClass),
76   (GBaseInitFunc) NULL,
77   (GBaseFinalizeFunc) NULL,
78   (GClassInitFunc) uim_cand_win_vertical_gtk_class_init,
79   (GClassFinalizeFunc) NULL,
80   NULL,                       /* class_data */
81   sizeof (UIMCandWinVerticalGtk),
82   0,                          /* n_preallocs */
83   (GInstanceInitFunc) uim_cand_win_vertical_gtk_init,
84 };
85 
86 static UIMCandWinGtkClass *parent_class = NULL;
87 
88 GType
uim_cand_win_vertical_gtk_get_type(void)89 uim_cand_win_vertical_gtk_get_type(void)
90 {
91   if (!cand_win_vertical_type)
92     cand_win_vertical_type = g_type_register_static(UIM_TYPE_CAND_WIN_GTK, "UIMCandWinVerticalGtk",
93 					   &object_info, (GTypeFlags)0);
94   return cand_win_vertical_type;
95 }
96 
97 GType
uim_cand_win_vertical_gtk_register_type(GTypeModule * module)98 uim_cand_win_vertical_gtk_register_type(GTypeModule *module)
99 {
100   if (!cand_win_vertical_type)
101     cand_win_vertical_type = g_type_module_register_type(module,
102 						UIM_TYPE_CAND_WIN_GTK,
103 						"UIMCandWinVerticalGtk",
104 						&object_info, 0);
105   return cand_win_vertical_type;
106 }
107 
108 static void
uim_cand_win_vertical_gtk_class_init(UIMCandWinGtkClass * klass)109 uim_cand_win_vertical_gtk_class_init (UIMCandWinGtkClass *klass)
110 {
111   GObjectClass *object_class = (GObjectClass *) klass;
112 
113   parent_class = g_type_class_peek_parent (klass);
114   object_class->dispose = uim_cand_win_vertical_gtk_dispose;
115 
116   klass->set_index = (void (*)(UIMCandWinGtk *, gint))uim_cand_win_vertical_gtk_set_index;
117   klass->set_page = (void (*)(UIMCandWinGtk *, gint))uim_cand_win_vertical_gtk_set_page;
118 }
119 
120 static void
uim_cand_win_vertical_gtk_init(UIMCandWinVerticalGtk * vertical_cwin)121 uim_cand_win_vertical_gtk_init (UIMCandWinVerticalGtk *vertical_cwin)
122 {
123   UIMCandWinGtk *cwin;
124   GtkCellRenderer *renderer;
125   GtkTreeViewColumn *column;
126   GtkTreeSelection *selection;
127 
128   cwin = UIM_CAND_WIN_GTK(vertical_cwin);
129 
130   cwin->view = gtk_tree_view_new();
131   gtk_container_add(GTK_CONTAINER(cwin->scrolled_window), cwin->view);
132 
133   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(cwin->view));
134 
135   gtk_tree_selection_set_select_function(selection,
136 					 tree_selection_change,
137 					 cwin,
138 					 NULL);
139   g_signal_connect (G_OBJECT(selection), "changed",
140 		    G_CALLBACK(tree_selection_changed), cwin);
141 
142   renderer = gtk_cell_renderer_text_new();
143   g_object_set(renderer, "scale", 0.8, (const gchar *)NULL);
144 
145   column = gtk_tree_view_column_new_with_attributes("No",
146 						    renderer,
147 						    "text", COLUMN_HEADING,
148 						    (const gchar *)NULL);
149   gtk_tree_view_append_column(GTK_TREE_VIEW(cwin->view), column);
150   gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
151 
152   renderer = gtk_cell_renderer_text_new();
153   g_object_set(renderer, "scale", 1.2, (const gchar *)NULL);
154   /*  g_object_set(renderer, "size-points", 20.0, NULL); */
155   column = gtk_tree_view_column_new_with_attributes("Text",
156 						    renderer,
157 						    "text", COLUMN_CANDIDATE,
158 						    (const gchar *)NULL);
159   gtk_tree_view_append_column(GTK_TREE_VIEW(cwin->view), column);
160   gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(cwin->view), TRUE);
161   gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(cwin->view), FALSE);
162   gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
163 
164   g_signal_connect(G_OBJECT(cwin->view), "button-press-event",
165 		   G_CALLBACK(tree_view_button_press), cwin);
166 
167   gtk_widget_show(cwin->view);
168 }
169 
170 static void
uim_cand_win_vertical_gtk_dispose(GObject * obj)171 uim_cand_win_vertical_gtk_dispose (GObject *obj)
172 {
173   if (G_OBJECT_CLASS (parent_class)->dispose)
174     G_OBJECT_CLASS (parent_class)->dispose(obj);
175 }
176 
177 UIMCandWinVerticalGtk *
uim_cand_win_vertical_gtk_new(void)178 uim_cand_win_vertical_gtk_new (void)
179 {
180   GObject *obj = g_object_new(UIM_TYPE_CAND_WIN_VERTICAL_GTK,
181 			      "type", GTK_WINDOW_POPUP,
182 			      NULL);
183 
184   return UIM_CAND_WIN_VERTICAL_GTK(obj);
185 }
186 
187 static gboolean
tree_selection_change(GtkTreeSelection * selection,GtkTreeModel * model,GtkTreePath * path,gboolean path_currently_selected,gpointer data)188 tree_selection_change(GtkTreeSelection *selection,
189 		      GtkTreeModel *model,
190 		      GtkTreePath *path,
191 		      gboolean path_currently_selected,
192 		      gpointer data)
193 {
194   UIMCandWinVerticalGtk *vertical_cwin = UIM_CAND_WIN_VERTICAL_GTK(data);
195   UIMCandWinGtk *cwin = UIM_CAND_WIN_GTK(vertical_cwin);
196   gint *indicies;
197   gint idx;
198 
199   if (!cwin)
200     return TRUE;
201 
202   if (cwin->block_index_selection)
203     return TRUE;
204 
205   indicies = gtk_tree_path_get_indices(path);
206   g_return_val_if_fail(indicies, TRUE);
207   idx = *indicies + cwin->display_limit * cwin->page_index;
208 
209   if (path_currently_selected && cwin->candidate_index >= 0) {
210     /* if emit "index-changed" here and IM deactivates this candwin,
211      * activates new candwin and selects a candidate on new candwin
212      * from index-changed callback, SEGV occurs in gtk because gtk tries to
213      * select on old candwin after return of this tree_selection_change().
214      * To avoid SEGV, instead of emitting before selection change by gtk,
215      * emit after selection changed by gtk. */
216     cwin->index_changed = TRUE;
217   }
218 
219   if (!path_currently_selected && cwin->candidate_index != idx) {
220     if (cwin->candidate_index >= 0) {
221       cwin->candidate_index = idx;
222     }
223 
224     uim_cand_win_gtk_update_label(cwin);
225 
226     if (cwin->candidate_index < 0)
227       return FALSE;
228     else
229       return TRUE;
230   } else {
231     uim_cand_win_gtk_update_label(cwin);
232 
233     return TRUE;
234   }
235 }
236 
237 static gboolean
tree_selection_changed(GtkTreeSelection * selection,gpointer data)238 tree_selection_changed(GtkTreeSelection *selection,
239 		       gpointer data)
240 {
241   GtkTreeModel *model;
242   GtkTreeIter iter;
243   UIMCandWinVerticalGtk *vertical_cwin = UIM_CAND_WIN_VERTICAL_GTK(data);
244   UIMCandWinGtk *cwin = UIM_CAND_WIN_GTK(vertical_cwin);
245 
246   if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
247     char *annotation = NULL;
248 
249     gtk_tree_model_get(model, &iter,
250 		       COLUMN_ANNOTATION, &annotation,
251 		       -1);
252 
253     if (annotation && *annotation) {
254       if (!cwin->sub_window.window)
255 	uim_cand_win_gtk_create_sub_window(cwin);
256       gtk_text_buffer_set_text(
257 	gtk_text_view_get_buffer(GTK_TEXT_VIEW(cwin->sub_window.text_view)),
258 	annotation, -1);
259       uim_cand_win_gtk_layout_sub_window(cwin);
260       gtk_widget_show(cwin->sub_window.window);
261       cwin->sub_window.active = TRUE;
262     } else {
263       if (cwin->sub_window.window) {
264 	gtk_widget_hide(cwin->sub_window.window);
265 	cwin->sub_window.active = FALSE;
266       }
267     }
268     free(annotation);
269   } else {
270     if (cwin->sub_window.window) {
271       gtk_widget_hide(cwin->sub_window.window);
272       cwin->sub_window.active = FALSE;
273     }
274   }
275 
276   if (cwin->index_changed) {
277     cwin->index_changed = FALSE;
278     g_signal_emit_by_name(G_OBJECT(cwin), "index-changed");
279   }
280 
281   return TRUE;
282 }
283 
284 static gboolean
tree_view_button_press(GtkWidget * widget,GdkEventButton * event,gpointer data)285 tree_view_button_press(GtkWidget *widget, GdkEventButton *event, gpointer data)
286 {
287   UIMCandWinVerticalGtk *vertical_cwin;
288   UIMCandWinGtk *cwin;
289   GtkTreePath *path;
290   gboolean exist, retval = FALSE;
291   gint *indicies;
292 
293   g_return_val_if_fail(GTK_IS_TREE_VIEW(widget), FALSE);
294   g_return_val_if_fail(UIM_CAND_WIN_VERTICAL_GTK(data), FALSE);
295 
296   vertical_cwin = UIM_CAND_WIN_VERTICAL_GTK(data);
297   cwin = UIM_CAND_WIN_GTK(vertical_cwin);
298 
299   exist = gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget),
300 					event->x, event->y,
301 					&path, NULL, NULL, NULL);
302   if (!exist)
303     return FALSE;
304 
305   indicies = gtk_tree_path_get_indices(path);
306 
307   /* don't relay button press event to empty row */
308   if (cwin->display_limit * cwin->page_index + *indicies >= cwin->nr_candidates)
309     retval = TRUE;
310 
311   gtk_tree_path_free(path);
312 
313   return retval;
314 }
315 
316 
317 void
uim_cand_win_vertical_gtk_set_index(UIMCandWinVerticalGtk * vertical_cwin,gint index)318 uim_cand_win_vertical_gtk_set_index(UIMCandWinVerticalGtk *vertical_cwin, gint index)
319 {
320   UIMCandWinGtk *cwin;
321   UIMCandWinVerticalGtkClass *vertical_cwin_class;
322   UIMCandWinGtkClass *cwin_class;
323 
324   g_return_if_fail(UIM_IS_CAND_WIN_VERTICAL_GTK(vertical_cwin));
325   cwin = UIM_CAND_WIN_GTK(vertical_cwin);
326 
327   /* call parent method */
328   vertical_cwin_class = UIM_CAND_WIN_VERTICAL_GTK_GET_CLASS(vertical_cwin);
329   cwin_class = g_type_class_peek_parent(vertical_cwin_class);
330   cwin_class->set_index(cwin, index);
331 
332   if (cwin->candidate_index >= 0) {
333     GtkTreePath *path;
334     gint pos = index;
335 
336     if (cwin->display_limit)
337       pos = cwin->candidate_index % cwin->display_limit;
338 
339     path = gtk_tree_path_new_from_indices(pos, -1);
340     gtk_tree_view_set_cursor(GTK_TREE_VIEW(cwin->view),
341 			     path, NULL, FALSE);
342     gtk_tree_path_free(path);
343 
344   } else {
345     GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(cwin->view));
346 
347     gtk_tree_selection_unselect_all(selection);
348     uim_cand_win_gtk_update_label(cwin);
349   }
350 }
351 
352 void
uim_cand_win_vertical_gtk_set_page(UIMCandWinVerticalGtk * vertical_cwin,gint page)353 uim_cand_win_vertical_gtk_set_page(UIMCandWinVerticalGtk *vertical_cwin, gint page)
354 {
355   guint len, new_page;
356   gint new_index;
357   UIMCandWinGtk *cwin;
358 
359   g_return_if_fail(UIM_IS_CAND_WIN_VERTICAL_GTK(vertical_cwin));
360   cwin = UIM_CAND_WIN_GTK(vertical_cwin);
361   g_return_if_fail(cwin->stores);
362 
363   len = cwin->stores->len;
364   g_return_if_fail(len);
365 
366   if (page < 0)
367     new_page = len - 1;
368   else if (page >= (gint) len)
369     new_page = 0;
370   else
371     new_page = page;
372 
373   gtk_tree_view_set_model(GTK_TREE_VIEW(cwin->view),
374 			  GTK_TREE_MODEL(cwin->stores->pdata[new_page]));
375 
376   cwin->page_index = new_page;
377 
378   if (cwin->display_limit) {
379     if (cwin->candidate_index >= 0)
380       new_index
381         = (new_page * cwin->display_limit) + (cwin->candidate_index % cwin->display_limit);
382     else
383       new_index = -1;
384   } else {
385     new_index = cwin->candidate_index;
386   }
387 
388   if (new_index >= (gint) cwin->nr_candidates)
389     new_index = cwin->nr_candidates - 1;
390 
391   uim_cand_win_gtk_set_index(cwin, new_index);
392 }
393