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