Mercurial > pidgin.yaz
annotate plugins/gestures/stroke-draw.c @ 10397:566177590bd5
[gaim-migrate @ 11627]
The typing icon was bigger than my menu text so every time someone started typing the contents of my window moved down and then moved back up when it went away.
I'm going to assume this won't mess up anyone's conversation windows, if it does I'll just figure out some other way to fix it.
Oh, and I guess this is my first commit. Hi Mom.
committer: Tailor Script <tailor@pidgin.im>
author | Etan Reisner <pidgin@unreliablesource.net> |
---|---|
date | Sun, 19 Dec 2004 02:00:44 +0000 |
parents | b23e6f9c4d2e |
children | 364a2ef907ae |
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 return FALSE; | |
198 } | |
199 return TRUE; | |
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 | |
259 guint | |
260 gstroke_signal_connect (GtkWidget *widget, | |
261 const gchar *name, | |
262 void (*func)(GtkWidget *widget, void *data), | |
263 gpointer data) | |
264 { | |
265 struct gstroke_func_and_data *func_and_data; | |
266 GHashTable *hash_table = | |
267 (GHashTable*)g_object_get_data(G_OBJECT(widget), GSTROKE_SIGNALS); | |
268 | |
269 if (!hash_table) | |
270 { | |
271 hash_table = g_hash_table_new (g_str_hash, g_str_equal); | |
272 g_object_set_data(G_OBJECT(widget), GSTROKE_SIGNALS, | |
273 (gpointer)hash_table); | |
274 } | |
275 func_and_data = g_new (struct gstroke_func_and_data, 1); | |
276 func_and_data->func = func; | |
277 func_and_data->data = data; | |
278 g_hash_table_insert (hash_table, (gpointer)name, (gpointer)func_and_data); | |
279 return TRUE; | |
280 } | |
281 | |
282 static void | |
283 gstroke_execute (GtkWidget *widget, const gchar *name) | |
284 { | |
285 | |
286 GHashTable *hash_table = | |
287 (GHashTable*)g_object_get_data(G_OBJECT(widget), GSTROKE_SIGNALS); | |
288 | |
289 #if 0 | |
5227
6d1707dc8c3d
[gaim-migrate @ 5597]
Christian Hammond <chipx86@chipx86.com>
parents:
4529
diff
changeset
|
290 gaim_debug(GAIM_DEBUG_MISC, "gestures", "gstroke %s\n", name); |
4390 | 291 #endif |
292 | |
293 if (hash_table) | |
294 { | |
295 struct gstroke_func_and_data *fd = | |
296 (struct gstroke_func_and_data*)g_hash_table_lookup (hash_table, name); | |
297 if (fd) | |
298 (*fd->func)(widget, fd->data); | |
299 } | |
300 } | |
301 | |
302 void | |
303 gstroke_cleanup (GtkWidget *widget) | |
304 { | |
305 struct gstroke_metrics *metrics; | |
306 GHashTable *hash_table = | |
307 (GHashTable*)g_object_get_data(G_OBJECT(widget), GSTROKE_SIGNALS); | |
308 if (hash_table) | |
309 /* FIXME: does this delete the elements too? */ | |
310 g_hash_table_destroy (hash_table); | |
311 | |
312 g_object_steal_data(G_OBJECT(widget), GSTROKE_SIGNALS); | |
313 | |
314 metrics = (struct gstroke_metrics*)g_object_get_data(G_OBJECT(widget), | |
315 GSTROKE_METRICS); | |
316 if (metrics) | |
317 g_free (metrics); | |
318 g_object_steal_data(G_OBJECT(widget), GSTROKE_METRICS); | |
319 } | |
320 | |
321 | |
322 /* This function should be written using Gtk+ primitives*/ | |
323 static void | |
324 gstroke_invisible_window_init (GtkWidget *widget) | |
325 { | |
326 XSetWindowAttributes w_attr; | |
327 XWindowAttributes orig_w_attr; | |
328 unsigned long mask, col_border, col_background; | |
329 unsigned int border_width; | |
330 XSizeHints hints; | |
331 Display *disp = GDK_WINDOW_XDISPLAY(widget->window); | |
332 Window wind = GDK_WINDOW_XWINDOW (widget->window); | |
333 int screen = DefaultScreen (disp); | |
334 | |
335 if (!gstroke_draw_strokes()) | |
336 return; | |
337 | |
338 gstroke_disp = disp; | |
339 | |
340 /* X server should save what's underneath */ | |
341 XGetWindowAttributes (gstroke_disp, wind, &orig_w_attr); | |
342 hints.x = orig_w_attr.x; | |
343 hints.y = orig_w_attr.y; | |
344 hints.width = orig_w_attr.width; | |
345 hints.height = orig_w_attr.height; | |
346 mask = CWSaveUnder; | |
347 w_attr.save_under = True; | |
348 | |
349 /* inhibit all the decorations */ | |
350 mask |= CWOverrideRedirect; | |
351 w_attr.override_redirect = True; | |
352 | |
353 /* Don't set a background, transparent window */ | |
354 mask |= CWBackPixmap; | |
355 w_attr.background_pixmap = None; | |
356 | |
357 /* Default input window look */ | |
358 col_background = WhitePixel (gstroke_disp, screen); | |
359 | |
360 /* no border for the window */ | |
4399
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
361 #if 0 |
4390 | 362 border_width = 5; |
4399
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
363 #endif |
4390 | 364 border_width = 0; |
4399
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
365 |
4390 | 366 col_border = BlackPixel (gstroke_disp, screen); |
367 | |
368 gstroke_window = XCreateSimpleWindow (gstroke_disp, wind, | |
369 0, 0, | |
370 hints.width - 2 * border_width, | |
371 hints.height - 2 * border_width, | |
372 border_width, | |
373 col_border, col_background); | |
374 | |
375 gstroke_gc = XCreateGC (gstroke_disp, gstroke_window, 0, NULL); | |
376 | |
377 XSetFunction (gstroke_disp, gstroke_gc, GXinvert); | |
378 | |
379 XChangeWindowAttributes (gstroke_disp, gstroke_window, mask, &w_attr); | |
380 | |
381 XSetLineAttributes (gstroke_disp, gstroke_gc, 2, LineSolid, | |
382 CapButt, JoinMiter); | |
383 XMapRaised (gstroke_disp, gstroke_window); | |
384 | |
385 #if 0 | |
386 /*FIXME: is this call really needed? If yes, does it need the real | |
387 argc and argv? */ | |
388 hints.flags = PPosition | PSize; | |
389 XSetStandardProperties (gstroke_disp, gstroke_window, "gstroke_test", NULL, | |
390 (Pixmap)NULL, NULL, 0, &hints); | |
391 | |
392 | |
393 /* Receive the close window client message */ | |
394 { | |
395 /* FIXME: is this really needed? If yes, something should be done | |
396 with wmdelete...*/ | |
397 Atom wmdelete = XInternAtom (gstroke_disp, "WM_DELETE_WINDOW", | |
398 False); | |
399 XSetWMProtocols (gstroke_disp, gstroke_window, &wmdelete, True); | |
400 } | |
401 #endif | |
402 } |