Re-arrange whiteboard to avoid prototypes.

Tue, 06 Aug 2019 03:18:32 -0400

author
Elliott Sales de Andrade <qulogic@pidgin.im>
date
Tue, 06 Aug 2019 03:18:32 -0400
changeset 39763
c819e240b60e
parent 39756
2c5f3e509f97
child 39764
abfcdf65a268

Re-arrange whiteboard to avoid prototypes.

pidgin/gtkwhiteboard.c file | annotate | diff | comparison | revisions
--- a/pidgin/gtkwhiteboard.c	Tue Aug 06 01:49:55 2019 -0400
+++ b/pidgin/gtkwhiteboard.c	Tue Aug 06 03:18:32 2019 -0400
@@ -74,43 +74,6 @@
 };
 
 /******************************************************************************
- * Prototypes
- *****************************************************************************/
-static void pidgin_whiteboard_create(PurpleWhiteboard *wb);
-
-static void pidgin_whiteboard_destroy(PurpleWhiteboard *wb);
-static gboolean whiteboard_close_cb(GtkWidget *widget, GdkEvent *event, PidginWhiteboard *gtkwb);
-
-/*static void pidginwhiteboard_button_start_press(GtkButton *button, gpointer data); */
-
-static gboolean pidgin_whiteboard_configure_event(GtkWidget *widget, GdkEventConfigure *event, gpointer data);
-static gboolean
-pidgin_whiteboard_draw_event(GtkWidget *widget, cairo_t *cr,
-	gpointer _gtkwb);
-
-static gboolean pidgin_whiteboard_brush_down(GtkWidget *widget, GdkEventButton *event, gpointer data);
-static gboolean pidgin_whiteboard_brush_motion(GtkWidget *widget, GdkEventMotion *event, gpointer data);
-static gboolean pidgin_whiteboard_brush_up(GtkWidget *widget, GdkEventButton *event, gpointer data);
-
-static void pidgin_whiteboard_draw_brush_point(PurpleWhiteboard *wb,
-												  int x, int y, int color, int size);
-static void pidgin_whiteboard_draw_brush_line(PurpleWhiteboard *wb, int x0, int y0,
-												int x1, int y1, int color, int size);
-
-static void pidgin_whiteboard_set_dimensions(PurpleWhiteboard *wb, int width, int height);
-static void pidgin_whiteboard_set_brush(PurpleWhiteboard *wb, int size, int color);
-static void pidgin_whiteboard_clear(PurpleWhiteboard *wb);
-
-static void pidgin_whiteboard_button_clear_press(GtkWidget *widget, gpointer data);
-static void pidgin_whiteboard_button_save_press(GtkWidget *widget, gpointer data);
-
-static void pidgin_whiteboard_set_canvas_as_icon(PidginWhiteboard *gtkwb);
-
-static void pidgin_whiteboard_rgb24_to_rgba(int color_rgb, GdkRGBA *color);
-
-static void color_selected(GtkColorButton *button, PidginWhiteboard *gtkwb);
-
-/******************************************************************************
  * Globals
  *****************************************************************************/
 
@@ -119,207 +82,15 @@
 static int MotionCount; /* Tracks how many brush motions made */
 static PidginWhiteboardBrushState brush_state = PIDGIN_WHITEBOARD_BRUSH_UP;
 
-static PurpleWhiteboardUiOps ui_ops =
-{
-	pidgin_whiteboard_create,
-	pidgin_whiteboard_destroy,
-	pidgin_whiteboard_set_dimensions,
-	pidgin_whiteboard_set_brush,
-	pidgin_whiteboard_draw_brush_point,
-	pidgin_whiteboard_draw_brush_line,
-	pidgin_whiteboard_clear,
-	NULL,
-	NULL,
-	NULL,
-	NULL
-};
-
 /******************************************************************************
- * API
+ * Helpers
  *****************************************************************************/
