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