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