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