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