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