Mercurial > pidgin
annotate plugins/gestures/stroke-draw.c @ 13901:e40263ba9680
[gaim-migrate @ 16388]
When moving a buddy on AIM, remove the buddy from the server list
before adding it to the new group (it used to be the other way
around).
The downside of this is that, if you're moving an ICQ buddy who
requires authorization, you'll have to rerequest authorization.
The upside of this is that it actually works, and moving an ICQ
buddy won't inadvertently delete them from your list.
committer: Tailor Script <tailor@pidgin.im>
author | Mark Doliner <mark@kingant.net> |
---|---|
date | Sat, 01 Jul 2006 18:39:13 +0000 |
parents | a99a0cc67713 |
children |
rev | line source |
---|---|
4390 | 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...*/ | |
4529
f630a793b9d4
[gaim-migrate @ 4807]
Christian Hammond <chipx86@chipx86.com>
parents:
4432
diff
changeset
|
25 static Display * gstroke_disp = NULL; |
4390 | 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 { | |
9855 | 36 void (*func)(GtkWidget *, void *); |
37 gpointer data; | |
4390 | 38 }; |
39 | |
40 | |
41 /*FIXME: maybe it's better to just make 2 static variables, not a | |
42 structure */ | |
43 struct mouse_position { | |
9855 | 44 struct s_point last_point; |
45 gboolean invalid; | |
4390 | 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 | |
9843 | 60 g_return_if_fail( widget != NULL ); |
61 | |
4390 | 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 { | |
9855 | 100 GtkWidget *widget; |
101 | |
102 g_return_val_if_fail(data != NULL, FALSE); | |
4390 | 103 |
9855 | 104 widget = GTK_WIDGET (data); |
105 record_stroke_segment (widget); | |
106 | |
107 return TRUE; | |
4390 | 108 } |
109 | |
9843 | 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 | |
4390 | 131 static gint |
132 process_event (GtkWidget *widget, GdkEvent *event, gpointer data G_GNUC_UNUSED) | |
133 { | |
134 static GtkWidget *original_widget = NULL; | |
5861
711db8ff91dd
[gaim-migrate @ 6292]
Christian Hammond <chipx86@chipx86.com>
parents:
5227
diff
changeset
|
135 static GdkCursor *cursor = NULL; |
711db8ff91dd
[gaim-migrate @ 6292]
Christian Hammond <chipx86@chipx86.com>
parents:
5227
diff
changeset
|
136 |
4390 | 137 switch (event->type) { |
138 case GDK_BUTTON_PRESS: | |
9843 | 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 } | |
4390 | 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 | |
5861
711db8ff91dd
[gaim-migrate @ 6292]
Christian Hammond <chipx86@chipx86.com>
parents:
5227
diff
changeset
|
156 if (cursor == NULL) |
711db8ff91dd
[gaim-migrate @ 6292]
Christian Hammond <chipx86@chipx86.com>
parents:
5227
diff
changeset
|
157 cursor = gdk_cursor_new(GDK_PENCIL); |
711db8ff91dd
[gaim-migrate @ 6292]
Christian Hammond <chipx86@chipx86.com>
parents:
5227
diff
changeset
|
158 |
4390 | 159 gdk_pointer_grab (widget->window, FALSE, |
5861
711db8ff91dd
[gaim-migrate @ 6292]
Christian Hammond <chipx86@chipx86.com>
parents:
5227
diff
changeset
|
160 GDK_BUTTON_RELEASE_MASK, NULL, cursor, |
4390 | 161 event->button.time); |
8555 | 162 timer_id = g_timeout_add (GSTROKE_TIMEOUT_DURATION, |
4390 | 163 gstroke_timeout, widget); |
164 return TRUE; | |
165 | |
166 case GDK_BUTTON_RELEASE: | |
167 if ((event->button.button != gstroke_get_mouse_button()) | |
4399
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
168 || (original_widget == NULL)) { |
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
169 |
9843 | 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; | |
4399
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
175 } |
4390 | 176 |
177 last_mouse_position.invalid = TRUE; | |
178 original_widget = NULL; | |
8555 | 179 g_source_remove (timer_id); |
4390 | 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 } | |
12204
2c7e79b6d7b2
[gaim-migrate @ 14506]
Richard Laager <rlaager@wiktel.com>
parents:
10814
diff
changeset
|
198 return FALSE; |
2c7e79b6d7b2
[gaim-migrate @ 14506]
Richard Laager <rlaager@wiktel.com>
parents:
10814
diff
changeset
|
199 |
4390 | 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 | |
7631 | 225 guint |
4390 | 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 | |
12667
a99a0cc67713
[gaim-migrate @ 15010]
Richard Laager <rlaager@wiktel.com>
parents:
12204
diff
changeset
|
259 void |
a99a0cc67713
[gaim-migrate @ 15010]
Richard Laager <rlaager@wiktel.com>
parents:
12204
diff
changeset
|
260 gstroke_disable(GtkWidget *widget) |
a99a0cc67713
[gaim-migrate @ 15010]
Richard Laager <rlaager@wiktel.com>
parents:
12204
diff
changeset
|
261 { |
a99a0cc67713
[gaim-migrate @ 15010]
Richard Laager <rlaager@wiktel.com>
parents:
12204
diff
changeset
|
262 g_signal_handlers_disconnect_by_func(G_OBJECT(widget), G_CALLBACK(process_event), NULL); |
a99a0cc67713
[gaim-migrate @ 15010]
Richard Laager <rlaager@wiktel.com>
parents:
12204
diff
changeset
|
263 } |
a99a0cc67713
[gaim-migrate @ 15010]
Richard Laager <rlaager@wiktel.com>
parents:
12204
diff
changeset
|
264 |
4390 | 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 | |
5227
6d1707dc8c3d
[gaim-migrate @ 5597]
Christian Hammond <chipx86@chipx86.com>
parents:
4529
diff
changeset
|
296 gaim_debug(GAIM_DEBUG_MISC, "gestures", "gstroke %s\n", name); |
4390 | 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 | |
10814 | 328 /* This function should be written using GTK+ primitives*/ |
4390 | 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 */ | |
4399
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
367 #if 0 |
4390 | 368 border_width = 5; |
4399
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
369 #endif |
4390 | 370 border_width = 0; |
4399
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
371 |
4390 | 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 } |