comparison pidgin/plugins/gestures/stroke-draw.c @ 15374:5fe8042783c1

Rename gtk/ and libgaim/ to pidgin/ and libpurple/
author Sean Egan <seanegan@gmail.com>
date Sat, 20 Jan 2007 02:32:10 +0000
parents
children 32c366eeeb99
comparison
equal deleted inserted replaced
15373:f79e0f4df793 15374:5fe8042783c1
1 /*
2 GNOME stroke implementation
3 Copyright (c) 2000, 2001 Dan Nicolaescu
4 See the file COPYING for distribution information.
5 */
6
7 #include "config.h"
8
9 #include <unistd.h>
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <glib.h>
13 #include <gtk/gtk.h>
14
15 #include <gdk/gdkx.h>
16 #include "gstroke.h"
17 #include "gstroke-internal.h"
18
19 #include <X11/Xlib.h>
20 #include <X11/Xutil.h>
21
22
23 static void gstroke_invisible_window_init (GtkWidget *widget);
24 /*FIXME: Maybe these should be put in a structure, and not static...*/
25 static Display * gstroke_disp = NULL;
26 static Window gstroke_window;
27 static GC gstroke_gc;
28 static int mouse_button = 2;
29 static gboolean draw_strokes = FALSE;
30
31 #define GSTROKE_TIMEOUT_DURATION 10
32
33 #define GSTROKE_SIGNALS "gstroke_signals"
34
35 struct gstroke_func_and_data {
36 void (*func)(GtkWidget *, void *);
37 gpointer data;
38 };
39
40
41 /*FIXME: maybe it's better to just make 2 static variables, not a
42 structure */
43 struct mouse_position {
44 struct s_point last_point;
45 gboolean invalid;
46 };
47
48
49 static struct mouse_position last_mouse_position;
50 static guint timer_id;
51
52 static void gstroke_execute (GtkWidget *widget, const gchar *name);
53
54 static void
55 record_stroke_segment (GtkWidget *widget)
56 {
57 gint x, y;
58 struct gstroke_metrics *metrics;
59
60 g_return_if_fail( widget != NULL );
61
62 gtk_widget_get_pointer (widget, &x, &y);
63
64 if (last_mouse_position.invalid)
65 last_mouse_position.invalid = FALSE;
66 else if (gstroke_draw_strokes())
67 {
68 #if 1
69 XDrawLine (gstroke_disp, gstroke_window, gstroke_gc,
70 last_mouse_position.last_point.x,
71 last_mouse_position.last_point.y,
72 x, y);
73 /* XFlush (gstroke_disp); */
74 #else
75 /* FIXME: this does not work. It will only work if we create a
76 corresponding GDK window for stroke_window and draw on
77 that... */
78 gdk_draw_line (widget->window, widget->style->fg_gc[GTK_STATE_NORMAL],
79 last_mouse_position.last_point.x,
80 last_mouse_position.last_point.y,
81 x,
82 y);
83 #endif
84 }
85
86 if (last_mouse_position.last_point.x != x
87 || last_mouse_position.last_point.y != y)
88 {
89 last_mouse_position.last_point.x = x;
90 last_mouse_position.last_point.y = y;
91 metrics = (struct gstroke_metrics *)g_object_get_data(G_OBJECT(widget),
92 GSTROKE_METRICS);
93 _gstroke_record (x, y, metrics);
94 }
95 }
96
97 static gint
98 gstroke_timeout (gpointer data)
99 {
100 GtkWidget *widget;
101
102 g_return_val_if_fail(data != NULL, FALSE);
103
104 widget = GTK_WIDGET (data);
105 record_stroke_segment (widget);
106
107 return TRUE;
108 }
109
110 static void gstroke_cancel(GdkEvent *event)
111 {
112 last_mouse_position.invalid = TRUE;
113
114 if (timer_id > 0)
115 g_source_remove (timer_id);
116
117 timer_id = 0;
118
119 if( event != NULL )
120 gdk_pointer_ungrab (event->button.time);
121
122
123 if (gstroke_draw_strokes() && gstroke_disp != NULL) {
124 /* get rid of the invisible stroke window */
125 XUnmapWindow (gstroke_disp, gstroke_window);
126 XFlush (gstroke_disp);
127 }
128
129 }
130
131 static gint
132 process_event (GtkWidget *widget, GdkEvent *event, gpointer data G_GNUC_UNUSED)
133 {
134 static GtkWidget *original_widget = NULL;
135 static GdkCursor *cursor = NULL;
136
137 switch (event->type) {
138 case GDK_BUTTON_PRESS:
139 if (event->button.button != gstroke_get_mouse_button()) {
140 /* Similar to the bug below catch when any other button is
141 * clicked after the middle button is clicked (but possibly
142 * not released)
143 */
144 gstroke_cancel(event);
145 original_widget = NULL;
146 break;
147 }
148
149 original_widget = widget; /* remeber the widget where
150 the stroke started */
151
152 gstroke_invisible_window_init (widget);
153
154 record_stroke_segment (widget);
155
156 if (cursor == NULL)
157 cursor = gdk_cursor_new(GDK_PENCIL);
158
159 gdk_pointer_grab (widget->window, FALSE,
160 GDK_BUTTON_RELEASE_MASK, NULL, cursor,
161 event->button.time);
162 timer_id = g_timeout_add (GSTROKE_TIMEOUT_DURATION,
163 gstroke_timeout, widget);
164 return TRUE;
165
166 case GDK_BUTTON_RELEASE:
167 if ((event->button.button != gstroke_get_mouse_button())
168 || (original_widget == NULL)) {
169
170 /* Nice bug when you hold down one button and press another. */
171 /* We'll just cancel the gesture instead. */
172 gstroke_cancel(event);
173 original_widget = NULL;
174 break;
175 }
176
177 last_mouse_position.invalid = TRUE;
178 original_widget = NULL;
179 g_source_remove (timer_id);
180 gdk_pointer_ungrab (event->button.time);
181 timer_id = 0;
182
183 {
184 char result[GSTROKE_MAX_SEQUENCE];
185 struct gstroke_metrics *metrics;
186
187 metrics = (struct gstroke_metrics *)g_object_get_data(G_OBJECT (widget),
188 GSTROKE_METRICS);
189 if (gstroke_draw_strokes()) {
190 /* get rid of the invisible stroke window */
191 XUnmapWindow (gstroke_disp, gstroke_window);
192 XFlush (gstroke_disp);
193 }
194
195 _gstroke_canonical (result, metrics);
196 gstroke_execute (widget, result);
197 }
198 return FALSE;
199
200 default:
201 break;
202 }
203
204 return FALSE;
205 }
206
207 void
208 gstroke_set_draw_strokes(gboolean draw)
209 {
210 draw_strokes = draw;
211 }
212
213 gboolean
214 gstroke_draw_strokes(void)
215 {
216 return draw_strokes;
217 }
218
219 void
220 gstroke_set_mouse_button(gint button)
221 {
222 mouse_button = button;
223 }
224
225 guint
226 gstroke_get_mouse_button(void)
227 {
228 return mouse_button;
229 }
230
231 void
232 gstroke_enable (GtkWidget *widget)
233 {
234 struct gstroke_metrics*
235 metrics = (struct gstroke_metrics *)g_object_get_data(G_OBJECT(widget),
236 GSTROKE_METRICS);
237 if (metrics == NULL)
238 {
239 metrics = (struct gstroke_metrics *)g_malloc (sizeof
240 (struct gstroke_metrics));
241 metrics->pointList = NULL;
242 metrics->min_x = 10000;
243 metrics->min_y = 10000;
244 metrics->max_x = 0;
245 metrics->max_y = 0;
246 metrics->point_count = 0;
247
248 g_object_set_data(G_OBJECT(widget), GSTROKE_METRICS, metrics);
249
250 g_signal_connect(G_OBJECT(widget), "event",
251 G_CALLBACK(process_event), NULL);
252 }
253 else
254 _gstroke_init (metrics);
255
256 last_mouse_position.invalid = TRUE;
257 }
258
259 void
260 gstroke_disable(GtkWidget *widget)
261 {
262 g_signal_handlers_disconnect_by_func(G_OBJECT(widget), G_CALLBACK(process_event), NULL);
263 }
264
265 guint
266 gstroke_signal_connect (GtkWidget *widget,
267 const gchar *name,
268 void (*func)(GtkWidget *widget, void *data),
269 gpointer data)
270 {
271 struct gstroke_func_and_data *func_and_data;
272 GHashTable *hash_table =
273 (GHashTable*)g_object_get_data(G_OBJECT(widget), GSTROKE_SIGNALS);
274
275 if (!hash_table)
276 {
277 hash_table = g_hash_table_new (g_str_hash, g_str_equal);
278 g_object_set_data(G_OBJECT(widget), GSTROKE_SIGNALS,
279 (gpointer)hash_table);
280 }
281 func_and_data = g_new (struct gstroke_func_and_data, 1);
282 func_and_data->func = func;
283 func_and_data->data = data;
284 g_hash_table_insert (hash_table, (gpointer)name, (gpointer)func_and_data);
285 return TRUE;
286 }
287
288 static void
289 gstroke_execute (GtkWidget *widget, const gchar *name)
290 {
291
292 GHashTable *hash_table =
293 (GHashTable*)g_object_get_data(G_OBJECT(widget), GSTROKE_SIGNALS);
294
295 #if 0
296 gaim_debug(GAIM_DEBUG_MISC, "gestures", "gstroke %s\n", name);
297 #endif
298
299 if (hash_table)
300 {
301 struct gstroke_func_and_data *fd =
302 (struct gstroke_func_and_data*)g_hash_table_lookup (hash_table, name);
303 if (fd)
304 (*fd->func)(widget, fd->data);
305 }
306 }
307
308 void
309 gstroke_cleanup (GtkWidget *widget)
310 {
311 struct gstroke_metrics *metrics;
312 GHashTable *hash_table =
313 (GHashTable*)g_object_get_data(G_OBJECT(widget), GSTROKE_SIGNALS);
314 if (hash_table)
315 /* FIXME: does this delete the elements too? */
316 g_hash_table_destroy (hash_table);
317
318 g_object_steal_data(G_OBJECT(widget), GSTROKE_SIGNALS);
319
320 metrics = (struct gstroke_metrics*)g_object_get_data(G_OBJECT(widget),
321 GSTROKE_METRICS);
322 if (metrics)
323 g_free (metrics);
324 g_object_steal_data(G_OBJECT(widget), GSTROKE_METRICS);
325 }
326
327
328 /* This function should be written using GTK+ primitives*/
329 static void
330 gstroke_invisible_window_init (GtkWidget *widget)
331 {
332 XSetWindowAttributes w_attr;
333 XWindowAttributes orig_w_attr;
334 unsigned long mask, col_border, col_background;
335 unsigned int border_width;
336 XSizeHints hints;
337 Display *disp = GDK_WINDOW_XDISPLAY(widget->window);
338 Window wind = GDK_WINDOW_XWINDOW (widget->window);
339 int screen = DefaultScreen (disp);
340
341 if (!gstroke_draw_strokes())
342 return;
343
344 gstroke_disp = disp;
345
346 /* X server should save what's underneath */
347 XGetWindowAttributes (gstroke_disp, wind, &orig_w_attr);
348 hints.x = orig_w_attr.x;
349 hints.y = orig_w_attr.y;
350 hints.width = orig_w_attr.width;
351 hints.height = orig_w_attr.height;
352 mask = CWSaveUnder;
353 w_attr.save_under = True;
354
355 /* inhibit all the decorations */
356 mask |= CWOverrideRedirect;
357 w_attr.override_redirect = True;
358
359 /* Don't set a background, transparent window */
360 mask |= CWBackPixmap;
361 w_attr.background_pixmap = None;
362
363 /* Default input window look */
364 col_background = WhitePixel (gstroke_disp, screen);
365
366 /* no border for the window */
367 #if 0
368 border_width = 5;
369 #endif
370 border_width = 0;
371
372 col_border = BlackPixel (gstroke_disp, screen);
373
374 gstroke_window = XCreateSimpleWindow (gstroke_disp, wind,
375 0, 0,
376 hints.width - 2 * border_width,
377 hints.height - 2 * border_width,
378 border_width,
379 col_border, col_background);
380
381 gstroke_gc = XCreateGC (gstroke_disp, gstroke_window, 0, NULL);
382
383 XSetFunction (gstroke_disp, gstroke_gc, GXinvert);
384
385 XChangeWindowAttributes (gstroke_disp, gstroke_window, mask, &w_attr);
386
387 XSetLineAttributes (gstroke_disp, gstroke_gc, 2, LineSolid,
388 CapButt, JoinMiter);
389 XMapRaised (gstroke_disp, gstroke_window);
390
391 #if 0
392 /*FIXME: is this call really needed? If yes, does it need the real
393 argc and argv? */
394 hints.flags = PPosition | PSize;
395 XSetStandardProperties (gstroke_disp, gstroke_window, "gstroke_test", NULL,
396 (Pixmap)NULL, NULL, 0, &hints);
397
398
399 /* Receive the close window client message */
400 {
401 /* FIXME: is this really needed? If yes, something should be done
402 with wmdelete...*/
403 Atom wmdelete = XInternAtom (gstroke_disp, "WM_DELETE_WINDOW",
404 False);
405 XSetWMProtocols (gstroke_disp, gstroke_window, &wmdelete, True);
406 }
407 #endif
408 }