Don't connect on signals on LXPanel - it's an object, use built-in handlers.
[lxde/lxpanel.git] / src / panel.c
CommitLineData
16fb8c2e 1/**
b840f7cc 2 * Copyright (c) 2006-2014 LxDE Developers, see the file AUTHORS for details.
e68b47dc
JH
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
a52c2257 19#ifdef HAVE_CONFIG_H
cf701cb7 20#include <config.h>
a52c2257
HJYP
21#endif
22
cf701cb7 23#include <glib/gi18n.h>
a52c2257 24#include <stdlib.h>
16fbda14 25#include <glib/gstdio.h>
a52c2257
HJYP
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>
e68b47dc 32#include <gdk/gdkx.h>
4f5769fc 33#include <libfm/fm-gtk.h>
a52c2257 34
1f4bc3aa
AG
35#define __LXPANEL_INTERNALS__
36
b31cb1d2 37#include "private.h"
a52c2257
HJYP
38#include "misc.h"
39#include "bg.h"
a52c2257 40
77886b88 41#include "lxpanelctl.h"
cf701cb7 42#include "dbg.h"
77886b88 43
a52c2257
HJYP
44static gchar *cfgfile = NULL;
45static gchar version[] = VERSION;
46gchar *cprofile = "default";
47
e2957bd2
HJYP
48static GtkWindowGroup* win_grp; /* window group used to limit the scope of model dialog. */
49
e68b47dc 50static int config = 0;
22242ed4 51FbEv *fbev = NULL;
a52c2257 52
cf701cb7 53GSList* all_panels = NULL; /* a single-linked list storing all panels */
a52c2257 54
f7cb330e
HJYP
55gboolean is_restarting = FALSE;
56
a7bd16a4
AG
57static int panel_start(LXPanel *p);
58static void panel_start_gui(LXPanel *p);
59static void ah_stop(LXPanel *p);
60static void on_root_bg_changed(FbBg *bg, LXPanel* p);
4b93d81e 61
95095259
HJYP
62gboolean is_in_lxde = FALSE;
63
424b1f06
AG
64static void lxpanel_size_request(GtkWidget *widget, GtkRequisition *req);
65static void lxpanel_realize(GtkWidget *widget);
66static gboolean lxpanel_button_press(GtkWidget *widget, GdkEventButton *event);
67static void lxpanel_size_allocate(GtkWidget *widget, GtkAllocation *a);
68static gboolean lxpanel_configure_event (GtkWidget *widget, GdkEventConfigure *e);
69static gboolean lxpanel_map_event(GtkWidget *widget, GdkEventAny *event);
70static void lxpanel_style_set(GtkWidget *widget, GtkStyle *prev);
71
a7bd16a4 72G_DEFINE_TYPE(PanelToplevel, lxpanel, GTK_TYPE_WINDOW);
cb22c87c 73
a7bd16a4 74static void lxpanel_finalize(GObject *object)
cb22c87c 75{
a7bd16a4
AG
76 LXPanel *self = LXPANEL(object);
77 Panel *p = self->priv;
78
79 if( p->config_changed )
80 lxpanel_config_save( self );
81 config_destroy(p->config);
82
83 g_free(p->workarea);
84 g_free( p->background_file );
85 g_slist_free( p->system_menus );
86
87 g_free( p->name );
88 g_free(p);
89
90 G_OBJECT_CLASS(lxpanel_parent_class)->finalize(object);
91}
92
93static void lxpanel_destroy(GtkObject *object)
cb22c87c 94{
a7bd16a4
AG
95 LXPanel *self = LXPANEL(object);
96 Panel *p = self->priv;
97 Display *xdisplay;
98
99 if (p->autohide)
100 ah_stop(self);
101
102 if (p->pref_dialog != NULL)
103 gtk_widget_destroy(p->pref_dialog);
104 p->pref_dialog = NULL;
105
106 if (p->plugin_pref_dialog != NULL)
107 /* just close the dialog, it will do all required cleanup */
108 gtk_dialog_response(GTK_DIALOG(p->plugin_pref_dialog), GTK_RESPONSE_CLOSE);
109
110 if (p->bg != NULL)
111 {
112 g_signal_handlers_disconnect_by_func(G_OBJECT(p->bg), on_root_bg_changed, self);
113 g_object_unref(p->bg);
114 p->bg = NULL;
115 }
116
117 if (p->initialized)
118 {
119 gtk_window_group_remove_window(win_grp, GTK_WINDOW(self));
120 xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
121 gdk_flush();
122 XFlush(xdisplay);
123 XSync(xdisplay, True);
124 p->initialized = FALSE;
125 }
126
127 GTK_OBJECT_CLASS(lxpanel_parent_class)->destroy(object);
cb22c87c 128}
a7bd16a4
AG
129
130static void lxpanel_class_init(PanelToplevelClass *klass)
cb22c87c 131{
a7bd16a4
AG
132 GObjectClass *gobject_class = (GObjectClass *)klass;
133 GtkObjectClass *gtk_object_class = (GtkObjectClass *)klass;
424b1f06 134 GtkWidgetClass *widget_class = (GtkWidgetClass *)klass;
a7bd16a4
AG
135
136 gobject_class->finalize = lxpanel_finalize;
137 gtk_object_class->destroy = lxpanel_destroy;
424b1f06
AG
138 widget_class->size_request = lxpanel_size_request;
139 widget_class->size_allocate = lxpanel_size_allocate;
140 widget_class->realize = lxpanel_realize;
141 widget_class->button_press_event = lxpanel_button_press;
142 widget_class->configure_event = lxpanel_configure_event;
143 widget_class->map_event = lxpanel_map_event;
144 widget_class->style_set = lxpanel_style_set;
cb22c87c
HJYP
145}
146
a7bd16a4 147static void lxpanel_init(PanelToplevel *self)
9dd114c4 148{
a7bd16a4
AG
149 Panel *p = g_new0(Panel, 1);
150
151 self->priv = p;
152 p->topgwin = self;
9dd114c4 153 p->allign = ALLIGN_CENTER;
154 p->edge = EDGE_NONE;
155 p->widthtype = WIDTH_PERCENT;
156 p->width = 100;
157 p->heighttype = HEIGHT_PIXEL;
158 p->height = PANEL_HEIGHT_DEFAULT;
64afc832 159 p->monitor = 0;
9dd114c4 160 p->setdocktype = 1;
161 p->setstrut = 1;
162 p->round_corners = 0;
163 p->autohide = 0;
164 p->visible = TRUE;
165 p->height_when_hidden = 2;
166 p->transparent = 0;
bf2140f8
MJ
167 p->alpha = 255;
168 gdk_color_parse("white", &p->gtintcolor);
169 p->tintcolor = gcolor2rgb24(&p->gtintcolor);
9dd114c4 170 p->usefontcolor = 0;
171 p->fontcolor = 0x00000000;
1869ef90
LK
172 p->usefontsize = 0;
173 p->fontsize = 10;
9dd114c4 174 p->spacing = 0;
8f9e6256 175 p->icon_size = PANEL_ICON_SIZE;
21f91705 176 p->icon_theme = gtk_icon_theme_get_default();
17fab6e5 177 p->config = config_new();
c1910c15 178 gtk_window_set_type_hint(GTK_WINDOW(self), GDK_WINDOW_TYPE_HINT_DOCK);
a7bd16a4
AG
179}
180
181/* Allocate and initialize new Panel structure. */
182static LXPanel* panel_allocate(void)
183{
184 return g_object_new(LX_TYPE_PANEL, NULL);
9dd114c4 185}
186
187/* Normalize panel configuration after load from file or reconfiguration. */
188static void panel_normalize_configuration(Panel* p)
189{
2918994e 190 panel_set_panel_configuration_changed( p );
9dd114c4 191 if (p->width < 0)
192 p->width = 100;
193 if (p->widthtype == WIDTH_PERCENT && p->width > 100)
194 p->width = 100;
195 p->heighttype = HEIGHT_PIXEL;
196 if (p->heighttype == HEIGHT_PIXEL) {
197 if (p->height < PANEL_HEIGHT_MIN)
198 p->height = PANEL_HEIGHT_MIN;
199 else if (p->height > PANEL_HEIGHT_MAX)
200 p->height = PANEL_HEIGHT_MAX;
201 }
64afc832
R
202 if (p->monitor < 0)
203 p->monitor = 0;
9dd114c4 204 if (p->background)
205 p->transparent = 0;
206}
207
a52c2257
HJYP
208/****************************************************
209 * panel's handlers for WM events *
210 ****************************************************/
a52c2257 211
22242ed4 212void panel_set_wm_strut(Panel *p)
a52c2257 213{
a7bd16a4
AG
214 _panel_set_wm_strut(p->topgwin);
215}
216
217void _panel_set_wm_strut(LXPanel *panel)
218{
d1d43629 219 int index;
09fa171b 220 Display *xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
a7bd16a4 221 Panel *p = panel->priv;
d1d43629 222 gulong strut_size;
223 gulong strut_lower;
224 gulong strut_upper;
a52c2257 225
9a2c13d0 226#if GTK_CHECK_VERSION(2, 20, 0)
a7bd16a4 227 if (!gtk_widget_get_mapped(GTK_WIDGET(panel)))
9a2c13d0 228#else
a7bd16a4 229 if (!GTK_WIDGET_MAPPED(panel))
9a2c13d0
AG
230#endif
231 return;
232 /* most wm's tend to ignore struts of unmapped windows, and that's how
233 * lxpanel hides itself. so no reason to set it. */
234 if (p->autohide && p->height_when_hidden <= 0)
235 return;
236
d1d43629 237 /* Dispatch on edge to set up strut parameters. */
238 switch (p->edge)
bee4c26e 239 {
d1d43629 240 case EDGE_LEFT:
241 index = 0;
242 strut_size = p->aw;
243 strut_lower = p->ay;
244 strut_upper = p->ay + p->ah;
245 break;
246 case EDGE_RIGHT:
247 index = 1;
248 strut_size = p->aw;
249 strut_lower = p->ay;
250 strut_upper = p->ay + p->ah;
251 break;
252 case EDGE_TOP:
253 index = 2;
254 strut_size = p->ah;
255 strut_lower = p->ax;
256 strut_upper = p->ax + p->aw;
257 break;
258 case EDGE_BOTTOM:
259 index = 3;
260 strut_size = p->ah;
261 strut_lower = p->ax;
262 strut_upper = p->ax + p->aw;
263 break;
264 default:
265 return;
bee4c26e
HJYP
266 }
267
176fb687 268 /* Handle autohide case. EWMH recommends having the strut be the minimized size. */
92955ae5 269 if (p->autohide)
176fb687 270 strut_size = p->height_when_hidden;
271
d1d43629 272 /* Set up strut value in property format. */
273 gulong desired_strut[12];
274 memset(desired_strut, 0, sizeof(desired_strut));
275 if (p->setstrut)
276 {
277 desired_strut[index] = strut_size;
278 desired_strut[4 + index * 2] = strut_lower;
279 desired_strut[5 + index * 2] = strut_upper;
280 }
281 else
282 {
283 strut_size = 0;
284 strut_lower = 0;
285 strut_upper = 0;
bee4c26e 286 }
bee4c26e 287
d1d43629 288 /* If strut value changed, set the property value on the panel window.
289 * This avoids property change traffic when the panel layout is recalculated but strut geometry hasn't changed. */
9a2c13d0 290 if ((p->strut_size != strut_size) || (p->strut_lower != strut_lower) || (p->strut_upper != strut_upper) || (p->strut_edge != p->edge))
d1d43629 291 {
292 p->strut_size = strut_size;
293 p->strut_lower = strut_lower;
294 p->strut_upper = strut_upper;
547ece89 295 p->strut_edge = p->edge;
a52c2257 296
d1d43629 297 /* If window manager supports STRUT_PARTIAL, it will ignore STRUT.
298 * Set STRUT also for window managers that do not support STRUT_PARTIAL. */
299 if (strut_size != 0)
300 {
09fa171b 301 XChangeProperty(xdisplay, p->topxwin, a_NET_WM_STRUT_PARTIAL,
d1d43629 302 XA_CARDINAL, 32, PropModeReplace, (unsigned char *) desired_strut, 12);
09fa171b 303 XChangeProperty(xdisplay, p->topxwin, a_NET_WM_STRUT,
d1d43629 304 XA_CARDINAL, 32, PropModeReplace, (unsigned char *) desired_strut, 4);
305 }
306 else
307 {
09fa171b
AG
308 XDeleteProperty(xdisplay, p->topxwin, a_NET_WM_STRUT);
309 XDeleteProperty(xdisplay, p->topxwin, a_NET_WM_STRUT_PARTIAL);
d1d43629 310 }
311 }
a52c2257
HJYP
312}
313
d18c9409 314static void process_client_msg ( XClientMessageEvent* ev )
77886b88 315{
8c44345a 316 int cmd = ev->data.b[0];
77886b88
HJYP
317 switch( cmd )
318 {
ace2a572 319#ifndef DISABLE_MENU
77886b88 320 case LXPANEL_CMD_SYS_MENU:
d18c9409
HJYP
321 {
322 GSList* l;
323 for( l = all_panels; l; l = l->next )
8c44345a 324 {
a7bd16a4 325 LXPanel* p = (LXPanel*)l->data;
dd731fde
AG
326 GList *plugins, *pl;
327
a7bd16a4 328 plugins = gtk_container_get_children(GTK_CONTAINER(p->priv->box));
dd731fde 329 for (pl = plugins; pl; pl = pl->next)
d18c9409 330 {
191694fb 331 const LXPanelPluginInit *init = PLUGIN_CLASS(pl->data);
dd731fde
AG
332 if (init->show_system_menu)
333 /* queue to show system menu */
334 init->show_system_menu(pl->data);
d18c9409 335 }
dd731fde 336 g_list_free(plugins);
8c44345a 337 }
77886b88 338 break;
d18c9409 339 }
ace2a572 340#endif
77886b88
HJYP
341 case LXPANEL_CMD_RUN:
342 gtk_run();
343 break;
344 case LXPANEL_CMD_CONFIG:
0c5f948f 345 {
a7bd16a4 346 LXPanel * p = ((all_panels != NULL) ? all_panels->data : NULL);
0c5f948f
MJ
347 if (p != NULL)
348 panel_configure(p, 0);
349 }
77886b88
HJYP
350 break;
351 case LXPANEL_CMD_RESTART:
352 restart();
353 break;
354 case LXPANEL_CMD_EXIT:
355 gtk_main_quit();
356 break;
357 }
358}
359
a52c2257 360static GdkFilterReturn
cf701cb7 361panel_event_filter(GdkXEvent *xevent, GdkEvent *event, gpointer not_used)
a52c2257
HJYP
362{
363 Atom at;
364 Window win;
365 XEvent *ev = (XEvent *) xevent;
366
367 ENTER;
368 DBG("win = 0x%x\n", ev->xproperty.window);
53b37ef2
HJYP
369 if (ev->type != PropertyNotify )
370 {
77886b88
HJYP
371 /* private client message from lxpanelctl */
372 if( ev->type == ClientMessage && ev->xproperty.atom == a_LXPANEL_CMD )
373 {
d18c9409 374 process_client_msg( (XClientMessageEvent*)ev );
77886b88 375 }
22242ed4
HJYP
376 else if( ev->type == DestroyNotify )
377 {
cf701cb7 378 fb_ev_emit_destroy( fbev, ((XDestroyWindowEvent*)ev)->window );
22242ed4 379 }
a52c2257 380 RET(GDK_FILTER_CONTINUE);
24053345 381 }
77886b88 382
a52c2257
HJYP
383 at = ev->xproperty.atom;
384 win = ev->xproperty.window;
53b37ef2
HJYP
385 if (win == GDK_ROOT_WINDOW())
386 {
387 if (at == a_NET_CLIENT_LIST)
388 {
389 fb_ev_emit(fbev, EV_CLIENT_LIST);
390 }
391 else if (at == a_NET_CURRENT_DESKTOP)
392 {
cf701cb7
HJYP
393 GSList* l;
394 for( l = all_panels; l; l = l->next )
a7bd16a4 395 ((LXPanel*)l->data)->priv->curdesk = get_net_current_desktop();
22242ed4 396 fb_ev_emit(fbev, EV_CURRENT_DESKTOP);
53b37ef2
HJYP
397 }
398 else if (at == a_NET_NUMBER_OF_DESKTOPS)
399 {
cf701cb7
HJYP
400 GSList* l;
401 for( l = all_panels; l; l = l->next )
a7bd16a4 402 ((LXPanel*)l->data)->priv->desknum = get_net_number_of_desktops();
22242ed4 403 fb_ev_emit(fbev, EV_NUMBER_OF_DESKTOPS);
53b37ef2
HJYP
404 }
405 else if (at == a_NET_DESKTOP_NAMES)
406 {
22242ed4 407 fb_ev_emit(fbev, EV_DESKTOP_NAMES);
53b37ef2
HJYP
408 }
409 else if (at == a_NET_ACTIVE_WINDOW)
410 {
22242ed4 411 fb_ev_emit(fbev, EV_ACTIVE_WINDOW );
53b37ef2
HJYP
412 }
413 else if (at == a_NET_CLIENT_LIST_STACKING)
414 {
22242ed4 415 fb_ev_emit(fbev, EV_CLIENT_LIST_STACKING);
53b37ef2
HJYP
416 }
417 else if (at == a_XROOTPMAP_ID)
418 {
cf701cb7
HJYP
419 GSList* l;
420 for( l = all_panels; l; l = l->next )
421 {
a7bd16a4
AG
422 LXPanel* p = (LXPanel*)l->data;
423 if (p->priv->transparent) {
424 fb_bg_notify_changed_bg(p->priv->bg);
cf701cb7
HJYP
425 }
426 }
53b37ef2
HJYP
427 }
428 else if (at == a_NET_WORKAREA)
429 {
cf701cb7
HJYP
430 GSList* l;
431 for( l = all_panels; l; l = l->next )
432 {
a7bd16a4
AG
433 LXPanel* p = (LXPanel*)l->data;
434 g_free( p->priv->workarea );
435 p->priv->workarea = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_WORKAREA, XA_CARDINAL, &p->priv->wa_len);
1d9dc649 436 /* print_wmdata(p); */
a52c2257 437 }
53b37ef2
HJYP
438 }
439 else
cf701cb7
HJYP
440 return GDK_FILTER_CONTINUE;
441
442 return GDK_FILTER_REMOVE;
a52c2257 443 }
cf701cb7 444 return GDK_FILTER_CONTINUE;
a52c2257
HJYP
445}
446
447/****************************************************
448 * panel's handlers for GTK events *
449 ****************************************************/
450
bee4c26e 451
4542c20d 452static void
a7bd16a4 453on_root_bg_changed(FbBg *bg, LXPanel* p)
4542c20d 454{
a7bd16a4 455 _panel_update_background( p );
4542c20d
HJYP
456}
457
a7bd16a4
AG
458void panel_determine_background_pixmap(Panel * panel, GtkWidget * widget, GdkWindow * window)
459{
460 _panel_determine_background_pixmap(panel->topgwin, widget);
461}
462
463void _panel_determine_background_pixmap(LXPanel * panel, GtkWidget * widget)
4542c20d 464{
2918994e 465 GdkPixmap * pixmap = NULL;
a7bd16a4
AG
466 GdkWindow * window = gtk_widget_get_window(widget);
467 Panel * p = panel->priv;
4542c20d 468
2918994e 469 /* Free p->bg if it is not going to be used. */
470 if (( ! p->transparent) && (p->bg != NULL))
471 {
a7bd16a4 472 g_signal_handlers_disconnect_by_func(G_OBJECT(p->bg), on_root_bg_changed, panel);
2918994e 473 g_object_unref(p->bg);
474 p->bg = NULL;
475 }
4542c20d 476
2918994e 477 if (p->background)
478 {
479 /* User specified background pixmap. */
480 if (p->background_file != NULL)
481 pixmap = fb_bg_get_pix_from_file(widget, p->background_file);
4542c20d 482 }
2918994e 483
484 else if (p->transparent)
4542c20d 485 {
2918994e 486 /* Transparent. Determine the appropriate value from the root pixmap. */
487 if (p->bg == NULL)
4542c20d 488 {
2918994e 489 p->bg = fb_bg_get_for_display();
a7bd16a4 490 g_signal_connect(G_OBJECT(p->bg), "changed", G_CALLBACK(on_root_bg_changed), panel);
4542c20d 491 }
2918994e 492 pixmap = fb_bg_get_xroot_pix_for_win(p->bg, widget);
493 if ((pixmap != NULL) && (pixmap != GDK_NO_BG) && (p->alpha != 0))
938806f9 494 fb_bg_composite(pixmap, &p->gtintcolor, p->alpha);
4542c20d
HJYP
495 }
496
2918994e 497 if (pixmap != NULL)
4542c20d 498 {
2918994e 499 gtk_widget_set_app_paintable(widget, TRUE );
500 gdk_window_set_back_pixmap(window, pixmap, FALSE);
501 g_object_unref(pixmap);
4542c20d
HJYP
502 }
503 else
2918994e 504 gtk_widget_set_app_paintable(widget, FALSE);
505}
4542c20d 506
2918994e 507/* Update the background of the entire panel.
508 * This function should only be called after the panel has been realized. */
509void panel_update_background(Panel * p)
510{
a7bd16a4
AG
511 _panel_update_background(p->topgwin);
512}
513
514void _panel_update_background(LXPanel * p)
515{
516 GtkWidget *w = GTK_WIDGET(p);
17fab6e5
AG
517 GList *plugins, *l;
518
2918994e 519 /* Redraw the top level widget. */
a7bd16a4
AG
520 _panel_determine_background_pixmap(p, w);
521 gdk_window_clear(gtk_widget_get_window(w));
522 gtk_widget_queue_draw(w);
2918994e 523
524 /* Loop over all plugins redrawing each plugin. */
a7bd16a4 525 plugins = gtk_container_get_children(GTK_CONTAINER(p->priv->box));
17fab6e5
AG
526 for (l = plugins; l != NULL; l = l->next)
527 plugin_widget_set_background(l->data, p);
528 g_list_free(plugins);
4542c20d 529}
a52c2257 530
a7bd16a4 531static gboolean delay_update_background( GtkWidget* p )
84fc1d55 532{
6589616e 533 /* Panel could be destroyed while background update scheduled */
09fa171b 534#if GTK_CHECK_VERSION(2, 20, 0)
a7bd16a4 535 if (gtk_widget_get_realized(p)) {
09fa171b 536#else
a7bd16a4 537 if (GTK_WIDGET_REALIZED(p)) {
09fa171b 538#endif
a7bd16a4
AG
539 gdk_display_sync( gtk_widget_get_display(p) );
540 _panel_update_background( LXPANEL(p) );
6589616e 541 }
0bcf9045 542
cf701cb7 543 return FALSE;
84fc1d55
HJYP
544}
545
424b1f06 546static void lxpanel_realize(GtkWidget *widget)
f2d54481 547{
424b1f06
AG
548 GTK_WIDGET_CLASS(lxpanel_parent_class)->realize(widget);
549
0bcf9045 550 g_idle_add_full( G_PRIORITY_LOW,
a7bd16a4 551 (GSourceFunc)delay_update_background, widget, NULL );
f2d54481
HJYP
552}
553
424b1f06 554static void lxpanel_style_set(GtkWidget *widget, GtkStyle* prev)
84fc1d55 555{
424b1f06
AG
556 GTK_WIDGET_CLASS(lxpanel_parent_class)->style_set(widget, prev);
557
0987f648 558 /* FIXME: This dirty hack is used to fix the background of systray... */
09fa171b
AG
559#if GTK_CHECK_VERSION(2, 20, 0)
560 if (gtk_widget_get_realized(widget))
561#else
cf701cb7 562 if( GTK_WIDGET_REALIZED( widget ) )
09fa171b 563#endif
0bcf9045 564 g_idle_add_full( G_PRIORITY_LOW,
a7bd16a4 565 (GSourceFunc)delay_update_background, widget, NULL );
84fc1d55 566}
4542c20d 567
424b1f06 568static void lxpanel_size_request(GtkWidget *widget, GtkRequisition *req)
a52c2257 569{
424b1f06
AG
570 Panel *p = LXPANEL(widget)->priv;
571
572 GTK_WIDGET_CLASS(lxpanel_parent_class)->size_request(widget, req);
4542c20d 573
9a2c13d0 574 if (!p->visible)
b957082c
JC
575 /* When the panel is in invisible state, the content box also got hidden, thus always
576 * report 0 size. Ask the content box instead for its size. */
577 gtk_widget_size_request(p->box, req);
578
424b1f06 579 /* FIXME: is this ever required? */
a52c2257 580 if (p->widthtype == WIDTH_REQUEST)
cfde283a 581 p->width = (p->orientation == GTK_ORIENTATION_HORIZONTAL) ? req->width : req->height;
a52c2257 582 if (p->heighttype == HEIGHT_REQUEST)
cfde283a 583 p->height = (p->orientation == GTK_ORIENTATION_HORIZONTAL) ? req->height : req->width;
a52c2257 584 calculate_position(p);
b957082c
JC
585
586 gtk_widget_set_size_request( widget, p->aw, p->ah );
a52c2257
HJYP
587}
588
424b1f06 589static void lxpanel_size_allocate(GtkWidget *widget, GtkAllocation *a)
a52c2257 590{
424b1f06
AG
591 Panel *p = LXPANEL(widget)->priv;
592
593 GTK_WIDGET_CLASS(lxpanel_parent_class)->size_allocate(widget, a);
594
a52c2257 595 if (p->widthtype == WIDTH_REQUEST)
cfde283a 596 p->width = (p->orientation == GTK_ORIENTATION_HORIZONTAL) ? a->width : a->height;
a52c2257 597 if (p->heighttype == HEIGHT_REQUEST)
cfde283a 598 p->height = (p->orientation == GTK_ORIENTATION_HORIZONTAL) ? a->height : a->width;
a52c2257 599 calculate_position(p);
4542c20d 600
424b1f06
AG
601 if (a->width != p->aw || a->height != p->ah || a->x != p->ax || a->y != p->ay)
602 {
603 gtk_window_move(GTK_WINDOW(widget), p->ax, p->ay);
604 _panel_set_wm_strut(LXPANEL(widget));
a52c2257 605 }
a52c2257
HJYP
606}
607
424b1f06 608static gboolean lxpanel_configure_event (GtkWidget *widget, GdkEventConfigure *e)
a52c2257 609{
424b1f06
AG
610 Panel *p = LXPANEL(widget)->priv;
611
a52c2257 612 if (e->width == p->cw && e->height == p->ch && e->x == p->cx && e->y == p->cy)
424b1f06 613 goto ok;
a52c2257
HJYP
614 p->cw = e->width;
615 p->ch = e->height;
616 p->cx = e->x;
617 p->cy = e->y;
4542c20d 618
a52c2257
HJYP
619 if (p->transparent)
620 fb_bg_notify_changed_bg(p->bg);
424b1f06
AG
621ok:
622 return GTK_WIDGET_CLASS(lxpanel_parent_class)->configure_event(widget, e);
a52c2257
HJYP
623}
624
9a2c13d0
AG
625/****************************************************
626 * autohide : borrowed from fbpanel *
627 ****************************************************/
628
629/* Autohide is behaviour when panel hides itself when mouse is "far enough"
630 * and pops up again when mouse comes "close enough".
631 * Formally, it's a state machine with 3 states that driven by mouse
632 * coordinates and timer:
633 * 1. VISIBLE - ensures that panel is visible. When/if mouse goes "far enough"
634 * switches to WAITING state
635 * 2. WAITING - starts timer. If mouse comes "close enough", stops timer and
636 * switches to VISIBLE. If timer expires, switches to HIDDEN
637 * 3. HIDDEN - hides panel. When mouse comes "close enough" switches to VISIBLE
638 *
639 * Note 1
640 * Mouse coordinates are queried every PERIOD milisec
641 *
642 * Note 2
643 * If mouse is less then GAP pixels to panel it's considered to be close,
644 * otherwise it's far
645 */
646
647#define GAP 2
648#define PERIOD 300
649
650typedef enum
651{
652 AH_STATE_VISIBLE,
653 AH_STATE_WAITING,
654 AH_STATE_HIDDEN
655} PanelAHState;
656
a7bd16a4 657static void ah_state_set(LXPanel *p, PanelAHState ah_state);
9a2c13d0
AG
658
659static gboolean
a7bd16a4 660mouse_watch(LXPanel *panel)
9a2c13d0 661{
a7bd16a4 662 Panel *p = panel->priv;
9a2c13d0
AG
663 gint x, y;
664
665 if (g_source_is_destroyed(g_main_current_source()))
666 return FALSE;
667
668 ENTER;
669 gdk_display_get_pointer(gdk_display_get_default(), NULL, &x, &y, NULL);
670
671/* Reduce sensitivity area
672 p->ah_far = ((x < p->cx - GAP) || (x > p->cx + p->cw + GAP)
673 || (y < p->cy - GAP) || (y > p->cy + p->ch + GAP));
674*/
675
676 gint cx, cy, cw, ch, gap;
677
678 cx = p->cx;
679 cy = p->cy;
680 cw = p->aw;
681 ch = p->ah;
682
683 /* reduce area which will raise panel so it does not interfere with apps */
684 if (p->ah_state == AH_STATE_HIDDEN) {
685 gap = MAX(p->height_when_hidden, GAP);
686 switch (p->edge) {
687 case EDGE_LEFT:
688 cw = gap;
689 break;
690 case EDGE_RIGHT:
691 cx = cx + cw - gap;
692 cw = gap;
693 break;
694 case EDGE_TOP:
695 ch = gap;
696 break;
697 case EDGE_BOTTOM:
698 cy = cy + ch - gap;
699 ch = gap;
700 break;
701 }
702 }
703 p->ah_far = ((x < cx) || (x > cx + cw) || (y < cy) || (y > cy + ch));
704
a7bd16a4 705 ah_state_set(panel, p->ah_state);
9a2c13d0
AG
706 RET(TRUE);
707}
708
709static gboolean ah_state_hide_timeout(gpointer p)
710{
711 if (!g_source_is_destroyed(g_main_current_source()))
712 {
713 ah_state_set(p, AH_STATE_HIDDEN);
a7bd16a4 714 ((LXPanel *)p)->priv->hide_timeout = 0;
9a2c13d0
AG
715 }
716 return FALSE;
717}
718
a7bd16a4 719static void ah_state_set(LXPanel *panel, PanelAHState ah_state)
9a2c13d0 720{
a7bd16a4
AG
721 Panel *p = panel->priv;
722
9a2c13d0
AG
723 ENTER;
724 if (p->ah_state != ah_state) {
725 p->ah_state = ah_state;
726 switch (ah_state) {
727 case AH_STATE_VISIBLE:
a7bd16a4 728 gtk_widget_show(GTK_WIDGET(panel));
9a2c13d0 729 gtk_widget_show(p->box);
a7bd16a4 730 gtk_window_stick(GTK_WINDOW(panel));
9a2c13d0
AG
731 p->visible = TRUE;
732 break;
733 case AH_STATE_WAITING:
a7bd16a4 734 p->hide_timeout = g_timeout_add(2 * PERIOD, ah_state_hide_timeout, panel);
9a2c13d0
AG
735 break;
736 case AH_STATE_HIDDEN:
737 if (p->height_when_hidden > 0)
738 gtk_widget_hide(p->box);
739 else
a7bd16a4 740 gtk_widget_hide(GTK_WIDGET(panel));
9a2c13d0
AG
741 p->visible = FALSE;
742 }
743 } else if (p->autohide && p->ah_far) {
e1fd11ee
AG
744 switch (ah_state) {
745 case AH_STATE_VISIBLE:
a7bd16a4 746 ah_state_set(panel, AH_STATE_WAITING);
e1fd11ee
AG
747 break;
748 case AH_STATE_WAITING:
749 break;
750 case AH_STATE_HIDDEN:
751 /* configurator might change height_when_hidden value */
752 if (p->height_when_hidden > 0)
753 {
754 if (gtk_widget_get_visible(p->box))
755 {
756 gtk_widget_hide(p->box);
a7bd16a4 757 gtk_widget_show(GTK_WIDGET(panel));
e1fd11ee
AG
758 }
759 }
760 else
a7bd16a4 761 if (gtk_widget_get_visible(GTK_WIDGET(panel)))
e1fd11ee 762 {
a7bd16a4 763 gtk_widget_hide(GTK_WIDGET(panel));
e1fd11ee
AG
764 gtk_widget_show(p->box);
765 }
766 }
9a2c13d0
AG
767 } else {
768 switch (ah_state) {
769 case AH_STATE_VISIBLE:
770 break;
771 case AH_STATE_WAITING:
772 if (p->hide_timeout)
773 g_source_remove(p->hide_timeout);
774 p->hide_timeout = 0;
775 /* continue with setting visible */
776 case AH_STATE_HIDDEN:
a7bd16a4 777 ah_state_set(panel, AH_STATE_VISIBLE);
9a2c13d0
AG
778 }
779 }
780 RET();
781}
782
783/* starts autohide behaviour */
a7bd16a4 784static void ah_start(LXPanel *p)
9a2c13d0
AG
785{
786 ENTER;
a7bd16a4
AG
787 if (!p->priv->mouse_timeout)
788 p->priv->mouse_timeout = g_timeout_add(PERIOD, (GSourceFunc) mouse_watch, p);
9a2c13d0
AG
789 RET();
790}
791
792/* stops autohide */
a7bd16a4 793static void ah_stop(LXPanel *p)
9a2c13d0
AG
794{
795 ENTER;
a7bd16a4
AG
796 if (p->priv->mouse_timeout) {
797 g_source_remove(p->priv->mouse_timeout);
798 p->priv->mouse_timeout = 0;
9a2c13d0 799 }
a7bd16a4
AG
800 if (p->priv->hide_timeout) {
801 g_source_remove(p->priv->hide_timeout);
802 p->priv->hide_timeout = 0;
9a2c13d0
AG
803 }
804 RET();
805}
806
424b1f06 807static gboolean lxpanel_map_event(GtkWidget *widget, GdkEventAny *event)
9a2c13d0 808{
424b1f06
AG
809 Panel *p = PLUGIN_PANEL(widget)->priv;
810
9a2c13d0 811 if (p->autohide)
a7bd16a4 812 ah_start(LXPANEL(widget));
424b1f06 813 return GTK_WIDGET_CLASS(lxpanel_parent_class)->map_event(widget, event);
9a2c13d0
AG
814}
815/* end of the autohide code
816 * ------------------------------------------------------------- */
817
fddae119
FC
818static gint
819panel_popupmenu_configure(GtkWidget *widget, gpointer user_data)
820{
a7bd16a4 821 panel_configure( (LXPanel*)user_data, 0 );
4b93d81e 822 return TRUE;
fddae119
FC
823}
824
9dd114c4 825/* Handler for "button_press_event" signal with Panel as parameter. */
424b1f06 826static gboolean lxpanel_button_press(GtkWidget *widget, GdkEventButton *event)
fddae119 827{
0bcf9045 828 if (event->button == 3) /* right button */
9dd114c4 829 {
424b1f06 830 GtkMenu* popup = (GtkMenu*) lxpanel_get_plugin_menu(LXPANEL(widget), NULL, FALSE);
9dd114c4 831 gtk_menu_popup(popup, NULL, NULL, NULL, NULL, event->button, event->time);
832 return TRUE;
0bcf9045 833 }
424b1f06 834 return GTK_WIDGET_CLASS(lxpanel_parent_class)->button_press_event(widget, event);
fddae119
FC
835}
836
17fab6e5 837static void panel_popupmenu_config_plugin( GtkMenuItem* item, GtkWidget* plugin )
cf701cb7 838{
a7bd16a4 839 Panel *panel = PLUGIN_PANEL(plugin)->priv;
17fab6e5 840
2f1173de 841 lxpanel_plugin_show_config_dialog(plugin);
930af9fd
HJYP
842
843 /* FIXME: this should be more elegant */
17fab6e5 844 panel->config_changed = TRUE;
cf701cb7
HJYP
845}
846
a7bd16a4 847static void panel_popupmenu_add_item( GtkMenuItem* item, LXPanel* panel )
4b93d81e
HJYP
848{
849 /* panel_add_plugin( panel, panel->topgwin ); */
8d8b66da 850 panel_configure( panel, 2 );
cf701cb7
HJYP
851}
852
17fab6e5 853static void panel_popupmenu_remove_item( GtkMenuItem* item, GtkWidget* plugin )
cf701cb7 854{
a7bd16a4 855 Panel* panel = PLUGIN_PANEL(plugin)->priv;
b7e8ad02
MJ
856
857 /* If the configuration dialog is open, there will certainly be a crash if the
858 * user manipulates the Configured Plugins list, after we remove this entry.
859 * Close the configuration dialog if it is open. */
860 if (panel->pref_dialog != NULL)
861 {
862 gtk_widget_destroy(panel->pref_dialog);
863 panel->pref_dialog = NULL;
864 }
17fab6e5
AG
865 config_setting_destroy(g_object_get_qdata(G_OBJECT(plugin), lxpanel_plugin_qconf));
866 /* reset conf pointer because the widget still may be referenced by configurator */
867 g_object_set_qdata(G_OBJECT(plugin), lxpanel_plugin_qconf, NULL);
b7e8ad02 868
a7bd16a4 869 lxpanel_config_save(PLUGIN_PANEL(plugin));
17fab6e5 870 gtk_widget_destroy(plugin);
cf701cb7
HJYP
871}
872
3e7b8eb7
HJYP
873/* FIXME: Potentially we can support multiple panels at the same edge,
874 * but currently this cannot be done due to some positioning problems. */
64afc832 875static char* gen_panel_name( int edge, gint monitor )
3e7b8eb7
HJYP
876{
877 const char* edge_str = num2str( edge_pair, edge, "" );
878 char* name = NULL;
1f4bc3aa 879 char* dir = _user_config_file_name("panels", NULL);
3e7b8eb7
HJYP
880 int i;
881 for( i = 0; i < G_MAXINT; ++i )
882 {
883 char* f;
64afc832
R
884 if(monitor != 0)
885 name = g_strdup_printf( "%s-m%d-%d", edge_str, monitor, i );
886 else if( G_LIKELY( i > 0 ) )
3e7b8eb7
HJYP
887 name = g_strdup_printf( "%s%d", edge_str, i );
888 else
889 name = g_strdup( edge_str );
64afc832 890
3e7b8eb7
HJYP
891 f = g_build_filename( dir, name, NULL );
892 if( ! g_file_test( f, G_FILE_TEST_EXISTS ) )
893 {
894 g_free( f );
895 break;
896 }
897 g_free( name );
898 g_free( f );
899 }
900 g_free( dir );
901 return name;
902}
903
904/* FIXME: Potentially we can support multiple panels at the same edge,
905 * but currently this cannot be done due to some positioning problems. */
a7bd16a4 906static void panel_popupmenu_create_panel( GtkMenuItem* item, LXPanel* panel )
cf701cb7 907{
64afc832
R
908 gint m, e, monitors;
909 GdkScreen *screen;
a7bd16a4
AG
910 LXPanel *new_panel = panel_allocate();
911 Panel *p = new_panel->priv;
9dd114c4 912
913 /* Allocate the edge. */
64afc832
R
914 screen = gdk_screen_get_default();
915 g_assert(screen);
916 monitors = gdk_screen_get_n_monitors(screen);
917 for(m=0; m<monitors; ++m)
918 {
919 /* try each of the four edges */
920 for(e=1; e<5; ++e)
921 {
a7bd16a4
AG
922 if(panel_edge_available(p,e,m)) {
923 p->edge = e;
924 p->monitor = m;
64afc832
R
925 goto found_edge;
926 }
927 }
928 }
929
a7bd16a4 930 gtk_widget_destroy(GTK_WIDGET(new_panel));
64afc832 931 ERR("Error adding panel: There is no room for another panel. All the edges are taken.\n");
bb4cf963 932 fm_show_error(NULL, NULL, _("There is no room for another panel. All the edges are taken."));
64afc832
R
933 return;
934
935found_edge:
a7bd16a4 936 p->name = gen_panel_name(p->edge, p->monitor);
9dd114c4 937
17fab6e5 938 /* create new config with first group "Global" */
a7bd16a4 939 config_group_add_subgroup(config_root_setting(p->config), "Global");
9dd114c4 940 panel_configure(new_panel, 0);
a7bd16a4 941 panel_normalize_configuration(p);
9dd114c4 942 panel_start_gui(new_panel);
a7bd16a4 943 gtk_widget_show_all(GTK_WIDGET(new_panel));
9dd114c4 944
a7bd16a4 945 lxpanel_config_save(new_panel);
9dd114c4 946 all_panels = g_slist_prepend(all_panels, new_panel);
cf701cb7
HJYP
947}
948
a7bd16a4 949static void panel_popupmenu_delete_panel( GtkMenuItem* item, LXPanel* panel )
cf701cb7
HJYP
950{
951 GtkWidget* dlg;
952 gboolean ok;
a7bd16a4 953 dlg = gtk_message_dialog_new_with_markup( GTK_WINDOW(panel),
cf701cb7
HJYP
954 GTK_DIALOG_MODAL,
955 GTK_MESSAGE_QUESTION,
956 GTK_BUTTONS_OK_CANCEL,
957 _("Really delete this panel?\n<b>Warning: This can not be recovered.</b>") );
d1d43629 958 panel_apply_icon(GTK_WINDOW(dlg));
cf701cb7
HJYP
959 gtk_window_set_title( (GtkWindow*)dlg, _("Confirm") );
960 ok = ( gtk_dialog_run( (GtkDialog*)dlg ) == GTK_RESPONSE_OK );
961 gtk_widget_destroy( dlg );
962 if( ok )
963 {
1f4bc3aa 964 gchar *fname;
cf701cb7 965 all_panels = g_slist_remove( all_panels, panel );
4b93d81e
HJYP
966
967 /* delete the config file of this panel */
a7bd16a4 968 fname = _user_config_file_name("panels", panel->priv->name);
4b93d81e 969 g_unlink( fname );
1f4bc3aa 970 g_free(fname);
a7bd16a4
AG
971 panel->priv->config_changed = 0;
972 gtk_widget_destroy(GTK_WIDGET(panel));
cf701cb7
HJYP
973 }
974}
975
e7a42ecf 976static void panel_popupmenu_about( GtkMenuItem* item, Panel* panel )
977{
978 GtkWidget *about;
979 const gchar* authors[] = {
980 "Hong Jen Yee (PCMan) <pcman.tw@gmail.com>",
981 "Jim Huang <jserv.tw@gmail.com>",
982 "Greg McNew <gmcnew@gmail.com> (battery plugin)",
983 "Fred Chien <cfsghost@gmail.com>",
984 "Daniel Kesler <kesler.daniel@gmail.com>",
985 "Juergen Hoetzel <juergen@archlinux.org>",
2918994e 986 "Marty Jack <martyj19@comcast.net>",
815e1027 987 "Martin Bagge <brother@bsnet.se>",
bb4cf963
AG
988 "Andriy Grytsenko <andrej@rep.kiev.ua>",
989 "Giuseppe Penone <giuspen@gmail.com>",
990 "Piotr Sipika <piotr.sipika@gmail.com>",
e7a42ecf 991 NULL
992 };
993 /* TRANSLATORS: Replace this string with your names, one name per line. */
994 gchar *translators = _( "translator-credits" );
995
996 about = gtk_about_dialog_new();
997 panel_apply_icon(GTK_WINDOW(about));
998 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about), VERSION);
09fa171b 999 gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about), _("LXPanel"));
4c91be3e
JL
1000
1001 if(gtk_icon_theme_has_icon(panel->icon_theme, "video-display"))
1002 {
1003 gtk_about_dialog_set_logo( GTK_ABOUT_DIALOG(about),
1004 gtk_icon_theme_load_icon(panel->icon_theme, "video-display", 48, 0, NULL));
1005 }
1006 else if (gtk_icon_theme_has_icon(panel->icon_theme, "start-here"))
1007 {
1008 gtk_about_dialog_set_logo( GTK_ABOUT_DIALOG(about),
1009 gtk_icon_theme_load_icon(panel->icon_theme, "start-here", 48, 0, NULL));
1010 }
0bcf9045 1011 else
4c91be3e
JL
1012 {
1013 gtk_about_dialog_set_logo( GTK_ABOUT_DIALOG(about),
0b806437 1014 gdk_pixbuf_new_from_file(PACKAGE_DATA_DIR "/images/my-computer.png", NULL));
4c91be3e
JL
1015 }
1016
b840f7cc 1017 gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(about), _("Copyright (C) 2008-2014"));
e7a42ecf 1018 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about), _( "Desktop panel for LXDE project"));
1019 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.");
1020 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about), "http://lxde.org/");
1021 gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(about), authors);
1022 gtk_about_dialog_set_translator_credits(GTK_ABOUT_DIALOG(about), translators);
1023 gtk_dialog_run(GTK_DIALOG(about));
0bcf9045 1024 gtk_widget_destroy(about);
e7a42ecf 1025}
1026
1027void panel_apply_icon( GtkWindow *w )
1028{
0bcf9045
AG
1029 GdkPixbuf* window_icon;
1030
1031 if(gtk_icon_theme_has_icon(gtk_icon_theme_get_default(), "video-display"))
4c91be3e 1032 {
0bcf9045
AG
1033 window_icon = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "video-display", 24, 0, NULL);
1034 }
1035 else if(gtk_icon_theme_has_icon(gtk_icon_theme_get_default(), "start-here"))
4c91be3e 1036 {
0bcf9045
AG
1037 window_icon = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "start-here", 24, 0, NULL);
1038 }
89496346 1039 else
4c91be3e 1040 {
0bcf9045
AG
1041 window_icon = gdk_pixbuf_new_from_file(PACKAGE_DATA_DIR "/images/my-computer.png", NULL);
1042 }
21f91705 1043 gtk_window_set_icon(w, window_icon);
e7a42ecf 1044}
1045
a7bd16a4 1046GtkMenu* lxpanel_get_plugin_menu( LXPanel* panel, GtkWidget* plugin, gboolean use_sub_menu )
cf701cb7 1047{
e3b89f43 1048 GtkWidget *menu_item, *img;
1049 GtkMenu *ret,*menu;
191694fb 1050 const LXPanelPluginInit *init;
cf701cb7 1051 char* tmp;
eb1762e5 1052
e3b89f43 1053 ret = menu = GTK_MENU(gtk_menu_new());
cf701cb7 1054
eb1762e5
AG
1055 if (plugin)
1056 {
1057 init = PLUGIN_CLASS(plugin);
1058 /* create single item - plugin instance settings */
1059 img = gtk_image_new_from_stock( GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU );
1060 tmp = g_strdup_printf( _("\"%s\" Settings"), _(init->name) );
1061 menu_item = gtk_image_menu_item_new_with_label( tmp );
1062 g_free( tmp );
1063 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1064 gtk_menu_shell_prepend(GTK_MENU_SHELL(ret), menu_item);
1065 if( init->config )
1066 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_config_plugin), plugin );
1067 else
1068 gtk_widget_set_sensitive( menu_item, FALSE );
1069 /* add custom items by plugin if requested */
1070 if (init->update_context_menu != NULL)
1071 use_sub_menu = init->update_context_menu(plugin, ret);
1072 /* append a separator */
1073 menu_item = gtk_separator_menu_item_new();
1074 gtk_menu_shell_append(GTK_MENU_SHELL(ret), menu_item);
1075 }
1076 if (use_sub_menu)
1077 menu = GTK_MENU(gtk_menu_new());
1078
4b93d81e
HJYP
1079 img = gtk_image_new_from_stock( GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU );
1080 menu_item = gtk_image_menu_item_new_with_label(_("Add / Remove Panel Items"));
1081 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1082 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1083 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_add_item), panel );
cf701cb7
HJYP
1084
1085 if( plugin )
1086 {
1087 img = gtk_image_new_from_stock( GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU );
17fab6e5 1088 tmp = g_strdup_printf( _("Remove \"%s\" From Panel"), _(init->name) );
cf701cb7
HJYP
1089 menu_item = gtk_image_menu_item_new_with_label( tmp );
1090 g_free( tmp );
1091 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1092 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1093 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_remove_item), plugin );
1094 }
1095
1096 menu_item = gtk_separator_menu_item_new();
1097 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1098
4b93d81e
HJYP
1099 img = gtk_image_new_from_stock( GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU );
1100 menu_item = gtk_image_menu_item_new_with_label(_("Panel Settings"));
1101 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1102 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1103 g_signal_connect(G_OBJECT(menu_item), "activate", G_CALLBACK(panel_popupmenu_configure), panel );
1104
cf701cb7
HJYP
1105 img = gtk_image_new_from_stock( GTK_STOCK_NEW, GTK_ICON_SIZE_MENU );
1106 menu_item = gtk_image_menu_item_new_with_label(_("Create New Panel"));
1107 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1108 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1109 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_create_panel), panel );
1110
1111 img = gtk_image_new_from_stock( GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU );
1112 menu_item = gtk_image_menu_item_new_with_label(_("Delete This Panel"));
1113 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1114 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1115 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_delete_panel), panel );
1116 if( ! all_panels->next ) /* if this is the only panel */
1117 gtk_widget_set_sensitive( menu_item, FALSE );
1118
e7a42ecf 1119 menu_item = gtk_separator_menu_item_new();
1120 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1121
1122 img = gtk_image_new_from_stock( GTK_STOCK_ABOUT, GTK_ICON_SIZE_MENU );
1123 menu_item = gtk_image_menu_item_new_with_label(_("About"));
1124 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1125 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
a7bd16a4 1126 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_about), panel->priv );
e7a42ecf 1127
cf701cb7
HJYP
1128 if( use_sub_menu )
1129 {
cf701cb7
HJYP
1130 menu_item = gtk_image_menu_item_new_with_label(_("Panel"));
1131 gtk_menu_shell_append(GTK_MENU_SHELL(ret), menu_item);
e3b89f43 1132 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), GTK_WIDGET(menu) );
3e7b8eb7
HJYP
1133 }
1134
eb1762e5 1135 gtk_widget_show_all(GTK_WIDGET(ret));
3e7b8eb7 1136
cf701cb7
HJYP
1137 g_signal_connect( ret, "selection-done", G_CALLBACK(gtk_widget_destroy), NULL );
1138 return ret;
1139}
1140
17fab6e5
AG
1141/* for old plugins compatibility */
1142GtkMenu* lxpanel_get_panel_menu( Panel* panel, Plugin* plugin, gboolean use_sub_menu )
1143{
a7bd16a4 1144 return lxpanel_get_plugin_menu(panel->topgwin, plugin->pwid, use_sub_menu);
17fab6e5
AG
1145}
1146
a52c2257
HJYP
1147/****************************************************
1148 * panel creation *
1149 ****************************************************/
176fb687 1150
a52c2257 1151static void
22242ed4 1152make_round_corners(Panel *p)
a52c2257 1153{
a97d06a6 1154 /* FIXME: This should be re-written with shape extension of X11 */
4542c20d 1155 /* gdk_window_shape_combine_mask() can be used */
bee4c26e
HJYP
1156}
1157
22242ed4 1158void panel_set_dock_type(Panel *p)
bee4c26e 1159{
09fa171b
AG
1160 Display *xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
1161
bee4c26e
HJYP
1162 if (p->setdocktype) {
1163 Atom state = a_NET_WM_WINDOW_TYPE_DOCK;
09fa171b 1164 XChangeProperty(xdisplay, p->topxwin,
bee4c26e
HJYP
1165 a_NET_WM_WINDOW_TYPE, XA_ATOM, 32,
1166 PropModeReplace, (unsigned char *) &state, 1);
1167 }
1168 else {
09fa171b 1169 XDeleteProperty( xdisplay, p->topxwin, a_NET_WM_WINDOW_TYPE );
bee4c26e 1170 }
a52c2257
HJYP
1171}
1172
176fb687 1173void panel_establish_autohide(Panel *p)
1174{
a7bd16a4
AG
1175 _panel_establish_autohide(p->topgwin);
1176}
1177
1178void _panel_establish_autohide(LXPanel *p)
1179{
1180 if (p->priv->autohide)
9a2c13d0
AG
1181 ah_start(p);
1182 else
176fb687 1183 {
9a2c13d0
AG
1184 ah_stop(p);
1185 ah_state_set(p, AH_STATE_VISIBLE);
176fb687 1186 }
1187}
1188
8f9e6256 1189/* Set an image from a file with scaling to the panel icon size. */
fcb35553 1190void panel_image_set_from_file(Panel * p, GtkWidget * image, const char * file)
8f9e6256 1191{
1192 GdkPixbuf * pixbuf = gdk_pixbuf_new_from_file_at_scale(file, p->icon_size, p->icon_size, TRUE, NULL);
1193 if (pixbuf != NULL)
1194 {
1195 gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf);
1196 g_object_unref(pixbuf);
1197 }
1198}
1199
a7bd16a4
AG
1200void lxpanel_image_set_from_file(LXPanel * p, GtkWidget * image, const char * file)
1201{
1202 panel_image_set_from_file(p->priv, image, file);
1203}
1204
c14620f2
MJ
1205/* Set an image from a icon theme with scaling to the panel icon size. */
1206gboolean panel_image_set_icon_theme(Panel * p, GtkWidget * image, const gchar * icon)
1207{
1b532bce 1208 if (gtk_icon_theme_has_icon(p->icon_theme, icon))
c14620f2 1209 {
1b532bce 1210 GdkPixbuf * pixbuf = gtk_icon_theme_load_icon(p->icon_theme, icon, p->icon_size, 0, NULL);
c14620f2
MJ
1211 gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf);
1212 g_object_unref(pixbuf);
1213 return TRUE;
1214 }
1215 return FALSE;
1216}
1217
a7bd16a4
AG
1218gboolean lxpanel_image_set_icon_theme(LXPanel * p, GtkWidget * image, const gchar * icon)
1219{
1220 return panel_image_set_icon_theme(p->priv, image, icon);
1221}
1222
0defe4b9 1223static void
a7bd16a4 1224panel_start_gui(LXPanel *panel)
a52c2257
HJYP
1225{
1226 Atom state[3];
1227 XWMHints wmhints;
1228 guint32 val;
09fa171b 1229 Display *xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
a7bd16a4
AG
1230 Panel *p = panel->priv;
1231 GtkWidget *w = GTK_WIDGET(panel);
6db11841 1232
a52c2257
HJYP
1233 ENTER;
1234
9dd114c4 1235 p->curdesk = get_net_current_desktop();
1236 p->desknum = get_net_number_of_desktops();
1237 p->workarea = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_WORKAREA, XA_CARDINAL, &p->wa_len);
1238
cb22c87c
HJYP
1239 /* main toplevel window */
1240 /* p->topgwin = gtk_window_new(GTK_WINDOW_TOPLEVEL); */
a7bd16a4 1241 gtk_widget_set_name(w, "PanelToplevel");
176fb687 1242 p->display = gdk_display_get_default();
a7bd16a4
AG
1243 gtk_container_set_border_width(GTK_CONTAINER(panel), 0);
1244 gtk_window_set_resizable(GTK_WINDOW(panel), FALSE);
1245 gtk_window_set_wmclass(GTK_WINDOW(panel), "panel", "lxpanel");
1246 gtk_window_set_title(GTK_WINDOW(panel), "panel");
1247 gtk_window_set_position(GTK_WINDOW(panel), GTK_WIN_POS_NONE);
1248 gtk_window_set_decorated(GTK_WINDOW(panel), FALSE);
77886b88 1249
a7bd16a4 1250 gtk_window_group_add_window( win_grp, (GtkWindow*)panel );
e2957bd2 1251
a7bd16a4 1252 gtk_widget_add_events( w, GDK_BUTTON_PRESS_MASK );
f2d54481 1253
a7bd16a4 1254 gtk_widget_realize(w);
175f73d1 1255 //gdk_window_set_decorations(gtk_widget_get_window(p->topgwin), 0);
2de71c90 1256
4542c20d 1257 // main layout manager as a single child of panel
a7bd16a4 1258 p->box = panel_box_new(panel, FALSE, 0);
a52c2257 1259 gtk_container_set_border_width(GTK_CONTAINER(p->box), 0);
a7bd16a4 1260 gtk_container_add(GTK_CONTAINER(panel), p->box);
a52c2257 1261 gtk_widget_show(p->box);
a97d06a6
HJYP
1262 if (p->round_corners)
1263 make_round_corners(p);
6db11841 1264
a7bd16a4 1265 p->topxwin = GDK_WINDOW_XWINDOW(gtk_widget_get_window(w));
a52c2257
HJYP
1266 DBG("topxwin = %x\n", p->topxwin);
1267
1268 /* the settings that should be done before window is mapped */
1269 wmhints.flags = InputHint;
1270 wmhints.input = 0;
09fa171b 1271 XSetWMHints (xdisplay, p->topxwin, &wmhints);
24053345 1272#define WIN_HINTS_SKIP_FOCUS (1<<0) /* "alt-tab" skips this win */
a52c2257 1273 val = WIN_HINTS_SKIP_FOCUS;
09fa171b
AG
1274 XChangeProperty(xdisplay, p->topxwin,
1275 XInternAtom(xdisplay, "_WIN_HINTS", False), XA_CARDINAL, 32,
a52c2257
HJYP
1276 PropModeReplace, (unsigned char *) &val, 1);
1277
bee4c26e 1278 panel_set_dock_type(p);
a52c2257
HJYP
1279
1280 /* window mapping point */
a7bd16a4 1281 gtk_widget_show_all(w);
a52c2257
HJYP
1282
1283 /* the settings that should be done after window is mapped */
a7bd16a4 1284 _panel_establish_autohide(panel);
a52c2257
HJYP
1285
1286 /* send it to running wm */
1287 Xclimsg(p->topxwin, a_NET_WM_DESKTOP, 0xFFFFFFFF, 0, 0, 0, 0);
1288 /* and assign it ourself just for case when wm is not running */
1289 val = 0xFFFFFFFF;
09fa171b 1290 XChangeProperty(xdisplay, p->topxwin, a_NET_WM_DESKTOP, XA_CARDINAL, 32,
a52c2257
HJYP
1291 PropModeReplace, (unsigned char *) &val, 1);
1292
1293 state[0] = a_NET_WM_STATE_SKIP_PAGER;
1294 state[1] = a_NET_WM_STATE_SKIP_TASKBAR;
1295 state[2] = a_NET_WM_STATE_STICKY;
09fa171b 1296 XChangeProperty(xdisplay, p->topxwin, a_NET_WM_STATE, XA_ATOM,
a52c2257
HJYP
1297 32, PropModeReplace, (unsigned char *) state, 3);
1298
a7bd16a4
AG
1299 _calculate_position(panel);
1300 gdk_window_move_resize(gtk_widget_get_window(w), p->ax, p->ay, p->aw, p->ah);
1301 _panel_set_wm_strut(panel);
5eaabb1b 1302 p->initialized = TRUE;
77886b88 1303
a52c2257
HJYP
1304 RET();
1305}
1306
2918994e 1307/* Exchange the "width" and "height" terminology for vertical and horizontal panels. */
1308void panel_adjust_geometry_terminology(Panel * p)
9dd114c4 1309{
8ed3b3d4 1310 if ((p->height_label != NULL) && (p->width_label != NULL)
1311 && (p->alignment_left_label != NULL) && (p->alignment_right_label != NULL))
9dd114c4 1312 {
1313 if ((p->edge == EDGE_TOP) || (p->edge == EDGE_BOTTOM))
1314 {
1315 gtk_label_set_text(GTK_LABEL(p->height_label), _("Height:"));
1316 gtk_label_set_text(GTK_LABEL(p->width_label), _("Width:"));
2918994e 1317 gtk_button_set_label(GTK_BUTTON(p->alignment_left_label), _("Left"));
1318 gtk_button_set_label(GTK_BUTTON(p->alignment_right_label), _("Right"));
9dd114c4 1319 }
1320 else
1321 {
1322 gtk_label_set_text(GTK_LABEL(p->height_label), _("Width:"));
1323 gtk_label_set_text(GTK_LABEL(p->width_label), _("Height:"));
2918994e 1324 gtk_button_set_label(GTK_BUTTON(p->alignment_left_label), _("Top"));
1325 gtk_button_set_label(GTK_BUTTON(p->alignment_right_label), _("Bottom"));
9dd114c4 1326 }
1327 }
1328}
1329
2918994e 1330/* Draw text into a label, with the user preference color and optionally bold. */
38ac664c
AG
1331void panel_draw_label_text(Panel * p, GtkWidget * label, const char * text,
1332 gboolean bold, float custom_size_factor,
1333 gboolean custom_color)
2918994e 1334{
2918994e 1335 if (text == NULL)
1336 {
1337 /* Null string. */
1338 gtk_label_set_text(GTK_LABEL(label), NULL);
83d410c1 1339 return;
2918994e 1340 }
1341
83d410c1
HG
1342 /* Compute an appropriate size so the font will scale with the panel's icon size. */
1343 int font_desc;
1344 if (p->usefontsize)
1345 font_desc = p->fontsize;
f669ce5e 1346 else
2918994e 1347 {
83d410c1
HG
1348 if (p->icon_size < 20)
1349 font_desc = 9;
1350 else if (p->icon_size >= 20 && p->icon_size < 36)
1351 font_desc = 10;
1352 else
1353 font_desc = 12;
1354 }
1355 font_desc *= custom_size_factor;
1356
1357 /* Check the string for characters that need to be escaped.
1358 * If any are found, create the properly escaped string and use it instead. */
38ac664c 1359 const char * valid_markup = text;
83d410c1 1360 char * escaped_text = NULL;
38ac664c 1361 const char * q;
83d410c1
HG
1362 for (q = text; *q != '\0'; q += 1)
1363 {
1364 if ((*q == '<') || (*q == '>') || (*q == '&'))
2918994e 1365 {
83d410c1
HG
1366 escaped_text = g_markup_escape_text(text, -1);
1367 valid_markup = escaped_text;
1368 break;
2918994e 1369 }
83d410c1 1370 }
2918994e 1371
83d410c1
HG
1372 gchar * formatted_text;
1373 if ((custom_color) && (p->usefontcolor))
1374 {
1375 /* Color, optionally bold. */
1376 formatted_text = g_strdup_printf("<span font_desc=\"%d\" color=\"#%06x\">%s%s%s</span>",
f669ce5e 1377 font_desc,
2918994e 1378 gcolor2rgb24(&p->gfontcolor),
1379 ((bold) ? "<b>" : ""),
1380 valid_markup,
1381 ((bold) ? "</b>" : ""));
83d410c1
HG
1382 }
1383 else
1384 {
1385 /* No color, optionally bold. */
1386 formatted_text = g_strdup_printf("<span font_desc=\"%d\">%s%s%s</span>",
f669ce5e 1387 font_desc,
1388 ((bold) ? "<b>" : ""),
1389 valid_markup,
1390 ((bold) ? "</b>" : ""));
2918994e 1391 }
83d410c1
HG
1392
1393 gtk_label_set_markup(GTK_LABEL(label), formatted_text);
1394 g_free(formatted_text);
1395 g_free(escaped_text);
2918994e 1396}
1397
a7bd16a4
AG
1398void lxpanel_draw_label_text(LXPanel * p, GtkWidget * label, const char * text,
1399 gboolean bold, float custom_size_factor,
1400 gboolean custom_color)
1401{
1402 panel_draw_label_text(p->priv, label, text, bold, custom_size_factor, custom_color);
1403}
1404
2918994e 1405void panel_set_panel_configuration_changed(Panel *p)
a97d06a6 1406{
a7bd16a4
AG
1407 _panel_set_panel_configuration_changed(p->topgwin);
1408}
1409
1410void _panel_set_panel_configuration_changed(LXPanel *panel)
1411{
1412 Panel *p = panel->priv;
17fab6e5 1413 GList *plugins, *l;
9dd114c4 1414
cfde283a 1415 GtkOrientation previous_orientation = p->orientation;
a97d06a6 1416 p->orientation = (p->edge == EDGE_TOP || p->edge == EDGE_BOTTOM)
cfde283a 1417 ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
9dd114c4 1418
cfde283a 1419 /* either first run or orientation was changed */
5eaabb1b 1420 if (!p->initialized || previous_orientation != p->orientation)
9dd114c4 1421 {
1422 panel_adjust_geometry_terminology(p);
5eaabb1b 1423 if (p->initialized)
f5c7784a 1424 p->height = ((p->orientation == GTK_ORIENTATION_HORIZONTAL) ? PANEL_HEIGHT_DEFAULT : PANEL_WIDTH_DEFAULT);
9dd114c4 1425 if (p->height_control != NULL)
1426 gtk_spin_button_set_value(GTK_SPIN_BUTTON(p->height_control), p->height);
2918994e 1427 if ((p->widthtype == WIDTH_PIXEL) && (p->width_control != NULL))
1428 {
cfde283a 1429 int value = ((p->orientation == GTK_ORIENTATION_HORIZONTAL) ? gdk_screen_width() : gdk_screen_height());
2918994e 1430 gtk_spin_button_set_range(GTK_SPIN_BUTTON(p->width_control), 0, value);
1431 gtk_spin_button_set_value(GTK_SPIN_BUTTON(p->width_control), value);
1432 }
9dd114c4 1433 }
1434
5eaabb1b 1435 /* FIXME: it's deprecated, kept for binary compatibility */
cfde283a 1436 if (p->orientation == GTK_ORIENTATION_HORIZONTAL) {
a97d06a6
HJYP
1437 p->my_box_new = gtk_hbox_new;
1438 p->my_separator_new = gtk_vseparator_new;
1439 } else {
1440 p->my_box_new = gtk_vbox_new;
1441 p->my_separator_new = gtk_hseparator_new;
1442 }
1443
1444 /* recreate the main layout box */
964b8b7e 1445 if (p->box != NULL)
1446 {
cfde283a 1447 gtk_orientable_set_orientation(GTK_ORIENTABLE(p->box), p->orientation);
a97d06a6 1448 }
9dd114c4 1449
a97d06a6
HJYP
1450 /* NOTE: This loop won't be executed when panel started since
1451 plugins are not loaded at that time.
1452 This is used when the orientation of the panel is changed
1453 from the config dialog, and plugins should be re-layout.
1454 */
17fab6e5
AG
1455 plugins = p->box ? gtk_container_get_children(GTK_CONTAINER(p->box)) : NULL;
1456 for( l = plugins; l; l = l->next ) {
1457 GtkWidget *w = (GtkWidget*)l->data;
191694fb 1458 const LXPanelPluginInit *init = PLUGIN_CLASS(w);
17fab6e5 1459 if (init->reconfigure)
a7bd16a4 1460 init->reconfigure(panel, w);
a97d06a6 1461 }
17fab6e5 1462 g_list_free(plugins);
3154b4ef
AG
1463 /* panel geometry changed? update panel background then */
1464 g_idle_add_full( G_PRIORITY_LOW,
a7bd16a4 1465 (GSourceFunc)delay_update_background, panel, NULL );
a97d06a6
HJYP
1466}
1467
a52c2257 1468static int
17fab6e5 1469panel_parse_global(Panel *p, config_setting_t *cfg)
a52c2257 1470{
4bca3e51
AG
1471 const char *str;
1472 gint i;
6db11841 1473
17fab6e5
AG
1474 /* check Global config */
1475 if (!cfg || strcmp(config_setting_get_name(cfg), "Global") != 0)
3e7b8eb7 1476 {
17fab6e5
AG
1477 ERR( "lxpanel: Global section not found\n");
1478 RET(0);
1479 }
4bca3e51
AG
1480 if (config_setting_lookup_string(cfg, "edge", &str))
1481 p->edge = str2num(edge_pair, str, EDGE_NONE);
1482 if (config_setting_lookup_string(cfg, "allign", &str))
1483 p->allign = str2num(allign_pair, str, ALLIGN_NONE);
1484 config_setting_lookup_int(cfg, "monitor", &p->monitor);
1485 config_setting_lookup_int(cfg, "margin", &p->margin);
1486 if (config_setting_lookup_string(cfg, "widthtype", &str))
1487 p->widthtype = str2num(width_pair, str, WIDTH_NONE);
1488 config_setting_lookup_int(cfg, "width", &p->width);
1489 if (config_setting_lookup_string(cfg, "heighttype", &str))
1490 p->heighttype = str2num(height_pair, str, HEIGHT_NONE);
1491 config_setting_lookup_int(cfg, "height", &p->height);
1492 if (config_setting_lookup_int(cfg, "spacing", &i) && i > 0)
1493 p->spacing = i;
1494 if (config_setting_lookup_int(cfg, "setdocktype", &i))
1495 p->setdocktype = i != 0;
1496 if (config_setting_lookup_int(cfg, "setpartialstrut", &i))
1497 p->setstrut = i != 0;
1498 if (config_setting_lookup_int(cfg, "RoundCorners", &i))
1499 p->round_corners = i != 0;
1500 if (config_setting_lookup_int(cfg, "transparent", &i))
1501 p->transparent = i != 0;
1502 if (config_setting_lookup_int(cfg, "alpha", &p->alpha))
17fab6e5 1503 {
17fab6e5
AG
1504 if (p->alpha > 255)
1505 p->alpha = 255;
1506 }
4bca3e51
AG
1507 if (config_setting_lookup_int(cfg, "autohide", &i))
1508 p->autohide = i != 0;
9a2c13d0
AG
1509 if (config_setting_lookup_int(cfg, "heightwhenhidden", &i))
1510 p->height_when_hidden = MAX(0, i);
4bca3e51 1511 if (config_setting_lookup_string(cfg, "tintcolor", &str))
17fab6e5 1512 {
4bca3e51 1513 if (!gdk_color_parse (str, &p->gtintcolor))
17fab6e5
AG
1514 gdk_color_parse ("white", &p->gtintcolor);
1515 p->tintcolor = gcolor2rgb24(&p->gtintcolor);
1516 DBG("tintcolor=%x\n", p->tintcolor);
1517 }
4bca3e51
AG
1518 if (config_setting_lookup_int(cfg, "usefontcolor", &i))
1519 p->usefontcolor = i != 0;
1520 if (config_setting_lookup_string(cfg, "fontcolor", &str))
17fab6e5 1521 {
4bca3e51 1522 if (!gdk_color_parse (str, &p->gfontcolor))
17fab6e5
AG
1523 gdk_color_parse ("black", &p->gfontcolor);
1524 p->fontcolor = gcolor2rgb24(&p->gfontcolor);
1525 DBG("fontcolor=%x\n", p->fontcolor);
1526 }
4bca3e51
AG
1527 if (config_setting_lookup_int(cfg, "usefontsize", &i))
1528 p->usefontsize = i != 0;
1529 if (config_setting_lookup_int(cfg, "fontsize", &i) && i > 0)
1530 p->fontsize = i;
1531 if (config_setting_lookup_int(cfg, "background", &i))
1532 p->background = i != 0;
1533 if (config_setting_lookup_string(cfg, "backgroundfile", &str))
1534 p->background_file = g_strdup(str);
1535 config_setting_lookup_int(cfg, "iconsize", &p->icon_size);
1536 if (config_setting_lookup_int(cfg, "loglevel", &configured_log_level))
17fab6e5 1537 {
17fab6e5
AG
1538 if (!log_level_set_on_commandline)
1539 log_level = configured_log_level;
a52c2257 1540 }
4542c20d 1541
9dd114c4 1542 panel_normalize_configuration(p);
239cb032 1543
9dd114c4 1544 return 1;
a52c2257
HJYP
1545}
1546
1547static int
a7bd16a4 1548panel_parse_plugin(LXPanel *p, config_setting_t *cfg)
a52c2257 1549{
4bca3e51 1550 const char *type = NULL;
db449f6e 1551
a52c2257 1552 ENTER;
4bca3e51 1553 config_setting_lookup_string(cfg, "type", &type);
17fab6e5 1554 DBG("plug %s\n", type);
db449f6e 1555
17fab6e5 1556 if (!type || lxpanel_add_plugin(p, type, cfg, -1) == NULL) {
a52c2257
HJYP
1557 ERR( "lxpanel: can't load %s plugin\n", type);
1558 goto error;
1559 }
a52c2257 1560 RET(1);
db449f6e 1561
17fab6e5 1562error:
a52c2257 1563 RET(0);
a52c2257
HJYP
1564}
1565
adf42c84 1566static int panel_start( LXPanel *p )
a52c2257 1567{
17fab6e5
AG
1568 config_setting_t *list, *s;
1569 int i;
db449f6e 1570
a52c2257
HJYP
1571 /* parse global section */
1572 ENTER;
8110399f 1573
a7bd16a4
AG
1574 list = config_setting_get_member(config_root_setting(p->priv->config), "");
1575 if (!list || !panel_parse_global(p->priv, config_setting_get_elem(list, 0)))
a52c2257
HJYP
1576 RET(0);
1577
9dd114c4 1578 panel_start_gui(p);
1579
17fab6e5
AG
1580 for (i = 1; (s = config_setting_get_elem(list, i)) != NULL; )
1581 if (strcmp(config_setting_get_name(s), "Plugin") == 0 &&
1582 panel_parse_plugin(p, s)) /* success on plugin start */
1583 i++;
1584 else /* remove invalid data from config */
1585 config_setting_remove_elem(list, i);
4542c20d
HJYP
1586
1587 /* update backgrond of panel and all plugins */
a7bd16a4 1588 _panel_update_background(p);
9dd114c4 1589 return 1;
a52c2257
HJYP
1590}
1591
8110399f 1592void panel_destroy(Panel *p)
a52c2257 1593{
a7bd16a4 1594 gtk_widget_destroy(GTK_WIDGET(p->topgwin));
a52c2257
HJYP
1595}
1596
a7bd16a4 1597static LXPanel* panel_new( const char* config_file, const char* config_name )
8110399f 1598{
a7bd16a4 1599 LXPanel* panel = NULL;
16fbda14 1600
17fab6e5 1601 if (G_LIKELY(config_file))
cf701cb7 1602 {
17fab6e5 1603 panel = panel_allocate();
a7bd16a4 1604 panel->priv->name = g_strdup(config_name);
17fab6e5 1605 g_debug("starting panel from file %s",config_file);
a7bd16a4 1606 if (!config_read_file(panel->priv->config, config_file) ||
17fab6e5 1607 !panel_start(panel))
3e7b8eb7 1608 {
17fab6e5 1609 ERR( "lxpanel: can't start panel\n");
a7bd16a4 1610 gtk_widget_destroy(GTK_WIDGET(panel));
17fab6e5 1611 panel = NULL;
cf701cb7 1612 }
cf701cb7
HJYP
1613 }
1614 return panel;
8110399f 1615}
a52c2257 1616
0defe4b9 1617static void
a52c2257
HJYP
1618usage()
1619{
e7cb732b
HJYP
1620 g_print(_("lxpanel %s - lightweight GTK2+ panel for UNIX desktops\n"), version);
1621 g_print(_("Command line options:\n"));
1622 g_print(_(" --help -- print this help and exit\n"));
1623 g_print(_(" --version -- print version and exit\n"));
1624 g_print(_(" --log <number> -- set log level 0-5. 0 - none 5 - chatty\n"));
2918994e 1625// g_print(_(" --configure -- launch configuration utility\n"));
e7cb732b
HJYP
1626 g_print(_(" --profile name -- use specified profile\n"));
1627 g_print("\n");
1628 g_print(_(" -h -- same as --help\n"));
1629 g_print(_(" -p -- same as --profile\n"));
1630 g_print(_(" -v -- same as --version\n"));
2918994e 1631 // g_print(_(" -C -- same as --configure\n"));
b37c9aae 1632 g_print(_("\nVisit http://lxde.org/ for detail.\n\n"));
a52c2257
HJYP
1633}
1634
e68b47dc
JH
1635/* Lightweight lock related functions - X clipboard hacks */
1636
1637#define CLIPBOARD_NAME "LXPANEL_SELECTION"
1638
1639/*
1640 * clipboard_get_func - dummy get_func for gtk_clipboard_set_with_data ()
1641 */
1642static void
1643clipboard_get_func(
1644 GtkClipboard *clipboard G_GNUC_UNUSED,
1645 GtkSelectionData *selection_data G_GNUC_UNUSED,
1646 guint info G_GNUC_UNUSED,
1647 gpointer user_data_or_owner G_GNUC_UNUSED)
1648{
1649}
1650
1651/*
1652 * clipboard_clear_func - dummy clear_func for gtk_clipboard_set_with_data ()
1653 */
1654static void clipboard_clear_func(
1655 GtkClipboard *clipboard G_GNUC_UNUSED,
1656 gpointer user_data_or_owner G_GNUC_UNUSED)
1657{
1658}
1659
1660/*
1661 * Lightweight version for checking single instance.
1662 * Try and get the CLIPBOARD_NAME clipboard instead of using file manipulation.
1663 *
1664 * Returns TRUE if successfully retrieved and FALSE otherwise.
1665 */
16fb8c2e 1666static gboolean check_main_lock()
e68b47dc
JH
1667{
1668 static const GtkTargetEntry targets[] = { { CLIPBOARD_NAME, 0, 0 } };
1669 gboolean retval = FALSE;
1670 GtkClipboard *clipboard;
1671 Atom atom;
09fa171b 1672 Display *xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
e68b47dc
JH
1673
1674 atom = gdk_x11_get_xatom_by_name(CLIPBOARD_NAME);
1675
09fa171b 1676 XGrabServer(xdisplay);
e68b47dc 1677
09fa171b 1678 if (XGetSelectionOwner(xdisplay, atom) != None)
e68b47dc
JH
1679 goto out;
1680
1681 clipboard = gtk_clipboard_get(gdk_atom_intern(CLIPBOARD_NAME, FALSE));
1682
1683 if (gtk_clipboard_set_with_data(clipboard, targets,
1684 G_N_ELEMENTS (targets),
1685 clipboard_get_func,
1686 clipboard_clear_func, NULL))
1687 retval = TRUE;
1688
1689out:
09fa171b 1690 XUngrabServer (xdisplay);
e68b47dc
JH
1691 gdk_flush ();
1692
1693 return retval;
1694}
1695#undef CLIPBOARD_NAME
1696
1f4bc3aa 1697static void _start_panels_from_dir(const char *panel_dir)
8110399f 1698{
1f4bc3aa
AG
1699 GDir* dir = g_dir_open( panel_dir, 0, NULL );
1700 const gchar* name;
cf701cb7 1701
1f4bc3aa
AG
1702 if( ! dir )
1703 {
1704 return;
1705 }
cf701cb7 1706
1f4bc3aa
AG
1707 while((name = g_dir_read_name(dir)) != NULL)
1708 {
1709 char* panel_config = g_build_filename( panel_dir, name, NULL );
0bcf9045 1710 if (strchr(panel_config, '~') == NULL) /* Skip editor backup files in case user has hand edited in this directory */
cf701cb7 1711 {
a7bd16a4 1712 LXPanel* panel = panel_new( panel_config, name );
1f4bc3aa
AG
1713 if( panel )
1714 all_panels = g_slist_prepend( all_panels, panel );
cf701cb7 1715 }
1f4bc3aa 1716 g_free( panel_config );
cf701cb7 1717 }
1f4bc3aa
AG
1718 g_dir_close( dir );
1719}
1720
1721static gboolean start_all_panels( )
1722{
1723 char *panel_dir;
1724
1725 /* try user panels */
1726 panel_dir = _user_config_file_name("panels", NULL);
1727 _start_panels_from_dir(panel_dir);
1728 g_free(panel_dir);
1729 if (all_panels != NULL)
1730 return TRUE;
1731 /* else try XDG fallback */
1732 panel_dir = _system_config_file_name("panels");
1733 _start_panels_from_dir(panel_dir);
1734 g_free(panel_dir);
1735 if (all_panels != NULL)
1736 return TRUE;
1737 /* last try at old fallback for compatibility reasons */
1738 panel_dir = _old_system_config_file_name("panels");
1739 _start_panels_from_dir(panel_dir);
1740 g_free(panel_dir);
cf701cb7 1741 return all_panels != NULL;
8110399f
HJYP
1742}
1743
cf701cb7
HJYP
1744void load_global_config();
1745void free_global_config();
1746
1f4bc3aa
AG
1747static void _ensure_user_config_dirs(void)
1748{
1749 char *dir = g_build_filename(g_get_user_config_dir(), "lxpanel", cprofile,
1750 "panels", NULL);
1751
1752 /* make sure the private profile and panels dir exists */
1753 g_mkdir_with_parents(dir, 0700);
1754 g_free(dir);
1755}
1756
8110399f 1757int main(int argc, char *argv[], char *env[])
a52c2257
HJYP
1758{
1759 int i;
9df6826f 1760 const char* desktop_name;
f3eacf81 1761 char *file;
f277dbb7 1762
a52c2257 1763 setlocale(LC_CTYPE, "");
f277dbb7 1764
0bcf9045
AG
1765 g_thread_init(NULL);
1766/* gdk_threads_init();
1767 gdk_threads_enter(); */
1d434622 1768
f3eacf81
AG
1769 /* Add a gtkrc file to be parsed too. */
1770 file = _user_config_file_name("gtkrc", NULL);
1771 gtk_rc_add_default_file(file);
1772 g_free(file);
1773
a52c2257
HJYP
1774 gtk_init(&argc, &argv);
1775
1776#ifdef ENABLE_NLS
1777 bindtextdomain ( GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR );
1778 bind_textdomain_codeset ( GETTEXT_PACKAGE, "UTF-8" );
1779 textdomain ( GETTEXT_PACKAGE );
1780#endif
1781
1782 XSetLocaleModifiers("");
2918994e 1783 XSetErrorHandler((XErrorHandler) panel_handle_x_error);
8110399f 1784
a52c2257 1785 resolve_atoms();
8110399f 1786
9df6826f
HJYP
1787 desktop_name = g_getenv("XDG_CURRENT_DESKTOP");
1788 is_in_lxde = desktop_name && (0 == strcmp(desktop_name, "LXDE"));
95095259 1789
a52c2257
HJYP
1790 for (i = 1; i < argc; i++) {
1791 if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
1792 usage();
1793 exit(0);
1794 } else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
1795 printf("lxpanel %s\n", version);
1796 exit(0);
1797 } else if (!strcmp(argv[i], "--log")) {
1798 i++;
1799 if (i == argc) {
1800 ERR( "lxpanel: missing log level\n");
1801 usage();
1802 exit(1);
1803 } else {
1804 log_level = atoi(argv[i]);
82ca42af 1805 log_level_set_on_commandline = true;
a52c2257
HJYP
1806 }
1807 } else if (!strcmp(argv[i], "--configure") || !strcmp(argv[i], "-C")) {
1808 config = 1;
1809 } else if (!strcmp(argv[i], "--profile") || !strcmp(argv[i], "-p")) {
1810 i++;
1811 if (i == argc) {
1812 ERR( "lxpanel: missing profile name\n");
1813 usage();
1814 exit(1);
1815 } else {
1816 cprofile = g_strdup(argv[i]);
1817 }
1818 } else {
1819 printf("lxpanel: unknown option - %s\n", argv[i]);
1820 usage();
1821 exit(1);
1822 }
1823 }
f277dbb7 1824
8110399f 1825 /* Check for duplicated lxpanel instances */
e68b47dc 1826 if (!check_main_lock() && !config) {
e7a42ecf 1827 printf("There is already an instance of LXPanel. Now to exit\n");
e68b47dc
JH
1828 exit(1);
1829 }
1830
1f4bc3aa
AG
1831 _ensure_user_config_dirs();
1832
730e3f7b 1833 /* Add our own icons to the search path of icon theme */
0b806437 1834 gtk_icon_theme_append_search_path( gtk_icon_theme_get_default(), PACKAGE_DATA_DIR "/images" );
730e3f7b 1835
cf701cb7 1836 fbev = fb_ev_new();
e2957bd2 1837 win_grp = gtk_window_group_new();
22242ed4 1838
f7cb330e
HJYP
1839restart:
1840 is_restarting = FALSE;
1841
17fab6e5
AG
1842 /* init LibFM */
1843 fm_gtk_init(NULL);
1844
1845 /* prepare modules data */
1846 _prepare_modules();
1847
cf701cb7
HJYP
1848 load_global_config();
1849
0bcf9045
AG
1850 /* NOTE: StructureNotifyMask is required by XRandR
1851 * See init_randr_support() in gdkscreen-x11.c of gtk+ for detail.
1852 */
0f83fb71 1853 gdk_window_set_events(gdk_get_default_root_window(), GDK_STRUCTURE_MASK |
1854 GDK_SUBSTRUCTURE_MASK | GDK_PROPERTY_CHANGE_MASK);
cf701cb7 1855 gdk_window_add_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, NULL);
8110399f 1856
cf701cb7
HJYP
1857 if( G_UNLIKELY( ! start_all_panels() ) )
1858 g_warning( "Config files are not found.\n" );
8110399f
HJYP
1859/*
1860 * FIXME: configure??
6a6ad54e
HJYP
1861 if (config)
1862 configure();
8110399f 1863*/
6a6ad54e 1864 gtk_main();
8110399f 1865
09fa171b 1866 XSelectInput (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), GDK_ROOT_WINDOW(), NoEventMask);
cf701cb7
HJYP
1867 gdk_window_remove_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, NULL);
1868
8110399f 1869 /* destroy all panels */
a7bd16a4 1870 g_slist_foreach( all_panels, (GFunc) gtk_widget_destroy, NULL );
2a26c0d8
HJYP
1871 g_slist_free( all_panels );
1872 all_panels = NULL;
6a6ad54e 1873 g_free( cfgfile );
5541b8d2 1874
cf701cb7
HJYP
1875 free_global_config();
1876
17fab6e5
AG
1877 _unload_modules();
1878 fm_gtk_finalize();
1879
f7cb330e
HJYP
1880 if( is_restarting )
1881 goto restart;
1882
74bf5dbf 1883 /* gdk_threads_leave(); */
3e71f8af 1884
e2957bd2 1885 g_object_unref(win_grp);
22242ed4
HJYP
1886 g_object_unref(fbev);
1887
a7bd16a4
AG
1888 /* FIXME: do restart more correct way:
1889 if (!is_restarting)
1890 return 0;
1891 if (strchr(argv[0], G_PATH_SEPARATOR))
1892 execve(argv[0], argv, env);
1893 else
1894 execve(g_find_program_in_path(argv[0]), argv, env);
1895 return 1; */
9c338caf 1896
a7bd16a4 1897 return 0;
9c338caf
AG
1898}
1899
a7bd16a4 1900GtkOrientation panel_get_orientation(LXPanel *panel)
9c338caf 1901{
a7bd16a4 1902 return panel->priv->orientation;
9c338caf
AG
1903}
1904
a7bd16a4 1905gint panel_get_icon_size(LXPanel *panel)
9c338caf 1906{
a7bd16a4 1907 return panel->priv->icon_size;
9c338caf
AG
1908}
1909
a7bd16a4 1910gint panel_get_height(LXPanel *panel)
9c338caf 1911{
a7bd16a4 1912 return panel->priv->height;
9c338caf
AG
1913}
1914
a7bd16a4 1915Window panel_get_xwindow(LXPanel *panel)
4718ca02 1916{
a7bd16a4 1917 return panel->priv->topxwin;
4718ca02
AG
1918}
1919
a7bd16a4 1920gint panel_get_monitor(LXPanel *panel)
4718ca02 1921{
a7bd16a4 1922 return panel->priv->monitor;
4718ca02
AG
1923}
1924
a7bd16a4 1925GtkStyle *panel_get_defstyle(LXPanel *panel)
9c338caf 1926{
a7bd16a4 1927 return panel->priv->defstyle;
9c338caf
AG
1928}
1929
a7bd16a4 1930GtkIconTheme *panel_get_icon_theme(LXPanel *panel)
9c338caf 1931{
a7bd16a4 1932 return panel->priv->icon_theme;
9c338caf
AG
1933}
1934
a7bd16a4 1935gboolean panel_is_at_bottom(LXPanel *panel)
4718ca02 1936{
a7bd16a4 1937 return panel->priv->edge == EDGE_BOTTOM;
4718ca02
AG
1938}
1939
072944bf
AG
1940gboolean panel_is_dynamic(LXPanel *panel)
1941{
1942 return panel->priv->widthtype == WIDTH_REQUEST;
1943}
1944
a7bd16a4 1945GtkWidget *panel_box_new(LXPanel *panel, gboolean homogeneous, gint spacing)
9c338caf 1946{
a7bd16a4 1947 if (panel->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
5eaabb1b
AG
1948 return gtk_hbox_new(homogeneous, spacing);
1949 return gtk_vbox_new(homogeneous, spacing);
9c338caf
AG
1950}
1951
a7bd16a4 1952GtkWidget *panel_separator_new(LXPanel *panel)
9c338caf 1953{
a7bd16a4 1954 if (panel->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
5eaabb1b
AG
1955 return gtk_vseparator_new();
1956 return gtk_hseparator_new();
9c338caf 1957}
10d93053 1958
191694fb 1959gboolean _class_is_present(const LXPanelPluginInit *init)
10d93053
AG
1960{
1961 GSList *sl;
1962
1963 for (sl = all_panels; sl; sl = sl->next )
1964 {
a7bd16a4 1965 LXPanel *panel = (LXPanel*)sl->data;
10d93053
AG
1966 GList *plugins, *p;
1967
a7bd16a4 1968 plugins = gtk_container_get_children(GTK_CONTAINER(panel->priv->box));
10d93053
AG
1969 for (p = plugins; p; p = p->next)
1970 if (PLUGIN_CLASS(p->data) == init)
1971 {
1972 g_list_free(plugins);
1973 return TRUE;
1974 }
1975 g_list_free(plugins);
1976 }
1977 return FALSE;
1978}