Mercurial > pidgin.yaz
comparison gtk/gtkwhiteboard.c @ 20389:e354528c4163
propagate from branch 'im.pidgin.gaim' (head 70ac931e4936c7916eec18a07fe46a0af0fd7403)
to branch 'im.pidgin.rlaager.merging.soc-msnp13-to-svn18164' (head 5b5cde92182d2a922a8e7e6c2308342a5490a8c9)
author | Richard Laager <rlaager@wiktel.com> |
---|---|
date | Sun, 15 Apr 2007 02:10:37 +0000 |
parents | 009db0b357b5 |
children |
comparison
equal
deleted
inserted
replaced
19796:21cb7a79ac7f | 20389:e354528c4163 |
---|---|
1 /* | |
2 * gaim | |
3 * | |
4 * Gaim is the legal property of its developers, whose names are too numerous | |
5 * to list here. Please refer to the COPYRIGHT file distributed with this | |
6 * source distribution. | |
7 * | |
8 * This program is free software; you can redistribute it and/or modify | |
9 * it under the terms of the GNU General Public License as published by | |
10 * the Free Software Foundation; either version 2 of the License, or | |
11 * (at your option) any later version. | |
12 * | |
13 * This program is distributed in the hope that it will be useful, | |
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 * GNU General Public License for more details. | |
17 * | |
18 * You should have received a copy of the GNU General Public License | |
19 * along with this program; if not, write to the Free Software | |
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
21 * | |
22 */ | |
23 | |
24 #include <stdlib.h> | |
25 | |
26 #include "internal.h" | |
27 #include "blist.h" | |
28 #include "debug.h" | |
29 | |
30 #include "gtkwhiteboard.h" | |
31 | |
32 /****************************************************************************** | |
33 * Prototypes | |
34 *****************************************************************************/ | |
35 static void gaim_gtk_whiteboard_create(GaimWhiteboard *wb); | |
36 | |
37 static void gaim_gtk_whiteboard_destroy(GaimWhiteboard *wb); | |
38 static gboolean whiteboard_close_cb(GtkWidget *widget, GdkEvent *event, GaimGtkWhiteboard *gtkwb); | |
39 | |
40 /*static void gaim_gtkwhiteboard_button_start_press(GtkButton *button, gpointer data); */ | |
41 | |
42 static gboolean gaim_gtk_whiteboard_configure_event(GtkWidget *widget, GdkEventConfigure *event, gpointer data); | |
43 static gboolean gaim_gtk_whiteboard_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data); | |
44 | |
45 static gboolean gaim_gtk_whiteboard_brush_down(GtkWidget *widget, GdkEventButton *event, gpointer data); | |
46 static gboolean gaim_gtk_whiteboard_brush_motion(GtkWidget *widget, GdkEventMotion *event, gpointer data); | |
47 static gboolean gaim_gtk_whiteboard_brush_up(GtkWidget *widget, GdkEventButton *event, gpointer data); | |
48 | |
49 static void gaim_gtk_whiteboard_draw_brush_point(GaimWhiteboard *wb, | |
50 int x, int y, int color, int size); | |
51 static void gaim_gtk_whiteboard_draw_brush_line(GaimWhiteboard *wb, int x0, int y0, | |
52 int x1, int y1, int color, int size); | |
53 | |
54 static void gaim_gtk_whiteboard_set_dimensions(GaimWhiteboard *wb, int width, int height); | |
55 static void gaim_gtk_whiteboard_set_brush(GaimWhiteboard *wb, int size, int color); | |
56 static void gaim_gtk_whiteboard_clear(GaimWhiteboard *wb); | |
57 | |
58 static void gaim_gtk_whiteboard_button_clear_press(GtkWidget *widget, gpointer data); | |
59 static void gaim_gtk_whiteboard_button_save_press(GtkWidget *widget, gpointer data); | |
60 | |
61 static void gaim_gtk_whiteboard_set_canvas_as_icon(GaimGtkWhiteboard *gtkwb); | |
62 | |
63 static void gaim_gtk_whiteboard_rgb24_to_rgb48(int color_rgb, GdkColor *color); | |
64 | |
65 static void color_select_dialog(GtkWidget *widget, GaimGtkWhiteboard *gtkwb); | |
66 | |
67 /****************************************************************************** | |
68 * Globals | |
69 *****************************************************************************/ | |
70 /* | |
71 GList *buttonList = NULL; | |
72 GdkColor DefaultColor[PALETTE_NUM_COLORS]; | |
73 */ | |
74 | |
75 static int LastX; /* Tracks last position of the mouse when drawing */ | |
76 static int LastY; | |
77 static int MotionCount; /* Tracks how many brush motions made */ | |
78 static int BrushState = BRUSH_STATE_UP; | |
79 | |
80 static GaimWhiteboardUiOps ui_ops = | |
81 { | |
82 gaim_gtk_whiteboard_create, | |
83 gaim_gtk_whiteboard_destroy, | |
84 gaim_gtk_whiteboard_set_dimensions, | |
85 gaim_gtk_whiteboard_set_brush, | |
86 gaim_gtk_whiteboard_draw_brush_point, | |
87 gaim_gtk_whiteboard_draw_brush_line, | |
88 gaim_gtk_whiteboard_clear | |
89 }; | |
90 | |
91 /****************************************************************************** | |
92 * API | |
93 *****************************************************************************/ | |
94 GaimWhiteboardUiOps *gaim_gtk_whiteboard_get_ui_ops(void) | |
95 { | |
96 return &ui_ops; | |
97 } | |
98 | |
99 static void gaim_gtk_whiteboard_create(GaimWhiteboard *wb) | |
100 { | |
101 GaimBuddy *buddy; | |
102 GtkWidget *window; | |
103 GtkWidget *drawing_area; | |
104 GtkWidget *vbox_controls; | |
105 GtkWidget *hbox_canvas_and_controls; | |
106 | |
107 /* | |
108 -------------------------- | |
109 |[][][][palette[][][][][]| | |
110 |------------------------| | |
111 | canvas | con | | |
112 | | trol| | |
113 | | s | | |
114 | | | | |
115 | | | | |
116 -------------------------- | |
117 */ | |
118 GtkWidget *clear_button; | |
119 GtkWidget *save_button; | |
120 GtkWidget *color_button; | |
121 | |
122 GaimGtkWhiteboard *gtkwb = g_new0(GaimGtkWhiteboard, 1); | |
123 | |
124 gtkwb->wb = wb; | |
125 wb->ui_data = gtkwb; | |
126 | |
127 /* Get dimensions (default?) for the whiteboard canvas */ | |
128 if (!gaim_whiteboard_get_dimensions(wb, >kwb->width, >kwb->height)) | |
129 { | |
130 /* Give some initial board-size */ | |
131 gtkwb->width = 300; | |
132 gtkwb->height = 250; | |
133 } | |
134 | |
135 if (!gaim_whiteboard_get_brush(wb, >kwb->brush_size, >kwb->brush_color)) | |
136 { | |
137 /* Give some initial brush-info */ | |
138 gtkwb->brush_size = 2; | |
139 gtkwb->brush_color = 0xff0000; | |
140 } | |
141 | |
142 window = gtk_window_new(GTK_WINDOW_TOPLEVEL); | |
143 gtkwb->window = window; | |
144 gtk_widget_set_name(window, wb->who); | |
145 | |
146 /* Try and set window title as the name of the buddy, else just use their | |
147 * username | |
148 */ | |
149 buddy = gaim_find_buddy(wb->account, wb->who); | |
150 | |
151 if (buddy != NULL) | |
152 gtk_window_set_title((GtkWindow*)(window), gaim_buddy_get_contact_alias(buddy)); | |
153 else | |
154 gtk_window_set_title((GtkWindow*)(window), wb->who); | |
155 | |
156 gtk_window_set_resizable((GtkWindow*)(window), FALSE); | |
157 | |
158 g_signal_connect(G_OBJECT(window), "delete_event", | |
159 G_CALLBACK(whiteboard_close_cb), gtkwb); | |
160 | |
161 #if 0 | |
162 int i; | |
163 | |
164 GtkWidget *hbox_palette; | |
165 GtkWidget *vbox_palette_above_canvas_and_controls; | |
166 GtkWidget *palette_color_box[PALETTE_NUM_COLORS]; | |
167 | |
168 /* Create vertical box to place palette above the canvas and controls */ | |
169 vbox_palette_above_canvas_and_controls = gtk_vbox_new(FALSE, 0); | |
170 gtk_container_add(GTK_CONTAINER(window), vbox_palette_above_canvas_and_controls); | |
171 gtk_widget_show(vbox_palette_above_canvas_and_controls); | |
172 | |
173 /* Create horizontal box for the palette and all its entries */ | |
174 hbox_palette = gtk_hbox_new(FALSE, 0); | |
175 gtk_box_pack_start(GTK_BOX(vbox_palette_above_canvas_and_controls), | |
176 hbox_palette, FALSE, FALSE, GAIM_HIG_BORDER); | |
177 gtk_widget_show(hbox_palette); | |
178 | |
179 /* Create horizontal box to seperate the canvas from the controls */ | |
180 hbox_canvas_and_controls = gtk_hbox_new(FALSE, 0); | |
181 gtk_box_pack_start(GTK_BOX(vbox_palette_above_canvas_and_controls), | |
182 hbox_canvas_and_controls, FALSE, FALSE, GAIM_HIG_BORDER); | |
183 gtk_widget_show(hbox_canvas_and_controls); | |
184 | |
185 for(i = 0; i < PALETTE_NUM_COLORS; i++) | |
186 { | |
187 palette_color_box[i] = gtk_image_new_from_pixbuf(NULL); | |
188 gtk_widget_set_size_request(palette_color_box[i], gtkwb->width / PALETTE_NUM_COLORS ,32); | |
189 gtk_container_add(GTK_CONTAINER(hbox_palette), palette_color_box[i]); | |
190 | |
191 gtk_widget_show(palette_color_box[i]); | |
192 } | |
193 #endif | |
194 | |
195 hbox_canvas_and_controls = gtk_hbox_new(FALSE, 0); | |
196 gtk_widget_show(hbox_canvas_and_controls); | |
197 | |
198 gtk_container_add(GTK_CONTAINER(window), hbox_canvas_and_controls); | |
199 gtk_container_set_border_width(GTK_CONTAINER(window), GAIM_HIG_BORDER); | |
200 | |
201 /* Create the drawing area */ | |
202 drawing_area = gtk_drawing_area_new(); | |
203 gtkwb->drawing_area = drawing_area; | |
204 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), gtkwb->width, gtkwb->height); | |
205 gtk_box_pack_start(GTK_BOX(hbox_canvas_and_controls), drawing_area, TRUE, TRUE, GAIM_HIG_BOX_SPACE); | |
206 | |
207 gtk_widget_show(drawing_area); | |
208 | |
209 /* Signals used to handle backing pixmap */ | |
210 g_signal_connect(G_OBJECT(drawing_area), "expose_event", | |
211 G_CALLBACK(gaim_gtk_whiteboard_expose_event), gtkwb); | |
212 | |
213 g_signal_connect(G_OBJECT(drawing_area), "configure_event", | |
214 G_CALLBACK(gaim_gtk_whiteboard_configure_event), gtkwb); | |
215 | |
216 /* Event signals */ | |
217 g_signal_connect(G_OBJECT(drawing_area), "button_press_event", | |
218 G_CALLBACK(gaim_gtk_whiteboard_brush_down), gtkwb); | |
219 | |
220 g_signal_connect(G_OBJECT(drawing_area), "motion_notify_event", | |
221 G_CALLBACK(gaim_gtk_whiteboard_brush_motion), gtkwb); | |
222 | |
223 g_signal_connect(G_OBJECT(drawing_area), "button_release_event", | |
224 G_CALLBACK(gaim_gtk_whiteboard_brush_up), gtkwb); | |
225 | |
226 gtk_widget_set_events(drawing_area, | |
227 GDK_EXPOSURE_MASK | | |
228 GDK_LEAVE_NOTIFY_MASK | | |
229 GDK_BUTTON_PRESS_MASK | | |
230 GDK_POINTER_MOTION_MASK | | |
231 GDK_BUTTON_RELEASE_MASK | | |
232 GDK_POINTER_MOTION_HINT_MASK); | |
233 | |
234 /* Create vertical box to contain the controls */ | |
235 vbox_controls = gtk_vbox_new(FALSE, 0); | |
236 gtk_box_pack_start(GTK_BOX(hbox_canvas_and_controls), | |
237 vbox_controls, FALSE, FALSE, GAIM_HIG_BOX_SPACE); | |
238 gtk_widget_show(vbox_controls); | |
239 | |
240 /* Add a clear button */ | |
241 clear_button = gtk_button_new_from_stock(GTK_STOCK_CLEAR); | |
242 gtk_box_pack_start(GTK_BOX(vbox_controls), clear_button, FALSE, FALSE, GAIM_HIG_BOX_SPACE); | |
243 gtk_widget_show(clear_button); | |
244 g_signal_connect(G_OBJECT(clear_button), "clicked", | |
245 G_CALLBACK(gaim_gtk_whiteboard_button_clear_press), gtkwb); | |
246 | |
247 /* Add a save button */ | |
248 save_button = gtk_button_new_from_stock(GTK_STOCK_SAVE); | |
249 gtk_box_pack_start(GTK_BOX(vbox_controls), save_button, FALSE, FALSE, GAIM_HIG_BOX_SPACE); | |
250 gtk_widget_show(save_button); | |
251 | |
252 g_signal_connect(G_OBJECT(save_button), "clicked", | |
253 G_CALLBACK(gaim_gtk_whiteboard_button_save_press), gtkwb); | |
254 | |
255 /* Add a color selector */ | |
256 color_button = gtk_button_new_from_stock(GTK_STOCK_SELECT_COLOR); | |
257 gtk_box_pack_start(GTK_BOX(vbox_controls), color_button, FALSE, FALSE, GAIM_HIG_BOX_SPACE); | |
258 gtk_widget_show(color_button); | |
259 g_signal_connect(G_OBJECT(color_button), "clicked", | |
260 G_CALLBACK(color_select_dialog), gtkwb); | |
261 | |
262 /* Make all this (window) visible */ | |
263 gtk_widget_show(window); | |
264 | |
265 gaim_gtk_whiteboard_set_canvas_as_icon(gtkwb); | |
266 | |
267 /* TODO Specific protocol/whiteboard assignment here? Needs a UI Op? */ | |
268 /* Set default brush size and color */ | |
269 /* | |
270 ds->brush_size = DOODLE_BRUSH_MEDIUM; | |
271 ds->brush_color = 0; | |
272 */ | |
273 } | |
274 | |
275 static void gaim_gtk_whiteboard_destroy(GaimWhiteboard *wb) | |
276 { | |
277 GaimGtkWhiteboard *gtkwb; | |
278 | |
279 g_return_if_fail(wb != NULL); | |
280 gtkwb = wb->ui_data; | |
281 g_return_if_fail(gtkwb != NULL); | |
282 | |
283 /* TODO Ask if user wants to save picture before the session is closed */ | |
284 | |
285 /* Clear graphical memory */ | |
286 if(gtkwb->pixmap) | |
287 { | |
288 g_object_unref(gtkwb->pixmap); | |
289 gtkwb->pixmap = NULL; | |
290 } | |
291 | |
292 if(gtkwb->window) | |
293 { | |
294 gtk_widget_destroy(gtkwb->window); | |
295 gtkwb->window = NULL; | |
296 } | |
297 g_free(gtkwb); | |
298 wb->ui_data = NULL; | |
299 } | |
300 | |
301 static gboolean whiteboard_close_cb(GtkWidget *widget, GdkEvent *event, GaimGtkWhiteboard *gtkwb) | |
302 { | |
303 GaimWhiteboard *wb; | |
304 | |
305 g_return_val_if_fail(gtkwb != NULL, FALSE); | |
306 wb = gtkwb->wb; | |
307 g_return_val_if_fail(wb != NULL, FALSE); | |
308 | |
309 gaim_whiteboard_destroy(wb); | |
310 | |
311 return FALSE; | |
312 } | |
313 | |
314 /* | |
315 * Whiteboard start button on conversation window (move this code to gtkconv? | |
316 * and use new prpl_info member?) | |
317 */ | |
318 #if 0 | |
319 static void gaim_gtkwhiteboard_button_start_press(GtkButton *button, gpointer data) | |
320 { | |
321 GaimConversation *conv = data; | |
322 GaimAccount *account = gaim_conversation_get_account(conv); | |
323 GaimConnection *gc = gaim_account_get_connection(account); | |
324 char *to = (char*)(gaim_conversation_get_name(conv)); | |
325 | |
326 /* Only handle this if local client requested Doodle session (else local | |
327 * client would have sent one) | |
328 */ | |
329 GaimWhiteboard *wb = gaim_whiteboard_get(account, to); | |
330 | |
331 /* Write a local message to this conversation showing that a request for a | |
332 * Doodle session has been made | |
333 */ | |
334 /* XXXX because otherwise gettext will see this string, even though it's | |
335 * in an #if 0 block. Remove the XXXX if you want to use this code. | |
336 * But, it really shouldn't be a Yahoo-specific string. ;) */ | |
337 gaim_conv_im_write(GAIM_CONV_IM(conv), "", XXXX_("Sent Doodle request."), | |
338 GAIM_MESSAGE_NICK | GAIM_MESSAGE_RECV, time(NULL)); | |
339 | |
340 yahoo_doodle_command_send_request(gc, to); | |
341 yahoo_doodle_command_send_ready(gc, to); | |
342 | |
343 /* Insert this 'session' in the list. At this point, it's only a requested | |
344 * session. | |
345 */ | |
346 wb = gaim_whiteboard_create(account, to, DOODLE_STATE_REQUESTING); | |
347 } | |
348 #endif | |
349 | |
350 static gboolean gaim_gtk_whiteboard_configure_event(GtkWidget *widget, GdkEventConfigure *event, gpointer data) | |
351 { | |
352 GaimGtkWhiteboard *gtkwb = (GaimGtkWhiteboard*)data; | |
353 | |
354 GdkPixmap *pixmap = gtkwb->pixmap; | |
355 | |
356 if(pixmap) | |
357 g_object_unref(pixmap); | |
358 | |
359 pixmap = gdk_pixmap_new(widget->window, | |
360 widget->allocation.width, | |
361 widget->allocation.height, | |
362 -1); | |
363 | |
364 gtkwb->pixmap = pixmap; | |
365 | |
366 gdk_draw_rectangle(pixmap, | |
367 widget->style->white_gc, | |
368 TRUE, | |
369 0, 0, | |
370 widget->allocation.width, | |
371 widget->allocation.height); | |
372 | |
373 return TRUE; | |
374 } | |
375 | |
376 static gboolean gaim_gtk_whiteboard_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data) | |
377 { | |
378 GaimGtkWhiteboard *gtkwb = (GaimGtkWhiteboard*)(data); | |
379 GdkPixmap *pixmap = gtkwb->pixmap; | |
380 | |
381 gdk_draw_drawable(widget->window, | |
382 widget->style->fg_gc[GTK_WIDGET_STATE(widget)], | |
383 pixmap, | |
384 event->area.x, event->area.y, | |
385 event->area.x, event->area.y, | |
386 event->area.width, event->area.height); | |
387 | |
388 return FALSE; | |
389 } | |
390 | |
391 static gboolean gaim_gtk_whiteboard_brush_down(GtkWidget *widget, GdkEventButton *event, gpointer data) | |
392 { | |
393 GaimGtkWhiteboard *gtkwb = (GaimGtkWhiteboard*)data; | |
394 GdkPixmap *pixmap = gtkwb->pixmap; | |
395 | |
396 GaimWhiteboard *wb = gtkwb->wb; | |
397 GList *draw_list = wb->draw_list; | |
398 | |
399 if(BrushState != BRUSH_STATE_UP) | |
400 { | |
401 /* Potential double-click DOWN to DOWN? */ | |
402 BrushState = BRUSH_STATE_DOWN; | |
403 | |
404 /* return FALSE; */ | |
405 } | |
406 | |
407 BrushState = BRUSH_STATE_DOWN; | |
408 | |
409 if(event->button == 1 && pixmap != NULL) | |
410 { | |
411 /* Check if draw_list has contents; if so, clear it */ | |
412 if(draw_list) | |
413 { | |
414 gaim_whiteboard_draw_list_destroy(draw_list); | |
415 draw_list = NULL; | |
416 } | |
417 | |
418 /* Set tracking variables */ | |
419 LastX = event->x; | |
420 LastY = event->y; | |
421 | |
422 MotionCount = 0; | |
423 | |
424 draw_list = g_list_append(draw_list, GINT_TO_POINTER(LastX)); | |
425 draw_list = g_list_append(draw_list, GINT_TO_POINTER(LastY)); | |
426 | |
427 gaim_gtk_whiteboard_draw_brush_point(gtkwb->wb, | |
428 event->x, event->y, | |
429 gtkwb->brush_color, gtkwb->brush_size); | |
430 } | |
431 | |
432 wb->draw_list = draw_list; | |
433 | |
434 return TRUE; | |
435 } | |
436 | |
437 static gboolean gaim_gtk_whiteboard_brush_motion(GtkWidget *widget, GdkEventMotion *event, gpointer data) | |
438 { | |
439 int x; | |
440 int y; | |
441 int dx; | |
442 int dy; | |
443 | |
444 GdkModifierType state; | |
445 | |
446 GaimGtkWhiteboard *gtkwb = (GaimGtkWhiteboard*)data; | |
447 GdkPixmap *pixmap = gtkwb->pixmap; | |
448 | |
449 GaimWhiteboard *wb = gtkwb->wb; | |
450 GList *draw_list = wb->draw_list; | |
451 | |
452 if(event->is_hint) | |
453 gdk_window_get_pointer(event->window, &x, &y, &state); | |
454 else | |
455 { | |
456 x = event->x; | |
457 y = event->y; | |
458 state = event->state; | |
459 } | |
460 | |
461 if(state & GDK_BUTTON1_MASK && pixmap != NULL) | |
462 { | |
463 if((BrushState != BRUSH_STATE_DOWN) && (BrushState != BRUSH_STATE_MOTION)) | |
464 { | |
465 gaim_debug_error("gtkwhiteboard", "***Bad brush state transition %d to MOTION\n", BrushState); | |
466 | |
467 BrushState = BRUSH_STATE_MOTION; | |
468 | |
469 return FALSE; | |
470 } | |
471 BrushState = BRUSH_STATE_MOTION; | |
472 | |
473 dx = x - LastX; | |
474 dy = y - LastY; | |
475 | |
476 MotionCount++; | |
477 | |
478 /* NOTE 100 is a temporary constant for how many deltas/motions in a | |
479 * stroke (needs UI Ops?) | |
480 */ | |
481 if(MotionCount == 100) | |
482 { | |
483 draw_list = g_list_append(draw_list, GINT_TO_POINTER(dx)); | |
484 draw_list = g_list_append(draw_list, GINT_TO_POINTER(dy)); | |
485 | |
486 /* Send draw list to the draw_list handler */ | |
487 gaim_whiteboard_send_draw_list(gtkwb->wb, draw_list); | |
488 | |
489 /* The brush stroke is finished, clear the list for another one */ | |
490 if(draw_list) | |
491 { | |
492 gaim_whiteboard_draw_list_destroy(draw_list); | |
493 draw_list = NULL; | |
494 } | |
495 | |
496 /* Reset motion tracking */ | |
497 MotionCount = 0; | |
498 | |
499 draw_list = g_list_append(draw_list, GINT_TO_POINTER(LastX)); | |
500 draw_list = g_list_append(draw_list, GINT_TO_POINTER(LastY)); | |
501 | |
502 dx = x - LastX; | |
503 dy = y - LastY; | |
504 } | |
505 | |
506 draw_list = g_list_append(draw_list, GINT_TO_POINTER(dx)); | |
507 draw_list = g_list_append(draw_list, GINT_TO_POINTER(dy)); | |
508 | |
509 gaim_gtk_whiteboard_draw_brush_line(gtkwb->wb, | |
510 LastX, LastY, | |
511 x, y, | |
512 gtkwb->brush_color, gtkwb->brush_size); | |
513 | |
514 /* Set tracking variables */ | |
515 LastX = x; | |
516 LastY = y; | |
517 } | |
518 | |
519 wb->draw_list = draw_list; | |
520 | |
521 return TRUE; | |
522 } | |
523 | |
524 static gboolean gaim_gtk_whiteboard_brush_up(GtkWidget *widget, GdkEventButton *event, gpointer data) | |
525 { | |
526 GaimGtkWhiteboard *gtkwb = (GaimGtkWhiteboard*)data; | |
527 GdkPixmap *pixmap = gtkwb->pixmap; | |
528 | |
529 GaimWhiteboard *wb = gtkwb->wb; | |
530 GList *draw_list = wb->draw_list; | |
531 | |
532 if((BrushState != BRUSH_STATE_DOWN) && (BrushState != BRUSH_STATE_MOTION)) | |
533 { | |
534 gaim_debug_error("gtkwhiteboard", "***Bad brush state transition %d to UP\n", BrushState); | |
535 | |
536 BrushState = BRUSH_STATE_UP; | |
537 | |
538 return FALSE; | |
539 } | |
540 BrushState = BRUSH_STATE_UP; | |
541 | |
542 if(event->button == 1 && pixmap != NULL) | |
543 { | |
544 /* If the brush was never moved, express two sets of two deltas That's a | |
545 * 'point,' but not for Yahoo! | |
546 */ | |
547 /* if((event->x == LastX) && (event->y == LastY)) */ | |
548 if(MotionCount == 0) | |
549 { | |
550 int index; | |
551 | |
552 /* For Yahoo!, a (0 0) indicates the end of drawing */ | |
553 /* FIXME: Yahoo Doodle specific! */ | |
554 for(index = 0; index < 2; index++) | |
555 { | |
556 draw_list = g_list_append(draw_list, 0); | |
557 draw_list = g_list_append(draw_list, 0); | |
558 } | |
559 } | |
560 /* | |
561 else | |
562 MotionCount = 0; | |
563 */ | |
564 | |
565 /* Send draw list to prpl draw_list handler */ | |
566 gaim_whiteboard_send_draw_list(gtkwb->wb, draw_list); | |
567 | |
568 gaim_gtk_whiteboard_set_canvas_as_icon(gtkwb); | |
569 | |
570 /* The brush stroke is finished, clear the list for another one */ | |
571 if(draw_list) | |
572 gaim_whiteboard_draw_list_destroy(draw_list); | |
573 | |
574 wb->draw_list = NULL; | |
575 } | |
576 | |
577 return TRUE; | |
578 } | |
579 | |
580 static void gaim_gtk_whiteboard_draw_brush_point(GaimWhiteboard *wb, int x, int y, int color, int size) | |
581 { | |
582 GaimGtkWhiteboard *gtkwb = wb->ui_data; | |
583 GtkWidget *widget = gtkwb->drawing_area; | |
584 GdkPixmap *pixmap = gtkwb->pixmap; | |
585 | |
586 GdkRectangle update_rect; | |
587 | |
588 GdkGC *gfx_con = gdk_gc_new(pixmap); | |
589 GdkColor col; | |
590 | |
591 update_rect.x = x - size / 2; | |
592 update_rect.y = y - size / 2; | |
593 update_rect.width = size; | |
594 update_rect.height = size; | |
595 | |
596 /* Interpret and convert color */ | |
597 gaim_gtk_whiteboard_rgb24_to_rgb48(color, &col); | |
598 | |
599 gdk_gc_set_rgb_fg_color(gfx_con, &col); | |
600 /* gdk_gc_set_rgb_bg_color(gfx_con, &col); */ | |
601 | |
602 /* NOTE 5 is a size constant for now... this is because of how poorly the | |
603 * gdk_draw_arc draws small circles | |
604 */ | |
605 if(size < 5) | |
606 { | |
607 /* Draw a rectangle/square */ | |
608 gdk_draw_rectangle(pixmap, | |
609 gfx_con, | |
610 TRUE, | |
611 update_rect.x, update_rect.y, | |
612 update_rect.width, update_rect.height); | |
613 } | |
614 else | |
615 { | |
616 /* Draw a circle */ | |
617 gdk_draw_arc(pixmap, | |
618 gfx_con, | |
619 TRUE, | |
620 update_rect.x, update_rect.y, | |
621 update_rect.width, update_rect.height, | |
622 0, FULL_CIRCLE_DEGREES); | |
623 } | |
624 | |
625 gtk_widget_queue_draw_area(widget, | |
626 update_rect.x, update_rect.y, | |
627 update_rect.width, update_rect.height); | |
628 | |
629 gdk_gc_unref(gfx_con); | |
630 } | |
631 | |
632 /* Uses Bresenham's algorithm (as provided by Wikipedia) */ | |
633 static void gaim_gtk_whiteboard_draw_brush_line(GaimWhiteboard *wb, int x0, int y0, int x1, int y1, int color, int size) | |
634 { | |
635 int temp; | |
636 | |
637 int xstep; | |
638 int ystep; | |
639 | |
640 int dx; | |
641 int dy; | |
642 | |
643 int error; | |
644 int derror; | |
645 | |
646 int x; | |
647 int y; | |
648 | |
649 gboolean steep = abs(y1 - y0) > abs(x1 - x0); | |
650 | |
651 if(steep) | |
652 { | |
653 temp = x0; x0 = y0; y0 = temp; | |
654 temp = x1; x1 = y1; y1 = temp; | |
655 } | |
656 | |
657 dx = abs(x1 - x0); | |
658 dy = abs(y1 - y0); | |
659 | |
660 error = 0; | |
661 derror = dy; | |
662 | |
663 x = x0; | |
664 y = y0; | |
665 | |
666 if(x0 < x1) | |
667 xstep = 1; | |
668 else | |
669 xstep = -1; | |
670 | |
671 if(y0 < y1) | |
672 ystep = 1; | |
673 else | |
674 ystep = -1; | |
675 | |
676 if(steep) | |
677 gaim_gtk_whiteboard_draw_brush_point(wb, y, x, color, size); | |
678 else | |
679 gaim_gtk_whiteboard_draw_brush_point(wb, x, y, color, size); | |
680 | |
681 while(x != x1) | |
682 { | |
683 x += xstep; | |
684 error += derror; | |
685 | |
686 if((error * 2) >= dx) | |
687 { | |
688 y += ystep; | |
689 error -= dx; | |
690 } | |
691 | |
692 if(steep) | |
693 gaim_gtk_whiteboard_draw_brush_point(wb, y, x, color, size); | |
694 else | |
695 gaim_gtk_whiteboard_draw_brush_point(wb, x, y, color, size); | |
696 } | |
697 } | |
698 | |
699 static void gaim_gtk_whiteboard_set_dimensions(GaimWhiteboard *wb, int width, int height) | |
700 { | |
701 GaimGtkWhiteboard *gtkwb = wb->ui_data; | |
702 | |
703 gtkwb->width = width; | |
704 gtkwb->height = height; | |
705 } | |
706 | |
707 static void gaim_gtk_whiteboard_set_brush(GaimWhiteboard *wb, int size, int color) | |
708 { | |
709 GaimGtkWhiteboard *gtkwb = wb->ui_data; | |
710 | |
711 gtkwb->brush_size = size; | |
712 gtkwb->brush_color = color; | |
713 } | |
714 | |
715 static void gaim_gtk_whiteboard_clear(GaimWhiteboard *wb) | |
716 { | |
717 GaimGtkWhiteboard *gtkwb = wb->ui_data; | |
718 GdkPixmap *pixmap = gtkwb->pixmap; | |
719 GtkWidget *drawing_area = gtkwb->drawing_area; | |
720 | |
721 gdk_draw_rectangle(pixmap, | |
722 drawing_area->style->white_gc, | |
723 TRUE, | |
724 0, 0, | |
725 drawing_area->allocation.width, | |
726 drawing_area->allocation.height); | |
727 | |
728 gtk_widget_queue_draw_area(drawing_area, | |
729 0, 0, | |
730 drawing_area->allocation.width, | |
731 drawing_area->allocation.height); | |
732 } | |
733 | |
734 static void gaim_gtk_whiteboard_button_clear_press(GtkWidget *widget, gpointer data) | |
735 { | |
736 GaimGtkWhiteboard *gtkwb = (GaimGtkWhiteboard*)(data); | |
737 | |
738 gaim_gtk_whiteboard_clear(gtkwb->wb); | |
739 | |
740 gaim_gtk_whiteboard_set_canvas_as_icon(gtkwb); | |
741 | |
742 /* Do protocol specific clearing procedures */ | |
743 gaim_whiteboard_send_clear(gtkwb->wb); | |
744 } | |
745 | |
746 static void gaim_gtk_whiteboard_button_save_press(GtkWidget *widget, gpointer data) | |
747 { | |
748 GaimGtkWhiteboard *gtkwb = (GaimGtkWhiteboard*)(data); | |
749 GdkPixbuf *pixbuf; | |
750 | |
751 GtkWidget *dialog; | |
752 | |
753 int result; | |
754 | |
755 #if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */ | |
756 dialog = gtk_file_chooser_dialog_new (_("Save File"), | |
757 GTK_WINDOW(gtkwb->window), | |
758 GTK_FILE_CHOOSER_ACTION_SAVE, | |
759 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, | |
760 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, | |
761 NULL); | |
762 | |
763 /* gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), (gboolean)(TRUE)); */ | |
764 | |
765 /* if(user_edited_a_new_document) */ | |
766 { | |
767 /* gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), default_folder_for_saving); */ | |
768 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "whiteboard.jpg"); | |
769 } | |
770 /* | |
771 else | |
772 gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (dialog), filename_for_existing_document); | |
773 */ | |
774 #else | |
775 dialog = gtk_file_selection_new(_("Save File")); | |
776 gtk_file_selection_set_filename(GTK_FILE_SELECTION(dialog), "whiteboard.jpg"); | |
777 #endif | |
778 result = gtk_dialog_run(GTK_DIALOG(dialog)); | |
779 | |
780 if(result == GTK_RESPONSE_ACCEPT) | |
781 { | |
782 char *filename; | |
783 | |
784 #if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */ | |
785 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); | |
786 #else | |
787 filename = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(dialog))); | |
788 #endif | |
789 gtk_widget_destroy(dialog); | |
790 | |
791 /* Makes an icon from the whiteboard's canvas 'image' */ | |
792 pixbuf = gdk_pixbuf_get_from_drawable(NULL, | |
793 (GdkDrawable*)(gtkwb->pixmap), | |
794 gdk_drawable_get_colormap(gtkwb->pixmap), | |
795 0, 0, | |
796 0, 0, | |
797 gtkwb->width, gtkwb->height); | |
798 | |
799 if(gdk_pixbuf_save(pixbuf, filename, "jpeg", NULL, "quality", "100", NULL)) | |
800 gaim_debug_info("gtkwhiteboard", "File Saved...\n"); | |
801 else | |
802 gaim_debug_info("gtkwhiteboard", "File not Saved... Error\n"); | |
803 g_free(filename); | |
804 } | |
805 else if(result == GTK_RESPONSE_CANCEL) | |
806 { | |
807 gtk_widget_destroy(dialog); | |
808 | |
809 gaim_debug_info("gtkwhiteboard", "File not Saved... Canceled\n"); | |
810 } | |
811 } | |
812 | |
813 static void gaim_gtk_whiteboard_set_canvas_as_icon(GaimGtkWhiteboard *gtkwb) | |
814 { | |
815 GdkPixbuf *pixbuf; | |
816 | |
817 /* Makes an icon from the whiteboard's canvas 'image' */ | |
818 pixbuf = gdk_pixbuf_get_from_drawable(NULL, | |
819 (GdkDrawable*)(gtkwb->pixmap), | |
820 gdk_drawable_get_colormap(gtkwb->pixmap), | |
821 0, 0, | |
822 0, 0, | |
823 gtkwb->width, gtkwb->height); | |
824 | |
825 gtk_window_set_icon((GtkWindow*)(gtkwb->window), pixbuf); | |
826 } | |
827 | |
828 static void gaim_gtk_whiteboard_rgb24_to_rgb48(int color_rgb, GdkColor *color) | |
829 { | |
830 color->red = (color_rgb >> 8) | 0xFF; | |
831 color->green = (color_rgb & 0xFF00) | 0xFF; | |
832 color->blue = ((color_rgb & 0xFF) << 8) | 0xFF; | |
833 } | |
834 | |
835 static void | |
836 change_color_cb(GtkColorSelection *selection, GaimGtkWhiteboard *gtkwb) | |
837 { | |
838 GdkColor color; | |
839 int old_size = 5; | |
840 int old_color = 0; | |
841 int new_color; | |
842 GaimWhiteboard *wb = gtkwb->wb; | |
843 | |
844 gtk_color_selection_get_current_color(selection, &color); | |
845 new_color = (color.red & 0xFF00) << 8; | |
846 new_color |= (color.green & 0xFF00); | |
847 new_color |= (color.blue & 0xFF00) >> 8; | |
848 | |
849 gaim_whiteboard_get_brush(wb, &old_size, &old_color); | |
850 gaim_whiteboard_send_brush(wb, old_size, new_color); | |
851 } | |
852 | |
853 static void color_selection_dialog_destroy(GtkWidget *w, GtkWidget *destroy) | |
854 { | |
855 gtk_widget_destroy(destroy); | |
856 } | |
857 | |
858 static void color_select_dialog(GtkWidget *widget, GaimGtkWhiteboard *gtkwb) | |
859 { | |
860 GdkColor color; | |
861 GtkColorSelectionDialog *dialog; | |
862 | |
863 dialog = (GtkColorSelectionDialog *)gtk_color_selection_dialog_new(_("Select color")); | |
864 | |
865 g_signal_connect(G_OBJECT(dialog->colorsel), "color-changed", | |
866 G_CALLBACK(change_color_cb), gtkwb); | |
867 | |
868 gtk_widget_destroy(dialog->cancel_button); | |
869 gtk_widget_destroy(dialog->help_button); | |
870 | |
871 g_signal_connect(G_OBJECT(dialog->ok_button), "clicked", | |
872 G_CALLBACK(color_selection_dialog_destroy), dialog); | |
873 | |
874 gtk_color_selection_set_has_palette(GTK_COLOR_SELECTION(dialog->colorsel), TRUE); | |
875 | |
876 gaim_gtk_whiteboard_rgb24_to_rgb48(gtkwb->brush_color, &color); | |
877 gtk_color_selection_set_current_color(GTK_COLOR_SELECTION(dialog->colorsel), &color); | |
878 | |
879 gtk_widget_show_all(GTK_WIDGET(dialog)); | |
880 } | |
881 |