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