remove priv font desc info in key 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 "fvkbd.h"
26 #include "misc-utility.h"
27 #include "gtk-misc-utility.h"
28 #include "gtk-vkb-button.h"
29 #include "pixmap-utility.h"
30 #include "fvkbd-pop-win.h"
31 #include "fvkbd-keyboard.h"
32 #include "fvkbd-keyboard-ui-gtk.h"
33 #include "fvkbd-key-ui-gtk.h"
34
35
36 #define DEFAULT_CHAMFER_SIZE 2
37
38 static gboolean longpress_detected = FALSE;
39 static guint longpress_timeout_id = 0;
40 static guint longpress_timeout_val = 800;
41 static guint pop_window_hide_timeout_id = 0;
42 static guint pop_window_hide_timeout_val = 5000;
43
44 static guint32 latest_mode_key_id = 0;
45
46 #define FVKBD_KEY_GTK_UI_GET_PRIVATE(object)\
47         (G_TYPE_INSTANCE_GET_PRIVATE((object), FVKBD_TYPE_KEY_GTK_UI, FvkbdKeyGtkUIPrivate))
48
49 typedef struct _FvkbdKeyGtkUIPrivate FvkbdKeyGtkUIPrivate;
50 struct _FvkbdKeyGtkUIPrivate {
51         GtkWidget *main_label;
52         GtkWidget *sub_label;
53         GtkWidget *img;
54         GtkWidget *img_dn;
55
56         PangoFontDescription *action_font_descs[KBD_FONT_TYPE_NUMBER];
57 };
58
59
60 G_DEFINE_TYPE (FvkbdKeyGtkUI, fvkbd_key_gtk_ui, FVKBD_TYPE_GTK_UI);
61
62
63 static void settle_pop_window(FvkbdKeyGtkUI *key_ui, gint number, gboolean reverse);
64 static void show_pop_window(FvkbdKeyGtkUI *key_ui, gboolean longpress);
65 static void hide_pop_window(void);
66 static gboolean hide_pop_window_cb(FvkbdKeyGtkUI *key_ui);
67 static gboolean longpress_detected_cb(FvkbdKeyGtkUI *key_ui);
68 static void _toggle_key_img (FvkbdKeyGtkUI *key_ui, gboolean down);
69 static PangoFontDescription * get_key_pango_font_description (FvkbdKeyGtkUI *unit, KbdFontType type);
70 static FvkbdKeyAction * _key_gtk_ui_get_current_action(FvkbdKeyGtkUI *key_ui);
71 static void fvkbd_key_shape_mask_none (FvkbdKeyGtkUI *key, GtkWidget *key_widget);
72 static void fvkbd_key_shape_mask_chamfer (FvkbdKeyGtkUI *key, GtkWidget *key_widget);
73 static void fvkbd_key_shape_mask_bitmap (FvkbdKeyGtkUI *key, GtkWidget *key_widget, KbdShapeInfo *info);
74 static void fvkbd_key_settle_bg_image (FvkbdKeyGtkUI *key, GtkWidget *key_widget);
75
76
77 static gboolean fvkbd_key_gtk_ui_build (FvkbdGtkUI *ui, GtkWidget **widget);
78 static gboolean fvkbd_key_gtk_ui_allocate (FvkbdGtkUI *ui, gint x, gint y,
79                                                 gfloat x_ratio, gfloat y_ratio);
80 static gboolean fvkbd_key_gtk_ui_destroy (FvkbdGtkUI *ui);
81 static gboolean fvkbd_key_gtk_ui_set_mode (FvkbdGtkUI *ui, gint id);
82
83 static void
84 fvkbd_key_gtk_ui_class_init (FvkbdKeyGtkUIClass *klass)
85 {
86         FvkbdGtkUIClass *ui_class = FVKBD_GTK_UI_CLASS(klass);
87
88         g_type_class_add_private(klass, sizeof(FvkbdKeyGtkUIPrivate));
89
90         ui_class->build = fvkbd_key_gtk_ui_build;
91         ui_class->allocate = fvkbd_key_gtk_ui_allocate;
92         ui_class->destroy = fvkbd_key_gtk_ui_destroy;
93         ui_class->set_mode = fvkbd_key_gtk_ui_set_mode;
94 }
95
96
97 static void
98 fvkbd_key_gtk_ui_init (FvkbdKeyGtkUI *self)
99 {
100         FvkbdKeyGtkUIPrivate *priv = FVKBD_KEY_GTK_UI_GET_PRIVATE(self);
101         int i;
102
103         priv->main_label = NULL;
104         priv->sub_label = NULL;
105         priv->img = NULL;
106         priv->img_dn = NULL;
107
108         for (i = 0; i < KBD_FONT_TYPE_NUMBER; i++) {
109                 priv->action_font_descs[i] = NULL;
110         }
111 }
112
113
114 FvkbdGtkUI *
115 fvkbd_key_gtk_ui_new (FvkbdUnit *unit)
116 {
117         FvkbdGtkUI *ui;
118
119         g_return_val_if_fail(FVKBD_IS_KEY(unit), NULL);
120
121         ui = g_object_new(FVKBD_TYPE_KEY_GTK_UI, NULL);
122         fvkbd_gtk_ui_set_unit(ui, unit);
123         fvkbd_unit_set_ui_data(ui->unit, ui);
124
125         return ui;
126 }
127
128
129 static void
130 settle_pop_window(FvkbdKeyGtkUI *key_ui, gint number, gboolean reverse)
131 {
132         FvkbdUnit *unit = FVKBD_GTK_UI(key_ui)->unit;
133         GtkWidget *w = get_pop_window();
134         GdkWindow *keywindow;
135
136         int pop_w = 0, pop_h = 0;
137         int x, y;
138         int ux, uy, uw, uh;
139         int ww, wh;
140         gfloat x_ratio, y_ratio;
141         GtkWidget *key_widget;
142
143         key_widget = fvkbd_gtk_ui_get_widget(FVKBD_GTK_UI(key_ui));
144         keywindow = gtk_widget_get_window(key_widget);
145
146         gdk_window_get_origin(keywindow, &x, &y);
147         gtk_window_get_size(GTK_WINDOW(w), &pop_w, &pop_h);
148
149         /* If key_widget don't have it's own gdk window, then gdk_window_get_origin will get the position
150             of it's parent window, say panel window, then we need to do some offset to gain the right position */
151         if (GTK_WIDGET_NO_WINDOW(key_widget)) {
152                 fvkbd_unit_get_position(unit, &ux, &uy);
153         } else {
154                 ux = 0;
155                 uy = 0;
156         }
157
158         if ((fvkbd_unit_get_size(unit, &uw, &uh) == 0) &&
159                 (fvkbd_gtk_ui_get_ratio(FVKBD_GTK_UI(key_ui), &x_ratio, &y_ratio) == 0)) {
160
161                 gtk_window_get_size(GTK_WINDOW(w), &ww, &wh);
162
163                 if (!reverse) {
164                         x += (ux * x_ratio);
165                 } else {
166                         x += ((ux + uw) * x_ratio - ww);
167                 }
168
169                 y += ((uy * y_ratio) - (wh - (uh * y_ratio)) / 2);
170         }
171
172         gtk_window_move(GTK_WINDOW(w), x, y);
173 }
174
175
176 static void
177 show_pop_window(FvkbdKeyGtkUI *key_ui, gboolean longpress)
178 {
179         FvkbdUnit *unit = FVKBD_GTK_UI(key_ui)->unit;
180         FvkbdKey *key = FVKBD_KEY(unit);
181         FvkbdKeyAction *action;
182         GtkWidget *w = get_pop_window();
183         GdkColor bgcolor, fgcolor;
184         int item_number = 1;
185         int i;
186         int ux, uy, uw, uh, pw, ph;
187         gfloat x_ratio, y_ratio;
188         gboolean reverse = FALSE;
189
190         if (fvkbd_key_pop_notify_disabled(key))
191                 return;
192
193         fvkbd_unit_get_size(fvkbd_unit_get_parent(unit), &pw, &ph);
194         if ((fvkbd_unit_get_position(unit, &ux, &uy) == 0)) {
195                 if (ux <= (pw / 2))
196                         reverse = FALSE;
197                 else
198                         reverse = TRUE;
199         }
200
201         fvkbd_gtk_ui_get_ratio(FVKBD_GTK_UI(key_ui), &x_ratio, &y_ratio);
202         fvkbd_unit_get_size(unit, &uw, &uh);
203
204         action = _key_gtk_ui_get_current_action(key_ui);
205         PangoFontDescription *desc;
206
207         desc = get_key_pango_font_description(key_ui, KBD_FONT_TYPE_POP);
208
209         switch (action->type) {
210
211         case KEY_ACTION_STRING:
212                 update_pop_win_item_string(action->disp, action->u.string, desc, reverse);
213                 item_number = 2;
214                 break;
215
216         case KEY_ACTION_STRING_GROUP:
217                 if (!longpress) {
218                         update_pop_win_item_string(action->disp, action->u.string, desc, reverse);
219                         item_number = 2;
220                 } else {
221                         update_pop_win_item_string_group(action->u.string_group, desc, reverse);
222                         for (i = 0; (i < MAX_POP_WIN_ITEMS) && *(action->u.string_group + i); i++)
223                                 item_number++;
224                 }
225                 break;
226
227         case KEY_ACTION_SYM:
228                 update_pop_win_item_sym(action->disp, action->u.sym, desc, reverse);
229                 item_number = 2;
230                 break;
231
232         default:
233                 hide_pop_window();
234                 return;
235         }
236
237         pop_win_set_height_request(uh * y_ratio);
238         pop_win_items_set_width_request(uw * x_ratio);
239
240         get_gdkcolor(unit, KBD_COLOR_TYPE_KEY_POP_BG, &bgcolor);
241         get_gdkcolor(unit, KBD_COLOR_TYPE_KEY_POP_FG, &fgcolor);
242
243         settle_pop_window_color(item_number, &bgcolor, &fgcolor);
244         // well, hiding pop window here, just wish the window size will be retrived
245         // correctly by gtk_window_get_size later in settle_pop_window.
246         hide_pop_window();
247         settle_pop_window(key_ui, item_number, reverse);
248         if (!GTK_WIDGET_VISIBLE(w))
249                 gtk_widget_show(w);
250 }
251
252
253 static void
254 hide_pop_window(void)
255 {
256         GtkWidget *w = get_pop_window();
257         if (GTK_WIDGET_VISIBLE(w))
258                 gtk_widget_hide(w);
259 }
260
261
262 static gboolean
263 hide_pop_window_cb(FvkbdKeyGtkUI *ui)
264 {
265         hide_pop_window();
266         pop_window_hide_timeout_id = 0;
267         
268         return FALSE;
269 }
270
271
272 static FvkbdKeyAction *
273 _key_gtk_ui_get_current_action(FvkbdKeyGtkUI *key_ui)
274 {
275         FvkbdKey *key = FVKBD_KEY(FVKBD_GTK_UI(key_ui)->unit);
276         FvkbdKeyboard *keyboard = fvkbd_keyboard_gtk_ui_get_keyboard();
277         gint mode;
278
279         mode = fvkbd_keyboard_get_current_mode(keyboard);
280         return fvkbd_key_get_action(key, mode);
281 }
282
283
284 static void
285 _key_gtk_ui_func_handlers(FvkbdKeyGtkUI *key_ui, FvkbdKeyAction *action, void *data)
286 {
287         gint err;
288         FvkbdKeyboardGtkUI *keyboard_ui = fvkbd_keyboard_gtk_ui_get_ui();
289         FvkbdKeyboard *keyboard = fvkbd_keyboard_gtk_ui_get_keyboard();
290
291         switch (action->u.func.type) {
292         case KBD_FUNC_MODE_SELECT:
293                 {
294                         int id;
295                         KeyboardModeStatus status = KEYBOARD_MODE_STATUS_NORMAL;
296
297                         id = *((int *)action->u.func.data);
298                         if (data)
299                                 status = *((KeyboardModeStatus *)data);
300
301                         err = keyboard_ui_change_mode(keyboard_ui, id, status);
302
303                         break;
304                 }
305
306         case KBD_FUNC_EXIT:
307         case KBD_FUNC_MENU:
308                 fvkbd_keyboard_do_func(keyboard, &(action->u.func));
309                 break;
310
311         default:
312                 break;
313         }
314 }
315
316
317 static gboolean
318 longpress_detected_cb(FvkbdKeyGtkUI *key_ui)
319 {
320         FvkbdKeyAction *action;
321
322         longpress_detected = TRUE;
323         action = _key_gtk_ui_get_current_action(key_ui);
324
325         switch (action->type) {
326         case KEY_ACTION_STRING_GROUP:
327                 show_pop_window(key_ui, TRUE);
328                 pop_window_hide_timeout_id = g_timeout_add(pop_window_hide_timeout_val,
329                                                 (GSourceFunc)hide_pop_window_cb, key_ui);
330
331                 break;
332
333         default:
334                 break;
335         }
336
337         longpress_timeout_id = 0;
338         
339         return FALSE;
340 }
341
342
343 static void
344 fvkbd_key_gtk_pressed_cb(GtkVkbButton *button, FvkbdKeyGtkUI *key_ui)
345 {
346         hide_pop_window();
347
348         longpress_detected = FALSE;
349
350         if (longpress_timeout_id != 0) {
351                 g_source_remove(longpress_timeout_id);
352                 longpress_timeout_id = 0;
353         }
354
355         if (pop_window_hide_timeout_id != 0) {
356                 g_source_remove(pop_window_hide_timeout_id);
357                 pop_window_hide_timeout_id = 0;
358         }
359
360         longpress_timeout_id = g_timeout_add(longpress_timeout_val,
361                                         (GSourceFunc)longpress_detected_cb, key_ui);
362
363         _toggle_key_img(key_ui, TRUE);
364         show_pop_window(key_ui, FALSE);
365 }
366
367
368 static void
369 fvkbd_key_gtk_released_cb(GtkVkbButton *button, FvkbdKeyGtkUI *key_ui)
370 {
371         FvkbdKeyboardGtkUI *keyboard_ui = fvkbd_keyboard_gtk_ui_get_ui();
372         FvkbdKeyboard *keyboard = fvkbd_keyboard_gtk_ui_get_keyboard();
373         guint32 key_id = FVKBD_UNIT(FVKBD_GTK_UI(key_ui)->unit)->uid;
374
375         FvkbdKeyAction *action;
376         gboolean need_reset_default_mode = TRUE;
377
378         _toggle_key_img(key_ui, FALSE);
379
380         /* if the candiate window is shown, then we do nothing and return */
381         if (pop_window_hide_timeout_id != 0) {
382                 return;
383         }
384
385         if (longpress_timeout_id != 0) {
386                 g_source_remove(longpress_timeout_id);
387                 longpress_timeout_id = 0;
388         }
389
390         hide_pop_window();
391
392         if ((action = _key_gtk_ui_get_current_action(key_ui)) == NULL)
393                 return;
394
395         switch (action->type) {
396         case KEY_ACTION_STRING:
397                 fvkbd_key_send_utf8_string(action->u.string);
398                 break;
399
400         case KEY_ACTION_STRING_GROUP:
401                 fvkbd_key_send_utf8_string(action->u.string_group[0]);
402                 break;
403
404         case KEY_ACTION_SYM:
405                 fvkbd_key_send_xkeysym(action->u.sym);
406                 break;
407
408         case KEY_ACTION_FUNC:
409                 switch (action->u.func.type) {
410                 case KBD_FUNC_MODE_SELECT:
411                         latest_mode_key_id = key_id;
412                         //if currently in tmp mode, another mode switch always lead to default mode
413                         if (fvkbd_keyboard_get_mode_status(keyboard) ==  KEYBOARD_MODE_STATUS_TEMP)
414                                 break;
415
416                         if (longpress_detected) {
417                                 KeyboardModeStatus status = KEYBOARD_MODE_STATUS_LOCK;
418
419                                 _key_gtk_ui_func_handlers(key_ui, action, &status);
420                         } else {
421                                 KeyboardModeStatus status = KEYBOARD_MODE_STATUS_TEMP;
422
423                                 _key_gtk_ui_func_handlers(key_ui, action, &status);
424                                 need_reset_default_mode = FALSE;
425                         }
426
427                         break;
428                 default:
429                         _key_gtk_ui_func_handlers(key_ui, action, NULL);
430                 }
431
432                 break;
433
434         case KEY_ACTION_SCRIPT:
435                 fvkbd_do_script(action->u.string);
436                 break;
437
438         default:
439                 break;
440         }
441
442         if ((fvkbd_keyboard_get_mode_status(keyboard) ==  KEYBOARD_MODE_STATUS_TEMP)
443                 && need_reset_default_mode)
444                 keyboard_ui_resume_previous_mode(keyboard_ui);
445
446 }
447
448
449 static void
450 fvkbd_key_gtk_size_allocate_cb (GtkVkbButton *button,
451                                         GtkAllocation *allocation, FvkbdKeyGtkUI *key_ui)
452 {
453         FvkbdKey *key = FVKBD_KEY(FVKBD_GTK_UI(key_ui)->unit);
454         KbdShapeInfo *info;
455
456         info = fvkbd_unit_get_qdata_recursive(FVKBD_UNIT(key),  quark_key_shape_info, NULL);
457         if (!info)
458                 return;
459
460         switch (info->shape_type) {
461         case KBD_SHAPE_NONE:
462                 fvkbd_key_shape_mask_none(key_ui, GTK_WIDGET(button));
463                 break;
464
465         case KBD_SHAPE_CHAMFER:
466                 fvkbd_key_shape_mask_chamfer(key_ui, GTK_WIDGET(button));
467                 break;
468
469         case KBD_SHAPE_BITMAP_MASK:
470                 fvkbd_key_shape_mask_bitmap(key_ui, GTK_WIDGET(button), info);
471                 break;
472
473         default:
474                 fvkbd_key_shape_mask_none(key_ui, GTK_WIDGET(button));
475         }
476 }
477
478 static void
479 fvkbd_key_settle_color (FvkbdKeyGtkUI *key_ui, GtkWidget *key_widget)
480 {
481         FvkbdKeyboard *keyboard = fvkbd_keyboard_gtk_ui_get_keyboard();
482         KeyboardModeStatus status = fvkbd_keyboard_get_mode_status(keyboard);
483         FvkbdKeyGtkUIPrivate *priv = FVKBD_KEY_GTK_UI_GET_PRIVATE(key_ui);
484         FvkbdUnit *unit = FVKBD_GTK_UI(key_ui)->unit;
485         GdkColor color;
486         GtkWidget *label;
487         gboolean reverse = ((latest_mode_key_id == unit->uid) &&
488                                 (status == KEYBOARD_MODE_STATUS_TEMP));
489
490         // set key background color
491         if (!reverse) {
492                 if (get_gdkcolor(unit, KBD_COLOR_TYPE_KEY_BG, &color)) {
493                         vkb_button_set_bg(key_widget, GTK_STATE_NORMAL, &color);
494                         vkb_button_set_bg(key_widget, GTK_STATE_PRELIGHT, &color);
495                 }
496
497                 if (get_gdkcolor(unit, KBD_COLOR_TYPE_KEY_FG, &color)) {
498                         vkb_button_set_bg(key_widget, GTK_STATE_ACTIVE, &color);
499                 }
500         } else {
501                 if (get_gdkcolor(unit, KBD_COLOR_TYPE_KEY_FG, &color)) {
502                         vkb_button_set_bg(key_widget, GTK_STATE_NORMAL, &color);
503                         vkb_button_set_bg(key_widget, GTK_STATE_PRELIGHT, &color);
504                 }
505
506                 if (get_gdkcolor(unit, KBD_COLOR_TYPE_KEY_BG, &color)) {
507                         vkb_button_set_bg(key_widget, GTK_STATE_ACTIVE, &color);
508                 }
509         }
510
511         // set main label color
512         label = priv->main_label;
513         if (label) {
514                 if (!reverse) {
515                         if (get_gdkcolor(unit, KBD_COLOR_TYPE_KEY_FG, &color)) {
516                                 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &color);
517                                 gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &color);
518                         }
519
520                         if (get_gdkcolor(unit, KBD_COLOR_TYPE_KEY_BG, &color)) {
521                                 gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &color);
522                         }
523                 } else {
524                         if (get_gdkcolor(unit, KBD_COLOR_TYPE_KEY_BG, &color)) {
525                                 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &color);
526                                 gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &color);
527                         }
528
529                         if (get_gdkcolor(unit, KBD_COLOR_TYPE_KEY_FG, &color)) {
530                                 gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &color);
531                         }
532                 }
533         }
534
535         // set sub label color
536         label = priv->sub_label;
537         if (label) {
538                 if (get_gdkcolor(unit, KBD_COLOR_TYPE_KEY_EXTRA_FG, &color)) {
539                         gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &color);
540                         gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &color);
541                 }
542
543                 if (get_gdkcolor(unit, KBD_COLOR_TYPE_KEY_BG, &color)) {
544                         gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &color);
545                 }
546         }
547 }
548
549
550 static PangoFontDescription *
551 get_key_pango_font_description (FvkbdKeyGtkUI *key_ui, KbdFontType type)
552 {
553         FvkbdGtkUI *ui = FVKBD_GTK_UI(key_ui);
554         gfloat x_ratio, y_ratio;
555         FvkbdKeyAction *action = _key_gtk_ui_get_current_action(key_ui);
556         KbdFontInfo *font = NULL;
557
558         PangoFontDescription *desc = NULL;
559
560         if ((type < 0) || (type >= KBD_FONT_TYPE_NUMBER))
561                 return NULL;
562
563         fvkbd_gtk_ui_get_ratio(FVKBD_GTK_UI(key_ui), &x_ratio, &y_ratio);
564
565         if(G_UNLIKELY(action->unique_font == TRUE)) {
566                 if ((font = action->font[type]) != NULL) {
567                         FvkbdKeyGtkUIPrivate *priv = FVKBD_KEY_GTK_UI_GET_PRIVATE(key_ui);
568
569                         desc = get_scaled_pango_font_description(font, x_ratio, y_ratio);
570
571                         if (priv->action_font_descs[type])
572                                 pango_font_description_free(priv->action_font_descs[type]);
573
574                         priv->action_font_descs[type] = desc;
575                 }
576         }
577
578         if (!desc) {
579                 PangoFontDescription **descs;
580
581                 descs = fvkbd_gtk_ui_get_qdata_recursive(ui, quark_ui_font_descs, NULL);
582                 if (descs)
583                         desc = *(descs + type);
584         }
585
586         return desc;
587 }
588
589
590 static void
591 fvkbd_key_settle_font (FvkbdKeyGtkUI *key_ui, GtkWidget *key_widget)
592 {
593         FvkbdKeyGtkUIPrivate *priv = FVKBD_KEY_GTK_UI_GET_PRIVATE(key_ui);
594         PangoFontDescription *desc;
595         GtkWidget *label;
596
597         // set main label font
598         label = priv->main_label;
599         if (label) {
600                 desc = get_key_pango_font_description(key_ui, KBD_FONT_TYPE_NORMAL);
601                 gtk_widget_modify_font(label, desc);
602         }
603
604         // set sub label font
605         label = priv->sub_label;
606         if (label) {
607                 desc = get_key_pango_font_description(key_ui, KBD_FONT_TYPE_EXTRA);
608                 gtk_widget_modify_font(label, desc);
609         }
610 }
611
612
613 static void
614 fvkbd_key_shape_mask_none (FvkbdKeyGtkUI *key_ui, GtkWidget *key_widget)
615 {
616         gtk_widget_shape_combine_mask(key_widget, NULL, 0, 0);
617 }
618
619
620 static void
621 fvkbd_key_shape_mask_chamfer (FvkbdKeyGtkUI *key_ui, GtkWidget *key_widget)
622 {
623         GdkBitmap *bitmap = NULL;
624         bitmap = get_chamfered_rectangle_bitmap(key_widget->allocation.width,
625                 key_widget->allocation.height, DEFAULT_CHAMFER_SIZE);
626         if (bitmap) {
627                 gtk_widget_shape_combine_mask(key_widget, bitmap, 0, 0);
628                 g_object_unref(bitmap);
629         }
630 }
631
632
633 static void
634 fvkbd_key_shape_mask_bitmap (FvkbdKeyGtkUI *key_ui, GtkWidget *key_widget,
635                                         KbdShapeInfo *info)
636 {
637         FvkbdUnit *unit = FVKBD_GTK_UI(key_ui)->unit;
638         gfloat x_ratio, y_ratio;
639         GdkBitmap *bitmap = NULL;
640         gboolean local = FALSE;
641
642         g_return_if_fail(info->shape_type == KBD_SHAPE_BITMAP_MASK);
643
644         if (!FVKBD_UNIT_HAS_KEY_SHAPE_INFO(unit))
645                 bitmap = fvkbd_gtk_ui_get_qdata_recursive(FVKBD_GTK_UI(key_ui),
646                                                         quark_key_shape_bitmap, NULL);
647
648         /* if this is a local shape info, or parent do not set bitmap data for some reason, then we create it locally */
649         if (!bitmap) {
650                 fvkbd_gtk_ui_get_ratio(FVKBD_GTK_UI(key_ui), &x_ratio, &y_ratio);
651                 bitmap = load_and_scale_bitmap(info->u.mask, x_ratio, y_ratio);
652                 local = TRUE;
653         }
654
655         if (bitmap) {
656                 gtk_widget_shape_combine_mask(key_widget, bitmap, 0, 0);
657                 if (local)
658                         g_object_unref(bitmap);
659         }
660 }
661
662
663 static void
664 fvkbd_key_settle_bg_image (FvkbdKeyGtkUI *key_ui, GtkWidget *key_widget)
665 {
666         FvkbdGtkUI *ui = FVKBD_GTK_UI(key_ui);
667         gfloat x_ratio, y_ratio;
668         FvkbdUnit *unit = ui->unit;
669         GdkPixmap *pixmap = NULL;
670
671         if (FVKBD_UNIT_HAS_KEY_BG_FILE(unit)) {
672                 gchar *bg_file = fvkbd_unit_get_qdata(unit, quark_key_bg_file);
673
674                 fvkbd_gtk_ui_get_ratio(ui, &x_ratio, &y_ratio);
675                 load_and_scale_pixmap_and_mask(bg_file, x_ratio, y_ratio, &pixmap, NULL);
676                 if (pixmap) {
677                         fvkbd_gtk_ui_set_qdata(ui, quark_key_bg_pixmap, pixmap,
678                                         (GDestroyNotify)g_object_unref);
679                         FVKBD_UI_SET_FLAG(ui, FLAG_KEY_BG_PIXMAP);
680                 }       
681         }
682
683         /* if no local bg img or fail to load it, then try parent */
684         if (!pixmap) {
685                 pixmap = fvkbd_gtk_ui_get_qdata_recursive(ui, quark_key_bg_pixmap, NULL);
686         }
687
688         vkb_button_set_bg_pixmap(key_widget, pixmap);
689 }
690
691 static GtkWidget *
692 _create_key_label (FvkbdKeyGtkUI *key_ui, gint id, KeyboardModeStatus status)
693 {
694         FvkbdKeyGtkUIPrivate *priv = FVKBD_KEY_GTK_UI_GET_PRIVATE(key_ui);
695         FvkbdUnit *unit = FVKBD_GTK_UI(key_ui)->unit;
696         FvkbdKey *key;
697         FvkbdKeyAction *action;
698
699         gint w,h;
700         gfloat x_ratio, y_ratio, ratio;
701         GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
702         GtkWidget *label = NULL, *sub_label = NULL;
703         GtkWidget *img = NULL, *img_dn = NULL;
704
705         g_return_val_if_fail(FVKBD_IS_KEY(unit), FALSE);
706         key = FVKBD_KEY(unit);
707
708
709         fvkbd_unit_get_size(unit, &w, &h);
710
711         if ((action = _key_gtk_ui_get_current_action(key_ui)) == NULL)
712                 goto done;
713
714         fvkbd_gtk_ui_get_ratio(FVKBD_GTK_UI(key_ui), &x_ratio, &y_ratio);
715         ratio = (x_ratio < y_ratio) ? x_ratio : y_ratio;
716
717         if (action->img != NULL) {
718                 img = load_and_scale_img(action->img, ratio, ratio);
719                 if (action->img_dn != NULL) {
720                         img_dn = load_and_scale_img(action->img_dn, ratio, ratio);
721                 }
722
723                 priv->img = img;
724                 priv->img_dn = img_dn;
725                 
726                 if (priv->img) {
727                         g_object_ref_sink(priv->img);
728                         gtk_container_add(GTK_CONTAINER(vbox), img);
729                 }
730                 
731                 if (priv->img_dn) {
732                         g_object_ref_sink(priv->img_dn);
733                 }
734
735                 goto done;
736         }
737
738         switch (action->type) {
739         case KEY_ACTION_STRING_GROUP:
740                 {
741                         gchar *label_str = NULL;
742
743                         #if 0
744                         // show all the optional characters
745                         label_str = g_strjoinv(" ", &(action->u.string_group[1]));
746                         #else
747                         // only show the first optional character
748                         label_str = g_strdup(action->u.string_group[1]);
749                         #endif
750
751                         sub_label = gtk_label_new(label_str);
752                         g_free(label_str);
753                         label = gtk_label_new(fvkbd_key_get_disp(key, id));
754                 }
755
756                 break;
757
758         case KEY_ACTION_STRING:
759         case KEY_ACTION_SYM:
760         case KEY_ACTION_SCRIPT:
761                 sub_label = gtk_label_new(NULL);
762                 label = gtk_label_new(fvkbd_key_get_disp(key, id));
763                 break;
764
765         case KEY_ACTION_FUNC:
766                 sub_label = gtk_label_new(NULL);
767                 if ((action->u.func.type == KBD_FUNC_MODE_SELECT)) {
768                         if (unit->uid == latest_mode_key_id) {
769                                 if (status == KEYBOARD_MODE_STATUS_TEMP) {
770                                         #if 0
771                                         gchar *disp;
772                                         gint p_mode;
773
774                                         p_mode = fvkbd_keyboard_get_previous_mode(fvkbd_keyboard_gtk_ui_get_keyboard());
775                                         disp = g_strconcat("<u>", fvkbd_key_get_disp(key, p_mode), "</u>", NULL);
776                                         label = gtk_label_new(disp);
777                                         gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
778                                         g_free(disp);
779                                         #endif
780                                 }
781                         }
782                 }
783
784                 if (!label)
785                         label = gtk_label_new(fvkbd_key_get_disp(key, id));
786                 break;
787
788         default:
789                 break;
790         }
791
792
793         priv->sub_label = sub_label;
794         if (sub_label)
795                 gtk_box_pack_start(GTK_BOX(vbox), sub_label, FALSE, FALSE, 0);
796
797         priv->main_label = label;
798         if (label)
799                 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
800
801 done:
802
803         gtk_widget_show_all(vbox);
804         return vbox;
805 }
806
807
808 static void
809 _toggle_key_img (FvkbdKeyGtkUI *key_ui, gboolean down)
810 {
811         FvkbdKeyGtkUIPrivate *priv = FVKBD_KEY_GTK_UI_GET_PRIVATE(key_ui);
812
813         GtkWidget *key_widget;
814         GtkWidget *key_label;
815         FvkbdKeyAction *action;
816
817         if ((action = _key_gtk_ui_get_current_action(key_ui)) == NULL)
818                 return;
819
820         if ((action->img == NULL) || (action->img_dn == NULL))
821                 return;
822
823         key_widget = fvkbd_gtk_ui_get_widget(FVKBD_GTK_UI(key_ui));
824         
825         key_label = gtk_bin_get_child(GTK_BIN(key_widget));
826
827         if (down) {
828                 gtk_container_remove(GTK_CONTAINER(key_label), priv->img);
829                 gtk_container_add(GTK_CONTAINER(key_label), priv->img_dn);
830                 gtk_widget_show(priv->img_dn);
831         } else {
832                 gtk_container_remove(GTK_CONTAINER(key_label), priv->img_dn);
833                 gtk_container_add(GTK_CONTAINER(key_label), priv->img);
834                 gtk_widget_show(priv->img);
835         }
836 }
837
838
839 static void
840 _update_key_label (FvkbdKeyGtkUI *key_ui, gint id, KeyboardModeStatus status)
841 {
842         FvkbdKeyGtkUIPrivate *priv = FVKBD_KEY_GTK_UI_GET_PRIVATE(key_ui);
843         GtkWidget *key_widget;
844         GtkWidget *key_label;
845
846         key_widget = fvkbd_gtk_ui_get_widget(FVKBD_GTK_UI(key_ui));
847         
848         key_label = gtk_bin_get_child(GTK_BIN(key_widget));
849
850         if (key_label) {
851                 if (priv->img) {
852                         g_object_unref(priv->img);
853                         priv->img = NULL;
854                 }
855
856                 if (priv->img_dn) {
857                         g_object_unref(priv->img_dn);
858                         priv->img_dn = NULL;
859                 }
860
861                 gtk_widget_destroy(key_label);
862                 priv->main_label = NULL;
863                 priv->sub_label = NULL;
864
865                 key_label = NULL;
866         }
867
868         key_label = _create_key_label(key_ui, id, status);
869         gtk_container_add(GTK_CONTAINER(key_widget), key_label);
870 }
871
872
873 static gboolean
874 fvkbd_key_gtk_ui_build (FvkbdGtkUI *ui, GtkWidget **widget)
875 {
876         FvkbdKeyGtkUI *key_ui = FVKBD_KEY_GTK_UI(ui);
877         FvkbdUnit *unit = ui->unit;
878         gint w,h;
879         GtkWidget *key_widget;
880         gboolean ret = TRUE;
881
882         g_return_val_if_fail(FVKBD_IS_KEY(ui->unit), FALSE);
883
884         key_widget = gtk_vkb_button_new();
885
886         fvkbd_unit_get_size(unit, &w, &h);
887
888         g_signal_connect(G_OBJECT(key_widget), "pressed",
889                         G_CALLBACK(fvkbd_key_gtk_pressed_cb), key_ui);
890         g_signal_connect(G_OBJECT(key_widget), "released",
891                         G_CALLBACK(fvkbd_key_gtk_released_cb), key_ui);
892         g_signal_connect(G_OBJECT(key_widget), "size-allocate",
893                         G_CALLBACK(fvkbd_key_gtk_size_allocate_cb), key_ui);
894
895         if (widget != NULL)
896                 *widget = key_widget;
897
898         fvkbd_gtk_ui_set_widget(ui, key_widget);
899         return ret;
900 }
901
902
903 static gboolean
904 fvkbd_key_gtk_ui_allocate (FvkbdGtkUI *ui, gint x, gint y, gfloat x_ratio, gfloat y_ratio)
905 {
906         FvkbdKeyboard *keyboard = fvkbd_keyboard_gtk_ui_get_keyboard();
907         FvkbdKeyGtkUI *key_ui = FVKBD_KEY_GTK_UI(ui);
908         FvkbdUnit *unit = ui->unit;
909         GtkWidget *key_widget;
910         FvkbdKeyAction *action;
911         int tmp_w, tmp_h;
912         int new_w, new_h;
913         GtkAllocation allocation;
914
915         g_return_val_if_fail(FVKBD_IS_KEY(unit), FALSE);
916
917         key_widget = fvkbd_gtk_ui_get_widget(ui);
918
919         if (fvkbd_unit_get_size(unit, &tmp_w, &tmp_h) == 0) {
920                 new_w = tmp_w * x_ratio;
921                 new_h = tmp_h * y_ratio;
922         } else
923                 return FALSE;
924
925         fvkbd_gtk_ui_set_ratio(ui, x_ratio, y_ratio);
926         allocation.x = x;
927         allocation.y = y;
928         allocation.width = new_w;
929         allocation.height = new_h;
930
931         // if width and height do not change, then just pass down the allocation information
932         if ((key_widget->allocation.width == new_w) && (key_widget->allocation.height == new_h)) {
933                 gtk_widget_size_allocate(key_widget, &allocation);
934                 return TRUE;
935         }
936
937         if ((action = _key_gtk_ui_get_current_action(key_ui)) == NULL)
938                 return FALSE;
939
940         if (FVKBD_UNIT_HAS_FONT_INFO(unit)) {
941                 PangoFontDescription **descs;
942                 KbdFontInfo *font;
943                 int i;
944
945                 descs = g_new(PangoFontDescription *, KBD_FONT_TYPE_NUMBER);
946                 for (i = 0; i < KBD_FONT_TYPE_NUMBER; i++) {
947                         font = fvkbd_unit_get_font(unit, i);
948                         if (font)
949                                 *(descs + i) = get_scaled_pango_font_description(font, x_ratio, y_ratio);
950                         else
951                                 *(descs + i) = NULL;
952                 }
953
954                 fvkbd_gtk_ui_set_qdata(ui, quark_ui_font_descs, descs,
955                                 (GDestroyNotify)free_ui_font_descs);
956                 FVKBD_UI_SET_FLAG(ui, FLAG_UI_FONT_DESCS);
957         }
958
959         if ((action->img != NULL) || (gtk_bin_get_child(GTK_BIN(key_widget)) == NULL)) {
960                 _update_key_label(key_ui,
961                                 fvkbd_keyboard_get_current_mode(keyboard),
962                                 fvkbd_keyboard_get_mode_status(keyboard));
963         }
964
965         // set color
966         fvkbd_key_settle_color(key_ui, key_widget);
967
968         // set font
969         fvkbd_key_settle_font(key_ui, key_widget);
970
971         fvkbd_key_settle_bg_image(key_ui, key_widget);
972         gtk_widget_size_allocate(key_widget, &allocation);
973
974         return TRUE;
975 }
976
977
978 static gboolean
979 fvkbd_key_gtk_ui_destroy (FvkbdGtkUI *ui)
980 {
981         FvkbdKeyGtkUI *key_ui = FVKBD_KEY_GTK_UI(ui);
982         FvkbdUnit *unit = ui->unit;
983         FvkbdKeyGtkUIPrivate *priv = FVKBD_KEY_GTK_UI_GET_PRIVATE(key_ui);
984         FvkbdKey *key;
985         gboolean ret = TRUE;
986         GtkWidget *key_widget;
987         int i;
988
989         g_return_val_if_fail(FVKBD_IS_KEY(unit), FALSE);
990
991         key = FVKBD_KEY(unit);
992         key_widget = fvkbd_gtk_ui_get_widget(ui);
993
994         gtk_widget_hide_all(key_widget);
995
996         for (i = 0; i < KBD_FONT_TYPE_NUMBER; i++) {
997                 if (priv->action_font_descs[i])
998                         pango_font_description_free(priv->action_font_descs[i]);
999         }
1000
1001         if (priv->img)
1002                 g_object_unref(priv->img);
1003         if (priv->img_dn)
1004                 g_object_unref(priv->img_dn);
1005
1006         gtk_widget_destroy(key_widget);
1007
1008         if (FVKBD_UI_HAS_KEY_SHAPE_BITMAP(ui)) {
1009                 FVKBD_UI_UNSET_FLAG(ui, FLAG_KEY_SHAPE_BITMAP);
1010                 fvkbd_gtk_ui_set_qdata(ui, quark_key_shape_bitmap, NULL, NULL);
1011         }
1012
1013         if (FVKBD_UI_HAS_KEY_BG_PIXMAP(ui)) {
1014                 FVKBD_UI_UNSET_FLAG(ui, FLAG_KEY_BG_PIXMAP);
1015                 fvkbd_gtk_ui_set_qdata(ui, quark_key_bg_pixmap, NULL, NULL);
1016         }
1017
1018         if (FVKBD_UI_HAS_FONT_DESCS(ui)) {
1019                 FVKBD_UI_UNSET_FLAG(ui, FLAG_UI_FONT_DESCS);
1020                 fvkbd_gtk_ui_set_qdata(ui, quark_ui_font_descs, NULL, NULL);
1021         }
1022
1023         fvkbd_unit_set_ui_data(unit, NULL);
1024         fvkbd_gtk_ui_set_widget(ui, NULL);
1025
1026         g_object_unref(ui);
1027         return ret;
1028 }
1029
1030
1031 static gboolean
1032 fvkbd_key_gtk_ui_set_mode (FvkbdGtkUI *ui, gint id)
1033 {
1034         FvkbdKeyGtkUI *key_ui;
1035         FvkbdKey *key;
1036         FvkbdKeyboard *keyboard = fvkbd_keyboard_gtk_ui_get_keyboard();
1037
1038         KeyboardModeStatus status;
1039         GtkWidget *key_widget;
1040
1041         g_return_val_if_fail(FVKBD_IS_KEY_GTK_UI(ui), FALSE);
1042
1043         key_ui = FVKBD_KEY_GTK_UI(ui);
1044         key = FVKBD_KEY(ui->unit);
1045         key_widget = fvkbd_gtk_ui_get_widget(ui);
1046         status = fvkbd_keyboard_get_mode_status(keyboard);
1047
1048         _update_key_label(key_ui, id, status);
1049
1050         // set color
1051         fvkbd_key_settle_color(key_ui, key_widget);
1052
1053         // settle font
1054         fvkbd_key_settle_font(key_ui, key_widget);
1055
1056         return TRUE;
1057 }
1058