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