Mercurial > pidgin
annotate plugins/gestures/stroke-draw.c @ 4891:cfa045006bec
[gaim-migrate @ 5221]
this saves the blist.xml file to an alternate name, and then moves it, that
way we don't lose your precious buddies if gaim crashes.
Of course, if gaim were to crash, it wouldn't be gaim's fault, it would be
the fault of some external force. This is because gaim is perfect, and
Sean is perfect. Yeah.
This should be done for .gaimrc too, but i'm too tired to do that right now.
committer: Tailor Script <tailor@pidgin.im>
| author | Nathan Walp <nwalp@pidgin.im> |
|---|---|
| date | Tue, 25 Mar 2003 06:35:45 +0000 |
| parents | f630a793b9d4 |
| children | 6d1707dc8c3d |
| 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 { | |
| 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()) | |
|
4399
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
129 || (original_widget == NULL)) { |
|
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
130 |
|
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
131 /* Nice bug when you hold down one button and press another. */ |
|
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
132 /* We'll just cancel the gesture instead. */ |
|
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
133 last_mouse_position.invalid = TRUE; |
|
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
134 original_widget = NULL; |
|
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
135 |
|
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
136 if (timer_id > 0) |
|
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
137 gtk_timeout_remove (timer_id); |
|
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
138 |
|
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
139 gdk_pointer_ungrab (event->button.time); |
|
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
140 timer_id = 0; |
|
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
141 |
|
4529
f630a793b9d4
[gaim-migrate @ 4807]
Christian Hammond <chipx86@chipx86.com>
parents:
4432
diff
changeset
|
142 if (gstroke_draw_strokes() && gstroke_disp != NULL) { |
|
4399
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
143 /* get rid of the invisible stroke window */ |
|
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
144 XUnmapWindow (gstroke_disp, gstroke_window); |
|
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
145 XFlush (gstroke_disp); |
|
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
146 } |
|
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
147 |
|
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
148 break; |
|
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
149 |
|
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
150 } |
| 4390 | 151 |
| 152 last_mouse_position.invalid = TRUE; | |
| 153 original_widget = NULL; | |
| 154 gtk_timeout_remove (timer_id); | |
| 155 gdk_pointer_ungrab (event->button.time); | |
| 156 timer_id = 0; | |
| 157 | |
| 158 { | |
| 159 char result[GSTROKE_MAX_SEQUENCE]; | |
| 160 struct gstroke_metrics *metrics; | |
| 161 | |
| 162 metrics = (struct gstroke_metrics *)g_object_get_data(G_OBJECT (widget), | |
| 163 GSTROKE_METRICS); | |
| 164 if (gstroke_draw_strokes()) { | |
| 165 /* get rid of the invisible stroke window */ | |
| 166 XUnmapWindow (gstroke_disp, gstroke_window); | |
| 167 XFlush (gstroke_disp); | |
| 168 } | |
| 169 | |
| 170 _gstroke_canonical (result, metrics); | |
| 171 gstroke_execute (widget, result); | |
| 172 return FALSE; | |
| 173 } | |
| 174 return TRUE; | |
| 175 default: | |
| 176 break; | |
| 177 } | |
| 178 | |
| 179 return FALSE; | |
| 180 } | |
| 181 | |
| 182 void | |
| 183 gstroke_set_draw_strokes(gboolean draw) | |
| 184 { | |
| 185 draw_strokes = draw; | |
| 186 } | |
| 187 | |
| 188 gboolean | |
| 189 gstroke_draw_strokes(void) | |
| 190 { | |
| 191 return draw_strokes; | |
| 192 } | |
| 193 | |
| 194 void | |
| 195 gstroke_set_mouse_button(gint button) | |
| 196 { | |
| 197 mouse_button = button; | |
| 198 } | |
| 199 | |
| 200 int | |
| 201 gstroke_get_mouse_button(void) | |
| 202 { | |
| 203 return mouse_button; | |
| 204 } | |
| 205 | |
| 206 void | |
| 207 gstroke_enable (GtkWidget *widget) | |
| 208 { | |
| 209 struct gstroke_metrics* | |
| 210 metrics = (struct gstroke_metrics *)g_object_get_data(G_OBJECT(widget), | |
| 211 GSTROKE_METRICS); | |
| 212 if (metrics == NULL) | |
| 213 { | |
| 214 metrics = (struct gstroke_metrics *)g_malloc (sizeof | |
| 215 (struct gstroke_metrics)); | |
| 216 metrics->pointList = NULL; | |
| 217 metrics->min_x = 10000; | |
| 218 metrics->min_y = 10000; | |
| 219 metrics->max_x = 0; | |
| 220 metrics->max_y = 0; | |
| 221 metrics->point_count = 0; | |
| 222 | |
| 223 g_object_set_data(G_OBJECT(widget), GSTROKE_METRICS, metrics); | |
| 224 | |
| 225 g_signal_connect(G_OBJECT(widget), "event", | |
| 226 G_CALLBACK(process_event), NULL); | |
| 227 } | |
| 228 else | |
| 229 _gstroke_init (metrics); | |
| 230 | |
| 231 last_mouse_position.invalid = TRUE; | |
| 232 } | |
| 233 | |
| 234 guint | |
| 235 gstroke_signal_connect (GtkWidget *widget, | |
| 236 const gchar *name, | |
| 237 void (*func)(GtkWidget *widget, void *data), | |
| 238 gpointer data) | |
| 239 { | |
| 240 struct gstroke_func_and_data *func_and_data; | |
| 241 GHashTable *hash_table = | |
| 242 (GHashTable*)g_object_get_data(G_OBJECT(widget), GSTROKE_SIGNALS); | |
| 243 | |
| 244 if (!hash_table) | |
| 245 { | |
| 246 hash_table = g_hash_table_new (g_str_hash, g_str_equal); | |
| 247 g_object_set_data(G_OBJECT(widget), GSTROKE_SIGNALS, | |
| 248 (gpointer)hash_table); | |
| 249 } | |
| 250 func_and_data = g_new (struct gstroke_func_and_data, 1); | |
| 251 func_and_data->func = func; | |
| 252 func_and_data->data = data; | |
| 253 g_hash_table_insert (hash_table, (gpointer)name, (gpointer)func_and_data); | |
| 254 return TRUE; | |
| 255 } | |
| 256 | |
| 257 static void | |
| 258 gstroke_execute (GtkWidget *widget, const gchar *name) | |
| 259 { | |
| 260 | |
| 261 GHashTable *hash_table = | |
| 262 (GHashTable*)g_object_get_data(G_OBJECT(widget), GSTROKE_SIGNALS); | |
| 263 | |
| 264 #if 0 | |
| 265 debug_printf("gstroke %s\n", name); | |
| 266 #endif | |
| 267 | |
| 268 if (hash_table) | |
| 269 { | |
| 270 struct gstroke_func_and_data *fd = | |
| 271 (struct gstroke_func_and_data*)g_hash_table_lookup (hash_table, name); | |
| 272 if (fd) | |
| 273 (*fd->func)(widget, fd->data); | |
| 274 } | |
| 275 } | |
| 276 | |
| 277 void | |
| 278 gstroke_cleanup (GtkWidget *widget) | |
| 279 { | |
| 280 struct gstroke_metrics *metrics; | |
| 281 GHashTable *hash_table = | |
| 282 (GHashTable*)g_object_get_data(G_OBJECT(widget), GSTROKE_SIGNALS); | |
| 283 if (hash_table) | |
| 284 /* FIXME: does this delete the elements too? */ | |
| 285 g_hash_table_destroy (hash_table); | |
| 286 | |
| 287 g_object_steal_data(G_OBJECT(widget), GSTROKE_SIGNALS); | |
| 288 | |
| 289 metrics = (struct gstroke_metrics*)g_object_get_data(G_OBJECT(widget), | |
| 290 GSTROKE_METRICS); | |
| 291 if (metrics) | |
| 292 g_free (metrics); | |
| 293 g_object_steal_data(G_OBJECT(widget), GSTROKE_METRICS); | |
| 294 } | |
| 295 | |
| 296 | |
| 297 /* This function should be written using Gtk+ primitives*/ | |
| 298 static void | |
| 299 gstroke_invisible_window_init (GtkWidget *widget) | |
| 300 { | |
| 301 XSetWindowAttributes w_attr; | |
| 302 XWindowAttributes orig_w_attr; | |
| 303 unsigned long mask, col_border, col_background; | |
| 304 unsigned int border_width; | |
| 305 XSizeHints hints; | |
| 306 Display *disp = GDK_WINDOW_XDISPLAY(widget->window); | |
| 307 Window wind = GDK_WINDOW_XWINDOW (widget->window); | |
| 308 int screen = DefaultScreen (disp); | |
| 309 | |
| 310 if (!gstroke_draw_strokes()) | |
| 311 return; | |
| 312 | |
| 313 gstroke_disp = disp; | |
| 314 | |
| 315 /* X server should save what's underneath */ | |
| 316 XGetWindowAttributes (gstroke_disp, wind, &orig_w_attr); | |
| 317 hints.x = orig_w_attr.x; | |
| 318 hints.y = orig_w_attr.y; | |
| 319 hints.width = orig_w_attr.width; | |
| 320 hints.height = orig_w_attr.height; | |
| 321 mask = CWSaveUnder; | |
| 322 w_attr.save_under = True; | |
| 323 | |
| 324 /* inhibit all the decorations */ | |
| 325 mask |= CWOverrideRedirect; | |
| 326 w_attr.override_redirect = True; | |
| 327 | |
| 328 /* Don't set a background, transparent window */ | |
| 329 mask |= CWBackPixmap; | |
| 330 w_attr.background_pixmap = None; | |
| 331 | |
| 332 /* Default input window look */ | |
| 333 col_background = WhitePixel (gstroke_disp, screen); | |
| 334 | |
| 335 /* no border for the window */ | |
|
4399
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
336 #if 0 |
| 4390 | 337 border_width = 5; |
|
4399
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
338 #endif |
| 4390 | 339 border_width = 0; |
|
4399
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
340 |
| 4390 | 341 col_border = BlackPixel (gstroke_disp, screen); |
| 342 | |
| 343 gstroke_window = XCreateSimpleWindow (gstroke_disp, wind, | |
| 344 0, 0, | |
| 345 hints.width - 2 * border_width, | |
| 346 hints.height - 2 * border_width, | |
| 347 border_width, | |
| 348 col_border, col_background); | |
| 349 | |
| 350 gstroke_gc = XCreateGC (gstroke_disp, gstroke_window, 0, NULL); | |
| 351 | |
| 352 XSetFunction (gstroke_disp, gstroke_gc, GXinvert); | |
| 353 | |
| 354 XChangeWindowAttributes (gstroke_disp, gstroke_window, mask, &w_attr); | |
| 355 | |
| 356 XSetLineAttributes (gstroke_disp, gstroke_gc, 2, LineSolid, | |
| 357 CapButt, JoinMiter); | |
| 358 XMapRaised (gstroke_disp, gstroke_window); | |
| 359 | |
| 360 #if 0 | |
| 361 /*FIXME: is this call really needed? If yes, does it need the real | |
| 362 argc and argv? */ | |
| 363 hints.flags = PPosition | PSize; | |
| 364 XSetStandardProperties (gstroke_disp, gstroke_window, "gstroke_test", NULL, | |
| 365 (Pixmap)NULL, NULL, 0, &hints); | |
| 366 | |
| 367 | |
| 368 /* Receive the close window client message */ | |
| 369 { | |
| 370 /* FIXME: is this really needed? If yes, something should be done | |
| 371 with wmdelete...*/ | |
| 372 Atom wmdelete = XInternAtom (gstroke_disp, "WM_DELETE_WINDOW", | |
| 373 False); | |
| 374 XSetWMProtocols (gstroke_disp, gstroke_window, &wmdelete, True); | |
| 375 } | |
| 376 #endif | |
| 377 } |