-PurpleWhiteboardUiOps *pidgin_whiteboard_get_ui_ops(void)
-{
-	return &ui_ops;
-}
-
-static void pidgin_whiteboard_create(PurpleWhiteboard *wb)
+static void
+pidgin_whiteboard_rgb24_to_rgba(int color_rgb, GdkRGBA *color)
 {
-	PurpleBuddy *buddy;
-	GtkWidget *window;
-	GtkWidget *drawing_area;
-	GtkWidget *vbox_controls;
-	GtkWidget *hbox_canvas_and_controls;
-
-	/*
-		--------------------------
-		|[][][][palette[][][][][]|
-		|------------------------|
-		|       canvas     | con |
-		|                  | trol|
-		|                  | s   |
-		|                  |     |
-		|                  |     |
-		--------------------------
-	*/
-	GtkWidget *clear_button;
-	GtkWidget *save_button;
-	GtkWidget *color_button;
-	GdkRGBA color;
-
-	PidginWhiteboard *gtkwb = g_new0(PidginWhiteboard, 1);
-	gtkwb->priv = g_new0(PidginWhiteboardPrivate, 1);
-
-	gtkwb->wb = wb;
-	purple_whiteboard_set_ui_data(wb, gtkwb);
-
-	/* Get dimensions (default?) for the whiteboard canvas */
-	if (!purple_whiteboard_get_dimensions(wb, &gtkwb->width, &gtkwb->height))
-	{
-		/* Give some initial board-size */
-		gtkwb->width = 300;
-		gtkwb->height = 250;
-	}
-
-	if (!purple_whiteboard_get_brush(wb, &gtkwb->brush_size, &gtkwb->brush_color))
-	{
-		/* Give some initial brush-info */
-		gtkwb->brush_size = 2;
-		gtkwb->brush_color = 0xff0000;
-	}
-
-	/* Try and set window title as the name of the buddy, else just use their
-	 * username
-	 */
-	buddy = purple_blist_find_buddy(purple_whiteboard_get_account(wb), purple_whiteboard_get_who(wb));
-
-	window = pidgin_create_window(buddy != NULL ? purple_buddy_get_contact_alias(buddy) : purple_whiteboard_get_who(wb), 0, NULL, FALSE);
-	gtkwb->window = window;
-	gtk_widget_set_name(window, purple_whiteboard_get_who(wb));
-
-	g_signal_connect(G_OBJECT(window), "delete_event",
-					 G_CALLBACK(whiteboard_close_cb), gtkwb);
-
-	hbox_canvas_and_controls = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
-	gtk_widget_show(hbox_canvas_and_controls);
-
-	gtk_container_add(GTK_CONTAINER(window), hbox_canvas_and_controls);
-	gtk_container_set_border_width(GTK_CONTAINER(window), PIDGIN_HIG_BORDER);
-
-	/* Create the drawing area */
-	drawing_area = gtk_drawing_area_new();
-	gtkwb->drawing_area = drawing_area;
-	gtk_widget_set_size_request(GTK_WIDGET(drawing_area), gtkwb->width, gtkwb->height);
-	gtk_box_pack_start(GTK_BOX(hbox_canvas_and_controls), drawing_area, TRUE, TRUE, PIDGIN_HIG_BOX_SPACE);
-
-	gtk_widget_show(drawing_area);
-
-	/* Signals used to handle backing pixmap */
-	g_signal_connect(G_OBJECT(drawing_area), "draw",
-		G_CALLBACK(pidgin_whiteboard_draw_event), gtkwb);
-
-	g_signal_connect(G_OBJECT(drawing_area), "configure-event",
-		G_CALLBACK(pidgin_whiteboard_configure_event), gtkwb);
-
-	/* Event signals */
-	g_signal_connect(G_OBJECT(drawing_area), "button_press_event",
-					 G_CALLBACK(pidgin_whiteboard_brush_down), gtkwb);
-
-	g_signal_connect(G_OBJECT(drawing_area), "motion_notify_event",
-					 G_CALLBACK(pidgin_whiteboard_brush_motion), gtkwb);
-
-	g_signal_connect(G_OBJECT(drawing_area), "button_release_event",
-					 G_CALLBACK(pidgin_whiteboard_brush_up), gtkwb);
-
-	gtk_widget_set_events(drawing_area,
-						  GDK_EXPOSURE_MASK |
-						  GDK_LEAVE_NOTIFY_MASK |
-						  GDK_BUTTON_PRESS_MASK |
-						  GDK_POINTER_MOTION_MASK |
-						  GDK_BUTTON_RELEASE_MASK |
-						  GDK_POINTER_MOTION_HINT_MASK);
-
-	/* Create vertical box to contain the controls */
-	vbox_controls = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
-	gtk_box_pack_start(GTK_BOX(hbox_canvas_and_controls),
-					vbox_controls, FALSE, FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtk_widget_show(vbox_controls);
-
-	/* Add a clear button */
-	clear_button = gtk_button_new_from_stock(GTK_STOCK_CLEAR);
-	gtk_box_pack_start(GTK_BOX(vbox_controls), clear_button, FALSE, FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtk_widget_show(clear_button);
-	g_signal_connect(G_OBJECT(clear_button), "clicked",
-					 G_CALLBACK(pidgin_whiteboard_button_clear_press), gtkwb);
-
-	/* Add a save button */
-	save_button = gtk_button_new_from_stock(GTK_STOCK_SAVE);
-	gtk_box_pack_start(GTK_BOX(vbox_controls), save_button, FALSE, FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtk_widget_show(save_button);
-
-	g_signal_connect(G_OBJECT(save_button), "clicked",
-					 G_CALLBACK(pidgin_whiteboard_button_save_press), gtkwb);
-
-	/* Add a color selector */
-	color_button = gtk_color_button_new();
-	gtk_box_pack_start(GTK_BOX(vbox_controls), color_button, FALSE, FALSE, PIDGIN_HIG_BOX_SPACE);
-	gtk_widget_show(color_button);
-
-	gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(color_button), FALSE);
-	pidgin_whiteboard_rgb24_to_rgba(gtkwb->brush_color, &color);
-	gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(color_button), &color);
-
-	g_signal_connect(G_OBJECT(color_button), "color-set",
-	                 G_CALLBACK(color_selected), gtkwb);
-
-	/* Make all this (window) visible */
-	gtk_widget_show(window);
-
-	pidgin_whiteboard_set_canvas_as_icon(gtkwb);
-
-	/* TODO Specific protocol/whiteboard assignment here? Needs a UI Op? */
-	/* Set default brush size and color */
-	/*
-	ds->brush_size = DOODLE_BRUSH_MEDIUM;
-	ds->brush_color = 0;
-	*/
-}
-
-static void pidgin_whiteboard_destroy(PurpleWhiteboard *wb)
-{
-	PidginWhiteboard *gtkwb;
-	GtkWidget *colour_dialog;
-
-	g_return_if_fail(wb != NULL);
-	gtkwb = purple_whiteboard_get_ui_data(wb);
-	g_return_if_fail(gtkwb != NULL);
-
-	/* TODO Ask if user wants to save picture before the session is closed */
-
-	/* Clear graphical memory */
-	if (gtkwb->priv->cr) {
-		cairo_destroy(gtkwb->priv->cr);
-		gtkwb->priv->cr = NULL;
-	}
-	if (gtkwb->priv->surface) {
-		cairo_surface_destroy(gtkwb->priv->surface);
-		gtkwb->priv->surface = NULL;
-	}
-
-	colour_dialog = g_object_get_data(G_OBJECT(gtkwb->window), "colour-dialog");
-	if (colour_dialog) {
-		gtk_widget_destroy(colour_dialog);
-		g_object_set_data(G_OBJECT(gtkwb->window), "colour-dialog", NULL);
-	}
-
-	if(gtkwb->window)
-	{
-		gtk_widget_destroy(gtkwb->window);
-		gtkwb->window = NULL;
-	}
-
-	g_free(gtkwb->priv);
-	g_free(gtkwb);
-	purple_whiteboard_set_ui_data(wb, NULL);
+	color->red = ((color_rgb >> 16) & 0xFF) / 255.0f;
+	color->green = ((color_rgb >> 8) & 0xFF) / 255.0f;
+	color->blue = (color_rgb & 0xFF) / 255.0f;
 }
 
 static gboolean whiteboard_close_cb(GtkWidget *widget, GdkEvent *event, PidginWhiteboard *gtkwb)
@@ -335,42 +106,6 @@
 	return FALSE;
 }
 
