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