comparison pidgin/gtkwhiteboard.c @ 15374:5fe8042783c1

Rename gtk/ and libgaim/ to pidgin/ and libpurple/
author Sean Egan <seanegan@gmail.com>
date Sat, 20 Jan 2007 02:32:10 +0000
parents
children d75099d2567e
comparison
equal deleted inserted replaced
15373:f79e0f4df793 15374:5fe8042783c1
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, &gtkwb->width, &gtkwb->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, &gtkwb->brush_size, &gtkwb->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