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