-/*
- * Whiteboard start button on conversation window (move this code to gtkconv?
- * and use new protocol member?)
- */
-#if 0
-static void pidginwhiteboard_button_start_press(GtkButton *button, gpointer data)
-{
-	PurpleConversation *conv = data;
-	PurpleAccount *account = purple_conversation_get_account(conv);
-	PurpleConnection *gc = purple_account_get_connection(account);
-	char *to = (char*)(purple_conversation_get_name(conv));
-
-	/* Only handle this if local client requested Doodle session (else local
-	 * client would have sent one)
-	 */
-	PurpleWhiteboard *wb = purple_whiteboard_get(account, to);
-
-	/* Write a local message to this conversation showing that a request for a
-	 * Doodle session has been made
-	 */
-	/* XXXX because otherwise gettext will see this string, even though it's
-	 * in an #if 0 block. Remove the XXXX if you want to use this code.
-	 * But, it really shouldn't be a Yahoo-specific string. ;) */
-	purple_im_conversation_write_message(PURPLE_CONV_IM(conv), "", XXXX_("Sent Doodle request."),
-					   PURPLE_MESSAGE_NICK | PURPLE_MESSAGE_RECV, time(NULL));
-
-	yahoo_doodle_command_send_request(gc, to);
-	yahoo_doodle_command_send_ready(gc, to);
-
-	/* Insert this 'session' in the list.  At this point, it's only a requested
-	 * session.
-	 */
-	wb = purple_whiteboard_new(account, to, DOODLE_STATE_REQUESTING);
-}
-#endif
-
 static gboolean pidgin_whiteboard_configure_event(GtkWidget *widget, GdkEventConfigure *event, gpointer data)
 {
 	PidginWhiteboard *gtkwb = (PidginWhiteboard*)data;
@@ -407,6 +142,114 @@
 	return FALSE;
 }
 
