comparison plugins/gestures/stroke-draw.c @ 4390:16540914c963

[gaim-migrate @ 4656] Added a mouse gestures plugin. Yes, you heard me. committer: Tailor Script <tailor@pidgin.im>
author Christian Hammond <chipx86@chipx86.com>
date Wed, 22 Jan 2003 13:57:27 +0000
parents
children ce8d35b435de
comparison
equal deleted inserted replaced
4389:e23a59b62700 4390:16540914c963
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;
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 gtk_widget_get_pointer (widget, &x, &y);
61
62 if (last_mouse_position.invalid)
63 last_mouse_position.invalid = FALSE;
64 else if (gstroke_draw_strokes())
65 {
66 #if 1
67 XDrawLine (gstroke_disp, gstroke_window, gstroke_gc,
68 last_mouse_position.last_point.x,
69 last_mouse_position.last_point.y,
70 x, y);
71 /* XFlush (gstroke_disp); */
72 #else
73 /* FIXME: this does not work. It will only work if we create a
74 corresponding GDK window for stroke_window and draw on
75 that... */
76 gdk_draw_line (widget->window, widget->style->fg_gc[GTK_STATE_NORMAL],
77 last_mouse_position.last_point.x,
78 last_mouse_position.last_point.y,
79 x,
80 y);
81 #endif
82 }
83
84 if (last_mouse_position.last_point.x != x
85 || last_mouse_position.last_point.y != y)
86 {
87 last_mouse_position.last_point.x = x;
88 last_mouse_position.last_point.y = y;
89 metrics = (struct gstroke_metrics *)g_object_get_data(G_OBJECT(widget),
90 GSTROKE_METRICS);
91 _gstroke_record (x, y, metrics);
92 }
93 }
94
95 static gint
96 gstroke_timeout (gpointer data)
97 {
98 GtkWidget *widget = GTK_WIDGET (data);
99 record_stroke_segment (widget);
100
101 return TRUE;
102 }
103
104 static gint
105 process_event (GtkWidget *widget, GdkEvent *event, gpointer data G_GNUC_UNUSED)
106 {
107 static GtkWidget *original_widget = NULL;
108 switch (event->type) {
109 case GDK_BUTTON_PRESS:
110 if (event->button.button != gstroke_get_mouse_button())
111 break;
112
113 original_widget = widget; /* remeber the widget where
114 the stroke started */
115
116 gstroke_invisible_window_init (widget);
117
118 record_stroke_segment (widget);
119
120 gdk_pointer_grab (widget->window, FALSE,
121 GDK_BUTTON_RELEASE_MASK, NULL, NULL,
122 event->button.time);
123 timer_id = gtk_timeout_add (GSTROKE_TIMEOUT_DURATION,
124 gstroke_timeout, widget);
125 return TRUE;
126
127 case GDK_BUTTON_RELEASE:
128 if ((event->button.button != gstroke_get_mouse_button())
129 || (original_widget == NULL))
130 /* the stroke probably did not start here... */
131 break;
132
133 last_mouse_position.invalid = TRUE;
134 original_widget = NULL;
135 gtk_timeout_remove (timer_id);
136 gdk_pointer_ungrab (event->button.time);
137 timer_id = 0;
138
139 {
140 char result[GSTROKE_MAX_SEQUENCE];
141 struct gstroke_metrics *metrics;
142
143 metrics = (struct gstroke_metrics *)g_object_get_data(G_OBJECT (widget),
144 GSTROKE_METRICS);
145 if (gstroke_draw_strokes()) {
146 /* get rid of the invisible stroke window */
147 XUnmapWindow (gstroke_disp, gstroke_window);
148 XFlush (gstroke_disp);
149 }
150
151 _gstroke_canonical (result, metrics);
152 gstroke_execute (widget, result);
153 return FALSE;
154 }
155 return TRUE;
156 default:
157 break;
158 }
159
160 return FALSE;
161 }
162
163 void
164 gstroke_set_draw_strokes(gboolean draw)
165 {
166 draw_strokes = draw;
167 }
168
169 gboolean
170 gstroke_draw_strokes(void)
171 {
172 return draw_strokes;
173 }
174
175 void
176 gstroke_set_mouse_button(gint button)
177 {
178 mouse_button = button;
179 }
180
181 int
182 gstroke_get_mouse_button(void)
183 {
184 return mouse_button;
185 }
186
187 void
188 gstroke_enable (GtkWidget *widget)
189 {
190 struct gstroke_metrics*
191 metrics = (struct gstroke_metrics *)g_object_get_data(G_OBJECT(widget),
192 GSTROKE_METRICS);
193 if (metrics == NULL)
194 {
195 metrics = (struct gstroke_metrics *)g_malloc (sizeof
196 (struct gstroke_metrics));
197 metrics->pointList = NULL;
198 metrics->min_x = 10000;
199 metrics->min_y = 10000;
200 metrics->max_x = 0;
201 metrics->max_y = 0;
202 metrics->point_count = 0;
203
204 g_object_set_data(G_OBJECT(widget), GSTROKE_METRICS, metrics);
205
206 g_signal_connect(G_OBJECT(widget), "event",
207 G_CALLBACK(process_event), NULL);
208 }
209 else
210 _gstroke_init (metrics);
211
212 last_mouse_position.invalid = TRUE;
213 }
214
215 guint
216 gstroke_signal_connect (GtkWidget *widget,
217 const gchar *name,
218 void (*func)(GtkWidget *widget, void *data),
219 gpointer data)
220 {
221 struct gstroke_func_and_data *func_and_data;
222 GHashTable *hash_table =
223 (GHashTable*)g_object_get_data(G_OBJECT(widget), GSTROKE_SIGNALS);
224
225 if (!hash_table)
226 {
227 hash_table = g_hash_table_new (g_str_hash, g_str_equal);
228 g_object_set_data(G_OBJECT(widget), GSTROKE_SIGNALS,
229 (gpointer)hash_table);
230 }
231 func_and_data = g_new (struct gstroke_func_and_data, 1);
232 func_and_data->func = func;
233 func_and_data->data = data;
234 g_hash_table_insert (hash_table, (gpointer)name, (gpointer)func_and_data);
235 return TRUE;
236 }
237
238 static void
239 gstroke_execute (GtkWidget *widget, const gchar *name)
240 {
241
242 GHashTable *hash_table =
243 (GHashTable*)g_object_get_data(G_OBJECT(widget), GSTROKE_SIGNALS);
244
245 #if 0
246 debug_printf("gstroke %s\n", name);
247 #endif
248
249 if (hash_table)
250 {
251 struct gstroke_func_and_data *fd =
252 (struct gstroke_func_and_data*)g_hash_table_lookup (hash_table, name);
253 if (fd)
254 (*fd->func)(widget, fd->data);
255 }
256 }
257
258 void
259 gstroke_cleanup (GtkWidget *widget)
260 {
261 struct gstroke_metrics *metrics;
262 GHashTable *hash_table =
263 (GHashTable*)g_object_get_data(G_OBJECT(widget), GSTROKE_SIGNALS);
264 if (hash_table)
265 /* FIXME: does this delete the elements too? */
266 g_hash_table_destroy (hash_table);
267
268 g_object_steal_data(G_OBJECT(widget), GSTROKE_SIGNALS);
269
270 metrics = (struct gstroke_metrics*)g_object_get_data(G_OBJECT(widget),
271 GSTROKE_METRICS);
272 if (metrics)
273 g_free (metrics);
274 g_object_steal_data(G_OBJECT(widget), GSTROKE_METRICS);
275 }
276
277
278 /* This function should be written using Gtk+ primitives*/
279 static void
280 gstroke_invisible_window_init (GtkWidget *widget)
281 {
282 XSetWindowAttributes w_attr;
283 XWindowAttributes orig_w_attr;
284 unsigned long mask, col_border, col_background;
285 unsigned int border_width;
286 XSizeHints hints;
287 Display *disp = GDK_WINDOW_XDISPLAY(widget->window);
288 Window wind = GDK_WINDOW_XWINDOW (widget->window);
289 int screen = DefaultScreen (disp);
290
291 if (!gstroke_draw_strokes())
292 return;
293
294 gstroke_disp = disp;
295
296 /* X server should save what's underneath */
297 XGetWindowAttributes (gstroke_disp, wind, &orig_w_attr);
298 hints.x = orig_w_attr.x;
299 hints.y = orig_w_attr.y;
300 hints.width = orig_w_attr.width;
301 hints.height = orig_w_attr.height;
302 mask = CWSaveUnder;
303 w_attr.save_under = True;
304
305 /* inhibit all the decorations */
306 mask |= CWOverrideRedirect;
307 w_attr.override_redirect = True;
308
309 /* Don't set a background, transparent window */
310 mask |= CWBackPixmap;
311 w_attr.background_pixmap = None;
312
313 /* Default input window look */
314 col_background = WhitePixel (gstroke_disp, screen);
315
316 /* no border for the window */
317 #ifdef DEBUG
318 border_width = 5;
319 #else
320 border_width = 0;
321 #endif
322 col_border = BlackPixel (gstroke_disp, screen);
323
324 gstroke_window = XCreateSimpleWindow (gstroke_disp, wind,
325 0, 0,
326 hints.width - 2 * border_width,
327 hints.height - 2 * border_width,
328 border_width,
329 col_border, col_background);
330
331 gstroke_gc = XCreateGC (gstroke_disp, gstroke_window, 0, NULL);
332
333 XSetFunction (gstroke_disp, gstroke_gc, GXinvert);
334
335 XChangeWindowAttributes (gstroke_disp, gstroke_window, mask, &w_attr);
336
337 XSetLineAttributes (gstroke_disp, gstroke_gc, 2, LineSolid,
338 CapButt, JoinMiter);
339 XMapRaised (gstroke_disp, gstroke_window);
340
341 #if 0
342 /*FIXME: is this call really needed? If yes, does it need the real
343 argc and argv? */
344 hints.flags = PPosition | PSize;
345 XSetStandardProperties (gstroke_disp, gstroke_window, "gstroke_test", NULL,
346 (Pixmap)NULL, NULL, 0, &hints);
347
348
349 /* Receive the close window client message */
350 {
351 /* FIXME: is this really needed? If yes, something should be done
352 with wmdelete...*/
353 Atom wmdelete = XInternAtom (gstroke_disp, "WM_DELETE_WINDOW",
354 False);
355 XSetWMProtocols (gstroke_disp, gstroke_window, &wmdelete, True);
356 }
357 #endif
358 }