1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
2 /* IM-JA Japanese Input Method Module for GTK-2.0
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
18 *
19 * Authors: Botond Botyanszki <boti@rocketmail.com>
20 *
21 */
22
23 #include <config.h>
24
25 #include <gtk/gtk.h>
26 #include <gtk/gtkimmodule.h>
27 #include <string.h>
28
29 #include "im-ja.h"
30 #include "im-ja-impl.h"
31 #include "common.h"
32 #include "error.h"
33 #include "conf.h"
34 #include "nls.h"
35 #include "preeditwin.h"
36 #include "preeditarea.h"
37 #include "actionmenu.h"
38
39 extern IMJAConfig cfg;
40 GList *preedit_windows = NULL;
41
42 static void destroy_win(PreeditWin *preedit_win, gboolean remove_signal);
43
44
preedit_window_disable_focus_out(GtkWidget * widget,GdkEvent * event,IMJAContext * cn)45 static void preedit_window_disable_focus_out(GtkWidget *widget, GdkEvent *event, IMJAContext* cn ) {
46 cn->preedit_win->can_hide = FALSE;
47 }
48
preedit_window_press_cb(GtkWidget * button,GdkEventButton * event,IMJAContext * cn)49 static void preedit_window_press_cb(GtkWidget *button, GdkEventButton *event, IMJAContext* cn) {
50 IM_JA_DEBUG("preedit_window_press_cb\n");
51 if (event->button == 1) {
52 im_ja_actionmenu_button_press_cb(button, event, &cn->preedit_win->menu,
53 IM_JA_STATUSWIN_MENU, cn);
54 }
55 }
56
preedit_window_show(IMJAContext * cn)57 void preedit_window_show(IMJAContext* cn) {
58 IM_JA_DEBUG("preedit_window_show\n");
59
60 if ((cn->input_method == IM_JA_DIRECT_INPUT) || (cn->input_method == IM_JA_KANJIPAD_INPUT)) {
61 preedit_window_hide(cn);
62 return;
63 }
64
65 #ifdef IMJA_TARGET_GTK
66 if ((cn->toplevel_gtk != NULL) && GTK_WIDGET_HAS_GRAB(cn->toplevel_gtk) == TRUE) {
67 IM_JA_DEBUG("GRABBED TOPLEVEL!\n");
68 }
69
70 /* FIXME: gtk bug */
71 if (cn->client_gtk != NULL) {
72 if (GTK_IS_ENTRY(cn->client_gtk) == TRUE) {
73 if (gtk_editable_get_editable(GTK_EDITABLE(cn->client_gtk)) == FALSE) {
74 IM_JA_DEBUG("non-editable GtkEntry.\n");
75 return;
76 }
77 }
78 if (GTK_IS_TEXT_VIEW(cn->client_gtk) == TRUE) {
79 if (gtk_text_view_get_editable(GTK_TEXT_VIEW(cn->client_gtk)) == FALSE) {
80 IM_JA_DEBUG("non-editable GtkTextView.\n");
81 return;
82 }
83 }
84 }
85 #endif
86
87 /* cn->preedit_win = g_object_get_data(G_OBJECT(cn->toplevel_gdk), "im-ja-preedit-window"); */
88 if (cn->preedit_win == NULL) {
89 GtkWidget *border;
90 GtkWidget *input_border;
91 GtkWidget *hbox;
92 /* IM_JA_DEBUG("preedit_window_create\n"); */
93 cn->preedit_win = g_new0(PreeditWin, 1);
94 cn->preedit_win->menu = NULL;
95
96 /* g_object_set_data(G_OBJECT(cn->toplevel_gdk), "im-ja-preedit-window", cn->preedit_win); */
97 preedit_windows = g_list_append(preedit_windows, cn->preedit_win);
98 cn->preedit_win->can_hide = TRUE;
99 cn->preedit_win->window = gtk_window_new(GTK_WINDOW_POPUP);
100 im_ja_join_modal_window(cn, cn->preedit_win->window);
101 /*gtk_window_set_decorated(GTK_WINDOW(cn->preedit_win->window), FALSE);*/
102 gtk_window_set_title(GTK_WINDOW(cn->preedit_win->window), _("[preedit window]"));
103 gtk_window_set_default_size(GTK_WINDOW(cn->preedit_win->window), 140, 25);
104
105 cn->preedit_win->eventbox = gtk_event_box_new();
106 gtk_container_add(GTK_CONTAINER(cn->preedit_win->window), cn->preedit_win->eventbox);
107 gtk_widget_show(cn->preedit_win->eventbox);
108 gtk_widget_realize(cn->preedit_win->eventbox);
109 gdk_window_set_cursor(cn->preedit_win->eventbox->window, gdk_cursor_new(GDK_HAND2));
110
111 /* If the preedit gets the focus, we should be still receiving keyboard input */
112 g_signal_connect(G_OBJECT(cn->preedit_win->eventbox), "enter_notify_event",
113 G_CALLBACK(im_ja_grab_add), cn);
114 g_signal_connect(G_OBJECT(cn->preedit_win->eventbox), "leave_notify_event",
115 G_CALLBACK(im_ja_grab_remove), cn);
116 g_signal_connect(G_OBJECT(cn->preedit_win->eventbox), "button_press_event",
117 G_CALLBACK(preedit_window_press_cb), cn);
118
119 border = gtk_frame_new(NULL);
120 gtk_frame_set_shadow_type(GTK_FRAME(border), GTK_SHADOW_OUT);
121 gtk_container_add(GTK_CONTAINER(cn->preedit_win->eventbox), border);
122
123 hbox = gtk_hbox_new(FALSE, 5);
124 gtk_container_add(GTK_CONTAINER(border), hbox);
125
126 input_border = gtk_frame_new(NULL);
127
128 /* cn->preedit_win->text_view = gtk_label_new(cn->preedit_buf); */
129 cn->preedit_win->text_view = preedit_area_new(cn->preedit_buf);
130
131 gtk_misc_set_alignment(GTK_MISC(cn->preedit_win->text_view), 0.0, 0.5);
132 gtk_container_add(GTK_CONTAINER(input_border), cn->preedit_win->text_view);
133 gtk_box_pack_start(GTK_BOX(hbox), input_border, TRUE, TRUE, 0);
134
135 cn->preedit_win->label = gtk_label_new(cfg.status_win_labels[cn->input_method]);
136 gtk_box_pack_start(GTK_BOX(hbox), cn->preedit_win->label, FALSE, FALSE, 0);
137
138
139 /* This hack is to disable flashy behaviour when window gets the focus*/
140 g_signal_connect(G_OBJECT(cn->preedit_win->window), "enter_notify_event",
141 G_CALLBACK(preedit_window_disable_focus_out), cn);
142 g_signal_connect_swapped(G_OBJECT(cn->preedit_win->text_view), "realize",
143 G_CALLBACK(preedit_window_update_location), cn);
144 /*
145 cn->preedit_win->size_allocate_id = g_signal_connect_swapped(G_OBJECT(cn->toplevel_gtk), "size_allocate",
146 G_CALLBACK(preedit_window_update_location), cn);
147 g_signal_connect_swapped(G_OBJECT(cn->toplevel_gtk), "destroy",
148 G_CALLBACK(toplevel_destroyed_cb), cn->preedit_win);
149 //cn->preedit_win->toplevel_gtk = cn->toplevel_gtk;
150 */
151 }
152 else {
153 gtk_window_resize(GTK_WINDOW(cn->preedit_win->window), 140, 25);
154 preedit_window_update(cn);
155 gtk_label_set_text(GTK_LABEL(cn->preedit_win->label), cfg.status_win_labels[cn->input_method]);
156 preedit_window_update_location(cn);
157 }
158 #ifdef IMJA_TARGET_XIM
159 preedit_window_hide_all();
160 #endif
161 gtk_widget_show_all(cn->preedit_win->window);
162 cn->preedit_win_on = TRUE;
163 }
164
preedit_window_force_hide(IMJAContext * cn)165 void preedit_window_force_hide(IMJAContext *cn) {
166 IM_JA_DEBUG("preedit_window_force_hide\n");
167
168 if (cn->preedit_win != NULL) {
169 gtk_widget_hide_all(cn->preedit_win->window);
170 }
171 }
172
preedit_window_hide(IMJAContext * cn)173 void preedit_window_hide(IMJAContext *cn) {
174 IM_JA_DEBUG("preedit_window_hide\n");
175
176 if (cn->preedit_win != NULL) {
177 if (cn->preedit_win->menu != NULL) {
178 /* don't hide if we have the menu pulled out */
179 if ((GTK_WIDGET_HAS_GRAB(cn->preedit_win->menu) == TRUE)
180 && (cn->preedit_win_on == TRUE)) return;
181 }
182 if (gtk_widget_is_focus(cn->preedit_win->window) == TRUE) return;
183
184 if (cn->preedit_win->can_hide == TRUE) {
185 gtk_widget_hide_all(cn->preedit_win->window);
186 }
187 }
188 }
189
190 /*
191 static void toplevel_destroyed_cb(GtkWidget *toplevel, GdkEvent *event, PreeditWin *preedit_win) {
192 IM_JA_DEBUG("toplevel_destroyed_cb()\n");
193 destroy_win(preedit_win, FALSE);
194 }
195 */
196
destroy_win(PreeditWin * preedit_win,gboolean remove_signal)197 static void destroy_win(PreeditWin *preedit_win, gboolean remove_signal) {
198 IM_JA_DEBUG("destroy_win() [%d]\n", (int) preedit_win);
199 if (preedit_win == NULL) return;
200 if (g_list_find(preedit_windows, preedit_win) == NULL) {
201 IM_JA_DEBUG("*ERROR* preedit window is already destroyed\n");
202 return;
203 }
204 preedit_windows = g_list_remove(preedit_windows, preedit_win);
205 /*
206 if (remove_signal == TRUE) {
207 if (GTK_IS_WIDGET(preedit_win->toplevel_gtk)) {
208 g_signal_handler_disconnect(preedit_win->toplevel_gtk, preedit_win->size_allocate_id);
209 }
210 }
211 */
212 if (GTK_IS_WIDGET(preedit_win->window) == TRUE) gtk_widget_destroy(preedit_win->window);
213 /* g_free(preedit_win); this causes a segfault in some cases */
214 }
215
preedit_window_destroy(IMJAContext * cn)216 void preedit_window_destroy(IMJAContext *cn) {
217 IM_JA_DEBUG("preedit_window_destroy()\n");
218 if (cn->preedit_win == NULL) return;
219 destroy_win(cn->preedit_win, TRUE);
220 cn->preedit_win = NULL;
221 }
222
preedit_window_destroy_all()223 void preedit_window_destroy_all() {
224 IM_JA_DEBUG("preedit_window_destroy_all()\n");
225 g_list_foreach(preedit_windows, (GFunc) destroy_win, (gpointer) TRUE);
226 g_list_free(preedit_windows);
227 preedit_windows = NULL;
228 }
229
preedit_window_hide_all()230 void preedit_window_hide_all() {
231 GList *windows = preedit_windows;
232 PreeditWin *preedit_win;
233
234 IM_JA_DEBUG("preedit_window_hide_all()\n");
235
236 while (windows != NULL) {
237 preedit_win = (PreeditWin *) windows->data;
238 if (preedit_win != NULL) {
239 if (gtk_widget_is_focus(preedit_win->window) == TRUE) return;
240 if (preedit_win->can_hide == TRUE) gtk_widget_hide_all(preedit_win->window);
241 }
242 windows = g_list_next(windows);
243 }
244 }
245
preedit_window_set_text(IMJAContext * cn,gchar * text)246 void preedit_window_set_text(IMJAContext *cn, gchar *text) {
247 GtkRequisition requisition;
248 gint width, height, new_w;
249
250 /* IM_JA_DEBUG("preedit_window_set_text\n"); */
251
252 preedit_area_set_text(PREEDIT_AREA(cn->preedit_win->text_view), text);
253
254 gtk_widget_size_request(GTK_WIDGET(cn->preedit_win->window), &requisition);
255 gtk_window_get_size(GTK_WINDOW(cn->preedit_win->window), &width, &height);
256
257 if (requisition.width < width) {
258 new_w = requisition.width > 140 ? requisition.width + 5 : 140;
259 gtk_window_resize(GTK_WINDOW(cn->preedit_win->window), new_w, 25);
260 }
261
262 }
263
preedit_window_set_attributes(IMJAContext * cn,PangoAttrList * attrs)264 void preedit_window_set_attributes(IMJAContext *cn, PangoAttrList *attrs) {
265 /* IM_JA_DEBUG("preedit_window_set_attributes\n"); */
266 preedit_area_set_attributes(PREEDIT_AREA(cn->preedit_win->text_view), attrs);
267 }
268
preedit_window_set_cursor_pos(IMJAContext * cn,gint pos)269 void preedit_window_set_cursor_pos(IMJAContext *cn, gint pos) {
270 /* IM_JA_DEBUG("preedit_window_set_cursor_pos\n"); */
271 preedit_area_set_cursor_pos(PREEDIT_AREA(cn->preedit_win->text_view), pos);
272 }
273
preedit_window_update_location(IMJAContext * cn)274 void preedit_window_update_location(IMJAContext *cn) {
275 gint target_x = 0, target_y = 0;
276 GdkRectangle toplevel_rect;
277 GdkRectangle client_rect;
278
279 /* IM_JA_DEBUG("im_ja_update_preedit_window_location\n"); */
280 if (cn->preedit_win != NULL) {
281
282 im_ja_get_toplevel_window_geometry(cn, &toplevel_rect);
283
284 if ((cn->preedit_win_pos_offset_x == 0) && (cn->preedit_win_pos_offset_y == 0)) {
285 im_ja_attach_bottom_left(cn, cn->preedit_win->window);
286 }
287 else {
288 im_ja_get_client_window_geometry(cn, &client_rect);
289 target_x = client_rect.x + cn->preedit_win_pos_offset_x;
290 target_y = client_rect.y + cn->preedit_win_pos_offset_y;
291 /* IM_JA_DEBUG("update preedit window location x: %d, y: %d\n", target_x, target_y); */
292
293 im_ja_move_within_rect(cn, &target_x, &target_y, &client_rect);
294 /* IM_JA_DEBUG("update preedit window location x: %d, y: %d\n", target_x, target_y); */
295 gtk_window_move((GTK_WINDOW(cn->preedit_win->window)), target_x, target_y);
296 }
297 }
298 }
299
preedit_window_update(IMJAContext * cn)300 void preedit_window_update(IMJAContext *cn) {
301 PangoAttrList *preedit_win_attrs;
302 PangoAttribute *backgr;
303 PangoAttribute *foregr;
304 PangoAttribute *underline;
305 gint preedit_cursor = 0;
306
307 /* IM_JA_DEBUG("preedit_window_update\n"); */
308
309 if ((cn->preedit_win == NULL) || (GTK_IS_WIDGET(cn->preedit_win->window) == FALSE)) return;
310
311 if (strlen(cn->preedit_buf) == 0) {
312 preedit_window_set_text(cn, "");
313 preedit_window_set_cursor_pos(cn, 0);
314 }
315
316 preedit_win_attrs = pango_attr_list_new(); /* Create new attrib list for the preedit window */
317
318 underline = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
319 underline->start_index = 0;
320 underline->end_index = strlen(cn->preedit_buf);
321 pango_attr_list_insert(preedit_win_attrs, underline);
322
323 if (cfg.custom_preedit_n == TRUE) {
324 foregr = pango_attr_foreground_new(cfg.preedit_colors[0].red, cfg.preedit_colors[0].green, cfg.preedit_colors[0].blue);
325 backgr = pango_attr_background_new(cfg.preedit_colors[1].red, cfg.preedit_colors[1].green, cfg.preedit_colors[1].blue);
326 backgr->start_index = 0;
327 foregr->start_index = 0;
328 backgr->end_index = strlen(cn->preedit_buf);
329 foregr->end_index = strlen(cn->preedit_buf);
330 pango_attr_list_insert(preedit_win_attrs, backgr);
331 pango_attr_list_insert(preedit_win_attrs, foregr);
332 }
333
334 if (cn->preedit_reverse_start != cn->preedit_reverse_end) {
335 /* Highlighted (==reverse) field */
336 if (cfg.custom_preedit_hl == TRUE) {
337 foregr = pango_attr_foreground_new(cfg.preedit_colors[2].red, cfg.preedit_colors[2].green, cfg.preedit_colors[2].blue);
338 backgr = pango_attr_background_new(cfg.preedit_colors[3].red, cfg.preedit_colors[3].green, cfg.preedit_colors[3].blue);
339 }
340 else { /* Use the default */
341 foregr = pango_attr_foreground_new(cn->original_colors[0].red, cn->original_colors[0].green, cn->original_colors[0].blue);
342 backgr = pango_attr_background_new(cn->original_colors[1].red, cn->original_colors[1].green, cn->original_colors[1].blue);
343 }
344 backgr->start_index = cn->preedit_reverse_start;
345 foregr->start_index = cn->preedit_reverse_start;
346 backgr->end_index = cn->preedit_reverse_end;
347 foregr->end_index = cn->preedit_reverse_end;
348
349 pango_attr_list_insert(preedit_win_attrs, backgr);
350 pango_attr_list_insert(preedit_win_attrs, foregr);
351 }
352
353 preedit_cursor = im_ja_get_cursor_pos_bytes(cn);
354 preedit_window_set_cursor_pos(cn, preedit_cursor);
355 preedit_window_set_text(cn, cn->preedit_buf);
356 preedit_window_set_attributes(cn, preedit_win_attrs);
357
358 }
359