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