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