+static void
+pidgin_whiteboard_set_canvas_as_icon(PidginWhiteboard *gtkwb)
+{
+	GdkPixbuf *pixbuf;
+
+	/* Makes an icon from the whiteboard's canvas 'image' */
+	pixbuf = gdk_pixbuf_get_from_surface(gtkwb->priv->surface, 0, 0,
+	                                     gtkwb->width, gtkwb->height);
+	gtk_window_set_icon(GTK_WINDOW(gtkwb->window), pixbuf);
+	g_object_unref(pixbuf);
+}
+
+static void
+pidgin_whiteboard_draw_brush_point(PurpleWhiteboard *wb, int x, int y,
+                                   int color, int size)
+{
+	PidginWhiteboard *gtkwb = purple_whiteboard_get_ui_data(wb);
+	GtkWidget *widget = gtkwb->drawing_area;
+	cairo_t *gfx_con = gtkwb->priv->cr;
+	GdkRGBA rgba;
+
+	/* Interpret and convert color */
+	pidgin_whiteboard_rgb24_to_rgba(color, &rgba);
+	gdk_cairo_set_source_rgba(gfx_con, &rgba);
+
+	/* Draw a circle */
+	cairo_arc(gfx_con, x, y, size / 2.0, 0.0, 2.0 * M_PI);
+	cairo_fill(gfx_con);
+
+	gtk_widget_queue_draw_area(widget, x - size / 2, y - size / 2, size,
+	                           size);
+}
+/* Uses Bresenham's algorithm (as provided by Wikipedia) */
+static void
+pidgin_whiteboard_draw_brush_line(PurpleWhiteboard *wb, int x0, int y0, int x1,
+                                  int y1, int color, int size)
+{
+	int temp;
+
+	int xstep;
+	int ystep;
+
+	int dx;
+	int dy;
+
+	int error;
+	int derror;
+
+	int x;
+	int y;
+
+	gboolean steep = abs(y1 - y0) > abs(x1 - x0);
+
+	if (steep) {
+		temp = x0;
+		x0 = y0;
+		y0 = temp;
+		temp = x1;
+		x1 = y1;
+		y1 = temp;
+	}
+
+	dx = abs(x1 - x0);
+	dy = abs(y1 - y0);
+
+	error = 0;
+	derror = dy;
+
+	x = x0;
+	y = y0;
+
+	if (x0 < x1) {
+		xstep = 1;
+	} else {
+		xstep = -1;
+	}
+
+	if (y0 < y1) {
+		ystep = 1;
+	} else {
+		ystep = -1;
+	}
+
+	if (steep) {
+		pidgin_whiteboard_draw_brush_point(wb, y, x, color, size);
+	} else {
+		pidgin_whiteboard_draw_brush_point(wb, x, y, color, size);
+	}
+
+	while (x != x1) {
+		x += xstep;
+		error += derror;
+
+		if ((error * 2) >= dx) {
+			y += ystep;
+			error -= dx;
+		}
+
+		if (steep) {
+			pidgin_whiteboard_draw_brush_point(wb, y, x, color,
+			                                   size);
+		} else {
+			pidgin_whiteboard_draw_brush_point(wb, x, y, color,
+			                                   size);
+		}
+	}
+}
+
 static gboolean pidgin_whiteboard_brush_down(GtkWidget *widget, GdkEventButton *event, gpointer data)
 {
 	PidginWhiteboard *gtkwb = (PidginWhiteboard*)data;
@@ -566,32 +409,28 @@
 		/* If the brush was never moved, express two sets of two deltas That's a
 		 * 'point,' but not for Yahoo!
 		 */
-		/* if((event->x == LastX) && (event->y == LastY)) */
 		if(MotionCount == 0)
 		{
 			int index;
 
 			/* For Yahoo!, a (0 0) indicates the end of drawing */
 			/* FIXME: Yahoo Doodle specific! */
-			for(index = 0; index < 2; index++)
-			{
+			for (index = 0; index < 2; index++) {
 				draw_list = g_list_append(draw_list, 0);
 				draw_list = g_list_append(draw_list, 0);
 			}
 		}
-		/*
-		else
-			MotionCount = 0;
-		*/
 
 		/* Send draw list to protocol draw_list handler */
 		purple_whiteboard_send_draw_list(gtkwb->wb, draw_list);
 
 		pidgin_whiteboard_set_canvas_as_icon(gtkwb);
 
-		/* The brush stroke is finished, clear the list for another one */
-		if(draw_list)
+		/* The brush stroke is finished, clear the list for another one
+		 */
+		if (draw_list) {
 			purple_whiteboard_draw_list_destroy(draw_list);
+		}
 
 		purple_whiteboard_set_draw_list(wb, NULL);
 	}
@@ -599,96 +438,6 @@
 	return TRUE;
 }
 
