Allow drag applications in menu plugin so they can be dropped somewhere.
[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;
e3b89f43 779 ret = menu = GTK_MENU(gtk_menu_new());
cf701cb7 780
4b93d81e
HJYP
781 img = gtk_image_new_from_stock( GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU );
782 menu_item = gtk_image_menu_item_new_with_label(_("Add / Remove Panel Items"));
783 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
784 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
785 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_add_item), panel );
cf701cb7
HJYP
786
787 if( plugin )
788 {
e56f2fdd 789 init = PLUGIN_CLASS(plugin);
cf701cb7 790 img = gtk_image_new_from_stock( GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU );
17fab6e5 791 tmp = g_strdup_printf( _("Remove \"%s\" From Panel"), _(init->name) );
cf701cb7
HJYP
792 menu_item = gtk_image_menu_item_new_with_label( tmp );
793 g_free( tmp );
794 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
795 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
796 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_remove_item), plugin );
797 }
798
799 menu_item = gtk_separator_menu_item_new();
800 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
801
4b93d81e
HJYP
802 img = gtk_image_new_from_stock( GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU );
803 menu_item = gtk_image_menu_item_new_with_label(_("Panel Settings"));
804 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
805 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
806 g_signal_connect(G_OBJECT(menu_item), "activate", G_CALLBACK(panel_popupmenu_configure), panel );
807
cf701cb7
HJYP
808 img = gtk_image_new_from_stock( GTK_STOCK_NEW, GTK_ICON_SIZE_MENU );
809 menu_item = gtk_image_menu_item_new_with_label(_("Create New Panel"));
810 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
811 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
812 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_create_panel), panel );
813
814 img = gtk_image_new_from_stock( GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU );
815 menu_item = gtk_image_menu_item_new_with_label(_("Delete This Panel"));
816 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
817 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
818 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_delete_panel), panel );
819 if( ! all_panels->next ) /* if this is the only panel */
820 gtk_widget_set_sensitive( menu_item, FALSE );
821
e7a42ecf 822 menu_item = gtk_separator_menu_item_new();
823 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
824
825 img = gtk_image_new_from_stock( GTK_STOCK_ABOUT, GTK_ICON_SIZE_MENU );
826 menu_item = gtk_image_menu_item_new_with_label(_("About"));
827 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
828 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
829 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_about), panel );
830
cf701cb7
HJYP
831 if( use_sub_menu )
832 {
e3b89f43 833 ret = GTK_MENU(gtk_menu_new());
cf701cb7
HJYP
834 menu_item = gtk_image_menu_item_new_with_label(_("Panel"));
835 gtk_menu_shell_append(GTK_MENU_SHELL(ret), menu_item);
e3b89f43 836 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), GTK_WIDGET(menu) );
cf701cb7 837
e3b89f43 838 gtk_widget_show_all(GTK_WIDGET(ret));
cf701cb7
HJYP
839 }
840
3e7b8eb7
HJYP
841 if( plugin )
842 {
843 menu_item = gtk_separator_menu_item_new();
844 gtk_menu_shell_prepend(GTK_MENU_SHELL(ret), menu_item);
845
846 img = gtk_image_new_from_stock( GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU );
17fab6e5 847 tmp = g_strdup_printf( _("\"%s\" Settings"), _(init->name) );
3e7b8eb7
HJYP
848 menu_item = gtk_image_menu_item_new_with_label( tmp );
849 g_free( tmp );
850 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
851 gtk_menu_shell_prepend(GTK_MENU_SHELL(ret), menu_item);
17fab6e5 852 if( init->config )
3e7b8eb7
HJYP
853 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_config_plugin), plugin );
854 else
855 gtk_widget_set_sensitive( menu_item, FALSE );
856 }
857
e3b89f43 858 gtk_widget_show_all(GTK_WIDGET(menu));
3e7b8eb7 859
cf701cb7
HJYP
860 g_signal_connect( ret, "selection-done", G_CALLBACK(gtk_widget_destroy), NULL );
861 return ret;
862}
863
17fab6e5
AG
864/* for old plugins compatibility */
865GtkMenu* lxpanel_get_panel_menu( Panel* panel, Plugin* plugin, gboolean use_sub_menu )
866{
867 return lxpanel_get_plugin_menu(panel, plugin->pwid, use_sub_menu);
868}
869
a52c2257
HJYP
870/****************************************************
871 * panel creation *
872 ****************************************************/
176fb687 873
a52c2257 874static void
22242ed4 875make_round_corners(Panel *p)
a52c2257 876{
a97d06a6 877 /* FIXME: This should be re-written with shape extension of X11 */
4542c20d 878 /* gdk_window_shape_combine_mask() can be used */
bee4c26e
HJYP
879}
880
22242ed4 881void panel_set_dock_type(Panel *p)
bee4c26e
HJYP
882{
883 if (p->setdocktype) {
884 Atom state = a_NET_WM_WINDOW_TYPE_DOCK;
885 XChangeProperty(GDK_DISPLAY(), p->topxwin,
886 a_NET_WM_WINDOW_TYPE, XA_ATOM, 32,
887 PropModeReplace, (unsigned char *) &state, 1);
888 }
889 else {
890 XDeleteProperty( GDK_DISPLAY(), p->topxwin, a_NET_WM_WINDOW_TYPE );
891 }
a52c2257
HJYP
892}
893
176fb687 894static void panel_set_visibility(Panel *p, gboolean visible)
895{
896 if ( ! visible) gtk_widget_hide(p->box);
897 p->visible = visible;
898 calculate_position(p);
899 gtk_widget_set_size_request(p->topgwin, p->aw, p->ah);
900 gdk_window_move(p->topgwin->window, p->ax, p->ay);
901 if (visible) gtk_widget_show(p->box);
902 panel_set_wm_strut(p);
903}
904
905static gboolean panel_leave_real(Panel *p)
906{
fab48ed0 907 /* If the pointer is grabbed by this application, leave the panel displayed.
908 * There is no way to determine if it is grabbed by another application, such as an application that has a systray icon. */
176fb687 909 if (gdk_display_pointer_is_grabbed(p->display))
910 return TRUE;
911
fab48ed0 912 /* If the pointer is inside the panel, leave the panel displayed. */
176fb687 913 gint x, y;
914 gdk_display_get_pointer(p->display, NULL, &x, &y, NULL);
915 if ((p->cx <= x) && (x <= (p->cx + p->cw)) && (p->cy <= y) && (y <= (p->cy + p->ch)))
916 return TRUE;
917
fab48ed0 918 /* If the panel is configured to autohide and if it is visible, hide the panel. */
176fb687 919 if ((p->autohide) && (p->visible))
920 panel_set_visibility(p, FALSE);
921
fab48ed0 922 /* Clear the timer. */
176fb687 923 p->hide_timeout = 0;
924 return FALSE;
925}
926
927static gboolean panel_enter(GtkImage *widget, GdkEventCrossing *event, Panel *p)
928{
fab48ed0 929 /* We may receive multiple enter-notify events when the pointer crosses into the panel.
930 * Do extra tests to make sure this does not cause misoperation such as blinking.
931 * If the pointer is inside the panel, unhide it. */
932 gint x, y;
933 gdk_display_get_pointer(p->display, NULL, &x, &y, NULL);
934 if ((p->cx <= x) && (x <= (p->cx + p->cw)) && (p->cy <= y) && (y <= (p->cy + p->ch)))
935 {
936 /* If the pointer is inside the panel and we have not already unhidden it, do so and
937 * set a timer to recheck it in a half second. */
938 if (p->hide_timeout == 0)
939 {
940 p->hide_timeout = g_timeout_add(500, (GSourceFunc) panel_leave_real, p);
941 panel_set_visibility(p, TRUE);
942 }
943 }
944 else
945 {
946 /* If the pointer is not inside the panel, simulate a timer expiration. */
947 panel_leave_real(p);
948 }
176fb687 949 return TRUE;
950}
951
952static gboolean panel_drag_motion(GtkWidget *widget, GdkDragContext *drag_context, gint x,
953 gint y, guint time, Panel *p)
954{
955 panel_enter(NULL, NULL, p);
956 return TRUE;
957}
958
959void panel_establish_autohide(Panel *p)
960{
961 if (p->autohide)
962 {
963 gtk_widget_add_events(p->topgwin, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
964 g_signal_connect(G_OBJECT(p->topgwin), "enter-notify-event", G_CALLBACK(panel_enter), p);
965 g_signal_connect(G_OBJECT(p->topgwin), "drag-motion", (GCallback) panel_drag_motion, p);
966 gtk_drag_dest_set(p->topgwin, GTK_DEST_DEFAULT_MOTION, NULL, 0, 0);
967 gtk_drag_dest_set_track_motion(p->topgwin, TRUE);
968 panel_enter(NULL, NULL, p);
969 }
970 else if ( ! p->visible)
971 {
972 gtk_widget_show(p->box);
973 p->visible = TRUE;
974 }
975}
976
8f9e6256 977/* Set an image from a file with scaling to the panel icon size. */
fcb35553 978void panel_image_set_from_file(Panel * p, GtkWidget * image, const char * file)
8f9e6256 979{
980 GdkPixbuf * pixbuf = gdk_pixbuf_new_from_file_at_scale(file, p->icon_size, p->icon_size, TRUE, NULL);
981 if (pixbuf != NULL)
982 {
983 gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf);
984 g_object_unref(pixbuf);
985 }
986}
987
c14620f2
MJ
988/* Set an image from a icon theme with scaling to the panel icon size. */
989gboolean panel_image_set_icon_theme(Panel * p, GtkWidget * image, const gchar * icon)
990{
1b532bce 991 if (gtk_icon_theme_has_icon(p->icon_theme, icon))
c14620f2 992 {
1b532bce 993 GdkPixbuf * pixbuf = gtk_icon_theme_load_icon(p->icon_theme, icon, p->icon_size, 0, NULL);
c14620f2
MJ
994 gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf);
995 g_object_unref(pixbuf);
996 return TRUE;
997 }
998 return FALSE;
999}
1000
0defe4b9 1001static void
22242ed4 1002panel_start_gui(Panel *p)
a52c2257
HJYP
1003{
1004 Atom state[3];
1005 XWMHints wmhints;
1006 guint32 val;
6db11841 1007
a52c2257
HJYP
1008 ENTER;
1009
9dd114c4 1010 p->curdesk = get_net_current_desktop();
1011 p->desknum = get_net_number_of_desktops();
1012 p->workarea = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_WORKAREA, XA_CARDINAL, &p->wa_len);
1013
cb22c87c
HJYP
1014 /* main toplevel window */
1015 /* p->topgwin = gtk_window_new(GTK_WINDOW_TOPLEVEL); */
1016 p->topgwin = (GtkWidget*)g_object_new(PANEL_TOPLEVEL_TYPE, NULL);
1017 gtk_widget_set_name(p->topgwin, "PanelToplevel");
176fb687 1018 p->display = gdk_display_get_default();
a52c2257
HJYP
1019 gtk_container_set_border_width(GTK_CONTAINER(p->topgwin), 0);
1020 gtk_window_set_resizable(GTK_WINDOW(p->topgwin), FALSE);
1021 gtk_window_set_wmclass(GTK_WINDOW(p->topgwin), "panel", "lxpanel");
1022 gtk_window_set_title(GTK_WINDOW(p->topgwin), "panel");
1023 gtk_window_set_position(GTK_WINDOW(p->topgwin), GTK_WIN_POS_NONE);
1024 gtk_window_set_decorated(GTK_WINDOW(p->topgwin), FALSE);
77886b88 1025
e2957bd2
HJYP
1026 gtk_window_group_add_window( win_grp, (GtkWindow*)p->topgwin );
1027
a52c2257
HJYP
1028 g_signal_connect(G_OBJECT(p->topgwin), "delete-event",
1029 G_CALLBACK(panel_delete_event), p);
1030 g_signal_connect(G_OBJECT(p->topgwin), "destroy-event",
1031 G_CALLBACK(panel_destroy_event), p);
1032 g_signal_connect (G_OBJECT (p->topgwin), "size-request",
1033 (GCallback) panel_size_req, p);
1034 g_signal_connect (G_OBJECT (p->topgwin), "size-allocate",
1035 (GCallback) panel_size_alloc, p);
1036 g_signal_connect (G_OBJECT (p->topgwin), "configure-event",
1037 (GCallback) panel_configure_event, p);
3e7b8eb7
HJYP
1038
1039 gtk_widget_add_events( p->topgwin, GDK_BUTTON_PRESS_MASK );
8e05828e 1040 g_signal_connect(G_OBJECT (p->topgwin), "button-press-event",
9dd114c4 1041 (GCallback) panel_button_press_event_with_panel, p);
f2d54481 1042
a52c2257
HJYP
1043 g_signal_connect (G_OBJECT (p->topgwin), "realize",
1044 (GCallback) panel_realize, p);
f2d54481 1045
84fc1d55
HJYP
1046 g_signal_connect (G_OBJECT (p->topgwin), "style-set",
1047 (GCallback)panel_style_set, p);
a52c2257
HJYP
1048 gtk_widget_realize(p->topgwin);
1049 //gdk_window_set_decorations(p->topgwin->window, 0);
2de71c90 1050
4542c20d 1051 // main layout manager as a single child of panel
a97d06a6 1052 p->box = p->my_box_new(FALSE, 0);
a52c2257 1053 gtk_container_set_border_width(GTK_CONTAINER(p->box), 0);
4542c20d
HJYP
1054// gtk_container_add(GTK_CONTAINER(p->bbox), p->box);
1055 gtk_container_add(GTK_CONTAINER(p->topgwin), p->box);
a52c2257 1056 gtk_widget_show(p->box);
a97d06a6
HJYP
1057 if (p->round_corners)
1058 make_round_corners(p);
6db11841 1059
a52c2257
HJYP
1060 p->topxwin = GDK_WINDOW_XWINDOW(GTK_WIDGET(p->topgwin)->window);
1061 DBG("topxwin = %x\n", p->topxwin);
1062
1063 /* the settings that should be done before window is mapped */
1064 wmhints.flags = InputHint;
1065 wmhints.input = 0;
bee4c26e 1066 XSetWMHints (GDK_DISPLAY(), p->topxwin, &wmhints);
24053345 1067#define WIN_HINTS_SKIP_FOCUS (1<<0) /* "alt-tab" skips this win */
a52c2257
HJYP
1068 val = WIN_HINTS_SKIP_FOCUS;
1069 XChangeProperty(GDK_DISPLAY(), p->topxwin,
1070 XInternAtom(GDK_DISPLAY(), "_WIN_HINTS", False), XA_CARDINAL, 32,
1071 PropModeReplace, (unsigned char *) &val, 1);
1072
bee4c26e 1073 panel_set_dock_type(p);
a52c2257
HJYP
1074
1075 /* window mapping point */
1076 gtk_widget_show_all(p->topgwin);
1077
1078 /* the settings that should be done after window is mapped */
176fb687 1079 panel_establish_autohide(p);
a52c2257
HJYP
1080
1081 /* send it to running wm */
1082 Xclimsg(p->topxwin, a_NET_WM_DESKTOP, 0xFFFFFFFF, 0, 0, 0, 0);
1083 /* and assign it ourself just for case when wm is not running */
1084 val = 0xFFFFFFFF;
1085 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_DESKTOP, XA_CARDINAL, 32,
1086 PropModeReplace, (unsigned char *) &val, 1);
1087
1088 state[0] = a_NET_WM_STATE_SKIP_PAGER;
1089 state[1] = a_NET_WM_STATE_SKIP_TASKBAR;
1090 state[2] = a_NET_WM_STATE_STICKY;
1091 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STATE, XA_ATOM,
1092 32, PropModeReplace, (unsigned char *) state, 3);
1093
a52c2257
HJYP
1094 calculate_position(p);
1095 gdk_window_move_resize(p->topgwin->window, p->ax, p->ay, p->aw, p->ah);
bee4c26e 1096 panel_set_wm_strut(p);
77886b88 1097
a52c2257
HJYP
1098 RET();
1099}
1100
2918994e 1101/* Exchange the "width" and "height" terminology for vertical and horizontal panels. */
1102void panel_adjust_geometry_terminology(Panel * p)
9dd114c4 1103{
8ed3b3d4 1104 if ((p->height_label != NULL) && (p->width_label != NULL)
1105 && (p->alignment_left_label != NULL) && (p->alignment_right_label != NULL))
9dd114c4 1106 {
1107 if ((p->edge == EDGE_TOP) || (p->edge == EDGE_BOTTOM))
1108 {
1109 gtk_label_set_text(GTK_LABEL(p->height_label), _("Height:"));
1110 gtk_label_set_text(GTK_LABEL(p->width_label), _("Width:"));
2918994e 1111 gtk_button_set_label(GTK_BUTTON(p->alignment_left_label), _("Left"));
1112 gtk_button_set_label(GTK_BUTTON(p->alignment_right_label), _("Right"));
9dd114c4 1113 }
1114 else
1115 {
1116 gtk_label_set_text(GTK_LABEL(p->height_label), _("Width:"));
1117 gtk_label_set_text(GTK_LABEL(p->width_label), _("Height:"));
2918994e 1118 gtk_button_set_label(GTK_BUTTON(p->alignment_left_label), _("Top"));
1119 gtk_button_set_label(GTK_BUTTON(p->alignment_right_label), _("Bottom"));
9dd114c4 1120 }
1121 }
1122}
1123
2918994e 1124/* Draw text into a label, with the user preference color and optionally bold. */
38ac664c
AG
1125void panel_draw_label_text(Panel * p, GtkWidget * label, const char * text,
1126 gboolean bold, float custom_size_factor,
1127 gboolean custom_color)
2918994e 1128{
2918994e 1129 if (text == NULL)
1130 {
1131 /* Null string. */
1132 gtk_label_set_text(GTK_LABEL(label), NULL);
83d410c1 1133 return;
2918994e 1134 }
1135
83d410c1
HG
1136 /* Compute an appropriate size so the font will scale with the panel's icon size. */
1137 int font_desc;
1138 if (p->usefontsize)
1139 font_desc = p->fontsize;
f669ce5e 1140 else
2918994e 1141 {
83d410c1
HG
1142 if (p->icon_size < 20)
1143 font_desc = 9;
1144 else if (p->icon_size >= 20 && p->icon_size < 36)
1145 font_desc = 10;
1146 else
1147 font_desc = 12;
1148 }
1149 font_desc *= custom_size_factor;
1150
1151 /* Check the string for characters that need to be escaped.
1152 * If any are found, create the properly escaped string and use it instead. */
38ac664c 1153 const char * valid_markup = text;
83d410c1 1154 char * escaped_text = NULL;
38ac664c 1155 const char * q;
83d410c1
HG
1156 for (q = text; *q != '\0'; q += 1)
1157 {
1158 if ((*q == '<') || (*q == '>') || (*q == '&'))
2918994e 1159 {
83d410c1
HG
1160 escaped_text = g_markup_escape_text(text, -1);
1161 valid_markup = escaped_text;
1162 break;
2918994e 1163 }
83d410c1 1164 }
2918994e 1165
83d410c1
HG
1166 gchar * formatted_text;
1167 if ((custom_color) && (p->usefontcolor))
1168 {
1169 /* Color, optionally bold. */
1170 formatted_text = g_strdup_printf("<span font_desc=\"%d\" color=\"#%06x\">%s%s%s</span>",
f669ce5e 1171 font_desc,
2918994e 1172 gcolor2rgb24(&p->gfontcolor),
1173 ((bold) ? "<b>" : ""),
1174 valid_markup,
1175 ((bold) ? "</b>" : ""));
83d410c1
HG
1176 }
1177 else
1178 {
1179 /* No color, optionally bold. */
1180 formatted_text = g_strdup_printf("<span font_desc=\"%d\">%s%s%s</span>",
f669ce5e 1181 font_desc,
1182 ((bold) ? "<b>" : ""),
1183 valid_markup,
1184 ((bold) ? "</b>" : ""));
2918994e 1185 }
83d410c1
HG
1186
1187 gtk_label_set_markup(GTK_LABEL(label), formatted_text);
1188 g_free(formatted_text);
1189 g_free(escaped_text);
2918994e 1190}
1191
1192void panel_set_panel_configuration_changed(Panel *p)
a97d06a6 1193{
17fab6e5 1194 GList *plugins, *l;
9dd114c4 1195
cfde283a 1196 GtkOrientation previous_orientation = p->orientation;
a97d06a6 1197 p->orientation = (p->edge == EDGE_TOP || p->edge == EDGE_BOTTOM)
cfde283a 1198 ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
9dd114c4 1199
cfde283a
AG
1200 /* either first run or orientation was changed */
1201 if (p->my_box_new == NULL || previous_orientation != p->orientation)
9dd114c4 1202 {
1203 panel_adjust_geometry_terminology(p);
f5c7784a
AG
1204 if (p->my_box_new != NULL)
1205 p->height = ((p->orientation == GTK_ORIENTATION_HORIZONTAL) ? PANEL_HEIGHT_DEFAULT : PANEL_WIDTH_DEFAULT);
9dd114c4 1206 if (p->height_control != NULL)
1207 gtk_spin_button_set_value(GTK_SPIN_BUTTON(p->height_control), p->height);
2918994e 1208 if ((p->widthtype == WIDTH_PIXEL) && (p->width_control != NULL))
1209 {
cfde283a 1210 int value = ((p->orientation == GTK_ORIENTATION_HORIZONTAL) ? gdk_screen_width() : gdk_screen_height());
2918994e 1211 gtk_spin_button_set_range(GTK_SPIN_BUTTON(p->width_control), 0, value);
1212 gtk_spin_button_set_value(GTK_SPIN_BUTTON(p->width_control), value);
1213 }
9dd114c4 1214 }
1215
cfde283a 1216 if (p->orientation == GTK_ORIENTATION_HORIZONTAL) {
a97d06a6
HJYP
1217 p->my_box_new = gtk_hbox_new;
1218 p->my_separator_new = gtk_vseparator_new;
1219 } else {
1220 p->my_box_new = gtk_vbox_new;
1221 p->my_separator_new = gtk_hseparator_new;
1222 }
1223
1224 /* recreate the main layout box */
964b8b7e 1225 if (p->box != NULL)
1226 {
cfde283a 1227 gtk_orientable_set_orientation(GTK_ORIENTABLE(p->box), p->orientation);
a97d06a6 1228 }
9dd114c4 1229
a97d06a6
HJYP
1230 /* NOTE: This loop won't be executed when panel started since
1231 plugins are not loaded at that time.
1232 This is used when the orientation of the panel is changed
1233 from the config dialog, and plugins should be re-layout.
1234 */
17fab6e5
AG
1235 plugins = p->box ? gtk_container_get_children(GTK_CONTAINER(p->box)) : NULL;
1236 for( l = plugins; l; l = l->next ) {
1237 GtkWidget *w = (GtkWidget*)l->data;
1238 LXPanelPluginInit *init = PLUGIN_CLASS(w);
1239 if (init->reconfigure)
1240 init->reconfigure(p, w);
a97d06a6 1241 }
17fab6e5 1242 g_list_free(plugins);
a97d06a6
HJYP
1243}
1244
a52c2257 1245static int
17fab6e5 1246panel_parse_global(Panel *p, config_setting_t *cfg)
a52c2257 1247{
4bca3e51
AG
1248 const char *str;
1249 gint i;
6db11841 1250
17fab6e5
AG
1251 /* check Global config */
1252 if (!cfg || strcmp(config_setting_get_name(cfg), "Global") != 0)
3e7b8eb7 1253 {
17fab6e5
AG
1254 ERR( "lxpanel: Global section not found\n");
1255 RET(0);
1256 }
4bca3e51
AG
1257 if (config_setting_lookup_string(cfg, "edge", &str))
1258 p->edge = str2num(edge_pair, str, EDGE_NONE);
1259 if (config_setting_lookup_string(cfg, "allign", &str))
1260 p->allign = str2num(allign_pair, str, ALLIGN_NONE);
1261 config_setting_lookup_int(cfg, "monitor", &p->monitor);
1262 config_setting_lookup_int(cfg, "margin", &p->margin);
1263 if (config_setting_lookup_string(cfg, "widthtype", &str))
1264 p->widthtype = str2num(width_pair, str, WIDTH_NONE);
1265 config_setting_lookup_int(cfg, "width", &p->width);
1266 if (config_setting_lookup_string(cfg, "heighttype", &str))
1267 p->heighttype = str2num(height_pair, str, HEIGHT_NONE);
1268 config_setting_lookup_int(cfg, "height", &p->height);
1269 if (config_setting_lookup_int(cfg, "spacing", &i) && i > 0)
1270 p->spacing = i;
1271 if (config_setting_lookup_int(cfg, "setdocktype", &i))
1272 p->setdocktype = i != 0;
1273 if (config_setting_lookup_int(cfg, "setpartialstrut", &i))
1274 p->setstrut = i != 0;
1275 if (config_setting_lookup_int(cfg, "RoundCorners", &i))
1276 p->round_corners = i != 0;
1277 if (config_setting_lookup_int(cfg, "transparent", &i))
1278 p->transparent = i != 0;
1279 if (config_setting_lookup_int(cfg, "alpha", &p->alpha))
17fab6e5 1280 {
17fab6e5
AG
1281 if (p->alpha > 255)
1282 p->alpha = 255;
1283 }
4bca3e51
AG
1284 if (config_setting_lookup_int(cfg, "autohide", &i))
1285 p->autohide = i != 0;
1286 config_setting_lookup_int(cfg, "heightwhenhidden", &p->height_when_hidden);
1287 if (config_setting_lookup_string(cfg, "tintcolor", &str))
17fab6e5 1288 {
4bca3e51 1289 if (!gdk_color_parse (str, &p->gtintcolor))
17fab6e5
AG
1290 gdk_color_parse ("white", &p->gtintcolor);
1291 p->tintcolor = gcolor2rgb24(&p->gtintcolor);
1292 DBG("tintcolor=%x\n", p->tintcolor);
1293 }
4bca3e51
AG
1294 if (config_setting_lookup_int(cfg, "usefontcolor", &i))
1295 p->usefontcolor = i != 0;
1296 if (config_setting_lookup_string(cfg, "fontcolor", &str))
17fab6e5 1297 {
4bca3e51 1298 if (!gdk_color_parse (str, &p->gfontcolor))
17fab6e5
AG
1299 gdk_color_parse ("black", &p->gfontcolor);
1300 p->fontcolor = gcolor2rgb24(&p->gfontcolor);
1301 DBG("fontcolor=%x\n", p->fontcolor);
1302 }
4bca3e51
AG
1303 if (config_setting_lookup_int(cfg, "usefontsize", &i))
1304 p->usefontsize = i != 0;
1305 if (config_setting_lookup_int(cfg, "fontsize", &i) && i > 0)
1306 p->fontsize = i;
1307 if (config_setting_lookup_int(cfg, "background", &i))
1308 p->background = i != 0;
1309 if (config_setting_lookup_string(cfg, "backgroundfile", &str))
1310 p->background_file = g_strdup(str);
1311 config_setting_lookup_int(cfg, "iconsize", &p->icon_size);
1312 if (config_setting_lookup_int(cfg, "loglevel", &configured_log_level))
17fab6e5 1313 {
17fab6e5
AG
1314 if (!log_level_set_on_commandline)
1315 log_level = configured_log_level;
a52c2257 1316 }
4542c20d 1317
9dd114c4 1318 panel_normalize_configuration(p);
239cb032 1319
9dd114c4 1320 return 1;
a52c2257
HJYP
1321}
1322
1323static int
17fab6e5 1324panel_parse_plugin(Panel *p, config_setting_t *cfg)
a52c2257 1325{
4bca3e51 1326 const char *type = NULL;
db449f6e 1327
a52c2257 1328 ENTER;
4bca3e51 1329 config_setting_lookup_string(cfg, "type", &type);
17fab6e5 1330 DBG("plug %s\n", type);
db449f6e 1331
17fab6e5 1332 if (!type || lxpanel_add_plugin(p, type, cfg, -1) == NULL) {
a52c2257
HJYP
1333 ERR( "lxpanel: can't load %s plugin\n", type);
1334 goto error;
1335 }
a52c2257 1336 RET(1);
db449f6e 1337
17fab6e5 1338error:
a52c2257 1339 RET(0);
a52c2257
HJYP
1340}
1341
17fab6e5 1342int panel_start( Panel *p )
a52c2257 1343{
17fab6e5
AG
1344 config_setting_t *list, *s;
1345 int i;
db449f6e 1346
a52c2257
HJYP
1347 /* parse global section */
1348 ENTER;
8110399f 1349
17fab6e5
AG
1350 list = config_setting_get_member(config_root_setting(p->config), "");
1351 if (!list || !panel_parse_global(p, config_setting_get_elem(list, 0)))
a52c2257
HJYP
1352 RET(0);
1353
9dd114c4 1354 panel_start_gui(p);
1355
17fab6e5
AG
1356 for (i = 1; (s = config_setting_get_elem(list, i)) != NULL; )
1357 if (strcmp(config_setting_get_name(s), "Plugin") == 0 &&
1358 panel_parse_plugin(p, s)) /* success on plugin start */
1359 i++;
1360 else /* remove invalid data from config */
1361 config_setting_remove_elem(list, i);
4542c20d
HJYP
1362
1363 /* update backgrond of panel and all plugins */
1364 panel_update_background( p );
9dd114c4 1365 return 1;
a52c2257
HJYP
1366}
1367
8110399f 1368void panel_destroy(Panel *p)
a52c2257
HJYP
1369{
1370 ENTER;
1371
2918994e 1372 if (p->pref_dialog != NULL)
1373 gtk_widget_destroy(p->pref_dialog);
1374 if (p->plugin_pref_dialog != NULL)
4ab8027b
AG
1375 /* just close the dialog, it will do all required cleanup */
1376 gtk_dialog_response(GTK_DIALOG(p->plugin_pref_dialog), GTK_RESPONSE_CLOSE);
2918994e 1377
1378 if (p->bg != NULL)
1379 {
1380 g_signal_handlers_disconnect_by_func(G_OBJECT(p->bg), on_root_bg_changed, p);
1381 g_object_unref(p->bg);
1382 }
1383
930af9fd
HJYP
1384 if( p->config_changed )
1385 panel_config_save( p );
17fab6e5 1386 config_destroy(p->config);
8c44345a 1387
4b93d81e 1388 if( p->topgwin )
17fab6e5
AG
1389 {
1390 gtk_window_group_remove_window( win_grp, GTK_WINDOW( p->topgwin ) );
4b93d81e 1391 gtk_widget_destroy(p->topgwin);
17fab6e5 1392 }
a52c2257 1393 g_free(p->workarea);
2de71c90 1394 g_free( p->background_file );
5297da29 1395 g_slist_free( p->system_menus );
a52c2257
HJYP
1396 gdk_flush();
1397 XFlush(GDK_DISPLAY());
1398 XSync(GDK_DISPLAY(), True);
8110399f
HJYP
1399
1400 g_free( p->name );
2918994e 1401 g_free(p);
a52c2257
HJYP
1402 RET();
1403}
1404
8110399f
HJYP
1405Panel* panel_new( const char* config_file, const char* config_name )
1406{
cf701cb7 1407 Panel* panel = NULL;
16fbda14 1408
17fab6e5 1409 if (G_LIKELY(config_file))
cf701cb7 1410 {
17fab6e5 1411 panel = panel_allocate();
20edd661 1412 panel->name = g_strdup(config_name);
17fab6e5
AG
1413 g_debug("starting panel from file %s",config_file);
1414 if (!config_read_file(panel->config, config_file) ||
1415 !panel_start(panel))
3e7b8eb7 1416 {
17fab6e5
AG
1417 ERR( "lxpanel: can't start panel\n");
1418 panel_destroy( panel );
1419 panel = NULL;
cf701cb7 1420 }
cf701cb7
HJYP
1421 }
1422 return panel;
8110399f 1423}
a52c2257 1424
0defe4b9 1425static void
a52c2257
HJYP
1426usage()
1427{
e7cb732b
HJYP
1428 g_print(_("lxpanel %s - lightweight GTK2+ panel for UNIX desktops\n"), version);
1429 g_print(_("Command line options:\n"));
1430 g_print(_(" --help -- print this help and exit\n"));
1431 g_print(_(" --version -- print version and exit\n"));
1432 g_print(_(" --log <number> -- set log level 0-5. 0 - none 5 - chatty\n"));
2918994e 1433// g_print(_(" --configure -- launch configuration utility\n"));
e7cb732b
HJYP
1434 g_print(_(" --profile name -- use specified profile\n"));
1435 g_print("\n");
1436 g_print(_(" -h -- same as --help\n"));
1437 g_print(_(" -p -- same as --profile\n"));
1438 g_print(_(" -v -- same as --version\n"));
2918994e 1439 // g_print(_(" -C -- same as --configure\n"));
b37c9aae 1440 g_print(_("\nVisit http://lxde.org/ for detail.\n\n"));
a52c2257
HJYP
1441}
1442
2918994e 1443int panel_handle_x_error(Display * d, XErrorEvent * ev)
a52c2257
HJYP
1444{
1445 char buf[256];
1446
a52c2257
HJYP
1447 if (log_level >= LOG_WARN) {
1448 XGetErrorText(GDK_DISPLAY(), ev->error_code, buf, 256);
1449 LOG(LOG_WARN, "lxpanel : X error: %s\n", buf);
1450 }
2918994e 1451 return 0; /* Ignored */
1452}
1453
1454int panel_handle_x_error_swallow_BadWindow_BadDrawable(Display * d, XErrorEvent * ev)
1455{
1456 if ((ev->error_code != BadWindow) && (ev->error_code != BadDrawable))
1457 panel_handle_x_error(d, ev);
1458 return 0; /* Ignored */
a52c2257
HJYP
1459}
1460
e68b47dc
JH
1461/* Lightweight lock related functions - X clipboard hacks */
1462
1463#define CLIPBOARD_NAME "LXPANEL_SELECTION"
1464
1465/*
1466 * clipboard_get_func - dummy get_func for gtk_clipboard_set_with_data ()
1467 */
1468static void
1469clipboard_get_func(
1470 GtkClipboard *clipboard G_GNUC_UNUSED,
1471 GtkSelectionData *selection_data G_GNUC_UNUSED,
1472 guint info G_GNUC_UNUSED,
1473 gpointer user_data_or_owner G_GNUC_UNUSED)
1474{
1475}
1476
1477/*
1478 * clipboard_clear_func - dummy clear_func for gtk_clipboard_set_with_data ()
1479 */
1480static void clipboard_clear_func(
1481 GtkClipboard *clipboard G_GNUC_UNUSED,
1482 gpointer user_data_or_owner G_GNUC_UNUSED)
1483{
1484}
1485
1486/*
1487 * Lightweight version for checking single instance.
1488 * Try and get the CLIPBOARD_NAME clipboard instead of using file manipulation.
1489 *
1490 * Returns TRUE if successfully retrieved and FALSE otherwise.
1491 */
16fb8c2e 1492static gboolean check_main_lock()
e68b47dc
JH
1493{
1494 static const GtkTargetEntry targets[] = { { CLIPBOARD_NAME, 0, 0 } };
1495 gboolean retval = FALSE;
1496 GtkClipboard *clipboard;
1497 Atom atom;
1498
1499 atom = gdk_x11_get_xatom_by_name(CLIPBOARD_NAME);
1500
1501 XGrabServer(GDK_DISPLAY());
1502
1503 if (XGetSelectionOwner(GDK_DISPLAY(), atom) != None)
1504 goto out;
1505
1506 clipboard = gtk_clipboard_get(gdk_atom_intern(CLIPBOARD_NAME, FALSE));
1507
1508 if (gtk_clipboard_set_with_data(clipboard, targets,
1509 G_N_ELEMENTS (targets),
1510 clipboard_get_func,
1511 clipboard_clear_func, NULL))
1512 retval = TRUE;
1513
1514out:
1515 XUngrabServer (GDK_DISPLAY ());
1516 gdk_flush ();
1517
1518 return retval;
1519}
1520#undef CLIPBOARD_NAME
1521
1f4bc3aa 1522static void _start_panels_from_dir(const char *panel_dir)
8110399f 1523{
1f4bc3aa
AG
1524 GDir* dir = g_dir_open( panel_dir, 0, NULL );
1525 const gchar* name;
cf701cb7 1526
1f4bc3aa
AG
1527 if( ! dir )
1528 {
1529 return;
1530 }
cf701cb7 1531
1f4bc3aa
AG
1532 while((name = g_dir_read_name(dir)) != NULL)
1533 {
1534 char* panel_config = g_build_filename( panel_dir, name, NULL );
1535 if (strchr(panel_config, '~') == NULL) /* Skip editor backup files in case user has hand edited in this directory */
cf701cb7 1536 {
1f4bc3aa
AG
1537 Panel* panel = panel_new( panel_config, name );
1538 if( panel )
1539 all_panels = g_slist_prepend( all_panels, panel );
cf701cb7 1540 }
1f4bc3aa 1541 g_free( panel_config );
cf701cb7 1542 }
1f4bc3aa
AG
1543 g_dir_close( dir );
1544}
1545
1546static gboolean start_all_panels( )
1547{
1548 char *panel_dir;
1549
1550 /* try user panels */
1551 panel_dir = _user_config_file_name("panels", NULL);
1552 _start_panels_from_dir(panel_dir);
1553 g_free(panel_dir);
1554 if (all_panels != NULL)
1555 return TRUE;
1556 /* else try XDG fallback */
1557 panel_dir = _system_config_file_name("panels");
1558 _start_panels_from_dir(panel_dir);
1559 g_free(panel_dir);
1560 if (all_panels != NULL)
1561 return TRUE;
1562 /* last try at old fallback for compatibility reasons */
1563 panel_dir = _old_system_config_file_name("panels");
1564 _start_panels_from_dir(panel_dir);
1565 g_free(panel_dir);
cf701cb7 1566 return all_panels != NULL;
8110399f
HJYP
1567}
1568
cf701cb7
HJYP
1569void load_global_config();
1570void free_global_config();
1571
1f4bc3aa
AG
1572static void _ensure_user_config_dirs(void)
1573{
1574 char *dir = g_build_filename(g_get_user_config_dir(), "lxpanel", cprofile,
1575 "panels", NULL);
1576
1577 /* make sure the private profile and panels dir exists */
1578 g_mkdir_with_parents(dir, 0700);
1579 g_free(dir);
1580}
1581
8110399f 1582int main(int argc, char *argv[], char *env[])
a52c2257
HJYP
1583{
1584 int i;
9df6826f 1585 const char* desktop_name;
f277dbb7 1586
a52c2257 1587 setlocale(LC_CTYPE, "");
f277dbb7 1588
1d434622 1589 g_thread_init(NULL);
74bf5dbf
AG
1590/* gdk_threads_init();
1591 gdk_threads_enter(); */
1d434622 1592
a52c2257
HJYP
1593 gtk_init(&argc, &argv);
1594
1595#ifdef ENABLE_NLS
1596 bindtextdomain ( GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR );
1597 bind_textdomain_codeset ( GETTEXT_PACKAGE, "UTF-8" );
1598 textdomain ( GETTEXT_PACKAGE );
1599#endif
1600
1601 XSetLocaleModifiers("");
2918994e 1602 XSetErrorHandler((XErrorHandler) panel_handle_x_error);
8110399f 1603
a52c2257 1604 resolve_atoms();
8110399f 1605
9df6826f
HJYP
1606 desktop_name = g_getenv("XDG_CURRENT_DESKTOP");
1607 is_in_lxde = desktop_name && (0 == strcmp(desktop_name, "LXDE"));
95095259 1608
a52c2257
HJYP
1609 for (i = 1; i < argc; i++) {
1610 if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
1611 usage();
1612 exit(0);
1613 } else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
1614 printf("lxpanel %s\n", version);
1615 exit(0);
1616 } else if (!strcmp(argv[i], "--log")) {
1617 i++;
1618 if (i == argc) {
1619 ERR( "lxpanel: missing log level\n");
1620 usage();
1621 exit(1);
1622 } else {
1623 log_level = atoi(argv[i]);
82ca42af 1624 log_level_set_on_commandline = true;
a52c2257
HJYP
1625 }
1626 } else if (!strcmp(argv[i], "--configure") || !strcmp(argv[i], "-C")) {
1627 config = 1;
1628 } else if (!strcmp(argv[i], "--profile") || !strcmp(argv[i], "-p")) {
1629 i++;
1630 if (i == argc) {
1631 ERR( "lxpanel: missing profile name\n");
1632 usage();
1633 exit(1);
1634 } else {
1635 cprofile = g_strdup(argv[i]);
1636 }
1637 } else {
1638 printf("lxpanel: unknown option - %s\n", argv[i]);
1639 usage();
1640 exit(1);
1641 }
1642 }
f277dbb7 1643
8110399f 1644 /* Check for duplicated lxpanel instances */
e68b47dc 1645 if (!check_main_lock() && !config) {
e7a42ecf 1646 printf("There is already an instance of LXPanel. Now to exit\n");
e68b47dc
JH
1647 exit(1);
1648 }
1649
1f4bc3aa
AG
1650 _ensure_user_config_dirs();
1651
730e3f7b 1652 /* Add our own icons to the search path of icon theme */
0b806437 1653 gtk_icon_theme_append_search_path( gtk_icon_theme_get_default(), PACKAGE_DATA_DIR "/images" );
730e3f7b 1654
cf701cb7 1655 fbev = fb_ev_new();
e2957bd2 1656 win_grp = gtk_window_group_new();
22242ed4 1657
f7cb330e
HJYP
1658restart:
1659 is_restarting = FALSE;
1660
17fab6e5
AG
1661 /* init LibFM */
1662 fm_gtk_init(NULL);
1663
1664 /* prepare modules data */
1665 _prepare_modules();
1666
cf701cb7
HJYP
1667 load_global_config();
1668
1d9dc649
HJYP
1669 /* NOTE: StructureNotifyMask is required by XRandR
1670 * See init_randr_support() in gdkscreen-x11.c of gtk+ for detail.
1671 */
0f83fb71 1672 gdk_window_set_events(gdk_get_default_root_window(), GDK_STRUCTURE_MASK |
1673 GDK_SUBSTRUCTURE_MASK | GDK_PROPERTY_CHANGE_MASK);
cf701cb7 1674 gdk_window_add_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, NULL);
8110399f 1675
cf701cb7
HJYP
1676 if( G_UNLIKELY( ! start_all_panels() ) )
1677 g_warning( "Config files are not found.\n" );
8110399f
HJYP
1678/*
1679 * FIXME: configure??
6a6ad54e
HJYP
1680 if (config)
1681 configure();
8110399f 1682*/
6a6ad54e 1683 gtk_main();
8110399f 1684
cf701cb7
HJYP
1685 XSelectInput (GDK_DISPLAY(), GDK_ROOT_WINDOW(), NoEventMask);
1686 gdk_window_remove_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, NULL);
1687
8110399f
HJYP
1688 /* destroy all panels */
1689 g_slist_foreach( all_panels, (GFunc) panel_destroy, NULL );
2a26c0d8
HJYP
1690 g_slist_free( all_panels );
1691 all_panels = NULL;
6a6ad54e 1692 g_free( cfgfile );
5541b8d2 1693
cf701cb7
HJYP
1694 free_global_config();
1695
17fab6e5
AG
1696 _unload_modules();
1697 fm_gtk_finalize();
1698
f7cb330e
HJYP
1699 if( is_restarting )
1700 goto restart;
1701
74bf5dbf 1702 /* gdk_threads_leave(); */
3e71f8af 1703
e2957bd2 1704 g_object_unref(win_grp);
22242ed4
HJYP
1705 g_object_unref(fbev);
1706
5541b8d2 1707 return 0;
a52c2257 1708}
9c338caf 1709
4718ca02 1710GtkOrientation panel_get_orientation(Panel *panel)
9c338caf 1711{
cfde283a 1712 return panel->orientation;
9c338caf
AG
1713}
1714
4718ca02 1715gint panel_get_icon_size(Panel *panel)
9c338caf
AG
1716{
1717 return panel->icon_size;
1718}
1719
4718ca02 1720gint panel_get_height(Panel *panel)
9c338caf
AG
1721{
1722 return panel->height;
1723}
1724
4718ca02 1725GtkWindow *panel_get_toplevel_window(Panel *panel)
9c338caf 1726{
4ab8027b 1727 return GTK_WINDOW(panel->topgwin);
9c338caf
AG
1728}
1729
4718ca02
AG
1730Window panel_get_xwindow(Panel *panel)
1731{
1732 return panel->topxwin;
1733}
1734
1735gint panel_get_monitor(Panel *panel)
1736{
1737 return panel->monitor;
1738}
1739
1740GtkStyle *panel_get_defstyle(Panel *panel)
9c338caf
AG
1741{
1742 return panel->defstyle;
1743}
1744
4718ca02 1745GtkIconTheme *panel_get_icon_theme(Panel *panel)
9c338caf
AG
1746{
1747 return panel->icon_theme;
1748}
1749
4718ca02
AG
1750gboolean panel_is_at_bottom(Panel *panel)
1751{
1752 return panel->edge == EDGE_BOTTOM;
1753}
1754
1755GtkWidget *panel_box_new(Panel *panel, gboolean homogeneous, gint spacing)
9c338caf
AG
1756{
1757 return panel->my_box_new(homogeneous, spacing);
1758}
1759
4718ca02 1760GtkWidget *panel_separator_new(Panel *panel)
9c338caf
AG
1761{
1762 return panel->my_separator_new();
1763}
10d93053
AG
1764
1765gboolean _class_is_present(LXPanelPluginInit *init)
1766{
1767 GSList *sl;
1768
1769 for (sl = all_panels; sl; sl = sl->next )
1770 {
1771 Panel *panel = (Panel*)sl->data;
1772 GList *plugins, *p;
1773
1774 plugins = gtk_container_get_children(GTK_CONTAINER(panel->box));
1775 for (p = plugins; p; p = p->next)
1776 if (PLUGIN_CLASS(p->data) == init)
1777 {
1778 g_list_free(plugins);
1779 return TRUE;
1780 }
1781 g_list_free(plugins);
1782 }
1783 return FALSE;
1784}