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