| |
1 /* |
| |
2 * pidgin |
| |
3 * |
| |
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. |
| |
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 pidgin_whiteboard_create(PurpleWhiteboard *wb); |
| |
36 |
| |
37 static void pidgin_whiteboard_destroy(PurpleWhiteboard *wb); |
| |
38 static gboolean whiteboard_close_cb(GtkWidget *widget, GdkEvent *event, PidginWhiteboard *gtkwb); |
| |
39 |
| |
40 /*static void pidginwhiteboard_button_start_press(GtkButton *button, gpointer data); */ |
| |
41 |
| |
42 static gboolean pidgin_whiteboard_configure_event(GtkWidget *widget, GdkEventConfigure *event, gpointer data); |
| |
43 static gboolean pidgin_whiteboard_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data); |
| |
44 |
| |
45 static gboolean pidgin_whiteboard_brush_down(GtkWidget *widget, GdkEventButton *event, gpointer data); |
| |
46 static gboolean pidgin_whiteboard_brush_motion(GtkWidget *widget, GdkEventMotion *event, gpointer data); |
| |
47 static gboolean pidgin_whiteboard_brush_up(GtkWidget *widget, GdkEventButton *event, gpointer data); |
| |
48 |
| |
49 static void pidgin_whiteboard_draw_brush_point(PurpleWhiteboard *wb, |
| |
50 int x, int y, int color, int size); |
| |
51 static void pidgin_whiteboard_draw_brush_line(PurpleWhiteboard *wb, int x0, int y0, |
| |
52 int x1, int y1, int color, int size); |
| |
53 |
| |
54 static void pidgin_whiteboard_set_dimensions(PurpleWhiteboard *wb, int width, int height); |
| |
55 static void pidgin_whiteboard_set_brush(PurpleWhiteboard *wb, int size, int color); |
| |
56 static void pidgin_whiteboard_clear(PurpleWhiteboard *wb); |
| |
57 |
| |
58 static void pidgin_whiteboard_button_clear_press(GtkWidget *widget, gpointer data); |
| |
59 static void pidgin_whiteboard_button_save_press(GtkWidget *widget, gpointer data); |
| |
60 |
| |
61 static void pidgin_whiteboard_set_canvas_as_icon(PidginWhiteboard *gtkwb); |
| |
62 |
| |
63 static void pidgin_whiteboard_rgb24_to_rgb48(int color_rgb, GdkColor *color); |
| |
64 |
| |
65 static void color_select_dialog(GtkWidget *widget, PidginWhiteboard *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 PurpleWhiteboardUiOps ui_ops = |
| |
81 { |
| |
82 pidgin_whiteboard_create, |
| |
83 pidgin_whiteboard_destroy, |
| |
84 pidgin_whiteboard_set_dimensions, |
| |
85 pidgin_whiteboard_set_brush, |
| |
86 pidgin_whiteboard_draw_brush_point, |
| |
87 pidgin_whiteboard_draw_brush_line, |
| |
88 pidgin_whiteboard_clear |
| |
89 }; |
| |
90 |
| |
91 /****************************************************************************** |
| |
92 * API |
| |
93 *****************************************************************************/ |
| |
94 PurpleWhiteboardUiOps *pidgin_whiteboard_get_ui_ops(void) |
| |
95 { |
| |
96 return &ui_ops; |
| |
97 } |
| |
98 |
| |
99 static void pidgin_whiteboard_create(PurpleWhiteboard *wb) |
| |
100 { |
| |
101 PurpleBuddy *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 PidginWhiteboard *gtkwb = g_new0(PidginWhiteboard, 1); |
| |
123 |
| |
124 gtkwb->wb = wb; |
| |
125 wb->ui_data = gtkwb; |
| |
126 |
| |
127 /* Get dimensions (default?) for the whiteboard canvas */ |
| |
128 if (!purple_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 (!purple_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 = purple_find_buddy(wb->account, wb->who); |
| |
150 |
| |
151 if (buddy != NULL) |
| |
152 gtk_window_set_title((GtkWindow*)(window), purple_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, PIDGIN_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, PIDGIN_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), PIDGIN_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, PIDGIN_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(pidgin_whiteboard_expose_event), gtkwb); |
| |
212 |
| |
213 g_signal_connect(G_OBJECT(drawing_area), "configure_event", |
| |
214 G_CALLBACK(pidgin_whiteboard_configure_event), gtkwb); |
| |
215 |
| |
216 /* Event signals */ |
| |
217 g_signal_connect(G_OBJECT(drawing_area), "button_press_event", |
| |
218 G_CALLBACK(pidgin_whiteboard_brush_down), gtkwb); |
| |
219 |
| |
220 g_signal_connect(G_OBJECT(drawing_area), "motion_notify_event", |
| |
221 G_CALLBACK(pidgin_whiteboard_brush_motion), gtkwb); |
| |
222 |
| |
223 g_signal_connect(G_OBJECT(drawing_area), "button_release_event", |
| |
224 G_CALLBACK(pidgin_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, PIDGIN_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, PIDGIN_HIG_BOX_SPACE); |
| |
243 gtk_widget_show(clear_button); |
| |
244 g_signal_connect(G_OBJECT(clear_button), "clicked", |
| |
245 G_CALLBACK(pidgin_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, PIDGIN_HIG_BOX_SPACE); |
| |
250 gtk_widget_show(save_button); |
| |
251 |
| |
252 g_signal_connect(G_OBJECT(save_button), "clicked", |
| |
253 G_CALLBACK(pidgin_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, PIDGIN_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 pidgin_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 pidgin_whiteboard_destroy(PurpleWhiteboard *wb) |
| |
276 { |
| |
277 PidginWhiteboard *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, PidginWhiteboard *gtkwb) |
| |
302 { |
| |
303 PurpleWhiteboard *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 purple_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 pidginwhiteboard_button_start_press(GtkButton *button, gpointer data) |
| |
320 { |
| |
321 PurpleConversation *conv = data; |
| |
322 PurpleAccount *account = purple_conversation_get_account(conv); |
| |
323 PurpleConnection *gc = purple_account_get_connection(account); |
| |
324 char *to = (char*)(purple_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 PurpleWhiteboard *wb = purple_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 purple_conv_im_write(PURPLE_CONV_IM(conv), "", XXXX_("Sent Doodle request."), |
| |
338 PURPLE_MESSAGE_NICK | PURPLE_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 = purple_whiteboard_create(account, to, DOODLE_STATE_REQUESTING); |
| |
347 } |
| |
348 #endif |
| |
349 |
| |
350 static gboolean pidgin_whiteboard_configure_event(GtkWidget *widget, GdkEventConfigure *event, gpointer data) |
| |
351 { |
| |
352 PidginWhiteboard *gtkwb = (PidginWhiteboard*)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 pidgin_whiteboard_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data) |
| |
377 { |
| |
378 PidginWhiteboard *gtkwb = (PidginWhiteboard*)(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 pidgin_whiteboard_brush_down(GtkWidget *widget, GdkEventButton *event, gpointer data) |
| |
392 { |
| |
393 PidginWhiteboard *gtkwb = (PidginWhiteboard*)data; |
| |
394 GdkPixmap *pixmap = gtkwb->pixmap; |
| |
395 |
| |
396 PurpleWhiteboard *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 purple_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 pidgin_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 pidgin_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 PidginWhiteboard *gtkwb = (PidginWhiteboard*)data; |
| |
447 GdkPixmap *pixmap = gtkwb->pixmap; |
| |
448 |
| |
449 PurpleWhiteboard *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 purple_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 purple_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 purple_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 pidgin_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 pidgin_whiteboard_brush_up(GtkWidget *widget, GdkEventButton *event, gpointer data) |
| |
525 { |
| |
526 PidginWhiteboard *gtkwb = (PidginWhiteboard*)data; |
| |
527 GdkPixmap *pixmap = gtkwb->pixmap; |
| |
528 |
| |
529 PurpleWhiteboard *wb = gtkwb->wb; |
| |
530 GList *draw_list = wb->draw_list; |
| |
531 |
| |
532 if((BrushState != BRUSH_STATE_DOWN) && (BrushState != BRUSH_STATE_MOTION)) |
| |
533 { |
| |
534 purple_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 purple_whiteboard_send_draw_list(gtkwb->wb, draw_list); |
| |
567 |
| |
568 pidgin_whiteboard_set_canvas_as_icon(gtkwb); |
| |
569 |
| |
570 /* The brush stroke is finished, clear the list for another one */ |
| |
571 if(draw_list) |
| |
572 purple_whiteboard_draw_list_destroy(draw_list); |
| |
573 |
| |
574 wb->draw_list = NULL; |
| |
575 } |
| |
576 |
| |
577 return TRUE; |
| |
578 } |
| |
579 |
| |
580 static void pidgin_whiteboard_draw_brush_point(PurpleWhiteboard *wb, int x, int y, int color, int size) |
| |
581 { |
| |
582 PidginWhiteboard *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 pidgin_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 pidgin_whiteboard_draw_brush_line(PurpleWhiteboard *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 pidgin_whiteboard_draw_brush_point(wb, y, x, color, size); |
| |
678 else |
| |
679 pidgin_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 pidgin_whiteboard_draw_brush_point(wb, y, x, color, size); |
| |
694 else |
| |
695 pidgin_whiteboard_draw_brush_point(wb, x, y, color, size); |
| |
696 } |
| |
697 } |
| |
698 |
| |
699 static void pidgin_whiteboard_set_dimensions(PurpleWhiteboard *wb, int width, int height) |
| |
700 { |
| |
701 PidginWhiteboard *gtkwb = wb->ui_data; |
| |
702 |
| |
703 gtkwb->width = width; |
| |
704 gtkwb->height = height; |
| |
705 } |
| |
706 |
| |
707 static void pidgin_whiteboard_set_brush(PurpleWhiteboard *wb, int size, int color) |
| |
708 { |
| |
709 PidginWhiteboard *gtkwb = wb->ui_data; |
| |
710 |
| |
711 gtkwb->brush_size = size; |
| |
712 gtkwb->brush_color = color; |
| |
713 } |
| |
714 |
| |
715 static void pidgin_whiteboard_clear(PurpleWhiteboard *wb) |
| |
716 { |
| |
717 PidginWhiteboard *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 pidgin_whiteboard_button_clear_press(GtkWidget *widget, gpointer data) |
| |
735 { |
| |
736 PidginWhiteboard *gtkwb = (PidginWhiteboard*)(data); |
| |
737 |
| |
738 pidgin_whiteboard_clear(gtkwb->wb); |
| |
739 |
| |
740 pidgin_whiteboard_set_canvas_as_icon(gtkwb); |
| |
741 |
| |
742 /* Do protocol specific clearing procedures */ |
| |
743 purple_whiteboard_send_clear(gtkwb->wb); |
| |
744 } |
| |
745 |
| |
746 static void pidgin_whiteboard_button_save_press(GtkWidget *widget, gpointer data) |
| |
747 { |
| |
748 PidginWhiteboard *gtkwb = (PidginWhiteboard*)(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 purple_debug_info("gtkwhiteboard", "File Saved...\n"); |
| |
801 else |
| |
802 purple_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 purple_debug_info("gtkwhiteboard", "File not Saved... Canceled\n"); |
| |
810 } |
| |
811 } |
| |
812 |
| |
813 static void pidgin_whiteboard_set_canvas_as_icon(PidginWhiteboard *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 pidgin_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, PidginWhiteboard *gtkwb) |
| |
837 { |
| |
838 GdkColor color; |
| |
839 int old_size = 5; |
| |
840 int old_color = 0; |
| |
841 int new_color; |
| |
842 PurpleWhiteboard *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 purple_whiteboard_get_brush(wb, &old_size, &old_color); |
| |
850 purple_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, PidginWhiteboard *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 pidgin_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 |