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