Only show one optional character on key button UI
[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         PangoFontDescription *font_desc;
38         PangoFontDescription *efont_desc;
39         PangoFontDescription *ofont_desc;
40 };
41
42 typedef struct _CandidateItem CandidateItem;
43 struct _CandidateItem {
44         GtkWidget *widget;
45         KeyActionType type;
46         gchar *disp;
47         union 
48         {
49                 gchar *string;
50                 KeySym sym;
51                 FvkbdKeyFunc func;
52         } u;
53 };
54
55 #define MAX_CANDIDATE_ITEMS 10
56
57 static guint longpress_timeout_id = 0;
58 static guint longpress_timeout_val = 800;
59 static guint pop_window_hide_timeout_id = 0;
60 static guint pop_window_hide_timeout_val = 5000;
61
62 static GtkWidget *pop_window = NULL;
63 static CandidateItem candidate_items[MAX_CANDIDATE_ITEMS];
64
65 static gboolean key_mode_do_switch_lock = FALSE;
66
67
68 static void set_candidate_item_string(gint n, gchar *disp, gchar *str, PangoFontDescription *desc);
69 static void candidate_item_clicked_cb(GtkButton *button, CandidateItem *item);
70 static void init_candidate_items(void);
71 static void update_candidate_item_none(void);
72 static void update_candidate_item_string(gchar *disp, gchar *str, PangoFontDescription *desc, gboolean reverse);
73 static void update_candidate_item_string_group(gchar **strs, PangoFontDescription *desc, gboolean reverse);
74 static void update_candidate_item_sym(gchar *disp, KeySym sym, PangoFontDescription *desc, gboolean reverse);
75 static GtkWidget *get_pop_window(void);
76 static void settle_pop_window(FvkbdKey *key, gint number, gboolean reverse);
77 static void show_pop_window(FvkbdKey *key, gboolean longpress);
78 static void hide_pop_window(void);
79 static gboolean hide_pop_window_cb(FvkbdKey *key);
80 static gboolean longpress_detected_cb(FvkbdKey *key);
81 static PangoFontDescription * get_key_pango_font_description (FvkbdUnit *unit, KbdFontType type);
82
83
84 static void
85 set_candidate_item_dummy(gint n)
86 {
87         GtkWidget *button;
88
89         if (n >= MAX_CANDIDATE_ITEMS)
90                 return;
91
92         candidate_items[n].type = KEY_ACTION_NONE;
93         button = candidate_items[n].widget;
94         gtk_button_set_label(GTK_BUTTON(button), "");
95 }
96
97
98 static void
99 set_candidate_item_string(gint n, gchar *disp, gchar *str, PangoFontDescription *desc)
100 {
101         GtkWidget *button;
102         GtkWidget *key_label;
103         if (str == NULL || n >= MAX_CANDIDATE_ITEMS)
104                 return;
105
106         candidate_items[n].type = KEY_ACTION_STRING;
107         candidate_items[n].u.string = str;
108         button = candidate_items[n].widget;
109         gtk_button_set_label(GTK_BUTTON(button), disp);
110         key_label = gtk_bin_get_child(GTK_BIN(button));
111         gtk_widget_modify_font(key_label, desc);
112 }
113
114
115 static void
116 set_candidate_item_color(gint n, KbdColorType type, GdkColor *color)
117 {
118         GtkWidget *button;
119         GtkWidget *label;
120
121         if (n >= MAX_CANDIDATE_ITEMS)
122                 return;
123
124         if ((type != KBD_COLOR_TYPE_KEY_POP_BG) && (type != KBD_COLOR_TYPE_KEY_POP_FG))
125                 return;
126
127         button = candidate_items[n].widget;
128         if (type == KBD_COLOR_TYPE_KEY_POP_BG) {
129                 gtk_widget_modify_bg(button, GTK_STATE_NORMAL, color);
130                 gtk_widget_modify_bg(button, GTK_STATE_PRELIGHT, color);
131                 gtk_widget_modify_bg(button, GTK_STATE_ACTIVE, color);
132         } else if (type == KBD_COLOR_TYPE_KEY_POP_FG){
133                 label = gtk_bin_get_child(GTK_BIN(button));
134                 if (label) {
135                         gtk_widget_modify_fg(label, GTK_STATE_NORMAL, color);
136                         gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, color);
137                         gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, color);
138                 }
139         }
140 }
141
142
143 static void
144 candidate_item_clicked_cb(GtkButton *button, CandidateItem *item)
145 {
146         switch (item->type) {
147
148         case KEY_ACTION_STRING:
149                 fvkbd_key_send_utf8_string(item->u.string);
150                 break;
151
152         case KEY_ACTION_SYM:
153                 fvkbd_key_send_xkeysym(item->u.sym);
154                 break;
155
156         default:
157                 break;
158         }
159
160         if (pop_window_hide_timeout_id != 0) {
161                 g_source_remove(pop_window_hide_timeout_id);
162                 pop_window_hide_timeout_id = 0;
163                 hide_pop_window();
164         }
165
166         if (fvkbd_in_temp_mode())
167                 fvkbd_keyboard_resume_default_mode();
168 }
169
170
171 static void
172 init_candidate_items(void)
173 {
174         int i;
175         for (i = 0; i < MAX_CANDIDATE_ITEMS; i++) {
176                 candidate_items[i].widget = gtk_button_new();
177                 gtk_button_set_relief(GTK_BUTTON(candidate_items[i].widget), GTK_RELIEF_NONE);
178                 g_signal_connect(G_OBJECT(candidate_items[i].widget), "clicked",
179                         G_CALLBACK(candidate_item_clicked_cb), &candidate_items[i]);
180         }
181 }
182
183
184 static void
185 set_candidate_items_size(int width, int height)
186 {
187         int i;
188         for (i = 0; i < MAX_CANDIDATE_ITEMS; i++) {
189                 gtk_widget_set_size_request(candidate_items[i].widget,
190                                                 width, height);
191         }
192 }
193
194
195 static void
196 update_candidate_item_none(void)
197 {
198         int i;
199
200         for (i = 0; i < MAX_CANDIDATE_ITEMS; i++)
201                 gtk_widget_hide(candidate_items[i].widget);
202 }
203
204
205 static void
206 update_candidate_item_string(gchar *disp, gchar *str, PangoFontDescription *desc,
207                                 gboolean reverse)
208 {
209         int i;
210
211         if (!reverse) {
212                 set_candidate_item_dummy(0);
213                 set_candidate_item_string(1, disp, str, desc);
214         } else {
215                 set_candidate_item_string(0, disp, str, desc);
216                 set_candidate_item_dummy(1);
217         }
218
219         gtk_widget_show(candidate_items[0].widget);
220         gtk_widget_show(candidate_items[1].widget);
221
222         for (i = 2; i < MAX_CANDIDATE_ITEMS; i++)
223                 gtk_widget_hide(candidate_items[i].widget);
224
225 }
226
227
228 static void
229 update_candidate_item_string_group(gchar **strs, PangoFontDescription *desc,
230                                         gboolean reverse)
231 {
232         int i;
233         int n = 0;
234         gchar **tmp;
235
236         if (strs == NULL)
237                 return;
238
239         for (i = 0, tmp = strs; (i < (MAX_CANDIDATE_ITEMS - 1)) && *tmp; i++, tmp++)
240                 n++;
241
242         if (!reverse) {
243                 set_candidate_item_dummy(0);
244                 gtk_widget_show(candidate_items[0].widget);
245
246                 for (i = 1; i <= n; i++, strs++) {
247                         set_candidate_item_string(i, *strs, *strs, desc);
248                         gtk_widget_show(candidate_items[i].widget);
249                 }
250         } else {
251                 for (i = 0; i < n; i++, strs++) {
252                         set_candidate_item_string(n - 1 - i, *strs, *strs, desc);
253                         gtk_widget_show(candidate_items[n - 1 - i].widget);
254                 }
255                 set_candidate_item_dummy(n);
256                 gtk_widget_show(candidate_items[n].widget);     
257
258         }
259
260         for (i = n + 1; i < MAX_CANDIDATE_ITEMS; i++)
261                 gtk_widget_hide(candidate_items[i].widget);
262 }
263
264
265 static void
266 update_candidate_item_sym(gchar *disp, KeySym sym, PangoFontDescription *desc,
267                                 gboolean reverse)
268 {
269         int i;
270         GtkWidget *button;
271         GtkWidget *key_label;
272
273         if (!reverse) {
274                 set_candidate_item_dummy(0);
275                 i = 1;
276         } else {
277                 set_candidate_item_dummy(1);
278                 i = 0;
279         }
280
281         gtk_widget_show(candidate_items[0].widget);
282         gtk_widget_show(candidate_items[1].widget);
283
284         candidate_items[i].type = KEY_ACTION_SYM;
285         candidate_items[i].u.sym = sym;
286         button = candidate_items[i].widget;
287         gtk_button_set_label(GTK_BUTTON(button), disp);
288         key_label = gtk_bin_get_child(GTK_BIN(button));
289         gtk_widget_modify_font(key_label, desc);
290         gtk_widget_show(button);
291
292         for (i = 2; i < MAX_CANDIDATE_ITEMS; i++)
293                 gtk_widget_hide(candidate_items[i].widget);
294 }
295
296
297 static GtkWidget *
298 get_pop_window(void)
299 {
300         if (pop_window == NULL) {
301                 GtkWidget *hbox, *frame;
302                 int i;
303
304                 pop_window = gtk_window_new(GTK_WINDOW_POPUP);
305                 gtk_window_set_accept_focus(GTK_WINDOW(pop_window), FALSE);
306                 gtk_window_set_resizable(GTK_WINDOW(pop_window), FALSE);
307
308                 frame = gtk_frame_new(NULL);
309                 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_OUT);
310
311                 hbox = gtk_hbox_new(FALSE, 0);
312
313                 gtk_container_add (GTK_CONTAINER (frame), hbox);
314
315                 init_candidate_items();
316                 for (i = 0; i < MAX_CANDIDATE_ITEMS; i++) {
317                         gtk_box_pack_start(GTK_BOX(hbox), candidate_items[i].widget,
318                                                 FALSE, FALSE, 0);
319                 }
320
321                 gtk_widget_show_all(frame);
322
323                 gtk_container_add (GTK_CONTAINER (pop_window), frame);
324         }
325
326         return pop_window;
327 }
328
329
330 static void
331 settle_pop_window_color(FvkbdKey *key, gint number)
332 {
333         GtkWidget *w = get_pop_window();
334         GdkColor bgcolor, fgcolor;
335         int i;
336
337         get_gdkcolor(FVKBD_UNIT(key), KBD_COLOR_TYPE_KEY_POP_BG, &bgcolor);
338         get_gdkcolor(FVKBD_UNIT(key), KBD_COLOR_TYPE_KEY_POP_FG, &fgcolor);
339
340         gtk_widget_modify_bg(w, GTK_STATE_NORMAL, &bgcolor);
341
342         for (i = 0; i < number; i++) {
343                 set_candidate_item_color(i, KBD_COLOR_TYPE_KEY_POP_BG, &bgcolor);
344                 set_candidate_item_color(i, KBD_COLOR_TYPE_KEY_POP_FG, &fgcolor);
345         }
346 }
347
348
349 static void
350 settle_pop_window(FvkbdKey *key, gint number, gboolean reverse)
351 {
352         GtkWidget *w = get_pop_window();
353         GdkWindow *keywindow;
354
355         int pop_w = 0, pop_h = 0;
356         int x, y;
357         int ux, uy, uw, uh;
358         int ww, wh;
359         gfloat x_ratio, y_ratio;
360         GtkWidget *key_widget;
361
362         key_widget = fvkbd_unit_get_widget(FVKBD_UNIT(key));
363         keywindow = gtk_widget_get_window(key_widget);
364
365         gdk_window_get_origin(keywindow, &x, &y);
366         gtk_window_get_size(GTK_WINDOW(w), &pop_w, &pop_h);
367
368         if ((fvkbd_unit_get_position(FVKBD_UNIT(key), &ux, &uy) == 0) &&
369                 (fvkbd_unit_get_size(FVKBD_UNIT(key), &uw, &uh) == 0) &&
370                 (fvkbd_unit_get_ratio(FVKBD_UNIT(key), &x_ratio, &y_ratio) == 0)) {
371                 if (!reverse) {
372                         x += (ux * x_ratio);
373                         y += (uy * y_ratio);
374                 } else {
375                         gtk_window_get_size(GTK_WINDOW(w), &ww, &wh);
376                         //x += ((ux - (uw * number)) * x_ratio);
377                         x += ((ux + uw) * x_ratio - ww);
378                         y += (uy * y_ratio);
379                 }
380         }
381
382         gtk_window_move(GTK_WINDOW(w), x, y);
383 }
384
385
386 static void
387 show_pop_window(FvkbdKey *key, gboolean longpress)
388 {
389         FvkbdKeyAction *action;
390         GtkWidget *w = get_pop_window();
391         int item_number = 1;
392         int i;
393         int ux, uy, uw, uh, pw, ph;
394         gfloat x_ratio, y_ratio;
395         gboolean reverse = FALSE;
396
397         fvkbd_unit_get_size(fvkbd_unit_get_parent(FVKBD_UNIT(key)), &pw, &ph);
398         if ((fvkbd_unit_get_position(FVKBD_UNIT(key), &ux, &uy) == 0)) {
399                 if (ux <= (pw / 2))
400                         reverse = FALSE;
401                 else
402                         reverse = TRUE;
403         }
404
405         fvkbd_unit_get_ratio(FVKBD_UNIT(key), &x_ratio, &y_ratio);
406         fvkbd_unit_get_size(FVKBD_UNIT(key), &uw, &uh);
407
408         action = fvkbd_key_get_current_action(key);
409         KeyUI *ui_data = fvkbd_unit_get_ui_data(FVKBD_UNIT(key));
410         PangoFontDescription *desc;
411
412         if (!ui_data->ofont_desc) {
413                 desc = get_key_pango_font_description(FVKBD_UNIT(key), KBD_FONT_TYPE_POP);
414         } else
415                 desc = ui_data->ofont_desc;
416
417         switch (action->type) {
418
419         case KEY_ACTION_STRING:
420                 update_candidate_item_string(action->disp, action->u.string, desc, reverse);
421                 item_number = 2;
422                 break;
423
424         case KEY_ACTION_STRING_GROUP:
425                 if (!longpress) {
426                         update_candidate_item_string(action->disp, action->u.string, desc, reverse);
427                         item_number = 2;
428                 } else {
429                         update_candidate_item_string_group(action->u.string_group, desc, reverse);
430                         for (i = 0; (i < MAX_CANDIDATE_ITEMS) && *(action->u.string_group + i); i++)
431                                 item_number++;
432                 }
433                 break;
434
435         case KEY_ACTION_SYM:
436                 update_candidate_item_sym(action->disp, action->u.sym, desc, reverse);
437                 item_number = 2;
438                 break;
439
440         default:
441                 update_candidate_item_none();
442                 hide_pop_window();
443                 return;
444         }
445
446         set_candidate_items_size(uw * x_ratio, uh * y_ratio);
447         settle_pop_window_color(key, item_number);
448         // well, hiding pop window here, just wish the window size will be retrived
449         // correctly by gtk_window_get_size later in settle_pop_window.
450         hide_pop_window();
451         settle_pop_window(key, item_number, reverse);
452         if (!GTK_WIDGET_VISIBLE(w))
453                 gtk_widget_show(w);
454 }
455
456
457 static void
458 hide_pop_window(void)
459 {
460         GtkWidget *w = get_pop_window();
461         if (GTK_WIDGET_VISIBLE(w))
462                 gtk_widget_hide(w);
463 }
464
465
466 static gboolean
467 hide_pop_window_cb(FvkbdKey *key)
468 {
469         hide_pop_window();
470         pop_window_hide_timeout_id = 0;
471         
472         return FALSE;
473 }
474
475
476 static gboolean
477 longpress_detected_cb(FvkbdKey *key)
478 {
479         FvkbdKeyAction *action;
480
481         action = fvkbd_key_get_current_action(key);
482
483         switch (action->type) {
484
485         case KEY_ACTION_STRING:
486         case KEY_ACTION_STRING_GROUP:
487         case KEY_ACTION_SYM:
488                 show_pop_window(key, TRUE);
489                 pop_window_hide_timeout_id = g_timeout_add(pop_window_hide_timeout_val,
490                                                 (GSourceFunc)hide_pop_window_cb, key);
491
492                 break;
493         case KEY_ACTION_FUNC:
494                 switch (action->u.func.type) {
495                 case FVKBD_KEY_FUNC_MODE_SELECT:
496                         key_mode_do_switch_lock = TRUE;
497                         break;
498
499                 default:
500                         break;
501                 }
502
503                 break;
504         default:
505                 break;
506         }
507
508         longpress_timeout_id = 0;
509         
510         return FALSE;
511 }
512
513
514 static void
515 fvkbd_key_gtk_pressed_cb(GtkButton *button, FvkbdKey *key)
516 {
517         if (longpress_timeout_id != 0) {
518                 g_source_remove(longpress_timeout_id);
519                 longpress_timeout_id = 0;
520         }
521
522         if (pop_window_hide_timeout_id != 0) {
523                 g_source_remove(pop_window_hide_timeout_id);
524                 pop_window_hide_timeout_id = 0;
525                 hide_pop_window();
526         }
527
528         longpress_timeout_id = g_timeout_add(longpress_timeout_val,
529                                         (GSourceFunc)longpress_detected_cb, key);
530
531         show_pop_window(key, FALSE);
532 }
533
534
535 static void
536 fvkbd_key_gtk_released_cb(GtkButton *button, FvkbdKey *key)
537 {
538         FvkbdKeyAction *action;
539         gboolean need_reset_default_mode = TRUE;
540
541         /* if the candiate window is shown, then we do nothing and return */
542         if (pop_window_hide_timeout_id != 0) {
543                 return;
544         }
545
546         if (longpress_timeout_id != 0) {
547                 g_source_remove(longpress_timeout_id);
548                 longpress_timeout_id = 0;
549         }
550
551         hide_pop_window();
552
553         if ((action = fvkbd_key_get_current_action(key)) == NULL)
554                 return;
555
556         switch (action->type) {
557         case KEY_ACTION_STRING:
558                 fvkbd_key_send_utf8_string(action->u.string);
559                 break;
560
561         case KEY_ACTION_STRING_GROUP:
562                 fvkbd_key_send_utf8_string(action->u.string_group[0]);
563                 break;
564
565         case KEY_ACTION_SYM:
566                 fvkbd_key_send_xkeysym(action->u.sym);
567                 break;
568
569         case KEY_ACTION_FUNC:
570                 switch (action->u.func.type) {
571                 case FVKBD_KEY_FUNC_MODE_SELECT:
572                         fvkbd_key_func_handlers(key, action, &key_mode_do_switch_lock);
573                         need_reset_default_mode = FALSE;
574                         key_mode_do_switch_lock = FALSE;
575                         break;
576
577                 default:
578                         fvkbd_key_func_handlers(key, action, NULL);
579                 }
580
581                 break;
582
583         case KEY_ACTION_SCRIPT:
584                 break;
585
586         default:
587                 break;
588         }
589
590         if (fvkbd_in_temp_mode() && need_reset_default_mode)
591                 fvkbd_keyboard_resume_default_mode();
592
593 }
594
595
596 static void
597 fvkbd_key_settle_color (FvkbdUnit *unit, GtkWidget *key_widget, gboolean lock)
598 {
599         KeyUI *ui_data = fvkbd_unit_get_ui_data(unit);
600         GdkColor color;
601         GtkWidget *label;
602
603         // set key background color
604         if (!lock) {
605                 if (get_gdkcolor(unit, KBD_COLOR_TYPE_KEY_BG, &color)) {
606                         gtk_widget_modify_bg(key_widget, GTK_STATE_NORMAL, &color);
607                         gtk_widget_modify_bg(key_widget, GTK_STATE_PRELIGHT, &color);
608                 }
609
610                 if (get_gdkcolor(unit, KBD_COLOR_TYPE_KEY_FG, &color)) {
611                         gtk_widget_modify_bg(key_widget, GTK_STATE_ACTIVE, &color);
612                 }
613         } else {
614                 if (get_gdkcolor(unit, KBD_COLOR_TYPE_KEY_FG, &color)) {
615                         gtk_widget_modify_bg(key_widget, GTK_STATE_NORMAL, &color);
616                         gtk_widget_modify_bg(key_widget, GTK_STATE_PRELIGHT, &color);
617                 }
618
619                 if (get_gdkcolor(unit, KBD_COLOR_TYPE_KEY_BG, &color)) {
620                         gtk_widget_modify_bg(key_widget, GTK_STATE_ACTIVE, &color);
621                 }
622         }
623         
624         // set main label color
625         label = ui_data->main_label;
626
627         if (!lock) {
628                 if (get_gdkcolor(unit, KBD_COLOR_TYPE_KEY_FG, &color)) {
629                         gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &color);
630                         gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &color);
631                 }
632
633                 if (get_gdkcolor(unit, KBD_COLOR_TYPE_KEY_BG, &color)) {
634                         gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &color);
635                 }
636         } else {
637                 if (get_gdkcolor(unit, KBD_COLOR_TYPE_KEY_BG, &color)) {
638                         gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &color);
639                         gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &color);
640                 }
641
642                 if (get_gdkcolor(unit, KBD_COLOR_TYPE_KEY_FG, &color)) {
643                         gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &color);
644                 }
645         }
646
647         // set sub label color
648         label = ui_data->sub_label;
649
650         if (get_gdkcolor(unit, KBD_COLOR_TYPE_KEY_EXTRA_FG, &color)) {
651                 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &color);
652                 gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &color);
653         }
654
655         if (get_gdkcolor(unit, KBD_COLOR_TYPE_KEY_BG, &color)) {
656                 gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &color);
657         }
658 }
659
660
661 static PangoFontDescription *
662 get_key_pango_font_description (FvkbdUnit *unit, KbdFontType type)
663 {
664         gfloat x_ratio, y_ratio, ratio;
665         KeyUI *ui_data = fvkbd_unit_get_ui_data(unit);
666         gchar *font_family = fvkbd_key_get_font_family(unit, type);
667         gchar *font_weight = fvkbd_key_get_font_weight(unit, type);
668         gint font_size = fvkbd_key_get_font_size(unit, type);
669
670         PangoFontDescription *desc = NULL;
671
672         fvkbd_unit_get_ratio(unit, &x_ratio, &y_ratio);
673         ratio = (x_ratio < y_ratio) ? x_ratio : y_ratio;
674         font_size = font_size * ratio;
675
676         switch (type) {
677                 case KBD_FONT_TYPE_NORMAL:
678                         if (!ui_data->font_desc)
679                                 ui_data->font_desc = get_pango_font_description_from_info(font_family, font_weight, font_size);
680                         desc = ui_data->font_desc;
681                         break;
682
683                 case KBD_FONT_TYPE_EXTRA:
684                         if (!ui_data->efont_desc)
685                                 ui_data->efont_desc = get_pango_font_description_from_info(font_family, font_weight, font_size);
686                         desc = ui_data->efont_desc;
687                         break;
688
689                 case KBD_FONT_TYPE_POP:
690                         if (!ui_data->ofont_desc)
691                                 ui_data->ofont_desc = get_pango_font_description_from_info(font_family, font_weight, font_size);
692                         desc = ui_data->ofont_desc;
693                         break;
694
695                 default:
696                         break;
697         }
698
699         return desc;
700 }
701
702
703 static void
704 fvkbd_key_settle_font (FvkbdUnit *unit, GtkWidget *key_widget)
705 {
706         KeyUI *ui_data = fvkbd_unit_get_ui_data(unit);
707         PangoFontDescription *desc;
708         GtkWidget *label;
709
710         // set main label font
711         label = ui_data->main_label;
712         if (!ui_data->font_desc) {
713                 desc = get_key_pango_font_description(unit, KBD_FONT_TYPE_NORMAL);
714         } else
715                 desc = ui_data->font_desc;
716
717         gtk_widget_modify_font(label, desc);
718
719         // set sub label font
720         label = ui_data->sub_label;
721         if (!ui_data->efont_desc) {
722                 desc = get_key_pango_font_description(unit, KBD_FONT_TYPE_EXTRA);
723         } else
724                 desc = ui_data->efont_desc;
725
726         gtk_widget_modify_font(label, desc);
727 }
728
729
730 static GtkWidget *
731 _create_key_label (FvkbdUnit *unit, gint id)
732 {
733         FvkbdKey *key;
734         FvkbdKeyAction *action;
735         KeyUI *ui_data = fvkbd_unit_get_ui_data(unit);
736         gint w,h;
737         GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
738         GtkWidget *label = NULL, *sub_label = NULL;
739
740         g_return_val_if_fail(FVKBD_IS_KEY(unit), FALSE);
741         key = FVKBD_KEY(unit);
742
743         fvkbd_unit_get_size(unit, &w, &h);
744
745         if ((action = fvkbd_key_get_current_action(key)) == NULL)
746                 goto done;
747
748         if (action->type == KEY_ACTION_STRING_GROUP) {
749                 gchar *label_str = NULL;
750
751                 #if 0
752                 // show all the optional characters
753                 label_str = g_strjoinv(" ", &(action->u.string_group[1]));
754                 #else
755                 // only show the first optional character
756                 label_str = g_strdup(action->u.string_group[1]);
757                 #endif
758
759                 sub_label = gtk_label_new(label_str);
760                 g_free(label_str);
761                 label = gtk_label_new(fvkbd_key_get_disp(key, id));
762         } else if (action->type == KEY_ACTION_STRING ||
763                 action->type == KEY_ACTION_STRING_GROUP ||
764                 action->type == KEY_ACTION_SYM ||
765                 action->type == KEY_ACTION_FUNC ||
766                 action->type == KEY_ACTION_SCRIPT) {
767
768                 sub_label = gtk_label_new(NULL);
769                 label = gtk_label_new(fvkbd_key_get_disp(key, id));
770         }
771
772         ui_data->sub_label = sub_label;
773         if (sub_label)
774                 gtk_box_pack_start(GTK_BOX(vbox), sub_label, FALSE, FALSE, 0);
775
776         ui_data->main_label = label;
777         gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
778
779 done:
780         gtk_widget_show_all(vbox);
781         return vbox;
782 }
783
784
785 static void
786 _update_key_label (FvkbdUnit *unit, gint id)
787 {
788         GtkWidget *key_widget;
789         GtkWidget *key_label;
790
791         key_widget = fvkbd_unit_get_widget(unit);
792         
793         key_label = gtk_bin_get_child(GTK_BIN(key_widget));
794
795         if (key_label) {
796                 gtk_widget_destroy(key_label);
797                 key_label = NULL;
798         }
799
800         key_label = _create_key_label(unit, id);
801         gtk_container_add(GTK_CONTAINER(key_widget), key_label);
802 }
803
804
805 static void
806 _set_key_label_tmp_mode (FvkbdUnit *unit)
807 {
808         KeyUI *ui_data = fvkbd_unit_get_ui_data(unit);
809         GtkWidget *label;
810         gchar *disp;
811
812         label = ui_data->main_label;
813         disp = g_strconcat("<u>", gtk_label_get_text(GTK_LABEL(label)), "</u>", NULL);
814         gtk_label_set_text(GTK_LABEL(label), disp);
815         gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
816         g_free(disp);
817 }
818
819
820 gboolean
821 fvkbd_key_build_ui (FvkbdUnit *unit, gpointer *widget)
822 {
823         FvkbdKey *key;
824         KeyUI *ui_data;
825         gint w,h;
826         gboolean ret = TRUE;
827
828         GtkWidget *key_widget;
829         GtkWidget *key_label;
830
831         g_return_val_if_fail(FVKBD_IS_KEY(unit), FALSE);
832         key = FVKBD_KEY(unit);
833
834         key_widget = gtk_button_new();
835
836         ui_data = g_new0(KeyUI, 1);
837         fvkbd_unit_set_ui_data(unit, ui_data);
838         ui_data->main_label = NULL;
839         ui_data->sub_label = NULL;
840         ui_data->font_desc = NULL;
841         ui_data->efont_desc = NULL;
842         ui_data->ofont_desc = NULL;
843
844         fvkbd_unit_get_size(unit, &w, &h);
845         gtk_widget_set_size_request(key_widget, w, h);
846
847         key_label = _create_key_label(unit, 0);
848         gtk_container_add(GTK_CONTAINER(key_widget), key_label);
849
850         // set color
851         fvkbd_key_settle_color(unit, key_widget, FALSE);
852
853         // set font
854         fvkbd_key_settle_font(unit, key_widget);
855
856         g_signal_connect(G_OBJECT(key_widget), "pressed",
857                         G_CALLBACK(fvkbd_key_gtk_pressed_cb), key);
858         g_signal_connect(G_OBJECT(key_widget), "released",
859                         G_CALLBACK(fvkbd_key_gtk_released_cb), key);
860
861         if (widget != NULL)
862                 *widget = key_widget;
863         fvkbd_unit_set_widget(unit, key_widget);
864         return ret;
865 }
866
867
868 gboolean
869 fvkbd_key_resize_ui (FvkbdUnit *unit, gfloat x_ratio, gfloat y_ratio)
870 {
871         FvkbdKey *key;
872         KeyUI *ui_data;
873         GtkWidget *key_widget;
874         //GtkWidget *key_label;
875         int tmp_w, tmp_h;
876         int new_w, new_h;
877
878         g_return_val_if_fail(FVKBD_IS_KEY(unit), FALSE);
879         key = FVKBD_KEY(unit);
880
881         ui_data = fvkbd_unit_get_ui_data(unit);
882         key_widget = fvkbd_unit_get_widget(unit);
883
884         if (fvkbd_unit_get_size(unit, &tmp_w, &tmp_h) == 0) {
885                 new_w = tmp_w * x_ratio;
886                 new_h = tmp_h * y_ratio;
887                 gtk_widget_set_size_request(key_widget, new_w, new_h);
888
889         } else
890                 return FALSE;
891
892         if (ui_data->font_desc) {
893                 pango_font_description_free(ui_data->font_desc);
894                 ui_data->font_desc = NULL;
895         }
896         if (ui_data->efont_desc) {
897                 pango_font_description_free(ui_data->efont_desc);
898                 ui_data->efont_desc = NULL;
899         }
900         if (ui_data->ofont_desc) {
901                 pango_font_description_free(ui_data->ofont_desc);
902                 ui_data->ofont_desc = NULL;
903         }
904
905         fvkbd_unit_set_ratio(unit, x_ratio, y_ratio);
906         fvkbd_key_settle_font(unit, key_widget);
907
908         return TRUE;
909 }
910
911
912 gboolean
913 fvkbd_key_destroy_ui (FvkbdUnit *unit)
914 {
915         FvkbdKey *key;
916         KeyUI *ui_data;
917         gboolean ret = TRUE;
918
919         GtkWidget *key_widget;
920
921         g_return_val_if_fail(FVKBD_IS_KEY(unit), FALSE);
922         key = FVKBD_KEY(unit);
923
924         key_widget = fvkbd_unit_get_widget(unit);
925
926         gtk_widget_hide_all(key_widget);
927         gtk_widget_destroy(key_widget);
928
929         fvkbd_unit_set_widget(unit, NULL);
930
931         ui_data = fvkbd_unit_get_ui_data(unit);
932         if (ui_data->font_desc)
933                 pango_font_description_free(ui_data->font_desc);
934         if (ui_data->efont_desc)
935                 pango_font_description_free(ui_data->efont_desc);
936         if (ui_data->ofont_desc)
937                 pango_font_description_free(ui_data->ofont_desc);
938         g_free(ui_data);
939         fvkbd_unit_set_ui_data(unit, NULL);
940
941         return ret;
942 }
943
944
945 gint
946 fvkbd_key_set_mode (FvkbdUnit *unit, gint id)
947 {
948         FvkbdKey *key;
949         FvkbdKeyAction *action;
950         GtkWidget *key_widget;
951         gboolean show_lock_mode = FALSE;
952
953         g_return_val_if_fail(FVKBD_IS_KEY(unit), -1);
954         key = FVKBD_KEY(unit);
955
956         key_widget = fvkbd_unit_get_widget(unit);
957
958         // Yes, we get the default action here to handle disp diffirently for tmply mode change
959         action = fvkbd_key_get_action(key, 0);
960
961         if ((action->type == KEY_ACTION_FUNC) &&
962                 (action->u.func.type == FVKBD_KEY_FUNC_MODE_SELECT)) {
963                 if ((fvkbd_in_temp_mode()) && ((*((int *)action->u.func.data)) == id)) {
964                         _update_key_label(unit, 0);
965                         _set_key_label_tmp_mode(unit);
966                 } else {
967                         _update_key_label(unit, id);
968                         if ((*((int *)action->u.func.data)) == id)
969                                 show_lock_mode = TRUE;
970                 }
971         } else {
972                 _update_key_label(unit, id);
973         }
974
975         // set color
976         fvkbd_key_settle_color(unit, key_widget, show_lock_mode);
977
978         // set font
979         fvkbd_key_settle_font(unit, key_widget);
980
981         return 0;
982 }
983