-static void pidgin_whiteboard_draw_brush_point(PurpleWhiteboard *wb, int x, int y, int color, int size)
-{
-	PidginWhiteboard *gtkwb = purple_whiteboard_get_ui_data(wb);
-	GtkWidget *widget = gtkwb->drawing_area;
-	cairo_t *gfx_con = gtkwb->priv->cr;
-	GdkRGBA rgba;
-
-	/* Interpret and convert color */
-	pidgin_whiteboard_rgb24_to_rgba(color, &rgba);
-	gdk_cairo_set_source_rgba(gfx_con, &rgba);
-
-	/* Draw a circle */
-	cairo_arc(gfx_con,
-	          x, y,
-	          size / 2.0,
-	          0.0, 2.0 * M_PI);
-	cairo_fill(gfx_con);
-
-	gtk_widget_queue_draw_area(widget,
-							   x - size / 2, y - size / 2,
-							   size, size);
-}
-
-/* Uses Bresenham's algorithm (as provided by Wikipedia) */
-static void pidgin_whiteboard_draw_brush_line(PurpleWhiteboard *wb, int x0, int y0, int x1, int y1, int color, int size)
-{
-	int temp;
-
-	int xstep;
-	int ystep;
-
-	int dx;
-	int dy;
-
-	int error;
-	int derror;
-
-	int x;
-	int y;
-
-	gboolean steep = abs(y1 - y0) > abs(x1 - x0);
-
-	if(steep)
-	{
-		temp = x0; x0 = y0; y0 = temp;
-		temp = x1; x1 = y1; y1 = temp;
-	}
-
-	dx = abs(x1 - x0);
-	dy = abs(y1 - y0);
-
-	error = 0;
-	derror = dy;
-
-	x = x0;
-	y = y0;
-
-	if(x0 < x1)
-		xstep = 1;
-	else
-		xstep = -1;
-
-	if(y0 < y1)
-		ystep = 1;
-	else
-		ystep = -1;
-
-	if(steep)
-		pidgin_whiteboard_draw_brush_point(wb, y, x, color, size);
-	else
-		pidgin_whiteboard_draw_brush_point(wb, x, y, color, size);
-
-	while(x != x1)
-	{
-		x += xstep;
-		error += derror;
-
-		if((error * 2) >= dx)
-		{
-			y += ystep;
-			error -= dx;
-		}
-
-		if(steep)
-			pidgin_whiteboard_draw_brush_point(wb, y, x, color, size);
-		else
-			pidgin_whiteboard_draw_brush_point(wb, x, y, color, size);
-	}
-}
-
 static void pidgin_whiteboard_set_dimensions(PurpleWhiteboard *wb, int width, int height)
 {
 	PidginWhiteboard *gtkwb = purple_whiteboard_get_ui_data(wb);
@@ -795,24 +544,6 @@
 		gtk_widget_destroy(dialog);
 }
 
