1 #include <config.h>
2 #include <string.h>
3 
4 #include <glib/gi18n.h>
5 #include <gdk/gdkkeysyms.h>
6 #include <gtk/gtk.h>
7 #include <gdk/gdk.h>
8 
9 #include "button-widget.h"
10 #include "panel-widget.h"
11 #include "panel-types.h"
12 #include "panel-util.h"
13 #include "panel-config-global.h"
14 #include "panel-marshal.h"
15 #include "panel-typebuiltins.h"
16 #include "panel-globals.h"
17 #include "panel-enums.h"
18 #include "panel-enums-gsettings.h"
19 
20 struct _ButtonWidgetPrivate {
21 	GtkIconTheme     *icon_theme;
22 	cairo_surface_t  *surface;
23 	cairo_surface_t  *surface_hc;
24 
25 	char             *filename;
26 
27 	PanelOrientation  orientation;
28 
29 	int               size;
30 
31 	guint             activatable   : 1;
32 	guint             ignore_leave  : 1;
33 	guint             arrow         : 1;
34 	guint             dnd_highlight : 1;
35 };
36 
37 static void button_widget_icon_theme_changed (ButtonWidget *button);
38 static void button_widget_reload_surface (ButtonWidget *button);
39 
40 enum {
41 	PROP_0,
42 	PROP_ACTIVATABLE,
43 	PROP_IGNORE_LEAVE,
44 	PROP_HAS_ARROW,
45 	PROP_DND_HIGHLIGHT,
46 	PROP_ORIENTATION,
47 	PROP_ICON_NAME
48 };
49 
50 #define BUTTON_WIDGET_DISPLACEMENT 2
51 
G_DEFINE_TYPE_WITH_PRIVATE(ButtonWidget,button_widget,GTK_TYPE_BUTTON)52 G_DEFINE_TYPE_WITH_PRIVATE (ButtonWidget, button_widget, GTK_TYPE_BUTTON)
53 
54 /* colorshift a surface */
55 static void
56 do_colorshift (cairo_surface_t *dest, cairo_surface_t *src, int shift)
57 {
58 	gint i, j;
59 	gint width, height, has_alpha, srcrowstride, destrowstride;
60 	guchar *target_pixels;
61 	guchar *original_pixels;
62 	int val;
63 	guchar r,g,b;
64 
65 	has_alpha = cairo_surface_get_content (src) != CAIRO_CONTENT_COLOR;
66 	width = cairo_image_surface_get_width (src);
67 	height = cairo_image_surface_get_height (src);
68 	srcrowstride = cairo_image_surface_get_stride (src);
69 	destrowstride = cairo_image_surface_get_stride (dest);
70 	original_pixels = cairo_image_surface_get_data (src);
71 	target_pixels = cairo_image_surface_get_data (dest);
72 
73 	for (i = 0; i < height; i++) {
74 		guchar *pixdest = target_pixels + i*destrowstride;
75 		guchar *pixsrc = original_pixels + i*srcrowstride;
76 		for (j = 0; j < width; j++) {
77 			r = *(pixsrc++);
78 			g = *(pixsrc++);
79 			b = *(pixsrc++);
80 			val = r + shift;
81 			*(pixdest++) = CLAMP(val, 0, 255);
82 			val = g + shift;
83 			*(pixdest++) = CLAMP(val, 0, 255);
84 			val = b + shift;
85 			*(pixdest++) = CLAMP(val, 0, 255);
86 			if (has_alpha)
87 				*(pixdest++) = *(pixsrc++);
88 		}
89 	}
90 }
91 
92 static cairo_surface_t *
make_hc_surface(cairo_surface_t * surface)93 make_hc_surface (cairo_surface_t *surface)
94 {
95 	cairo_t *cr;
96 	cairo_surface_t *new;
97 
98 	if (!surface)
99 		return NULL;
100 
101 	new = cairo_surface_create_similar (surface,
102 			                    cairo_surface_get_content (surface),
103 			                    cairo_image_surface_get_width (surface),
104 			                    cairo_image_surface_get_height (surface));
105 
106 	do_colorshift (new, surface, 30);
107 
108 	cr = cairo_create (new);
109 	cairo_set_operator (cr, CAIRO_OPERATOR_DEST_IN);
110 	cairo_mask_surface (cr, surface, 0, 0);
111 	cairo_destroy (cr);
112 
113 	return new;
114 }
115 
116 static void
button_widget_realize(GtkWidget * widget)117 button_widget_realize(GtkWidget *widget)
118 {
119 	gtk_widget_add_events (widget, GDK_POINTER_MOTION_MASK |
120 			       GDK_POINTER_MOTION_HINT_MASK |
121 			       GDK_KEY_PRESS_MASK);
122 
123 	GTK_WIDGET_CLASS (button_widget_parent_class)->realize (widget);
124 
125 	BUTTON_WIDGET (widget)->priv->icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget));
126 	g_signal_connect_object (BUTTON_WIDGET (widget)->priv->icon_theme,
127 				 "changed",
128 				 G_CALLBACK (button_widget_icon_theme_changed),
129 				 widget,
130 				 G_CONNECT_SWAPPED);
131 
132 	button_widget_reload_surface (BUTTON_WIDGET (widget));
133 }
134 
135 static void
button_widget_unrealize(GtkWidget * widget)136 button_widget_unrealize (GtkWidget *widget)
137 {
138 	g_signal_handlers_disconnect_by_func (BUTTON_WIDGET (widget)->priv->icon_theme,
139 					      G_CALLBACK (button_widget_icon_theme_changed),
140 					      widget);
141 
142 	GTK_WIDGET_CLASS (button_widget_parent_class)->unrealize (widget);
143 }
144 
145 static void
button_widget_unset_surfaces(ButtonWidget * button)146 button_widget_unset_surfaces (ButtonWidget *button)
147 {
148 	if (button->priv->surface)
149 		cairo_surface_destroy (button->priv->surface);
150 	button->priv->surface = NULL;
151 
152 	if (button->priv->surface_hc)
153 		cairo_surface_destroy (button->priv->surface_hc);
154 	button->priv->surface_hc = NULL;
155 }
156 
157 static void
button_widget_reload_surface(ButtonWidget * button)158 button_widget_reload_surface (ButtonWidget *button)
159 {
160 	button_widget_unset_surfaces (button);
161 
162 	if (button->priv->size <= 1 || button->priv->icon_theme == NULL)
163 		return;
164 
165 	if (button->priv->filename != NULL &&
166 	    button->priv->filename [0] != '\0') {
167 		gint scale;
168 		char *error = NULL;
169 
170 		scale = gtk_widget_get_scale_factor (GTK_WIDGET (button));
171 
172 		button->priv->surface =
173 			panel_load_icon (button->priv->icon_theme,
174 					 button->priv->filename,
175 					 button->priv->size * scale,
176 					 (button->priv->orientation & PANEL_VERTICAL_MASK)   ? button->priv->size * scale : -1,
177 					 (button->priv->orientation & PANEL_HORIZONTAL_MASK) ? button->priv->size * scale: -1,
178 					 &error);
179 		if (error) {
180 			/* FIXME: this is not rendered at button->priv->size */
181 			GtkIconTheme *icon_theme = gtk_icon_theme_get_default();
182 			button->priv->surface = gtk_icon_theme_load_surface (icon_theme,
183 							       "image-missing",
184 							       GTK_ICON_SIZE_BUTTON,
185 							       scale,
186 							       NULL,
187 							       GTK_ICON_LOOKUP_FORCE_SVG | GTK_ICON_LOOKUP_USE_BUILTIN,
188 							       NULL);
189 			g_free (error);
190 		}
191 	}
192 
193 	button->priv->surface_hc = make_hc_surface (button->priv->surface);
194 
195 	gtk_widget_queue_resize (GTK_WIDGET (button));
196 }
197 
198 static void
button_widget_icon_theme_changed(ButtonWidget * button)199 button_widget_icon_theme_changed (ButtonWidget *button)
200 {
201 	if (button->priv->filename != NULL)
202 		button_widget_reload_surface (button);
203 }
204 
205 static void
button_widget_finalize(GObject * object)206 button_widget_finalize (GObject *object)
207 {
208 	ButtonWidget *button = (ButtonWidget *) object;
209 
210 	button_widget_unset_surfaces (button);
211 
212 	g_free (button->priv->filename);
213 	button->priv->filename = NULL;
214 
215 	G_OBJECT_CLASS (button_widget_parent_class)->finalize (object);
216 }
217 
218 static void
button_widget_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)219 button_widget_get_property (GObject    *object,
220 			    guint       prop_id,
221 			    GValue     *value,
222 			    GParamSpec *pspec)
223 {
224 	ButtonWidget *button;
225 
226 	g_return_if_fail (BUTTON_IS_WIDGET (object));
227 
228 	button = BUTTON_WIDGET (object);
229 
230 	switch (prop_id) {
231 	case PROP_ACTIVATABLE:
232 		g_value_set_boolean (value, button->priv->activatable);
233 		break;
234 	case PROP_IGNORE_LEAVE:
235 		g_value_set_boolean (value, button->priv->ignore_leave);
236 		break;
237 	case PROP_HAS_ARROW:
238 		g_value_set_boolean (value, button->priv->arrow);
239 		break;
240 	case PROP_DND_HIGHLIGHT:
241 		g_value_set_boolean (value, button->priv->dnd_highlight);
242 		break;
243 	case PROP_ORIENTATION:
244 		g_value_set_enum (value, button->priv->orientation);
245 		break;
246 	case PROP_ICON_NAME:
247 		g_value_set_string (value, button->priv->filename);
248 		break;
249 	default:
250 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
251 		break;
252 	}
253 }
254 
255 static void
button_widget_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)256 button_widget_set_property (GObject      *object,
257 			    guint         prop_id,
258 			    const GValue *value,
259 			    GParamSpec   *pspec)
260 {
261 	ButtonWidget *button;
262 
263 	g_return_if_fail (BUTTON_IS_WIDGET (object));
264 
265 	button = BUTTON_WIDGET (object);
266 
267 	switch (prop_id) {
268 	case PROP_ACTIVATABLE:
269 		button_widget_set_activatable (button, g_value_get_boolean (value));
270 		break;
271 	case PROP_IGNORE_LEAVE:
272 		button_widget_set_ignore_leave (button, g_value_get_boolean (value));
273 		break;
274 	case PROP_HAS_ARROW:
275 		button_widget_set_has_arrow (button, g_value_get_boolean (value));
276 		break;
277 	case PROP_DND_HIGHLIGHT:
278 		button_widget_set_dnd_highlight (button, g_value_get_boolean (value));
279 		break;
280 	case PROP_ORIENTATION:
281 		button_widget_set_orientation (button, g_value_get_enum (value));
282 		break;
283 	case PROP_ICON_NAME:
284 		button_widget_set_icon_name (button, g_value_get_string (value));
285 		break;
286 	default:
287 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
288 		break;
289 	}
290 }
291 
292 static GtkArrowType
calc_arrow(PanelOrientation orientation,int button_width,int button_height,int * x,int * y,gdouble * angle,gdouble * size)293 calc_arrow (PanelOrientation  orientation,
294 	    int               button_width,
295 	    int               button_height,
296 	    int              *x,
297 	    int              *y,
298 	    gdouble          *angle,
299 	    gdouble          *size)
300 {
301 	GtkArrowType retval = GTK_ARROW_UP;
302 
303 	if (orientation & PANEL_HORIZONTAL_MASK) {
304 		if (button_width > 50)
305 			button_width = 50;
306 	} else {
307 		if (button_height > 50)
308 			button_height = 50;
309 	}
310 
311 	*size = ((orientation & PANEL_HORIZONTAL_MASK) ? button_width : button_height) / 2;
312 	*angle = 0;
313 
314 	switch (orientation) {
315 	case PANEL_ORIENTATION_TOP:
316 		*x     = (button_width - (*size)) / 2;
317 		*y     = button_height * .99 - (*size) / (3/2);	/* 3/2 is the approximate ratio of GTK arrows */
318 		*angle = G_PI;
319 		retval = GTK_ARROW_DOWN;
320 		break;
321 	case PANEL_ORIENTATION_BOTTOM:
322 		*x     = (button_width - (*size)) / 2;
323 		*y     = button_height * .01;
324 		*angle = 0;
325 		retval = GTK_ARROW_UP;
326 		break;
327 	case PANEL_ORIENTATION_LEFT:
328 		*x     = button_width * .99 - (*size) / (3/2);	/* 3/2 is the approximate ratio of GTK arrows */
329 		*y     = (button_height - (*size)) / 2;
330 		*angle = G_PI / 2;
331 		retval = GTK_ARROW_RIGHT;
332 		break;
333 	case PANEL_ORIENTATION_RIGHT:
334 		*x     = button_width * .01;
335 		*y     = (button_height - (*size)) / 2;
336 		*angle = 3 * (G_PI / 2);
337 		retval = GTK_ARROW_LEFT;
338 		break;
339 	}
340 
341 	return retval;
342 }
343 
344 static gboolean
button_widget_draw(GtkWidget * widget,cairo_t * cr)345 button_widget_draw (GtkWidget *widget,
346 		    cairo_t *cr)
347 {
348 	ButtonWidget *button_widget;
349 	int width;
350 	int height;
351 	GtkStyleContext *context;
352 	GtkStateFlags state_flags;
353 	int off;
354 	int x, y, w, h;
355 	int scale;
356 
357 	g_return_val_if_fail (BUTTON_IS_WIDGET (widget), FALSE);
358 
359 	button_widget = BUTTON_WIDGET (widget);
360 
361 	if (!button_widget->priv->surface_hc && !button_widget->priv->surface)
362 		return FALSE;
363 
364 	state_flags = gtk_widget_get_state_flags (widget);
365 	width = gtk_widget_get_allocated_width (widget);
366 	height = gtk_widget_get_allocated_height (widget);
367 	scale = gtk_widget_get_scale_factor (widget);
368 
369 	/* offset for pressed buttons */
370 	off = (button_widget->priv->activatable &&
371 		(state_flags & GTK_STATE_FLAG_PRELIGHT) && (state_flags & GTK_STATE_FLAG_ACTIVE)) ?
372 		BUTTON_WIDGET_DISPLACEMENT * height / 48.0 : 0;
373 
374 	w = cairo_image_surface_get_width (button_widget->priv->surface) / scale;
375 	h = cairo_image_surface_get_height (button_widget->priv->surface) / scale;
376 	x = off + (width - w) / 2;
377 	y = off + (height - h) / 2;
378 
379 	cairo_save (cr);
380 
381 	if (!button_widget->priv->activatable) {
382 		cairo_set_source_surface (cr, button_widget->priv->surface, x, y);
383 		cairo_mask_surface (cr, button_widget->priv->surface, x, y);
384 		cairo_set_operator (cr, CAIRO_OPERATOR_HSL_SATURATION);
385 		cairo_set_source_rgba (cr, 0, 0, 0, 0.2);
386 	} else if (panel_global_config_get_highlight_when_over () &&
387 		   (state_flags & GTK_STATE_FLAG_PRELIGHT || gtk_widget_has_focus (widget))) {
388 		cairo_set_source_surface (cr, button_widget->priv->surface_hc, x, y);
389 	} else {
390 		cairo_set_source_surface (cr, button_widget->priv->surface, x, y);
391 	}
392 
393 	cairo_paint (cr);
394 	cairo_restore (cr);
395 
396 	context = gtk_widget_get_style_context (widget);
397 
398 	if (button_widget->priv->arrow) {
399 		gdouble angle, size;
400 		gtk_style_context_save (context);
401 		gtk_style_context_set_state (context, state_flags);
402 
403 		calc_arrow (button_widget->priv->orientation,
404 					 width, height,
405 					 &x, &y,
406 					 &angle, &size);
407 
408 		cairo_save (cr);
409 		gtk_render_arrow (context, cr, angle, x, y, size);
410 		cairo_restore (cr);
411 
412 		gtk_style_context_restore (context);
413 	}
414 
415 	if (button_widget->priv->dnd_highlight) {
416 
417 		cairo_save (cr);
418 		cairo_set_line_width (cr, 1);
419 		cairo_set_source_rgb (cr, 0., 0., 0.);
420 		cairo_rectangle (cr, 0.5, 0.5, width - 1, height - 1);
421 		cairo_stroke (cr);
422 		cairo_restore (cr);
423 	}
424 
425 	if (gtk_widget_has_focus (widget)) {
426 		gtk_style_context_save (context);
427 		gtk_style_context_set_state (context, state_flags);
428 
429 		cairo_save (cr);
430 		gtk_render_focus (context, cr, 0, 0, width, height);
431 		cairo_restore (cr);
432 
433 		gtk_style_context_restore (context);
434 	}
435 
436 	return FALSE;
437 }
438 
439 static void
button_widget_get_preferred_width(GtkWidget * widget,gint * minimal_width,gint * natural_width)440 button_widget_get_preferred_width (GtkWidget *widget,
441 				   gint *minimal_width,
442 				   gint *natural_width)
443 {
444  	ButtonWidget *button_widget = BUTTON_WIDGET (widget);
445 	GtkWidget *parent;
446 	int size;
447 
448 	parent = gtk_widget_get_parent (widget);
449 
450 	if (button_widget->priv->orientation & PANEL_HORIZONTAL_MASK){
451 		size = gtk_widget_get_allocated_height (parent);
452 
453 		/* should get this value (50) from gsettings, user defined value in properties of the panel (max_icon_size) OR use 48*/
454 		if ( size > 50 )
455 			size = 50;
456 
457 	} else
458 		size = gtk_widget_get_allocated_width (parent);
459 
460 	*minimal_width = *natural_width = size;
461 }
462 
463 static void
button_widget_get_preferred_height(GtkWidget * widget,gint * minimal_height,gint * natural_height)464 button_widget_get_preferred_height (GtkWidget *widget,
465 				    gint *minimal_height,
466 				    gint *natural_height)
467 {
468 	ButtonWidget *button_widget = BUTTON_WIDGET (widget);
469 	GtkWidget *parent;
470 	int size;
471 
472 	parent = gtk_widget_get_parent (widget);
473 
474 	if (button_widget->priv->orientation & PANEL_HORIZONTAL_MASK)
475 		size = gtk_widget_get_allocated_height (parent);
476 	else {
477 		size = gtk_widget_get_allocated_width (parent);
478 
479 		/* should get this value (50) from gsettings, user defined value in properties of the panel (max_icon_size) OR use 48*/
480 		if ( size > 50 )
481 			size = 50;
482 
483 	}
484 
485 	*minimal_height = *natural_height = size;
486 }
487 
488 static void
button_widget_size_allocate(GtkWidget * widget,GtkAllocation * allocation)489 button_widget_size_allocate (GtkWidget     *widget,
490 			     GtkAllocation *allocation)
491 {
492 	ButtonWidget *button_widget = BUTTON_WIDGET (widget);
493 	int           size;
494 
495 	/* should get this value (50) from gsettings, user defined value in properties of the panel (max_icon_size) OR use 48?*/
496 	if (button_widget->priv->orientation & PANEL_HORIZONTAL_MASK) {
497 		if ( allocation->height > 50 ) {
498 			allocation->width = 50;
499 		}
500 	} else {
501 		if ( allocation->width > 50 ) {
502 			allocation->height = 50;
503 		}
504 	}
505 
506 	GTK_WIDGET_CLASS (button_widget_parent_class)->size_allocate (widget, allocation);
507 
508 	if (button_widget->priv->orientation & PANEL_HORIZONTAL_MASK)
509 		size = allocation->height;
510 	else
511 		size = allocation->width;
512 
513 	if (size < 22)
514 		size = 16;
515 	else if (size < 24)
516 		size = 22;
517 	else if (size < 32)
518 		size = 24;
519 	else if (size < 48)
520 		size = 32;
521 	else
522 		size = 48;
523 
524 	if (button_widget->priv->size != size) {
525 		button_widget->priv->size = size;
526 
527 		button_widget_reload_surface (button_widget);
528 	}
529 }
530 
531 static void
button_widget_activate(GtkButton * button)532 button_widget_activate (GtkButton *button)
533 {
534 	ButtonWidget *button_widget = BUTTON_WIDGET (button);
535 
536 	if (!button_widget->priv->activatable)
537 		return;
538 
539 	if (GTK_BUTTON_CLASS (button_widget_parent_class)->activate)
540 		GTK_BUTTON_CLASS (button_widget_parent_class)->activate (button);
541 }
542 
543 static gboolean
button_widget_button_press(GtkWidget * widget,GdkEventButton * event)544 button_widget_button_press (GtkWidget *widget, GdkEventButton *event)
545 {
546 	g_return_val_if_fail (BUTTON_IS_WIDGET (widget), FALSE);
547 	g_return_val_if_fail (event != NULL, FALSE);
548 
549 	if (event->button == 1 && BUTTON_WIDGET (widget)->priv->activatable &&
550 	/* we don't want to have two/three "click" events for double/triple
551 	 * clicks. FIXME: this is only a workaround, waiting for bug 159101 */
552 	    event->type == GDK_BUTTON_PRESS)
553 		return GTK_WIDGET_CLASS (button_widget_parent_class)->button_press_event (widget, event);
554 
555 	return FALSE;
556 }
557 
558 static gboolean
button_widget_enter_notify(GtkWidget * widget,GdkEventCrossing * event)559 button_widget_enter_notify (GtkWidget *widget, GdkEventCrossing *event)
560 {
561 	gboolean in_button;
562 
563 	g_return_val_if_fail (BUTTON_IS_WIDGET (widget), FALSE);
564 
565 	GtkStateFlags state_flags = gtk_widget_get_state_flags (widget);
566 	in_button = state_flags & GTK_STATE_FLAG_PRELIGHT;
567 
568 	GTK_WIDGET_CLASS (button_widget_parent_class)->enter_notify_event (widget, event);
569 
570 	state_flags = gtk_widget_get_state_flags (widget);
571 	if (in_button != (state_flags & GTK_STATE_FLAG_PRELIGHT) &&
572 	    panel_global_config_get_highlight_when_over ())
573 		gtk_widget_queue_draw (widget);
574 
575 	return FALSE;
576 }
577 
578 static gboolean
button_widget_leave_notify(GtkWidget * widget,GdkEventCrossing * event)579 button_widget_leave_notify (GtkWidget *widget, GdkEventCrossing *event)
580 {
581 	gboolean in_button;
582 
583 	g_return_val_if_fail (BUTTON_IS_WIDGET (widget), FALSE);
584 
585 	GtkStateFlags state_flags = gtk_widget_get_state_flags (widget);
586 	in_button = state_flags & GTK_STATE_FLAG_PRELIGHT;
587 
588 	GTK_WIDGET_CLASS (button_widget_parent_class)->leave_notify_event (widget, event);
589 
590 	state_flags = gtk_widget_get_state_flags (widget);
591 	if (in_button != (state_flags & GTK_STATE_FLAG_PRELIGHT) &&
592 	    panel_global_config_get_highlight_when_over ())
593 		gtk_widget_queue_draw (widget);
594 
595 	return FALSE;
596 }
597 
598 static void
button_widget_init(ButtonWidget * button)599 button_widget_init (ButtonWidget *button)
600 {
601 	button->priv = button_widget_get_instance_private (button);
602 
603 	button->priv->icon_theme = NULL;
604 	button->priv->surface    = NULL;
605 	button->priv->surface_hc = NULL;
606 
607 	button->priv->filename   = NULL;
608 
609 	button->priv->orientation = PANEL_ORIENTATION_TOP;
610 
611 	button->priv->size = 0;
612 
613 	button->priv->activatable   = FALSE;
614 	button->priv->ignore_leave  = FALSE;
615 	button->priv->arrow         = FALSE;
616 	button->priv->dnd_highlight = FALSE;
617 }
618 
619 static void
button_widget_class_init(ButtonWidgetClass * klass)620 button_widget_class_init (ButtonWidgetClass *klass)
621 {
622 	GObjectClass *gobject_class   = (GObjectClass   *) klass;
623 	GtkWidgetClass *widget_class  = (GtkWidgetClass *) klass;
624 	GtkButtonClass *button_class  = (GtkButtonClass *) klass;
625 
626 	gobject_class->finalize     = button_widget_finalize;
627 	gobject_class->get_property = button_widget_get_property;
628 	gobject_class->set_property = button_widget_set_property;
629 
630 	widget_class->realize            = button_widget_realize;
631 	widget_class->unrealize          = button_widget_unrealize;
632 	widget_class->size_allocate      = button_widget_size_allocate;
633 	widget_class->get_preferred_width = button_widget_get_preferred_width;
634 	widget_class->get_preferred_height = button_widget_get_preferred_height;
635 	widget_class->draw               = button_widget_draw;
636 	widget_class->button_press_event = button_widget_button_press;
637 	widget_class->enter_notify_event = button_widget_enter_notify;
638 	widget_class->leave_notify_event = button_widget_leave_notify;
639 
640 	button_class->activate = button_widget_activate;
641 
642 	g_object_class_install_property (
643 			gobject_class,
644 			PROP_ACTIVATABLE,
645 			g_param_spec_boolean ("activatable",
646 					      "Activatable",
647 					      "Whether the button is activatable",
648 					      TRUE,
649 					      G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
650 
651 	g_object_class_install_property (
652 			gobject_class,
653 			PROP_IGNORE_LEAVE,
654 			g_param_spec_boolean ("ignore-leave",
655 					      "Ignore leaving to not unhighlight the icon",
656 					      "Whether or not to unhighlight the icon when the cursor leaves it",
657 					      FALSE,
658 					      G_PARAM_READWRITE));
659 
660 	g_object_class_install_property (
661 			gobject_class,
662 			PROP_HAS_ARROW,
663 			g_param_spec_boolean ("has-arrow",
664 					      "Has Arrow",
665 					      "Whether or not to draw an arrow indicator",
666 					      FALSE,
667 					      G_PARAM_READWRITE));
668 
669 	g_object_class_install_property (
670 			gobject_class,
671 			PROP_DND_HIGHLIGHT,
672 			g_param_spec_boolean ("dnd-highlight",
673 					      "Drag and drop Highlight",
674 					      "Whether or not to highlight the icon during drag and drop",
675 					      FALSE,
676 					      G_PARAM_READWRITE));
677 
678 	g_object_class_install_property (
679 			gobject_class,
680 			PROP_ORIENTATION,
681 			g_param_spec_enum ("orientation",
682 					   "Orientation",
683 					   "The ButtonWidget orientation",
684 					   PANEL_TYPE_ORIENTATION,
685 					   PANEL_ORIENTATION_TOP,
686 					   G_PARAM_READWRITE));
687 
688 	g_object_class_install_property (
689 			gobject_class,
690 			PROP_ICON_NAME,
691 			g_param_spec_string ("icon-name",
692 					     "Icon Name",
693 					     "The desired icon for the ButtonWidget",
694 					     NULL,
695 					     G_PARAM_READWRITE));
696 }
697 
698 GtkWidget *
button_widget_new(const char * filename,gboolean arrow,PanelOrientation orientation)699 button_widget_new (const char       *filename,
700 		   gboolean          arrow,
701 		   PanelOrientation  orientation)
702 {
703 	GtkWidget *retval;
704 
705 	retval = g_object_new (
706 			BUTTON_TYPE_WIDGET,
707 			"has-arrow", arrow,
708 			"orientation", orientation,
709 			"icon-name", filename,
710 			NULL);
711 
712 	return retval;
713 }
714 
715 void
button_widget_set_activatable(ButtonWidget * button,gboolean activatable)716 button_widget_set_activatable (ButtonWidget *button,
717 			       gboolean      activatable)
718 {
719 	g_return_if_fail (BUTTON_IS_WIDGET (button));
720 
721 	activatable = activatable != FALSE;
722 
723 	if (button->priv->activatable != activatable) {
724 		button->priv->activatable = activatable;
725 
726 		if (gtk_widget_is_drawable (GTK_WIDGET (button)))
727 			gtk_widget_queue_draw (GTK_WIDGET (button));
728 
729 		g_object_notify (G_OBJECT (button), "activatable");
730 	}
731 }
732 
733 gboolean
button_widget_get_activatable(ButtonWidget * button)734 button_widget_get_activatable (ButtonWidget *button)
735 {
736 	g_return_val_if_fail (BUTTON_IS_WIDGET (button), FALSE);
737 
738 	return button->priv->activatable;
739 }
740 
741 void
button_widget_set_icon_name(ButtonWidget * button,const char * icon_name)742 button_widget_set_icon_name (ButtonWidget *button,
743 			     const char   *icon_name)
744 {
745 	g_return_if_fail (BUTTON_IS_WIDGET (button));
746 
747 	if (button->priv->filename && icon_name &&
748 	    !strcmp (button->priv->filename, icon_name))
749 		return;
750 
751 	if (button->priv->filename)
752 		g_free (button->priv->filename);
753 	button->priv->filename = g_strdup (icon_name);
754 
755 	button_widget_reload_surface (button);
756 
757 	g_object_notify (G_OBJECT (button), "icon-name");
758 }
759 
760 const char *
button_widget_get_icon_name(ButtonWidget * button)761 button_widget_get_icon_name (ButtonWidget *button)
762 {
763 	g_return_val_if_fail (BUTTON_IS_WIDGET (button), NULL);
764 
765 	return button->priv->filename;
766 }
767 
768 void
button_widget_set_orientation(ButtonWidget * button,PanelOrientation orientation)769 button_widget_set_orientation (ButtonWidget     *button,
770 			       PanelOrientation  orientation)
771 {
772 	g_return_if_fail (BUTTON_IS_WIDGET (button));
773 
774 	if (button->priv->orientation == orientation)
775 		return;
776 
777 	button->priv->orientation = orientation;
778 
779 	/* Force a re-scale */
780 	button->priv->size = -1;
781 
782 	gtk_widget_queue_resize (GTK_WIDGET (button));
783 
784 	g_object_notify (G_OBJECT (button), "orientation");
785 }
786 
787 PanelOrientation
button_widget_get_orientation(ButtonWidget * button)788 button_widget_get_orientation (ButtonWidget *button)
789 {
790 	g_return_val_if_fail (BUTTON_IS_WIDGET (button), 0);
791 
792 	return button->priv->orientation;
793 }
794 
795 void
button_widget_set_has_arrow(ButtonWidget * button,gboolean has_arrow)796 button_widget_set_has_arrow (ButtonWidget *button,
797 			     gboolean      has_arrow)
798 {
799 	g_return_if_fail (BUTTON_IS_WIDGET (button));
800 
801 	has_arrow = has_arrow != FALSE;
802 
803 	if (button->priv->arrow == has_arrow)
804 		return;
805 
806 	button->priv->arrow = has_arrow;
807 
808 	gtk_widget_queue_draw (GTK_WIDGET (button));
809 
810 	g_object_notify (G_OBJECT (button), "has-arrow");
811 }
812 
813 gboolean
button_widget_get_has_arrow(ButtonWidget * button)814 button_widget_get_has_arrow (ButtonWidget *button)
815 {
816 	g_return_val_if_fail (BUTTON_IS_WIDGET (button), FALSE);
817 
818 	return button->priv->arrow;
819 }
820 
821 void
button_widget_set_dnd_highlight(ButtonWidget * button,gboolean dnd_highlight)822 button_widget_set_dnd_highlight (ButtonWidget *button,
823 				 gboolean      dnd_highlight)
824 {
825 	g_return_if_fail (BUTTON_IS_WIDGET (button));
826 
827 	dnd_highlight = dnd_highlight != FALSE;
828 
829 	if (button->priv->dnd_highlight == dnd_highlight)
830 		return;
831 
832 	button->priv->dnd_highlight = dnd_highlight;
833 
834 	gtk_widget_queue_draw (GTK_WIDGET (button));
835 
836 	g_object_notify (G_OBJECT (button), "dnd-highlight");
837 }
838 
839 gboolean
button_widget_get_dnd_highlight(ButtonWidget * button)840 button_widget_get_dnd_highlight (ButtonWidget *button)
841 {
842 	g_return_val_if_fail (BUTTON_IS_WIDGET (button), FALSE);
843 
844 	return button->priv->dnd_highlight;
845 }
846 
847 void
button_widget_set_ignore_leave(ButtonWidget * button,gboolean ignore_leave)848 button_widget_set_ignore_leave (ButtonWidget *button,
849 				gboolean      ignore_leave)
850 {
851 	g_return_if_fail (BUTTON_IS_WIDGET (button));
852 
853 	ignore_leave = ignore_leave != FALSE;
854 
855 	if (button->priv->ignore_leave == ignore_leave)
856 		return;
857 
858 	button->priv->ignore_leave = ignore_leave;
859 
860 	gtk_widget_queue_draw (GTK_WIDGET (button));
861 
862 	g_object_notify (G_OBJECT (button), "ignore-leave");
863 }
864 
865 gboolean
button_widget_get_ignore_leave(ButtonWidget * button)866 button_widget_get_ignore_leave (ButtonWidget *button)
867 {
868 	g_return_val_if_fail (BUTTON_IS_WIDGET (button), FALSE);
869 
870 	return button->priv->ignore_leave;
871 }
872 
873 GtkIconTheme *
button_widget_get_icon_theme(ButtonWidget * button)874 button_widget_get_icon_theme (ButtonWidget *button)
875 {
876 	g_return_val_if_fail (BUTTON_IS_WIDGET (button), NULL);
877 
878 	return button->priv->icon_theme;
879 }
880 
881 cairo_surface_t *
button_widget_get_surface(ButtonWidget * button)882 button_widget_get_surface (ButtonWidget *button)
883 {
884 	g_return_val_if_fail (BUTTON_IS_WIDGET (button), NULL);
885 
886 	if (!button->priv->surface)
887 		return NULL;
888 
889 	return cairo_surface_reference (button->priv->surface);
890 }
891