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