-static void pidgin_whiteboard_set_canvas_as_icon(PidginWhiteboard *gtkwb)
-{
-	GdkPixbuf *pixbuf;
-
-	/* Makes an icon from the whiteboard's canvas 'image' */
-	pixbuf = gdk_pixbuf_get_from_surface(gtkwb->priv->surface,
-		0, 0, gtkwb->width, gtkwb->height);
-	gtk_window_set_icon(GTK_WINDOW(gtkwb->window), pixbuf);
-	g_object_unref(pixbuf);
-}
-
-static void pidgin_whiteboard_rgb24_to_rgba(int color_rgb, GdkRGBA *color)
-{
-	color->red   = ((color_rgb >> 16) & 0xFF) / 255.0f;
-	color->green = ((color_rgb >> 8) & 0xFF) / 255.0f;
-	color->blue  = (color_rgb & 0xFF) / 255.0f;
-}
-
 static void
 color_selected(GtkColorButton *button, PidginWhiteboard *gtkwb)
 {
@@ -831,3 +562,220 @@
 	purple_whiteboard_send_brush(wb, old_size, new_color);
 }
 
+static void
+pidgin_whiteboard_create(PurpleWhiteboard *wb)
+{
+	PurpleBuddy *buddy;
+	GtkWidget *window;
+	GtkWidget *drawing_area;
+	GtkWidget *vbox_controls;
+	GtkWidget *hbox_canvas_and_controls;
+
+	/*
+	        --------------------------
+	        |[][][][palette[][][][][]|
+	        |------------------------|
+	        |       canvas     | con |
+	        |                  | trol|
+	        |                  | s   |
+	        |                  |     |
+	        |                  |     |
+	        --------------------------
+	*/
+	GtkWidget *clear_button;
+	GtkWidget *save_button;
+	GtkWidget *color_button;
+	GdkRGBA color;
+
+	PidginWhiteboard *gtkwb = g_new0(PidginWhiteboard, 1);
+	gtkwb->priv = g_new0(PidginWhiteboardPrivate, 1);
+
+	gtkwb->wb = wb;
+	purple_whiteboard_set_ui_data(wb, gtkwb);
+
+	/* Get dimensions (default?) for the whiteboard canvas */
+	if (!purple_whiteboard_get_dimensions(wb, &gtkwb->width,
+	                                      &gtkwb->height)) {
+		/* Give some initial board-size */
+		gtkwb->width = 300;
+		gtkwb->height = 250;
+	}
+
+	if (!purple_whiteboard_get_brush(wb, &gtkwb->brush_size,
+	                                 &gtkwb->brush_color)) {
+		/* Give some initial brush-info */
+		gtkwb->brush_size = 2;
+		gtkwb->brush_color = 0xff0000;
+	}
+
+	/* Try and set window title as the name of the buddy, else just use
+	 * their username
+	 */
+	buddy = purple_blist_find_buddy(purple_whiteboard_get_account(wb),
+	                                purple_whiteboard_get_who(wb));
+
+	window = pidgin_create_window(
+	        buddy != NULL ? purple_buddy_get_contact_alias(buddy)
+	                      : purple_whiteboard_get_who(wb),
+	        0, NULL, FALSE);
+	gtkwb->window = window;
+	gtk_widget_set_name(window, purple_whiteboard_get_who(wb));
+
+	g_signal_connect(G_OBJECT(window), "delete_event",
+	                 G_CALLBACK(whiteboard_close_cb), gtkwb);
+
+	hbox_canvas_and_controls = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+	gtk_widget_show(hbox_canvas_and_controls);
+
+	gtk_container_add(GTK_CONTAINER(window), hbox_canvas_and_controls);
+	gtk_container_set_border_width(GTK_CONTAINER(window),
+	                               PIDGIN_HIG_BORDER);
+
+	/* Create the drawing area */
+	drawing_area = gtk_drawing_area_new();
+	gtkwb->drawing_area = drawing_area;
+	gtk_widget_set_size_request(GTK_WIDGET(drawing_area), gtkwb->width,
+	                            gtkwb->height);
+	gtk_box_pack_start(GTK_BOX(hbox_canvas_and_controls), drawing_area,
+	                   TRUE, TRUE, PIDGIN_HIG_BOX_SPACE);
+
+	gtk_widget_show(drawing_area);
+
+	/* Signals used to handle backing pixmap */
+	g_signal_connect(G_OBJECT(drawing_area), "draw",
+	                 G_CALLBACK(pidgin_whiteboard_draw_event), gtkwb);
+
+	g_signal_connect(G_OBJECT(drawing_area), "configure-event",
+	                 G_CALLBACK(pidgin_whiteboard_configure_event), gtkwb);
+
+	/* Event signals */
+	g_signal_connect(G_OBJECT(drawing_area), "button_press_event",
+	                 G_CALLBACK(pidgin_whiteboard_brush_down), gtkwb);
+
+	g_signal_connect(G_OBJECT(drawing_area), "motion_notify_event",
+	                 G_CALLBACK(pidgin_whiteboard_brush_motion), gtkwb);
+
+	g_signal_connect(G_OBJECT(drawing_area), "button_release_event",
+	                 G_CALLBACK(pidgin_whiteboard_brush_up), gtkwb);
+
+	gtk_widget_set_events(
+	        drawing_area,
+	        GDK_EXPOSURE_MASK | GDK_LEAVE_NOTIFY_MASK |
+	                GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK |
+	                GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_HINT_MASK);
+
+	/* Create vertical box to contain the controls */
+	vbox_controls = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
+	gtk_box_pack_start(GTK_BOX(hbox_canvas_and_controls), vbox_controls,
+	                   FALSE, FALSE, PIDGIN_HIG_BOX_SPACE);
+	gtk_widget_show(vbox_controls);
+
+	/* Add a clear button */
+	clear_button = gtk_button_new_from_stock(GTK_STOCK_CLEAR);
+	gtk_box_pack_start(GTK_BOX(vbox_controls), clear_button, FALSE, FALSE,
+	                   PIDGIN_HIG_BOX_SPACE);
+	gtk_widget_show(clear_button);
+	g_signal_connect(G_OBJECT(clear_button), "clicked",
+	                 G_CALLBACK(pidgin_whiteboard_button_clear_press),
+	                 gtkwb);
+
+	/* Add a save button */
+	save_button = gtk_button_new_from_stock(GTK_STOCK_SAVE);
+	gtk_box_pack_start(GTK_BOX(vbox_controls), save_button, FALSE, FALSE,
+	                   PIDGIN_HIG_BOX_SPACE);
+	gtk_widget_show(save_button);
+
+	g_signal_connect(G_OBJECT(save_button), "clicked",
+	                 G_CALLBACK(pidgin_whiteboard_button_save_press),
+	                 gtkwb);
+
+	/* Add a color selector */
+	color_button = gtk_color_button_new();
+	gtk_box_pack_start(GTK_BOX(vbox_controls), color_button, FALSE, FALSE,
+	                   PIDGIN_HIG_BOX_SPACE);
+	gtk_widget_show(color_button);
+
+	gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(color_button), FALSE);
+	pidgin_whiteboard_rgb24_to_rgba(gtkwb->brush_color, &color);
+	gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(color_button), &color);
+
+	g_signal_connect(G_OBJECT(color_button), "color-set",
+	                 G_CALLBACK(color_selected), gtkwb);
+
+	/* Make all this (window) visible */
+	gtk_widget_show(window);
+
+	pidgin_whiteboard_set_canvas_as_icon(gtkwb);
+
+	/* TODO Specific protocol/whiteboard assignment here? Needs a UI Op? */
+	/* Set default brush size and color */
+	/*
+	ds->brush_size = DOODLE_BRUSH_MEDIUM;
+	ds->brush_color = 0;
+	*/
+}
+
+static void
+pidgin_whiteboard_destroy(PurpleWhiteboard *wb)
+{
+	PidginWhiteboard *gtkwb;
+	GtkWidget *colour_dialog;
+
+	g_return_if_fail(wb != NULL);
+	gtkwb = purple_whiteboard_get_ui_data(wb);
+	g_return_if_fail(gtkwb != NULL);
+
+	/* TODO Ask if user wants to save picture before the session is closed
+	 */
+
+	/* Clear graphical memory */
+	if (gtkwb->priv->cr) {
+		cairo_destroy(gtkwb->priv->cr);
+		gtkwb->priv->cr = NULL;
+	}
+	if (gtkwb->priv->surface) {
+		cairo_surface_destroy(gtkwb->priv->surface);
+		gtkwb->priv->surface = NULL;
+	}
+
+	colour_dialog =
+	        g_object_get_data(G_OBJECT(gtkwb->window), "colour-dialog");
+	if (colour_dialog) {
+		gtk_widget_destroy(colour_dialog);
+		g_object_set_data(G_OBJECT(gtkwb->window), "colour-dialog",
+		                  NULL);
+	}
+
+	if (gtkwb->window) {
+		gtk_widget_destroy(gtkwb->window);
+		gtkwb->window = NULL;
+	}
+
+	g_free(gtkwb->priv);
+	g_free(gtkwb);
+	purple_whiteboard_set_ui_data(wb, NULL);
+}
+
+/******************************************************************************
+ * API
+ *****************************************************************************/
+static PurpleWhiteboardUiOps ui_ops =
+{
+	pidgin_whiteboard_create,
+	pidgin_whiteboard_destroy,
+	pidgin_whiteboard_set_dimensions,
+	pidgin_whiteboard_set_brush,
+	pidgin_whiteboard_draw_brush_point,
+	pidgin_whiteboard_draw_brush_line,
+	pidgin_whiteboard_clear,
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+PurpleWhiteboardUiOps *
+pidgin_whiteboard_get_ui_ops(void)
+{
+	return &ui_ops;
+}

mercurial