1 /*
2 * Copyright (C) 2020 The HIME team, Taiwan
3 * Copyright (C) 2011 Edward Der-Hua Liu, Hsin-Chu, Taiwan
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation version 2.1
8 * of the License.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 #include "hime.h"
21
22 #include "gst.h"
23 #include "pho.h"
24 #include "win-sym.h"
25
26 /* "destroy_window = FALSE" should be ok with both GTK+ 2.x and 3.x
27 * gcin use TRUE for GTK+ 3.x, but caleb- always patch it to FALSE
28 */
29 #if 0
30 int destroy_window = TRUE;
31 #else
32 int destroy_window = FALSE;
33 #endif
34
35 GtkWidget *gwin0 = NULL;
36 extern GtkWidget *gwin1;
37 extern Display *dpy;
38 static GtkWidget *top_bin;
39 int current_hime_inner_frame;
40
41 static GtkWidget *hbox_edit;
42 static PangoAttrList *attr_list, *attr_list_blank;
43 extern gboolean test_mode;
44
45 void compact_win0 ();
46 void move_win0 (int x, int y);
47 void get_win0_geom ();
48
49 static struct {
50 GtkWidget *vbox;
51 GtkWidget *label;
52 // GtkWidget *line;
53 int x;
54 } chars[MAX_PH_BF_EXT];
55
56 static GtkWidget *button_pho;
57 static GtkWidget *label_pho;
58 extern char text_pho[];
59 extern int text_pho_N;
60 static GtkWidget *button_eng_ph;
61 //static int max_yl;
62
63 static void create_win0_gui ();
64
recreate_win0()65 static void recreate_win0 () {
66 memset (chars, 0, sizeof (chars));
67 label_pho = NULL;
68
69 create_win0_gui ();
70 }
71
72 #if USE_TSIN
change_win0_style()73 void change_win0_style () {
74 if (!top_bin || current_hime_inner_frame == hime_inner_frame)
75 return;
76
77 gtk_widget_destroy (top_bin);
78 top_bin = NULL;
79
80 current_hime_inner_frame = hime_inner_frame;
81 recreate_win0 ();
82 }
83 #endif
84
85 /* there is a bug in gtk, if the widget is created and hasn't been processed by
86 gtk_main(), the coodinate of the widget is sometimes invalid.
87 We use pre-create to overcome this bug.
88 */
89
90 void drawcursor ();
91 void open_select_pho ();
92 void create_phrase_save_menu (GdkEventButton *event);
93
mouse_char_callback(GtkWidget * widget,GdkEventButton * event,gpointer data)94 static void mouse_char_callback (GtkWidget *widget, GdkEventButton *event, gpointer data) {
95 tss.c_idx = GPOINTER_TO_INT (data);
96 drawcursor ();
97
98 switch (event->button) {
99 case 1:
100 case 2:
101 open_select_pho ();
102 break;
103 case 3: {
104 create_phrase_save_menu (event);
105 break;
106 }
107 }
108 }
109
create_char(int index)110 static void create_char (int index) {
111
112 if (!hbox_edit)
113 return;
114
115 GdkColor fg;
116 gdk_color_parse (hime_win_color_fg, &fg);
117 GdkColor color_bg;
118 gdk_color_parse (tsin_phrase_line_color, &color_bg);
119
120 int i = index;
121 {
122 if (chars[i].vbox)
123 return;
124
125 GtkWidget *event_box = gtk_event_box_new ();
126 gtk_event_box_set_visible_window (GTK_EVENT_BOX (event_box), FALSE);
127 chars[i].vbox = event_box;
128 g_signal_connect (
129 G_OBJECT (event_box), "button-press-event",
130 G_CALLBACK (mouse_char_callback), GINT_TO_POINTER (index));
131
132 gtk_box_pack_start (GTK_BOX (hbox_edit), event_box, FALSE, FALSE, 0);
133 GtkWidget *vbox = gtk_vbox_new (FALSE, 0);
134 gtk_orientable_set_orientation (GTK_ORIENTABLE (vbox), GTK_ORIENTATION_VERTICAL);
135 gtk_container_add (GTK_CONTAINER (event_box), vbox);
136
137 GtkWidget *label = gtk_label_new (NULL);
138 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
139
140 set_label_font_size (label, hime_font_size);
141 chars[i].label = label;
142
143 if (hime_win_color_use) {
144 #if !GTK_CHECK_VERSION(3, 0, 0)
145 gtk_widget_modify_fg (label, GTK_STATE_NORMAL, &fg);
146 #else
147 GdkRGBA rgbfg;
148 gdk_rgba_parse (&rgbfg, gdk_color_to_string (&fg));
149 gtk_widget_override_color (label, GTK_STATE_FLAG_NORMAL, &rgbfg);
150 #endif
151 }
152 gtk_widget_show_all (event_box);
153 }
154 }
155
156 extern gboolean b_use_full_space;
157
158 void show_win0 ();
159
disp_char(int index,char * ch)160 void disp_char (int index, char *ch) {
161 if (hime_edit_display_ap_only ())
162 return;
163 if (!top_bin)
164 show_win0 ();
165
166 // dbg("disp_char %d %s\n", index, ch);
167 create_char (index);
168 GtkWidget *label = chars[index].label;
169
170 if (label) {
171 if (ch[0] == ' ' && ch[1] == ' ')
172 set_label_space (label);
173 else {
174 gtk_label_set_text (GTK_LABEL (label), ch);
175 }
176 }
177
178 get_win0_geom ();
179 if (win_x + win_xl >= dpy_xl)
180 move_win0 (dpy_xl - win_xl, win_y);
181
182 gtk_widget_show_all (chars[index].vbox);
183 }
184
hide_char(int index)185 void hide_char (int index) {
186 if (!chars[index].vbox)
187 return;
188 gtk_label_set_text (GTK_LABEL (chars[index].label), "");
189 gtk_widget_hide (chars[index].vbox);
190 }
191
clear_chars_all()192 void clear_chars_all () {
193 int i;
194 for (i = 0; i < MAX_PH_BF_EXT; i++) {
195 hide_char (i);
196 }
197
198 compact_win0 ();
199 }
200
set_cursor_tsin(int index)201 void set_cursor_tsin (int index) {
202 GtkWidget *label = chars[index].label;
203
204 if (!label)
205 return;
206
207 if (hime_edit_display_ap_only ())
208 return;
209
210 gtk_label_set_attributes (GTK_LABEL (label), attr_list);
211 }
212
clr_tsin_cursor(int index)213 void clr_tsin_cursor (int index) {
214 GtkWidget *label = chars[index].label;
215
216 if (!label)
217 return;
218 gtk_label_set_attributes (GTK_LABEL (label), attr_list_blank);
219 }
220
221 void hide_win0 ();
222
disp_tsin_pho(int index,char * pho)223 void disp_tsin_pho (int index, char *pho) {
224 if (hime_display_on_the_spot_key ()) {
225 if (gwin0 && gtk_widget_get_visible (gwin0))
226 hide_win0 ();
227 return;
228 }
229
230 if (button_pho && !gtk_widget_get_visible (button_pho))
231 gtk_widget_show (button_pho);
232
233 text_pho_N = pin_juyin ? 6 : 3;
234 disp_pho_sub (label_pho, index, pho);
235 }
236
237 void disp_tsin_pho (int index, char *pho);
clr_in_area_pho_tsin()238 void clr_in_area_pho_tsin () {
239 int i;
240 for (i = 0; i < text_pho_N; i++)
241 disp_tsin_pho (i, " ");
242 }
243
get_widget_xy(GtkWidget * win,GtkWidget * widget,int * rx,int * ry)244 int get_widget_xy (GtkWidget *win, GtkWidget *widget, int *rx, int *ry) {
245 if (!win && !widget)
246 p_err ("get_widget_xy err");
247
248 // gdk_flush();
249
250 GtkRequisition sz;
251 gtk_widget_get_preferred_size (widget, NULL, &sz);
252 int wx, wy;
253
254 wx = wy = 0;
255
256 gtk_widget_translate_coordinates (widget, win,
257 0, sz.height, &wx, &wy);
258
259 gtk_widget_translate_coordinates (widget, win,
260 0, sz.height, &wx, &wy);
261
262 // dbg("%d wx:%d\n", index, wx);
263
264 int win_x, win_y;
265
266 gtk_window_get_position (GTK_WINDOW (win), &win_x, &win_y);
267 int win_xl, win_yl;
268 get_win_size (win, &win_xl, &win_yl);
269
270 if (wx > win_xl)
271 wx = win_xl;
272
273 *rx = win_x + wx;
274 *ry = win_y + wy;
275 return wx;
276 }
277
278 void getRootXY (Window win, int wx, int wy, int *tx, int *ty);
279 void disp_selections (int x, int y);
disp_tsin_select(int index)280 void disp_tsin_select (int index) {
281 int x, y;
282
283 if (index < 0)
284 return;
285
286 // dbg("hime_edit_display_ap_only() %d\n", hime_edit_display_ap_only());
287
288 if (hime_edit_display_ap_only ()) {
289 getRootXY (current_CS->client_win, current_CS->spot_location.x, current_CS->spot_location.y, &x, &y);
290 } else {
291 #if 1
292 int i;
293 // bug in GTK, widget position is wrong, repeat util find one
294 for (i = index; i >= 0; i--) {
295 gtk_widget_show_now (chars[i].label);
296 gtk_widget_show (chars[i].vbox);
297 gtk_main_iteration_do (FALSE);
298
299 int tx = get_widget_xy (gwin0, chars[i].vbox, &x, &y);
300
301 if (tx >= 0)
302 break;
303 }
304 #else
305 get_widget_xy (gwin0, chars[index].vbox, &x, &y);
306 #endif
307 get_win0_geom ();
308 }
309 disp_selections (x, y);
310 }
311
312 #define MIN_X_SIZE 32
313
314 static int best_win_x, best_win_y;
315
raw_move(int x,int y)316 static void raw_move (int x, int y) {
317 int xl, yl;
318
319 if (!gwin0)
320 return;
321
322 get_win_size (gwin0, &xl, &yl);
323
324 if (x + xl > dpy_xl)
325 x = dpy_xl - xl;
326 if (y + yl > dpy_yl)
327 y = dpy_yl - yl;
328
329 gtk_window_move (GTK_WINDOW (gwin0), x, y);
330 // dbg("gwin0:%x raw_move %d,%d\n", gwin0, x, y);
331 }
332
compact_win0()333 void compact_win0 () {
334 if (!gwin0)
335 return;
336
337 // max_yl = 0;
338 raw_move (best_win_x, best_win_y);
339 }
340
341 gboolean tsin_has_input ();
342 GtkWidget *gwin_sym;
343
move_win0(int x,int y)344 void move_win0 (int x, int y) {
345 // dbg("--- gwin0:%x move_win0 %d,%d\n", gwin0, x,y);
346 best_win_x = x;
347 best_win_y = y;
348
349 if (gwin0)
350 gtk_window_get_size (GTK_WINDOW (gwin0), &win_xl, &win_yl);
351
352 if (x + win_xl > dpy_xl)
353 x = dpy_xl - win_xl;
354 if (x < 0)
355 x = 0;
356
357 if (y + win_yl > dpy_yl)
358 y = dpy_yl - win_yl;
359 if (y < 0)
360 y = 0;
361
362 // dbg("move_win0 %d,%d\n",x, y);
363
364 if (gwin0)
365 gtk_window_move (GTK_WINDOW (gwin0), x, y);
366
367 // dbg("move_win0 %d %d\n",x,y);
368 win_x = x;
369 win_y = y;
370
371 move_win_sym ();
372 }
373
disp_tsin_eng_pho(int eng_pho)374 void disp_tsin_eng_pho (int eng_pho) {
375 static unich_t *eng_pho_strs[] = {N_ ("英"), N_ ("注")};
376
377 if (!button_eng_ph)
378 return;
379
380 gtk_button_set_label (GTK_BUTTON (button_eng_ph), _ (eng_pho_strs[eng_pho]));
381 }
382
mouse_button_callback(GtkWidget * widget,GdkEventButton * event,gpointer data)383 static void mouse_button_callback (GtkWidget *widget, GdkEventButton *event, gpointer data) {
384 // dbg("mouse_button_callback %d\n", event->button);
385 switch (event->button) {
386 case 1:
387 toggle_win_sym ();
388 break;
389 case 2:
390 inmd_switch_popup_handler (widget, (GdkEvent *) event);
391 break;
392 case 3:
393 exec_hime_setup ();
394 break;
395 }
396 }
397
398 void tsin_toggle_eng_ch ();
399
create_win0()400 void create_win0 () {
401 if (gwin0)
402 return;
403 #if _DEBUG && 0
404 dbg ("create_win0\n");
405 #endif
406 gwin0 = gtk_window_new (GTK_WINDOW_TOPLEVEL);
407 gtk_window_set_has_resize_grip (GTK_WINDOW (gwin0), FALSE);
408 gtk_container_set_border_width (GTK_CONTAINER (gwin0), 0);
409 gtk_widget_realize (gwin0);
410 set_no_focus (gwin0);
411 }
412
413 void create_win1 ();
414
create_cursor_attr()415 static void create_cursor_attr () {
416 if (attr_list)
417 pango_attr_list_unref (attr_list);
418
419 GdkColor color_bg, color_fg;
420 if (hime_win_color_use)
421 gdk_color_parse (tsin_cursor_color, &color_bg);
422 else
423 gdk_color_parse (TSIN_CURSOR_COLOR_DEFAULT, &color_bg);
424 gdk_color_parse ("white", &color_fg);
425
426 attr_list = pango_attr_list_new ();
427 attr_list_blank = pango_attr_list_new ();
428
429 PangoAttribute *blue_bg = pango_attr_background_new (
430 color_bg.red, color_bg.green, color_bg.blue);
431 blue_bg->start_index = 0;
432 blue_bg->end_index = 128;
433 pango_attr_list_insert (attr_list, blue_bg);
434
435 PangoAttribute *white_fg = pango_attr_foreground_new (
436 color_fg.red, color_fg.green, color_fg.blue);
437 white_fg->start_index = 0;
438 white_fg->end_index = 128;
439 pango_attr_list_insert (attr_list, white_fg);
440 }
441
442 void init_tsin_selection_win ();
443
set_win0_bg()444 static void set_win0_bg () {
445 #if 1
446 change_win_bg (gwin0);
447 #endif
448 }
449
450 void change_win1_font ();
451
create_win0_gui()452 static void create_win0_gui () {
453 if (top_bin)
454 return;
455
456 GtkWidget *vbox_top = gtk_vbox_new (FALSE, 0);
457 gtk_orientable_set_orientation (GTK_ORIENTABLE (vbox_top), GTK_ORIENTATION_VERTICAL);
458 gtk_container_set_border_width (GTK_CONTAINER (gwin0), 0);
459
460 if (hime_inner_frame) {
461 GtkWidget *frame;
462 top_bin = frame = gtk_frame_new (NULL);
463 gtk_container_set_border_width (GTK_CONTAINER (frame), 0);
464 gtk_container_add (GTK_CONTAINER (gwin0), frame);
465 gtk_container_add (GTK_CONTAINER (frame), vbox_top);
466 } else {
467 top_bin = vbox_top;
468 gtk_container_add (GTK_CONTAINER (gwin0), vbox_top);
469 }
470
471 memset (chars, 0, sizeof (chars));
472
473 GtkWidget *hbox_row1 = gtk_hbox_new (FALSE, 0);
474 /* This packs the button into the gwin0 (a gtk container). */
475 gtk_box_pack_start (GTK_BOX (vbox_top), hbox_row1, FALSE, FALSE, 0);
476
477 hbox_edit = gtk_hbox_new (FALSE, 0);
478 gtk_container_set_border_width (GTK_CONTAINER (hbox_edit), 0);
479 /* This packs the button into the gwin0 (a gtk container). */
480 gtk_box_pack_start (GTK_BOX (hbox_row1), hbox_edit, FALSE, FALSE, 0);
481
482 create_cursor_attr ();
483
484 button_pho = gtk_button_new ();
485 gtk_container_set_border_width (GTK_CONTAINER (button_pho), 0);
486 gtk_box_pack_start (GTK_BOX (hbox_row1), button_pho, FALSE, FALSE, 0);
487
488 g_signal_connect (G_OBJECT (button_pho), "button-press-event",
489 G_CALLBACK (mouse_button_callback), NULL);
490 #if GTK_CHECK_VERSION(2, 18, 0)
491 gtk_widget_set_can_focus (button_pho, FALSE);
492 gtk_widget_set_can_default (button_pho, FALSE);
493 #else
494 GTK_WIDGET_UNSET_FLAGS (button_pho, GTK_CAN_FOCUS | GTK_CAN_DEFAULT);
495 #endif
496
497 #if 0
498 if (left_right_button_tips) {
499 #if GTK_CHECK_VERSION(2, 12, 0)
500 gtk_widget_set_tooltip_text (button_pho, _("Left:Symbol selection tables Right:Preferences"));
501 #else
502 GtkTooltips *button_pho_tips = gtk_tooltips_new ();
503 gtk_tooltips_set_tip (GTK_TOOLTIPS (button_pho_tips), button_pho, _("Left:Symbol selection tables Right:Preferences"),NULL);
504 #endif
505 }
506 #endif
507
508 label_pho = gtk_label_new ("");
509 set_label_font_size (label_pho, hime_font_size_tsin_pho_in);
510 gtk_container_add (GTK_CONTAINER (button_pho), label_pho);
511
512 clr_in_area_pho_tsin ();
513
514 gtk_widget_show_all (gwin0);
515 // gdk_flush();
516 gtk_widget_hide (gwin0);
517
518 init_tsin_selection_win ();
519
520 set_win0_bg ();
521
522 // change_win1_font();
523 }
524
destroy_top_bin()525 static void destroy_top_bin () {
526 if (!top_bin)
527 return;
528 gtk_widget_destroy (top_bin);
529 top_bin = NULL;
530 label_pho = NULL;
531 button_pho = NULL;
532 button_eng_ph = NULL;
533 hbox_edit = NULL;
534 memset (chars, 0, sizeof (chars));
535 }
536
537 #if USE_TSIN
destroy_win0()538 void destroy_win0 () {
539 if (!gwin0)
540 return;
541 destroy_top_bin ();
542 gtk_widget_destroy (gwin0);
543 gwin0 = NULL;
544 }
545 #endif
546
get_win0_geom()547 void get_win0_geom () {
548 if (!gwin0)
549 return;
550 gtk_window_get_position (GTK_WINDOW (gwin0), &win_x, &win_y);
551 get_win_size (gwin0, &win_xl, &win_yl);
552 }
553
554 gboolean tsin_has_input ();
555 extern gboolean force_show;
556 void raise_tsin_selection_win ();
557
show_win0()558 void show_win0 () {
559 #if _DEBUG && 1
560 dbg ("show_win0 pop:%d in:%d for:%d \n", hime_pop_up_win, tsin_has_input (), force_show);
561 #endif
562 create_win0 ();
563 create_win0_gui ();
564
565 if (hime_pop_up_win && !tsin_has_input () && !force_show) {
566 // dbg("show ret\n");
567 return;
568 }
569
570 #if 0
571 if (!gtk_widget_get_visible(gwin0))
572 #endif
573 {
574 // dbg("gtk_widget_show %x\n", gwin0);
575 move_win0 (win_x, win_y);
576 gtk_widget_show (gwin0);
577 }
578
579 show_win_sym ();
580
581 if (current_CS->b_raise_window) {
582 gtk_window_present (GTK_WINDOW (gwin0));
583 raise_tsin_selection_win ();
584 }
585 }
586
587 void hide_selections_win ();
hide_win0()588 void hide_win0 () {
589 if (!gwin0)
590 return;
591
592 gtk_widget_hide (gwin0);
593 if (destroy_window)
594 destroy_win0 ();
595 else
596 destroy_top_bin ();
597
598 hide_selections_win ();
599 hide_win_sym ();
600 }
601
602 void bell ();
603
604 #if USE_TSIN
change_tsin_font_size()605 void change_tsin_font_size () {
606 if (!top_bin)
607 return;
608
609 GdkColor fg;
610 gdk_color_parse (hime_win_color_fg, &fg);
611
612 set_label_font_size (label_pho, hime_font_size_tsin_pho_in);
613
614 int i;
615 for (i = 0; i < MAX_PH_BF_EXT; i++) {
616 GtkWidget *label = chars[i].label;
617 if (!label)
618 continue;
619
620 set_label_font_size (label, hime_font_size);
621
622 if (hime_win_color_use) {
623 #if !GTK_CHECK_VERSION(3, 0, 0)
624 gtk_widget_modify_fg (label, GTK_STATE_NORMAL, &fg);
625 #else
626 GdkRGBA rgbfg;
627 gdk_rgba_parse (&rgbfg, gdk_color_to_string (&fg));
628 gtk_widget_override_color (label, GTK_STATE_FLAG_NORMAL, &rgbfg);
629 #endif
630 }
631 }
632
633 compact_win0 ();
634
635 set_win0_bg ();
636 }
637 #endif // USE_TSIN
638
show_button_pho(gboolean bshow)639 void show_button_pho (gboolean bshow) {
640 if (!button_pho)
641 return;
642
643 if (bshow)
644 gtk_widget_show (button_pho);
645 else {
646 gtk_widget_hide (button_pho);
647 compact_win0 ();
648 }
649 }
650
651 char *get_full_str ();
652
win_tsin_disp_half_full()653 void win_tsin_disp_half_full () {
654 if (label_pho == NULL)
655 show_win0 ();
656
657 if (hime_win_color_use)
658 gtk_label_set_markup (GTK_LABEL (label_pho), get_full_str ());
659 else
660 gtk_label_set_text (GTK_LABEL (label_pho), get_full_str ());
661 compact_win0 ();
662 }
663
664 void drawcursor ();
665
666 #if USE_TSIN
change_tsin_color()667 void change_tsin_color () {
668 create_cursor_attr ();
669
670 drawcursor ();
671 }
672 #endif
673