Mercurial > pidgin
annotate plugins/gestures/stroke-draw.c @ 9584:fe35f55ee984
[gaim-migrate @ 10427]
" When joining a jabber conference many jabber servers
send a recap of the last 20 or so messages. If you
have sounds enabled, this will result in either 20
sounds in row, or worse if mixing is available, a
horrible mix of 20 overlapping sounds. These recap
messages can be identifed be the presence of the
"jabber:x:delay". This patch identifies delayed
messages, passes that information through flags from
the prpl to the core, and then on to the gui. Detailed
changes:
Add GAIM_MESSAGE_DELAYED to GaimMessageFlags to
indicate a delayed message.
Change gtkconv.c to not play sounds when either
GAIM_MESSAGE_DELAYED or GAIM_MESSAGE_SYSTEM are set.
Add GaimConvChatFlags, parallel to GaimConvImFlags, to
pass flags from protocols to core. Currently contains
two flags:
GAIM_CONV_CHAT_WHISPER
GAIM_CONV_CHAT_DELAYED
Change fourth arg of serv_got_chat_in() from "int
whisper" to "GaimConvChatFlags chatflags".
Change jabber prpl to set delayed flag when the
"jabber:x:delay" element is present. Change toc
protocol since it uses the whisper flag." --Nathan Fredrickson
Date: 2004-07-24 00:49
Sender: marv_sfAccepting Donations
Logged In: YES
user_id=790708
I'm not sure I like naming the flags "DELAYED". I mean
that's okay inside jabber since that's what the jabber
protocol refers to it as, but for the the GAIM_*_DELAYED
flags, I think they should be named something else.
I thought about NOSOUND, but I decided that was wrong,
because the flag should say what kind of message it is, not
what to do with it, that's up to the UI to decide.
What's up with not playing sounds on GAIM_MESSAGE_SYSTEM?
This sounds unrelated to this. Are there times when we want
to play sounds on system messages?
Date: 2004-07-24 09:13
Sender: noif
Logged In: YES
user_id=365548
I purposely did not use a name that implied what the UI
should do with the flag. The only characteristic that makes
these messages unique is that they've been stored in the
server for some period of time and are not current. I'm
open to a better flag name than "DELAYED"... I thought about
"RECAP", but that seemed less generalized than "DELAYED".
As for not playing sounds on GAIM_MESSAGE_SYSTEM, that can
be removed if it's controversial. I think I slipped that
in since the setting of the topic was still playing a sound
every time you joined a jabber conference.
I think we can change the flag name ourselves if something else is better.
committer: Tailor Script <tailor@pidgin.im>
| author | Luke Schierer <lschiere@pidgin.im> |
|---|---|
| date | Sat, 24 Jul 2004 15:18:32 +0000 |
| parents | e0535ba0d667 |
| children | 19fd43d52d18 |
| 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; | |
|
5861
711db8ff91dd
[gaim-migrate @ 6292]
Christian Hammond <chipx86@chipx86.com>
parents:
5227
diff
changeset
|
108 static GdkCursor *cursor = NULL; |
|
711db8ff91dd
[gaim-migrate @ 6292]
Christian Hammond <chipx86@chipx86.com>
parents:
5227
diff
changeset
|
109 |
| 4390 | 110 switch (event->type) { |
| 111 case GDK_BUTTON_PRESS: | |
| 112 if (event->button.button != gstroke_get_mouse_button()) | |
|
5861
711db8ff91dd
[gaim-migrate @ 6292]
Christian Hammond <chipx86@chipx86.com>
parents:
5227
diff
changeset
|
113 break; |
| 4390 | 114 |
| 115 original_widget = widget; /* remeber the widget where | |
| 116 the stroke started */ | |
| 117 | |
| 118 gstroke_invisible_window_init (widget); | |
| 119 | |
| 120 record_stroke_segment (widget); | |
| 121 | |
|
5861
711db8ff91dd
[gaim-migrate @ 6292]
Christian Hammond <chipx86@chipx86.com>
parents:
5227
diff
changeset
|
122 if (cursor == NULL) |
|
711db8ff91dd
[gaim-migrate @ 6292]
Christian Hammond <chipx86@chipx86.com>
parents:
5227
diff
changeset
|
123 cursor = gdk_cursor_new(GDK_PENCIL); |
|
711db8ff91dd
[gaim-migrate @ 6292]
Christian Hammond <chipx86@chipx86.com>
parents:
5227
diff
changeset
|
124 |
| 4390 | 125 gdk_pointer_grab (widget->window, FALSE, |
|
5861
711db8ff91dd
[gaim-migrate @ 6292]
Christian Hammond <chipx86@chipx86.com>
parents:
5227
diff
changeset
|
126 GDK_BUTTON_RELEASE_MASK, NULL, cursor, |
| 4390 | 127 event->button.time); |
| 8555 | 128 timer_id = g_timeout_add (GSTROKE_TIMEOUT_DURATION, |
| 4390 | 129 gstroke_timeout, widget); |
| 130 return TRUE; | |
| 131 | |
| 132 case GDK_BUTTON_RELEASE: | |
| 133 if ((event->button.button != gstroke_get_mouse_button()) | |
|
4399
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 /* Nice bug when you hold down one button and press another. */ |
|
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
137 /* We'll just cancel the gesture instead. */ |
|
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
138 last_mouse_position.invalid = TRUE; |
|
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
139 original_widget = NULL; |
|
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
140 |
|
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
141 if (timer_id > 0) |
| 8555 | 142 g_source_remove (timer_id); |
|
4399
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
143 |
|
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
144 gdk_pointer_ungrab (event->button.time); |
|
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
145 timer_id = 0; |
|
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
146 |
|
4529
f630a793b9d4
[gaim-migrate @ 4807]
Christian Hammond <chipx86@chipx86.com>
parents:
4432
diff
changeset
|
147 if (gstroke_draw_strokes() && gstroke_disp != NULL) { |
|
4399
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
148 /* get rid of the invisible stroke window */ |
|
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
149 XUnmapWindow (gstroke_disp, gstroke_window); |
|
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
150 XFlush (gstroke_disp); |
|
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
151 } |
|
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
152 |
|
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
153 break; |
|
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
154 |
|
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
155 } |
| 4390 | 156 |
| 157 last_mouse_position.invalid = TRUE; | |
| 158 original_widget = NULL; | |
| 8555 | 159 g_source_remove (timer_id); |
| 4390 | 160 gdk_pointer_ungrab (event->button.time); |
| 161 timer_id = 0; | |
| 162 | |
| 163 { | |
| 164 char result[GSTROKE_MAX_SEQUENCE]; | |
| 165 struct gstroke_metrics *metrics; | |
| 166 | |
| 167 metrics = (struct gstroke_metrics *)g_object_get_data(G_OBJECT (widget), | |
| 168 GSTROKE_METRICS); | |
| 169 if (gstroke_draw_strokes()) { | |
| 170 /* get rid of the invisible stroke window */ | |
| 171 XUnmapWindow (gstroke_disp, gstroke_window); | |
| 172 XFlush (gstroke_disp); | |
| 173 } | |
| 174 | |
| 175 _gstroke_canonical (result, metrics); | |
| 176 gstroke_execute (widget, result); | |
| 177 return FALSE; | |
| 178 } | |
| 179 return TRUE; | |
| 180 default: | |
| 181 break; | |
| 182 } | |
| 183 | |
| 184 return FALSE; | |
| 185 } | |
| 186 | |
| 187 void | |
| 188 gstroke_set_draw_strokes(gboolean draw) | |
| 189 { | |
| 190 draw_strokes = draw; | |
| 191 } | |
| 192 | |
| 193 gboolean | |
| 194 gstroke_draw_strokes(void) | |
| 195 { | |
| 196 return draw_strokes; | |
| 197 } | |
| 198 | |
| 199 void | |
| 200 gstroke_set_mouse_button(gint button) | |
| 201 { | |
| 202 mouse_button = button; | |
| 203 } | |
| 204 | |
| 7631 | 205 guint |
| 4390 | 206 gstroke_get_mouse_button(void) |
| 207 { | |
| 208 return mouse_button; | |
| 209 } | |
| 210 | |
| 211 void | |
| 212 gstroke_enable (GtkWidget *widget) | |
| 213 { | |
| 214 struct gstroke_metrics* | |
| 215 metrics = (struct gstroke_metrics *)g_object_get_data(G_OBJECT(widget), | |
| 216 GSTROKE_METRICS); | |
| 217 if (metrics == NULL) | |
| 218 { | |
| 219 metrics = (struct gstroke_metrics *)g_malloc (sizeof | |
| 220 (struct gstroke_metrics)); | |
| 221 metrics->pointList = NULL; | |
| 222 metrics->min_x = 10000; | |
| 223 metrics->min_y = 10000; | |
| 224 metrics->max_x = 0; | |
| 225 metrics->max_y = 0; | |
| 226 metrics->point_count = 0; | |
| 227 | |
| 228 g_object_set_data(G_OBJECT(widget), GSTROKE_METRICS, metrics); | |
| 229 | |
| 230 g_signal_connect(G_OBJECT(widget), "event", | |
| 231 G_CALLBACK(process_event), NULL); | |
| 232 } | |
| 233 else | |
| 234 _gstroke_init (metrics); | |
| 235 | |
| 236 last_mouse_position.invalid = TRUE; | |
| 237 } | |
| 238 | |
| 239 guint | |
| 240 gstroke_signal_connect (GtkWidget *widget, | |
| 241 const gchar *name, | |
| 242 void (*func)(GtkWidget *widget, void *data), | |
| 243 gpointer data) | |
| 244 { | |
| 245 struct gstroke_func_and_data *func_and_data; | |
| 246 GHashTable *hash_table = | |
| 247 (GHashTable*)g_object_get_data(G_OBJECT(widget), GSTROKE_SIGNALS); | |
| 248 | |
| 249 if (!hash_table) | |
| 250 { | |
| 251 hash_table = g_hash_table_new (g_str_hash, g_str_equal); | |
| 252 g_object_set_data(G_OBJECT(widget), GSTROKE_SIGNALS, | |
| 253 (gpointer)hash_table); | |
| 254 } | |
| 255 func_and_data = g_new (struct gstroke_func_and_data, 1); | |
| 256 func_and_data->func = func; | |
| 257 func_and_data->data = data; | |
| 258 g_hash_table_insert (hash_table, (gpointer)name, (gpointer)func_and_data); | |
| 259 return TRUE; | |
| 260 } | |
| 261 | |
| 262 static void | |
| 263 gstroke_execute (GtkWidget *widget, const gchar *name) | |
| 264 { | |
| 265 | |
| 266 GHashTable *hash_table = | |
| 267 (GHashTable*)g_object_get_data(G_OBJECT(widget), GSTROKE_SIGNALS); | |
| 268 | |
| 269 #if 0 | |
|
5227
6d1707dc8c3d
[gaim-migrate @ 5597]
Christian Hammond <chipx86@chipx86.com>
parents:
4529
diff
changeset
|
270 gaim_debug(GAIM_DEBUG_MISC, "gestures", "gstroke %s\n", name); |
| 4390 | 271 #endif |
| 272 | |
| 273 if (hash_table) | |
| 274 { | |
| 275 struct gstroke_func_and_data *fd = | |
| 276 (struct gstroke_func_and_data*)g_hash_table_lookup (hash_table, name); | |
| 277 if (fd) | |
| 278 (*fd->func)(widget, fd->data); | |
| 279 } | |
| 280 } | |
| 281 | |
| 282 void | |
| 283 gstroke_cleanup (GtkWidget *widget) | |
| 284 { | |
| 285 struct gstroke_metrics *metrics; | |
| 286 GHashTable *hash_table = | |
| 287 (GHashTable*)g_object_get_data(G_OBJECT(widget), GSTROKE_SIGNALS); | |
| 288 if (hash_table) | |
| 289 /* FIXME: does this delete the elements too? */ | |
| 290 g_hash_table_destroy (hash_table); | |
| 291 | |
| 292 g_object_steal_data(G_OBJECT(widget), GSTROKE_SIGNALS); | |
| 293 | |
| 294 metrics = (struct gstroke_metrics*)g_object_get_data(G_OBJECT(widget), | |
| 295 GSTROKE_METRICS); | |
| 296 if (metrics) | |
| 297 g_free (metrics); | |
| 298 g_object_steal_data(G_OBJECT(widget), GSTROKE_METRICS); | |
| 299 } | |
| 300 | |
| 301 | |
| 302 /* This function should be written using Gtk+ primitives*/ | |
| 303 static void | |
| 304 gstroke_invisible_window_init (GtkWidget *widget) | |
| 305 { | |
| 306 XSetWindowAttributes w_attr; | |
| 307 XWindowAttributes orig_w_attr; | |
| 308 unsigned long mask, col_border, col_background; | |
| 309 unsigned int border_width; | |
| 310 XSizeHints hints; | |
| 311 Display *disp = GDK_WINDOW_XDISPLAY(widget->window); | |
| 312 Window wind = GDK_WINDOW_XWINDOW (widget->window); | |
| 313 int screen = DefaultScreen (disp); | |
| 314 | |
| 315 if (!gstroke_draw_strokes()) | |
| 316 return; | |
| 317 | |
| 318 gstroke_disp = disp; | |
| 319 | |
| 320 /* X server should save what's underneath */ | |
| 321 XGetWindowAttributes (gstroke_disp, wind, &orig_w_attr); | |
| 322 hints.x = orig_w_attr.x; | |
| 323 hints.y = orig_w_attr.y; | |
| 324 hints.width = orig_w_attr.width; | |
| 325 hints.height = orig_w_attr.height; | |
| 326 mask = CWSaveUnder; | |
| 327 w_attr.save_under = True; | |
| 328 | |
| 329 /* inhibit all the decorations */ | |
| 330 mask |= CWOverrideRedirect; | |
| 331 w_attr.override_redirect = True; | |
| 332 | |
| 333 /* Don't set a background, transparent window */ | |
| 334 mask |= CWBackPixmap; | |
| 335 w_attr.background_pixmap = None; | |
| 336 | |
| 337 /* Default input window look */ | |
| 338 col_background = WhitePixel (gstroke_disp, screen); | |
| 339 | |
| 340 /* no border for the window */ | |
|
4399
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
341 #if 0 |
| 4390 | 342 border_width = 5; |
|
4399
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
343 #endif |
| 4390 | 344 border_width = 0; |
|
4399
ce8d35b435de
[gaim-migrate @ 4668]
Christian Hammond <chipx86@chipx86.com>
parents:
4390
diff
changeset
|
345 |
| 4390 | 346 col_border = BlackPixel (gstroke_disp, screen); |
| 347 | |
| 348 gstroke_window = XCreateSimpleWindow (gstroke_disp, wind, | |
| 349 0, 0, | |
| 350 hints.width - 2 * border_width, | |
| 351 hints.height - 2 * border_width, | |
| 352 border_width, | |
| 353 col_border, col_background); | |
| 354 | |
| 355 gstroke_gc = XCreateGC (gstroke_disp, gstroke_window, 0, NULL); | |
| 356 | |
| 357 XSetFunction (gstroke_disp, gstroke_gc, GXinvert); | |
| 358 | |
| 359 XChangeWindowAttributes (gstroke_disp, gstroke_window, mask, &w_attr); | |
| 360 | |
| 361 XSetLineAttributes (gstroke_disp, gstroke_gc, 2, LineSolid, | |
| 362 CapButt, JoinMiter); | |
| 363 XMapRaised (gstroke_disp, gstroke_window); | |
| 364 | |
| 365 #if 0 | |
| 366 /*FIXME: is this call really needed? If yes, does it need the real | |
| 367 argc and argv? */ | |
| 368 hints.flags = PPosition | PSize; | |
| 369 XSetStandardProperties (gstroke_disp, gstroke_window, "gstroke_test", NULL, | |
| 370 (Pixmap)NULL, NULL, 0, &hints); | |
| 371 | |
| 372 | |
| 373 /* Receive the close window client message */ | |
| 374 { | |
| 375 /* FIXME: is this really needed? If yes, something should be done | |
| 376 with wmdelete...*/ | |
| 377 Atom wmdelete = XInternAtom (gstroke_disp, "WM_DELETE_WINDOW", | |
| 378 False); | |
| 379 XSetWMProtocols (gstroke_disp, gstroke_window, &wmdelete, True); | |
| 380 } | |
| 381 #endif | |
| 382 } |
