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