Merged pidgin/main into default
[pidgin-git.git] / pidgin / gtkwhiteboard.c
blob934fdc564f91c342b95893dcc355226099f4eb9d
1 /*
2 * pidgin
4 * Pidgin 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.
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
24 #include "internal.h"
25 #include "buddylist.h"
26 #include "debug.h"
27 #include "pidgin.h"
28 #include "whiteboard.h"
30 #include "gtk3compat.h"
31 #include "gtkwhiteboard.h"
32 #include "gtkutils.h"
34 typedef enum {
35 PIDGIN_WHITEBOARD_BRUSH_UP,
36 PIDGIN_WHITEBOARD_BRUSH_DOWN,
37 PIDGIN_WHITEBOARD_BRUSH_MOTION
38 } PidginWhiteboardBrushState;
40 typedef struct _PidginWhiteboard PidginWhiteboard;
41 typedef struct _PidginWhiteboardPrivate PidginWhiteboardPrivate;
43 struct _PidginWhiteboardPrivate {
44 cairo_t *cr;
45 cairo_surface_t *surface;
48 /**
49 * PidginWhiteboard:
50 * @priv: Internal data
51 * @wb: Backend data for this whiteboard
52 * @window: Window for the Doodle session
53 * @drawing_area: Drawing area
54 * @width: Canvas width
55 * @height: Canvas height
56 * @brush_color: Foreground color
57 * @brush_size: Brush size
59 * A PidginWhiteboard
61 struct _PidginWhiteboard
63 PidginWhiteboardPrivate *priv;
65 PurpleWhiteboard *wb;
67 GtkWidget *window;
68 GtkWidget *drawing_area;
70 int width;
71 int height;
72 int brush_color;
73 int brush_size;
76 /******************************************************************************
77 * Prototypes
78 *****************************************************************************/
79 static void pidgin_whiteboard_create(PurpleWhiteboard *wb);
81 static void pidgin_whiteboard_destroy(PurpleWhiteboard *wb);
82 static gboolean whiteboard_close_cb(GtkWidget *widget, GdkEvent *event, PidginWhiteboard *gtkwb);
84 /*static void pidginwhiteboard_button_start_press(GtkButton *button, gpointer data); */
86 static gboolean pidgin_whiteboard_configure_event(GtkWidget *widget, GdkEventConfigure *event, gpointer data);
87 static gboolean
88 pidgin_whiteboard_draw_event(GtkWidget *widget, cairo_t *cr,
89 gpointer _gtkwb);
91 static gboolean pidgin_whiteboard_brush_down(GtkWidget *widget, GdkEventButton *event, gpointer data);
92 static gboolean pidgin_whiteboard_brush_motion(GtkWidget *widget, GdkEventMotion *event, gpointer data);
93 static gboolean pidgin_whiteboard_brush_up(GtkWidget *widget, GdkEventButton *event, gpointer data);
95 static void pidgin_whiteboard_draw_brush_point(PurpleWhiteboard *wb,
96 int x, int y, int color, int size);
97 static void pidgin_whiteboard_draw_brush_line(PurpleWhiteboard *wb, int x0, int y0,
98 int x1, int y1, int color, int size);
100 static void pidgin_whiteboard_set_dimensions(PurpleWhiteboard *wb, int width, int height);
101 static void pidgin_whiteboard_set_brush(PurpleWhiteboard *wb, int size, int color);
102 static void pidgin_whiteboard_clear(PurpleWhiteboard *wb);
104 static void pidgin_whiteboard_button_clear_press(GtkWidget *widget, gpointer data);
105 static void pidgin_whiteboard_button_save_press(GtkWidget *widget, gpointer data);
107 static void pidgin_whiteboard_set_canvas_as_icon(PidginWhiteboard *gtkwb);
109 static void pidgin_whiteboard_rgb24_to_rgba(int color_rgb, GdkRGBA *color);
111 static void color_selected(GtkColorButton *button, PidginWhiteboard *gtkwb);
113 /******************************************************************************
114 * Globals
115 *****************************************************************************/
117 static int LastX; /* Tracks last position of the mouse when drawing */
118 static int LastY;
119 static int MotionCount; /* Tracks how many brush motions made */
120 static PidginWhiteboardBrushState brush_state = PIDGIN_WHITEBOARD_BRUSH_UP;
122 static PurpleWhiteboardUiOps ui_ops =
124 pidgin_whiteboard_create,
125 pidgin_whiteboard_destroy,
126 pidgin_whiteboard_set_dimensions,
127 pidgin_whiteboard_set_brush,
128 pidgin_whiteboard_draw_brush_point,
129 pidgin_whiteboard_draw_brush_line,
130 pidgin_whiteboard_clear,
131 NULL,
132 NULL,
133 NULL,
134 NULL
137 /******************************************************************************
138 * API
139 *****************************************************************************/
140 PurpleWhiteboardUiOps *pidgin_whiteboard_get_ui_ops(void)
142 return &ui_ops;
145 static void pidgin_whiteboard_create(PurpleWhiteboard *wb)
147 PurpleBuddy *buddy;
148 GtkWidget *window;
149 GtkWidget *drawing_area;
150 GtkWidget *vbox_controls;
151 GtkWidget *hbox_canvas_and_controls;
154 --------------------------
155 |[][][][palette[][][][][]|
156 |------------------------|
157 | canvas | con |
158 | | trol|
159 | | s |
160 | | |
161 | | |
162 --------------------------
164 GtkWidget *clear_button;
165 GtkWidget *save_button;
166 GtkWidget *color_button;
167 GdkRGBA color;
169 PidginWhiteboard *gtkwb = g_new0(PidginWhiteboard, 1);
170 gtkwb->priv = g_new0(PidginWhiteboardPrivate, 1);
172 gtkwb->wb = wb;
173 purple_whiteboard_set_ui_data(wb, gtkwb);
175 /* Get dimensions (default?) for the whiteboard canvas */
176 if (!purple_whiteboard_get_dimensions(wb, &gtkwb->width, &gtkwb->height))
178 /* Give some initial board-size */
179 gtkwb->width = 300;
180 gtkwb->height = 250;
183 if (!purple_whiteboard_get_brush(wb, &gtkwb->brush_size, &gtkwb->brush_color))
185 /* Give some initial brush-info */
186 gtkwb->brush_size = 2;
187 gtkwb->brush_color = 0xff0000;
190 /* Try and set window title as the name of the buddy, else just use their
191 * username
193 buddy = purple_blist_find_buddy(purple_whiteboard_get_account(wb), purple_whiteboard_get_who(wb));
195 window = pidgin_create_window(buddy != NULL ? purple_buddy_get_contact_alias(buddy) : purple_whiteboard_get_who(wb), 0, NULL, FALSE);
196 gtkwb->window = window;
197 gtk_widget_set_name(window, purple_whiteboard_get_who(wb));
199 g_signal_connect(G_OBJECT(window), "delete_event",
200 G_CALLBACK(whiteboard_close_cb), gtkwb);
202 hbox_canvas_and_controls = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
203 gtk_widget_show(hbox_canvas_and_controls);
205 gtk_container_add(GTK_CONTAINER(window), hbox_canvas_and_controls);
206 gtk_container_set_border_width(GTK_CONTAINER(window), PIDGIN_HIG_BORDER);
208 /* Create the drawing area */
209 drawing_area = gtk_drawing_area_new();
210 gtkwb->drawing_area = drawing_area;
211 gtk_widget_set_size_request(GTK_WIDGET(drawing_area), gtkwb->width, gtkwb->height);
212 gtk_box_pack_start(GTK_BOX(hbox_canvas_and_controls), drawing_area, TRUE, TRUE, PIDGIN_HIG_BOX_SPACE);
214 gtk_widget_show(drawing_area);
216 /* Signals used to handle backing pixmap */
217 g_signal_connect(G_OBJECT(drawing_area), "draw",
218 G_CALLBACK(pidgin_whiteboard_draw_event), gtkwb);
220 g_signal_connect(G_OBJECT(drawing_area), "configure-event",
221 G_CALLBACK(pidgin_whiteboard_configure_event), gtkwb);
223 /* Event signals */
224 g_signal_connect(G_OBJECT(drawing_area), "button_press_event",
225 G_CALLBACK(pidgin_whiteboard_brush_down), gtkwb);
227 g_signal_connect(G_OBJECT(drawing_area), "motion_notify_event",
228 G_CALLBACK(pidgin_whiteboard_brush_motion), gtkwb);
230 g_signal_connect(G_OBJECT(drawing_area), "button_release_event",
231 G_CALLBACK(pidgin_whiteboard_brush_up), gtkwb);
233 gtk_widget_set_events(drawing_area,
234 GDK_EXPOSURE_MASK |
235 GDK_LEAVE_NOTIFY_MASK |
236 GDK_BUTTON_PRESS_MASK |
237 GDK_POINTER_MOTION_MASK |
238 GDK_BUTTON_RELEASE_MASK |
239 GDK_POINTER_MOTION_HINT_MASK);
241 /* Create vertical box to contain the controls */
242 vbox_controls = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
243 gtk_box_pack_start(GTK_BOX(hbox_canvas_and_controls),
244 vbox_controls, FALSE, FALSE, PIDGIN_HIG_BOX_SPACE);
245 gtk_widget_show(vbox_controls);
247 /* Add a clear button */
248 clear_button = gtk_button_new_from_stock(GTK_STOCK_CLEAR);
249 gtk_box_pack_start(GTK_BOX(vbox_controls), clear_button, FALSE, FALSE, PIDGIN_HIG_BOX_SPACE);
250 gtk_widget_show(clear_button);
251 g_signal_connect(G_OBJECT(clear_button), "clicked",
252 G_CALLBACK(pidgin_whiteboard_button_clear_press), gtkwb);
254 /* Add a save button */
255 save_button = gtk_button_new_from_stock(GTK_STOCK_SAVE);
256 gtk_box_pack_start(GTK_BOX(vbox_controls), save_button, FALSE, FALSE, PIDGIN_HIG_BOX_SPACE);
257 gtk_widget_show(save_button);
259 g_signal_connect(G_OBJECT(save_button), "clicked",
260 G_CALLBACK(pidgin_whiteboard_button_save_press), gtkwb);
262 /* Add a color selector */
263 color_button = gtk_color_button_new();
264 gtk_box_pack_start(GTK_BOX(vbox_controls), color_button, FALSE, FALSE, PIDGIN_HIG_BOX_SPACE);
265 gtk_widget_show(color_button);
267 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(color_button), FALSE);
268 pidgin_whiteboard_rgb24_to_rgba(gtkwb->brush_color, &color);
269 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(color_button), &color);
271 g_signal_connect(G_OBJECT(color_button), "color-set",
272 G_CALLBACK(color_selected), gtkwb);
274 /* Make all this (window) visible */
275 gtk_widget_show(window);
277 pidgin_whiteboard_set_canvas_as_icon(gtkwb);
279 /* TODO Specific protocol/whiteboard assignment here? Needs a UI Op? */
280 /* Set default brush size and color */
282 ds->brush_size = DOODLE_BRUSH_MEDIUM;
283 ds->brush_color = 0;
287 static void pidgin_whiteboard_destroy(PurpleWhiteboard *wb)
289 PidginWhiteboard *gtkwb;
290 GtkWidget *colour_dialog;
292 g_return_if_fail(wb != NULL);
293 gtkwb = purple_whiteboard_get_ui_data(wb);
294 g_return_if_fail(gtkwb != NULL);
296 /* TODO Ask if user wants to save picture before the session is closed */
298 /* Clear graphical memory */
299 if (gtkwb->priv->cr) {
300 cairo_destroy(gtkwb->priv->cr);
301 gtkwb->priv->cr = NULL;
303 if (gtkwb->priv->surface) {
304 cairo_surface_destroy(gtkwb->priv->surface);
305 gtkwb->priv->surface = NULL;
308 colour_dialog = g_object_get_data(G_OBJECT(gtkwb->window), "colour-dialog");
309 if (colour_dialog) {
310 gtk_widget_destroy(colour_dialog);
311 g_object_set_data(G_OBJECT(gtkwb->window), "colour-dialog", NULL);
314 if(gtkwb->window)
316 gtk_widget_destroy(gtkwb->window);
317 gtkwb->window = NULL;
320 g_free(gtkwb->priv);
321 g_free(gtkwb);
322 purple_whiteboard_set_ui_data(wb, NULL);
325 static gboolean whiteboard_close_cb(GtkWidget *widget, GdkEvent *event, PidginWhiteboard *gtkwb)
327 PurpleWhiteboard *wb;
329 g_return_val_if_fail(gtkwb != NULL, FALSE);
330 wb = gtkwb->wb;
331 g_return_val_if_fail(wb != NULL, FALSE);
333 g_object_unref(wb);
335 return FALSE;
339 * Whiteboard start button on conversation window (move this code to gtkconv?
340 * and use new protocol member?)
342 #if 0
343 static void pidginwhiteboard_button_start_press(GtkButton *button, gpointer data)
345 PurpleConversation *conv = data;
346 PurpleAccount *account = purple_conversation_get_account(conv);
347 PurpleConnection *gc = purple_account_get_connection(account);
348 char *to = (char*)(purple_conversation_get_name(conv));
350 /* Only handle this if local client requested Doodle session (else local
351 * client would have sent one)
353 PurpleWhiteboard *wb = purple_whiteboard_get(account, to);
355 /* Write a local message to this conversation showing that a request for a
356 * Doodle session has been made
358 /* XXXX because otherwise gettext will see this string, even though it's
359 * in an #if 0 block. Remove the XXXX if you want to use this code.
360 * But, it really shouldn't be a Yahoo-specific string. ;) */
361 purple_im_conversation_write_message(PURPLE_CONV_IM(conv), "", XXXX_("Sent Doodle request."),
362 PURPLE_MESSAGE_NICK | PURPLE_MESSAGE_RECV, time(NULL));
364 yahoo_doodle_command_send_request(gc, to);
365 yahoo_doodle_command_send_ready(gc, to);
367 /* Insert this 'session' in the list. At this point, it's only a requested
368 * session.
370 wb = purple_whiteboard_new(account, to, DOODLE_STATE_REQUESTING);
372 #endif
374 static gboolean pidgin_whiteboard_configure_event(GtkWidget *widget, GdkEventConfigure *event, gpointer data)
376 PidginWhiteboard *gtkwb = (PidginWhiteboard*)data;
377 cairo_t *cr;
378 GtkAllocation allocation;
379 GdkRGBA white = {1.0, 1.0, 1.0, 1.0};
381 if (gtkwb->priv->cr)
382 cairo_destroy(gtkwb->priv->cr);
383 if (gtkwb->priv->surface)
384 cairo_surface_destroy(gtkwb->priv->surface);
386 gtk_widget_get_allocation(widget, &allocation);
388 gtkwb->priv->surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24,
389 allocation.width, allocation.height);
390 gtkwb->priv->cr = cr = cairo_create(gtkwb->priv->surface);
391 gdk_cairo_set_source_rgba(cr, &white);
392 cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
393 cairo_fill(cr);
395 return TRUE;
398 static gboolean
399 pidgin_whiteboard_draw_event(GtkWidget *widget, cairo_t *cr,
400 gpointer _gtkwb)
402 PidginWhiteboard *gtkwb = _gtkwb;
404 cairo_set_source_surface(cr, gtkwb->priv->surface, 0, 0);
405 cairo_paint(cr);
407 return FALSE;
410 static gboolean pidgin_whiteboard_brush_down(GtkWidget *widget, GdkEventButton *event, gpointer data)
412 PidginWhiteboard *gtkwb = (PidginWhiteboard*)data;
414 PurpleWhiteboard *wb = gtkwb->wb;
415 GList *draw_list = purple_whiteboard_get_draw_list(wb);
417 if (brush_state != PIDGIN_WHITEBOARD_BRUSH_UP) {
418 /* Potential double-click DOWN to DOWN? */
419 brush_state = PIDGIN_WHITEBOARD_BRUSH_DOWN;
421 /* return FALSE; */
424 brush_state = PIDGIN_WHITEBOARD_BRUSH_DOWN;
426 if(event->button == 1 && gtkwb->priv->cr != NULL)
428 /* Check if draw_list has contents; if so, clear it */
429 if(draw_list)
431 purple_whiteboard_draw_list_destroy(draw_list);
432 draw_list = NULL;
435 /* Set tracking variables */
436 LastX = event->x;
437 LastY = event->y;
439 MotionCount = 0;
441 draw_list = g_list_append(draw_list, GINT_TO_POINTER(LastX));
442 draw_list = g_list_append(draw_list, GINT_TO_POINTER(LastY));
444 pidgin_whiteboard_draw_brush_point(gtkwb->wb,
445 event->x, event->y,
446 gtkwb->brush_color, gtkwb->brush_size);
449 purple_whiteboard_set_draw_list(wb, draw_list);
451 return TRUE;
454 static gboolean pidgin_whiteboard_brush_motion(GtkWidget *widget, GdkEventMotion *event, gpointer data)
456 int x;
457 int y;
458 int dx;
459 int dy;
461 GdkModifierType state;
463 PidginWhiteboard *gtkwb = (PidginWhiteboard*)data;
465 PurpleWhiteboard *wb = gtkwb->wb;
466 GList *draw_list = purple_whiteboard_get_draw_list(wb);
468 if(event->is_hint)
469 gdk_window_get_device_position(event->window, event->device, &x, &y,
470 &state);
471 else
473 x = event->x;
474 y = event->y;
475 state = event->state;
478 if(state & GDK_BUTTON1_MASK && gtkwb->priv->cr != NULL)
480 if ((brush_state != PIDGIN_WHITEBOARD_BRUSH_DOWN) &&
481 (brush_state != PIDGIN_WHITEBOARD_BRUSH_MOTION))
483 purple_debug_error("gtkwhiteboard",
484 "***Bad brush state transition %d to MOTION\n",
485 brush_state);
487 brush_state = PIDGIN_WHITEBOARD_BRUSH_MOTION;
489 return FALSE;
491 brush_state = PIDGIN_WHITEBOARD_BRUSH_MOTION;
493 dx = x - LastX;
494 dy = y - LastY;
496 MotionCount++;
498 /* NOTE 100 is a temporary constant for how many deltas/motions in a
499 * stroke (needs UI Ops?)
501 if(MotionCount == 100)
503 draw_list = g_list_append(draw_list, GINT_TO_POINTER(dx));
504 draw_list = g_list_append(draw_list, GINT_TO_POINTER(dy));
506 /* Send draw list to the draw_list handler */
507 purple_whiteboard_send_draw_list(gtkwb->wb, draw_list);
509 /* The brush stroke is finished, clear the list for another one */
510 if(draw_list)
512 purple_whiteboard_draw_list_destroy(draw_list);
513 draw_list = NULL;
516 /* Reset motion tracking */
517 MotionCount = 0;
519 draw_list = g_list_append(draw_list, GINT_TO_POINTER(LastX));
520 draw_list = g_list_append(draw_list, GINT_TO_POINTER(LastY));
522 dx = x - LastX;
523 dy = y - LastY;
526 draw_list = g_list_append(draw_list, GINT_TO_POINTER(dx));
527 draw_list = g_list_append(draw_list, GINT_TO_POINTER(dy));
529 pidgin_whiteboard_draw_brush_line(gtkwb->wb,
530 LastX, LastY,
531 x, y,
532 gtkwb->brush_color, gtkwb->brush_size);
534 /* Set tracking variables */
535 LastX = x;
536 LastY = y;
539 purple_whiteboard_set_draw_list(wb, draw_list);
541 return TRUE;
544 static gboolean pidgin_whiteboard_brush_up(GtkWidget *widget, GdkEventButton *event, gpointer data)
546 PidginWhiteboard *gtkwb = (PidginWhiteboard*)data;
548 PurpleWhiteboard *wb = gtkwb->wb;
549 GList *draw_list = purple_whiteboard_get_draw_list(wb);
551 if ((brush_state != PIDGIN_WHITEBOARD_BRUSH_DOWN) &&
552 (brush_state != PIDGIN_WHITEBOARD_BRUSH_MOTION))
554 purple_debug_error("gtkwhiteboard",
555 "***Bad brush state transition %d to UP\n",
556 brush_state);
558 brush_state = PIDGIN_WHITEBOARD_BRUSH_UP;
560 return FALSE;
562 brush_state = PIDGIN_WHITEBOARD_BRUSH_UP;
564 if(event->button == 1 && gtkwb->priv->cr != NULL)
566 /* If the brush was never moved, express two sets of two deltas That's a
567 * 'point,' but not for Yahoo!
569 /* if((event->x == LastX) && (event->y == LastY)) */
570 if(MotionCount == 0)
572 int index;
574 /* For Yahoo!, a (0 0) indicates the end of drawing */
575 /* FIXME: Yahoo Doodle specific! */
576 for(index = 0; index < 2; index++)
578 draw_list = g_list_append(draw_list, 0);
579 draw_list = g_list_append(draw_list, 0);
583 else
584 MotionCount = 0;
587 /* Send draw list to protocol draw_list handler */
588 purple_whiteboard_send_draw_list(gtkwb->wb, draw_list);
590 pidgin_whiteboard_set_canvas_as_icon(gtkwb);
592 /* The brush stroke is finished, clear the list for another one */
593 if(draw_list)
594 purple_whiteboard_draw_list_destroy(draw_list);
596 purple_whiteboard_set_draw_list(wb, NULL);
599 return TRUE;
602 static void pidgin_whiteboard_draw_brush_point(PurpleWhiteboard *wb, int x, int y, int color, int size)
604 PidginWhiteboard *gtkwb = purple_whiteboard_get_ui_data(wb);
605 GtkWidget *widget = gtkwb->drawing_area;
606 cairo_t *gfx_con = gtkwb->priv->cr;
607 GdkRGBA rgba;
609 /* Interpret and convert color */
610 pidgin_whiteboard_rgb24_to_rgba(color, &rgba);
611 gdk_cairo_set_source_rgba(gfx_con, &rgba);
613 /* Draw a circle */
614 cairo_arc(gfx_con,
615 x, y,
616 size / 2.0,
617 0.0, 2.0 * M_PI);
618 cairo_fill(gfx_con);
620 gtk_widget_queue_draw_area(widget,
621 x - size / 2, y - size / 2,
622 size, size);
625 /* Uses Bresenham's algorithm (as provided by Wikipedia) */
626 static void pidgin_whiteboard_draw_brush_line(PurpleWhiteboard *wb, int x0, int y0, int x1, int y1, int color, int size)
628 int temp;
630 int xstep;
631 int ystep;
633 int dx;
634 int dy;
636 int error;
637 int derror;
639 int x;
640 int y;
642 gboolean steep = abs(y1 - y0) > abs(x1 - x0);
644 if(steep)
646 temp = x0; x0 = y0; y0 = temp;
647 temp = x1; x1 = y1; y1 = temp;
650 dx = abs(x1 - x0);
651 dy = abs(y1 - y0);
653 error = 0;
654 derror = dy;
656 x = x0;
657 y = y0;
659 if(x0 < x1)
660 xstep = 1;
661 else
662 xstep = -1;
664 if(y0 < y1)
665 ystep = 1;
666 else
667 ystep = -1;
669 if(steep)
670 pidgin_whiteboard_draw_brush_point(wb, y, x, color, size);
671 else
672 pidgin_whiteboard_draw_brush_point(wb, x, y, color, size);
674 while(x != x1)
676 x += xstep;
677 error += derror;
679 if((error * 2) >= dx)
681 y += ystep;
682 error -= dx;
685 if(steep)
686 pidgin_whiteboard_draw_brush_point(wb, y, x, color, size);
687 else
688 pidgin_whiteboard_draw_brush_point(wb, x, y, color, size);
692 static void pidgin_whiteboard_set_dimensions(PurpleWhiteboard *wb, int width, int height)
694 PidginWhiteboard *gtkwb = purple_whiteboard_get_ui_data(wb);
696 gtkwb->width = width;
697 gtkwb->height = height;
700 static void pidgin_whiteboard_set_brush(PurpleWhiteboard *wb, int size, int color)
702 PidginWhiteboard *gtkwb = purple_whiteboard_get_ui_data(wb);
704 gtkwb->brush_size = size;
705 gtkwb->brush_color = color;
708 static void pidgin_whiteboard_clear(PurpleWhiteboard *wb)
710 PidginWhiteboard *gtkwb = purple_whiteboard_get_ui_data(wb);
711 GtkWidget *drawing_area = gtkwb->drawing_area;
712 cairo_t *cr = gtkwb->priv->cr;
713 GtkAllocation allocation;
714 GdkRGBA white = {1.0, 1.0, 1.0, 1.0};
716 gtk_widget_get_allocation(drawing_area, &allocation);
718 gdk_cairo_set_source_rgba(cr, &white);
719 cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
720 cairo_fill(cr);
722 gtk_widget_queue_draw_area(drawing_area, 0, 0,
723 allocation.width, allocation.height);
726 static void pidgin_whiteboard_button_clear_press(GtkWidget *widget, gpointer data)
728 PidginWhiteboard *gtkwb = (PidginWhiteboard*)(data);
730 /* Confirm whether the user really wants to clear */
731 GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(gtkwb->window),
732 GTK_DIALOG_DESTROY_WITH_PARENT,
733 GTK_MESSAGE_QUESTION,
734 GTK_BUTTONS_YES_NO,
735 _("Do you really want to clear?"));
736 gint response = gtk_dialog_run(GTK_DIALOG(dialog));
737 gtk_widget_destroy(dialog);
739 if (response == GTK_RESPONSE_YES)
741 pidgin_whiteboard_clear(gtkwb->wb);
743 pidgin_whiteboard_set_canvas_as_icon(gtkwb);
745 /* Do protocol specific clearing procedures */
746 purple_whiteboard_send_clear(gtkwb->wb);
750 static void
751 pidgin_whiteboard_button_save_press(GtkWidget *widget, gpointer _gtkwb)
753 PidginWhiteboard *gtkwb = _gtkwb;
754 GdkPixbuf *pixbuf;
755 GtkWidget *dialog;
756 int result;
758 dialog = gtk_file_chooser_dialog_new(_("Save File"),
759 GTK_WINDOW(gtkwb->window), GTK_FILE_CHOOSER_ACTION_SAVE,
760 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE,
761 GTK_RESPONSE_ACCEPT, NULL);
763 gtk_file_chooser_set_do_overwrite_confirmation(
764 GTK_FILE_CHOOSER(dialog), TRUE);
766 gtk_file_chooser_set_current_name(
767 GTK_FILE_CHOOSER(dialog), "whiteboard.png");
769 result = gtk_dialog_run(GTK_DIALOG(dialog));
771 if (result == GTK_RESPONSE_ACCEPT) {
772 gboolean success;
773 gchar *filename =
774 gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
776 gtk_widget_destroy(dialog);
778 pixbuf = gdk_pixbuf_get_from_surface(gtkwb->priv->surface, 0, 0,
779 gtkwb->width, gtkwb->height);
781 success = gdk_pixbuf_save(pixbuf, filename, "png", NULL,
782 "compression", "9", NULL);
783 g_object_unref(pixbuf);
784 if (success) {
785 purple_debug_info("gtkwhiteboard",
786 "whiteboard saved to \"%s\"", filename);
787 } else {
788 purple_notify_error(NULL, _("Whiteboard"),
789 _("Unable to save the file"), NULL, NULL);
790 purple_debug_error("gtkwhiteboard", "whiteboard "
791 "couldn't be saved to \"%s\"", filename);
793 g_free(filename);
794 } else if (result == GTK_RESPONSE_CANCEL)
795 gtk_widget_destroy(dialog);
798 static void pidgin_whiteboard_set_canvas_as_icon(PidginWhiteboard *gtkwb)
800 GdkPixbuf *pixbuf;
802 /* Makes an icon from the whiteboard's canvas 'image' */
803 pixbuf = gdk_pixbuf_get_from_surface(gtkwb->priv->surface,
804 0, 0, gtkwb->width, gtkwb->height);
805 gtk_window_set_icon(GTK_WINDOW(gtkwb->window), pixbuf);
806 g_object_unref(pixbuf);
809 static void pidgin_whiteboard_rgb24_to_rgba(int color_rgb, GdkRGBA *color)
811 color->red = ((color_rgb >> 16) & 0xFF) / 255.0f;
812 color->green = ((color_rgb >> 8) & 0xFF) / 255.0f;
813 color->blue = (color_rgb & 0xFF) / 255.0f;
816 static void
817 color_selected(GtkColorButton *button, PidginWhiteboard *gtkwb)
819 GdkRGBA color;
820 PurpleWhiteboard *wb = gtkwb->wb;
821 int old_size, old_color;
822 int new_color;
824 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(button), &color);
826 new_color = (unsigned int)(color.red * 255) << 16;
827 new_color |= (unsigned int)(color.green * 255) << 8;
828 new_color |= (unsigned int)(color.blue * 255);
830 purple_whiteboard_get_brush(wb, &old_size, &old_color);
831 purple_whiteboard_send_brush(wb, old_size, new_color);