Policy change on placement of Close popup menu item
[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>
a52c2257
HJYP
33
34#include "plugin.h"
35#include "panel.h"
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
3e7b8eb7 55static int panel_start( Panel *p, char **fp );
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
9dd114c4 61/* Allocate and initialize new Panel structure. */
62static Panel* panel_allocate(void)
63{
2918994e 64 Panel* p = g_new0(Panel, 1);
9dd114c4 65 p->allign = ALLIGN_CENTER;
66 p->edge = EDGE_NONE;
67 p->widthtype = WIDTH_PERCENT;
68 p->width = 100;
69 p->heighttype = HEIGHT_PIXEL;
70 p->height = PANEL_HEIGHT_DEFAULT;
71 p->setdocktype = 1;
72 p->setstrut = 1;
73 p->round_corners = 0;
74 p->autohide = 0;
75 p->visible = TRUE;
76 p->height_when_hidden = 2;
77 p->transparent = 0;
78 p->alpha = 127;
79 p->tintcolor = 0xFFFFFFFF;
80 p->usefontcolor = 0;
81 p->fontcolor = 0x00000000;
82 p->spacing = 0;
8f9e6256 83 p->icon_size = PANEL_ICON_SIZE;
9dd114c4 84 return p;
85}
86
87/* Normalize panel configuration after load from file or reconfiguration. */
88static void panel_normalize_configuration(Panel* p)
89{
2918994e 90 panel_set_panel_configuration_changed( p );
9dd114c4 91 if (p->width < 0)
92 p->width = 100;
93 if (p->widthtype == WIDTH_PERCENT && p->width > 100)
94 p->width = 100;
95 p->heighttype = HEIGHT_PIXEL;
96 if (p->heighttype == HEIGHT_PIXEL) {
97 if (p->height < PANEL_HEIGHT_MIN)
98 p->height = PANEL_HEIGHT_MIN;
99 else if (p->height > PANEL_HEIGHT_MAX)
100 p->height = PANEL_HEIGHT_MAX;
101 }
102 if (p->background)
103 p->transparent = 0;
104}
105
a52c2257
HJYP
106/****************************************************
107 * panel's handlers for WM events *
108 ****************************************************/
a52c2257 109
22242ed4 110void panel_set_wm_strut(Panel *p)
a52c2257 111{
d1d43629 112 int index;
113 gulong strut_size;
114 gulong strut_lower;
115 gulong strut_upper;
a52c2257 116
d1d43629 117 /* Dispatch on edge to set up strut parameters. */
118 switch (p->edge)
bee4c26e 119 {
d1d43629 120 case EDGE_LEFT:
121 index = 0;
122 strut_size = p->aw;
123 strut_lower = p->ay;
124 strut_upper = p->ay + p->ah;
125 break;
126 case EDGE_RIGHT:
127 index = 1;
128 strut_size = p->aw;
129 strut_lower = p->ay;
130 strut_upper = p->ay + p->ah;
131 break;
132 case EDGE_TOP:
133 index = 2;
134 strut_size = p->ah;
135 strut_lower = p->ax;
136 strut_upper = p->ax + p->aw;
137 break;
138 case EDGE_BOTTOM:
139 index = 3;
140 strut_size = p->ah;
141 strut_lower = p->ax;
142 strut_upper = p->ax + p->aw;
143 break;
144 default:
145 return;
bee4c26e
HJYP
146 }
147
176fb687 148 /* Handle autohide case. EWMH recommends having the strut be the minimized size. */
149 if ( ! p->visible)
150 strut_size = p->height_when_hidden;
151
d1d43629 152 /* Set up strut value in property format. */
153 gulong desired_strut[12];
154 memset(desired_strut, 0, sizeof(desired_strut));
155 if (p->setstrut)
156 {
157 desired_strut[index] = strut_size;
158 desired_strut[4 + index * 2] = strut_lower;
159 desired_strut[5 + index * 2] = strut_upper;
160 }
161 else
162 {
163 strut_size = 0;
164 strut_lower = 0;
165 strut_upper = 0;
bee4c26e 166 }
bee4c26e 167
d1d43629 168 /* If strut value changed, set the property value on the panel window.
169 * This avoids property change traffic when the panel layout is recalculated but strut geometry hasn't changed. */
170 if ((GTK_WIDGET_MAPPED(p->topgwin))
171 && ((p->strut_size != strut_size) || (p->strut_lower != strut_lower) || (p->strut_upper != strut_upper)))
172 {
173 p->strut_size = strut_size;
174 p->strut_lower = strut_lower;
175 p->strut_upper = strut_upper;
a52c2257 176
d1d43629 177 /* If window manager supports STRUT_PARTIAL, it will ignore STRUT.
178 * Set STRUT also for window managers that do not support STRUT_PARTIAL. */
179 if (strut_size != 0)
180 {
181 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT_PARTIAL,
182 XA_CARDINAL, 32, PropModeReplace, (unsigned char *) desired_strut, 12);
183 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT,
184 XA_CARDINAL, 32, PropModeReplace, (unsigned char *) desired_strut, 4);
185 }
186 else
187 {
188 XDeleteProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT);
189 XDeleteProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT_PARTIAL);
190 }
191 }
a52c2257
HJYP
192}
193
e996608e 194/* defined in plugins/menu.c */
8c44345a 195gboolean show_system_menu( gpointer system_menu );
e996608e 196
3e7b8eb7
HJYP
197/* defined in configurator.c */
198void panel_configure(Panel* p, int sel_page );
9dd114c4 199gboolean panel_edge_available(Panel* p, int edge);
3e7b8eb7 200
e996608e 201/* built-in commands, defined in configurator.c */
77886b88
HJYP
202void restart(void);
203void gtk_run(void);
e3b89f43 204void panel_destroy(Panel *p);
77886b88 205
d18c9409 206static void process_client_msg ( XClientMessageEvent* ev )
77886b88 207{
8c44345a 208 int cmd = ev->data.b[0];
77886b88
HJYP
209 switch( cmd )
210 {
ace2a572 211#ifndef DISABLE_MENU
77886b88 212 case LXPANEL_CMD_SYS_MENU:
d18c9409
HJYP
213 {
214 GSList* l;
215 for( l = all_panels; l; l = l->next )
8c44345a 216 {
d18c9409
HJYP
217 Panel* p = (Panel*)l->data;
218 if( p->system_menus )
219 {
220 /* show_system_menu( p->system_menus->data ); */
221 /* FIXME: I've no idea why this doesn't work without timeout
222 under some WMs, like icewm. */
223 g_timeout_add( 200, (GSourceFunc)show_system_menu,
224 p->system_menus->data );
225 }
8c44345a 226 }
77886b88 227 break;
d18c9409 228 }
ace2a572
JH
229#endif
230#ifndef DISABLE_MENU
77886b88
HJYP
231 case LXPANEL_CMD_RUN:
232 gtk_run();
233 break;
ace2a572 234#endif
77886b88 235 case LXPANEL_CMD_CONFIG:
8110399f 236 //FIXME: configure();
77886b88
HJYP
237 break;
238 case LXPANEL_CMD_RESTART:
239 restart();
240 break;
241 case LXPANEL_CMD_EXIT:
242 gtk_main_quit();
243 break;
244 }
245}
246
a52c2257 247static GdkFilterReturn
cf701cb7 248panel_event_filter(GdkXEvent *xevent, GdkEvent *event, gpointer not_used)
a52c2257
HJYP
249{
250 Atom at;
251 Window win;
252 XEvent *ev = (XEvent *) xevent;
253
254 ENTER;
255 DBG("win = 0x%x\n", ev->xproperty.window);
77886b88
HJYP
256 if (ev->type != PropertyNotify ) {
257 /* private client message from lxpanelctl */
258 if( ev->type == ClientMessage && ev->xproperty.atom == a_LXPANEL_CMD )
259 {
d18c9409 260 process_client_msg( (XClientMessageEvent*)ev );
77886b88 261 }
22242ed4
HJYP
262 else if( ev->type == DestroyNotify )
263 {
cf701cb7 264 fb_ev_emit_destroy( fbev, ((XDestroyWindowEvent*)ev)->window );
22242ed4 265 }
a52c2257 266 RET(GDK_FILTER_CONTINUE);
24053345 267 }
77886b88 268
a52c2257
HJYP
269 at = ev->xproperty.atom;
270 win = ev->xproperty.window;
a52c2257 271 if (win == GDK_ROOT_WINDOW()) {
cf701cb7
HJYP
272 if (at == a_NET_CLIENT_LIST) {
273 fb_ev_emit(fbev, EV_CLIENT_LIST);
274 } else if (at == a_NET_CURRENT_DESKTOP) {
275 GSList* l;
276 for( l = all_panels; l; l = l->next )
277 ((Panel*)l->data)->curdesk = get_net_current_desktop();
22242ed4 278 fb_ev_emit(fbev, EV_CURRENT_DESKTOP);
cf701cb7
HJYP
279 } else if (at == a_NET_NUMBER_OF_DESKTOPS) {
280 GSList* l;
281 for( l = all_panels; l; l = l->next )
282 ((Panel*)l->data)->desknum = get_net_number_of_desktops();
22242ed4 283 fb_ev_emit(fbev, EV_NUMBER_OF_DESKTOPS);
cf701cb7 284 } else if (at == a_NET_DESKTOP_NAMES) {
22242ed4 285 fb_ev_emit(fbev, EV_DESKTOP_NAMES);
a52c2257 286 } else if (at == a_NET_ACTIVE_WINDOW) {
22242ed4 287 fb_ev_emit(fbev, EV_ACTIVE_WINDOW );
cf701cb7 288 } else if (at == a_NET_CLIENT_LIST_STACKING) {
22242ed4 289 fb_ev_emit(fbev, EV_CLIENT_LIST_STACKING);
a52c2257 290 } else if (at == a_XROOTPMAP_ID) {
cf701cb7
HJYP
291 GSList* l;
292 for( l = all_panels; l; l = l->next )
293 {
294 Panel* p = (Panel*)l->data;
295 if (p->transparent) {
296 fb_bg_notify_changed_bg(p->bg);
297 }
298 }
299 } else if (at == a_NET_WORKAREA) {
300 GSList* l;
301 for( l = all_panels; l; l = l->next )
302 {
303 Panel* p = (Panel*)l->data;
304 g_free( p->workarea );
305 p->workarea = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_WORKAREA, XA_CARDINAL, &p->wa_len);
1d9dc649 306 /* print_wmdata(p); */
a52c2257 307 }
a52c2257 308 } else
cf701cb7
HJYP
309 return GDK_FILTER_CONTINUE;
310
311 return GDK_FILTER_REMOVE;
a52c2257 312 }
cf701cb7 313 return GDK_FILTER_CONTINUE;
a52c2257
HJYP
314}
315
316/****************************************************
317 * panel's handlers for GTK events *
318 ****************************************************/
319
bee4c26e 320
a52c2257
HJYP
321static gint
322panel_delete_event(GtkWidget * widget, GdkEvent * event, gpointer data)
323{
324 ENTER;
325 RET(FALSE);
326}
327
328static gint
329panel_destroy_event(GtkWidget * widget, GdkEvent * event, gpointer data)
330{
22242ed4 331 //Panel *p = (Panel *) data;
a52c2257
HJYP
332 //if (!p->self_destroy)
333 gtk_main_quit();
334 RET(FALSE);
335}
336
4542c20d 337static void
22242ed4 338on_root_bg_changed(FbBg *bg, Panel* p)
4542c20d
HJYP
339{
340 panel_update_background( p );
341}
342
2918994e 343void panel_determine_background_pixmap(Panel * p, GtkWidget * widget, GdkWindow * window)
4542c20d 344{
2918994e 345 GdkPixmap * pixmap = NULL;
4542c20d 346
2918994e 347 /* Free p->bg if it is not going to be used. */
348 if (( ! p->transparent) && (p->bg != NULL))
349 {
350 g_signal_handlers_disconnect_by_func(G_OBJECT(p->bg), on_root_bg_changed, p);
351 g_object_unref(p->bg);
352 p->bg = NULL;
353 }
4542c20d 354
2918994e 355 if (p->background)
356 {
357 /* User specified background pixmap. */
358 if (p->background_file != NULL)
359 pixmap = fb_bg_get_pix_from_file(widget, p->background_file);
4542c20d 360 }
2918994e 361
362 else if (p->transparent)
4542c20d 363 {
2918994e 364 /* Transparent. Determine the appropriate value from the root pixmap. */
365 if (p->bg == NULL)
4542c20d 366 {
2918994e 367 p->bg = fb_bg_get_for_display();
368 g_signal_connect(G_OBJECT(p->bg), "changed", G_CALLBACK(on_root_bg_changed), p);
4542c20d 369 }
2918994e 370 pixmap = fb_bg_get_xroot_pix_for_win(p->bg, widget);
371 if ((pixmap != NULL) && (pixmap != GDK_NO_BG) && (p->alpha != 0))
372 fb_bg_composite(pixmap, widget->style->black_gc, p->tintcolor, p->alpha);
4542c20d
HJYP
373 }
374
2918994e 375 if (pixmap != NULL)
4542c20d 376 {
2918994e 377 gtk_widget_set_app_paintable(widget, TRUE );
378 gdk_window_set_back_pixmap(window, pixmap, FALSE);
379 g_object_unref(pixmap);
4542c20d
HJYP
380 }
381 else
2918994e 382 gtk_widget_set_app_paintable(widget, FALSE);
383}
4542c20d 384
2918994e 385/* Update the background of the entire panel.
386 * This function should only be called after the panel has been realized. */
387void panel_update_background(Panel * p)
388{
389 /* Redraw the top level widget. */
390 panel_determine_background_pixmap(p, p->topgwin, p->topgwin->window);
391 gdk_window_clear(p->topgwin->window);
392 gtk_widget_queue_draw(p->topgwin);
393 if (gtk_events_pending()) gtk_main_iteration();
394
395 /* Loop over all plugins redrawing each plugin. */
396 GList * l;
397 for (l = p->plugins; l != NULL; l = l->next)
4542c20d 398 {
2918994e 399 Plugin * pl = (Plugin *) l->data;
400 plugin_set_background(pl, p);
4542c20d
HJYP
401 }
402
4542c20d 403}
a52c2257 404
84fc1d55
HJYP
405static gboolean delay_update_background( Panel* p )
406{
6589616e
JH
407 /* Panel could be destroyed while background update scheduled */
408 if ( p->topgwin && GTK_WIDGET_REALIZED ( p->topgwin ) ) {
409 gdk_display_sync( gtk_widget_get_display(p->topgwin) );
410 panel_update_background( p );
411 }
412
cf701cb7 413 return FALSE;
84fc1d55
HJYP
414}
415
416static void
f2d54481
HJYP
417panel_realize(GtkWidget *widget, Panel *p)
418{
e3b89f43 419 g_idle_add_full( G_PRIORITY_LOW,
420 (GSourceFunc)delay_update_background, p, NULL );
f2d54481
HJYP
421}
422
423static void
84fc1d55
HJYP
424panel_style_set(GtkWidget *widget, GtkStyle* prev, Panel *p)
425{
0987f648 426 /* FIXME: This dirty hack is used to fix the background of systray... */
cf701cb7 427 if( GTK_WIDGET_REALIZED( widget ) )
e3b89f43 428 g_idle_add_full( G_PRIORITY_LOW,
429 (GSourceFunc)delay_update_background, p, NULL );
84fc1d55 430}
4542c20d 431
a52c2257 432static gint
22242ed4 433panel_size_req(GtkWidget *widget, GtkRequisition *req, Panel *p)
a52c2257
HJYP
434{
435 ENTER;
4542c20d 436
a52c2257
HJYP
437 if (p->widthtype == WIDTH_REQUEST)
438 p->width = (p->orientation == ORIENT_HORIZ) ? req->width : req->height;
439 if (p->heighttype == HEIGHT_REQUEST)
440 p->height = (p->orientation == ORIENT_HORIZ) ? req->height : req->width;
441 calculate_position(p);
442 req->width = p->aw;
443 req->height = p->ah;
4542c20d 444
a52c2257
HJYP
445 RET( TRUE );
446}
447
448static gint
22242ed4 449panel_size_alloc(GtkWidget *widget, GtkAllocation *a, Panel *p)
a52c2257
HJYP
450{
451 ENTER;
a52c2257
HJYP
452 if (p->widthtype == WIDTH_REQUEST)
453 p->width = (p->orientation == ORIENT_HORIZ) ? a->width : a->height;
454 if (p->heighttype == HEIGHT_REQUEST)
455 p->height = (p->orientation == ORIENT_HORIZ) ? a->height : a->width;
456 calculate_position(p);
4542c20d 457
a52c2257 458 if (a->width == p->aw && a->height == p->ah && a->x == p->ax && a->y == p ->ay) {
a52c2257
HJYP
459 RET(TRUE);
460 }
461
462 gtk_window_move(GTK_WINDOW(p->topgwin), p->ax, p->ay);
bee4c26e 463 panel_set_wm_strut(p);
a52c2257
HJYP
464 RET(TRUE);
465}
466
a52c2257 467static gboolean
22242ed4 468panel_configure_event (GtkWidget *widget, GdkEventConfigure *e, Panel *p)
a52c2257
HJYP
469{
470 ENTER;
471 if (e->width == p->cw && e->height == p->ch && e->x == p->cx && e->y == p->cy)
472 RET(TRUE);
473 p->cw = e->width;
474 p->ch = e->height;
475 p->cx = e->x;
476 p->cy = e->y;
4542c20d 477
a52c2257
HJYP
478 if (p->transparent)
479 fb_bg_notify_changed_bg(p->bg);
bee4c26e 480
4542c20d 481 RET(FALSE);
a52c2257
HJYP
482}
483
fddae119
FC
484static gint
485panel_popupmenu_configure(GtkWidget *widget, gpointer user_data)
486{
4b93d81e
HJYP
487 panel_configure( (Panel*)user_data, 0 );
488 return TRUE;
fddae119
FC
489}
490
9dd114c4 491/* Handler for "button_press_event" signal with Panel as parameter. */
492static gboolean panel_button_press_event_with_panel(GtkWidget *widget, GdkEventButton *event, Panel *panel)
fddae119 493{
9dd114c4 494 if (event->button == 3) /* right button */
495 {
496 GtkMenu* popup = (GtkMenu*) lxpanel_get_panel_menu(panel, NULL, FALSE);
497 gtk_menu_popup(popup, NULL, NULL, NULL, NULL, event->button, event->time);
498 return TRUE;
499 }
fddae119
FC
500 return FALSE;
501}
502
cf701cb7
HJYP
503static void panel_popupmenu_config_plugin( GtkMenuItem* item, Plugin* plugin )
504{
e3b89f43 505 plugin->class->config( plugin, GTK_WINDOW(plugin->panel->topgwin) );
930af9fd
HJYP
506
507 /* FIXME: this should be more elegant */
508 plugin->panel->config_changed = TRUE;
cf701cb7
HJYP
509}
510
4b93d81e
HJYP
511static void panel_popupmenu_add_item( GtkMenuItem* item, Panel* panel )
512{
513 /* panel_add_plugin( panel, panel->topgwin ); */
8d8b66da 514 panel_configure( panel, 2 );
cf701cb7
HJYP
515}
516
517static void panel_popupmenu_remove_item( GtkMenuItem* item, Plugin* plugin )
518{
519 Panel* panel = plugin->panel;
520 panel->plugins = g_list_remove( panel->plugins, plugin );
2918994e 521 plugin_delete(plugin);
4b93d81e 522 panel_config_save( plugin->panel );
cf701cb7
HJYP
523}
524
3e7b8eb7
HJYP
525/* FIXME: Potentially we can support multiple panels at the same edge,
526 * but currently this cannot be done due to some positioning problems. */
527static char* gen_panel_name( int edge )
528{
529 const char* edge_str = num2str( edge_pair, edge, "" );
530 char* name = NULL;
531 char* dir = get_config_file( cprofile, "panels", FALSE );
532 int i;
533 for( i = 0; i < G_MAXINT; ++i )
534 {
535 char* f;
536 if( G_LIKELY( i > 0 ) )
537 name = g_strdup_printf( "%s%d", edge_str, i );
538 else
539 name = g_strdup( edge_str );
540 f = g_build_filename( dir, name, NULL );
541 if( ! g_file_test( f, G_FILE_TEST_EXISTS ) )
542 {
543 g_free( f );
544 break;
545 }
546 g_free( name );
547 g_free( f );
548 }
549 g_free( dir );
550 return name;
551}
552
9dd114c4 553static void try_allocate_edge(Panel* p, int edge)
554{
555 if ((p->edge == EDGE_NONE) && (panel_edge_available(p, edge)))
556 p->edge = edge;
557}
558
3e7b8eb7
HJYP
559/* FIXME: Potentially we can support multiple panels at the same edge,
560 * but currently this cannot be done due to some positioning problems. */
cf701cb7
HJYP
561static void panel_popupmenu_create_panel( GtkMenuItem* item, Panel* panel )
562{
9dd114c4 563 Panel* new_panel = panel_allocate();
564
565 /* Allocate the edge. */
566 try_allocate_edge(new_panel, EDGE_BOTTOM);
567 try_allocate_edge(new_panel, EDGE_TOP);
568 try_allocate_edge(new_panel, EDGE_LEFT);
569 try_allocate_edge(new_panel, EDGE_RIGHT);
570 new_panel->name = gen_panel_name(new_panel->edge);
571
572 panel_configure(new_panel, 0);
573 panel_normalize_configuration(new_panel);
574 panel_start_gui(new_panel);
575 gtk_widget_show_all(new_panel->topgwin);
576
577 panel_config_save(new_panel);
578 all_panels = g_slist_prepend(all_panels, new_panel);
cf701cb7
HJYP
579}
580
581static void panel_popupmenu_delete_panel( GtkMenuItem* item, Panel* panel )
582{
583 GtkWidget* dlg;
584 gboolean ok;
e3b89f43 585 dlg = gtk_message_dialog_new_with_markup( GTK_WINDOW(panel->topgwin),
cf701cb7
HJYP
586 GTK_DIALOG_MODAL,
587 GTK_MESSAGE_QUESTION,
588 GTK_BUTTONS_OK_CANCEL,
589 _("Really delete this panel?\n<b>Warning: This can not be recovered.</b>") );
d1d43629 590 panel_apply_icon(GTK_WINDOW(dlg));
cf701cb7
HJYP
591 gtk_window_set_title( (GtkWindow*)dlg, _("Confirm") );
592 ok = ( gtk_dialog_run( (GtkDialog*)dlg ) == GTK_RESPONSE_OK );
593 gtk_widget_destroy( dlg );
594 if( ok )
595 {
4b93d81e 596 gchar *fname, *dir;
cf701cb7 597 all_panels = g_slist_remove( all_panels, panel );
4b93d81e
HJYP
598
599 /* delete the config file of this panel */
600 dir = get_config_file( cprofile, "panels", FALSE );
601 fname = g_build_filename( dir, panel->name, NULL );
602 g_free( dir );
603 g_unlink( fname );
930af9fd 604 panel->config_changed = 0;
cf701cb7
HJYP
605 panel_destroy( panel );
606 }
607}
608
e7a42ecf 609static void panel_popupmenu_about( GtkMenuItem* item, Panel* panel )
610{
611 GtkWidget *about;
612 const gchar* authors[] = {
613 "Hong Jen Yee (PCMan) <pcman.tw@gmail.com>",
614 "Jim Huang <jserv.tw@gmail.com>",
615 "Greg McNew <gmcnew@gmail.com> (battery plugin)",
616 "Fred Chien <cfsghost@gmail.com>",
617 "Daniel Kesler <kesler.daniel@gmail.com>",
618 "Juergen Hoetzel <juergen@archlinux.org>",
2918994e 619 "Marty Jack <martyj19@comcast.net>",
e7a42ecf 620 NULL
621 };
622 /* TRANSLATORS: Replace this string with your names, one name per line. */
623 gchar *translators = _( "translator-credits" );
624
625 about = gtk_about_dialog_new();
626 panel_apply_icon(GTK_WINDOW(about));
627 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about), VERSION);
628 gtk_about_dialog_set_name(GTK_ABOUT_DIALOG(about), _("LXPanel"));
629 gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(about), gdk_pixbuf_new_from_file(PACKAGE_DATA_DIR "/lxpanel/images/my-computer.png", NULL));
630 gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(about), _("Copyright (C) 2008-2009"));
631 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about), _( "Desktop panel for LXDE project"));
632 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.");
633 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about), "http://lxde.org/");
634 gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(about), authors);
635 gtk_about_dialog_set_translator_credits(GTK_ABOUT_DIALOG(about), translators);
636 gtk_dialog_run(GTK_DIALOG(about));
637 gtk_widget_destroy(about);
638}
639
640void panel_apply_icon( GtkWindow *w )
641{
642 gtk_window_set_icon_from_file(w, PACKAGE_DATA_DIR "/lxpanel/images/my-computer.png", NULL);
643}
644
9dd114c4 645GtkMenu* lxpanel_get_panel_menu( Panel* panel, Plugin* plugin, gboolean use_sub_menu )
cf701cb7 646{
e3b89f43 647 GtkWidget *menu_item, *img;
648 GtkMenu *ret,*menu;
649
cf701cb7 650 char* tmp;
e3b89f43 651 ret = menu = GTK_MENU(gtk_menu_new());
cf701cb7 652
4b93d81e
HJYP
653 img = gtk_image_new_from_stock( GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU );
654 menu_item = gtk_image_menu_item_new_with_label(_("Add / Remove Panel Items"));
655 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
656 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
657 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_add_item), panel );
cf701cb7
HJYP
658
659 if( plugin )
660 {
661 img = gtk_image_new_from_stock( GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU );
662 tmp = g_strdup_printf( _("Remove \"%s\" From Panel"), _(plugin->class->name) );
663 menu_item = gtk_image_menu_item_new_with_label( tmp );
664 g_free( tmp );
665 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
666 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
667 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_remove_item), plugin );
668 }
669
670 menu_item = gtk_separator_menu_item_new();
671 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
672
4b93d81e
HJYP
673 img = gtk_image_new_from_stock( GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU );
674 menu_item = gtk_image_menu_item_new_with_label(_("Panel Settings"));
675 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
676 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
677 g_signal_connect(G_OBJECT(menu_item), "activate", G_CALLBACK(panel_popupmenu_configure), panel );
678
cf701cb7
HJYP
679 img = gtk_image_new_from_stock( GTK_STOCK_NEW, GTK_ICON_SIZE_MENU );
680 menu_item = gtk_image_menu_item_new_with_label(_("Create New Panel"));
681 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
682 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
683 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_create_panel), panel );
3e7b8eb7
HJYP
684 /* FIXME: Potentially we can support multiple panels at the same edge,
685 * but currently this cannot be done due to some positioning problems. */
686 /* currently, disable the option when there are already four panels */
687 if( g_slist_length( all_panels ) >= 4 )
688 gtk_widget_set_sensitive( menu_item, FALSE );
cf701cb7
HJYP
689
690 img = gtk_image_new_from_stock( GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU );
691 menu_item = gtk_image_menu_item_new_with_label(_("Delete This Panel"));
692 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
693 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
694 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_delete_panel), panel );
695 if( ! all_panels->next ) /* if this is the only panel */
696 gtk_widget_set_sensitive( menu_item, FALSE );
697
e7a42ecf 698 menu_item = gtk_separator_menu_item_new();
699 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
700
701 img = gtk_image_new_from_stock( GTK_STOCK_ABOUT, GTK_ICON_SIZE_MENU );
702 menu_item = gtk_image_menu_item_new_with_label(_("About"));
703 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
704 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
705 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_about), panel );
706
cf701cb7
HJYP
707 if( use_sub_menu )
708 {
e3b89f43 709 ret = GTK_MENU(gtk_menu_new());
cf701cb7
HJYP
710 menu_item = gtk_image_menu_item_new_with_label(_("Panel"));
711 gtk_menu_shell_append(GTK_MENU_SHELL(ret), menu_item);
e3b89f43 712 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), GTK_WIDGET(menu) );
cf701cb7 713
e3b89f43 714 gtk_widget_show_all(GTK_WIDGET(ret));
cf701cb7
HJYP
715 }
716
3e7b8eb7
HJYP
717 if( plugin )
718 {
719 menu_item = gtk_separator_menu_item_new();
720 gtk_menu_shell_prepend(GTK_MENU_SHELL(ret), menu_item);
721
722 img = gtk_image_new_from_stock( GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU );
723 tmp = g_strdup_printf( _("\"%s\" Settings"), _(plugin->class->name) );
724 menu_item = gtk_image_menu_item_new_with_label( tmp );
725 g_free( tmp );
726 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
727 gtk_menu_shell_prepend(GTK_MENU_SHELL(ret), menu_item);
728 if( plugin->class->config )
729 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_config_plugin), plugin );
730 else
731 gtk_widget_set_sensitive( menu_item, FALSE );
732 }
733
e3b89f43 734 gtk_widget_show_all(GTK_WIDGET(menu));
3e7b8eb7 735
cf701cb7
HJYP
736 g_signal_connect( ret, "selection-done", G_CALLBACK(gtk_widget_destroy), NULL );
737 return ret;
738}
739
a52c2257
HJYP
740/****************************************************
741 * panel creation *
742 ****************************************************/
176fb687 743
a52c2257 744static void
22242ed4 745make_round_corners(Panel *p)
a52c2257 746{
a97d06a6 747 /* FIXME: This should be re-written with shape extension of X11 */
4542c20d 748 /* gdk_window_shape_combine_mask() can be used */
bee4c26e
HJYP
749}
750
22242ed4 751void panel_set_dock_type(Panel *p)
bee4c26e
HJYP
752{
753 if (p->setdocktype) {
754 Atom state = a_NET_WM_WINDOW_TYPE_DOCK;
755 XChangeProperty(GDK_DISPLAY(), p->topxwin,
756 a_NET_WM_WINDOW_TYPE, XA_ATOM, 32,
757 PropModeReplace, (unsigned char *) &state, 1);
758 }
759 else {
760 XDeleteProperty( GDK_DISPLAY(), p->topxwin, a_NET_WM_WINDOW_TYPE );
761 }
a52c2257
HJYP
762}
763
176fb687 764static void panel_set_visibility(Panel *p, gboolean visible)
765{
766 if ( ! visible) gtk_widget_hide(p->box);
767 p->visible = visible;
768 calculate_position(p);
769 gtk_widget_set_size_request(p->topgwin, p->aw, p->ah);
770 gdk_window_move(p->topgwin->window, p->ax, p->ay);
771 if (visible) gtk_widget_show(p->box);
772 panel_set_wm_strut(p);
773}
774
775static gboolean panel_leave_real(Panel *p)
776{
777 if (gdk_display_pointer_is_grabbed(p->display))
778 return TRUE;
779
780 gint x, y;
781 gdk_display_get_pointer(p->display, NULL, &x, &y, NULL);
782 if ((p->cx <= x) && (x <= (p->cx + p->cw)) && (p->cy <= y) && (y <= (p->cy + p->ch)))
783 return TRUE;
784
785 if ((p->autohide) && (p->visible))
786 panel_set_visibility(p, FALSE);
787
788 p->hide_timeout = 0;
789 return FALSE;
790}
791
792static gboolean panel_enter(GtkImage *widget, GdkEventCrossing *event, Panel *p)
793{
794 if (p->hide_timeout)
795 return FALSE;
796
797 p->hide_timeout = g_timeout_add(500, (GSourceFunc) panel_leave_real, p);
798
799 panel_set_visibility(p, TRUE);
800 return TRUE;
801}
802
803static gboolean panel_drag_motion(GtkWidget *widget, GdkDragContext *drag_context, gint x,
804 gint y, guint time, Panel *p)
805{
806 panel_enter(NULL, NULL, p);
807 return TRUE;
808}
809
810void panel_establish_autohide(Panel *p)
811{
812 if (p->autohide)
813 {
814 gtk_widget_add_events(p->topgwin, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
815 g_signal_connect(G_OBJECT(p->topgwin), "enter-notify-event", G_CALLBACK(panel_enter), p);
816 g_signal_connect(G_OBJECT(p->topgwin), "drag-motion", (GCallback) panel_drag_motion, p);
817 gtk_drag_dest_set(p->topgwin, GTK_DEST_DEFAULT_MOTION, NULL, 0, 0);
818 gtk_drag_dest_set_track_motion(p->topgwin, TRUE);
819 panel_enter(NULL, NULL, p);
820 }
821 else if ( ! p->visible)
822 {
823 gtk_widget_show(p->box);
824 p->visible = TRUE;
825 }
826}
827
8f9e6256 828/* Set an image from a file with scaling to the panel icon size. */
829void panel_image_set_from_file(Panel * p, GtkWidget * image, char * file)
830{
831 GdkPixbuf * pixbuf = gdk_pixbuf_new_from_file_at_scale(file, p->icon_size, p->icon_size, TRUE, NULL);
832 if (pixbuf != NULL)
833 {
834 gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf);
835 g_object_unref(pixbuf);
836 }
837}
838
0defe4b9 839static void
22242ed4 840panel_start_gui(Panel *p)
a52c2257
HJYP
841{
842 Atom state[3];
843 XWMHints wmhints;
844 guint32 val;
6db11841 845
a52c2257
HJYP
846 ENTER;
847
9dd114c4 848 p->curdesk = get_net_current_desktop();
849 p->desknum = get_net_number_of_desktops();
850 p->workarea = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_WORKAREA, XA_CARDINAL, &p->wa_len);
851
a52c2257
HJYP
852 // main toplevel window
853 p->topgwin = gtk_window_new(GTK_WINDOW_TOPLEVEL);
176fb687 854 p->display = gdk_display_get_default();
a52c2257
HJYP
855 gtk_container_set_border_width(GTK_CONTAINER(p->topgwin), 0);
856 gtk_window_set_resizable(GTK_WINDOW(p->topgwin), FALSE);
857 gtk_window_set_wmclass(GTK_WINDOW(p->topgwin), "panel", "lxpanel");
858 gtk_window_set_title(GTK_WINDOW(p->topgwin), "panel");
859 gtk_window_set_position(GTK_WINDOW(p->topgwin), GTK_WIN_POS_NONE);
860 gtk_window_set_decorated(GTK_WINDOW(p->topgwin), FALSE);
77886b88 861
e2957bd2
HJYP
862 gtk_window_group_add_window( win_grp, (GtkWindow*)p->topgwin );
863
a52c2257
HJYP
864 g_signal_connect(G_OBJECT(p->topgwin), "delete-event",
865 G_CALLBACK(panel_delete_event), p);
866 g_signal_connect(G_OBJECT(p->topgwin), "destroy-event",
867 G_CALLBACK(panel_destroy_event), p);
868 g_signal_connect (G_OBJECT (p->topgwin), "size-request",
869 (GCallback) panel_size_req, p);
870 g_signal_connect (G_OBJECT (p->topgwin), "size-allocate",
871 (GCallback) panel_size_alloc, p);
872 g_signal_connect (G_OBJECT (p->topgwin), "configure-event",
873 (GCallback) panel_configure_event, p);
3e7b8eb7
HJYP
874
875 gtk_widget_add_events( p->topgwin, GDK_BUTTON_PRESS_MASK );
fddae119 876 g_signal_connect(G_OBJECT (p->topgwin), "button_press_event",
9dd114c4 877 (GCallback) panel_button_press_event_with_panel, p);
f2d54481 878
a52c2257
HJYP
879 g_signal_connect (G_OBJECT (p->topgwin), "realize",
880 (GCallback) panel_realize, p);
f2d54481 881
84fc1d55
HJYP
882 g_signal_connect (G_OBJECT (p->topgwin), "style-set",
883 (GCallback)panel_style_set, p);
a52c2257
HJYP
884 gtk_widget_realize(p->topgwin);
885 //gdk_window_set_decorations(p->topgwin->window, 0);
2de71c90 886
4542c20d 887 // main layout manager as a single child of panel
a97d06a6 888 p->box = p->my_box_new(FALSE, 0);
a52c2257 889 gtk_container_set_border_width(GTK_CONTAINER(p->box), 0);
4542c20d
HJYP
890// gtk_container_add(GTK_CONTAINER(p->bbox), p->box);
891 gtk_container_add(GTK_CONTAINER(p->topgwin), p->box);
a52c2257 892 gtk_widget_show(p->box);
a97d06a6
HJYP
893 if (p->round_corners)
894 make_round_corners(p);
6db11841 895
a52c2257
HJYP
896 p->topxwin = GDK_WINDOW_XWINDOW(GTK_WIDGET(p->topgwin)->window);
897 DBG("topxwin = %x\n", p->topxwin);
898
899 /* the settings that should be done before window is mapped */
900 wmhints.flags = InputHint;
901 wmhints.input = 0;
bee4c26e 902 XSetWMHints (GDK_DISPLAY(), p->topxwin, &wmhints);
24053345 903#define WIN_HINTS_SKIP_FOCUS (1<<0) /* "alt-tab" skips this win */
a52c2257
HJYP
904 val = WIN_HINTS_SKIP_FOCUS;
905 XChangeProperty(GDK_DISPLAY(), p->topxwin,
906 XInternAtom(GDK_DISPLAY(), "_WIN_HINTS", False), XA_CARDINAL, 32,
907 PropModeReplace, (unsigned char *) &val, 1);
908
bee4c26e 909 panel_set_dock_type(p);
a52c2257
HJYP
910
911 /* window mapping point */
912 gtk_widget_show_all(p->topgwin);
913
914 /* the settings that should be done after window is mapped */
176fb687 915 panel_establish_autohide(p);
a52c2257
HJYP
916
917 /* send it to running wm */
918 Xclimsg(p->topxwin, a_NET_WM_DESKTOP, 0xFFFFFFFF, 0, 0, 0, 0);
919 /* and assign it ourself just for case when wm is not running */
920 val = 0xFFFFFFFF;
921 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_DESKTOP, XA_CARDINAL, 32,
922 PropModeReplace, (unsigned char *) &val, 1);
923
924 state[0] = a_NET_WM_STATE_SKIP_PAGER;
925 state[1] = a_NET_WM_STATE_SKIP_TASKBAR;
926 state[2] = a_NET_WM_STATE_STICKY;
927 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STATE, XA_ATOM,
928 32, PropModeReplace, (unsigned char *) state, 3);
929
a52c2257
HJYP
930 calculate_position(p);
931 gdk_window_move_resize(p->topgwin->window, p->ax, p->ay, p->aw, p->ah);
bee4c26e 932 panel_set_wm_strut(p);
77886b88 933
a52c2257
HJYP
934 RET();
935}
936
2918994e 937/* Exchange the "width" and "height" terminology for vertical and horizontal panels. */
938void panel_adjust_geometry_terminology(Panel * p)
9dd114c4 939{
940 if ((p->height_label != NULL) && (p->width_label != NULL))
941 {
942 if ((p->edge == EDGE_TOP) || (p->edge == EDGE_BOTTOM))
943 {
944 gtk_label_set_text(GTK_LABEL(p->height_label), _("Height:"));
945 gtk_label_set_text(GTK_LABEL(p->width_label), _("Width:"));
2918994e 946 gtk_button_set_label(GTK_BUTTON(p->alignment_left_label), _("Left"));
947 gtk_button_set_label(GTK_BUTTON(p->alignment_right_label), _("Right"));
9dd114c4 948 }
949 else
950 {
951 gtk_label_set_text(GTK_LABEL(p->height_label), _("Width:"));
952 gtk_label_set_text(GTK_LABEL(p->width_label), _("Height:"));
2918994e 953 gtk_button_set_label(GTK_BUTTON(p->alignment_left_label), _("Top"));
954 gtk_button_set_label(GTK_BUTTON(p->alignment_right_label), _("Bottom"));
9dd114c4 955 }
956 }
957}
958
2918994e 959/* Draw text into a label, with the user preference color and optionally bold. */
960void panel_draw_label_text(Panel * p, GtkWidget * label, char * text, gboolean bold)
961{
962 char buffer[512];
963
964 if (text == NULL)
965 {
966 /* Null string. */
967 gtk_label_set_text(GTK_LABEL(label), NULL);
968 }
969
f669ce5e 970 else
2918994e 971 {
f669ce5e 972 /* Compute an appropriate size so the font will scale with the panel's icon size. */
973 int font_desc;
974 if (p->icon_size < 20)
975 font_desc = 9;
976 else if (p->icon_size >= 20 && p->icon_size < 26)
977 font_desc = 10;
978 else if (p->icon_size >= 26 && p->icon_size < 32)
979 font_desc = 14;
980 else if (p->icon_size >= 32 && p->icon_size < 40)
981 font_desc = 16;
982 else if (p->icon_size >= 40 && p->icon_size < 52)
983 font_desc = 18;
984 else
985 font_desc = 20;
2918994e 986
987 /* Check the string for characters that need to be escaped.
988 * If any are found, create the properly escaped string and use it instead. */
f669ce5e 989 char * valid_markup = text;
990 char * escaped_text = NULL;
2918994e 991 char * q;
992 for (q = text; *q != '\0'; q += 1)
993 {
994 if ((*q == '<') || (*q == '>') || (*q == '&'))
995 {
996 escaped_text = g_markup_escape_text(text, -1);
997 valid_markup = escaped_text;
998 break;
999 }
1000 }
1001
1002 if (p->usefontcolor)
1003 {
1004 /* Color, optionally bold. */
f669ce5e 1005 g_snprintf(buffer, sizeof(buffer), "<span font_desc=\"%d\" color=\"#%06x\">%s%s%s</span>",
1006 font_desc,
2918994e 1007 gcolor2rgb24(&p->gfontcolor),
1008 ((bold) ? "<b>" : ""),
1009 valid_markup,
1010 ((bold) ? "</b>" : ""));
1011 gtk_label_set_markup(GTK_LABEL(label), buffer);
1012 }
f669ce5e 1013 else
2918994e 1014 {
f669ce5e 1015 /* No color, optionally bold. */
1016 g_snprintf(buffer, sizeof(buffer), "<span font_desc=\"%d\">%s%s%s</span>",
1017 font_desc,
1018 ((bold) ? "<b>" : ""),
1019 valid_markup,
1020 ((bold) ? "</b>" : ""));
2918994e 1021 gtk_label_set_markup(GTK_LABEL(label), buffer);
1022 }
1023 g_free(escaped_text);
1024 }
2918994e 1025}
1026
1027void panel_set_panel_configuration_changed(Panel *p)
a97d06a6
HJYP
1028{
1029 GList* l;
9dd114c4 1030
1031 int previous_orientation = p->orientation;
a97d06a6
HJYP
1032 p->orientation = (p->edge == EDGE_TOP || p->edge == EDGE_BOTTOM)
1033 ? ORIENT_HORIZ : ORIENT_VERT;
9dd114c4 1034
1035 if (previous_orientation != p->orientation)
1036 {
1037 panel_adjust_geometry_terminology(p);
1038 p->height = ((p->orientation == ORIENT_HORIZ) ? PANEL_HEIGHT_DEFAULT : PANEL_WIDTH_DEFAULT);
1039 if (p->height_control != NULL)
1040 gtk_spin_button_set_value(GTK_SPIN_BUTTON(p->height_control), p->height);
2918994e 1041 if ((p->widthtype == WIDTH_PIXEL) && (p->width_control != NULL))
1042 {
1043 int value = ((p->orientation == ORIENT_HORIZ) ? gdk_screen_width() : gdk_screen_height());
1044 gtk_spin_button_set_range(GTK_SPIN_BUTTON(p->width_control), 0, value);
1045 gtk_spin_button_set_value(GTK_SPIN_BUTTON(p->width_control), value);
1046 }
1047
9dd114c4 1048 }
1049
a97d06a6
HJYP
1050 if (p->orientation == ORIENT_HORIZ) {
1051 p->my_box_new = gtk_hbox_new;
1052 p->my_separator_new = gtk_vseparator_new;
1053 } else {
1054 p->my_box_new = gtk_vbox_new;
1055 p->my_separator_new = gtk_hseparator_new;
1056 }
1057
1058 /* recreate the main layout box */
1059 if( p->box ) {
5a343ad5
JH
1060 GtkBox* newbox = GTK_BOX(recreate_box( GTK_BOX(p->box), p->orientation ));
1061 if( GTK_WIDGET(newbox) != p->box ) {
1062 p->box = GTK_WIDGET(newbox);
4542c20d 1063 gtk_container_add( GTK_CONTAINER(p->topgwin), GTK_WIDGET(newbox) );
a97d06a6
HJYP
1064 }
1065 }
9dd114c4 1066
a97d06a6
HJYP
1067 /* NOTE: This loop won't be executed when panel started since
1068 plugins are not loaded at that time.
1069 This is used when the orientation of the panel is changed
1070 from the config dialog, and plugins should be re-layout.
1071 */
1072 for( l = p->plugins; l; l = l->next ) {
22242ed4 1073 Plugin* pl = (Plugin*)l->data;
2918994e 1074 if( pl->class->panel_configuration_changed ) {
1075 pl->class->panel_configuration_changed( pl );
a97d06a6
HJYP
1076 }
1077 }
1078}
1079
a52c2257 1080static int
22242ed4 1081panel_parse_global(Panel *p, char **fp)
a52c2257
HJYP
1082{
1083 line s;
1084 s.len = 256;
6db11841 1085
3e7b8eb7
HJYP
1086 if( G_LIKELY( fp ) )
1087 {
1088 while (lxpanel_get_line(fp, &s) != LINE_NONE) {
1089 if (s.type == LINE_VAR) {
1090 if (!g_ascii_strcasecmp(s.t[0], "edge")) {
1091 p->edge = str2num(edge_pair, s.t[1], EDGE_NONE);
1092 } else if (!g_ascii_strcasecmp(s.t[0], "allign")) {
1093 p->allign = str2num(allign_pair, s.t[1], ALLIGN_NONE);
1094 } else if (!g_ascii_strcasecmp(s.t[0], "margin")) {
1095 p->margin = atoi(s.t[1]);
1096 } else if (!g_ascii_strcasecmp(s.t[0], "widthtype")) {
1097 p->widthtype = str2num(width_pair, s.t[1], WIDTH_NONE);
1098 } else if (!g_ascii_strcasecmp(s.t[0], "width")) {
1099 p->width = atoi(s.t[1]);
1100 } else if (!g_ascii_strcasecmp(s.t[0], "heighttype")) {
1101 p->heighttype = str2num(height_pair, s.t[1], HEIGHT_NONE);
1102 } else if (!g_ascii_strcasecmp(s.t[0], "height")) {
1103 p->height = atoi(s.t[1]);
1104 } else if (!g_ascii_strcasecmp(s.t[0], "spacing")) {
1105 p->spacing = atoi(s.t[1]);
1106 } else if (!g_ascii_strcasecmp(s.t[0], "SetDockType")) {
1107 p->setdocktype = str2num(bool_pair, s.t[1], 0);
1108 } else if (!g_ascii_strcasecmp(s.t[0], "SetPartialStrut")) {
1109 p->setstrut = str2num(bool_pair, s.t[1], 0);
1110 } else if (!g_ascii_strcasecmp(s.t[0], "RoundCorners")) {
1111 p->round_corners = str2num(bool_pair, s.t[1], 0);
1112 } else if (!g_ascii_strcasecmp(s.t[0], "Transparent")) {
1113 p->transparent = str2num(bool_pair, s.t[1], 0);
1114 } else if (!g_ascii_strcasecmp(s.t[0], "Alpha")) {
1115 p->alpha = atoi(s.t[1]);
1116 if (p->alpha > 255)
1117 p->alpha = 255;
176fb687 1118 } else if (!g_ascii_strcasecmp(s.t[0], "AutoHide")) {
1119 p->autohide = str2num(bool_pair, s.t[1], 0);
1120 } else if (!g_ascii_strcasecmp(s.t[0], "HeightWhenHidden")) {
1121 p->height_when_hidden = atoi(s.t[1]);
3e7b8eb7
HJYP
1122 } else if (!g_ascii_strcasecmp(s.t[0], "TintColor")) {
1123 if (!gdk_color_parse (s.t[1], &p->gtintcolor))
1124 gdk_color_parse ("white", &p->gtintcolor);
1125 p->tintcolor = gcolor2rgb24(&p->gtintcolor);
1126 DBG("tintcolor=%x\n", p->tintcolor);
1127 } else if (!g_ascii_strcasecmp(s.t[0], "useFontColor")) {
1128 p->usefontcolor = str2num(bool_pair, s.t[1], 0);
1129 } else if (!g_ascii_strcasecmp(s.t[0], "FontColor")) {
1130 if (!gdk_color_parse (s.t[1], &p->gfontcolor))
1131 gdk_color_parse ("black", &p->gfontcolor);
1132 p->fontcolor = gcolor2rgb24(&p->gfontcolor);
1133 DBG("fontcolor=%x\n", p->fontcolor);
1134 } else if (!g_ascii_strcasecmp(s.t[0], "Background")) {
1135 p->background = str2num(bool_pair, s.t[1], 0);
1136 } else if( !g_ascii_strcasecmp(s.t[0], "BackgroundFile") ) {
1137 p->background_file = g_strdup( s.t[1] );
8f9e6256 1138 } else if (!g_ascii_strcasecmp(s.t[0], "IconSize")) {
1139 p->icon_size = atoi(s.t[1]);
3e7b8eb7
HJYP
1140 } else {
1141 ERR( "lxpanel: %s - unknown var in Global section\n", s.t[0]);
3e7b8eb7
HJYP
1142 }
1143 } else if (s.type == LINE_BLOCK_END) {
1144 break;
a52c2257 1145 } else {
3e7b8eb7 1146 ERR( "lxpanel: illegal in this context %s\n", s.str);
a52c2257
HJYP
1147 RET(0);
1148 }
a52c2257
HJYP
1149 }
1150 }
4542c20d 1151
9dd114c4 1152 panel_normalize_configuration(p);
239cb032 1153
9dd114c4 1154 return 1;
a52c2257
HJYP
1155}
1156
1157static int
22242ed4 1158panel_parse_plugin(Panel *p, char **fp)
a52c2257
HJYP
1159{
1160 line s;
22242ed4 1161 Plugin *plug = NULL;
a52c2257 1162 gchar *type = NULL;
a52c2257 1163 int expand , padding, border;
db449f6e
HJYP
1164 char* pconfig = NULL;
1165
a52c2257
HJYP
1166 ENTER;
1167 s.len = 256;
a52c2257 1168 border = expand = padding = 0;
c69ac68e 1169 while (lxpanel_get_line(fp, &s) != LINE_BLOCK_END) {
a52c2257
HJYP
1170 if (s.type == LINE_NONE) {
1171 ERR( "lxpanel: bad line %s\n", s.str);
1172 goto error;
1173 }
1174 if (s.type == LINE_VAR) {
1175 if (!g_ascii_strcasecmp(s.t[0], "type")) {
1176 type = g_strdup(s.t[1]);
1177 DBG("plug %s\n", type);
1178 } else if (!g_ascii_strcasecmp(s.t[0], "expand"))
1179 expand = str2num(bool_pair, s.t[1], 0);
1180 else if (!g_ascii_strcasecmp(s.t[0], "padding"))
1181 padding = atoi(s.t[1]);
1182 else if (!g_ascii_strcasecmp(s.t[0], "border"))
1183 border = atoi(s.t[1]);
1184 else {
1185 ERR( "lxpanel: unknown var %s\n", s.t[0]);
a52c2257
HJYP
1186 }
1187 } else if (s.type == LINE_BLOCK_START) {
1188 if (!g_ascii_strcasecmp(s.t[0], "Config")) {
db449f6e 1189 pconfig = *fp;
a52c2257
HJYP
1190 int pno = 1;
1191 while (pno) {
1192 get_line_as_is(fp, &s);
1193 if (s.type == LINE_NONE) {
1194 ERR( "lxpanel: unexpected eof\n");
1195 goto error;
1196 } else if (s.type == LINE_BLOCK_START) {
1197 pno++;
1198 } else if (s.type == LINE_BLOCK_END) {
1199 pno--;
bee4c26e 1200 }
db449f6e 1201 }
a52c2257
HJYP
1202 } else {
1203 ERR( "lxpanel: unknown block %s\n", s.t[0]);
1204 goto error;
1205 }
1206 } else {
1207 ERR( "lxpanel: illegal in this context %s\n", s.str);
1208 goto error;
1209 }
1210 }
db449f6e 1211
a52c2257
HJYP
1212 if (!type || !(plug = plugin_load(type))) {
1213 ERR( "lxpanel: can't load %s plugin\n", type);
1214 goto error;
1215 }
db449f6e 1216
a52c2257 1217 plug->panel = p;
2918994e 1218 if (plug->class->expand_available) plug->expand = expand;
a52c2257
HJYP
1219 plug->padding = padding;
1220 plug->border = border;
a52c2257 1221 DBG("starting\n");
db449f6e 1222 if (!plugin_start(plug, pconfig ? &pconfig : NULL)) {
a52c2257
HJYP
1223 ERR( "lxpanel: can't start plugin %s\n", type);
1224 goto error;
1225 }
1226 DBG("plug %s\n", type);
1227 p->plugins = g_list_append(p->plugins, plug);
0dcb6bf5
HJYP
1228
1229 g_free( type );
a52c2257 1230 RET(1);
db449f6e 1231
a52c2257 1232 error:
2918994e 1233 if (plug != NULL)
1234 plugin_unload(plug);
a52c2257 1235 g_free(type);
a52c2257 1236 RET(0);
a52c2257
HJYP
1237}
1238
3e7b8eb7 1239int panel_start( Panel *p, char **fp )
a52c2257
HJYP
1240{
1241 line s;
db449f6e 1242
a52c2257
HJYP
1243 /* parse global section */
1244 ENTER;
1245 s.len = 256;
8110399f 1246
c69ac68e 1247 if ((lxpanel_get_line(fp, &s) != LINE_BLOCK_START) || g_ascii_strcasecmp(s.t[0], "Global")) {
a52c2257
HJYP
1248 ERR( "lxpanel: config file must start from Global section\n");
1249 RET(0);
1250 }
1251 if (!panel_parse_global(p, fp))
1252 RET(0);
1253
9dd114c4 1254 panel_start_gui(p);
1255
c69ac68e 1256 while (lxpanel_get_line(fp, &s) != LINE_NONE) {
a52c2257
HJYP
1257 if ((s.type != LINE_BLOCK_START) || g_ascii_strcasecmp(s.t[0], "Plugin")) {
1258 ERR( "lxpanel: expecting Plugin section\n");
1259 RET(0);
1260 }
9939506e 1261 panel_parse_plugin(p, fp);
a52c2257 1262 }
4542c20d
HJYP
1263
1264 /* update backgrond of panel and all plugins */
1265 panel_update_background( p );
9dd114c4 1266 return 1;
a52c2257
HJYP
1267}
1268
1269static void
1270delete_plugin(gpointer data, gpointer udata)
1271{
2918994e 1272 plugin_delete((Plugin *)data);
a52c2257
HJYP
1273}
1274
8110399f 1275void panel_destroy(Panel *p)
a52c2257
HJYP
1276{
1277 ENTER;
1278
2918994e 1279 if (p->pref_dialog != NULL)
1280 gtk_widget_destroy(p->pref_dialog);
1281 if (p->plugin_pref_dialog != NULL)
1282 {
1283 gtk_widget_destroy(p->plugin_pref_dialog);
1284 p->plugin_pref_dialog = NULL;
1285 }
1286
1287 if (p->bg != NULL)
1288 {
1289 g_signal_handlers_disconnect_by_func(G_OBJECT(p->bg), on_root_bg_changed, p);
1290 g_object_unref(p->bg);
1291 }
1292
930af9fd
HJYP
1293 if( p->config_changed )
1294 panel_config_save( p );
1295
a52c2257
HJYP
1296 g_list_foreach(p->plugins, delete_plugin, NULL);
1297 g_list_free(p->plugins);
1298 p->plugins = NULL;
8c44345a 1299
5297da29 1300 if( p->system_menus ){
8c44345a 1301 do{
5297da29 1302 } while ( g_source_remove_by_user_data( p->system_menus ) );
8c44345a
HJYP
1303 }
1304
16fbda14 1305 gtk_window_group_remove_window( win_grp, GTK_WINDOW( p->topgwin ) );
e2957bd2 1306
4b93d81e
HJYP
1307 if( p->topgwin )
1308 gtk_widget_destroy(p->topgwin);
a52c2257 1309 g_free(p->workarea);
2de71c90 1310 g_free( p->background_file );
5297da29 1311 g_slist_free( p->system_menus );
a52c2257
HJYP
1312 gdk_flush();
1313 XFlush(GDK_DISPLAY());
1314 XSync(GDK_DISPLAY(), True);
8110399f
HJYP
1315
1316 g_free( p->name );
2918994e 1317 g_free(p);
a52c2257
HJYP
1318 RET();
1319}
1320
8110399f
HJYP
1321Panel* panel_new( const char* config_file, const char* config_name )
1322{
1323 char *fp, *pfp; /* point to current position of profile data in memory */
cf701cb7 1324 Panel* panel = NULL;
16fbda14 1325
3e7b8eb7 1326 if( G_LIKELY(config_file) )
cf701cb7 1327 {
3e7b8eb7
HJYP
1328 g_file_get_contents( config_file, &fp, NULL, NULL );
1329 if( fp )
1330 {
9dd114c4 1331 panel = panel_allocate();
3e7b8eb7
HJYP
1332 panel->name = g_strdup( config_name );
1333 pfp = fp;
1334
1335 if (! panel_start( panel, &pfp )) {
1336 ERR( "lxpanel: can't start panel\n");
1337 panel_destroy( panel );
1338 panel = NULL;
1339 }
cf701cb7 1340
3e7b8eb7 1341 g_free( fp );
cf701cb7 1342 }
cf701cb7
HJYP
1343 }
1344 return panel;
8110399f 1345}
a52c2257 1346
0defe4b9 1347static void
a52c2257
HJYP
1348usage()
1349{
e7cb732b
HJYP
1350 g_print(_("lxpanel %s - lightweight GTK2+ panel for UNIX desktops\n"), version);
1351 g_print(_("Command line options:\n"));
1352 g_print(_(" --help -- print this help and exit\n"));
1353 g_print(_(" --version -- print version and exit\n"));
1354 g_print(_(" --log <number> -- set log level 0-5. 0 - none 5 - chatty\n"));
2918994e 1355// g_print(_(" --configure -- launch configuration utility\n"));
e7cb732b
HJYP
1356 g_print(_(" --profile name -- use specified profile\n"));
1357 g_print("\n");
1358 g_print(_(" -h -- same as --help\n"));
1359 g_print(_(" -p -- same as --profile\n"));
1360 g_print(_(" -v -- same as --version\n"));
2918994e 1361 // g_print(_(" -C -- same as --configure\n"));
b37c9aae 1362 g_print(_("\nVisit http://lxde.org/ for detail.\n\n"));
a52c2257
HJYP
1363}
1364
2918994e 1365int panel_handle_x_error(Display * d, XErrorEvent * ev)
a52c2257
HJYP
1366{
1367 char buf[256];
1368
a52c2257
HJYP
1369 if (log_level >= LOG_WARN) {
1370 XGetErrorText(GDK_DISPLAY(), ev->error_code, buf, 256);
1371 LOG(LOG_WARN, "lxpanel : X error: %s\n", buf);
1372 }
2918994e 1373 return 0; /* Ignored */
1374}
1375
1376int panel_handle_x_error_swallow_BadWindow_BadDrawable(Display * d, XErrorEvent * ev)
1377{
1378 if ((ev->error_code != BadWindow) && (ev->error_code != BadDrawable))
1379 panel_handle_x_error(d, ev);
1380 return 0; /* Ignored */
a52c2257
HJYP
1381}
1382
e68b47dc
JH
1383/* Lightweight lock related functions - X clipboard hacks */
1384
1385#define CLIPBOARD_NAME "LXPANEL_SELECTION"
1386
1387/*
1388 * clipboard_get_func - dummy get_func for gtk_clipboard_set_with_data ()
1389 */
1390static void
1391clipboard_get_func(
1392 GtkClipboard *clipboard G_GNUC_UNUSED,
1393 GtkSelectionData *selection_data G_GNUC_UNUSED,
1394 guint info G_GNUC_UNUSED,
1395 gpointer user_data_or_owner G_GNUC_UNUSED)
1396{
1397}
1398
1399/*
1400 * clipboard_clear_func - dummy clear_func for gtk_clipboard_set_with_data ()
1401 */
1402static void clipboard_clear_func(
1403 GtkClipboard *clipboard G_GNUC_UNUSED,
1404 gpointer user_data_or_owner G_GNUC_UNUSED)
1405{
1406}
1407
1408/*
1409 * Lightweight version for checking single instance.
1410 * Try and get the CLIPBOARD_NAME clipboard instead of using file manipulation.
1411 *
1412 * Returns TRUE if successfully retrieved and FALSE otherwise.
1413 */
16fb8c2e 1414static gboolean check_main_lock()
e68b47dc
JH
1415{
1416 static const GtkTargetEntry targets[] = { { CLIPBOARD_NAME, 0, 0 } };
1417 gboolean retval = FALSE;
1418 GtkClipboard *clipboard;
1419 Atom atom;
1420
1421 atom = gdk_x11_get_xatom_by_name(CLIPBOARD_NAME);
1422
1423 XGrabServer(GDK_DISPLAY());
1424
1425 if (XGetSelectionOwner(GDK_DISPLAY(), atom) != None)
1426 goto out;
1427
1428 clipboard = gtk_clipboard_get(gdk_atom_intern(CLIPBOARD_NAME, FALSE));
1429
1430 if (gtk_clipboard_set_with_data(clipboard, targets,
1431 G_N_ELEMENTS (targets),
1432 clipboard_get_func,
1433 clipboard_clear_func, NULL))
1434 retval = TRUE;
1435
1436out:
1437 XUngrabServer (GDK_DISPLAY ());
1438 gdk_flush ();
1439
1440 return retval;
1441}
1442#undef CLIPBOARD_NAME
1443
8110399f
HJYP
1444static gboolean start_all_panels( )
1445{
cf701cb7 1446 gboolean is_global;
4b93d81e 1447 for( is_global = 0; ! all_panels && is_global < 2; ++is_global )
cf701cb7
HJYP
1448 {
1449 char* panel_dir = get_config_file( cprofile, "panels", is_global );
1450 GDir* dir = g_dir_open( panel_dir, 0, NULL );
e3b89f43 1451 const gchar* name;
cf701cb7
HJYP
1452
1453 if( ! dir )
1454 {
1455 g_free( panel_dir );
1456 continue;
1457 }
1458
1459 while( name = g_dir_read_name( dir ) )
1460 {
1461 char* panel_config = g_build_filename( panel_dir, name, NULL );
2918994e 1462 if (strchr(panel_config, '~') == NULL) /* Skip editor backup files in case user has hand edited in this directory */
1463 {
1464 Panel* panel = panel_new( panel_config, name );
1465 if( panel )
1466 all_panels = g_slist_prepend( all_panels, panel );
1467 }
cf701cb7
HJYP
1468 g_free( panel_config );
1469 }
1470 g_dir_close( dir );
1471 g_free( panel_dir );
1472 }
1473 return all_panels != NULL;
8110399f
HJYP
1474}
1475
cf701cb7
HJYP
1476void load_global_config();
1477void free_global_config();
1478
8110399f 1479int main(int argc, char *argv[], char *env[])
a52c2257
HJYP
1480{
1481 int i;
16fbda14 1482 const char* session_name;
f277dbb7 1483
a52c2257 1484 setlocale(LC_CTYPE, "");
f277dbb7 1485
a52c2257
HJYP
1486 gtk_init(&argc, &argv);
1487
1488#ifdef ENABLE_NLS
1489 bindtextdomain ( GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR );
1490 bind_textdomain_codeset ( GETTEXT_PACKAGE, "UTF-8" );
1491 textdomain ( GETTEXT_PACKAGE );
1492#endif
1493
1494 XSetLocaleModifiers("");
2918994e 1495 XSetErrorHandler((XErrorHandler) panel_handle_x_error);
8110399f 1496
a52c2257 1497 resolve_atoms();
8110399f 1498
95095259
HJYP
1499 session_name = g_getenv("DESKTOP_SESSION");
1500 is_in_lxde = session_name && (0 == strcmp(session_name, "LXDE"));
1501
a52c2257
HJYP
1502 for (i = 1; i < argc; i++) {
1503 if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
1504 usage();
1505 exit(0);
1506 } else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
1507 printf("lxpanel %s\n", version);
1508 exit(0);
1509 } else if (!strcmp(argv[i], "--log")) {
1510 i++;
1511 if (i == argc) {
1512 ERR( "lxpanel: missing log level\n");
1513 usage();
1514 exit(1);
1515 } else {
1516 log_level = atoi(argv[i]);
1517 }
1518 } else if (!strcmp(argv[i], "--configure") || !strcmp(argv[i], "-C")) {
1519 config = 1;
1520 } else if (!strcmp(argv[i], "--profile") || !strcmp(argv[i], "-p")) {
1521 i++;
1522 if (i == argc) {
1523 ERR( "lxpanel: missing profile name\n");
1524 usage();
1525 exit(1);
1526 } else {
1527 cprofile = g_strdup(argv[i]);
1528 }
1529 } else {
1530 printf("lxpanel: unknown option - %s\n", argv[i]);
1531 usage();
1532 exit(1);
1533 }
1534 }
f277dbb7 1535
8110399f 1536 /* Check for duplicated lxpanel instances */
e68b47dc 1537 if (!check_main_lock() && !config) {
e7a42ecf 1538 printf("There is already an instance of LXPanel. Now to exit\n");
e68b47dc
JH
1539 exit(1);
1540 }
1541
f277dbb7
HJYP
1542 /* Add our own icons to the search path of icon theme */
1543 gtk_icon_theme_append_search_path( gtk_icon_theme_get_default(),
1544 PACKAGE_DATA_DIR "/lxpanel/images" );
1545
cf701cb7 1546 fbev = fb_ev_new();
e2957bd2 1547 win_grp = gtk_window_group_new();
22242ed4 1548
f7cb330e
HJYP
1549restart:
1550 is_restarting = FALSE;
1551
cf701cb7
HJYP
1552 load_global_config();
1553
1d9dc649
HJYP
1554 /* NOTE: StructureNotifyMask is required by XRandR
1555 * See init_randr_support() in gdkscreen-x11.c of gtk+ for detail.
1556 */
1557 XSelectInput (GDK_DISPLAY(), GDK_ROOT_WINDOW(), StructureNotifyMask|SubstructureNotifyMask|PropertyChangeMask);
cf701cb7 1558 gdk_window_add_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, NULL);
8110399f 1559
cf701cb7
HJYP
1560 if( G_UNLIKELY( ! start_all_panels() ) )
1561 g_warning( "Config files are not found.\n" );
8110399f
HJYP
1562/*
1563 * FIXME: configure??
6a6ad54e
HJYP
1564 if (config)
1565 configure();
8110399f 1566*/
6a6ad54e 1567 gtk_main();
8110399f 1568
cf701cb7
HJYP
1569 XSelectInput (GDK_DISPLAY(), GDK_ROOT_WINDOW(), NoEventMask);
1570 gdk_window_remove_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, NULL);
1571
8110399f
HJYP
1572 /* destroy all panels */
1573 g_slist_foreach( all_panels, (GFunc) panel_destroy, NULL );
2a26c0d8
HJYP
1574 g_slist_free( all_panels );
1575 all_panels = NULL;
6a6ad54e 1576 g_free( cfgfile );
5541b8d2 1577
cf701cb7
HJYP
1578 free_global_config();
1579
f7cb330e
HJYP
1580 if( is_restarting )
1581 goto restart;
1582
e2957bd2 1583 g_object_unref(win_grp);
22242ed4
HJYP
1584 g_object_unref(fbev);
1585
5541b8d2 1586 return 0;
a52c2257 1587}