Change the set mode behavior
[fvkbd:fvkbd.git] / ui / gtk / fvkbd-key-ui-gtk.c
1 /*
2  * fvkbd-key-ui-gtk.c key unit gtk ui for fvkbd  
3  *
4  * Copyright (C) 2009, Intel Corporation.
5  *
6  * Author: Raymond Liu <raymond.liu@intel.com>
7  * 
8  * 
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * version 2.1 as published by the Free Software Foundation.
12  * 
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  * 
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21  * 02110-1301 USA
22  */
23
24
25 #include <gtk/gtk.h>
26 #include "fvkbd.h"
27 #include "fvkbd-keyboard.h"
28 #include "gtk-misc-utility.h"
29
30 #include "fvkbd-key.h"
31
32 typedef struct _KeyUI KeyUI;
33 struct _KeyUI {
34         GtkWidget *main_label;
35         GtkWidget *sub_label;
36 };
37
38 typedef struct _CandidateItem CandidateItem;
39 struct _CandidateItem {
40         GtkWidget *widget;
41         KeyActionType type;
42         gchar *disp;
43         union 
44         {
45                 gchar *string;
46                 KeySym sym;
47                 FvkbdKeyFunc func;
48         } u;
49 };
50
51 #define MAX_CANDIDATE_ITEMS 10
52 #define CANDIDATE_ITEM_DEF_WIDTH 80
53 #define CANDIDATE_ITEM_DEF_HEIGHT 80
54
55 static guint longpress_timeout_id = 0;
56 static guint longpress_timeout_val = 800;
57 static guint pop_window_hide_timeout_id = 0;
58 static guint pop_window_hide_timeout_val = 5000;
59
60 static GtkWidget *pop_window = NULL;
61 static CandidateItem candidate_items[MAX_CANDIDATE_ITEMS];
62
63 static gboolean key_mode_do_switch_lock = FALSE;
64
65
66 static void set_candidate_item_string(gint n, gchar *disp, gchar *str);
67 static void candidate_item_clicked_cb(GtkButton *button, CandidateItem *item);
68 static void init_candidate_items(void);
69 static void update_candidate_item_none(void);
70 static void update_candidate_item_string(gchar *disp, gchar *str);
71 static void update_candidate_item_string_group(gchar **strs);
72 static void update_candidate_item_sym(gchar *disp, KeySym sym);
73 static GtkWidget *get_pop_window(void);
74 static void settle_pop_window(FvkbdKey *key);
75 static void show_pop_window(FvkbdKey *key, gboolean longpress);
76 static void hide_pop_window(void);
77 static gboolean hide_pop_window_cb(FvkbdKey *key);
78 static gboolean longpress_detected_cb(FvkbdKey *key);
79
80
81 static void
82 set_candidate_item_string(gint n, gchar *disp, gchar *str)
83 {
84         GtkWidget *button;
85         if (str == NULL || n >= MAX_CANDIDATE_ITEMS)
86                 return;
87
88         candidate_items[n].type = KEY_ACTION_STRING;
89         candidate_items[n].u.string = str;
90         button = candidate_items[n].widget;
91         gtk_button_set_label(GTK_BUTTON(button), disp);
92 }
93
94
95 static void
96 candidate_item_clicked_cb(GtkButton *button, CandidateItem *item)
97 {
98         switch (item->type) {
99
100         case KEY_ACTION_STRING:
101                 fvkbd_key_send_utf8_string(item->u.string);
102                 break;
103
104         case KEY_ACTION_SYM:
105                 fvkbd_key_send_xkeysym(item->u.sym);
106                 break;
107
108         default:
109                 break;
110         }
111
112         if (pop_window_hide_timeout_id != 0) {
113                 g_source_remove(pop_window_hide_timeout_id);
114                 pop_window_hide_timeout_id = 0;
115                 hide_pop_window();
116         }
117
118         if (fvkbd_in_temp_mode())
119                 fvkbd_keyboard_resume_default_mode();
120 }
121
122
123 static void
124 init_candidate_items(void)
125 {
126         int i;
127         for (i = 0; i < MAX_CANDIDATE_ITEMS; i++) {
128                 candidate_items[i].widget = gtk_button_new();
129                 gtk_widget_set_size_request(candidate_items[i].widget,
130                                                 CANDIDATE_ITEM_DEF_WIDTH,
131                                                 CANDIDATE_ITEM_DEF_HEIGHT);
132                 g_signal_connect(G_OBJECT(candidate_items[i].widget), "clicked",
133                         G_CALLBACK(candidate_item_clicked_cb), &candidate_items[i]);
134         }
135 }
136
137
138 static void
139 update_candidate_item_none(void)
140 {
141         int i;
142
143         for (i = 0; i < MAX_CANDIDATE_ITEMS; i++)
144                 gtk_widget_hide(candidate_items[i].widget);
145 }
146
147
148 static void
149 update_candidate_item_string(gchar *disp, gchar *str)
150 {
151         int i;
152
153         set_candidate_item_string(0, disp, str);
154         gtk_widget_show(candidate_items[0].widget);
155
156         for (i = 1; i < MAX_CANDIDATE_ITEMS; i++)
157                 gtk_widget_hide(candidate_items[i].widget);
158
159 }
160
161
162 static void
163 update_candidate_item_string_group(gchar **strs)
164 {
165         int i;
166         if (strs == NULL)
167                 return;
168
169         for (i = 0; (i < MAX_CANDIDATE_ITEMS) && *strs; i++, strs++) {
170                 set_candidate_item_string(i, *strs, *strs);
171                 gtk_widget_show(candidate_items[i].widget);
172         }
173
174         for (; i < MAX_CANDIDATE_ITEMS; i++)
175                 gtk_widget_hide(candidate_items[i].widget);
176 }
177
178
179 static void
180 update_candidate_item_sym(gchar *disp, KeySym sym)
181 {
182         int i;
183         GtkWidget *button;
184
185         candidate_items[0].type = KEY_ACTION_SYM;
186         candidate_items[0].u.sym = sym;
187         button = candidate_items[0].widget;
188         gtk_button_set_label(GTK_BUTTON(button), disp);
189         gtk_widget_show(button);
190
191         for (i = 1; i < MAX_CANDIDATE_ITEMS; i++)
192                 gtk_widget_hide(candidate_items[i].widget);
193 }
194
195
196 static GtkWidget *
197 get_pop_window(void)
198 {
199         if (pop_window == NULL) {
200                 GtkWidget *hbox, *frame;
201                 int i;
202
203                 pop_window = gtk_window_new(GTK_WINDOW_POPUP);
204                 gtk_window_set_accept_focus(GTK_WINDOW(pop_window), FALSE);
205                 gtk_window_set_resizable(GTK_WINDOW(pop_window), FALSE);
206
207                 frame = gtk_frame_new(NULL);
208                 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_OUT);
209
210                 hbox = gtk_hbox_new(FALSE, 0);
211
212                 gtk_container_add (GTK_CONTAINER (frame), hbox);
213
214                 init_candidate_items();
215                 for (i = 0; i < MAX_CANDIDATE_ITEMS; i++) {
216                         gtk_box_pack_start(GTK_BOX(hbox), candidate_items[i].widget,
217                                                 FALSE, FALSE, 0);
218                 }
219
220                 gtk_widget_show_all(frame);
221
222                 gtk_container_add (GTK_CONTAINER (pop_window), frame);
223         }
224
225         return pop_window;
226 }
227
228
229 static void
230 settle_pop_window(FvkbdKey *key)
231 {
232         GtkWidget *w = get_pop_window();
233         GdkScreen *screen;
234         int pointer_x = 0, pointer_y = 0;
235         int pop_w = 0, pop_h = 0;
236         int x, y;
237         GtkWidget *key_widget;
238
239         key_widget = fvkbd_unit_get_widget(FVKBD_UNIT(key));
240
241         screen = gtk_widget_get_screen (key_widget);
242         gdk_display_get_pointer (gdk_screen_get_display (screen),
243                                  NULL, &pointer_x, &pointer_y, NULL);
244
245         gtk_window_get_size(GTK_WINDOW(w), &pop_w, &pop_h);
246
247         if ((y = (pointer_y - pop_h)) < 0)
248                 y = pointer_y + 2;
249         x = pointer_x + 2;
250
251         gtk_window_move(GTK_WINDOW(w), x, y);
252 }
253
254
255 static void
256 show_pop_window(FvkbdKey *key, gboolean longpress)
257 {
258         FvkbdKeyAction *action;
259         GtkWidget *w = get_pop_window();
260
261         action = fvkbd_key_get_current_action(key);
262
263         switch (action->type) {
264
265         case KEY_ACTION_STRING:
266                 update_candidate_item_string(action->disp, action->u.string);
267                 break;
268
269         case KEY_ACTION_STRING_GROUP:
270                 if (!longpress)
271                         update_candidate_item_string(action->disp, action->u.string);
272                 else
273                         update_candidate_item_string_group(action->u.string_group);
274                 break;
275
276         case KEY_ACTION_SYM:
277                 update_candidate_item_sym(action->disp, action->u.sym);
278                 break;
279
280         default:
281                 update_candidate_item_none();
282                 hide_pop_window();
283                 return;
284         }
285
286         settle_pop_window(key);
287         if (!GTK_WIDGET_VISIBLE(w))
288                 gtk_widget_show(w);
289 }
290
291
292 static void
293 hide_pop_window(void)
294 {
295         GtkWidget *w = get_pop_window();
296         if (GTK_WIDGET_VISIBLE(w))
297                 gtk_widget_hide(w);
298 }
299
300
301 static gboolean
302 hide_pop_window_cb(FvkbdKey *key)
303 {
304         hide_pop_window();
305         pop_window_hide_timeout_id = 0;
306         
307         return FALSE;
308 }
309
310
311 static gboolean
312 longpress_detected_cb(FvkbdKey *key)
313 {
314         FvkbdKeyAction *action;
315
316         action = fvkbd_key_get_current_action(key);
317
318         switch (action->type) {
319
320         case KEY_ACTION_STRING:
321         case KEY_ACTION_STRING_GROUP:
322         case KEY_ACTION_SYM:
323                 show_pop_window(key, TRUE);
324                 pop_window_hide_timeout_id = g_timeout_add(pop_window_hide_timeout_val,
325                                                 (GSourceFunc)hide_pop_window_cb, key);
326
327                 break;
328         case KEY_ACTION_FUNC:
329                 switch (action->u.func.type) {
330                 case FVKBD_KEY_FUNC_MODE_SELECT:
331                         key_mode_do_switch_lock = TRUE;
332                         break;
333
334                 default:
335                         break;
336                 }
337
338                 break;
339         default:
340                 break;
341         }
342
343         longpress_timeout_id = 0;
344         
345         return FALSE;
346 }
347
348
349 static void
350 fvkbd_key_gtk_pressed_cb(GtkButton *button, FvkbdKey *key)
351 {
352         if (longpress_timeout_id != 0) {
353                 g_source_remove(longpress_timeout_id);
354                 longpress_timeout_id = 0;
355         }
356
357         if (pop_window_hide_timeout_id != 0) {
358                 g_source_remove(pop_window_hide_timeout_id);
359                 pop_window_hide_timeout_id = 0;
360         }
361
362         longpress_timeout_id = g_timeout_add(longpress_timeout_val,
363                                         (GSourceFunc)longpress_detected_cb, key);
364
365         show_pop_window(key, FALSE);
366 }
367
368
369 static void
370 fvkbd_key_gtk_released_cb(GtkButton *button, FvkbdKey *key)
371 {
372         FvkbdKeyAction *action;
373         gboolean need_reset_default_mode = TRUE;
374
375         /* if the candiate window is shown, then we do nothing and return */
376         if (pop_window_hide_timeout_id != 0) {
377                 return;
378         }
379
380         if (longpress_timeout_id != 0) {
381                 g_source_remove(longpress_timeout_id);
382                 longpress_timeout_id = 0;
383         }
384
385         hide_pop_window();
386
387         if ((action = fvkbd_key_get_current_action(key)) == NULL)
388                 return;
389
390         switch (action->type) {
391         case KEY_ACTION_STRING:
392                 fvkbd_key_send_utf8_string(action->u.string);
393                 break;
394
395         case KEY_ACTION_STRING_GROUP:
396                 fvkbd_key_send_utf8_string(action->u.string_group[0]);
397                 break;
398
399         case KEY_ACTION_SYM:
400                 fvkbd_key_send_xkeysym(action->u.sym);
401                 break;
402
403         case KEY_ACTION_FUNC:
404                 switch (action->u.func.type) {
405                 case FVKBD_KEY_FUNC_MODE_SELECT:
406                         fvkbd_key_func_handlers(key, action, &key_mode_do_switch_lock);
407                         need_reset_default_mode = FALSE;
408                         key_mode_do_switch_lock = FALSE;
409                         break;
410
411                 default:
412                         fvkbd_key_func_handlers(key, action, NULL);
413                 }
414
415                 break;
416
417         case KEY_ACTION_SCRIPT:
418                 break;
419
420         default:
421                 break;
422         }
423
424         if (fvkbd_in_temp_mode() && need_reset_default_mode)
425                 fvkbd_keyboard_resume_default_mode();
426
427 }
428
429
430 static gboolean
431 get_key_gdkcolor (FvkbdUnit *unit, KeyColorType type, GdkColor *color)
432 {
433         KbdColor kc;
434
435         if (!fvkbd_key_get_color(unit, type, &kc))
436                 return FALSE;
437
438         if (!color)
439                 return FALSE;
440
441         kbdcolor_to_gdkcolor(kc,color);
442         return TRUE;
443 }
444
445
446 static gboolean
447 get_key_gdkcolor_reverse (FvkbdUnit *unit, KeyColorType type, GdkColor *color)
448 {
449         KbdColor kc;
450
451         if (!fvkbd_key_get_color(unit, type, &kc))
452                 return FALSE;
453
454         kc.r = 255 - kc.r;
455         kc.g = 255 - kc.g;
456         kc.b = 255 - kc.b;
457
458         if (!color)
459                 return FALSE;
460
461         kbdcolor_to_gdkcolor(kc,color);
462         return TRUE;
463 }
464
465
466 static void
467 fvkbd_key_settle_color (FvkbdUnit *unit, GtkWidget *key_widget)
468 {
469         KeyUI *ui_data = fvkbd_unit_get_ui_data(unit);
470         GdkColor color;
471         GtkWidget *label;
472
473         if (get_key_gdkcolor(unit, KEY_COLOR_TYPE_BG, &color)) {
474                 gtk_widget_modify_bg(key_widget, GTK_STATE_NORMAL, &color);
475                 gtk_widget_modify_bg(key_widget, GTK_STATE_PRELIGHT, &color);
476         }
477
478         if (get_key_gdkcolor_reverse(unit, KEY_COLOR_TYPE_BG, &color)) {
479                 gtk_widget_modify_bg(key_widget, GTK_STATE_ACTIVE, &color);
480         }
481
482         // set main label color
483         label = ui_data->main_label;
484
485         if (get_key_gdkcolor(unit, KEY_COLOR_TYPE_FG, &color)) {
486                 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &color);
487                 gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &color);
488         }
489
490         if (get_key_gdkcolor_reverse(unit, KEY_COLOR_TYPE_FG, &color)) {
491                 gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &color);
492         }
493
494         // set sub label color
495         label = ui_data->sub_label;
496
497         if (get_key_gdkcolor(unit, KEY_COLOR_TYPE_EXTRA_FG, &color)) {
498                 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &color);
499                 gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &color);
500         }
501 }
502
503
504 static void
505 fvkbd_key_settle_font (FvkbdUnit *unit, GtkWidget *key_widget)
506 {
507         KeyUI *ui_data = fvkbd_unit_get_ui_data(unit);
508         PangoFontDescription *desc;
509         GtkWidget *label;
510
511         // set main label font
512         label = ui_data->main_label;
513
514         desc = get_pango_font_description();
515         gtk_widget_modify_font(label, desc);
516 }
517
518
519 static GtkWidget *
520 _create_key_label (FvkbdUnit *unit, gint id)
521 {
522         FvkbdKey *key;
523         FvkbdKeyAction *action;
524         KeyUI *ui_data = fvkbd_unit_get_ui_data(unit);
525         gint w,h;
526         GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
527         GtkWidget *label = NULL, *sub_label = NULL;
528
529         g_return_val_if_fail(FVKBD_IS_KEY(unit), FALSE);
530         key = FVKBD_KEY(unit);
531
532         fvkbd_unit_get_size(unit, &w, &h);
533
534         if ((action = fvkbd_key_get_current_action(key)) == NULL)
535                 goto done;
536
537         if (action->type == KEY_ACTION_STRING_GROUP) {
538                 gchar *label_str = NULL;
539
540                 label_str = g_strjoinv(" ", &(action->u.string_group[1]));
541                 sub_label = gtk_label_new(label_str);
542                 g_free(label_str);
543                 label = gtk_label_new(fvkbd_key_get_disp(key, id));
544         } else if (action->type == KEY_ACTION_STRING ||
545                 action->type == KEY_ACTION_STRING_GROUP ||
546                 action->type == KEY_ACTION_SYM ||
547                 action->type == KEY_ACTION_FUNC) {
548
549                 sub_label = gtk_label_new(NULL);
550                 label = gtk_label_new(fvkbd_key_get_disp(key, id));
551         }
552
553         ui_data->sub_label = sub_label;
554         if (sub_label)
555                 gtk_box_pack_start(GTK_BOX(vbox), sub_label, FALSE, FALSE, 0);
556
557         ui_data->main_label = label;
558         gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
559
560 done:
561         gtk_widget_show_all(vbox);
562         return vbox;
563 }
564
565
566 static void
567 _update_key_label (FvkbdUnit *unit, gint id)
568 {
569         GtkWidget *key_widget;
570         GtkWidget *key_label;
571
572         key_widget = fvkbd_unit_get_widget(unit);
573         
574         key_label = gtk_bin_get_child(GTK_BIN(key_widget));
575
576         if (key_label) {
577                 gtk_widget_destroy(key_label);
578                 key_label = NULL;
579         }
580
581         key_label = _create_key_label(unit, id);
582         gtk_container_add(GTK_CONTAINER(key_widget), key_label);
583
584         // set color
585         fvkbd_key_settle_color(unit, key_widget);
586
587         // set font
588         fvkbd_key_settle_font(unit, key_widget);
589 }
590
591
592 static void
593 _set_key_label_tmp_mode (FvkbdUnit *unit)
594 {
595         KeyUI *ui_data = fvkbd_unit_get_ui_data(unit);
596         GtkWidget *label;
597         gchar *disp;
598
599         label = ui_data->main_label;
600         disp = g_strconcat("<u>", gtk_label_get_text(GTK_LABEL(label)), "</u>", NULL);
601         gtk_label_set_text(GTK_LABEL(label), disp);
602         gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
603         g_free(disp);
604 }
605
606
607 gboolean
608 fvkbd_key_build_ui (FvkbdUnit *unit, gpointer *widget)
609 {
610         FvkbdKey *key;
611         KeyUI *ui_data;
612         gint w,h;
613         gboolean ret = TRUE;
614
615         GtkWidget *key_widget;
616         GtkWidget *key_label;
617
618         g_return_val_if_fail(FVKBD_IS_KEY(unit), FALSE);
619         key = FVKBD_KEY(unit);
620
621         key_widget = gtk_button_new();
622
623         ui_data = g_new0(KeyUI, 1);
624         fvkbd_unit_set_ui_data(unit, ui_data);
625         ui_data->main_label = NULL;
626         ui_data->sub_label = NULL;
627
628         fvkbd_unit_get_size(unit, &w, &h);
629         gtk_widget_set_size_request(key_widget, w, h);
630
631         key_label = _create_key_label(unit, 0);
632         gtk_container_add(GTK_CONTAINER(key_widget), key_label);
633
634         // set color
635         fvkbd_key_settle_color(unit, key_widget);
636
637         // set font
638         fvkbd_key_settle_font(unit, key_widget);
639
640         g_signal_connect(G_OBJECT(key_widget), "pressed",
641                         G_CALLBACK(fvkbd_key_gtk_pressed_cb), key);
642         g_signal_connect(G_OBJECT(key_widget), "released",
643                         G_CALLBACK(fvkbd_key_gtk_released_cb), key);
644
645         if (widget != NULL)
646                 *widget = key_widget;
647         fvkbd_unit_set_widget(unit, key_widget);
648         return ret;
649 }
650
651
652 gboolean
653 fvkbd_key_destroy_ui (FvkbdUnit *unit)
654 {
655         FvkbdKey *key;
656         KeyUI *ui_data;
657         gboolean ret = TRUE;
658
659         GtkWidget *key_widget;
660
661         g_return_val_if_fail(FVKBD_IS_KEY(unit), FALSE);
662         key = FVKBD_KEY(unit);
663
664         key_widget = fvkbd_unit_get_widget(unit);
665
666         gtk_widget_hide_all(key_widget);
667         gtk_widget_destroy(key_widget);
668
669         fvkbd_unit_set_widget(unit, NULL);
670
671         ui_data = fvkbd_unit_get_ui_data(unit);
672         g_free(ui_data);
673         fvkbd_unit_set_ui_data(unit, NULL);
674
675         return ret;
676 }
677
678
679 gint
680 fvkbd_key_set_mode (FvkbdUnit *unit, gint id)
681 {
682         FvkbdKey *key;
683         FvkbdKeyAction *action;
684         GtkWidget *key_widget;
685
686         g_return_val_if_fail(FVKBD_IS_KEY(unit), -1);
687         key = FVKBD_KEY(unit);
688
689         key_widget = fvkbd_unit_get_widget(unit);
690
691         // Yes, we get the default action here to handle disp diffirently for tmply mode change
692         action = fvkbd_key_get_action(key, 0);
693
694         if ((action->type == KEY_ACTION_FUNC) &&
695                 (action->u.func.type == FVKBD_KEY_FUNC_MODE_SELECT)) {
696                 if ((fvkbd_in_temp_mode()) && ((*((int *)action->u.func.data)) == id)) {
697                         _update_key_label(unit, 0);
698                         _set_key_label_tmp_mode(unit);
699                 }
700         } else {
701                 _update_key_label(unit, id);
702         }
703
704         return 0;
705 }
706