1 /* $Id$ */
2 /* Copyright (c) 2011-2016 Pierre Pronchery <khorben@defora.org> */
3 /* This file is part of DeforaOS Desktop Keyboard */
4 /* All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
23 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
28
29
30
31 #include <stdlib.h>
32 #include <string.h>
33 #include <gdk/gdkx.h>
34 #include <Desktop.h>
35 #include "common.h"
36 #include "key.h"
37
38
39 /* KeyboardKey */
40 /* private */
41 /* types */
42 typedef struct _KeyboardKeyModifier
43 {
44 unsigned int modifier;
45 unsigned int keysym;
46 char * label;
47 } KeyboardKeyModifier;
48
49 struct _KeyboardKey
50 {
51 GtkWidget * widget;
52 GtkWidget * label;
53 GtkWidget * popup;
54 GtkWidget * button;
55 KeyboardKeyModifier key;
56 KeyboardKeyModifier * modifiers;
57 size_t modifiers_cnt;
58 KeyboardKeyModifier * current;
59 };
60
61
62 /* prototypes */
63 static void _keyboard_key_create_popup(KeyboardKey * key);
64
65 /* callbacks */
66 static gboolean _on_keyboard_key_button_press(GtkWidget * widget,
67 GdkEventButton * event, gpointer data);
68 static gboolean _on_keyboard_key_button_release(GtkWidget * widget,
69 GdkEventButton * event, gpointer data);
70
71
72 /* public */
73 /* functions */
74 /* keyboard_key_new */
keyboard_key_new(unsigned int keysym,char const * label)75 KeyboardKey * keyboard_key_new(unsigned int keysym, char const * label)
76 {
77 KeyboardKey * key;
78
79 if((key = malloc(sizeof(*key))) == NULL)
80 return NULL;
81 if(keysym_is_modifier(keysym))
82 key->widget = gtk_toggle_button_new();
83 else
84 key->widget = gtk_button_new();
85 g_signal_connect(G_OBJECT(key->widget), "button-press-event",
86 G_CALLBACK(_on_keyboard_key_button_press), key);
87 g_signal_connect(G_OBJECT(key->widget), "button-release-event",
88 G_CALLBACK(_on_keyboard_key_button_release), key);
89 key->label = gtk_label_new(label);
90 gtk_container_add(GTK_CONTAINER(key->widget), key->label);
91 key->popup = NULL;
92 key->button = NULL;
93 key->key.modifier = 0;
94 key->key.keysym = keysym;
95 key->key.label = strdup(label);
96 key->modifiers = NULL;
97 key->modifiers_cnt = 0;
98 key->current = &key->key;
99 if(key->key.label == NULL)
100 {
101 keyboard_key_delete(key);
102 return NULL;
103 }
104 return key;
105 }
106
107
108 /* keyboard_key_delete */
keyboard_key_delete(KeyboardKey * key)109 void keyboard_key_delete(KeyboardKey * key)
110 {
111 size_t i;
112
113 for(i = 0; i < key->modifiers_cnt; i++)
114 free(key->modifiers[i].label);
115 free(key->modifiers);
116 free(key->key.label);
117 free(key);
118 }
119
120
121 /* accessors */
122 /* keyboard_key_get_keysym */
keyboard_key_get_keysym(KeyboardKey * key)123 unsigned int keyboard_key_get_keysym(KeyboardKey * key)
124 {
125 return key->current->keysym;
126 }
127
128
129 /* keyboard_key_get_label_widget */
keyboard_key_get_label_widget(KeyboardKey * key)130 GtkWidget * keyboard_key_get_label_widget(KeyboardKey * key)
131 {
132 return key->label;
133 }
134
135
136 /* keyboard_key_get_widget */
keyboard_key_get_widget(KeyboardKey * key)137 GtkWidget * keyboard_key_get_widget(KeyboardKey * key)
138 {
139 return key->widget;
140 }
141
142
143 /* keyboard_key_set_background */
144 # if GTK_CHECK_VERSION(3, 0, 0)
keyboard_key_set_background(KeyboardKey * key,GdkRGBA * color)145 void keyboard_key_set_background(KeyboardKey * key, GdkRGBA * color)
146 {
147 _keyboard_key_create_popup(key);
148 gtk_widget_override_background_color(key->widget, GTK_STATE_FLAG_NORMAL,
149 color);
150 gtk_widget_override_background_color(key->popup, GTK_STATE_FLAG_NORMAL,
151 color);
152 gtk_widget_override_background_color(key->button, GTK_STATE_FLAG_NORMAL,
153 color);
154 }
155 # else
keyboard_key_set_background(KeyboardKey * key,GdkColor * color)156 void keyboard_key_set_background(KeyboardKey * key, GdkColor * color)
157 {
158 _keyboard_key_create_popup(key);
159 gtk_widget_modify_bg(key->widget, GTK_STATE_NORMAL, color);
160 gtk_widget_modify_bg(key->popup, GTK_STATE_NORMAL, color);
161 gtk_widget_modify_bg(key->button, GTK_STATE_NORMAL, color);
162 }
163 #endif
164
165
166 /* keyboard_key_set_font */
keyboard_key_set_font(KeyboardKey * key,PangoFontDescription * font)167 void keyboard_key_set_font(KeyboardKey * key, PangoFontDescription * font)
168 {
169 _keyboard_key_create_popup(key);
170 gtk_widget_override_font(key->label, font);
171 gtk_widget_override_font(gtk_bin_get_child(GTK_BIN(key->button)), font);
172 }
173
174
175 /* keyboard_key_set_foreground */
176 #if GTK_CHECK_VERSION(3, 0, 0)
keyboard_key_set_foreground(KeyboardKey * key,GdkRGBA * color)177 void keyboard_key_set_foreground(KeyboardKey * key, GdkRGBA * color)
178 {
179 _keyboard_key_create_popup(key);
180 gtk_widget_override_color(key->label, GTK_STATE_FLAG_NORMAL, color);
181 gtk_widget_override_color(key->button, GTK_STATE_FLAG_NORMAL, color);
182 }
183 #else
keyboard_key_set_foreground(KeyboardKey * key,GdkColor * color)184 void keyboard_key_set_foreground(KeyboardKey * key, GdkColor * color)
185 {
186 _keyboard_key_create_popup(key);
187 gtk_widget_modify_fg(key->label, GTK_STATE_NORMAL, color);
188 gtk_widget_modify_fg(key->button, GTK_STATE_NORMAL, color);
189 }
190 #endif
191
192
193 /* keyboard_key_set_modifier */
keyboard_key_set_modifier(KeyboardKey * key,unsigned int modifier,unsigned int keysym,char const * label)194 int keyboard_key_set_modifier(KeyboardKey * key, unsigned int modifier,
195 unsigned int keysym, char const * label)
196 {
197 char * p;
198 KeyboardKeyModifier * q;
199
200 if(label == NULL || (p = strdup(label)) == NULL)
201 return -1;
202 if(modifier == 0)
203 {
204 key->key.keysym = keysym;
205 free(key->key.label);
206 key->key.label = p;
207 return 0;
208 }
209 if((q = realloc(key->modifiers, sizeof(*q) * (key->modifiers_cnt + 1)))
210 == NULL)
211 {
212 free(p);
213 return -1;
214 }
215 key->modifiers = q;
216 q = &key->modifiers[key->modifiers_cnt++];
217 q->modifier = modifier;
218 q->keysym = keysym;
219 q->label = p;
220 return 0;
221 }
222
223
224 /* useful */
225 /* keyboard_key_apply_modifier */
keyboard_key_apply_modifier(KeyboardKey * key,unsigned int modifier)226 void keyboard_key_apply_modifier(KeyboardKey * key, unsigned int modifier)
227 {
228 char const * label = key->key.label;
229 size_t i;
230
231 key->current = &key->key;
232 if(modifier != 0)
233 for(i = 0; i < key->modifiers_cnt; i++)
234 if(key->modifiers[i].modifier == modifier)
235 {
236 label = key->modifiers[i].label;
237 key->current = &key->modifiers[i];
238 break;
239 }
240 gtk_label_set_text(GTK_LABEL(key->label), label);
241 }
242
243
244 /* private */
245 /* functions */
246 /* keyboard_key_create_popup */
247 #if !GTK_CHECK_VERSION(3, 0, 0)
248 /* callbacks */
249 static void _create_popup_on_realize(gpointer data);
250 #endif
251
_keyboard_key_create_popup(KeyboardKey * key)252 static void _keyboard_key_create_popup(KeyboardKey * key)
253 {
254 if(key->popup != NULL)
255 return;
256 key->popup = gtk_window_new(GTK_WINDOW_POPUP);
257 #if !GTK_CHECK_VERSION(3, 0, 0)
258 g_signal_connect(key->popup, "realize", G_CALLBACK(
259 _create_popup_on_realize), NULL);
260 #endif
261 key->button = gtk_button_new_with_label(gtk_label_get_text(GTK_LABEL(
262 key->label)));
263 gtk_button_set_alignment(GTK_BUTTON(key->button), 0.5, 0.1);
264 gtk_container_add(GTK_CONTAINER(key->popup), key->button);
265 }
266
267 #if !GTK_CHECK_VERSION(3, 0, 0)
268 /* callbacks */
_create_popup_on_realize(gpointer data)269 static void _create_popup_on_realize(gpointer data)
270 {
271 GtkWidget * window = data;
272 int width;
273 int height;
274 GdkBitmap * mask;
275 GdkGC * gc;
276 GdkColor black = { 0, 0, 0, 0 };
277 GdkColor white = { 0xffffffff, 0xffff, 0xffff, 0xffff };
278
279 /* XXX potential invalid assumption as to how the button is drawn */
280 gtk_window_get_size(GTK_WINDOW(window), &width, &height);
281 mask = gdk_pixmap_new(NULL, width, height, 1);
282 gc = gdk_gc_new(mask);
283 gdk_gc_set_foreground(gc, &white);
284 gdk_draw_rectangle(mask, gc, TRUE, 0, 0, width, height);
285 gdk_gc_set_foreground(gc, &black);
286 /* left column */
287 gdk_draw_rectangle(mask, gc, TRUE, 0, 0, 1, height);
288 /* top row */
289 gdk_draw_rectangle(mask, gc, TRUE, 0, 0, width, 1);
290 /* right column */
291 gdk_draw_rectangle(mask, gc, TRUE, width - 1, 0, 1, height);
292 /* bottom row */
293 gdk_draw_rectangle(mask, gc, TRUE, 0, height - 1, width, 1);
294 /* top left corner */
295 gdk_draw_rectangle(mask, gc, TRUE, 0, 0, 2, 2);
296 /* top right corner */
297 gdk_draw_rectangle(mask, gc, TRUE, width - 2, 0, 2, 2);
298 /* bottom left corner */
299 gdk_draw_rectangle(mask, gc, TRUE, 0, height - 2, 2, 2);
300 /* bottom right corner */
301 gdk_draw_rectangle(mask, gc, TRUE, width - 2, height - 2, 2, 2);
302 gtk_widget_shape_combine_mask(window, mask, 0, 0);
303 g_object_unref(gc);
304 g_object_unref(mask);
305 }
306 #endif
307
308
309 /* callbacks */
310 /* on_keyboard_key_button_press */
_on_keyboard_key_button_press(GtkWidget * widget,GdkEventButton * event,gpointer data)311 static gboolean _on_keyboard_key_button_press(GtkWidget * widget,
312 GdkEventButton * event, gpointer data)
313 {
314 KeyboardKey * key = data;
315 gint width;
316 gint height;
317
318 #if GTK_CHECK_VERSION(2, 24, 0)
319 width = gdk_window_get_width(event->window);
320 height = gdk_window_get_height(event->window);
321 #else
322 gdk_window_get_size(event->window, &width, &height);
323 #endif
324 _keyboard_key_create_popup(key);
325 gtk_widget_set_size_request(key->popup, width + 8, height * 2);
326 gtk_window_move(GTK_WINDOW(key->popup), event->x_root - event->x - 4,
327 event->y_root - event->y - height * 2);
328 gtk_widget_show_all(key->popup);
329 return FALSE;
330 }
331
332
333 /* on_keyboard_key_button_release */
_on_keyboard_key_button_release(GtkWidget * widget,GdkEventButton * event,gpointer data)334 static gboolean _on_keyboard_key_button_release(GtkWidget * widget,
335 GdkEventButton * event, gpointer data)
336 {
337 KeyboardKey * key = data;
338
339 if(key->popup != NULL)
340 gtk_widget_hide(key->popup);
341 return FALSE;
342 }
343