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