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