1 /*
2  * Copyright (C) 2006-2010 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
3  *               2006-2008 Jim Huang <jserv.tw@gmail.com>
4  *               2008 Fred Chien <fred@lxde.org>
5  *               2009 Jürgen Hötzel <juergen@archlinux.org>
6  *               2009-2010 Marty Jack <martyj19@comcast.net>
7  *               2010 Lajos Kamocsay <lajos@panka.com>
8  *               2012 Piotr Sipika <Piotr.Sipika@gmail.com>
9  *               2012-2013 Henry Gebhardt <hsggebhardt@gmail.com>
10  *               2012 Jack Chen <speed.up08311990@gmail.com>
11  *               2012 Rafał Mużyło <galtgendo@gmail.com>
12  *               2012 Michael Rawson <michaelrawson76@gmail.com>
13  *               2012 Julien Lavergne <julien.lavergne@gmail.com>
14  *               2013 Rouslan <rouslan-k@users.sourceforge.net>
15  *               2013 peadaredwards <peadaredwards@users.sourceforge.net>
16  *               2014-2016 Andriy Grytsenko <andrej@rep.kiev.ua>
17  *               2015 Rafał Mużyło <galtgendo@gmail.com>
18  *               2015 Hanno Zulla <hhz@users.sf.net>
19  *
20  * This file is a part of LXPanel project.
21  *
22  * This program is free software; you can redistribute it and/or modify
23  * it under the terms of the GNU General Public License as published by
24  * the Free Software Foundation; either version 2 of the License, or
25  * (at your option) any later version.
26  *
27  * This program is distributed in the hope that it will be useful,
28  * but WITHOUT ANY WARRANTY; without even the implied warranty of
29  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
30  * GNU General Public License for more details.
31  *
32  * You should have received a copy of the GNU General Public License
33  * along with this program; if not, write to the Free Software Foundation,
34  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
35  */
36 
37 #ifdef HAVE_CONFIG_H
38 #include <config.h>
39 #endif
40 
41 #include <glib/gi18n.h>
42 #include <stdlib.h>
43 #include <glib/gstdio.h>
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #include <unistd.h>
47 #include <errno.h>
48 #include <locale.h>
49 #include <string.h>
50 #include <gdk/gdkx.h>
51 #include <libfm/fm-gtk.h>
52 #include <cairo-xlib.h>
53 
54 #define __LXPANEL_INTERNALS__
55 
56 #include "private.h"
57 #include "misc.h"
58 #include "space.h"
59 
60 #include "lxpanelctl.h"
61 #include "dbg.h"
62 #include "gtk-compat.h"
63 
64 gchar *cprofile = "default";
65 
66 GSList* all_panels = NULL;  /* a single-linked list storing all panels */
67 
68 gboolean is_in_lxde = FALSE;
69 
70 static GtkWindowGroup* win_grp = NULL; /* window group used to limit the scope of model dialog. */
71 
72 static gulong monitors_handler = 0;
73 
74 static void panel_start_gui(LXPanel *p, config_setting_t *list);
75 static void ah_start(LXPanel *p);
76 static void ah_stop(LXPanel *p);
77 static void _panel_update_background(LXPanel * p, gboolean enforce);
78 
79 enum
80 {
81     ICON_SIZE_CHANGED,
82     PANEL_FONT_CHANGED,
83     N_SIGNALS
84 };
85 
86 static guint signals[N_SIGNALS];
87 
88 G_DEFINE_TYPE(PanelToplevel, lxpanel, GTK_TYPE_WINDOW);
89 
lxpanel_finalize(GObject * object)90 static void lxpanel_finalize(GObject *object)
91 {
92     LXPanel *self = LXPANEL(object);
93     Panel *p = self->priv;
94 
95     if( p->config_changed )
96         lxpanel_config_save( self );
97     config_destroy(p->config);
98 
99     //XFree(p->workarea);
100     g_free( p->background_file );
101     g_slist_free( p->system_menus );
102 
103     g_free( p->name );
104     g_free(p);
105 
106     G_OBJECT_CLASS(lxpanel_parent_class)->finalize(object);
107 }
108 
panel_stop_gui(LXPanel * self)109 static void panel_stop_gui(LXPanel *self)
110 {
111     Panel *p = self->priv;
112     Display *xdisplay;
113 
114     g_debug("panel_stop_gui on '%s'", p->name);
115     if (p->autohide)
116         ah_stop(self);
117 
118     if (p->pref_dialog != NULL)
119         gtk_widget_destroy(p->pref_dialog);
120     p->pref_dialog = NULL;
121 
122     if (p->plugin_pref_dialog != NULL)
123         /* just close the dialog, it will do all required cleanup */
124         gtk_dialog_response(GTK_DIALOG(p->plugin_pref_dialog), GTK_RESPONSE_CLOSE);
125 
126 
127     if (p->initialized)
128     {
129         gtk_window_group_remove_window(win_grp, GTK_WINDOW(self));
130         xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
131         gdk_flush();
132         XFlush(xdisplay);
133         XSync(xdisplay, True);
134         p->initialized = FALSE;
135     }
136     if (p->surface != NULL)
137     {
138         cairo_surface_destroy(p->surface);
139         p->surface = NULL;
140     }
141 
142     if (p->background_update_queued)
143     {
144         g_source_remove(p->background_update_queued);
145         p->background_update_queued = 0;
146     }
147     if (p->strut_update_queued)
148     {
149         g_source_remove(p->strut_update_queued);
150         p->strut_update_queued = 0;
151     }
152     if (p->reconfigure_queued)
153     {
154         g_source_remove(p->reconfigure_queued);
155         p->reconfigure_queued = 0;
156     }
157 
158     if (gtk_bin_get_child(GTK_BIN(self)))
159     {
160         gtk_widget_destroy(p->box);
161         p->box = NULL;
162     }
163 }
164 
165 #if GTK_CHECK_VERSION(3, 0, 0)
lxpanel_destroy(GtkWidget * object)166 static void lxpanel_destroy(GtkWidget *object)
167 #else
168 static void lxpanel_destroy(GtkObject *object)
169 #endif
170 {
171     LXPanel *self = LXPANEL(object);
172 
173     panel_stop_gui(self);
174 
175 #if GTK_CHECK_VERSION(3, 0, 0)
176     GTK_WIDGET_CLASS(lxpanel_parent_class)->destroy(object);
177 #else
178     GTK_OBJECT_CLASS(lxpanel_parent_class)->destroy(object);
179 #endif
180 }
181 
idle_update_background(gpointer p)182 static gboolean idle_update_background(gpointer p)
183 {
184     LXPanel *panel = LXPANEL(p);
185 
186     if (g_source_is_destroyed(g_main_current_source()))
187         return FALSE;
188 
189     /* Panel could be destroyed while background update scheduled */
190     if (gtk_widget_get_realized(p))
191     {
192         gdk_display_sync( gtk_widget_get_display(p) );
193         _panel_update_background(panel, FALSE);
194     }
195     panel->priv->background_update_queued = 0;
196 
197     return FALSE;
198 }
199 
_panel_queue_update_background(LXPanel * panel)200 void _panel_queue_update_background(LXPanel *panel)
201 {
202     if (panel->priv->background_update_queued)
203         return;
204     panel->priv->background_update_queued = g_idle_add_full(G_PRIORITY_HIGH,
205                                                             idle_update_background,
206                                                             panel, NULL);
207 }
208 
idle_update_strut(gpointer p)209 static gboolean idle_update_strut(gpointer p)
210 {
211     LXPanel *panel = LXPANEL(p);
212 
213     if (g_source_is_destroyed(g_main_current_source()))
214         return FALSE;
215 
216     _panel_set_wm_strut(panel);
217     panel->priv->strut_update_queued = 0;
218 
219     return FALSE;
220 }
221 
lxpanel_realize(GtkWidget * widget)222 static void lxpanel_realize(GtkWidget *widget)
223 {
224     GTK_WIDGET_CLASS(lxpanel_parent_class)->realize(widget);
225 
226     _panel_queue_update_background(LXPANEL(widget));
227 }
228 
lxpanel_style_set(GtkWidget * widget,GtkStyle * prev)229 static void lxpanel_style_set(GtkWidget *widget, GtkStyle* prev)
230 {
231     GTK_WIDGET_CLASS(lxpanel_parent_class)->style_set(widget, prev);
232 
233     /* FIXME: This dirty hack is used to fix the background of systray... */
234     _panel_queue_update_background(LXPANEL(widget));
235 }
236 
lxpanel_size_request(GtkWidget * widget,GtkRequisition * req)237 static void lxpanel_size_request(GtkWidget *widget, GtkRequisition *req)
238 {
239     LXPanel *panel = LXPANEL(widget);
240     Panel *p = panel->priv;
241     GdkRectangle rect;
242 
243 #if !GTK_CHECK_VERSION(3, 0, 0)
244     GTK_WIDGET_CLASS(lxpanel_parent_class)->size_request(widget, req);
245 #else
246     GTK_WIDGET_CLASS(lxpanel_parent_class)->get_preferred_width(widget, &req->width, &req->width);
247     GTK_WIDGET_CLASS(lxpanel_parent_class)->get_preferred_height(widget, &req->height, &req->height);
248 #endif
249 
250     if (!p->visible)
251         /* When the panel is in invisible state, the content box also got hidden, thus always
252          * report 0 size.  Ask the content box instead for its size. */
253         gtk_widget_size_request(p->box, req);
254 
255     rect.width = req->width;
256     rect.height = req->height;
257     _calculate_position(panel, &rect);
258     req->width = rect.width;
259     req->height = rect.height;
260     /* update data ahead of configuration request */
261     p->cw = rect.width;
262     p->ch = rect.height;
263 }
264 
265 #if GTK_CHECK_VERSION(3, 0, 0)
266 static void
lxpanel_get_preferred_width(GtkWidget * widget,gint * minimal_width,gint * natural_width)267 lxpanel_get_preferred_width (GtkWidget *widget,
268                              gint      *minimal_width,
269                              gint      *natural_width)
270 {
271   GtkRequisition requisition;
272 
273   lxpanel_size_request (widget, &requisition);
274 
275   if (minimal_width)
276       *minimal_width = requisition.width;
277   if (natural_width)
278       *natural_width = requisition.width;
279 }
280 
281 static void
lxpanel_get_preferred_height(GtkWidget * widget,gint * minimal_height,gint * natural_height)282 lxpanel_get_preferred_height (GtkWidget *widget,
283                               gint      *minimal_height,
284                               gint      *natural_height)
285 {
286   GtkRequisition requisition;
287 
288   lxpanel_size_request (widget, &requisition);
289 
290   if (minimal_height)
291       *minimal_height = requisition.height;
292   if (natural_height)
293       *natural_height = requisition.height;
294 }
295 #endif
296 
lxpanel_size_allocate(GtkWidget * widget,GtkAllocation * a)297 static void lxpanel_size_allocate(GtkWidget *widget, GtkAllocation *a)
298 {
299     LXPanel *panel = LXPANEL(widget);
300     Panel *p = panel->priv;
301     GdkRectangle rect;
302     gint x, y;
303 
304     /* some WM like mwm are too generous giving us space more than requested
305        so let correct it right now, as much as we can */
306     rect.x = a->x;
307     rect.y = a->y;
308     rect.width = MAX(8, MIN(p->cw, a->width));
309     rect.height = MAX(8, MIN(p->ch, a->height));
310     _calculate_position(panel, &rect);
311 
312     GTK_WIDGET_CLASS(lxpanel_parent_class)->size_allocate(widget, &rect);
313 
314     if (p->widthtype == WIDTH_REQUEST)
315         p->width = (p->orientation == GTK_ORIENTATION_HORIZONTAL) ? rect.width : rect.height;
316     if (p->heighttype == HEIGHT_REQUEST)
317         p->height = (p->orientation == GTK_ORIENTATION_HORIZONTAL) ? rect.height : rect.width;
318 
319     if (!gtk_widget_get_realized(widget))
320         return;
321 
322     /* get real coords since a contains 0, 0 */
323     gdk_window_get_origin(gtk_widget_get_window(widget), &x, &y);
324     p->ax = rect.x;
325     p->ay = rect.y;
326 
327     if (rect.width != p->aw || rect.height != p->ah || x != p->ax || y != p->ay)
328     {
329         p->aw = rect.width;
330         p->ah = rect.height;
331         gtk_window_move(GTK_WINDOW(widget), p->ax, p->ay);
332         /* SF bug #708: strut update does not work while in size allocation */
333         if (!panel->priv->strut_update_queued)
334             panel->priv->strut_update_queued = g_idle_add_full(G_PRIORITY_HIGH,
335                                                                idle_update_strut,
336                                                                panel, NULL);
337         _panel_queue_update_background(panel);
338     }
339 
340     if (gtk_widget_get_mapped(widget))
341         _panel_establish_autohide(panel);
342 }
343 
lxpanel_configure_event(GtkWidget * widget,GdkEventConfigure * e)344 static gboolean lxpanel_configure_event (GtkWidget *widget, GdkEventConfigure *e)
345 {
346     Panel *p = LXPANEL(widget)->priv;
347 
348     p->cw = e->width;
349     p->ch = e->height;
350     p->cx = e->x;
351     p->cy = e->y;
352 
353     return GTK_WIDGET_CLASS(lxpanel_parent_class)->configure_event(widget, e);
354 }
355 
lxpanel_map_event(GtkWidget * widget,GdkEventAny * event)356 static gboolean lxpanel_map_event(GtkWidget *widget, GdkEventAny *event)
357 {
358     Panel *p = PLUGIN_PANEL(widget)->priv;
359 
360     if (p->autohide)
361         ah_start(LXPANEL(widget));
362     return GTK_WIDGET_CLASS(lxpanel_parent_class)->map_event(widget, event);
363 }
364 
365 /* Handler for "button_press_event" signal with Panel as parameter. */
lxpanel_button_press(GtkWidget * widget,GdkEventButton * event)366 static gboolean lxpanel_button_press(GtkWidget *widget, GdkEventButton *event)
367 {
368     LXPanel *panel = PLUGIN_PANEL(widget);
369 
370     if ((event->state & gtk_accelerator_get_default_mod_mask()) != 0)
371         /* ignore clicks with modifiers */
372         return FALSE;
373 
374     if (event->button == 3) /* right button */
375     {
376         GtkMenu* popup = (GtkMenu*) lxpanel_get_plugin_menu(panel, NULL, FALSE);
377         gtk_menu_popup(popup, NULL, NULL, NULL, NULL, event->button, event->time);
378         return TRUE;
379     }
380     else if (event->button == 2) /* middle button */
381     {
382         Panel *p = panel->priv;
383         if (p->move_state == PANEL_MOVE_STOP)
384         {
385             gdk_window_get_origin(event->window, &p->move_x, &p->move_y);
386             p->move_x += event->x - p->ax;
387             p->move_y += event->y - p->ay;
388             p->move_state = PANEL_MOVE_DETECT;
389             p->move_device = event->device;
390             /* rest of work see in panel-plugin-move.c file */
391             return TRUE;
392         }
393     }
394     return FALSE;
395 }
396 
lxpanel_class_init(PanelToplevelClass * klass)397 static void lxpanel_class_init(PanelToplevelClass *klass)
398 {
399     GObjectClass *gobject_class = (GObjectClass *)klass;
400 #if !GTK_CHECK_VERSION(3, 0, 0)
401     GtkObjectClass *gtk_object_class = (GtkObjectClass *)klass;
402 #endif
403     GtkWidgetClass *widget_class = (GtkWidgetClass *)klass;
404 
405     gobject_class->finalize = lxpanel_finalize;
406 #if GTK_CHECK_VERSION(3, 0, 0)
407     widget_class->destroy = lxpanel_destroy;
408 #else
409     gtk_object_class->destroy = lxpanel_destroy;
410 #endif
411     widget_class->realize = lxpanel_realize;
412 #if GTK_CHECK_VERSION(3, 0, 0)
413     widget_class->get_preferred_width = lxpanel_get_preferred_width;
414     widget_class->get_preferred_height = lxpanel_get_preferred_height;
415 #else
416     widget_class->size_request = lxpanel_size_request;
417 #endif
418     widget_class->size_allocate = lxpanel_size_allocate;
419     widget_class->configure_event = lxpanel_configure_event;
420     widget_class->style_set = lxpanel_style_set;
421     widget_class->map_event = lxpanel_map_event;
422     widget_class->button_press_event = lxpanel_button_press;
423     widget_class->button_release_event = _lxpanel_button_release;
424     widget_class->motion_notify_event = _lxpanel_motion_notify;
425 
426     signals[ICON_SIZE_CHANGED] =
427         g_signal_new("icon-size-changed",
428                      G_TYPE_FROM_CLASS(klass),
429                      G_SIGNAL_RUN_LAST,
430                      G_STRUCT_OFFSET(PanelToplevelClass, icon_size_changed),
431                      NULL, NULL,
432                      g_cclosure_marshal_VOID__VOID,
433                      G_TYPE_NONE, 0, G_TYPE_NONE);
434 
435     signals[PANEL_FONT_CHANGED] =
436         g_signal_new("panel-font-changed",
437                      G_TYPE_FROM_CLASS(klass),
438                      G_SIGNAL_RUN_LAST,
439                      G_STRUCT_OFFSET(PanelToplevelClass, panel_font_changed),
440                      NULL, NULL,
441                      g_cclosure_marshal_VOID__VOID,
442                      G_TYPE_NONE, 0, G_TYPE_NONE);
443 }
444 
lxpanel_init(PanelToplevel * self)445 static void lxpanel_init(PanelToplevel *self)
446 {
447     Panel *p = g_new0(Panel, 1);
448 
449     self->priv = p;
450     p->topgwin = self;
451     p->align = ALIGN_CENTER;
452     p->edge = EDGE_NONE;
453     p->widthtype = WIDTH_PERCENT;
454     p->width = 100;
455     p->heighttype = HEIGHT_PIXEL;
456     p->height = PANEL_HEIGHT_DEFAULT;
457     p->monitor = 0;
458     p->setdocktype = 1;
459     p->setstrut = 1;
460     p->round_corners = 0;
461     p->autohide = 0;
462     p->visible = TRUE;
463     p->height_when_hidden = 2;
464     p->transparent = 0;
465     p->alpha = 255;
466     gdk_color_parse("white", &p->gtintcolor);
467     p->tintcolor = gcolor2rgb24(&p->gtintcolor);
468     p->usefontcolor = 0;
469     p->fontcolor = 0x00000000;
470     p->usefontsize = 0;
471     p->fontsize = 10;
472     p->spacing = 0;
473     p->icon_size = PANEL_ICON_SIZE;
474     p->icon_theme = gtk_icon_theme_get_default();
475     p->config = config_new();
476     p->defstyle = gtk_widget_get_default_style();
477 }
478 
479 /* Allocate and initialize new Panel structure. */
panel_allocate(GdkScreen * screen)480 static LXPanel* panel_allocate(GdkScreen *screen)
481 {
482     return g_object_new(LX_TYPE_PANEL,
483                         "border-width", 0,
484                         "decorated", FALSE,
485                         "name", "PanelToplevel",
486                         "resizable", FALSE,
487                         "title", "panel",
488                         "type-hint", GDK_WINDOW_TYPE_HINT_DOCK,
489                         "window-position", GTK_WIN_POS_NONE,
490                         "screen", screen,
491                         NULL);
492 }
493 
_panel_emit_icon_size_changed(LXPanel * p)494 void _panel_emit_icon_size_changed(LXPanel *p)
495 {
496     g_signal_emit(p, signals[ICON_SIZE_CHANGED], 0);
497 }
498 
_panel_emit_font_changed(LXPanel * p)499 void _panel_emit_font_changed(LXPanel *p)
500 {
501     g_signal_emit(p, signals[PANEL_FONT_CHANGED], 0);
502 }
503 
504 /* Normalize panel configuration after load from file or reconfiguration. */
panel_normalize_configuration(Panel * p)505 static void panel_normalize_configuration(Panel* p)
506 {
507     panel_set_panel_configuration_changed( p );
508     if (p->width < 0)
509         p->width = 100;
510     if (p->widthtype == WIDTH_PERCENT && p->width > 100)
511         p->width = 100;
512     p->heighttype = HEIGHT_PIXEL;
513     if (p->heighttype == HEIGHT_PIXEL) {
514         if (p->height < PANEL_HEIGHT_MIN)
515             p->height = PANEL_HEIGHT_MIN;
516         else if (p->height > PANEL_HEIGHT_MAX)
517             p->height = PANEL_HEIGHT_MAX;
518     }
519     if (p->monitor < 0)
520         p->monitor = -1;
521     if (p->background)
522         p->transparent = 0;
523 }
524 
_panel_edge_can_strut(LXPanel * panel,int edge,gint monitor,gulong * size)525 gboolean _panel_edge_can_strut(LXPanel *panel, int edge, gint monitor, gulong *size)
526 {
527     Panel *p;
528     GdkScreen *screen;
529     GdkRectangle rect;
530     GdkRectangle rect2;
531     gint n, i;
532     gulong s;
533 
534     if (!gtk_widget_get_mapped(GTK_WIDGET(panel)))
535         return FALSE;
536 
537     p = panel->priv;
538     /* Handle autohide case.  EWMH recommends having the strut be the minimized size. */
539     if (p->autohide)
540         s = p->height_when_hidden;
541     else switch (edge)
542     {
543     case EDGE_LEFT:
544     case EDGE_RIGHT:
545         s = p->aw;
546         break;
547     case EDGE_TOP:
548     case EDGE_BOTTOM:
549         s = p->ah;
550         break;
551     default: /* error! */
552         return FALSE;
553     }
554     if (s == 0)
555         return FALSE; /* nothing to strut here */
556 
557     if (monitor < 0) /* screen span */
558     {
559         if (G_LIKELY(size))
560             *size = s;
561         return TRUE;
562     }
563 
564     screen = gtk_widget_get_screen(GTK_WIDGET(panel));
565     n = gdk_screen_get_n_monitors(screen);
566     if (monitor >= n) /* hidden now */
567         return FALSE;
568     gdk_screen_get_monitor_geometry(screen, monitor, &rect);
569     switch (edge)
570     {
571         case EDGE_LEFT:
572             rect.width = rect.x;
573             rect.x = 0;
574             s += rect.width;
575             break;
576         case EDGE_RIGHT:
577             rect.x += rect.width;
578             rect.width = gdk_screen_get_width(screen) - rect.x;
579             s += rect.width;
580             break;
581         case EDGE_TOP:
582             rect.height = rect.y;
583             rect.y = 0;
584             s += rect.height;
585             break;
586         case EDGE_BOTTOM:
587             rect.y += rect.height;
588             rect.height = gdk_screen_get_height(screen) - rect.y;
589             s += rect.height;
590             break;
591         default: ;
592     }
593     if (rect.height == 0 || rect.width == 0) ; /* on a border of monitor */
594     else
595     {
596         for (i = 0; i < n; i++)
597         {
598             if (i == monitor)
599                 continue;
600             gdk_screen_get_monitor_geometry(screen, i, &rect2);
601             if (gdk_rectangle_intersect(&rect, &rect2, NULL))
602                 /* that monitor lies over the edge */
603                 return FALSE;
604         }
605     }
606     if (G_LIKELY(size))
607         *size = s;
608     return TRUE;
609 }
610 
611 /****************************************************
612  *         panel's handlers for WM events           *
613  ****************************************************/
614 
panel_set_wm_strut(Panel * p)615 void panel_set_wm_strut(Panel *p)
616 {
617     _panel_set_wm_strut(p->topgwin);
618 }
619 
_panel_set_wm_strut(LXPanel * panel)620 void _panel_set_wm_strut(LXPanel *panel)
621 {
622     int index;
623     Display *xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
624     Panel *p = panel->priv;
625     gulong strut_size;
626     gulong strut_lower;
627     gulong strut_upper;
628 
629     if (!gtk_widget_get_mapped(GTK_WIDGET(panel)))
630         return;
631     /* most wm's tend to ignore struts of unmapped windows, and that's how
632      * lxpanel hides itself. so no reason to set it. */
633     if (p->autohide && p->height_when_hidden <= 0)
634         return;
635 
636     /* Dispatch on edge to set up strut parameters. */
637     switch (p->edge)
638     {
639         case EDGE_LEFT:
640             index = 0;
641             strut_lower = p->ay;
642             strut_upper = p->ay + p->ah;
643             break;
644         case EDGE_RIGHT:
645             index = 1;
646             strut_lower = p->ay;
647             strut_upper = p->ay + p->ah;
648             break;
649         case EDGE_TOP:
650             index = 2;
651             strut_lower = p->ax;
652             strut_upper = p->ax + p->aw;
653             break;
654         case EDGE_BOTTOM:
655             index = 3;
656             strut_lower = p->ax;
657             strut_upper = p->ax + p->aw;
658             break;
659         default:
660             return;
661     }
662 
663     /* Set up strut value in property format. */
664     gulong desired_strut[12];
665     memset(desired_strut, 0, sizeof(desired_strut));
666     if (p->setstrut &&
667         _panel_edge_can_strut(panel, p->edge, p->monitor, &strut_size))
668     {
669         desired_strut[index] = strut_size;
670         desired_strut[4 + index * 2] = strut_lower;
671         desired_strut[5 + index * 2] = strut_upper - 1;
672     }
673     else
674     {
675         strut_size = 0;
676         strut_lower = 0;
677         strut_upper = 0;
678     }
679 
680     /* If strut value changed, set the property value on the panel window.
681      * This avoids property change traffic when the panel layout is recalculated but strut geometry hasn't changed. */
682     if ((p->strut_size != strut_size) || (p->strut_lower != strut_lower) || (p->strut_upper != strut_upper) || (p->strut_edge != p->edge))
683     {
684         p->strut_size = strut_size;
685         p->strut_lower = strut_lower;
686         p->strut_upper = strut_upper;
687         p->strut_edge = p->edge;
688 
689         /* If window manager supports STRUT_PARTIAL, it will ignore STRUT.
690          * Set STRUT also for window managers that do not support STRUT_PARTIAL. */
691         if (strut_size != 0)
692         {
693             XChangeProperty(xdisplay, p->topxwin, a_NET_WM_STRUT_PARTIAL,
694                 XA_CARDINAL, 32, PropModeReplace,  (unsigned char *) desired_strut, 12);
695             XChangeProperty(xdisplay, p->topxwin, a_NET_WM_STRUT,
696                 XA_CARDINAL, 32, PropModeReplace,  (unsigned char *) desired_strut, 4);
697         }
698         else
699         {
700             XDeleteProperty(xdisplay, p->topxwin, a_NET_WM_STRUT);
701             XDeleteProperty(xdisplay, p->topxwin, a_NET_WM_STRUT_PARTIAL);
702         }
703     }
704 }
705 
706 /****************************************************
707  *         panel's handlers for GTK events          *
708  ****************************************************/
709 
paint_root_pixmap(LXPanel * panel,cairo_t * cr)710 static void paint_root_pixmap(LXPanel *panel, cairo_t *cr)
711 {
712     /*
713      * this code was extracted from code for FbBg object
714      *
715      * Copyright (C) 2001, 2002 Ian McKellar <yakk@yakk.net>
716      *                     2002 Sun Microsystems, Inc.
717      */
718     XGCValues gcv;
719     uint mask;
720     Window xroot;
721     GC gc;
722     Display *dpy;
723     Pixmap *prop;
724 #if GTK_CHECK_VERSION(3, 0, 0)
725     cairo_surface_t *surface;
726 #else
727     GdkPixmap *pixmap;
728 #endif
729     Pixmap xpixmap;
730     Panel *p = panel->priv;
731 
732     dpy = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
733     xroot = DefaultRootWindow(dpy);
734     gcv.ts_x_origin = 0;
735     gcv.ts_y_origin = 0;
736     gcv.fill_style = FillTiled;
737     mask = GCTileStipXOrigin | GCTileStipYOrigin | GCFillStyle;
738     prop = get_xaproperty(xroot, a_XROOTPMAP_ID, XA_PIXMAP, NULL);
739     if (prop)
740     {
741         gcv.tile = *prop;
742         mask |= GCTile;
743         XFree(prop);
744     }
745     gc = XCreateGC(dpy, xroot, mask, &gcv);
746 #if GTK_CHECK_VERSION(3, 0, 0)
747     xpixmap = XCreatePixmap(dpy, xroot, p->aw, p->ah,
748                             DefaultDepth(dpy, DefaultScreen(dpy)));
749     surface = cairo_xlib_surface_create(dpy, xpixmap,
750                                         DefaultVisual(dpy, DefaultScreen(dpy)),
751                                         p->aw, p->ah);
752 #else
753     pixmap = gdk_pixmap_new(gtk_widget_get_window(GTK_WIDGET(panel)),
754                             p->aw, p->ah, -1);
755     xpixmap = gdk_x11_drawable_get_xid(pixmap);
756 #endif
757     XSetTSOrigin(dpy, gc, -p->ax, -p->ay);
758     XFillRectangle(dpy, xpixmap, gc, 0, 0, p->aw, p->ah);
759     XFreeGC(dpy, gc);
760 #if GTK_CHECK_VERSION(3, 0, 0)
761     cairo_set_source_surface(cr, surface, 0, 0);
762 #else
763     gdk_cairo_set_source_pixmap(cr, pixmap, 0, 0);
764 #endif
765     cairo_paint(cr);
766 #if GTK_CHECK_VERSION(3, 0, 0)
767     cairo_surface_destroy(surface);
768     XFreePixmap(dpy, xpixmap);
769 #else
770     g_object_unref(pixmap);
771 #endif
772 }
773 
_panel_determine_background_pixmap(LXPanel * panel)774 void _panel_determine_background_pixmap(LXPanel * panel)
775 {
776 #if GTK_CHECK_VERSION(3, 0, 0)
777     cairo_pattern_t *pattern;
778 #else
779     GdkPixmap * pixmap = NULL;
780 #endif
781     GtkWidget * widget = GTK_WIDGET(panel);
782     GdkWindow * window = gtk_widget_get_window(widget);
783     Panel * p = panel->priv;
784     cairo_t *cr;
785     gint x = 0, y = 0;
786 
787     if (!p->background && !p->transparent)
788         goto not_paintable;
789     else if (p->aw <= 1 || p->ah <= 1)
790         goto not_paintable;
791     else if (p->surface == NULL)
792     {
793         GdkPixbuf *pixbuf = NULL;
794 
795         p->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, p->aw, p->ah);
796         cr = cairo_create(p->surface);
797         if (p->background)
798         {
799             /* User specified background pixmap. */
800             pixbuf = gdk_pixbuf_new_from_file(p->background_file, NULL);
801         }
802         if ((p->transparent && p->alpha != 255) || /* ignore it for opaque panel */
803             (pixbuf != NULL && gdk_pixbuf_get_has_alpha(pixbuf)))
804         {
805             /* Transparent.  Determine the appropriate value from the root pixmap. */
806             paint_root_pixmap(panel, cr);
807         }
808         if (pixbuf != NULL)
809         {
810             gint w = gdk_pixbuf_get_width(pixbuf);
811             gint h = gdk_pixbuf_get_height(pixbuf);
812 
813             /* Tile the image */
814             for (y = 0; y < p->ah; y += h)
815                 for (x = 0; x < p->aw; x += w)
816                 {
817                     gdk_cairo_set_source_pixbuf(cr, pixbuf, x, y);
818                     cairo_paint(cr);
819                 }
820             y = 0;
821             g_object_unref(pixbuf);
822         }
823         else
824         {
825             /* Either color is set or image is invalid, fill the background */
826             gdk_cairo_set_source_color(cr, &p->gtintcolor);
827             cairo_paint_with_alpha(cr, p->transparent ? (double)p->alpha/255 : 1.0);
828         }
829         cairo_destroy(cr);
830     }
831 
832     if (p->surface != NULL)
833     {
834         gtk_widget_set_app_paintable(widget, TRUE);
835 #if GTK_CHECK_VERSION(3, 0, 0)
836         pattern = cairo_pattern_create_for_surface(p->surface);
837         gdk_window_set_background_pattern(window, pattern);
838         cairo_pattern_destroy(pattern);
839 #else
840         pixmap = gdk_pixmap_new(window, p->aw, p->ah, -1);
841         cr = gdk_cairo_create(pixmap);
842         cairo_set_source_surface(cr, p->surface, 0, 0);
843         cairo_paint(cr);
844         cairo_destroy(cr);
845         gdk_window_set_back_pixmap(window, pixmap, FALSE);
846         g_object_unref(pixmap);
847 #endif
848     }
849     else
850     {
851 not_paintable:
852         gtk_widget_set_app_paintable(widget, FALSE);
853     }
854 }
855 
panel_determine_background_pixmap(Panel * panel,GtkWidget * widget,GdkWindow * window)856 void panel_determine_background_pixmap(Panel * panel, GtkWidget * widget, GdkWindow * window)
857 {
858     if (GTK_WIDGET(panel->topgwin) != widget)
859     {
860         /* Backward compatibility:
861            reset background for the child, using background of panel */
862         gtk_widget_set_app_paintable(widget, (panel->background || panel->transparent));
863 #if GTK_CHECK_VERSION(3, 0, 0)
864         gdk_window_set_background_pattern(window, NULL);
865 #else
866         gdk_window_set_back_pixmap(window, NULL, TRUE);
867 #endif
868     }
869     else
870         _panel_determine_background_pixmap(panel->topgwin);
871 }
872 
873 /* Update the background of the entire panel.
874  * This function should only be called after the panel has been realized. */
panel_update_background(Panel * p)875 void panel_update_background(Panel * p)
876 {
877     _panel_update_background(p->topgwin, TRUE);
878 }
879 
_panel_update_background(LXPanel * p,gboolean enforce)880 static void _panel_update_background(LXPanel * p, gboolean enforce)
881 {
882     GtkWidget *w = GTK_WIDGET(p);
883     GList *plugins = NULL, *l;
884 
885     /* reset background image */
886     if (p->priv->surface != NULL) /* FIXME: honor enforce on composited screen */
887     {
888         cairo_surface_destroy(p->priv->surface);
889         p->priv->surface = NULL;
890     }
891 
892     /* Redraw the top level widget. */
893     _panel_determine_background_pixmap(p);
894 #if !GTK_CHECK_VERSION(3, 0, 0)
895     gdk_window_clear(gtk_widget_get_window(w));
896 #endif
897     gtk_widget_queue_draw(w);
898 
899     /* Loop over all plugins redrawing each plugin. */
900     if (p->priv->box != NULL)
901         plugins = gtk_container_get_children(GTK_CONTAINER(p->priv->box));
902     for (l = plugins; l != NULL; l = l->next)
903         plugin_widget_set_background(l->data, p);
904     g_list_free(plugins);
905 }
906 
907 /****************************************************
908  *         autohide : borrowed from fbpanel         *
909  ****************************************************/
910 
911 /* Autohide is behaviour when panel hides itself when mouse is "far enough"
912  * and pops up again when mouse comes "close enough".
913  * Formally, it's a state machine with 3 states that driven by mouse
914  * coordinates and timer:
915  * 1. VISIBLE - ensures that panel is visible. When/if mouse goes "far enough"
916  *      switches to WAITING state
917  * 2. WAITING - starts timer. If mouse comes "close enough", stops timer and
918  *      switches to VISIBLE.  If timer expires, switches to HIDDEN
919  * 3. HIDDEN - hides panel. When mouse comes "close enough" switches to VISIBLE
920  *
921  * Note 1
922  * Mouse coordinates are queried every PERIOD milisec
923  *
924  * Note 2
925  * If mouse is less then GAP pixels to panel it's considered to be close,
926  * otherwise it's far
927  */
928 
929 #define GAP 2
930 #define PERIOD 300
931 
932 typedef enum
933 {
934     AH_STATE_VISIBLE,
935     AH_STATE_WAITING,
936     AH_STATE_HIDDEN
937 } PanelAHState;
938 
939 static void ah_state_set(LXPanel *p, PanelAHState ah_state);
940 
941 static gboolean
mouse_watch(LXPanel * panel)942 mouse_watch(LXPanel *panel)
943 {
944     Panel *p = panel->priv;
945     gint x, y;
946 
947     if (g_source_is_destroyed(g_main_current_source()))
948         return FALSE;
949 
950     ENTER;
951     gdk_display_get_pointer(gdk_display_get_default(), NULL, &x, &y, NULL);
952 
953 /*  Reduce sensitivity area
954     p->ah_far = ((x < p->cx - GAP) || (x > p->cx + p->cw + GAP)
955         || (y < p->cy - GAP) || (y > p->cy + p->ch + GAP));
956 */
957 
958     gint cx, cy, cw, ch, gap;
959 
960     cx = p->ax;
961     cy = p->ay;
962     cw = p->cw;
963     ch = p->ch;
964 
965     if (p->move_state != PANEL_MOVE_STOP)
966         /* prevent autohide when dragging is on */
967         return TRUE;
968 
969     if (cw == 1) cw = 0;
970     if (ch == 1) ch = 0;
971     /* reduce area which will raise panel so it does not interfere with apps */
972     if (p->ah_state == AH_STATE_HIDDEN) {
973         gap = MAX(p->height_when_hidden, GAP);
974         switch (p->edge) {
975         case EDGE_LEFT:
976             cw = gap;
977             break;
978         case EDGE_RIGHT:
979             cx = cx + cw - gap;
980             cw = gap;
981             break;
982         case EDGE_TOP:
983             ch = gap;
984             break;
985         case EDGE_BOTTOM:
986             cy = cy + ch - gap;
987             ch = gap;
988             break;
989        }
990     }
991     p->ah_far = ((x < cx) || (x > cx + cw) || (y < cy) || (y > cy + ch));
992 
993     ah_state_set(panel, p->ah_state);
994     RET(TRUE);
995 }
996 
ah_state_hide_timeout(gpointer p)997 static gboolean ah_state_hide_timeout(gpointer p)
998 {
999     if (!g_source_is_destroyed(g_main_current_source()))
1000     {
1001         ah_state_set(p, AH_STATE_HIDDEN);
1002         ((LXPanel *)p)->priv->hide_timeout = 0;
1003     }
1004     return FALSE;
1005 }
1006 
ah_state_set(LXPanel * panel,PanelAHState ah_state)1007 static void ah_state_set(LXPanel *panel, PanelAHState ah_state)
1008 {
1009     Panel *p = panel->priv;
1010     GdkRectangle rect;
1011 
1012     ENTER;
1013     if (p->ah_state != ah_state) {
1014         p->ah_state = ah_state;
1015         switch (ah_state) {
1016         case AH_STATE_VISIBLE:
1017             p->visible = TRUE;
1018             _calculate_position(panel, &rect);
1019             gtk_window_move(GTK_WINDOW(panel), rect.x, rect.y);
1020             gtk_widget_show(GTK_WIDGET(panel));
1021             gtk_widget_show(p->box);
1022             gtk_widget_queue_resize(GTK_WIDGET(panel));
1023             gtk_window_stick(GTK_WINDOW(panel));
1024             break;
1025         case AH_STATE_WAITING:
1026             if (p->hide_timeout)
1027                 g_source_remove(p->hide_timeout);
1028             p->hide_timeout = g_timeout_add(2 * PERIOD, ah_state_hide_timeout, panel);
1029             break;
1030         case AH_STATE_HIDDEN:
1031             if (p->height_when_hidden > 0)
1032                 gtk_widget_hide(p->box);
1033             else
1034                 gtk_widget_hide(GTK_WIDGET(panel));
1035             p->visible = FALSE;
1036         }
1037     } else if (p->autohide && p->ah_far) {
1038         switch (ah_state) {
1039         case AH_STATE_VISIBLE:
1040             ah_state_set(panel, AH_STATE_WAITING);
1041             break;
1042         case AH_STATE_WAITING:
1043             break;
1044         case AH_STATE_HIDDEN:
1045             /* configurator might change height_when_hidden value */
1046             if (p->height_when_hidden > 0)
1047             {
1048                 if (gtk_widget_get_visible(p->box))
1049                 {
1050                     gtk_widget_hide(p->box);
1051                     gtk_widget_show(GTK_WIDGET(panel));
1052                 }
1053             }
1054             else
1055                 if (gtk_widget_get_visible(GTK_WIDGET(panel)))
1056                 {
1057                     gtk_widget_hide(GTK_WIDGET(panel));
1058                     gtk_widget_show(p->box);
1059                 }
1060         }
1061     } else {
1062         switch (ah_state) {
1063         case AH_STATE_VISIBLE:
1064             break;
1065         case AH_STATE_WAITING:
1066             if (p->hide_timeout)
1067                 g_source_remove(p->hide_timeout);
1068             p->hide_timeout = 0;
1069             /* continue with setting visible */
1070         case AH_STATE_HIDDEN:
1071             ah_state_set(panel, AH_STATE_VISIBLE);
1072         }
1073     }
1074     RET();
1075 }
1076 
1077 /* starts autohide behaviour */
ah_start(LXPanel * p)1078 static void ah_start(LXPanel *p)
1079 {
1080     ENTER;
1081     if (!p->priv->mouse_timeout)
1082         p->priv->mouse_timeout = g_timeout_add(PERIOD, (GSourceFunc) mouse_watch, p);
1083     RET();
1084 }
1085 
1086 /* stops autohide */
ah_stop(LXPanel * p)1087 static void ah_stop(LXPanel *p)
1088 {
1089     ENTER;
1090     if (p->priv->mouse_timeout) {
1091         g_source_remove(p->priv->mouse_timeout);
1092         p->priv->mouse_timeout = 0;
1093     }
1094     if (p->priv->hide_timeout) {
1095         g_source_remove(p->priv->hide_timeout);
1096         p->priv->hide_timeout = 0;
1097     }
1098     RET();
1099 }
1100 /* end of the autohide code
1101  * ------------------------------------------------------------- */
1102 
1103 static gint
panel_popupmenu_configure(GtkWidget * widget,gpointer user_data)1104 panel_popupmenu_configure(GtkWidget *widget, gpointer user_data)
1105 {
1106     panel_configure( (LXPanel*)user_data, 0 );
1107     return TRUE;
1108 }
1109 
panel_popupmenu_config_plugin(GtkMenuItem * item,GtkWidget * plugin)1110 static void panel_popupmenu_config_plugin( GtkMenuItem* item, GtkWidget* plugin )
1111 {
1112     Panel *panel = PLUGIN_PANEL(plugin)->priv;
1113 
1114     lxpanel_plugin_show_config_dialog(plugin);
1115 
1116     /* FIXME: this should be more elegant */
1117     panel->config_changed = TRUE;
1118 }
1119 
panel_popupmenu_add_item(GtkMenuItem * item,LXPanel * panel)1120 static void panel_popupmenu_add_item( GtkMenuItem* item, LXPanel* panel )
1121 {
1122     /* panel_add_plugin( panel, panel->topgwin ); */
1123     panel_configure( panel, 2 );
1124 }
1125 
panel_popupmenu_remove_item(GtkMenuItem * item,GtkWidget * plugin)1126 static void panel_popupmenu_remove_item( GtkMenuItem* item, GtkWidget* plugin )
1127 {
1128     lxpanel_remove_plugin(PLUGIN_PANEL(plugin), plugin);
1129 }
1130 
lxpanel_remove_plugin(LXPanel * p,GtkWidget * plugin)1131 void lxpanel_remove_plugin(LXPanel *p, GtkWidget *plugin)
1132 {
1133     Panel* panel = p->priv;
1134 
1135     /* If the configuration dialog is open, there will certainly be a crash if the
1136      * user manipulates the Configured Plugins list, after we remove this entry.
1137      * Close the configuration dialog if it is open. */
1138     if (panel->pref_dialog != NULL)
1139     {
1140         gtk_widget_destroy(panel->pref_dialog);
1141         panel->pref_dialog = NULL;
1142     }
1143     _lxpanel_remove_plugin(p, plugin);
1144 }
1145 
_lxpanel_remove_plugin(LXPanel * p,GtkWidget * plugin)1146 void _lxpanel_remove_plugin(LXPanel *p, GtkWidget *plugin)
1147 {
1148     Panel* panel = p->priv;
1149     GtkWidget *prev, *next;
1150     GList *children;
1151     GtkAllocation alloc;
1152     gint idx = -1, size1, size2;
1153     gboolean expand;
1154 
1155     config_setting_destroy(g_object_get_qdata(G_OBJECT(plugin), lxpanel_plugin_qconf));
1156     /* reset conf pointer because the widget still may be referenced by configurator */
1157     g_object_set_qdata(G_OBJECT(plugin), lxpanel_plugin_qconf, NULL);
1158 
1159     /* squash previous and next spaces if this plugin was between two spaces */
1160     children = gtk_container_get_children(GTK_CONTAINER(panel->box));
1161     idx = g_list_index(children, plugin);
1162     if (idx > 0)
1163     {
1164 
1165         prev = g_list_nth_data(children, idx - 1);
1166         next = g_list_nth_data(children, idx + 1);
1167         if (next && PANEL_IS_SPACE(next) && PANEL_IS_SPACE(prev))
1168         {
1169             expand = FALSE;
1170             gtk_container_child_get(GTK_CONTAINER(panel->box), prev, "expand", &expand, NULL);
1171             if (expand == TRUE)
1172             {
1173                 /* prev is expandable, remove next */
1174                 config_setting_destroy(g_object_get_qdata(G_OBJECT(next), lxpanel_plugin_qconf));
1175                 g_object_set_qdata(G_OBJECT(next), lxpanel_plugin_qconf, NULL);
1176                 gtk_widget_destroy(next);
1177             }
1178             else
1179             {
1180                 gtk_container_child_get(GTK_CONTAINER(panel->box), next, "expand", &expand, NULL);
1181                 if (expand == TRUE)
1182                 {
1183                     /* next is expandable, remove prev */
1184                     config_setting_destroy(g_object_get_qdata(G_OBJECT(prev), lxpanel_plugin_qconf));
1185                     g_object_set_qdata(G_OBJECT(prev), lxpanel_plugin_qconf, NULL);
1186                     gtk_widget_destroy(prev);
1187                 }
1188                 else
1189                 {
1190                     /* calculate size of prev -- add size of this and next to it */
1191                     size1 = _panel_space_get_size(prev);
1192                     size2 = _panel_space_get_size(next);
1193                     gtk_widget_get_allocation(plugin, &alloc);
1194                     if (panel->orientation == GTK_ORIENTATION_HORIZONTAL)
1195                         size1 += alloc.width + size2;
1196                     else
1197                         size1 += alloc.height + size2;
1198                     /* remove next */
1199                     config_setting_destroy(g_object_get_qdata(G_OBJECT(next), lxpanel_plugin_qconf));
1200                     g_object_set_qdata(G_OBJECT(next), lxpanel_plugin_qconf, NULL);
1201                     gtk_widget_destroy(next);
1202                     _panel_space_resize(prev, size1);
1203                 }
1204             }
1205         }
1206     }
1207     g_list_free(children);
1208 
1209     lxpanel_config_save(p);
1210     gtk_widget_destroy(plugin);
1211 }
1212 
1213 /* FIXME: Potentially we can support multiple panels at the same edge,
1214  * but currently this cannot be done due to some positioning problems. */
gen_panel_name(int edge,gint monitor)1215 static char* gen_panel_name( int edge, gint monitor )
1216 {
1217     const char* edge_str = num2str( edge_pair, edge, "" );
1218     char* name = NULL;
1219     char* dir = _user_config_file_name("panels", NULL);
1220     int i;
1221     for( i = 0; i < G_MAXINT; ++i )
1222     {
1223         char* f;
1224         if(monitor != 0)
1225             name = g_strdup_printf( "%s-m%d-%d", edge_str, monitor, i );
1226         else if( G_LIKELY( i > 0 ) )
1227             name =  g_strdup_printf( "%s%d", edge_str, i );
1228         else
1229             name = g_strdup( edge_str );
1230 
1231         f = g_build_filename( dir, name, NULL );
1232         if( ! g_file_test( f, G_FILE_TEST_EXISTS ) )
1233         {
1234             g_free( f );
1235             break;
1236         }
1237         g_free( name );
1238         g_free( f );
1239     }
1240     g_free( dir );
1241     return name;
1242 }
1243 
1244 /* FIXME: Potentially we can support multiple panels at the same edge,
1245  * but currently this cannot be done due to some positioning problems. */
panel_popupmenu_create_panel(GtkMenuItem * item,LXPanel * panel)1246 static void panel_popupmenu_create_panel( GtkMenuItem* item, LXPanel* panel )
1247 {
1248     gint m, e, monitors;
1249     GdkScreen *screen = gtk_widget_get_screen(GTK_WIDGET(panel));
1250     LXPanel *new_panel = panel_allocate(screen);
1251     Panel *p = new_panel->priv;
1252     config_setting_t *global;
1253 
1254     /* Allocate the edge. */
1255     g_assert(screen);
1256     monitors = gdk_screen_get_n_monitors(screen);
1257     /* try to allocate edge on current monitor first */
1258     m = panel->priv->monitor;
1259     if (m < 0)
1260     {
1261         /* panel is spanned over the screen, guess from pointer now */
1262         gint x, y;
1263         gdk_display_get_pointer(gdk_display_get_default(), NULL, &x, &y, NULL);
1264         m = gdk_screen_get_monitor_at_point(screen, x, y);
1265     }
1266     for (e = 1; e < 5; ++e)
1267     {
1268         if (panel_edge_available(p, e, m))
1269         {
1270             p->edge = e;
1271             p->monitor = m;
1272             goto found_edge;
1273         }
1274     }
1275     /* try all monitors */
1276     for(m=0; m<monitors; ++m)
1277     {
1278         /* try each of the four edges */
1279         for(e=1; e<5; ++e)
1280         {
1281             if(panel_edge_available(p,e,m)) {
1282                 p->edge = e;
1283                 p->monitor = m;
1284                 goto found_edge;
1285             }
1286         }
1287     }
1288 
1289     gtk_widget_destroy(GTK_WIDGET(new_panel));
1290     g_warning("Error adding panel: There is no room for another panel. All the edges are taken.");
1291     fm_show_error(NULL, NULL, _("There is no room for another panel. All the edges are taken."));
1292     return;
1293 
1294 found_edge:
1295     p->name = gen_panel_name(p->edge, p->monitor);
1296 
1297     /* create new config with first group "Global" */
1298     global = config_group_add_subgroup(config_root_setting(p->config), "Global");
1299     config_group_set_string(global, "edge", num2str(edge_pair, p->edge, "none"));
1300     config_group_set_int(global, "monitor", p->monitor);
1301     panel_configure(new_panel, 0);
1302     panel_normalize_configuration(p);
1303     panel_start_gui(new_panel, NULL);
1304 
1305     lxpanel_config_save(new_panel);
1306     all_panels = g_slist_prepend(all_panels, new_panel);
1307 }
1308 
panel_popupmenu_delete_panel(GtkMenuItem * item,LXPanel * panel)1309 static void panel_popupmenu_delete_panel( GtkMenuItem* item, LXPanel* panel )
1310 {
1311     GtkWidget* dlg;
1312     gboolean ok;
1313     dlg = gtk_message_dialog_new_with_markup( GTK_WINDOW(panel),
1314                                                     GTK_DIALOG_MODAL,
1315                                                     GTK_MESSAGE_QUESTION,
1316                                                     GTK_BUTTONS_OK_CANCEL,
1317                                                     _("Really delete this panel?\n<b>Warning: This can not be recovered.</b>") );
1318     panel_apply_icon(GTK_WINDOW(dlg));
1319     gtk_window_set_title( (GtkWindow*)dlg, _("Confirm") );
1320     ok = ( gtk_dialog_run( (GtkDialog*)dlg ) == GTK_RESPONSE_OK );
1321     gtk_widget_destroy( dlg );
1322     if( ok )
1323     {
1324         gchar *fname;
1325         all_panels = g_slist_remove( all_panels, panel );
1326 
1327         /* delete the config file of this panel */
1328         fname = _user_config_file_name("panels", panel->priv->name);
1329         g_unlink( fname );
1330         g_free(fname);
1331         panel->priv->config_changed = 0;
1332         gtk_widget_destroy(GTK_WIDGET(panel));
1333     }
1334 }
1335 
panel_popupmenu_about(GtkMenuItem * item,Panel * panel)1336 static void panel_popupmenu_about( GtkMenuItem* item, Panel* panel )
1337 {
1338     GtkWidget *about;
1339     const gchar* authors[] = {
1340         "Hong Jen Yee (PCMan) <pcman.tw@gmail.com>",
1341         "Jim Huang <jserv.tw@gmail.com>",
1342         "Greg McNew <gmcnew@gmail.com> (battery plugin)",
1343         "Fred Chien <cfsghost@gmail.com>",
1344         "Daniel Kesler <kesler.daniel@gmail.com>",
1345         "Juergen Hoetzel <juergen@archlinux.org>",
1346         "Marty Jack <martyj19@comcast.net>",
1347         "Martin Bagge <brother@bsnet.se>",
1348         "Andriy Grytsenko <andrej@rep.kiev.ua>",
1349         "Giuseppe Penone <giuspen@gmail.com>",
1350         "Piotr Sipika <piotr.sipika@gmail.com>",
1351         NULL
1352     };
1353     /* TRANSLATORS: Replace this string with your names, one name per line. */
1354     gchar *translators = _( "translator-credits" );
1355 
1356     about = gtk_about_dialog_new();
1357     panel_apply_icon(GTK_WINDOW(about));
1358     gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about), VERSION);
1359     gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about), _("LXPanel"));
1360 
1361     if(gtk_icon_theme_has_icon(panel->icon_theme, "video-display"))
1362     {
1363          gtk_about_dialog_set_logo( GTK_ABOUT_DIALOG(about),
1364                                     gtk_icon_theme_load_icon(panel->icon_theme, "video-display", 48, 0, NULL));
1365     }
1366     else if (gtk_icon_theme_has_icon(panel->icon_theme, "start-here"))
1367     {
1368          gtk_about_dialog_set_logo( GTK_ABOUT_DIALOG(about),
1369                                     gtk_icon_theme_load_icon(panel->icon_theme, "start-here", 48, 0, NULL));
1370     }
1371     else
1372     {
1373         gtk_about_dialog_set_logo(  GTK_ABOUT_DIALOG(about),
1374                                     gdk_pixbuf_new_from_file(PACKAGE_DATA_DIR "/images/my-computer.png", NULL));
1375     }
1376 
1377     gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(about), _("Copyright (C) 2008-2016"));
1378     gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about), _( "Desktop panel for LXDE project"));
1379     gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about), "This program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.");
1380     gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about), "http://lxde.org/");
1381     gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(about), authors);
1382     gtk_about_dialog_set_translator_credits(GTK_ABOUT_DIALOG(about), translators);
1383     gtk_dialog_run(GTK_DIALOG(about));
1384     gtk_widget_destroy(about);
1385 }
1386 
panel_apply_icon(GtkWindow * w)1387 void panel_apply_icon( GtkWindow *w )
1388 {
1389     GdkPixbuf* window_icon;
1390 
1391     if(gtk_icon_theme_has_icon(gtk_icon_theme_get_default(), "video-display"))
1392     {
1393         window_icon = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "video-display", 24, 0, NULL);
1394     }
1395     else if(gtk_icon_theme_has_icon(gtk_icon_theme_get_default(), "start-here"))
1396     {
1397         window_icon = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "start-here", 24, 0, NULL);
1398     }
1399     else
1400     {
1401         window_icon = gdk_pixbuf_new_from_file(PACKAGE_DATA_DIR "/images/my-computer.png", NULL);
1402     }
1403     gtk_window_set_icon(w, window_icon);
1404 }
1405 
lxpanel_get_plugin_menu(LXPanel * panel,GtkWidget * plugin,gboolean use_sub_menu)1406 GtkMenu* lxpanel_get_plugin_menu( LXPanel* panel, GtkWidget* plugin, gboolean use_sub_menu )
1407 {
1408     GtkWidget  *menu_item, *img;
1409     GtkMenu *ret,*menu;
1410     const LXPanelPluginInit *init;
1411     char* tmp;
1412 
1413     ret = menu = GTK_MENU(gtk_menu_new());
1414 
1415     if (plugin)
1416     {
1417         init = PLUGIN_CLASS(plugin);
1418         /* create single item - plugin instance settings */
1419         img = gtk_image_new_from_stock( GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU );
1420         tmp = g_strdup_printf(_("\"%s\" Settings"),
1421                               g_dgettext(init->gettext_package, init->name));
1422         menu_item = gtk_image_menu_item_new_with_label( tmp );
1423         g_free( tmp );
1424         gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1425         gtk_menu_shell_prepend(GTK_MENU_SHELL(ret), menu_item);
1426         if( init->config )
1427             g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_config_plugin), plugin );
1428         else
1429             gtk_widget_set_sensitive( menu_item, FALSE );
1430         /* add custom items by plugin if requested */
1431         if (init->update_context_menu != NULL)
1432             use_sub_menu = init->update_context_menu(plugin, ret);
1433         /* append a separator */
1434         menu_item = gtk_separator_menu_item_new();
1435         gtk_menu_shell_append(GTK_MENU_SHELL(ret), menu_item);
1436     }
1437     if (use_sub_menu)
1438         menu = GTK_MENU(gtk_menu_new());
1439 
1440     img = gtk_image_new_from_stock( GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU );
1441     menu_item = gtk_image_menu_item_new_with_label(_("Add / Remove Panel Items"));
1442     gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1443     gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1444     g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_add_item), panel );
1445 
1446     if( plugin )
1447     {
1448         img = gtk_image_new_from_stock( GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU );
1449         tmp = g_strdup_printf( _("Remove \"%s\" From Panel"), _(init->name) );
1450         menu_item = gtk_image_menu_item_new_with_label( tmp );
1451         g_free( tmp );
1452         gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1453         gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1454         g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_remove_item), plugin );
1455     }
1456 
1457     menu_item = gtk_separator_menu_item_new();
1458     gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1459 
1460     img = gtk_image_new_from_stock( GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU );
1461     menu_item = gtk_image_menu_item_new_with_label(_("Panel Settings"));
1462     gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1463     gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1464     g_signal_connect(G_OBJECT(menu_item), "activate", G_CALLBACK(panel_popupmenu_configure), panel );
1465 
1466     img = gtk_image_new_from_stock( GTK_STOCK_NEW, GTK_ICON_SIZE_MENU );
1467     menu_item = gtk_image_menu_item_new_with_label(_("Create New Panel"));
1468     gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1469     gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1470     g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_create_panel), panel );
1471 
1472     img = gtk_image_new_from_stock( GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU );
1473     menu_item = gtk_image_menu_item_new_with_label(_("Delete This Panel"));
1474     gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1475     gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1476     g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_delete_panel), panel );
1477     if( ! all_panels->next )    /* if this is the only panel */
1478         gtk_widget_set_sensitive( menu_item, FALSE );
1479 
1480     menu_item = gtk_separator_menu_item_new();
1481     gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1482 
1483     img = gtk_image_new_from_stock( GTK_STOCK_ABOUT, GTK_ICON_SIZE_MENU );
1484     menu_item = gtk_image_menu_item_new_with_label(_("About"));
1485     gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1486     gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1487     g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_about), panel->priv );
1488 
1489     if( use_sub_menu )
1490     {
1491         menu_item = gtk_image_menu_item_new_with_label(_("Panel"));
1492         gtk_menu_shell_append(GTK_MENU_SHELL(ret), menu_item);
1493         gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), GTK_WIDGET(menu) );
1494     }
1495 
1496     gtk_widget_show_all(GTK_WIDGET(ret));
1497 
1498     g_signal_connect( ret, "selection-done", G_CALLBACK(gtk_widget_destroy), NULL );
1499     return ret;
1500 }
1501 
1502 /* for old plugins compatibility */
lxpanel_get_panel_menu(Panel * panel,Plugin * plugin,gboolean use_sub_menu)1503 GtkMenu* lxpanel_get_panel_menu( Panel* panel, Plugin* plugin, gboolean use_sub_menu )
1504 {
1505     return lxpanel_get_plugin_menu(panel->topgwin, plugin->pwid, use_sub_menu);
1506 }
1507 
1508 /****************************************************
1509  *         panel creation                           *
1510  ****************************************************/
1511 
1512 static void
make_round_corners(Panel * p)1513 make_round_corners(Panel *p)
1514 {
1515     /* FIXME: This should be re-written with shape extension of X11 */
1516     /* gdk_window_shape_combine_mask() can be used */
1517 }
1518 
panel_set_dock_type(Panel * p)1519 void panel_set_dock_type(Panel *p)
1520 {
1521     Display *xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
1522 
1523     if (p->setdocktype) {
1524         Atom state = a_NET_WM_WINDOW_TYPE_DOCK;
1525         XChangeProperty(xdisplay, p->topxwin,
1526                         a_NET_WM_WINDOW_TYPE, XA_ATOM, 32,
1527                         PropModeReplace, (unsigned char *) &state, 1);
1528     }
1529     else {
1530         XDeleteProperty( xdisplay, p->topxwin, a_NET_WM_WINDOW_TYPE );
1531     }
1532 }
1533 
panel_establish_autohide(Panel * p)1534 void panel_establish_autohide(Panel *p)
1535 {
1536     _panel_establish_autohide(p->topgwin);
1537 }
1538 
_panel_establish_autohide(LXPanel * p)1539 void _panel_establish_autohide(LXPanel *p)
1540 {
1541     if (p->priv->autohide)
1542         ah_start(p);
1543     else
1544     {
1545         ah_stop(p);
1546         ah_state_set(p, AH_STATE_VISIBLE);
1547     }
1548 }
1549 
1550 /* Set an image from a file with scaling to the panel icon size. */
panel_image_set_from_file(Panel * p,GtkWidget * image,const char * file)1551 void panel_image_set_from_file(Panel * p, GtkWidget * image, const char * file)
1552 {
1553     GdkPixbuf * pixbuf = gdk_pixbuf_new_from_file_at_scale(file, p->icon_size, p->icon_size, TRUE, NULL);
1554     if (pixbuf != NULL)
1555     {
1556         gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf);
1557         g_object_unref(pixbuf);
1558     }
1559 }
1560 
lxpanel_image_set_from_file(LXPanel * p,GtkWidget * image,const char * file)1561 void lxpanel_image_set_from_file(LXPanel * p, GtkWidget * image, const char * file)
1562 {
1563     panel_image_set_from_file(p->priv, image, file);
1564 }
1565 
1566 /* Set an image from a icon theme with scaling to the panel icon size. */
panel_image_set_icon_theme(Panel * p,GtkWidget * image,const gchar * icon)1567 gboolean panel_image_set_icon_theme(Panel * p, GtkWidget * image, const gchar * icon)
1568 {
1569     if (gtk_icon_theme_has_icon(p->icon_theme, icon))
1570     {
1571         GdkPixbuf * pixbuf = gtk_icon_theme_load_icon(p->icon_theme, icon, p->icon_size, 0, NULL);
1572         gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf);
1573         g_object_unref(pixbuf);
1574         return TRUE;
1575     }
1576     return FALSE;
1577 }
1578 
lxpanel_image_set_icon_theme(LXPanel * p,GtkWidget * image,const gchar * icon)1579 gboolean lxpanel_image_set_icon_theme(LXPanel * p, GtkWidget * image, const gchar * icon)
1580 {
1581     return panel_image_set_icon_theme(p->priv, image, icon);
1582 }
1583 
1584 static int
panel_parse_plugin(LXPanel * p,config_setting_t * cfg)1585 panel_parse_plugin(LXPanel *p, config_setting_t *cfg)
1586 {
1587     const char *type = NULL;
1588 
1589     ENTER;
1590     config_setting_lookup_string(cfg, "type", &type);
1591     DBG("plug %s\n", type);
1592 
1593     if (!type || lxpanel_add_plugin(p, type, cfg, -1) == NULL) {
1594         g_warning( "lxpanel: can't load %s plugin", type);
1595         goto error;
1596     }
1597     RET(1);
1598 
1599 error:
1600     RET(0);
1601 }
1602 
1603 static void
panel_start_gui(LXPanel * panel,config_setting_t * list)1604 panel_start_gui(LXPanel *panel, config_setting_t *list)
1605 {
1606     Atom state[3];
1607     XWMHints wmhints;
1608     gulong val;
1609     Screen *xscreen = GDK_SCREEN_XSCREEN(gtk_widget_get_screen(GTK_WIDGET(panel)));
1610     Display *xdisplay = DisplayOfScreen(xscreen);
1611     Panel *p = panel->priv;
1612     GtkWidget *w = GTK_WIDGET(panel);
1613     config_setting_t *s;
1614     GdkRectangle rect;
1615     int i;
1616 
1617     ENTER;
1618 
1619     g_debug("panel_start_gui on '%s'", p->name);
1620     p->curdesk = get_net_current_desktop();
1621     p->desknum = get_net_number_of_desktops();
1622     //p->workarea = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_WORKAREA, XA_CARDINAL, &p->wa_len);
1623     p->ax = p->ay = p->aw = p->ah = 0;
1624 
1625     p->display = gdk_display_get_default();
1626     gtk_window_set_wmclass(GTK_WINDOW(panel), "panel", "lxpanel");
1627 
1628     if (G_UNLIKELY(win_grp == NULL))
1629     {
1630         win_grp = gtk_window_group_new();
1631         g_object_add_weak_pointer(G_OBJECT(win_grp), (gpointer *)&win_grp);
1632         gtk_window_group_add_window(win_grp, (GtkWindow*)panel);
1633         g_object_unref(win_grp);
1634     }
1635     else
1636         gtk_window_group_add_window(win_grp, (GtkWindow*)panel);
1637 
1638     gtk_widget_add_events( w, GDK_BUTTON_PRESS_MASK );
1639 
1640     gtk_widget_realize(w);
1641     //gdk_window_set_decorations(gtk_widget_get_window(p->topgwin), 0);
1642 
1643     // main layout manager as a single child of panel
1644     p->box = panel_box_new(panel, FALSE, 0);
1645     gtk_container_set_border_width(GTK_CONTAINER(p->box), 0);
1646     gtk_container_add(GTK_CONTAINER(panel), p->box);
1647     gtk_widget_show(p->box);
1648     if (p->round_corners)
1649         make_round_corners(p);
1650 
1651     p->topxwin = GDK_WINDOW_XID(gtk_widget_get_window(w));
1652     DBG("topxwin = %x\n", p->topxwin);
1653 
1654     /* the settings that should be done before window is mapped */
1655     wmhints.flags = InputHint;
1656     wmhints.input = 0;
1657     XSetWMHints (xdisplay, p->topxwin, &wmhints);
1658 #define WIN_HINTS_SKIP_FOCUS      (1<<0)    /* "alt-tab" skips this win */
1659     val = WIN_HINTS_SKIP_FOCUS;
1660     XChangeProperty(xdisplay, p->topxwin,
1661           XInternAtom(xdisplay, "_WIN_HINTS", False), XA_CARDINAL, 32,
1662           PropModeReplace, (unsigned char *) &val, 1);
1663 
1664     panel_set_dock_type(p);
1665 
1666     /* window mapping point */
1667     p->visible = TRUE;
1668     _calculate_position(panel, &rect);
1669     gtk_window_move(GTK_WINDOW(panel), rect.x, rect.y);
1670     gtk_window_present(GTK_WINDOW(panel));
1671 
1672     /* the settings that should be done after window is mapped */
1673 
1674     /* send it to running wm */
1675     Xclimsgx(xscreen, p->topxwin, a_NET_WM_DESKTOP, G_MAXULONG, 0, 0, 0, 0);
1676     /* and assign it ourself just for case when wm is not running */
1677     val = G_MAXULONG;
1678     XChangeProperty(xdisplay, p->topxwin, a_NET_WM_DESKTOP, XA_CARDINAL, 32,
1679           PropModeReplace, (unsigned char *) &val, 1);
1680 
1681     state[0] = a_NET_WM_STATE_SKIP_PAGER;
1682     state[1] = a_NET_WM_STATE_SKIP_TASKBAR;
1683     state[2] = a_NET_WM_STATE_STICKY;
1684     XChangeProperty(xdisplay, p->topxwin, a_NET_WM_STATE, XA_ATOM,
1685           32, PropModeReplace, (unsigned char *) state, 3);
1686 
1687     p->initialized = TRUE;
1688 
1689     if (list) for (i = 1; (s = config_setting_get_elem(list, i)) != NULL; )
1690         if (strcmp(config_setting_get_name(s), "Plugin") == 0 &&
1691             panel_parse_plugin(panel, s)) /* success on plugin start */
1692             i++;
1693         else /* remove invalid data from config */
1694             config_setting_remove_elem(list, i);
1695 
1696     RET();
1697 }
1698 
1699 /* Exchange the "width" and "height" terminology for vertical and horizontal panels. */
panel_adjust_geometry_terminology(Panel * p)1700 void panel_adjust_geometry_terminology(Panel * p)
1701 {
1702     if ((p->height_label != NULL) && (p->width_label != NULL)
1703     && (p->alignment_left_label != NULL) && (p->alignment_right_label != NULL))
1704     {
1705         if ((p->edge == EDGE_TOP) || (p->edge == EDGE_BOTTOM))
1706         {
1707             gtk_label_set_text(GTK_LABEL(p->height_label), _("Height:"));
1708             gtk_label_set_text(GTK_LABEL(p->width_label), _("Width:"));
1709             gtk_button_set_label(GTK_BUTTON(p->alignment_left_label), _("Left"));
1710             gtk_button_set_label(GTK_BUTTON(p->alignment_right_label), _("Right"));
1711         }
1712         else
1713         {
1714             gtk_label_set_text(GTK_LABEL(p->height_label), _("Width:"));
1715             gtk_label_set_text(GTK_LABEL(p->width_label), _("Height:"));
1716             gtk_button_set_label(GTK_BUTTON(p->alignment_left_label), _("Top"));
1717             gtk_button_set_label(GTK_BUTTON(p->alignment_right_label), _("Bottom"));
1718         }
1719     }
1720 }
1721 
1722 /* Draw text into a label, with the user preference color and optionally bold. */
panel_draw_label_text(Panel * p,GtkWidget * label,const char * text,gboolean bold,float custom_size_factor,gboolean custom_color)1723 void panel_draw_label_text(Panel * p, GtkWidget * label, const char * text,
1724                            gboolean bold, float custom_size_factor,
1725                            gboolean custom_color)
1726 {
1727     if (text == NULL)
1728     {
1729         /* Null string. */
1730         gtk_label_set_text(GTK_LABEL(label), NULL);
1731         return;
1732     }
1733 
1734     /* Compute an appropriate size so the font will scale with the panel's icon size. */
1735     int font_desc;
1736     if (p->usefontsize)
1737         font_desc = p->fontsize;
1738     else
1739     {
1740         GtkStyle *style = gtk_widget_get_style(label);
1741         font_desc = pango_font_description_get_size(style->font_desc) / PANGO_SCALE;
1742     }
1743     font_desc *= custom_size_factor;
1744 
1745     /* Check the string for characters that need to be escaped.
1746      * If any are found, create the properly escaped string and use it instead. */
1747     const char * valid_markup = text;
1748     char * escaped_text = NULL;
1749     const char * q;
1750     for (q = text; *q != '\0'; q += 1)
1751     {
1752         if ((*q == '<') || (*q == '>') || (*q == '&'))
1753         {
1754             escaped_text = g_markup_escape_text(text, -1);
1755             valid_markup = escaped_text;
1756             break;
1757         }
1758     }
1759 
1760     gchar * formatted_text;
1761     if ((custom_color) && (p->usefontcolor))
1762     {
1763         /* Color, optionally bold. */
1764         formatted_text = g_strdup_printf("<span font_desc=\"%d\" color=\"#%06x\">%s%s%s</span>",
1765                 font_desc,
1766                 gcolor2rgb24(&p->gfontcolor),
1767                 ((bold) ? "<b>" : ""),
1768                 valid_markup,
1769                 ((bold) ? "</b>" : ""));
1770     }
1771     else
1772     {
1773         /* No color, optionally bold. */
1774         formatted_text = g_strdup_printf("<span font_desc=\"%d\">%s%s%s</span>",
1775                 font_desc,
1776                 ((bold) ? "<b>" : ""),
1777                 valid_markup,
1778                 ((bold) ? "</b>" : ""));
1779     }
1780 
1781     gtk_label_set_markup(GTK_LABEL(label), formatted_text);
1782     g_free(formatted_text);
1783     g_free(escaped_text);
1784 }
1785 
lxpanel_draw_label_text(LXPanel * p,GtkWidget * label,const char * text,gboolean bold,float custom_size_factor,gboolean custom_color)1786 void lxpanel_draw_label_text(LXPanel * p, GtkWidget * label, const char * text,
1787                            gboolean bold, float custom_size_factor,
1788                            gboolean custom_color)
1789 {
1790     panel_draw_label_text(p->priv, label, text, bold, custom_size_factor, custom_color);
1791 }
1792 
panel_set_panel_configuration_changed(Panel * p)1793 void panel_set_panel_configuration_changed(Panel *p)
1794 {
1795     _panel_set_panel_configuration_changed(p->topgwin);
1796 }
1797 
_update_orientation(Panel * p)1798 static inline void _update_orientation(Panel *p)
1799 {
1800     p->orientation = (p->edge == EDGE_TOP || p->edge == EDGE_BOTTOM)
1801                         ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1802 }
1803 
_panel_idle_reconfigure(gpointer widget)1804 static gboolean _panel_idle_reconfigure(gpointer widget)
1805 {
1806     LXPanel *panel;
1807     Panel *p;
1808     GList *plugins, *l;
1809     GtkOrientation previous_orientation;
1810 
1811     if (g_source_is_destroyed(g_main_current_source()))
1812         return FALSE;
1813 
1814     panel = LXPANEL(widget);
1815     p = panel->priv;
1816     previous_orientation = p->orientation;
1817     _update_orientation(p);
1818 
1819     /* either first run or orientation was changed */
1820     if (previous_orientation != p->orientation)
1821     {
1822         panel_adjust_geometry_terminology(p);
1823         p->height = ((p->orientation == GTK_ORIENTATION_HORIZONTAL) ? PANEL_HEIGHT_DEFAULT : PANEL_WIDTH_DEFAULT);
1824         if (p->height_control != NULL)
1825             gtk_spin_button_set_value(GTK_SPIN_BUTTON(p->height_control), p->height);
1826         if ((p->widthtype == WIDTH_PIXEL) && (p->width_control != NULL))
1827         {
1828             int value = ((p->orientation == GTK_ORIENTATION_HORIZONTAL) ? gdk_screen_width() : gdk_screen_height());
1829             gtk_spin_button_set_range(GTK_SPIN_BUTTON(p->width_control), 0, value);
1830             gtk_spin_button_set_value(GTK_SPIN_BUTTON(p->width_control), value);
1831         }
1832     }
1833 
1834     /* FIXME: it's deprecated, kept for binary compatibility */
1835     if (p->orientation == GTK_ORIENTATION_HORIZONTAL) {
1836         p->my_box_new = gtk_hbox_new;
1837         p->my_separator_new = gtk_vseparator_new;
1838     } else {
1839         p->my_box_new = gtk_vbox_new;
1840         p->my_separator_new = gtk_hseparator_new;
1841     }
1842 
1843     /* recreate the main layout box */
1844     if (p->box != NULL)
1845     {
1846         gtk_orientable_set_orientation(GTK_ORIENTABLE(p->box), p->orientation);
1847     }
1848 
1849     /* NOTE: This loop won't be executed when panel started since
1850        plugins are not loaded at that time.
1851        This is used when the orientation of the panel is changed
1852        from the config dialog, and plugins should be re-layout.
1853     */
1854     plugins = p->box ? gtk_container_get_children(GTK_CONTAINER(p->box)) : NULL;
1855     for( l = plugins; l; l = l->next ) {
1856         GtkWidget *w = (GtkWidget*)l->data;
1857         const LXPanelPluginInit *init = PLUGIN_CLASS(w);
1858         if (init->reconfigure)
1859             init->reconfigure(panel, w);
1860     }
1861     g_list_free(plugins);
1862     /* panel geometry changed? update panel background then */
1863     _panel_queue_update_background(panel);
1864 
1865     p->reconfigure_queued = 0;
1866 
1867     return FALSE;
1868 }
1869 
_panel_set_panel_configuration_changed(LXPanel * panel)1870 void _panel_set_panel_configuration_changed(LXPanel *panel)
1871 {
1872     if (panel->priv->reconfigure_queued)
1873         return;
1874     panel->priv->reconfigure_queued = g_idle_add(_panel_idle_reconfigure, panel);
1875 }
1876 
1877 static int
panel_parse_global(Panel * p,config_setting_t * cfg)1878 panel_parse_global(Panel *p, config_setting_t *cfg)
1879 {
1880     const char *str;
1881     gint i;
1882 
1883     /* check Global config */
1884     if (!cfg || strcmp(config_setting_get_name(cfg), "Global") != 0)
1885     {
1886         g_warning( "lxpanel: Global section not found");
1887         RET(0);
1888     }
1889     if (config_setting_lookup_string(cfg, "edge", &str))
1890         p->edge = str2num(edge_pair, str, EDGE_NONE);
1891     if (config_setting_lookup_string(cfg, "align", &str) ||
1892         /* NOTE: supporting "allign" for backward compatibility */
1893         config_setting_lookup_string(cfg, "allign", &str))
1894         p->align = str2num(allign_pair, str, ALIGN_NONE);
1895     config_setting_lookup_int(cfg, "monitor", &p->monitor);
1896     config_setting_lookup_int(cfg, "margin", &p->margin);
1897     if (config_setting_lookup_string(cfg, "widthtype", &str))
1898         p->widthtype = str2num(width_pair, str, WIDTH_NONE);
1899     config_setting_lookup_int(cfg, "width", &p->width);
1900     if (config_setting_lookup_string(cfg, "heighttype", &str))
1901         p->heighttype = str2num(height_pair, str, HEIGHT_NONE);
1902     config_setting_lookup_int(cfg, "height", &p->height);
1903     if (config_setting_lookup_int(cfg, "spacing", &i) && i > 0)
1904         p->spacing = i;
1905     if (config_setting_lookup_int(cfg, "setdocktype", &i))
1906         p->setdocktype = i != 0;
1907     if (config_setting_lookup_int(cfg, "setpartialstrut", &i))
1908         p->setstrut = i != 0;
1909     if (config_setting_lookup_int(cfg, "RoundCorners", &i))
1910         p->round_corners = i != 0;
1911     if (config_setting_lookup_int(cfg, "transparent", &i))
1912         p->transparent = i != 0;
1913     if (config_setting_lookup_int(cfg, "alpha", &p->alpha))
1914     {
1915         if (p->alpha > 255)
1916             p->alpha = 255;
1917     }
1918     if (config_setting_lookup_int(cfg, "autohide", &i))
1919         p->autohide = i != 0;
1920     if (config_setting_lookup_int(cfg, "heightwhenhidden", &i))
1921         p->height_when_hidden = MAX(0, i);
1922     if (config_setting_lookup_string(cfg, "tintcolor", &str))
1923     {
1924         if (!gdk_color_parse (str, &p->gtintcolor))
1925             gdk_color_parse ("white", &p->gtintcolor);
1926         p->tintcolor = gcolor2rgb24(&p->gtintcolor);
1927             DBG("tintcolor=%x\n", p->tintcolor);
1928     }
1929     if (config_setting_lookup_int(cfg, "usefontcolor", &i))
1930         p->usefontcolor = i != 0;
1931     if (config_setting_lookup_string(cfg, "fontcolor", &str))
1932     {
1933         if (!gdk_color_parse (str, &p->gfontcolor))
1934             gdk_color_parse ("black", &p->gfontcolor);
1935         p->fontcolor = gcolor2rgb24(&p->gfontcolor);
1936             DBG("fontcolor=%x\n", p->fontcolor);
1937     }
1938     if (config_setting_lookup_int(cfg, "usefontsize", &i))
1939         p->usefontsize = i != 0;
1940     if (config_setting_lookup_int(cfg, "fontsize", &i) && i > 0)
1941         p->fontsize = i;
1942     if (config_setting_lookup_int(cfg, "background", &i))
1943         p->background = i != 0;
1944     if (config_setting_lookup_string(cfg, "backgroundfile", &str))
1945         p->background_file = g_strdup(str);
1946     config_setting_lookup_int(cfg, "iconsize", &p->icon_size);
1947 
1948     _update_orientation(p);
1949     panel_normalize_configuration(p);
1950 
1951     return 1;
1952 }
1953 
on_monitors_changed(GdkScreen * screen,gpointer unused)1954 static void on_monitors_changed(GdkScreen* screen, gpointer unused)
1955 {
1956     GSList *pl;
1957     int monitors = gdk_screen_get_n_monitors(screen);
1958 
1959     for (pl = all_panels; pl; pl = pl->next)
1960     {
1961         LXPanel *p = pl->data;
1962 
1963         /* handle connecting and disconnecting monitors now */
1964         if (p->priv->monitor < monitors && !p->priv->initialized)
1965             panel_start_gui(p, config_setting_get_member(config_root_setting(p->priv->config), ""));
1966         else if (p->priv->monitor >= monitors && p->priv->initialized)
1967             panel_stop_gui(p);
1968         /* resize panel if appropriate monitor changed its size or position */
1969         else
1970         {
1971             /* SF bug #666: after screen resize panel cannot establish
1972                right size since cannot be moved while is hidden */
1973             ah_state_set(p, AH_STATE_VISIBLE);
1974             gtk_widget_queue_resize(GTK_WIDGET(p));
1975         }
1976     }
1977 }
1978 
panel_start(LXPanel * p)1979 static int panel_start(LXPanel *p)
1980 {
1981     config_setting_t *list;
1982     GdkScreen *screen = gtk_widget_get_screen(GTK_WIDGET(p));
1983 
1984     /* parse global section */
1985     ENTER;
1986 
1987     list = config_setting_get_member(config_root_setting(p->priv->config), "");
1988     if (!list || !panel_parse_global(p->priv, config_setting_get_elem(list, 0)))
1989         RET(0);
1990 
1991     if (p->priv->monitor < gdk_screen_get_n_monitors(screen))
1992         panel_start_gui(p, list);
1993     if (monitors_handler == 0)
1994         monitors_handler = g_signal_connect(screen, "monitors-changed",
1995                                             G_CALLBACK(on_monitors_changed), NULL);
1996     return 1;
1997 }
1998 
panel_destroy(Panel * p)1999 void panel_destroy(Panel *p)
2000 {
2001     gtk_widget_destroy(GTK_WIDGET(p->topgwin));
2002 }
2003 
panel_new(const char * config_file,const char * config_name)2004 LXPanel* panel_new( const char* config_file, const char* config_name )
2005 {
2006     LXPanel* panel = NULL;
2007 
2008     if (G_LIKELY(config_file))
2009     {
2010         panel = panel_allocate(gdk_screen_get_default());
2011         panel->priv->name = g_strdup(config_name);
2012         g_debug("starting panel from file %s",config_file);
2013         if (!config_read_file(panel->priv->config, config_file) ||
2014             !panel_start(panel))
2015         {
2016             g_warning( "lxpanel: can't start panel");
2017             gtk_widget_destroy(GTK_WIDGET(panel));
2018             panel = NULL;
2019         }
2020     }
2021     return panel;
2022 }
2023 
2024 
panel_get_orientation(LXPanel * panel)2025 GtkOrientation panel_get_orientation(LXPanel *panel)
2026 {
2027     return panel->priv->orientation;
2028 }
2029 
panel_get_icon_size(LXPanel * panel)2030 gint panel_get_icon_size(LXPanel *panel)
2031 {
2032     return panel->priv->icon_size;
2033 }
2034 
panel_get_height(LXPanel * panel)2035 gint panel_get_height(LXPanel *panel)
2036 {
2037     return panel->priv->height;
2038 }
2039 
panel_get_xwindow(LXPanel * panel)2040 Window panel_get_xwindow(LXPanel *panel)
2041 {
2042     return panel->priv->topxwin;
2043 }
2044 
panel_get_monitor(LXPanel * panel)2045 gint panel_get_monitor(LXPanel *panel)
2046 {
2047     return panel->priv->monitor;
2048 }
2049 
panel_get_defstyle(LXPanel * panel)2050 GtkStyle *panel_get_defstyle(LXPanel *panel)
2051 {
2052     return panel->priv->defstyle;
2053 }
2054 
panel_get_icon_theme(LXPanel * panel)2055 GtkIconTheme *panel_get_icon_theme(LXPanel *panel)
2056 {
2057     return panel->priv->icon_theme;
2058 }
2059 
panel_is_at_bottom(LXPanel * panel)2060 gboolean panel_is_at_bottom(LXPanel *panel)
2061 {
2062     return panel->priv->edge == EDGE_BOTTOM;
2063 }
2064 
panel_is_dynamic(LXPanel * panel)2065 gboolean panel_is_dynamic(LXPanel *panel)
2066 {
2067     return panel->priv->widthtype == WIDTH_REQUEST;
2068 }
2069 
panel_box_new(LXPanel * panel,gboolean homogeneous,gint spacing)2070 GtkWidget *panel_box_new(LXPanel *panel, gboolean homogeneous, gint spacing)
2071 {
2072     if (panel->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
2073         return gtk_hbox_new(homogeneous, spacing);
2074     return gtk_vbox_new(homogeneous, spacing);
2075 }
2076 
panel_separator_new(LXPanel * panel)2077 GtkWidget *panel_separator_new(LXPanel *panel)
2078 {
2079     if (panel->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
2080         return gtk_vseparator_new();
2081     return gtk_hseparator_new();
2082 }
2083 
_class_is_present(const LXPanelPluginInit * init)2084 gboolean _class_is_present(const LXPanelPluginInit *init)
2085 {
2086     GSList *sl;
2087 
2088     for (sl = all_panels; sl; sl = sl->next )
2089     {
2090         LXPanel *panel = (LXPanel*)sl->data;
2091         GList *plugins, *p;
2092 
2093         if (panel->priv->box == NULL)
2094             continue;
2095         plugins = gtk_container_get_children(GTK_CONTAINER(panel->priv->box));
2096         for (p = plugins; p; p = p->next)
2097             if (PLUGIN_CLASS(p->data) == init)
2098             {
2099                 g_list_free(plugins);
2100                 return TRUE;
2101             }
2102         g_list_free(plugins);
2103     }
2104     return FALSE;
2105 }
2106