1 /*
2 * Copyright (C) 2020 The HIME team, Taiwan
3 * Copyright (C) 2011 Edward Der-Hua Liu, Hsin-Chu, Taiwan
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation version 2.1
8 * of the License.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 #include <signal.h>
21
22 #include "hime.h"
23
24 #include "gst.h"
25 #include "gtab.h"
26 #include "pho.h"
27
28 #if !GTK_CHECK_VERSION(2, 90, 6)
29 extern GdkPixbuf *gdk_pixbuf_get_from_surface (cairo_surface_t *surface, gint src_x, gint src_y, gint width, gint height);
30 #endif
31
32 extern void destroy_other_tray ();
33
34 GtkStatusIcon *tray_icon;
35 static GdkPixbuf *pixbuf, *pixbuf_ch;
36 static PangoLayout *pango;
37 static cairo_t *cr;
38 static GtkWidget *tray_menu = NULL;
39
40 static int iw, ih;
41
42 static GdkColor red_color_fg;
43 static GdkColor blue_color_fg;
44
45 #define HIME_TRAY_PNG "hime-tray.png"
46 static char pixbuf_ch_fname[512];
47
48 void toggle_gb_output ();
49 extern gboolean gb_output;
50
51 static char full[] = N_ ("全"), engst[] = N_ ("ABC"), sim[] = N_ ("简");
52 extern int current_shape_mode ();
53
destroy_tray_icon()54 void destroy_tray_icon () {
55 if (tray_icon != NULL) {
56 // Workaround: to release the space on notification area
57 gtk_status_icon_set_visible (tray_icon, FALSE);
58 g_object_unref (tray_icon);
59 tray_icon = NULL;
60 }
61 if (tray_menu) {
62 gtk_widget_destroy (tray_menu);
63 tray_menu = NULL;
64 }
65 if (pixbuf) {
66 g_object_unref (pixbuf);
67 pixbuf = NULL;
68 }
69 if (pixbuf_ch) {
70 g_object_unref (pixbuf_ch);
71 pixbuf_ch = NULL;
72 }
73 }
74
get_text_w_h(char * s,int * w,int * h)75 static void get_text_w_h (char *s, int *w, int *h) {
76 pango_layout_set_text (pango, s, strlen (s));
77 pango_layout_get_pixel_size (pango, w, h);
78 }
79
draw_icon()80 static void draw_icon () {
81 gboolean tsin_pho_mode ();
82
83 if (!tray_icon)
84 return;
85
86 GdkPixbuf *pix = ((!current_CS) ||
87 (current_CS->im_state != HIME_STATE_CHINESE))
88 ? pixbuf
89 : pixbuf_ch;
90
91 int w = 0, h = 0;
92 iw = gtk_status_icon_get_size (tray_icon), ih = gtk_status_icon_get_size (tray_icon);
93
94 cairo_surface_t *cst = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, iw, ih);
95 cr = cairo_create (cst);
96 gdk_cairo_set_source_color (cr, &red_color_fg);
97
98 if (pix) {
99 gdk_cairo_set_source_pixbuf (cr, pix, 0, 0);
100 cairo_paint (cr);
101 } else {
102 get_text_w_h (inmd[current_CS->in_method].cname, &w, &h);
103 cairo_move_to (cr, 0, 0);
104 pango_cairo_show_layout (cr, pango);
105 }
106
107 if (current_CS) {
108 gdk_cairo_set_source_color (cr, &red_color_fg);
109 if (current_shape_mode ()) {
110 get_text_w_h (full, &w, &h);
111 cairo_move_to (cr, iw - w, ih - h);
112 pango_cairo_show_layout (cr, pango);
113 }
114 if (current_CS->im_state == HIME_STATE_CHINESE && !tsin_pho_mode ()) {
115 gdk_cairo_set_source_color (cr, &blue_color_fg);
116 get_text_w_h (engst, &w, &h);
117 cairo_move_to (cr, 0, 0);
118 pango_cairo_show_layout (cr, pango);
119 }
120 }
121
122 if (gb_output) {
123 gdk_cairo_set_source_color (cr, &red_color_fg);
124 get_text_w_h (sim, &w, &h);
125 cairo_move_to (cr, 0, ih - h);
126 pango_cairo_show_layout (cr, pango);
127 }
128 cairo_destroy (cr);
129 cr = NULL;
130 GdkPixbuf *icon_pixbuf_output = gdk_pixbuf_get_from_surface (cst, 0, 0, iw, ih);
131 cairo_surface_destroy (cst);
132 cst = NULL;
133 gtk_status_icon_set_from_pixbuf (tray_icon, icon_pixbuf_output);
134 g_object_unref (icon_pixbuf_output);
135 icon_pixbuf_output = NULL;
136 pix = NULL;
137 }
138
139 void get_icon_path (char *iconame, char fname[]);
140 gboolean create_tray (gpointer data);
141
load_tray_icon()142 void load_tray_icon () {
143 if (!hime_status_tray)
144 return;
145 if (!tray_icon) {
146 create_tray (NULL);
147 return;
148 }
149 // wrong width & height if it is not embedded-ready
150 if (!gtk_status_icon_is_embedded (tray_icon))
151 return;
152 iw = gtk_status_icon_get_size (tray_icon), ih = gtk_status_icon_get_size (tray_icon);
153 if (!pixbuf) {
154 char icon_fname[128];
155 get_icon_path (HIME_TRAY_PNG, icon_fname);
156 pixbuf = gdk_pixbuf_new_from_file_at_size (icon_fname, iw, ih, NULL);
157 }
158 char *iconame = HIME_TRAY_PNG;
159 // if (current_CS && current_CS->in_method && inmd)
160 // Workaround due to issue #161
161 if (current_CS && current_CS->im_state != HIME_STATE_DISABLED && current_CS->im_state != HIME_STATE_ENG_FULL)
162 iconame = inmd[current_CS->in_method].icon;
163 char fname[512];
164 if (iconame)
165 get_icon_path (iconame, fname);
166 if (strcmp (pixbuf_ch_fname, fname) && pixbuf_ch) {
167 g_object_unref (pixbuf_ch);
168 pixbuf_ch = NULL;
169 }
170 if (!pixbuf_ch) {
171 strcpy (pixbuf_ch_fname, fname);
172 pixbuf_ch = gdk_pixbuf_new_from_file_at_size (fname, iw, ih, NULL);
173 }
174 draw_icon ();
175 iconame = NULL;
176 }
177
178 void exec_hime_setup_ (GtkCheckMenuItem *checkmenuitem, gpointer dat);
179
180 void cb_trad_sim_toggle ();
181 void cb_trad_sim_toggle_ (GtkCheckMenuItem *checkmenuitem, gpointer dat);
182
183 void cb_sim2trad (GtkCheckMenuItem *checkmenuitem, gpointer dat);
184 void cb_trad2sim (GtkCheckMenuItem *checkmenuitem, gpointer dat);
185
186 void quit_hime (GtkCheckMenuItem *checkmenuitem, gpointer dat);
187
188 void cb_tog_phospeak (GtkCheckMenuItem *checkmenuitem, gpointer dat);
189
190 void kbm_toggle_ (GtkCheckMenuItem *checkmenuitem, gpointer dat);
191 extern gboolean win_kbm_on;
192
193 void cb_inmd_menu (GtkCheckMenuItem *checkmenuitem, gpointer dat);
194
195 #include "mitem.h"
196
197 static MITEM mitems[] = {
198 {N_ ("Configuration"), GTK_STOCK_PREFERENCES, exec_hime_setup_, NULL},
199 {N_ ("Exit"), GTK_STOCK_QUIT, quit_hime, NULL},
200 {N_ ("Text-to-speech"), NULL, cb_tog_phospeak, &phonetic_speak},
201 {N_ ("Trad. to Simp. conversion tool"), NULL, cb_trad2sim, NULL},
202 {N_ ("Simp. to Trad. conversion tool"), NULL, cb_sim2trad, NULL},
203 {N_ ("Select input methods"), NULL, cb_inmd_menu, NULL},
204 {N_ ("Virtual keyboard"), NULL, kbm_toggle_, &hime_show_win_kbm},
205 {N_ ("Simplified Chinese output"), NULL, cb_trad_sim_toggle_, &gb_output},
206 {NULL, NULL, NULL, NULL}};
207
208 GtkWidget *create_tray_menu (MITEM *mitems);
209 void update_item_active_all ();
210
211 gint inmd_switch_popup_handler (GtkWidget *widget, GdkEvent *event);
212
reload_tray_icon()213 void reload_tray_icon () {
214 if (pixbuf) {
215 g_object_unref (pixbuf);
216 pixbuf = NULL;
217 }
218 if (pixbuf_ch) {
219 g_object_unref (pixbuf_ch);
220 pixbuf_ch = NULL;
221 }
222 load_tray_icon ();
223 }
224
tray_size_changed_cb(GtkStatusIcon * status_icon,gint * size,gpointer user_data)225 gboolean tray_size_changed_cb (GtkStatusIcon *status_icon, gint *size, gpointer user_data) {
226 reload_tray_icon ();
227 return FALSE;
228 }
229
tray_embedded_cb(GtkStatusIcon * status_icon,GParamSpec * pspec,gpointer user_data)230 gboolean tray_embedded_cb (GtkStatusIcon *status_icon, GParamSpec *pspec, gpointer user_data) {
231 if (gtk_status_icon_is_embedded (tray_icon)) {
232 reload_tray_icon ();
233 }
234 return TRUE;
235 }
236
237 void toggle_im_enabled (), kbm_toggle ();
tray_button_press_event_cb(GtkStatusIcon * status_icon,GdkEventButton * event,gpointer userdata)238 gboolean tray_button_press_event_cb (GtkStatusIcon *status_icon, GdkEventButton *event, gpointer userdata) {
239 switch (event->button) {
240 case 1:
241 if (event->state & GDK_SHIFT_MASK)
242 inmd_switch_popup_handler (NULL, (GdkEvent *) event);
243 else
244 toggle_im_enabled ();
245 break;
246 case 2:
247 #if 0
248 inmd_switch_popup_handler(NULL, (GdkEvent *)event);
249 #else
250 kbm_toggle ();
251 dbg ("win_kbm_on %d\n", win_kbm_on);
252 update_item_active_all ();
253 #endif
254 break;
255 case 3:
256 if (!tray_menu)
257 tray_menu = create_tray_menu (mitems);
258 gtk_menu_popup (GTK_MENU (tray_menu), NULL, NULL, gtk_status_icon_position_menu, tray_icon, event->button, event->time);
259 break;
260 }
261
262 return TRUE;
263 }
264
265 void update_item_active (MITEM *mitems);
266
update_item_active_single()267 void update_item_active_single () {
268 update_item_active (mitems);
269 }
270
create_tray(gpointer data)271 gboolean create_tray (gpointer data) {
272 if (tray_icon)
273 return FALSE;
274
275 destroy_other_tray ();
276
277 tray_icon = gtk_status_icon_new ();
278
279 g_signal_connect (G_OBJECT (tray_icon), "button-press-event",
280 G_CALLBACK (tray_button_press_event_cb), NULL);
281
282 g_signal_connect (G_OBJECT (tray_icon), "size-changed",
283 G_CALLBACK (tray_size_changed_cb), NULL);
284
285 g_signal_connect (G_OBJECT (tray_icon), "notify::embedded",
286 G_CALLBACK (tray_embedded_cb), NULL);
287
288 #if GTK_CHECK_VERSION(2, 12, 0)
289 gtk_status_icon_set_tooltip_text (tray_icon, _ ("Left:Toggle alphabet-numeric mode Middle:Virtual Keyboard Right:Preferences"));
290 #else
291 GtkTooltips *tips = gtk_tooltips_new ();
292 gtk_status_icon_set_tooltip (GTK_TOOLTIPS (tips), tray_icon, _ ("Left:Toggle alphabet-numeric mode Middle:Virtual Keyboard Right:Preferences"), NULL);
293 #endif
294
295 // Initiate Pango for drawing texts from default setting
296 GtkWidget *wi = gtk_label_new (NULL); // for reference
297 PangoContext *context = gtk_widget_get_pango_context (wi);
298 PangoFontDescription *desc = pango_font_description_copy (pango_context_get_font_description (context)); // Copy one from wi for pango
299 pango_font_description_set_size (desc, 9 * PANGO_SCALE);
300 pango = gtk_widget_create_pango_layout (wi, NULL);
301 pango_layout_set_font_description (pango, desc);
302
303 gdk_color_parse ("red", &red_color_fg);
304 gdk_color_parse ("blue", &blue_color_fg);
305
306 gtk_widget_destroy (wi);
307
308 load_tray_icon ();
309 return FALSE;
310 }
311
is_exist_tray()312 gboolean is_exist_tray () {
313 return tray_icon != NULL;
314 }
315
init_tray()316 void init_tray () {
317 g_timeout_add (200, create_tray, NULL); // Old setting is 5000 here.
318 }
319