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