1 /*
2  * mate-panel-applet.c: panel applet writing library.
3  *
4  * Copyright (c) 2010 Carlos Garcia Campos <carlosgc@gnome.org>
5  * Copyright (C) 2001 Sun Microsystems, Inc.
6  * Copyright (C) 2012-2021 MATE Developers
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library 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 GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  *
23  * Authors:
24  *     Mark McLoughlin <mark@skynet.ie>
25  */
26 
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30 
31 #include <unistd.h>
32 #include <stdlib.h>
33 #include <string.h>
34 
35 #include <glib/gi18n-lib.h>
36 #include <cairo.h>
37 #include <gdk/gdk.h>
38 #include <gdk/gdkkeysyms.h>
39 #include <gtk/gtk.h>
40 
41 #ifdef HAVE_X11
42 #include <cairo-xlib.h>
43 #include <gdk/gdkx.h>
44 #include <gtk/gtkx.h>
45 #include <X11/Xatom.h>
46 #include "panel-plug-private.h"
47 #endif
48 
49 #include "mate-panel-applet.h"
50 #include "panel-applet-private.h"
51 #include "mate-panel-applet-factory.h"
52 #include "mate-panel-applet-marshal.h"
53 #include "mate-panel-applet-enums.h"
54 
55 typedef struct {
56 	GtkWidget         *plug;
57 	GDBusConnection   *connection;
58 
59 	gboolean           out_of_process;
60 
61 	char              *id;
62 	GClosure          *closure;
63 	char              *object_path;
64 	guint              object_id;
65 	char              *prefs_path;
66 
67 	GtkUIManager      *ui_manager;
68 	GtkActionGroup    *applet_action_group;
69 	GtkActionGroup    *panel_action_group;
70 
71 	MatePanelAppletFlags   flags;
72 	MatePanelAppletOrient  orient;
73 	guint              size;
74 	char              *background;
75 
76 	int                previous_width;
77 	int                previous_height;
78 
79 	int               *size_hints;
80 	int                size_hints_len;
81 
82 	gboolean           moving_focus_out;
83 
84 	gboolean           locked;
85 	gboolean           locked_down;
86 } MatePanelAppletPrivate;
87 
88 enum {
89 	CHANGE_ORIENT,
90 	CHANGE_SIZE,
91 	CHANGE_BACKGROUND,
92 	MOVE_FOCUS_OUT_OF_APPLET,
93 	LAST_SIGNAL
94 };
95 
96 static guint mate_panel_applet_signals[LAST_SIGNAL] = { 0 };
97 
98 enum {
99 	PROP_0,
100 	PROP_OUT_OF_PROCESS,
101 	PROP_ID,
102 	PROP_CLOSURE,
103 	PROP_CONNECTION,
104 	PROP_PREFS_PATH,
105 	PROP_ORIENT,
106 	PROP_SIZE,
107 	PROP_BACKGROUND,
108 	PROP_FLAGS,
109 	PROP_SIZE_HINTS,
110 	PROP_LOCKED,
111 	PROP_LOCKED_DOWN
112 };
113 
114 static void       mate_panel_applet_handle_background   (MatePanelApplet       *applet);
115 static GtkAction *mate_panel_applet_menu_get_action     (MatePanelApplet       *applet,
116 						    const gchar       *action);
117 static void       mate_panel_applet_menu_update_actions (MatePanelApplet       *applet);
118 static void       mate_panel_applet_menu_cmd_remove     (GtkAction         *action,
119 						    MatePanelApplet       *applet);
120 static void       mate_panel_applet_menu_cmd_move       (GtkAction         *action,
121 						    MatePanelApplet       *applet);
122 static void       mate_panel_applet_menu_cmd_lock       (GtkAction         *action,
123 						    MatePanelApplet       *applet);
124 static void       mate_panel_applet_register_object     (MatePanelApplet       *applet);
125 void	_mate_panel_applet_apply_css	(GtkWidget* widget, MatePanelAppletBackgroundType type);
126 
127 static const gchar panel_menu_ui[] =
128 	"<ui>\n"
129 	"  <popup name=\"MatePanelAppletPopup\" action=\"PopupAction\">\n"
130 	"    <placeholder name=\"AppletItems\"/>\n"
131 	"    <separator/>\n"
132 	"    <menuitem name=\"RemoveItem\" action=\"Remove\"/>\n"
133 	"    <menuitem name=\"MoveItem\" action=\"Move\"/>\n"
134 	"    <separator/>\n"
135 	"    <menuitem name=\"LockItem\" action=\"Lock\"/>\n"
136 	"  </popup>\n"
137 	"</ui>\n";
138 
139 static const GtkActionEntry menu_entries[] = {
140 	{ "Remove", "list-remove", N_("_Remove From Panel"),
141 	  NULL, NULL,
142 	  G_CALLBACK (mate_panel_applet_menu_cmd_remove) },
143 	{ "Move", NULL, N_("_Move"),
144 	  NULL, NULL,
145 	  G_CALLBACK (mate_panel_applet_menu_cmd_move) }
146 };
147 
148 static const GtkToggleActionEntry menu_toggle_entries[] = {
149 	{ "Lock", NULL, N_("Loc_k To Panel"),
150 	  NULL, NULL,
151 	  G_CALLBACK (mate_panel_applet_menu_cmd_lock) }
152 };
153 
G_DEFINE_TYPE_WITH_PRIVATE(MatePanelApplet,mate_panel_applet,GTK_TYPE_EVENT_BOX)154 G_DEFINE_TYPE_WITH_PRIVATE (MatePanelApplet, mate_panel_applet, GTK_TYPE_EVENT_BOX)
155 
156 #define MATE_PANEL_APPLET_INTERFACE   "org.mate.panel.applet.Applet"
157 #define MATE_PANEL_APPLET_OBJECT_PATH "/org/mate/panel/applet/%s/%d"
158 
159 char *
160 mate_panel_applet_get_preferences_path (MatePanelApplet *applet)
161 {
162 	MatePanelAppletPrivate *priv;
163 
164 	g_return_val_if_fail (MATE_PANEL_IS_APPLET (applet), NULL);
165 
166 	priv = mate_panel_applet_get_instance_private (applet);
167 	if (!priv->prefs_path)
168 		return NULL;
169 
170 	return g_strdup (priv->prefs_path);
171 }
172 
173 static void
mate_panel_applet_set_preferences_path(MatePanelApplet * applet,const char * prefs_path)174 mate_panel_applet_set_preferences_path (MatePanelApplet *applet,
175 				  const char  *prefs_path)
176 {
177 	MatePanelAppletPrivate *priv;
178 
179 	priv = mate_panel_applet_get_instance_private (applet);
180 
181 	if (priv->prefs_path == prefs_path)
182 		return;
183 
184 	if (g_strcmp0 (priv->prefs_path, prefs_path) == 0)
185 		return;
186 
187 	if (prefs_path)
188 		priv->prefs_path = g_strdup (prefs_path);
189 
190 	g_object_notify (G_OBJECT (applet), "prefs-path");
191 }
192 
193 MatePanelAppletFlags
mate_panel_applet_get_flags(MatePanelApplet * applet)194 mate_panel_applet_get_flags (MatePanelApplet *applet)
195 {
196 	MatePanelAppletPrivate *priv;
197 
198 	g_return_val_if_fail (MATE_PANEL_IS_APPLET (applet), MATE_PANEL_APPLET_FLAGS_NONE);
199 
200 	priv = mate_panel_applet_get_instance_private (applet);
201 
202 	return priv->flags;
203 }
204 
205 void
mate_panel_applet_set_flags(MatePanelApplet * applet,MatePanelAppletFlags flags)206 mate_panel_applet_set_flags (MatePanelApplet      *applet,
207 			MatePanelAppletFlags  flags)
208 {
209 	MatePanelAppletPrivate *priv;
210 
211 	g_return_if_fail (MATE_PANEL_IS_APPLET (applet));
212 
213 	priv = mate_panel_applet_get_instance_private (applet);
214 
215 	if (priv->flags == flags)
216 		return;
217 
218 	priv->flags = flags;
219 
220 	g_object_notify (G_OBJECT (applet), "flags");
221 
222 	if (priv->connection) {
223 		GVariantBuilder  builder;
224 		GVariantBuilder  invalidated_builder;
225 		GError          *error = NULL;
226 
227 		g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
228 		g_variant_builder_init (&invalidated_builder, G_VARIANT_TYPE ("as"));
229 
230 		g_variant_builder_add (&builder, "{sv}", "Flags",
231 				       g_variant_new_uint32 (priv->flags));
232 
233 		g_dbus_connection_emit_signal (priv->connection,
234 		                               NULL,
235 		                               priv->object_path,
236 		                               "org.freedesktop.DBus.Properties",
237 		                               "PropertiesChanged",
238 		                               g_variant_new ("(sa{sv}as)",
239 		                                              MATE_PANEL_APPLET_INTERFACE,
240 		                                              &builder,
241 		                                              &invalidated_builder),
242 		                               &error);
243 		if (error) {
244 			g_printerr ("Failed to send signal PropertiesChanged::Flags: %s\n",
245 				    error->message);
246 			g_error_free (error);
247 		}
248 		g_variant_builder_clear (&builder);
249 		g_variant_builder_clear (&invalidated_builder);
250 	}
251 }
252 
253 static void
mate_panel_applet_size_hints_ensure(MatePanelApplet * applet,int new_size)254 mate_panel_applet_size_hints_ensure (MatePanelApplet *applet,
255 				int          new_size)
256 {
257 	MatePanelAppletPrivate *priv;
258 
259 	priv = mate_panel_applet_get_instance_private (applet);
260 	if (priv->size_hints && priv->size_hints_len < new_size) {
261 		g_free (priv->size_hints);
262 		priv->size_hints = g_new (gint, new_size);
263 	} else if (!priv->size_hints) {
264 		priv->size_hints = g_new (gint, new_size);
265 	}
266 	priv->size_hints_len = new_size;
267 }
268 
269 static gboolean
mate_panel_applet_size_hints_changed(MatePanelApplet * applet,const int * size_hints,int n_elements,int base_size)270 mate_panel_applet_size_hints_changed (MatePanelApplet *applet,
271 				 const int   *size_hints,
272 				 int          n_elements,
273 				 int          base_size)
274 {
275 	MatePanelAppletPrivate *priv;
276 	gint i;
277 
278 	priv = mate_panel_applet_get_instance_private (applet);
279 
280 	if (!priv->size_hints)
281 		return TRUE;
282 
283 	if (priv->size_hints_len != n_elements)
284 		return TRUE;
285 
286 	for (i = 0; i < n_elements; i++) {
287 		if (size_hints[i] + base_size != priv->size_hints[i])
288 			return TRUE;
289 	}
290 
291 	return FALSE;
292 }
293 
294 /**
295  * mate_panel_applet_set_size_hints:
296  * @applet: applet
297  * @size_hints: (array length=n_elements): List of integers
298  * @n_elements: Length of @size_hints
299  * @base_size: base_size
300  */
301 void
mate_panel_applet_set_size_hints(MatePanelApplet * applet,const int * size_hints,int n_elements,int base_size)302 mate_panel_applet_set_size_hints (MatePanelApplet *applet,
303 			     const int   *size_hints,
304 			     int          n_elements,
305 			     int          base_size)
306 {
307 	MatePanelAppletPrivate *priv;
308 	gint i;
309 
310 	/* Make sure property has really changed to avoid bus traffic */
311 	if (!mate_panel_applet_size_hints_changed (applet, size_hints, n_elements, base_size))
312 		return;
313 
314 	priv = mate_panel_applet_get_instance_private (applet);
315 
316 	mate_panel_applet_size_hints_ensure (applet, n_elements);
317 	for (i = 0; i < n_elements; i++)
318 		priv->size_hints[i] = size_hints[i] + base_size;
319 
320 	g_object_notify (G_OBJECT (applet), "size-hints");
321 
322 	if (priv->connection) {
323 		GVariantBuilder  builder;
324 		GVariantBuilder  invalidated_builder;
325 		GVariant       **children;
326 		GError          *error = NULL;
327 
328 		g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
329 		g_variant_builder_init (&invalidated_builder, G_VARIANT_TYPE ("as"));
330 
331 		children = g_new (GVariant *, priv->size_hints_len);
332 		for (i = 0; i < n_elements; i++)
333 			children[i] = g_variant_new_int32 (priv->size_hints[i]);
334 		g_variant_builder_add (&builder, "{sv}", "SizeHints",
335 		                       g_variant_new_array (G_VARIANT_TYPE_INT32,
336 		                       children, priv->size_hints_len));
337 		g_free (children);
338 
339 		g_dbus_connection_emit_signal (priv->connection,
340 		                               NULL,
341 		                               priv->object_path,
342 		                               "org.freedesktop.DBus.Properties",
343 		                               "PropertiesChanged",
344 		                               g_variant_new ("(sa{sv}as)",
345 		                                              MATE_PANEL_APPLET_INTERFACE,
346 		                                              &builder,
347 		                                              &invalidated_builder),
348 		                               &error);
349 		if (error) {
350 			g_printerr ("Failed to send signal PropertiesChanged::SizeHints: %s\n",
351 				    error->message);
352 			g_error_free (error);
353 		}
354 		g_variant_builder_clear (&builder);
355 		g_variant_builder_clear (&invalidated_builder);
356 	}
357 }
358 
359 guint
mate_panel_applet_get_size(MatePanelApplet * applet)360 mate_panel_applet_get_size (MatePanelApplet *applet)
361 {
362 	MatePanelAppletPrivate *priv;
363 
364 	g_return_val_if_fail (MATE_PANEL_IS_APPLET (applet), 0);
365 
366 	priv = mate_panel_applet_get_instance_private (applet);
367 
368 	return priv->size;
369 }
370 
371 /* Applets cannot set their size, so API is not public. */
372 static void
mate_panel_applet_set_size(MatePanelApplet * applet,guint size)373 mate_panel_applet_set_size (MatePanelApplet *applet,
374 		       guint        size)
375 {
376 	MatePanelAppletPrivate *priv;
377 
378 	g_return_if_fail (MATE_PANEL_IS_APPLET (applet));
379 
380 	priv = mate_panel_applet_get_instance_private (applet);
381 
382 	if (priv->size == size)
383 		return;
384 
385 	priv->size = size;
386 	g_signal_emit (G_OBJECT (applet),
387 		       mate_panel_applet_signals [CHANGE_SIZE],
388 		       0, size);
389 
390 	g_object_notify (G_OBJECT (applet), "size");
391 }
392 
393 MatePanelAppletOrient
mate_panel_applet_get_orient(MatePanelApplet * applet)394 mate_panel_applet_get_orient (MatePanelApplet *applet)
395 {
396 	MatePanelAppletPrivate *priv;
397 
398 	g_return_val_if_fail (MATE_PANEL_IS_APPLET (applet), 0);
399 
400 	priv = mate_panel_applet_get_instance_private (applet);
401 
402 	return priv->orient;
403 }
404 
405 /* Applets cannot set their orientation, so API is not public. */
406 static void
mate_panel_applet_set_orient(MatePanelApplet * applet,MatePanelAppletOrient orient)407 mate_panel_applet_set_orient (MatePanelApplet      *applet,
408 			 MatePanelAppletOrient orient)
409 {
410 	MatePanelAppletPrivate *priv;
411 
412 	g_return_if_fail (MATE_PANEL_IS_APPLET (applet));
413 
414 	priv = mate_panel_applet_get_instance_private (applet);
415 
416 	if (priv->orient == orient)
417 		return;
418 
419 	priv->orient = orient;
420 	g_signal_emit (G_OBJECT (applet),
421 		       mate_panel_applet_signals [CHANGE_ORIENT],
422 		       0, orient);
423 
424 	g_object_notify (G_OBJECT (applet), "orient");
425 }
426 
427 static void
mate_panel_applet_set_locked(MatePanelApplet * applet,gboolean locked)428 mate_panel_applet_set_locked (MatePanelApplet *applet,
429 			 gboolean     locked)
430 {
431 	MatePanelAppletPrivate *priv;
432 	GtkAction *action;
433 
434 	g_return_if_fail (MATE_PANEL_IS_APPLET (applet));
435 
436 	priv = mate_panel_applet_get_instance_private (applet);
437 
438 	if (priv->locked == locked)
439 		return;
440 
441 	priv->locked = locked;
442 
443 	action = mate_panel_applet_menu_get_action (applet, "Lock");
444 	g_signal_handlers_block_by_func (action,
445 					 mate_panel_applet_menu_cmd_lock,
446 					 applet);
447 	gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), locked);
448 	g_signal_handlers_unblock_by_func (action,
449 					   mate_panel_applet_menu_cmd_lock,
450 					   applet);
451 
452 	mate_panel_applet_menu_update_actions (applet);
453 
454 	g_object_notify (G_OBJECT (applet), "locked");
455 
456 	if (priv->connection) {
457 		GError *error = NULL;
458 
459 		g_dbus_connection_emit_signal (priv->connection,
460 		                               NULL,
461 		                               priv->object_path,
462 		                               MATE_PANEL_APPLET_INTERFACE,
463 		                               locked ? "Lock" : "Unlock",
464 		                               NULL, &error);
465 		if (error) {
466 			g_printerr ("Failed to send signal %s: %s\n",
467 				    locked ? "Lock" : "Unlock",
468 				    error->message);
469 			g_error_free (error);
470 		}
471 	}
472 }
473 
474 gboolean
mate_panel_applet_get_locked_down(MatePanelApplet * applet)475 mate_panel_applet_get_locked_down (MatePanelApplet *applet)
476 {
477 	MatePanelAppletPrivate *priv;
478 
479 	g_return_val_if_fail (MATE_PANEL_IS_APPLET (applet), FALSE);
480 
481 	priv = mate_panel_applet_get_instance_private (applet);
482 
483 	return priv->locked_down;
484 }
485 
486 /* Applets cannot set the lockdown state, so API is not public. */
487 static void
mate_panel_applet_set_locked_down(MatePanelApplet * applet,gboolean locked_down)488 mate_panel_applet_set_locked_down (MatePanelApplet *applet,
489 			      gboolean     locked_down)
490 {
491 	MatePanelAppletPrivate *priv;
492 
493 	g_return_if_fail (MATE_PANEL_IS_APPLET (applet));
494 
495 	priv = mate_panel_applet_get_instance_private (applet);
496 
497 	if (priv->locked_down == locked_down)
498 		return;
499 
500 	priv->locked_down = locked_down;
501 	mate_panel_applet_menu_update_actions (applet);
502 
503 	g_object_notify (G_OBJECT (applet), "locked-down");
504 }
505 
506 #ifdef HAVE_X11
507 
508 static Atom _net_wm_window_type = None;
509 static Atom _net_wm_window_type_dock = None;
510 static Atom _net_active_window = None;
511 
512 static void
mate_panel_applet_init_atoms(Display * xdisplay)513 mate_panel_applet_init_atoms (Display *xdisplay)
514 {
515 	if (_net_wm_window_type == None)
516 		_net_wm_window_type = XInternAtom (xdisplay,
517 						   "_NET_WM_WINDOW_TYPE",
518 						   False);
519 
520 	if (_net_wm_window_type_dock == None)
521 		_net_wm_window_type_dock = XInternAtom (xdisplay,
522 							"_NET_WM_WINDOW_TYPE_DOCK",
523 							False);
524 
525 	if (_net_active_window == None)
526 		_net_active_window = XInternAtom (xdisplay,
527 						  "_NET_ACTIVE_WINDOW",
528 						  False);
529 }
530 
531 static Window
mate_panel_applet_find_toplevel_dock_window(MatePanelApplet * applet,Display * xdisplay)532 mate_panel_applet_find_toplevel_dock_window (MatePanelApplet *applet,
533 					Display	    *xdisplay)
534 {
535 	GtkWidget  *toplevel;
536 	Window	    xwin;
537 	Window	    root, parent, *child;
538 	int	    num_children;
539 
540 	toplevel = gtk_widget_get_toplevel (GTK_WIDGET (applet));
541 	if (!gtk_widget_get_realized (toplevel))
542 		return None;
543 
544 	xwin = GDK_WINDOW_XID (gtk_widget_get_window (toplevel));
545 
546 	child = NULL;
547 	parent = root = None;
548 	do {
549 		Atom	type_return;
550 		Atom	window_type;
551 		int	format_return;
552 		gulong	number_return, bytes_after_return;
553 		guchar *data_return;
554 
555 		XGetWindowProperty (xdisplay,
556 				    xwin,
557 				    _net_wm_window_type,
558 				    0, 1, False,
559 				    XA_ATOM,
560 				    &type_return, &format_return,
561 				    &number_return,
562 				    &bytes_after_return,
563 				    &data_return);
564 
565 		if (type_return == XA_ATOM) {
566 			window_type = *(Atom *) data_return;
567 
568 			XFree (data_return);
569 			data_return = NULL;
570 
571 			if (window_type == _net_wm_window_type_dock)
572 				return xwin;
573 		}
574 
575 		if (!XQueryTree (xdisplay,
576 			   xwin,
577 			   &root, &parent, &child,
578 			   (guint *) &num_children)) {
579 			   return None;
580 		}
581 
582 		if (child && num_children > 0)
583 			XFree (child);
584 
585 		xwin = parent;
586 
587 	} while (xwin != None && xwin != root);
588 
589 	return None;
590 }
591 
592 #endif /* HAVE_X11 */
593 
594 /* This function
595  *   1) Gets the window id of the panel that contains the applet
596  *	using XQueryTree and XGetWindowProperty to find an ancestor
597  *	window with the _NET_WM_WINDOW_TYPE_DOCK window type.
598  *   2) Sends a _NET_ACTIVE_WINDOW message to get that panel focused
599  */
600 void
mate_panel_applet_request_focus(MatePanelApplet * applet,guint32 timestamp)601 mate_panel_applet_request_focus (MatePanelApplet	 *applet,
602 			    guint32	  timestamp)
603 {
604 #ifdef HAVE_X11
605 	MatePanelAppletPrivate *priv;
606 	GdkScreen  *screen;
607 	GdkWindow  *root;
608 	GdkDisplay *display;
609 	Display	   *xdisplay;
610 	Window	    dock_xwindow;
611 	Window	    xroot;
612 	XEvent	    xev;
613 
614 	if (!GDK_IS_X11_DISPLAY (gdk_display_get_default ()))
615 		return;
616 
617 	g_return_if_fail (MATE_PANEL_IS_APPLET (applet));
618 
619 	priv    = mate_panel_applet_get_instance_private (applet);
620 	screen  = gtk_window_get_screen (GTK_WINDOW (priv->plug));
621 	root	= gdk_screen_get_root_window (screen);
622 	display = gdk_screen_get_display (screen);
623 
624 	xdisplay = GDK_DISPLAY_XDISPLAY (display);
625 	xroot	 = GDK_WINDOW_XID (root);
626 
627 	mate_panel_applet_init_atoms (xdisplay);
628 
629 	dock_xwindow = mate_panel_applet_find_toplevel_dock_window (applet, xdisplay);
630 	if (dock_xwindow == None)
631 		return;
632 
633 	xev.xclient.type	 = ClientMessage;
634 	xev.xclient.serial	 = 0;
635 	xev.xclient.send_event	 = True;
636 	xev.xclient.window	 = dock_xwindow;
637 	xev.xclient.message_type = _net_active_window;
638 	xev.xclient.format	 = 32;
639 	xev.xclient.data.l[0]	 = 1; /* requestor type; we're an app, I guess */
640 	xev.xclient.data.l[1]	 = timestamp;
641 	xev.xclient.data.l[2]	 = None; /* "currently active window", supposedly */
642 	xev.xclient.data.l[3]	 = 0;
643 	xev.xclient.data.l[4]	 = 0;
644 
645 	XSendEvent (xdisplay,
646 		    xroot, False,
647 		    SubstructureRedirectMask | SubstructureNotifyMask,
648 		    &xev);
649 #endif
650 }
651 
652 static GtkAction *
mate_panel_applet_menu_get_action(MatePanelApplet * applet,const gchar * action)653 mate_panel_applet_menu_get_action (MatePanelApplet *applet,
654 			      const gchar *action)
655 {
656 	MatePanelAppletPrivate *priv;
657 
658 	priv = mate_panel_applet_get_instance_private (applet);
659 
660 	return gtk_action_group_get_action (priv->panel_action_group, action);
661 }
662 
663 static void
mate_panel_applet_menu_update_actions(MatePanelApplet * applet)664 mate_panel_applet_menu_update_actions (MatePanelApplet *applet)
665 {
666 	MatePanelAppletPrivate *priv;
667 
668 	priv = mate_panel_applet_get_instance_private (applet);
669 	g_object_set (mate_panel_applet_menu_get_action (applet, "Lock"),
670 	              "visible", !priv->locked_down,
671 	              NULL);
672 	g_object_set (mate_panel_applet_menu_get_action (applet, "Move"),
673 	              "sensitive", !priv->locked,
674 	              "visible", !priv->locked_down,
675 	              NULL);
676 	g_object_set (mate_panel_applet_menu_get_action (applet, "Remove"),
677 	              "sensitive", !priv->locked,
678 	              "visible", !priv->locked_down,
679 	              NULL);
680 }
681 
682 static void
mate_panel_applet_menu_cmd_remove(GtkAction * action,MatePanelApplet * applet)683 mate_panel_applet_menu_cmd_remove (GtkAction   *action,
684 			      MatePanelApplet *applet)
685 {
686 	MatePanelAppletPrivate *priv;
687 	GError *error = NULL;
688 
689 	priv = mate_panel_applet_get_instance_private (applet);
690 
691 	if (!priv->connection)
692 		return;
693 
694 	g_dbus_connection_emit_signal (priv->connection,
695 	                               NULL,
696 	                               priv->object_path,
697 	                               MATE_PANEL_APPLET_INTERFACE,
698 	                               "RemoveFromPanel",
699 	                               NULL, &error);
700 	if (error) {
701 		g_printerr ("Failed to send signal RemoveFromPanel: %s\n",
702 			    error->message);
703 		g_error_free (error);
704 	}
705 }
706 
707 static void
mate_panel_applet_menu_cmd_move(GtkAction * action,MatePanelApplet * applet)708 mate_panel_applet_menu_cmd_move (GtkAction   *action,
709 			    MatePanelApplet *applet)
710 {
711 	MatePanelAppletPrivate *priv;
712 	GError                 *error = NULL;
713 
714 	priv = mate_panel_applet_get_instance_private (applet);
715 
716 	if (!priv->connection)
717 		return;
718 
719 	g_dbus_connection_emit_signal (priv->connection,
720 				       NULL,
721 				       priv->object_path,
722 				       MATE_PANEL_APPLET_INTERFACE,
723 				       "Move",
724 				       NULL, &error);
725 	if (error) {
726 		g_printerr ("Failed to send signal RemoveFromPanel: %s\n",
727 			    error->message);
728 		g_error_free (error);
729 	}
730 }
731 
732 static void
mate_panel_applet_menu_cmd_lock(GtkAction * action,MatePanelApplet * applet)733 mate_panel_applet_menu_cmd_lock (GtkAction   *action,
734 			    MatePanelApplet *applet)
735 {
736 	gboolean locked;
737 
738 	locked = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
739 	mate_panel_applet_set_locked (applet, locked);
740 }
741 
742 void
mate_panel_applet_setup_menu(MatePanelApplet * applet,const gchar * xml,GtkActionGroup * applet_action_group)743 mate_panel_applet_setup_menu (MatePanelApplet    *applet,
744 			 const gchar    *xml,
745 			 GtkActionGroup *applet_action_group)
746 {
747 	MatePanelAppletPrivate *priv;
748 	gchar  *new_xml;
749 	GError *error = NULL;
750 
751 	g_return_if_fail (MATE_PANEL_IS_APPLET (applet));
752 	g_return_if_fail (xml != NULL);
753 
754 	priv = mate_panel_applet_get_instance_private (applet);
755 
756 	if (priv->applet_action_group)
757 		return;
758 
759 	priv->applet_action_group = g_object_ref (applet_action_group);
760 	gtk_ui_manager_insert_action_group (priv->ui_manager,
761 					    applet_action_group, 0);
762 
763 	new_xml = g_strdup_printf ("<ui><popup name=\"MatePanelAppletPopup\" action=\"AppletItems\">"
764 				   "<placeholder name=\"AppletItems\">%s\n</placeholder>\n"
765 				   "</popup></ui>\n", xml);
766 	gtk_ui_manager_add_ui_from_string (priv->ui_manager, new_xml, -1, &error);
767 	g_free (new_xml);
768 	gtk_ui_manager_ensure_update (priv->ui_manager);
769 	if (error) {
770 		g_warning ("Error merging menus: %s\n", error->message);
771 		g_error_free (error);
772 	}
773 }
774 
775 void
mate_panel_applet_setup_menu_from_file(MatePanelApplet * applet,const gchar * filename,GtkActionGroup * applet_action_group)776 mate_panel_applet_setup_menu_from_file (MatePanelApplet    *applet,
777 				   const gchar    *filename,
778 				   GtkActionGroup *applet_action_group)
779 {
780 	gchar  *xml = NULL;
781 	GError *error = NULL;
782 
783 	if (g_file_get_contents (filename, &xml, NULL, &error)) {
784 		mate_panel_applet_setup_menu (applet, xml, applet_action_group);
785 	} else {
786 		g_warning ("%s", error->message);
787 		g_error_free (error);
788 	}
789 
790 	g_free (xml);
791 }
792 
793 /**
794  * mate_panel_applet_setup_menu_from_resource:
795  * @applet: a #MatePanelApplet.
796  * @resource_path: a resource path
797  * @action_group: a #GtkActionGroup.
798  *
799  * Sets up the context menu of @applet. @filename is a resource path to a menu
800  * XML file, containing a #GtkUIManager UI definition that describes how to
801  * display the menu items. @action_group contains the various #GtkAction that
802  * are referenced in @xml.
803  *
804  * See also the <link linkend="getting-started.context-menu">Context
805  * Menu</link> section.
806  *
807  * Since: 1.20.1
808  **/
809 void
mate_panel_applet_setup_menu_from_resource(MatePanelApplet * applet,const gchar * resource_path,GtkActionGroup * action_group)810 mate_panel_applet_setup_menu_from_resource (MatePanelApplet    *applet,
811 				       const gchar    *resource_path,
812 				       GtkActionGroup *action_group)
813 {
814 	GBytes *bytes;
815 	GError *error = NULL;
816 
817 	bytes = g_resources_lookup_data (resource_path,
818 					 G_RESOURCE_LOOKUP_FLAGS_NONE,
819 					 &error);
820 
821 	if (bytes) {
822 		mate_panel_applet_setup_menu (applet,
823 					 g_bytes_get_data (bytes, NULL),
824 					 action_group);
825 	} else {
826 		g_warning ("%s", error->message);
827 		g_error_free (error);
828 	}
829 
830 	g_bytes_unref (bytes);
831 }
832 
833 static void
mate_panel_applet_finalize(GObject * object)834 mate_panel_applet_finalize (GObject *object)
835 {
836 	MatePanelApplet        *applet;
837 	MatePanelAppletPrivate *priv;
838 
839 	applet = MATE_PANEL_APPLET (object);
840 	priv   = mate_panel_applet_get_instance_private (applet);
841 
842 	if (priv->connection) {
843 		if (priv->object_id)
844 			g_dbus_connection_unregister_object (priv->connection,
845 							     priv->object_id);
846 		priv->object_id = 0;
847 		g_object_unref (priv->connection);
848 		priv->connection = NULL;
849 	}
850 
851 	if (priv->object_path) {
852 		g_free (priv->object_path);
853 		priv->object_path = NULL;
854 	}
855 
856 	mate_panel_applet_set_preferences_path (applet, NULL);
857 
858 	if (priv->applet_action_group) {
859 		g_object_unref (priv->applet_action_group);
860 		priv->applet_action_group = NULL;
861 	}
862 
863 	if (priv->panel_action_group) {
864 		g_object_unref (priv->panel_action_group);
865 		priv->panel_action_group = NULL;
866 	}
867 
868 	if (priv->ui_manager) {
869 		g_object_unref (priv->ui_manager);
870 		priv->ui_manager = NULL;
871 	}
872 
873 	g_free (priv->size_hints);
874 	g_free (priv->prefs_path);
875 	g_free (priv->background);
876 	g_free (priv->id);
877 
878 	/* closure is owned by the factory */
879 	priv->closure = NULL;
880 
881 	G_OBJECT_CLASS (mate_panel_applet_parent_class)->finalize (object);
882 }
883 
884 static gboolean
container_has_focusable_child(GtkContainer * container)885 container_has_focusable_child (GtkContainer *container)
886 {
887 	GList *list;
888 	GList *t;
889 	gboolean retval = FALSE;
890 
891 	list = gtk_container_get_children (container);
892 
893 	for (t = list; t; t = t->next) {
894 		GtkWidget *child = GTK_WIDGET (t->data);
895 		if (gtk_widget_get_can_focus (child)) {
896 			retval = TRUE;
897 			break;
898 		} else if (GTK_IS_CONTAINER (child)) {
899 			retval = container_has_focusable_child (GTK_CONTAINER (child));
900 			if (retval)
901 				break;
902 		}
903 	}
904 	g_list_free (list);
905 	return retval;
906 }
907 
908 static void
mate_panel_applet_menu_popup(MatePanelApplet * applet,GdkEvent * event)909 mate_panel_applet_menu_popup (MatePanelApplet *applet,
910                               GdkEvent    *event)
911 {
912 	MatePanelAppletPrivate *priv;
913 	GtkWidget *menu;
914 
915 	priv = mate_panel_applet_get_instance_private (applet);
916 	menu = gtk_ui_manager_get_widget (priv->ui_manager,
917 	                                  "/MatePanelAppletPopup");
918 
919 /* Set up theme and transparency support */
920 	GtkWidget *toplevel = gtk_widget_get_toplevel (menu);
921 /* Fix any failures of compiz/other wm's to communicate with gtk for transparency */
922 	GdkScreen *screen = gtk_widget_get_screen(GTK_WIDGET(toplevel));
923 	GdkVisual *visual = gdk_screen_get_rgba_visual(screen);
924 	gtk_widget_set_visual(GTK_WIDGET(toplevel), visual);
925 /* Set menu and it's toplevel window to follow panel theme */
926 	GtkStyleContext *context;
927 	context = gtk_widget_get_style_context (GTK_WIDGET(toplevel));
928 	gtk_style_context_add_class(context,"gnome-panel-menu-bar");
929 	gtk_style_context_add_class(context,"mate-panel-menu-bar");
930 	GdkGravity widget_anchor = GDK_GRAVITY_NORTH_WEST;
931 	GdkGravity menu_anchor = GDK_GRAVITY_NORTH_WEST;
932 	switch (priv->orient) {
933 	case MATE_PANEL_APPLET_ORIENT_UP:
934 		menu_anchor = GDK_GRAVITY_SOUTH_WEST;
935 		break;
936 	case MATE_PANEL_APPLET_ORIENT_DOWN:
937 		widget_anchor = GDK_GRAVITY_SOUTH_WEST;
938 		break;
939 	case MATE_PANEL_APPLET_ORIENT_LEFT:
940 		menu_anchor = GDK_GRAVITY_NORTH_EAST;
941 		break;
942 	case MATE_PANEL_APPLET_ORIENT_RIGHT:
943 		widget_anchor = GDK_GRAVITY_NORTH_EAST;
944 		break;
945 	}
946 	gtk_menu_popup_at_widget (GTK_MENU (menu),
947 	                          GTK_WIDGET (applet),
948 	                          widget_anchor,
949 	                          menu_anchor,
950 	                          event);
951 }
952 
953 static gboolean
mate_panel_applet_can_focus(GtkWidget * widget)954 mate_panel_applet_can_focus (GtkWidget *widget)
955 {
956 	/*
957 	 * A MatePanelApplet widget can focus if it has a tooltip or it does
958 	 * not have any focusable children.
959 	 */
960 	if (gtk_widget_get_has_tooltip (widget))
961 		return TRUE;
962 
963 	if (!MATE_PANEL_IS_APPLET (widget))
964 		return FALSE;
965 
966 	return !container_has_focusable_child (GTK_CONTAINER (widget));
967 }
968 
969 /* Taken from libmatecomponentui/matecomponent/matecomponent-plug.c */
970 static gboolean
mate_panel_applet_button_event(MatePanelApplet * applet,GdkEventButton * event)971 mate_panel_applet_button_event (MatePanelApplet      *applet,
972 			   GdkEventButton *event)
973 {
974 #ifdef HAVE_X11
975 	MatePanelAppletPrivate *priv;
976 	GtkWidget              *widget;
977 	GdkWindow              *window;
978 	GdkWindow              *socket_window;
979 	XEvent                  xevent;
980 	GdkDisplay             *display;
981 
982 	priv = mate_panel_applet_get_instance_private (applet);
983 
984 	if (!priv->out_of_process)
985 		return FALSE;
986 
987 	widget = priv->plug;
988 
989 	if (!gtk_widget_is_toplevel (widget))
990 		return FALSE;
991 
992 	window = gtk_widget_get_window (widget);
993 	socket_window = gtk_plug_get_socket_window (GTK_PLUG (widget));
994 
995 	display = gdk_display_get_default ();
996 
997 	if (!GDK_IS_X11_DISPLAY (display))
998 		return FALSE;
999 
1000 	if (event->type == GDK_BUTTON_PRESS) {
1001 		GdkSeat *seat;
1002 
1003 		xevent.xbutton.type = ButtonPress;
1004 
1005 		seat = gdk_display_get_default_seat (display);
1006 
1007 		/* X does an automatic pointer grab on button press
1008 		 * if we have both button press and release events
1009 		 * selected.
1010 		 * We don't want to hog the pointer on our parent.
1011 		 */
1012 		gdk_seat_ungrab (seat);
1013 	} else {
1014 		xevent.xbutton.type = ButtonRelease;
1015 	}
1016 
1017 	xevent.xbutton.display     = GDK_WINDOW_XDISPLAY (window);
1018 	xevent.xbutton.window      = GDK_WINDOW_XID (socket_window);
1019 	xevent.xbutton.root        = GDK_WINDOW_XID (gdk_screen_get_root_window
1020 							 (gdk_window_get_screen (window)));
1021 	/*
1022 	 * FIXME: the following might cause
1023 	 *        big problems for non-GTK apps
1024 	 */
1025 	xevent.xbutton.x           = 0;
1026 	xevent.xbutton.y           = 0;
1027 	xevent.xbutton.x_root      = 0;
1028 	xevent.xbutton.y_root      = 0;
1029 	xevent.xbutton.state       = event->state;
1030 	xevent.xbutton.button      = event->button;
1031 	xevent.xbutton.same_screen = TRUE; /* FIXME ? */
1032 
1033 	gdk_x11_display_error_trap_push (display);
1034 
1035 	XSendEvent (GDK_WINDOW_XDISPLAY (window),
1036 		    GDK_WINDOW_XID (socket_window),
1037 		    False, NoEventMask, &xevent);
1038 
1039 	gdk_display_flush (display);
1040 	gdk_x11_display_error_trap_pop_ignored (display);
1041 
1042 	return TRUE;
1043 #else
1044 	return FALSE;
1045 #endif
1046 }
1047 
1048 static gboolean
mate_panel_applet_button_press(GtkWidget * widget,GdkEventButton * event)1049 mate_panel_applet_button_press (GtkWidget      *widget,
1050 			   GdkEventButton *event)
1051 {
1052 	MatePanelApplet *applet = MATE_PANEL_APPLET (widget);
1053 
1054 	if (!container_has_focusable_child (GTK_CONTAINER (applet))) {
1055 		if (!gtk_widget_has_focus (widget)) {
1056 			gtk_widget_set_can_focus (widget, TRUE);
1057 			gtk_widget_grab_focus (widget);
1058 		}
1059 	}
1060 
1061 	if (event->button == 3) {
1062 		mate_panel_applet_menu_popup (applet, (GdkEvent *) event);
1063 
1064 		return TRUE;
1065 	}
1066 
1067 	return mate_panel_applet_button_event (applet, event);
1068 }
1069 
1070 static gboolean
mate_panel_applet_button_release(GtkWidget * widget,GdkEventButton * event)1071 mate_panel_applet_button_release (GtkWidget      *widget,
1072 			     GdkEventButton *event)
1073 {
1074 	MatePanelApplet *applet = MATE_PANEL_APPLET (widget);
1075 
1076 	return mate_panel_applet_button_event (applet, event);
1077 }
1078 
1079 /*Open the applet context menu only on Menu key
1080  *Do not open it on Return or some applets won't work
1081  */
1082 static gboolean
mate_panel_applet_key_press_event(GtkWidget * widget,GdkEventKey * event)1083 mate_panel_applet_key_press_event (GtkWidget   *widget,
1084 			      GdkEventKey *event)
1085 {
1086     if (event->keyval == GDK_KEY_Menu) {
1087         mate_panel_applet_menu_popup (MATE_PANEL_APPLET (widget), (GdkEvent *) event);
1088         return TRUE;
1089     }
1090     else
1091         return FALSE;
1092 }
1093 
1094 static void
mate_panel_applet_get_preferred_width(GtkWidget * widget,int * minimum_width,int * natural_width)1095 mate_panel_applet_get_preferred_width (GtkWidget *widget,
1096 				       int       *minimum_width,
1097 				       int       *natural_width)
1098 {
1099 	GTK_WIDGET_CLASS (mate_panel_applet_parent_class)->get_preferred_width (widget,
1100 										minimum_width,
1101 										natural_width);
1102 
1103 #if !GTK_CHECK_VERSION (3, 23, 0)
1104 	MatePanelAppletPrivate *priv;
1105 
1106 	priv = mate_panel_applet_get_instance_private (MATE_PANEL_APPLET (widget));
1107 	if (priv->out_of_process) {
1108 		/* Out-of-process applets end up scaled up doubly with GTK 3.22.
1109 		 * For these builds divide by the scale factor to ensure
1110 		 * they are back at their own intended size.
1111 		 */
1112 		gint scale;
1113 		scale = gtk_widget_get_scale_factor (widget);
1114 		*minimum_width /= scale;
1115 		*natural_width /= scale;
1116 	}
1117 #endif
1118 }
1119 
1120 static void
mate_panel_applet_get_preferred_height(GtkWidget * widget,int * minimum_height,int * natural_height)1121 mate_panel_applet_get_preferred_height (GtkWidget *widget,
1122 					int       *minimum_height,
1123 					int       *natural_height)
1124 {
1125 	GTK_WIDGET_CLASS (mate_panel_applet_parent_class)->get_preferred_height (widget,
1126 										minimum_height,
1127 										natural_height);
1128 
1129 #if !GTK_CHECK_VERSION (3, 23, 0)
1130 	MatePanelAppletPrivate *priv;
1131 
1132 	priv = mate_panel_applet_get_instance_private (MATE_PANEL_APPLET (widget));
1133 	if (priv->out_of_process) {
1134 		gint scale;
1135 		/* Out-of-process applets end up scaled up doubly with GTK 3.22.
1136 		 * For these builds divide by the scale factor to ensure
1137 		 * they are back at their own intended size.
1138 		 */
1139 		scale = gtk_widget_get_scale_factor (widget);
1140 		*minimum_height /= scale;
1141 		*natural_height /= scale;
1142 	}
1143 #endif
1144 }
1145 
1146 static GtkSizeRequestMode
mate_panel_applet_get_request_mode(GtkWidget * widget)1147 mate_panel_applet_get_request_mode (GtkWidget *widget)
1148 {
1149 	/* Do not use GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH
1150 	 * or GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT
1151 	 * to avoid problems with in-process applets
1152 	 * when the panel is not expanded
1153 	 * See https://github.com/mate-desktop/mate-panel/issues/797
1154 	 * and https://github.com/mate-desktop/mate-panel/issues/799
1155 	 * Out of process applets already use GTK_SIZE_REQUEST_CONSTANT_SIZE
1156 	 */
1157 	return GTK_SIZE_REQUEST_CONSTANT_SIZE;
1158 }
1159 
1160 static void
mate_panel_applet_size_allocate(GtkWidget * widget,GtkAllocation * allocation)1161 mate_panel_applet_size_allocate (GtkWidget     *widget,
1162 			    GtkAllocation *allocation)
1163 {
1164 	MatePanelAppletPrivate *priv;
1165 	GtkAllocation  child_allocation;
1166 	MatePanelApplet   *applet;
1167 
1168 	if (!mate_panel_applet_can_focus (widget)) {
1169 		GTK_WIDGET_CLASS (mate_panel_applet_parent_class)->size_allocate (widget, allocation);
1170 	} else {
1171 		int border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
1172 
1173 		gtk_widget_set_allocation (widget, allocation);
1174 		GtkBin *bin = GTK_BIN (widget);
1175 
1176 		child_allocation.x = 0;
1177 		child_allocation.y = 0;
1178 
1179 		child_allocation.width  = MAX (allocation->width  - border_width * 2, 0);
1180 		child_allocation.height = MAX (allocation->height - border_width * 2, 0);
1181 
1182 		if (gtk_widget_get_realized (widget))
1183 			gdk_window_move_resize (gtk_widget_get_window (widget),
1184 						allocation->x + border_width,
1185 						allocation->y + border_width,
1186 						child_allocation.width,
1187 						child_allocation.height);
1188 
1189 		GtkWidget *child = gtk_bin_get_child (bin);
1190 		if (child)
1191 			gtk_widget_size_allocate (child, &child_allocation);
1192 	}
1193 
1194 	applet = MATE_PANEL_APPLET (widget);
1195 	priv   = mate_panel_applet_get_instance_private (applet);
1196 
1197 	if ((priv->previous_height != allocation->height) ||
1198 	    (priv->previous_width  != allocation->width)) {
1199 		priv->previous_height = allocation->height;
1200 		priv->previous_width  = allocation->width;
1201 		mate_panel_applet_handle_background (applet);
1202 	}
1203 }
1204 
mate_panel_applet_draw(GtkWidget * widget,cairo_t * cr)1205 static gboolean mate_panel_applet_draw(GtkWidget* widget, cairo_t* cr)
1206 {
1207 	GtkStyleContext *context;
1208 	int border_width;
1209 	gdouble x, y, width, height;
1210 
1211 	GTK_WIDGET_CLASS (mate_panel_applet_parent_class)->draw(widget, cr);
1212 
1213         if (!gtk_widget_has_focus (widget))
1214 		return FALSE;
1215 
1216 	width = gtk_widget_get_allocated_width (widget);
1217 	height = gtk_widget_get_allocated_height (widget);
1218 
1219 	border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
1220 
1221 	x = 0;
1222 	y = 0;
1223 
1224 	width -= 2 * border_width;
1225 	height -= 2 * border_width;
1226 
1227 	context = gtk_widget_get_style_context (widget);
1228 	gtk_style_context_save (context);
1229 
1230 	cairo_save (cr);
1231 	gtk_render_focus (context, cr, x, y, width, height);
1232 	cairo_restore (cr);
1233 
1234 	gtk_style_context_restore (context);
1235 
1236 	return FALSE;
1237 }
1238 
1239 static gboolean
mate_panel_applet_focus(GtkWidget * widget,GtkDirectionType dir)1240 mate_panel_applet_focus (GtkWidget        *widget,
1241 		    GtkDirectionType  dir)
1242 {
1243 	MatePanelAppletPrivate *priv;
1244 	gboolean ret;
1245 	GtkWidget *previous_focus_child;
1246 
1247 	g_return_val_if_fail (MATE_PANEL_IS_APPLET (widget), FALSE);
1248 
1249 	priv = mate_panel_applet_get_instance_private (MATE_PANEL_APPLET (widget));
1250 	if (priv->moving_focus_out) {
1251 		/*
1252 		 * Applet will retain focus if there is nothing else on the
1253 		 * panel to get focus
1254 		 */
1255 		priv->moving_focus_out = FALSE;
1256 		return FALSE;
1257 	}
1258 
1259 	previous_focus_child = gtk_container_get_focus_child (GTK_CONTAINER (widget));
1260 	if (!previous_focus_child && !gtk_widget_has_focus (widget)) {
1261 		if (gtk_widget_get_has_tooltip (widget)) {
1262 			gtk_widget_set_can_focus (widget, TRUE);
1263 			gtk_widget_grab_focus (widget);
1264 			gtk_widget_set_can_focus (widget, FALSE);
1265 			return TRUE;
1266 		}
1267 	}
1268 	ret = GTK_WIDGET_CLASS (mate_panel_applet_parent_class)->focus (widget, dir);
1269 
1270 	if (!ret && !previous_focus_child) {
1271 		if (!gtk_widget_has_focus (widget))  {
1272 			/*
1273 			 * Applet does not have a widget which can focus so set
1274 			 * the focus on the applet unless it already had focus
1275 			 * because it had a tooltip.
1276 			 */
1277 			gtk_widget_set_can_focus (widget, TRUE);
1278 			gtk_widget_grab_focus (widget);
1279 			gtk_widget_set_can_focus (widget, FALSE);
1280 			ret = TRUE;
1281 		}
1282 	}
1283 
1284 	return ret;
1285 }
1286 
1287 static gboolean
mate_panel_applet_parse_color(const gchar * color_str,GdkRGBA * color)1288 mate_panel_applet_parse_color (const gchar *color_str,
1289 			       GdkRGBA     *color)
1290 {
1291 	g_assert (color_str && color);
1292 
1293 	return gdk_rgba_parse (color, color_str);
1294 }
1295 
1296 #ifdef HAVE_X11
1297 static gboolean
mate_panel_applet_parse_pixmap_str(const char * str,Window * xid,int * x,int * y)1298 mate_panel_applet_parse_pixmap_str (const char *str,
1299 			       Window          *xid,
1300 			       int             *x,
1301 			       int             *y)
1302 {
1303 	char **elements;
1304 	char  *tmp;
1305 
1306 	g_return_val_if_fail (str != NULL, FALSE);
1307 	g_return_val_if_fail (xid != NULL, FALSE);
1308 	g_return_val_if_fail (x != NULL, FALSE);
1309 	g_return_val_if_fail (y != NULL, FALSE);
1310 
1311 	elements = g_strsplit (str, ",", -1);
1312 
1313 	if (!elements)
1314 		return FALSE;
1315 
1316 	if (!elements [0] || !*elements [0] ||
1317 	    !elements [1] || !*elements [1] ||
1318 	    !elements [2] || !*elements [2])
1319 		goto ERROR_AND_FREE;
1320 
1321 	*xid = strtol (elements [0], &tmp, 10);
1322 	if (tmp == elements [0])
1323 		goto ERROR_AND_FREE;
1324 
1325 	*x   = strtol (elements [1], &tmp, 10);
1326 	if (tmp == elements [1])
1327 		goto ERROR_AND_FREE;
1328 
1329 	*y   = strtol (elements [2], &tmp, 10);
1330 	if (tmp == elements [2])
1331 		goto ERROR_AND_FREE;
1332 
1333 	g_strfreev (elements);
1334 	return TRUE;
1335 
1336 ERROR_AND_FREE:
1337 	g_strfreev (elements);
1338 	return FALSE;
1339 }
1340 
1341 static cairo_surface_t *
mate_panel_applet_create_foreign_surface_for_display(GdkDisplay * display,GdkVisual * visual,Window xid)1342 mate_panel_applet_create_foreign_surface_for_display (GdkDisplay *display,
1343                                                       GdkVisual  *visual,
1344                                                       Window      xid)
1345 {
1346 	Status result = 0;
1347 	Window window;
1348 	gint x, y;
1349 	guint width, height, border, depth;
1350 
1351 	gdk_x11_display_error_trap_push (display);
1352 	result = XGetGeometry (GDK_DISPLAY_XDISPLAY (display), xid, &window,
1353 	                       &x, &y, &width, &height, &border, &depth);
1354 	gdk_x11_display_error_trap_pop_ignored (display);
1355 
1356 	if (result == 0)
1357 		return NULL;
1358 
1359 	return cairo_xlib_surface_create (GDK_DISPLAY_XDISPLAY (display),
1360 	                                  xid, gdk_x11_visual_get_xvisual (visual),
1361 	                                  width, height);
1362 }
1363 
1364 static cairo_pattern_t *
mate_panel_applet_get_pattern_from_pixmap(MatePanelApplet * applet,Window xid,int x,int y)1365 mate_panel_applet_get_pattern_from_pixmap (MatePanelApplet *applet,
1366 			 Window           xid,
1367 			 int              x,
1368 			 int              y)
1369 {
1370 	cairo_surface_t *background;
1371 	cairo_surface_t *surface;
1372 	GdkWindow       *window;
1373 	int              width;
1374 	int              height;
1375 	GdkDisplay      *display;
1376 	cairo_t         *cr;
1377 	cairo_pattern_t *pattern;
1378 
1379 	g_return_val_if_fail (MATE_PANEL_IS_APPLET (applet), NULL);
1380 
1381 	if (!gtk_widget_get_realized (GTK_WIDGET (applet)))
1382 		return NULL;
1383 
1384 	window = gtk_widget_get_window (GTK_WIDGET (applet));
1385 	display = gdk_window_get_display (window);
1386 
1387 	background = mate_panel_applet_create_foreign_surface_for_display (display,
1388 									   gdk_window_get_visual (window),
1389 									   xid);
1390 
1391 	/* background can be NULL if the user changes the background very fast.
1392 	 * We'll get the next update, so it's not a big deal. */
1393 	if (!background || cairo_surface_status (background) != CAIRO_STATUS_SUCCESS) {
1394 		if (background)
1395 			cairo_surface_destroy (background);
1396 		return NULL;
1397 	}
1398 
1399 	width = gdk_window_get_width(window);
1400 	height = gdk_window_get_height(window);
1401 	surface = gdk_window_create_similar_surface (window,
1402 	                            CAIRO_CONTENT_COLOR_ALPHA,
1403 	                            width,
1404 	                            height);
1405 	gdk_x11_display_error_trap_push (display);
1406 	cr = cairo_create (surface);
1407 	cairo_set_source_surface (cr, background, -x, -y);
1408 	cairo_rectangle (cr, 0, 0, width, height);
1409 	cairo_fill (cr);
1410 	gdk_x11_display_error_trap_pop_ignored (display);
1411 
1412 	cairo_surface_destroy (background);
1413 	pattern = NULL;
1414 
1415 	if (cairo_status (cr) == CAIRO_STATUS_SUCCESS) {
1416 		pattern = cairo_pattern_create_for_surface (surface);
1417 	}
1418 
1419 	cairo_destroy (cr);
1420 	cairo_surface_destroy (surface);
1421 
1422 	return pattern;
1423 }
1424 #endif
1425 
1426 static MatePanelAppletBackgroundType
mate_panel_applet_handle_background_string(MatePanelApplet * applet,GdkRGBA * color,cairo_pattern_t ** pattern)1427 mate_panel_applet_handle_background_string (MatePanelApplet  *applet,
1428 					    GdkRGBA          *color,
1429 					    cairo_pattern_t **pattern)
1430 {
1431 	MatePanelAppletPrivate         *priv;
1432 	MatePanelAppletBackgroundType   retval;
1433 	char                          **elements;
1434 
1435 	priv = mate_panel_applet_get_instance_private (applet);
1436 	retval = PANEL_NO_BACKGROUND;
1437 
1438 	if (!gtk_widget_get_realized (GTK_WIDGET (applet)) || !priv->background)
1439 		return retval;
1440 
1441 	elements = g_strsplit (priv->background, ":", -1);
1442 
1443 	if (elements [0] && !strcmp (elements [0], "none" )) {
1444 		retval = PANEL_NO_BACKGROUND;
1445 
1446 	} else if (elements [0] && !strcmp (elements [0], "color")) {
1447 		g_return_val_if_fail (color != NULL, PANEL_NO_BACKGROUND);
1448 
1449 		if (!elements [1] || !mate_panel_applet_parse_color (elements [1], color)) {
1450 
1451 			g_warning ("Incomplete '%s' background type received", elements [0]);
1452 			g_strfreev (elements);
1453 			return PANEL_NO_BACKGROUND;
1454 		}
1455 
1456 		retval = PANEL_COLOR_BACKGROUND;
1457 
1458 	} else if (elements [0] && !strcmp (elements [0], "pixmap")) {
1459 #ifdef HAVE_X11
1460 		if (GDK_IS_X11_DISPLAY (gdk_display_get_default ())) {
1461 			Window pixmap_id;
1462 			int             x, y;
1463 
1464 			g_return_val_if_fail (pattern != NULL, PANEL_NO_BACKGROUND);
1465 
1466 			if (!mate_panel_applet_parse_pixmap_str (elements [1], &pixmap_id, &x, &y)) {
1467 				g_warning ("Incomplete '%s' background type received: %s",
1468 					elements [0], elements [1]);
1469 
1470 				g_strfreev (elements);
1471 				return PANEL_NO_BACKGROUND;
1472 			}
1473 
1474 			*pattern = mate_panel_applet_get_pattern_from_pixmap (applet, pixmap_id, x, y);
1475 			if (!*pattern) {
1476 				g_warning ("Failed to get pattern %s", elements [1]);
1477 				g_strfreev (elements);
1478 				return PANEL_NO_BACKGROUND;
1479 			}
1480 
1481 			retval = PANEL_PIXMAP_BACKGROUND;
1482 		} else
1483 #endif
1484 		{ /* not using X11 */
1485 			g_warning("Received pixmap background type, which is only supported on X11");
1486 		}
1487 	} else
1488 		g_warning ("Unknown background type received");
1489 
1490 	g_strfreev (elements);
1491 
1492 	return retval;
1493 }
1494 
1495 MatePanelAppletBackgroundType
mate_panel_applet_get_background(MatePanelApplet * applet,GdkRGBA * color,cairo_pattern_t ** pattern)1496 mate_panel_applet_get_background (MatePanelApplet  *applet,
1497 				  GdkRGBA          *color,
1498 				  cairo_pattern_t **pattern)
1499 {
1500 	g_return_val_if_fail (MATE_PANEL_IS_APPLET (applet), PANEL_NO_BACKGROUND);
1501 
1502 	/* initial sanity */
1503 	if (pattern != NULL)
1504 		*pattern = NULL;
1505 
1506 	if (color != NULL)
1507 		memset (color, 0, sizeof (GdkRGBA));
1508 
1509 	return mate_panel_applet_handle_background_string (applet, color, pattern);
1510 }
1511 
1512 static void
mate_panel_applet_set_background_string(MatePanelApplet * applet,const gchar * background)1513 mate_panel_applet_set_background_string (MatePanelApplet *applet,
1514 				    const gchar *background)
1515 {
1516 	MatePanelAppletPrivate *priv;
1517 
1518 	priv = mate_panel_applet_get_instance_private (applet);
1519 
1520 	if (priv->background == background)
1521 		return;
1522 
1523 	if (g_strcmp0 (priv->background, background) == 0)
1524 		return;
1525 
1526 	g_free (priv->background);
1527 	priv->background = background ? g_strdup (background) : NULL;
1528 	mate_panel_applet_handle_background (applet);
1529 
1530 	g_object_notify (G_OBJECT (applet), "background");
1531 }
1532 
1533 static void
mate_panel_applet_handle_background(MatePanelApplet * applet)1534 mate_panel_applet_handle_background (MatePanelApplet *applet)
1535 {
1536 	MatePanelAppletBackgroundType  type;
1537 
1538 	GdkRGBA                    color;
1539 	cairo_pattern_t           *pattern;
1540 
1541 	type = mate_panel_applet_get_background (applet, &color, &pattern);
1542 
1543 	if (!gdk_screen_is_composited (gdk_screen_get_default ())) {
1544 		color.alpha = 1.;
1545 	}
1546 
1547 	switch (type) {
1548 	case PANEL_NO_BACKGROUND:
1549 		g_signal_emit (G_OBJECT (applet),
1550 			       mate_panel_applet_signals [CHANGE_BACKGROUND],
1551 			       0, PANEL_NO_BACKGROUND, NULL, NULL);
1552 		break;
1553 	case PANEL_COLOR_BACKGROUND:
1554 		g_signal_emit (G_OBJECT (applet),
1555 			       mate_panel_applet_signals [CHANGE_BACKGROUND],
1556 			       0, PANEL_COLOR_BACKGROUND, &color, NULL);
1557 		break;
1558 	case PANEL_PIXMAP_BACKGROUND:
1559 		g_signal_emit (G_OBJECT (applet),
1560 			       mate_panel_applet_signals [CHANGE_BACKGROUND],
1561 
1562 			       0, PANEL_PIXMAP_BACKGROUND, NULL, pattern);
1563 
1564 
1565 		cairo_pattern_destroy (pattern);
1566 
1567 		break;
1568 	default:
1569 		g_assert_not_reached ();
1570 		break;
1571 	}
1572 }
1573 
1574 static void
mate_panel_applet_realize(GtkWidget * widget)1575 mate_panel_applet_realize (GtkWidget *widget)
1576 {
1577 	MatePanelApplet        *applet;
1578 	MatePanelAppletPrivate *priv;
1579 
1580 	GTK_WIDGET_CLASS (mate_panel_applet_parent_class)->realize (widget);
1581 
1582 	applet = MATE_PANEL_APPLET (widget);
1583 	priv = mate_panel_applet_get_instance_private (applet);
1584 	if (priv->background)
1585 		mate_panel_applet_handle_background (applet);
1586 }
1587 
1588 static void
mate_panel_applet_move_focus_out_of_applet(MatePanelApplet * applet,GtkDirectionType dir)1589 mate_panel_applet_move_focus_out_of_applet (MatePanelApplet      *applet,
1590 				       GtkDirectionType  dir)
1591 {
1592 	MatePanelAppletPrivate *priv;
1593 	GtkWidget              *toplevel;
1594 
1595 	priv = mate_panel_applet_get_instance_private (applet);
1596 	priv->moving_focus_out = TRUE;
1597 	toplevel = gtk_widget_get_toplevel (GTK_WIDGET (applet));
1598 	g_return_if_fail (toplevel);
1599 
1600 	gtk_widget_child_focus (toplevel, dir);
1601 	priv->moving_focus_out = FALSE;
1602 }
1603 
1604 static void
mate_panel_applet_change_background(MatePanelApplet * applet,MatePanelAppletBackgroundType type,GdkRGBA * color,cairo_pattern_t * pattern)1605 mate_panel_applet_change_background(MatePanelApplet *applet,
1606 				    MatePanelAppletBackgroundType type,
1607 				    GdkRGBA* color,
1608 				    cairo_pattern_t *pattern)
1609 {
1610 	MatePanelAppletPrivate *priv;
1611 	GdkWindow              *window;
1612 
1613 	priv = mate_panel_applet_get_instance_private (applet);
1614 
1615 	if (priv->out_of_process)
1616 		window = gtk_widget_get_window (GTK_WIDGET (priv->plug));
1617 	else
1618 		window = gtk_widget_get_window (GTK_WIDGET (applet));
1619 
1620 	gtk_widget_set_app_paintable (GTK_WIDGET (applet),TRUE);
1621 
1622 	if (priv->out_of_process)
1623 		_mate_panel_applet_apply_css (GTK_WIDGET (priv->plug),type);
1624 
1625 	switch (type) {
1626 		case PANEL_NO_BACKGROUND:
1627 			if (priv->out_of_process){
1628 				pattern = cairo_pattern_create_rgba (0,0,0,0);     /* Using NULL here breaks transparent */
1629 				gdk_window_set_background_pattern(window,pattern); /* backgrounds set by GTK theme */
1630 			}
1631 			break;
1632 		case PANEL_COLOR_BACKGROUND:
1633 			if (priv->out_of_process){
1634 				gdk_window_set_background_rgba(window,color);
1635 				gtk_widget_queue_draw (priv->plug); /*change the bg right away always */
1636 			}
1637 			break;
1638 		case PANEL_PIXMAP_BACKGROUND:
1639 			if (priv->out_of_process){
1640 				gdk_window_set_background_pattern(window,pattern);
1641 				gtk_widget_queue_draw (priv->plug); /*change the bg right away always */
1642 			}
1643 			break;
1644 		default:
1645 			g_assert_not_reached ();
1646 			break;
1647 	}
1648 
1649 	if (priv->out_of_process){
1650 		GtkStyleContext *context;
1651 
1652 		context = gtk_widget_get_style_context (GTK_WIDGET (priv->plug));
1653 
1654 		if (priv->orient == MATE_PANEL_APPLET_ORIENT_UP ||
1655 		    priv->orient == MATE_PANEL_APPLET_ORIENT_DOWN)
1656 			gtk_style_context_add_class (context, "horizontal");
1657 		else
1658 			gtk_style_context_add_class (context, "vertical");
1659 	}
1660 }
1661 
1662 static void
mate_panel_applet_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1663 mate_panel_applet_get_property (GObject    *object,
1664                                 guint       prop_id,
1665                                 GValue     *value,
1666                                 GParamSpec *pspec)
1667 {
1668 	MatePanelApplet        *applet;
1669 	MatePanelAppletPrivate *priv;
1670 
1671 	applet = MATE_PANEL_APPLET (object);
1672 	priv = mate_panel_applet_get_instance_private (applet);
1673 
1674 	switch (prop_id) {
1675 		case PROP_OUT_OF_PROCESS:
1676 			g_value_set_boolean (value, priv->out_of_process);
1677 			break;
1678 		case PROP_ID:
1679 			g_value_set_string (value, priv->id);
1680 			break;
1681 		case PROP_CLOSURE:
1682 			g_value_set_pointer (value, priv->closure);
1683 			break;
1684 		case PROP_CONNECTION:
1685 			g_value_set_object (value, priv->connection);
1686 			break;
1687 		case PROP_PREFS_PATH:
1688 			g_value_set_string (value, priv->prefs_path);
1689 			break;
1690 		case PROP_ORIENT:
1691 			g_value_set_uint (value, priv->orient);
1692 			break;
1693 		case PROP_SIZE:
1694 			g_value_set_uint (value, priv->size);
1695 			break;
1696 		case PROP_BACKGROUND:
1697 			g_value_set_string (value, priv->background);
1698 			break;
1699 		case PROP_FLAGS:
1700 			g_value_set_uint (value, priv->flags);
1701 			break;
1702 		case PROP_SIZE_HINTS: {
1703 			GVariant **children;
1704 			GVariant  *variant;
1705 			gint       i;
1706 
1707 			children = g_new (GVariant *, priv->size_hints_len);
1708 			for (i = 0; i < priv->size_hints_len; i++)
1709 				children[i] = g_variant_new_int32 (priv->size_hints[i]);
1710 			variant = g_variant_new_array (G_VARIANT_TYPE_INT32,
1711 			                               children, priv->size_hints_len);
1712 			g_free (children);
1713 			g_value_set_pointer (value, variant);
1714 			break;
1715 		}
1716 		case PROP_LOCKED:
1717 			g_value_set_boolean (value, priv->locked);
1718 			break;
1719 		case PROP_LOCKED_DOWN:
1720 			g_value_set_boolean (value, priv->locked_down);
1721 			break;
1722 		default:
1723 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1724 	}
1725 }
1726 
1727 static void
mate_panel_applet_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1728 mate_panel_applet_set_property (GObject      *object,
1729 			   guint         prop_id,
1730 			   const GValue *value,
1731 			   GParamSpec   *pspec)
1732 {
1733 	MatePanelApplet        *applet;
1734 	MatePanelAppletPrivate *priv;
1735 
1736 	applet = MATE_PANEL_APPLET (object);
1737 	priv   = mate_panel_applet_get_instance_private (applet);
1738 
1739 	switch (prop_id) {
1740 	case PROP_OUT_OF_PROCESS:
1741 		priv->out_of_process = g_value_get_boolean (value);
1742 		break;
1743 	case PROP_ID:
1744 		priv->id = g_value_dup_string (value);
1745 		break;
1746 	case PROP_CLOSURE:
1747 		priv->closure = g_value_get_pointer (value);
1748 		g_closure_set_marshal (priv->closure,
1749 				       mate_panel_applet_marshal_BOOLEAN__STRING);
1750 		break;
1751 	case PROP_CONNECTION:
1752 		priv->connection = g_value_dup_object (value);
1753 		break;
1754 	case PROP_PREFS_PATH:
1755 		mate_panel_applet_set_preferences_path (applet, g_value_get_string (value));
1756 		break;
1757 	case PROP_ORIENT:
1758 		mate_panel_applet_set_orient (applet, g_value_get_uint (value));
1759 		break;
1760 	case PROP_SIZE:
1761 		mate_panel_applet_set_size (applet, g_value_get_uint (value));
1762 		break;
1763 	case PROP_BACKGROUND:
1764 		mate_panel_applet_set_background_string (applet, g_value_get_string (value));
1765 		break;
1766 	case PROP_FLAGS:
1767 		mate_panel_applet_set_flags (applet, g_value_get_uint (value));
1768 		break;
1769 	case PROP_SIZE_HINTS: {
1770 		const int *size_hints;
1771 		gsize      n_elements;
1772 
1773 		size_hints = g_variant_get_fixed_array (g_value_get_pointer (value),
1774 							&n_elements, sizeof (gint32));
1775 		mate_panel_applet_set_size_hints (applet, size_hints, n_elements, 0);
1776 	}
1777 		break;
1778 	case PROP_LOCKED:
1779 		mate_panel_applet_set_locked (applet, g_value_get_boolean (value));
1780 		break;
1781 	case PROP_LOCKED_DOWN:
1782 		mate_panel_applet_set_locked_down (applet, g_value_get_boolean (value));
1783 		break;
1784 	default:
1785 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1786 	}
1787 }
1788 
1789 static void
add_tab_bindings(GtkBindingSet * binding_set,GdkModifierType modifiers,GtkDirectionType direction)1790 add_tab_bindings (GtkBindingSet   *binding_set,
1791 		  GdkModifierType  modifiers,
1792 		  GtkDirectionType direction)
1793 {
1794 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_Tab, modifiers,
1795 				      "move_focus_out_of_applet", 1,
1796 				      GTK_TYPE_DIRECTION_TYPE, direction);
1797 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Tab, modifiers,
1798 				      "move_focus_out_of_applet", 1,
1799 				      GTK_TYPE_DIRECTION_TYPE, direction);
1800 }
1801 
1802 static void
mate_panel_applet_setup(MatePanelApplet * applet)1803 mate_panel_applet_setup (MatePanelApplet *applet)
1804 {
1805 	MatePanelAppletPrivate *priv;
1806 	GValue   value = {0, };
1807 	GArray  *params;
1808 	gint     i;
1809 	gboolean ret;
1810 
1811 	priv = mate_panel_applet_get_instance_private (applet);
1812 
1813 	g_assert ((priv->id != NULL) && (priv->closure != NULL));
1814 
1815 	params = g_array_sized_new (FALSE, TRUE, sizeof (GValue), 2);
1816 	value.g_type = 0;
1817 	g_value_init (&value, G_TYPE_OBJECT);
1818 	g_value_set_object (&value, G_OBJECT (applet));
1819 	g_array_append_val (params, value);
1820 
1821 	value.g_type = 0;
1822 	g_value_init (&value, G_TYPE_STRING);
1823 	g_value_set_string (&value, priv->id);
1824 	g_array_append_val (params, value);
1825 
1826 	value.g_type = 0;
1827 	g_value_init (&value, G_TYPE_BOOLEAN);
1828 
1829 	g_closure_invoke (priv->closure,
1830 	                  &value, params->len,
1831 	                  (GValue *) params->data,
1832 	                  NULL);
1833 
1834 	for (i = 0; i < params->len; i++)
1835 		g_value_unset (&g_array_index (params, GValue, i));
1836 	g_array_free (params, TRUE);
1837 
1838 	ret = g_value_get_boolean (&value);
1839 	g_value_unset (&value);
1840 
1841 	if (!ret) { /* FIXME */
1842 		g_warning ("need to free the control here");
1843 
1844 		return;
1845 	}
1846 }
1847 
_mate_panel_applet_apply_css(GtkWidget * widget,MatePanelAppletBackgroundType type)1848 void _mate_panel_applet_apply_css(GtkWidget* widget, MatePanelAppletBackgroundType type)
1849 {
1850 	GtkStyleContext* context;
1851 
1852 	context = gtk_widget_get_style_context (widget);
1853 
1854 	switch (type) {
1855 	case PANEL_NO_BACKGROUND:
1856 		gtk_style_context_remove_class (context, "mate-custom-panel-background");
1857 		break;
1858 	case PANEL_COLOR_BACKGROUND:
1859 	case PANEL_PIXMAP_BACKGROUND:
1860 		gtk_style_context_add_class (context, "mate-custom-panel-background");
1861 		break;
1862 	default:
1863 		g_assert_not_reached ();
1864 		break;
1865 	}
1866 }
1867 
1868 #ifdef HAVE_X11
_mate_panel_applet_prepare_css(GtkStyleContext * context)1869 static void _mate_panel_applet_prepare_css (GtkStyleContext *context)
1870 {
1871 	GtkCssProvider  *provider;
1872 
1873 	g_return_if_fail (GDK_IS_X11_DISPLAY (gdk_display_get_default ()));
1874 	provider = gtk_css_provider_new ();
1875 	gtk_css_provider_load_from_data (provider,
1876 					 "#PanelPlug {\n"
1877 					 " background-repeat: no-repeat;\n" /*disable in gtk theme features */
1878 					 " background-size: cover; "        /*that don't work on panel-toplevel */
1879 					 " }\n"
1880 					 ".mate-custom-panel-background{\n" /*prepare CSS for user set theme */
1881 					 " background-color: rgba (0, 0, 0, 0);\n"
1882 					 " background-image: none;\n"
1883 					 "}",
1884 					 -1, NULL);
1885 
1886 	gtk_style_context_add_provider (context,
1887 					GTK_STYLE_PROVIDER (provider),
1888 					GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
1889 	g_object_unref (provider);
1890 }
1891 #endif /* HAVE_X11 */
1892 
1893 static void
mate_panel_applet_init(MatePanelApplet * applet)1894 mate_panel_applet_init (MatePanelApplet *applet)
1895 {
1896 	MatePanelAppletPrivate *priv;
1897 
1898 	priv = mate_panel_applet_get_instance_private (applet);
1899 
1900 	priv->flags  = MATE_PANEL_APPLET_FLAGS_NONE;
1901 	priv->orient = MATE_PANEL_APPLET_ORIENT_UP;
1902 	priv->size   = 24;
1903 
1904 	priv->panel_action_group = gtk_action_group_new ("PanelActions");
1905 	gtk_action_group_set_translation_domain (priv->panel_action_group, GETTEXT_PACKAGE);
1906 	gtk_action_group_add_actions (priv->panel_action_group,
1907 	                              menu_entries,
1908 	                              G_N_ELEMENTS (menu_entries),
1909 	                              applet);
1910 	gtk_action_group_add_toggle_actions (priv->panel_action_group,
1911 	                                     menu_toggle_entries,
1912 	                                     G_N_ELEMENTS (menu_toggle_entries),
1913 	                                     applet);
1914 
1915 	priv->ui_manager = gtk_ui_manager_new ();
1916 	gtk_ui_manager_insert_action_group (priv->ui_manager,
1917 	                                    priv->panel_action_group, 1);
1918 	gtk_ui_manager_add_ui_from_string (priv->ui_manager,
1919 	                                   panel_menu_ui, -1, NULL);
1920 
1921 	gtk_widget_set_events (GTK_WIDGET (applet),
1922 			       GDK_BUTTON_PRESS_MASK |
1923 			       GDK_BUTTON_RELEASE_MASK);
1924 }
1925 
1926 static GObject *
mate_panel_applet_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_properties)1927 mate_panel_applet_constructor (GType                  type,
1928                           guint                  n_construct_properties,
1929                           GObjectConstructParam *construct_properties)
1930 {
1931 	GObject     *object;
1932 	MatePanelApplet *applet;
1933 	MatePanelAppletPrivate *priv;
1934 
1935 	object = G_OBJECT_CLASS (mate_panel_applet_parent_class)->constructor (type,
1936 	                                                                  n_construct_properties,
1937 	                                                                  construct_properties);
1938 	applet = MATE_PANEL_APPLET (object);
1939 	priv   = mate_panel_applet_get_instance_private (applet);
1940 
1941 	if (!priv->out_of_process)
1942 		return object;
1943 
1944 #ifdef HAVE_X11
1945 	if (GDK_IS_X11_DISPLAY (gdk_display_get_default ()))
1946 	{
1947 		GtkStyleContext *context;
1948 		GtkWidget       *widget;
1949 
1950 		priv->plug = gtk_plug_new (0);
1951 		widget = GTK_WIDGET (priv->plug);
1952 		gtk_widget_set_visual (widget,
1953 		                       gdk_screen_get_rgba_visual (gtk_widget_get_screen (widget)));
1954 		context = gtk_widget_get_style_context (widget);
1955 		gtk_style_context_add_class (context, "gnome-panel-menu-bar");
1956 		gtk_style_context_add_class (context, "mate-panel-menu-bar");
1957 		gtk_widget_set_name (widget, "PanelPlug");
1958 		_mate_panel_applet_prepare_css (context);
1959 
1960 		g_signal_connect_swapped (G_OBJECT (priv->plug), "embedded",
1961 		                          G_CALLBACK (mate_panel_applet_setup),
1962 		                          applet);
1963 
1964 		gtk_container_add (GTK_CONTAINER (priv->plug), GTK_WIDGET (applet));
1965 	} else
1966 #endif
1967 	{ /* not using X11 */
1968 		g_warning ("Requested construction of an out-of-process applet, which is only possible on X11");
1969 	}
1970 
1971 	return object;
1972 }
1973 
1974 static void
mate_panel_applet_constructed(GObject * object)1975 mate_panel_applet_constructed (GObject* object)
1976 {
1977 	MatePanelApplet* applet = MATE_PANEL_APPLET(object);
1978 
1979 	/* Rename the class to have compatibility with all GTK2 themes
1980 	 * https://github.com/perberos/Mate-Desktop-Environment/issues/27
1981 	 */
1982 	gtk_widget_set_name(GTK_WIDGET(applet), "PanelApplet");
1983 
1984 	mate_panel_applet_register_object (applet);
1985 }
1986 
1987 static void
mate_panel_applet_class_init(MatePanelAppletClass * klass)1988 mate_panel_applet_class_init (MatePanelAppletClass *klass)
1989 {
1990 	GObjectClass   *gobject_class = (GObjectClass *) klass;
1991 	GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
1992 	GtkBindingSet *binding_set;
1993 
1994 	gobject_class->get_property = mate_panel_applet_get_property;
1995 	gobject_class->set_property = mate_panel_applet_set_property;
1996 	gobject_class->constructor = mate_panel_applet_constructor;
1997 	gobject_class->constructed = mate_panel_applet_constructed;
1998 	klass->move_focus_out_of_applet = mate_panel_applet_move_focus_out_of_applet;
1999 	klass->change_background = mate_panel_applet_change_background;
2000 	widget_class->button_press_event = mate_panel_applet_button_press;
2001 	widget_class->button_release_event = mate_panel_applet_button_release;
2002 	widget_class->get_request_mode = mate_panel_applet_get_request_mode;
2003 	widget_class->get_preferred_width = mate_panel_applet_get_preferred_width;
2004 	widget_class->get_preferred_height = mate_panel_applet_get_preferred_height;
2005 	widget_class->draw = mate_panel_applet_draw;
2006 	widget_class->size_allocate = mate_panel_applet_size_allocate;
2007 	widget_class->focus = mate_panel_applet_focus;
2008 	widget_class->realize = mate_panel_applet_realize;
2009 	widget_class->key_press_event = mate_panel_applet_key_press_event;
2010 
2011 
2012 	gobject_class->finalize = mate_panel_applet_finalize;
2013 
2014 	g_object_class_install_property (gobject_class,
2015 	                  PROP_OUT_OF_PROCESS,
2016 	                  g_param_spec_boolean ("out-of-process",
2017 	                               "out-of-process",
2018 	                               "out-of-process",
2019 	                                TRUE,
2020 	                                G_PARAM_CONSTRUCT_ONLY |
2021 	                                G_PARAM_READWRITE));
2022 	g_object_class_install_property (gobject_class,
2023 					 PROP_ID,
2024 					 g_param_spec_string ("id",
2025 							      "Id",
2026 							      "The Applet identifier",
2027 							      NULL,
2028 							      G_PARAM_CONSTRUCT_ONLY |
2029 							      G_PARAM_READWRITE));
2030 	g_object_class_install_property (gobject_class,
2031 					 PROP_CLOSURE,
2032 					 g_param_spec_pointer ("closure",
2033 							       "GClosure",
2034 							       "The Applet closure",
2035 							       G_PARAM_CONSTRUCT_ONLY |
2036 							       G_PARAM_READWRITE));
2037 	g_object_class_install_property (gobject_class,
2038 					 PROP_CONNECTION,
2039 					 g_param_spec_object ("connection",
2040 							      "Connection",
2041 							      "The DBus Connection",
2042 							      G_TYPE_DBUS_CONNECTION,
2043 							      G_PARAM_CONSTRUCT_ONLY |
2044 							      G_PARAM_READWRITE));
2045 	g_object_class_install_property (gobject_class,
2046 					 PROP_PREFS_PATH,
2047 					 g_param_spec_string ("prefs-path",
2048 							      "PrefsPath",
2049 							      "GSettings Preferences Path",
2050 							      NULL,
2051 							      G_PARAM_READWRITE));
2052 	g_object_class_install_property (gobject_class,
2053 					 PROP_ORIENT,
2054 					 g_param_spec_uint ("orient",
2055 							    "Orient",
2056 							    "Panel Applet Orientation",
2057 							    MATE_PANEL_APPLET_ORIENT_FIRST,
2058 							    MATE_PANEL_APPLET_ORIENT_LAST,
2059 							    MATE_PANEL_APPLET_ORIENT_UP,
2060 							    G_PARAM_READWRITE));
2061 	g_object_class_install_property (gobject_class,
2062 					 PROP_SIZE,
2063 					 g_param_spec_uint ("size",
2064 							    "Size",
2065 							    "Panel Applet Size",
2066 							    0, G_MAXUINT, 0,
2067 							    G_PARAM_READWRITE));
2068 	g_object_class_install_property (gobject_class,
2069 					 PROP_BACKGROUND,
2070 					 g_param_spec_string ("background",
2071 							      "Background",
2072 							      "Panel Applet Background",
2073 							      NULL,
2074 							      G_PARAM_READWRITE));
2075 	g_object_class_install_property (gobject_class,
2076 					 PROP_FLAGS,
2077 					 g_param_spec_uint ("flags",
2078 							    "Flags",
2079 							    "Panel Applet flags",
2080 							    MATE_PANEL_APPLET_FLAGS_NONE,
2081 							    MATE_PANEL_APPLET_FLAGS_ALL,
2082 							    MATE_PANEL_APPLET_FLAGS_NONE,
2083 							    G_PARAM_READWRITE));
2084 	g_object_class_install_property (gobject_class,
2085 					 PROP_SIZE_HINTS,
2086 					 /* FIXME: value_array? */
2087 					 g_param_spec_pointer ("size-hints",
2088 							       "SizeHints",
2089 							       "Panel Applet Size Hints",
2090 							       G_PARAM_READWRITE));
2091 	g_object_class_install_property (gobject_class,
2092 					 PROP_LOCKED,
2093 					 g_param_spec_boolean ("locked",
2094 							       "Locked",
2095 							       "Whether Panel Applet is locked",
2096 							       FALSE,
2097 							       G_PARAM_READWRITE));
2098 	g_object_class_install_property (gobject_class,
2099 					 PROP_LOCKED_DOWN,
2100 					 g_param_spec_boolean ("locked-down",
2101 							       "LockedDown",
2102 							       "Whether Panel Applet is locked down",
2103 							       FALSE,
2104 							       G_PARAM_READWRITE));
2105 
2106 	mate_panel_applet_signals [CHANGE_ORIENT] =
2107                 g_signal_new ("change_orient",
2108                               G_TYPE_FROM_CLASS (klass),
2109                               G_SIGNAL_RUN_LAST,
2110                               G_STRUCT_OFFSET (MatePanelAppletClass, change_orient),
2111                               NULL,
2112 			      NULL,
2113                               mate_panel_applet_marshal_VOID__UINT,
2114                               G_TYPE_NONE,
2115 			      1,
2116 			      G_TYPE_UINT);
2117 
2118 	mate_panel_applet_signals [CHANGE_SIZE] =
2119                 g_signal_new ("change_size",
2120                               G_TYPE_FROM_CLASS (klass),
2121                               G_SIGNAL_RUN_LAST,
2122                               G_STRUCT_OFFSET (MatePanelAppletClass, change_size),
2123                               NULL,
2124 			      NULL,
2125                               mate_panel_applet_marshal_VOID__INT,
2126                               G_TYPE_NONE,
2127 			      1,
2128 			      G_TYPE_INT);
2129 
2130 	mate_panel_applet_signals [CHANGE_BACKGROUND] =
2131                 g_signal_new ("change_background",
2132                               G_TYPE_FROM_CLASS (klass),
2133                               G_SIGNAL_RUN_LAST,
2134                               G_STRUCT_OFFSET (MatePanelAppletClass, change_background),
2135                               NULL,
2136 			      NULL,
2137                               mate_panel_applet_marshal_VOID__ENUM_BOXED_OBJECT,
2138                               G_TYPE_NONE,
2139 			      3,
2140 			      PANEL_TYPE_MATE_PANEL_APPLET_BACKGROUND_TYPE,
2141 			      GDK_TYPE_RGBA,
2142 			      CAIRO_GOBJECT_TYPE_PATTERN);
2143 
2144 	mate_panel_applet_signals [MOVE_FOCUS_OUT_OF_APPLET] =
2145                 g_signal_new ("move_focus_out_of_applet",
2146                               G_TYPE_FROM_CLASS (klass),
2147                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
2148                               G_STRUCT_OFFSET (MatePanelAppletClass, move_focus_out_of_applet),
2149                               NULL,
2150 			      NULL,
2151                               mate_panel_applet_marshal_VOID__ENUM,
2152                               G_TYPE_NONE,
2153 			      1,
2154 			      GTK_TYPE_DIRECTION_TYPE);
2155 
2156 	binding_set = gtk_binding_set_by_class (gobject_class);
2157 	add_tab_bindings (binding_set, 0, GTK_DIR_TAB_FORWARD);
2158 	add_tab_bindings (binding_set, GDK_SHIFT_MASK, GTK_DIR_TAB_BACKWARD);
2159 	add_tab_bindings (binding_set, GDK_CONTROL_MASK, GTK_DIR_TAB_FORWARD);
2160 	add_tab_bindings (binding_set, GDK_CONTROL_MASK | GDK_SHIFT_MASK, GTK_DIR_TAB_BACKWARD);
2161 
2162 	gtk_widget_class_set_css_name (widget_class, "PanelApplet");
2163 }
2164 
mate_panel_applet_new(void)2165 GtkWidget* mate_panel_applet_new(void)
2166 {
2167 	MatePanelApplet* applet = g_object_new(PANEL_TYPE_APPLET, NULL);
2168 
2169 	return GTK_WIDGET(applet);
2170 }
2171 
2172 static GdkEvent *
button_press_event_new(MatePanelApplet * applet,guint button,guint time)2173 button_press_event_new (MatePanelApplet *applet,
2174                         guint        button,
2175                         guint        time)
2176 {
2177   GdkDisplay *display;
2178   GdkSeat *seat;
2179   GdkDevice *device;
2180   GdkEvent *event;
2181 
2182   display = gdk_display_get_default ();
2183   seat = gdk_display_get_default_seat (display);
2184   device = gdk_seat_get_pointer (seat);
2185 
2186   event = gdk_event_new (GDK_BUTTON_PRESS);
2187 
2188   event->button.time = time;
2189   event->button.button = button;
2190 
2191   gdk_event_set_device (event, device);
2192 
2193   return event;
2194 }
2195 
2196 static void
method_call_cb(GDBusConnection * connection,const gchar * sender,const gchar * object_path,const gchar * interface_name,const gchar * method_name,GVariant * parameters,GDBusMethodInvocation * invocation,gpointer user_data)2197 method_call_cb (GDBusConnection       *connection,
2198                 const gchar           *sender,
2199                 const gchar           *object_path,
2200                 const gchar           *interface_name,
2201                 const gchar           *method_name,
2202                 GVariant              *parameters,
2203                 GDBusMethodInvocation *invocation,
2204                 gpointer               user_data)
2205 {
2206 	MatePanelApplet *applet = MATE_PANEL_APPLET (user_data);
2207 
2208 	if (g_strcmp0 (method_name, "PopupMenu") == 0) {
2209 		guint button;
2210 		guint time;
2211 
2212 		g_variant_get (parameters, "(uu)", &button, &time);
2213 
2214 		GdkEvent *event = button_press_event_new (applet, button, time);
2215 		mate_panel_applet_menu_popup (applet, event);
2216 		gdk_event_free (event);
2217 
2218 		g_dbus_method_invocation_return_value (invocation, NULL);
2219 	}
2220 }
2221 
2222 static GVariant *
get_property_cb(GDBusConnection * connection,const gchar * sender,const gchar * object_path,const gchar * interface_name,const gchar * property_name,GError ** error,gpointer user_data)2223 get_property_cb (GDBusConnection *connection,
2224 		 const gchar     *sender,
2225 		 const gchar     *object_path,
2226 		 const gchar     *interface_name,
2227 		 const gchar     *property_name,
2228 		 GError         **error,
2229 		 gpointer         user_data)
2230 {
2231 	MatePanelAppletPrivate *priv;
2232 	GVariant *retval = NULL;
2233 
2234 	priv = mate_panel_applet_get_instance_private (MATE_PANEL_APPLET (user_data));
2235 
2236 	if (g_strcmp0 (property_name, "PrefsPath") == 0) {
2237 		retval = g_variant_new_string (priv->prefs_path ? priv->prefs_path : "");
2238 	} else if (g_strcmp0 (property_name, "Orient") == 0) {
2239 		retval = g_variant_new_uint32 (priv->orient);
2240 	} else if (g_strcmp0 (property_name, "Size") == 0) {
2241 		retval = g_variant_new_uint32 (priv->size);
2242 	} else if (g_strcmp0 (property_name, "Background") == 0) {
2243 		retval = g_variant_new_string (priv->background ? priv->background : "");
2244 	} else if (g_strcmp0 (property_name, "Flags") == 0) {
2245 		retval = g_variant_new_uint32 (priv->flags);
2246 	} else if (g_strcmp0 (property_name, "SizeHints") == 0) {
2247 		GVariant **children;
2248 		gint       i;
2249 
2250 		children = g_new (GVariant *, priv->size_hints_len);
2251 		for (i = 0; i < priv->size_hints_len; i++)
2252 			children[i] = g_variant_new_int32 (priv->size_hints[i]);
2253 		retval = g_variant_new_array (G_VARIANT_TYPE_INT32,
2254 		                              children, priv->size_hints_len);
2255 		g_free (children);
2256 	} else if (g_strcmp0 (property_name, "Locked") == 0) {
2257 		retval = g_variant_new_boolean (priv->locked);
2258 	} else if (g_strcmp0 (property_name, "LockedDown") == 0) {
2259 		retval = g_variant_new_boolean (priv->locked_down);
2260 	}
2261 
2262 	return retval;
2263 }
2264 
2265 static gboolean
set_property_cb(GDBusConnection * connection,const gchar * sender,const gchar * object_path,const gchar * interface_name,const gchar * property_name,GVariant * value,GError ** error,gpointer user_data)2266 set_property_cb (GDBusConnection *connection,
2267 		 const gchar     *sender,
2268 		 const gchar     *object_path,
2269 		 const gchar     *interface_name,
2270 		 const gchar     *property_name,
2271 		 GVariant        *value,
2272 		 GError         **error,
2273 		 gpointer         user_data)
2274 {
2275 	MatePanelApplet *applet = MATE_PANEL_APPLET (user_data);
2276 
2277 	if (g_strcmp0 (property_name, "PrefsPath") == 0) {
2278 		mate_panel_applet_set_preferences_path (applet, g_variant_get_string (value, NULL));
2279 	} else if (g_strcmp0 (property_name, "Orient") == 0) {
2280 		mate_panel_applet_set_orient (applet, g_variant_get_uint32 (value));
2281 	} else if (g_strcmp0 (property_name, "Size") == 0) {
2282 		mate_panel_applet_set_size (applet, g_variant_get_uint32 (value));
2283 	} else if (g_strcmp0 (property_name, "Background") == 0) {
2284 		mate_panel_applet_set_background_string (applet, g_variant_get_string (value, NULL));
2285 	} else if (g_strcmp0 (property_name, "Flags") == 0) {
2286 		mate_panel_applet_set_flags (applet, g_variant_get_uint32 (value));
2287 	} else if (g_strcmp0 (property_name, "SizeHints") == 0) {
2288 		const int *size_hints;
2289 		gsize      n_elements;
2290 
2291 		size_hints = g_variant_get_fixed_array (value, &n_elements, sizeof (gint32));
2292 		mate_panel_applet_set_size_hints (applet, size_hints, n_elements, 0);
2293 	} else if (g_strcmp0 (property_name, "Locked") == 0) {
2294 		mate_panel_applet_set_locked (applet, g_variant_get_boolean (value));
2295 	} else if (g_strcmp0 (property_name, "LockedDown") == 0) {
2296 		mate_panel_applet_set_locked_down (applet, g_variant_get_boolean (value));
2297 	}
2298 
2299 	return TRUE;
2300 }
2301 
2302 static const gchar introspection_xml[] =
2303 	"<node>"
2304 	  "<interface name='org.mate.panel.applet.Applet'>"
2305 	    "<method name='PopupMenu'>"
2306 	      "<arg name='button' type='u' direction='in'/>"
2307 	      "<arg name='time' type='u' direction='in'/>"
2308 	    "</method>"
2309 	    "<property name='PrefsPath' type='s' access='readwrite'/>"
2310 	    "<property name='Orient' type='u' access='readwrite' />"
2311 	    "<property name='Size' type='u' access='readwrite'/>"
2312 	    "<property name='Background' type='s' access='readwrite'/>"
2313 	    "<property name='Flags' type='u' access='readwrite'/>"
2314 	    "<property name='SizeHints' type='ai' access='readwrite'/>"
2315 	    "<property name='Locked' type='b' access='readwrite'/>"
2316 	    "<property name='LockedDown' type='b' access='readwrite'/>"
2317 	    "<signal name='Move' />"
2318 	    "<signal name='RemoveFromPanel' />"
2319 	    "<signal name='Lock' />"
2320 	    "<signal name='Unlock' />"
2321 	  "</interface>"
2322 	"</node>";
2323 
2324 static const GDBusInterfaceVTable interface_vtable = {
2325 	method_call_cb,
2326 	get_property_cb,
2327 	set_property_cb
2328 };
2329 
2330 static GDBusNodeInfo *introspection_data = NULL;
2331 
2332 static void
mate_panel_applet_register_object(MatePanelApplet * applet)2333 mate_panel_applet_register_object (MatePanelApplet *applet)
2334 {
2335 	MatePanelAppletPrivate *priv;
2336 	GError     *error = NULL;
2337 	static gint id = 0;
2338 
2339 	if (!introspection_data)
2340 		introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
2341 
2342 	priv = mate_panel_applet_get_instance_private (applet);
2343 	priv->object_path = g_strdup_printf (MATE_PANEL_APPLET_OBJECT_PATH, priv->id, id++);
2344 	priv->object_id =
2345 		g_dbus_connection_register_object (priv->connection,
2346 		                                   priv->object_path,
2347 		                                   introspection_data->interfaces[0],
2348 		                                   &interface_vtable,
2349 		                                   applet, NULL,
2350 		                                   &error);
2351 	if (!priv->object_id) {
2352 		g_printerr ("Failed to register object %s: %s\n", priv->object_path, error->message);
2353 		g_error_free (error);
2354 	}
2355 }
2356 
mate_panel_applet_factory_main_finalized(gpointer data,GObject * object)2357 static void mate_panel_applet_factory_main_finalized(gpointer data, GObject* object)
2358 {
2359 	gtk_main_quit();
2360 
2361 	if (introspection_data)
2362 	{
2363 		g_dbus_node_info_unref(introspection_data);
2364 		introspection_data = NULL;
2365 	}
2366 }
2367 
2368 #ifdef HAVE_X11
2369 static int (*_x_error_func) (Display *, XErrorEvent *);
2370 
2371 static int
_x_error_handler(Display * display,XErrorEvent * error)2372 _x_error_handler (Display *display, XErrorEvent *error)
2373 {
2374 	if (!error->error_code)
2375 		return 0;
2376 
2377 	/* If we got a BadDrawable or a BadWindow, we ignore it for now.
2378 	 * FIXME: We need to somehow distinguish real errors from
2379 	 * X-server-induced errors. Keeping a list of windows for which we
2380 	 * will ignore BadDrawables would be a good idea. */
2381 	if (error->error_code == BadDrawable ||
2382 	    error->error_code == BadWindow)
2383 		return 0;
2384 
2385 	return _x_error_func (display, error);
2386 }
2387 
2388 /*
2389  * To do graphical embedding in the X window system, MATE Panel
2390  * uses the classic foreign-window-reparenting trick. The
2391  * GtkPlug/GtkSocket widgets are used for this purpose. However,
2392  * serious robustness problems arise if the GtkSocket end of the
2393  * connection unexpectedly dies. The X server sends out DestroyNotify
2394  * events for the descendants of the GtkPlug (i.e., your embedded
2395  * component's windows) in effectively random order. Furthermore, if
2396  * you happened to be drawing on any of those windows when the
2397  * GtkSocket was destroyed (a common state of affairs), an X error
2398  * will kill your application.
2399  *
2400  * To solve this latter problem, MATE Panel sets up its own X error
2401  * handler which ignores certain X errors that might have been
2402  * caused by such a scenario. Other X errors get passed to gdk_x_error
2403  * normally.
2404  */
2405 static void
_mate_panel_applet_setup_x_error_handler(void)2406 _mate_panel_applet_setup_x_error_handler (void)
2407 {
2408 	static gboolean error_handler_setup = FALSE;
2409 
2410 	if (error_handler_setup)
2411 		return;
2412 
2413 	error_handler_setup = TRUE;
2414 
2415 	_x_error_func = XSetErrorHandler (_x_error_handler);
2416 }
2417 #endif
2418 
2419 static int
_mate_panel_applet_factory_main_internal(const gchar * factory_id,gboolean out_process,GType applet_type,MatePanelAppletFactoryCallback callback,gpointer user_data)2420 _mate_panel_applet_factory_main_internal (const gchar               *factory_id,
2421 				     gboolean                   out_process,
2422 				     GType                      applet_type,
2423 				     MatePanelAppletFactoryCallback callback,
2424 					 gpointer                   user_data)
2425 {
2426 	MatePanelAppletFactory* factory;
2427 	GClosure* closure;
2428 
2429 	g_return_val_if_fail(factory_id != NULL, 1);
2430 	g_return_val_if_fail(callback != NULL, 1);
2431 	g_assert(g_type_is_a(applet_type, PANEL_TYPE_APPLET));
2432 
2433 
2434 #ifdef HAVE_X11
2435 	if (GDK_IS_X11_DISPLAY (gdk_display_get_default ())) {
2436 		/*Use this both in and out of process as the tray applet always uses GtkSocket
2437 		*to handle GtkStatusIcons whether the tray itself is built in or out of process
2438 		*/
2439 		_mate_panel_applet_setup_x_error_handler();
2440 	} else
2441 #endif
2442 	{ /* not using X11 */
2443 		if (out_process) {
2444 			g_warning("Requested out-of-process applet, which is only supported on X11");
2445 			return 1;
2446 		}
2447 	}
2448 
2449 	closure = g_cclosure_new(G_CALLBACK(callback), user_data, NULL);
2450 	factory = mate_panel_applet_factory_new(factory_id, out_process,  applet_type, closure);
2451 	g_closure_unref(closure);
2452 
2453 	if (mate_panel_applet_factory_register_service(factory))
2454 	{
2455 		if (out_process)
2456 		{
2457 			g_object_weak_ref(G_OBJECT(factory), mate_panel_applet_factory_main_finalized, NULL);
2458 			gtk_main();
2459 		}
2460 
2461 		return 0;
2462 	}
2463 
2464 	g_object_unref (factory);
2465 
2466 	return 1;
2467 }
2468 
2469 /**
2470  * mate_panel_applet_factory_main:
2471  * @out_process: boolean, dummy to support applets sending it
2472  * @factory_id: Factory ID.
2473  * @applet_type: GType of the applet this factory creates.
2474  * @callback: (scope call): Callback to be called when a new applet is to be created.
2475  * @data: (closure): Callback data.
2476  *
2477  * Returns: 0 on success, 1 if there is an error.
2478  */
2479 int
mate_panel_applet_factory_main(const gchar * factory_id,gboolean out_process,GType applet_type,MatePanelAppletFactoryCallback callback,gpointer user_data)2480 mate_panel_applet_factory_main (const gchar               *factory_id,
2481                gboolean                   out_process, /*Dummy to support applets w issues with this */
2482 			   GType                      applet_type,
2483 			   MatePanelAppletFactoryCallback callback,
2484 			   gpointer                   user_data)
2485 {
2486 	return _mate_panel_applet_factory_main_internal (factory_id, TRUE, applet_type,
2487 						    callback, user_data);
2488 }
2489 
2490 /**
2491  * mate_panel_applet_factory_setup_in_process: (skip)
2492  * @factory_id: Factory ID.
2493  * @applet_type: GType of the applet this factory creates.
2494  * @callback: (scope call): Callback to be called when a new applet is to be created.
2495  * @data: (closure): Callback data.
2496  *
2497  * Returns: 0 on success, 1 if there is an error.
2498  */
2499 int
mate_panel_applet_factory_setup_in_process(const gchar * factory_id,GType applet_type,MatePanelAppletFactoryCallback callback,gpointer user_data)2500 mate_panel_applet_factory_setup_in_process (const gchar               *factory_id,
2501 				       GType                      applet_type,
2502 				       MatePanelAppletFactoryCallback callback,
2503 				       gpointer                   user_data)
2504 {
2505 	return _mate_panel_applet_factory_main_internal (factory_id, FALSE, applet_type,
2506 						    callback, user_data);
2507 }
2508 
2509 /**
2510  * mate_panel_applet_set_background_widget:
2511  * @applet: a #PanelApplet.
2512  * @widget: a #GtkWidget.
2513  *
2514  * Configure #PanelApplet to automatically draw the background of the applet on
2515  * @widget. It is generally enough to call this function with @applet as
2516  * @widget.
2517  *
2518  * Deprecated: 3.20: Do not use this API. Since 3.20 this function does nothing.
2519  **/
2520 
2521 void
mate_panel_applet_set_background_widget(MatePanelApplet * applet,GtkWidget * widget)2522 mate_panel_applet_set_background_widget (MatePanelApplet *applet,
2523 				    GtkWidget   *widget)
2524 {
2525 }
2526 
2527 guint32
mate_panel_applet_get_xid(MatePanelApplet * applet,GdkScreen * screen)2528 mate_panel_applet_get_xid (MatePanelApplet *applet,
2529 		      GdkScreen   *screen)
2530 {
2531 	MatePanelAppletPrivate *priv;
2532 
2533 	priv = mate_panel_applet_get_instance_private (applet);
2534 
2535 	/* out_of_process should only be true on X11, so an extra runtime Wayland check is not needed */
2536 	if (priv->out_of_process == FALSE)
2537 		return 0;
2538 
2539 #ifdef HAVE_X11
2540 	gtk_window_set_screen (GTK_WINDOW (priv->plug), screen);
2541 	gtk_widget_show (priv->plug);
2542 
2543 	return gtk_plug_get_id (GTK_PLUG (priv->plug));
2544 #else
2545 	return 0;
2546 #endif
2547 }
2548 
2549 const gchar *
mate_panel_applet_get_object_path(MatePanelApplet * applet)2550 mate_panel_applet_get_object_path (MatePanelApplet *applet)
2551 {
2552 	MatePanelAppletPrivate *priv;
2553 
2554 	priv = mate_panel_applet_get_instance_private (applet);
2555 
2556 	return priv->object_path;
2557 }
2558 
2559 G_MODULE_EXPORT GtkWidget *
mate_panel_applet_get_applet_widget(const gchar * factory_id,guint uid)2560 mate_panel_applet_get_applet_widget (const gchar *factory_id,
2561                                 guint        uid)
2562 {
2563 	GtkWidget *widget;
2564 
2565 	widget = mate_panel_applet_factory_get_applet_widget (factory_id, uid);
2566 	if (!widget) {
2567 		return NULL;
2568 	}
2569 
2570 	mate_panel_applet_setup (MATE_PANEL_APPLET (widget));
2571 
2572 	return widget;
2573 }
2574