1 /* Copyright (C) 2011 Edward Der-Hua Liu, Hsin-Chu, Taiwan
2  *
3  * This library is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU Lesser General Public
5  * License as published by the Free Software Foundation version 2.1
6  * of the License.
7  *
8  * This library is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public
14  * License along with this library; if not, write to the Free Software
15  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
16  */
17 
18 #include <sys/stat.h>
19 
20 #include "hime.h"
21 
22 #include "gtab-buf.h"
23 #include "gtab.h"
24 #include "pho.h"
25 #include "win-sym.h"
26 
27 static GtkWidget *gwin_sym = NULL;
28 static int cur_in_method;
29 gboolean win_sym_enabled = 0;
30 
31 typedef struct {
32     char **sym;
33     int symN;
34 } SYM_ROW;
35 
36 static SYM_ROW *syms;
37 static int symsN;
38 
39 typedef struct {
40     SYM_ROW *syms;
41     int symsN;
42 } PAGE;
43 
44 static PAGE *pages;
45 static int pagesN;
46 static int idx;
47 
48 extern char *TableDir;
49 
watch_fopen(char * filename,time_t * pfile_modify_time)50 FILE *watch_fopen (char *filename, time_t *pfile_modify_time) {
51     FILE *fp;
52     char fname[256];
53 
54     get_hime_user_or_sys_fname (filename, fname);
55 
56     if ((fp = fopen (fname, "rb")) == NULL) {
57         strcat (strcat (strcpy (fname, TableDir), "/"), filename);
58 
59         if ((fp = fopen (fname, "rb")) == NULL)
60             return NULL;
61     }
62 
63     struct stat st;
64     fstat (fileno (fp), &st);
65 
66     if (st.st_mtime == *pfile_modify_time) {
67         fclose (fp);
68         return NULL;
69     }
70 
71     *pfile_modify_time = st.st_mtime;
72     return fp;
73 }
74 
save_page()75 static void save_page () {
76     if (!symsN)
77         return;
78 
79     pages = trealloc (pages, PAGE, pagesN + 1);
80     pages[pagesN].syms = syms;
81     pages[pagesN].symsN = symsN;
82     pagesN++;
83     syms = NULL;
84     symsN = 0;
85 }
86 
read_syms()87 static gboolean read_syms () {
88     FILE *fp;
89     static char symbol_table[] = "symbol-table";
90     static time_t file_modify_time;
91 
92     if ((fp = watch_fopen (symbol_table, &file_modify_time)) == NULL)
93         return FALSE;
94 
95     skip_utf8_sigature (fp);
96 
97     int pg;
98     for (pg = 0; pg < pagesN; pg++) {
99         syms = pages[pg].syms;
100         symsN = pages[pg].symsN;
101 
102         int i;
103         for (i = 0; i < symsN; i++) {
104             int j;
105             for (j = 0; j < syms[i].symN; j++)
106                 if (syms[i].sym[j])
107                     free (syms[i].sym[j]);
108         }
109         free (syms);
110     }
111     pagesN = 0;
112     pages = NULL;
113     syms = NULL;
114     symsN = 0;
115 
116     while (!feof (fp)) {
117         char tt[1024];
118 
119         memset (tt, 0, sizeof (tt));
120         myfgets (tt, sizeof (tt), fp);
121         //    dbg("%d] %s\n",strlen(tt), tt);
122 
123 #if 0
124     int len=strlen(tt);
125     if (!len)
126       continue;
127 
128     if (tt[len-1]=='\n') {
129       tt[len-1]=0;
130     }
131 #endif
132 
133         if (tt[0] == 0)
134             save_page ();
135 
136         if (tt[0] == '#')
137             continue;
138 
139         char *p = tt;
140 
141         syms = trealloc (syms, SYM_ROW, symsN + 1);
142         SYM_ROW *psym = &syms[symsN++];
143         memset (psym, 0, sizeof (SYM_ROW));
144 
145         while (*p) {
146             char *n = p;
147 
148             while (*n && *n != '\t')
149                 n++;
150 
151             *n = 0;
152 
153             psym->sym = trealloc (psym->sym, char *, psym->symN + 1);
154             psym->sym[psym->symN++] = strdup (p);
155 
156             p = n + 1;
157         }
158 
159         if (!psym->symN) {
160             free (syms);
161             syms = NULL;
162             symsN = 0;
163         }
164     }
165 
166     if (symsN)
167         save_page ();
168 
169     fclose (fp);
170 
171     idx = 0;
172     syms = pages[idx].syms;
173     symsN = pages[idx].symsN;
174 
175     return TRUE;
176 }
177 
178 gboolean add_to_tsin_buf (char *str, phokey_t *pho, int len);
179 void send_text_call_back (char *text);
180 void tsin_reset_in_pho (), reset_gtab_all (), clr_in_area_pho ();
181 void force_preedit_shift ();
182 gboolean output_gbuf ();
183 void output_buffer_call_back ();
184 gboolean gtab_cursor_end (), gtab_phrase_on ();
185 void flush_tsin_buffer ();
186 gboolean tsin_cursor_end ();
187 void add_to_tsin_buf_str (char *str);
188 
189 extern int c_len;
190 extern short gbufN;
cb_button_sym(GtkButton * button,GtkWidget * label)191 static void cb_button_sym (GtkButton *button, GtkWidget *label) {
192     //  dbg("cb_button_sym\n");
193     char *str = (char *) gtk_label_get_text (GTK_LABEL (label));
194 
195 #if USE_TSIN
196     if (current_method_type () == method_type_TSIN && current_CS->im_state == HIME_STATE_CHINESE) {
197         add_to_tsin_buf_str (str);
198         if (hime_punc_auto_send && tsin_cursor_end ()) {
199             flush_tsin_buffer ();
200             output_buffer_call_back ();
201         } else {
202             force_preedit_shift ();
203         }
204     } else
205 #endif
206         if (gtab_phrase_on ()) {
207         insert_gbuf_nokey (str);
208         if (hime_punc_auto_send && gtab_cursor_end ()) {
209             output_gbuf ();
210             output_buffer_call_back ();
211         } else
212             force_preedit_shift ();
213     } else {
214         send_text_call_back (str);
215     }
216 
217     switch (current_method_type ()) {
218     case method_type_PHO:
219         clr_in_area_pho ();
220         break;
221 #if USE_TSIN
222     case method_type_TSIN:
223         tsin_reset_in_pho ();
224         break;
225 #endif
226     case method_type_MODULE:
227         break;
228     default:
229         reset_gtab_all ();
230         break;
231     }
232 
233     if (hime_win_sym_click_close) {
234         win_sym_enabled = 0;
235         hide_win_sym ();
236     }
237 }
238 
239 void update_active_in_win_geom ();
240 extern int win_status_y;
241 
move_win_sym()242 void move_win_sym () {
243 #if 0
244   dbg("move_win_sym %d\n", current_CS->in_method);
245 #endif
246     if (!gwin_sym)
247         return;
248 
249     int wx, wy;
250 #if 0
251   if (hime_pop_up_win) {
252     wx = dpy_xl;
253   } else
254 #endif
255     {
256         //  dbg("win_y: %d  %d\n", win_y, win_yl);
257         update_active_in_win_geom ();
258 
259         wx = win_x;
260         wy = win_y + win_yl;
261     }
262 
263     int winsym_xl, winsym_yl;
264     get_win_size (gwin_sym, &winsym_xl, &winsym_yl);
265 
266     if (wx + winsym_xl > dpy_xl)
267         wx = dpy_xl - winsym_xl;
268     if (wx < 0)
269         wx = 0;
270 
271 #if 0
272   if (hime_pop_up_win) {
273     wy = win_status_y - winsym_yl;
274   } else
275 #endif
276     {
277         if (wy + winsym_yl > dpy_yl)
278             wy = win_y - winsym_yl;
279         if (wy < 0)
280             wy = 0;
281     }
282 
283     gtk_window_move (GTK_WINDOW (gwin_sym), wx, wy);
284 }
285 
hide_win_sym()286 void hide_win_sym () {
287     if (!gwin_sym)
288         return;
289     gtk_widget_hide (gwin_sym);
290 }
291 
show_win_sym()292 void show_win_sym () {
293     if (!current_CS)
294         return;
295 
296     if (!gwin_sym || !win_sym_enabled || current_CS->im_state == HIME_STATE_DISABLED)
297         return;
298 #if 0
299   dbg("show_win_sym\n");
300 #endif
301     gtk_widget_show_all (gwin_sym);
302     move_win_sym ();
303 }
304 
305 void lookup_gtab_out (char *ch, char *out);
306 void str_to_all_phokey_chars (char *b5_str, char *out);
307 
sym_lookup_key(char * instr,char * outstr)308 static void sym_lookup_key (char *instr, char *outstr) {
309     if (current_method_type () == method_type_PHO || current_method_type () == method_type_TSIN) {
310         str_to_all_phokey_chars (instr, outstr);
311     } else {
312         outstr[0] = 0;
313 
314         while (*instr) {
315             char tt[512];
316             tt[0] = 0;
317             lookup_gtab_out (instr, tt);
318             strcat (outstr, tt);
319 
320             instr += utf8_sz (instr);
321 
322             if (*instr)
323                 strcat (outstr, " | ");
324         }
325     }
326 }
327 
destory_win()328 static void destory_win () {
329     if (gwin_sym)
330         gtk_widget_destroy (gwin_sym);
331     gwin_sym = NULL;
332 }
333 
disp_win_sym()334 static void disp_win_sym () {
335     syms = pages[idx].syms;
336     symsN = pages[idx].symsN;
337     destory_win ();
338     //  win_sym_enabled = 0;
339     create_win_sym ();
340 }
341 
win_sym_page_up()342 gboolean win_sym_page_up () {
343     if (!win_sym_enabled)
344         return FALSE;
345     idx--;
346     if (idx < 0)
347         idx = pagesN - 1;
348     disp_win_sym ();
349     return TRUE;
350 }
351 
win_sym_page_down()352 gboolean win_sym_page_down () {
353     //  dbg("win_sym_page_down\n");
354     if (!win_sym_enabled)
355         return FALSE;
356     idx = (idx + 1) % pagesN;
357     disp_win_sym ();
358     return TRUE;
359 }
360 
button_scroll_event(GtkWidget * widget,GdkEventScroll * event,gpointer user_data)361 static gboolean button_scroll_event (GtkWidget *widget, GdkEventScroll *event, gpointer user_data) {
362     if (pagesN < 2)
363         return TRUE;
364 
365     switch (event->direction) {
366     case GDK_SCROLL_UP:
367         win_sym_page_up ();
368         break;
369     case GDK_SCROLL_DOWN:
370         win_sym_page_down ();
371         break;
372     default:
373         break;
374     }
375 
376     return TRUE;
377 }
378 
mouse_button_callback_up_down(GtkWidget * widget,GdkEventButton * event,gpointer data)379 static void mouse_button_callback_up_down (GtkWidget *widget, GdkEventButton *event, gpointer data) {
380     GdkEventScroll sc;
381     sc.direction = data ? GDK_SCROLL_UP : GDK_SCROLL_DOWN;
382     button_scroll_event (NULL, &sc, NULL);
383 }
384 
create_win_sym()385 void create_win_sym () {
386     if (!current_CS) {
387         dbg ("create_win_sym, null CS\n");
388         return;
389     }
390 
391     if (current_CS->in_method < 0) {
392         p_err ("bad current_CS %d\n", current_CS->in_method);
393     }
394 
395     if (current_method_type () != method_type_PHO && current_method_type () != method_type_TSIN && current_method_type () != method_type_MODULE && !cur_inmd)
396         return;
397 
398     if (read_syms () || cur_in_method != current_CS->in_method) {
399         destory_win ();
400     } else {
401         if (!syms)
402             return;
403     }
404 
405     if (gwin_sym) {
406         if (win_sym_enabled)
407             show_win_sym ();
408         else
409             hide_win_sym ();
410 
411         return;
412     }
413 
414     gwin_sym = gtk_window_new (GTK_WINDOW_TOPLEVEL);
415     gtk_window_set_has_resize_grip (GTK_WINDOW (gwin_sym), FALSE);
416 
417     cur_in_method = current_CS->in_method;
418 
419     GtkWidget *hbox_top = gtk_hbox_new (FALSE, 0);
420     gtk_container_add (GTK_CONTAINER (gwin_sym), hbox_top);
421 
422     GtkWidget *vbox_top = gtk_vbox_new (FALSE, 0);
423     gtk_orientable_set_orientation (GTK_ORIENTABLE (vbox_top), GTK_ORIENTATION_VERTICAL);
424     gtk_box_pack_start (GTK_BOX (hbox_top), vbox_top, TRUE, TRUE, 0);
425 
426     gtk_container_set_border_width (GTK_CONTAINER (vbox_top), 0);
427 
428     int i;
429     for (i = 0; i < symsN; i++) {
430         SYM_ROW *psym = &syms[i];
431         GtkWidget *hbox_row = gtk_hbox_new (FALSE, 0);
432         gtk_box_pack_start (GTK_BOX (vbox_top), hbox_row, FALSE, FALSE, 0);
433         gtk_container_set_border_width (GTK_CONTAINER (hbox_row), 0);
434 
435         int j;
436         for (j = 0; j < psym->symN; j++) {
437             char *str = psym->sym[j];
438 
439             if (!str[0])
440                 continue;
441 
442             GtkWidget *button = gtk_button_new ();
443             GtkWidget *label = gtk_label_new (str);
444 
445             gtk_container_add (GTK_CONTAINER (button), label);
446             set_label_font_size (label, hime_font_size_symbol);
447 
448             gtk_container_set_border_width (GTK_CONTAINER (button), 0);
449             gtk_box_pack_start (GTK_BOX (hbox_row), button, FALSE, FALSE, 0);
450 
451             if (utf8_str_N (str) > 0) {
452                 char phos[512];
453 
454                 sym_lookup_key (str, phos);
455 
456                 int phos_len = strlen (phos);
457 
458                 if (phos_len) {
459 #if GTK_CHECK_VERSION(2, 12, 0)
460                     gtk_widget_set_tooltip_text (button, phos);
461 #else
462                     GtkTooltips *button_pho_tips = gtk_tooltips_new ();
463                     gtk_tooltips_set_tip (GTK_TOOLTIPS (button_pho_tips), button, phos, NULL);
464 #endif
465                 }
466             }
467 
468             g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (cb_button_sym), label);
469         }
470     }
471 
472     gtk_box_pack_start (GTK_BOX (hbox_top), gtk_vseparator_new (), FALSE, FALSE, 0);
473 
474     GtkWidget *vbox_arrow = gtk_vbox_new (TRUE, 0);
475     gtk_orientable_set_orientation (GTK_ORIENTABLE (vbox_arrow), GTK_ORIENTATION_VERTICAL);
476     gtk_box_pack_start (GTK_BOX (hbox_top), vbox_arrow, TRUE, TRUE, 0);
477     GtkWidget *eve_up = gtk_event_box_new (), *eve_down = gtk_event_box_new ();
478     gtk_event_box_set_visible_window (GTK_EVENT_BOX (eve_up), FALSE);
479     gtk_event_box_set_visible_window (GTK_EVENT_BOX (eve_down), FALSE);
480     gtk_box_pack_start (GTK_BOX (vbox_arrow), eve_up, TRUE, TRUE, 0);
481     gtk_container_add (GTK_CONTAINER (eve_up), gtk_arrow_new (GTK_ARROW_UP, GTK_SHADOW_IN));
482     gtk_box_pack_start (GTK_BOX (vbox_arrow), eve_down, TRUE, TRUE, 0);
483     gtk_container_add (GTK_CONTAINER (eve_down), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_IN));
484 
485     g_signal_connect (G_OBJECT (eve_up), "button-press-event", G_CALLBACK (mouse_button_callback_up_down), (gpointer) 1);
486     g_signal_connect (G_OBJECT (eve_down), "button-press-event", G_CALLBACK (mouse_button_callback_up_down), NULL);
487 
488     gtk_widget_realize (gwin_sym);
489     set_no_focus (gwin_sym);
490 
491     if (win_sym_enabled)
492         gtk_widget_show_all (gwin_sym);
493 
494     g_signal_connect (G_OBJECT (gwin_sym), "scroll-event", G_CALLBACK (button_scroll_event), NULL);
495 
496     move_win_sym ();
497 #if 0
498   dbg("in_method:%d\n", current_CS->in_method);
499 #endif
500     return;
501 }
502 
toggle_win_sym()503 void toggle_win_sym () {
504     win_sym_enabled ^= 1;
505     create_win_sym ();
506 }
507 
change_win_sym_font_size()508 void change_win_sym_font_size () {
509 }
510