Add logic to have a visible name on grouped tasks
[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 an integer value into a label, with the user preference color and optionally bold. */
960void panel_draw_label_integer(Panel * p, GtkWidget * label, int value, gboolean bold)
961{
962 char buffer[12];
963 sprintf(buffer, "%d", value);
964 panel_draw_label_text(p, label, buffer, bold);
965
966}
967
968/* Draw text into a label, with the user preference color and optionally bold. */
969void panel_draw_label_text(Panel * p, GtkWidget * label, char * text, gboolean bold)
970{
971 char buffer[512];
972
973 if (text == NULL)
974 {
975 /* Null string. */
976 gtk_label_set_text(GTK_LABEL(label), NULL);
977 }
978
979 else if ((p->usefontcolor) || (bold))
980 {
981 char * valid_markup = text;
982 char * escaped_text = NULL;
983
984 /* Check the string for characters that need to be escaped.
985 * If any are found, create the properly escaped string and use it instead. */
986 char * q;
987 for (q = text; *q != '\0'; q += 1)
988 {
989 if ((*q == '<') || (*q == '>') || (*q == '&'))
990 {
991 escaped_text = g_markup_escape_text(text, -1);
992 valid_markup = escaped_text;
993 break;
994 }
995 }
996
997 if (p->usefontcolor)
998 {
999 /* Color, optionally bold. */
1000 g_snprintf(buffer, sizeof(buffer), "<span color=\"#%06x\">%s%s%s</span>",
1001 gcolor2rgb24(&p->gfontcolor),
1002 ((bold) ? "<b>" : ""),
1003 valid_markup,
1004 ((bold) ? "</b>" : ""));
1005 gtk_label_set_markup(GTK_LABEL(label), buffer);
1006 }
1007 else if (bold)
1008 {
1009 /* No color, bold. */
1010 g_snprintf(buffer, sizeof(buffer), "<span><b>%s</b></span>", valid_markup);
1011 gtk_label_set_markup(GTK_LABEL(label), buffer);
1012 }
1013 g_free(escaped_text);
1014 }
1015
1016 else
1017 {
1018 /* No color, no bold. */
1019 gtk_label_set_text(GTK_LABEL(label), text);
1020 }
1021}
1022
1023void panel_set_panel_configuration_changed(Panel *p)
a97d06a6
HJYP
1024{
1025 GList* l;
9dd114c4 1026
1027 int previous_orientation = p->orientation;
a97d06a6
HJYP
1028 p->orientation = (p->edge == EDGE_TOP || p->edge == EDGE_BOTTOM)
1029 ? ORIENT_HORIZ : ORIENT_VERT;
9dd114c4 1030
1031 if (previous_orientation != p->orientation)
1032 {
1033 panel_adjust_geometry_terminology(p);
1034 p->height = ((p->orientation == ORIENT_HORIZ) ? PANEL_HEIGHT_DEFAULT : PANEL_WIDTH_DEFAULT);
1035 if (p->height_control != NULL)
1036 gtk_spin_button_set_value(GTK_SPIN_BUTTON(p->height_control), p->height);
2918994e 1037 if ((p->widthtype == WIDTH_PIXEL) && (p->width_control != NULL))
1038 {
1039 int value = ((p->orientation == ORIENT_HORIZ) ? gdk_screen_width() : gdk_screen_height());
1040 gtk_spin_button_set_range(GTK_SPIN_BUTTON(p->width_control), 0, value);
1041 gtk_spin_button_set_value(GTK_SPIN_BUTTON(p->width_control), value);
1042 }
1043
9dd114c4 1044 }
1045
a97d06a6
HJYP
1046 if (p->orientation == ORIENT_HORIZ) {
1047 p->my_box_new = gtk_hbox_new;
1048 p->my_separator_new = gtk_vseparator_new;
1049 } else {
1050 p->my_box_new = gtk_vbox_new;
1051 p->my_separator_new = gtk_hseparator_new;
1052 }
1053
1054 /* recreate the main layout box */
1055 if( p->box ) {
5a343ad5
JH
1056 GtkBox* newbox = GTK_BOX(recreate_box( GTK_BOX(p->box), p->orientation ));
1057 if( GTK_WIDGET(newbox) != p->box ) {
1058 p->box = GTK_WIDGET(newbox);
4542c20d 1059 gtk_container_add( GTK_CONTAINER(p->topgwin), GTK_WIDGET(newbox) );
a97d06a6
HJYP
1060 }
1061 }
9dd114c4 1062
a97d06a6
HJYP
1063 /* NOTE: This loop won't be executed when panel started since
1064 plugins are not loaded at that time.
1065 This is used when the orientation of the panel is changed
1066 from the config dialog, and plugins should be re-layout.
1067 */
1068 for( l = p->plugins; l; l = l->next ) {
22242ed4 1069 Plugin* pl = (Plugin*)l->data;
2918994e 1070 if( pl->class->panel_configuration_changed ) {
1071 pl->class->panel_configuration_changed( pl );
a97d06a6
HJYP
1072 }
1073 }
1074}
1075
a52c2257 1076static int
22242ed4 1077panel_parse_global(Panel *p, char **fp)
a52c2257
HJYP
1078{
1079 line s;
1080 s.len = 256;
6db11841 1081
3e7b8eb7
HJYP
1082 if( G_LIKELY( fp ) )
1083 {
1084 while (lxpanel_get_line(fp, &s) != LINE_NONE) {
1085 if (s.type == LINE_VAR) {
1086 if (!g_ascii_strcasecmp(s.t[0], "edge")) {
1087 p->edge = str2num(edge_pair, s.t[1], EDGE_NONE);
1088 } else if (!g_ascii_strcasecmp(s.t[0], "allign")) {
1089 p->allign = str2num(allign_pair, s.t[1], ALLIGN_NONE);
1090 } else if (!g_ascii_strcasecmp(s.t[0], "margin")) {
1091 p->margin = atoi(s.t[1]);
1092 } else if (!g_ascii_strcasecmp(s.t[0], "widthtype")) {
1093 p->widthtype = str2num(width_pair, s.t[1], WIDTH_NONE);
1094 } else if (!g_ascii_strcasecmp(s.t[0], "width")) {
1095 p->width = atoi(s.t[1]);
1096 } else if (!g_ascii_strcasecmp(s.t[0], "heighttype")) {
1097 p->heighttype = str2num(height_pair, s.t[1], HEIGHT_NONE);
1098 } else if (!g_ascii_strcasecmp(s.t[0], "height")) {
1099 p->height = atoi(s.t[1]);
1100 } else if (!g_ascii_strcasecmp(s.t[0], "spacing")) {
1101 p->spacing = atoi(s.t[1]);
1102 } else if (!g_ascii_strcasecmp(s.t[0], "SetDockType")) {
1103 p->setdocktype = str2num(bool_pair, s.t[1], 0);
1104 } else if (!g_ascii_strcasecmp(s.t[0], "SetPartialStrut")) {
1105 p->setstrut = str2num(bool_pair, s.t[1], 0);
1106 } else if (!g_ascii_strcasecmp(s.t[0], "RoundCorners")) {
1107 p->round_corners = str2num(bool_pair, s.t[1], 0);
1108 } else if (!g_ascii_strcasecmp(s.t[0], "Transparent")) {
1109 p->transparent = str2num(bool_pair, s.t[1], 0);
1110 } else if (!g_ascii_strcasecmp(s.t[0], "Alpha")) {
1111 p->alpha = atoi(s.t[1]);
1112 if (p->alpha > 255)
1113 p->alpha = 255;
176fb687 1114 } else if (!g_ascii_strcasecmp(s.t[0], "AutoHide")) {
1115 p->autohide = str2num(bool_pair, s.t[1], 0);
1116 } else if (!g_ascii_strcasecmp(s.t[0], "HeightWhenHidden")) {
1117 p->height_when_hidden = atoi(s.t[1]);
3e7b8eb7
HJYP
1118 } else if (!g_ascii_strcasecmp(s.t[0], "TintColor")) {
1119 if (!gdk_color_parse (s.t[1], &p->gtintcolor))
1120 gdk_color_parse ("white", &p->gtintcolor);
1121 p->tintcolor = gcolor2rgb24(&p->gtintcolor);
1122 DBG("tintcolor=%x\n", p->tintcolor);
1123 } else if (!g_ascii_strcasecmp(s.t[0], "useFontColor")) {
1124 p->usefontcolor = str2num(bool_pair, s.t[1], 0);
1125 } else if (!g_ascii_strcasecmp(s.t[0], "FontColor")) {
1126 if (!gdk_color_parse (s.t[1], &p->gfontcolor))
1127 gdk_color_parse ("black", &p->gfontcolor);
1128 p->fontcolor = gcolor2rgb24(&p->gfontcolor);
1129 DBG("fontcolor=%x\n", p->fontcolor);
1130 } else if (!g_ascii_strcasecmp(s.t[0], "Background")) {
1131 p->background = str2num(bool_pair, s.t[1], 0);
1132 } else if( !g_ascii_strcasecmp(s.t[0], "BackgroundFile") ) {
1133 p->background_file = g_strdup( s.t[1] );
8f9e6256 1134 } else if (!g_ascii_strcasecmp(s.t[0], "IconSize")) {
1135 p->icon_size = atoi(s.t[1]);
3e7b8eb7
HJYP
1136 } else {
1137 ERR( "lxpanel: %s - unknown var in Global section\n", s.t[0]);
3e7b8eb7
HJYP
1138 }
1139 } else if (s.type == LINE_BLOCK_END) {
1140 break;
a52c2257 1141 } else {
3e7b8eb7 1142 ERR( "lxpanel: illegal in this context %s\n", s.str);
a52c2257
HJYP
1143 RET(0);
1144 }
a52c2257
HJYP
1145 }
1146 }
4542c20d 1147
9dd114c4 1148 panel_normalize_configuration(p);
239cb032 1149
9dd114c4 1150 return 1;
a52c2257
HJYP
1151}
1152
1153static int
22242ed4 1154panel_parse_plugin(Panel *p, char **fp)
a52c2257
HJYP
1155{
1156 line s;
22242ed4 1157 Plugin *plug = NULL;
a52c2257 1158 gchar *type = NULL;
a52c2257 1159 int expand , padding, border;
db449f6e
HJYP
1160 char* pconfig = NULL;
1161
a52c2257
HJYP
1162 ENTER;
1163 s.len = 256;
a52c2257 1164 border = expand = padding = 0;
c69ac68e 1165 while (lxpanel_get_line(fp, &s) != LINE_BLOCK_END) {
a52c2257
HJYP
1166 if (s.type == LINE_NONE) {
1167 ERR( "lxpanel: bad line %s\n", s.str);
1168 goto error;
1169 }
1170 if (s.type == LINE_VAR) {
1171 if (!g_ascii_strcasecmp(s.t[0], "type")) {
1172 type = g_strdup(s.t[1]);
1173 DBG("plug %s\n", type);
1174 } else if (!g_ascii_strcasecmp(s.t[0], "expand"))
1175 expand = str2num(bool_pair, s.t[1], 0);
1176 else if (!g_ascii_strcasecmp(s.t[0], "padding"))
1177 padding = atoi(s.t[1]);
1178 else if (!g_ascii_strcasecmp(s.t[0], "border"))
1179 border = atoi(s.t[1]);
1180 else {
1181 ERR( "lxpanel: unknown var %s\n", s.t[0]);
a52c2257
HJYP
1182 }
1183 } else if (s.type == LINE_BLOCK_START) {
1184 if (!g_ascii_strcasecmp(s.t[0], "Config")) {
db449f6e 1185 pconfig = *fp;
a52c2257
HJYP
1186 int pno = 1;
1187 while (pno) {
1188 get_line_as_is(fp, &s);
1189 if (s.type == LINE_NONE) {
1190 ERR( "lxpanel: unexpected eof\n");
1191 goto error;
1192 } else if (s.type == LINE_BLOCK_START) {
1193 pno++;
1194 } else if (s.type == LINE_BLOCK_END) {
1195 pno--;
bee4c26e 1196 }
db449f6e 1197 }
a52c2257
HJYP
1198 } else {
1199 ERR( "lxpanel: unknown block %s\n", s.t[0]);
1200 goto error;
1201 }
1202 } else {
1203 ERR( "lxpanel: illegal in this context %s\n", s.str);
1204 goto error;
1205 }
1206 }
db449f6e 1207
a52c2257
HJYP
1208 if (!type || !(plug = plugin_load(type))) {
1209 ERR( "lxpanel: can't load %s plugin\n", type);
1210 goto error;
1211 }
db449f6e 1212
a52c2257 1213 plug->panel = p;
2918994e 1214 if (plug->class->expand_available) plug->expand = expand;
a52c2257
HJYP
1215 plug->padding = padding;
1216 plug->border = border;
a52c2257 1217 DBG("starting\n");
db449f6e 1218 if (!plugin_start(plug, pconfig ? &pconfig : NULL)) {
a52c2257
HJYP
1219 ERR( "lxpanel: can't start plugin %s\n", type);
1220 goto error;
1221 }
1222 DBG("plug %s\n", type);
1223 p->plugins = g_list_append(p->plugins, plug);
0dcb6bf5
HJYP
1224
1225 g_free( type );
a52c2257 1226 RET(1);
db449f6e 1227
a52c2257 1228 error:
2918994e 1229 if (plug != NULL)
1230 plugin_unload(plug);
a52c2257 1231 g_free(type);
a52c2257 1232 RET(0);
a52c2257
HJYP
1233}
1234
3e7b8eb7 1235int panel_start( Panel *p, char **fp )
a52c2257
HJYP
1236{
1237 line s;
db449f6e 1238
a52c2257
HJYP
1239 /* parse global section */
1240 ENTER;
1241 s.len = 256;
8110399f 1242
c69ac68e 1243 if ((lxpanel_get_line(fp, &s) != LINE_BLOCK_START) || g_ascii_strcasecmp(s.t[0], "Global")) {
a52c2257
HJYP
1244 ERR( "lxpanel: config file must start from Global section\n");
1245 RET(0);
1246 }
1247 if (!panel_parse_global(p, fp))
1248 RET(0);
1249
9dd114c4 1250 panel_start_gui(p);
1251
c69ac68e 1252 while (lxpanel_get_line(fp, &s) != LINE_NONE) {
a52c2257
HJYP
1253 if ((s.type != LINE_BLOCK_START) || g_ascii_strcasecmp(s.t[0], "Plugin")) {
1254 ERR( "lxpanel: expecting Plugin section\n");
1255 RET(0);
1256 }
9939506e 1257 panel_parse_plugin(p, fp);
a52c2257 1258 }
4542c20d
HJYP
1259
1260 /* update backgrond of panel and all plugins */
1261 panel_update_background( p );
9dd114c4 1262 return 1;
a52c2257
HJYP
1263}
1264
1265static void
1266delete_plugin(gpointer data, gpointer udata)
1267{
2918994e 1268 plugin_delete((Plugin *)data);
a52c2257
HJYP
1269}
1270
8110399f 1271void panel_destroy(Panel *p)
a52c2257
HJYP
1272{
1273 ENTER;
1274
2918994e 1275 if (p->pref_dialog != NULL)
1276 gtk_widget_destroy(p->pref_dialog);
1277 if (p->plugin_pref_dialog != NULL)
1278 {
1279 gtk_widget_destroy(p->plugin_pref_dialog);
1280 p->plugin_pref_dialog = NULL;
1281 }
1282
1283 if (p->bg != NULL)
1284 {
1285 g_signal_handlers_disconnect_by_func(G_OBJECT(p->bg), on_root_bg_changed, p);
1286 g_object_unref(p->bg);
1287 }
1288
930af9fd
HJYP
1289 if( p->config_changed )
1290 panel_config_save( p );
1291
a52c2257
HJYP
1292 g_list_foreach(p->plugins, delete_plugin, NULL);
1293 g_list_free(p->plugins);
1294 p->plugins = NULL;
8c44345a 1295
5297da29 1296 if( p->system_menus ){
8c44345a 1297 do{
5297da29 1298 } while ( g_source_remove_by_user_data( p->system_menus ) );
8c44345a
HJYP
1299 }
1300
16fbda14 1301 gtk_window_group_remove_window( win_grp, GTK_WINDOW( p->topgwin ) );
e2957bd2 1302
4b93d81e
HJYP
1303 if( p->topgwin )
1304 gtk_widget_destroy(p->topgwin);
a52c2257 1305 g_free(p->workarea);
2de71c90 1306 g_free( p->background_file );
5297da29 1307 g_slist_free( p->system_menus );
a52c2257
HJYP
1308 gdk_flush();
1309 XFlush(GDK_DISPLAY());
1310 XSync(GDK_DISPLAY(), True);
8110399f
HJYP
1311
1312 g_free( p->name );
2918994e 1313 g_free(p);
a52c2257
HJYP
1314 RET();
1315}
1316
8110399f
HJYP
1317Panel* panel_new( const char* config_file, const char* config_name )
1318{
1319 char *fp, *pfp; /* point to current position of profile data in memory */
cf701cb7 1320 Panel* panel = NULL;
16fbda14 1321
3e7b8eb7 1322 if( G_LIKELY(config_file) )
cf701cb7 1323 {
3e7b8eb7
HJYP
1324 g_file_get_contents( config_file, &fp, NULL, NULL );
1325 if( fp )
1326 {
9dd114c4 1327 panel = panel_allocate();
3e7b8eb7
HJYP
1328 panel->name = g_strdup( config_name );
1329 pfp = fp;
1330
1331 if (! panel_start( panel, &pfp )) {
1332 ERR( "lxpanel: can't start panel\n");
1333 panel_destroy( panel );
1334 panel = NULL;
1335 }
cf701cb7 1336
3e7b8eb7 1337 g_free( fp );
cf701cb7 1338 }
cf701cb7
HJYP
1339 }
1340 return panel;
8110399f 1341}
a52c2257 1342
0defe4b9 1343static void
a52c2257
HJYP
1344usage()
1345{
e7cb732b
HJYP
1346 g_print(_("lxpanel %s - lightweight GTK2+ panel for UNIX desktops\n"), version);
1347 g_print(_("Command line options:\n"));
1348 g_print(_(" --help -- print this help and exit\n"));
1349 g_print(_(" --version -- print version and exit\n"));
1350 g_print(_(" --log <number> -- set log level 0-5. 0 - none 5 - chatty\n"));
2918994e 1351// g_print(_(" --configure -- launch configuration utility\n"));
e7cb732b
HJYP
1352 g_print(_(" --profile name -- use specified profile\n"));
1353 g_print("\n");
1354 g_print(_(" -h -- same as --help\n"));
1355 g_print(_(" -p -- same as --profile\n"));
1356 g_print(_(" -v -- same as --version\n"));
2918994e 1357 // g_print(_(" -C -- same as --configure\n"));
b37c9aae 1358 g_print(_("\nVisit http://lxde.org/ for detail.\n\n"));
a52c2257
HJYP
1359}
1360
2918994e 1361int panel_handle_x_error(Display * d, XErrorEvent * ev)
a52c2257
HJYP
1362{
1363 char buf[256];
1364
a52c2257
HJYP
1365 if (log_level >= LOG_WARN) {
1366 XGetErrorText(GDK_DISPLAY(), ev->error_code, buf, 256);
1367 LOG(LOG_WARN, "lxpanel : X error: %s\n", buf);
1368 }
2918994e 1369 return 0; /* Ignored */
1370}
1371
1372int panel_handle_x_error_swallow_BadWindow_BadDrawable(Display * d, XErrorEvent * ev)
1373{
1374 if ((ev->error_code != BadWindow) && (ev->error_code != BadDrawable))
1375 panel_handle_x_error(d, ev);
1376 return 0; /* Ignored */
a52c2257
HJYP
1377}
1378
e68b47dc
JH
1379/* Lightweight lock related functions - X clipboard hacks */
1380
1381#define CLIPBOARD_NAME "LXPANEL_SELECTION"
1382
1383/*
1384 * clipboard_get_func - dummy get_func for gtk_clipboard_set_with_data ()
1385 */
1386static void
1387clipboard_get_func(
1388 GtkClipboard *clipboard G_GNUC_UNUSED,
1389 GtkSelectionData *selection_data G_GNUC_UNUSED,
1390 guint info G_GNUC_UNUSED,
1391 gpointer user_data_or_owner G_GNUC_UNUSED)
1392{
1393}
1394
1395/*
1396 * clipboard_clear_func - dummy clear_func for gtk_clipboard_set_with_data ()
1397 */
1398static void clipboard_clear_func(
1399 GtkClipboard *clipboard G_GNUC_UNUSED,
1400 gpointer user_data_or_owner G_GNUC_UNUSED)
1401{
1402}
1403
1404/*
1405 * Lightweight version for checking single instance.
1406 * Try and get the CLIPBOARD_NAME clipboard instead of using file manipulation.
1407 *
1408 * Returns TRUE if successfully retrieved and FALSE otherwise.
1409 */
16fb8c2e 1410static gboolean check_main_lock()
e68b47dc
JH
1411{
1412 static const GtkTargetEntry targets[] = { { CLIPBOARD_NAME, 0, 0 } };
1413 gboolean retval = FALSE;
1414 GtkClipboard *clipboard;
1415 Atom atom;
1416
1417 atom = gdk_x11_get_xatom_by_name(CLIPBOARD_NAME);
1418
1419 XGrabServer(GDK_DISPLAY());
1420
1421 if (XGetSelectionOwner(GDK_DISPLAY(), atom) != None)
1422 goto out;
1423
1424 clipboard = gtk_clipboard_get(gdk_atom_intern(CLIPBOARD_NAME, FALSE));
1425
1426 if (gtk_clipboard_set_with_data(clipboard, targets,
1427 G_N_ELEMENTS (targets),
1428 clipboard_get_func,
1429 clipboard_clear_func, NULL))
1430 retval = TRUE;
1431
1432out:
1433 XUngrabServer (GDK_DISPLAY ());
1434 gdk_flush ();
1435
1436 return retval;
1437}
1438#undef CLIPBOARD_NAME
1439
8110399f
HJYP
1440static gboolean start_all_panels( )
1441{
cf701cb7 1442 gboolean is_global;
4b93d81e 1443 for( is_global = 0; ! all_panels && is_global < 2; ++is_global )
cf701cb7
HJYP
1444 {
1445 char* panel_dir = get_config_file( cprofile, "panels", is_global );
1446 GDir* dir = g_dir_open( panel_dir, 0, NULL );
e3b89f43 1447 const gchar* name;
cf701cb7
HJYP
1448
1449 if( ! dir )
1450 {
1451 g_free( panel_dir );
1452 continue;
1453 }
1454
1455 while( name = g_dir_read_name( dir ) )
1456 {
1457 char* panel_config = g_build_filename( panel_dir, name, NULL );
2918994e 1458 if (strchr(panel_config, '~') == NULL) /* Skip editor backup files in case user has hand edited in this directory */
1459 {
1460 Panel* panel = panel_new( panel_config, name );
1461 if( panel )
1462 all_panels = g_slist_prepend( all_panels, panel );
1463 }
cf701cb7
HJYP
1464 g_free( panel_config );
1465 }
1466 g_dir_close( dir );
1467 g_free( panel_dir );
1468 }
1469 return all_panels != NULL;
8110399f
HJYP
1470}
1471
cf701cb7
HJYP
1472void load_global_config();
1473void free_global_config();
1474
8110399f 1475int main(int argc, char *argv[], char *env[])
a52c2257
HJYP
1476{
1477 int i;
16fbda14 1478 const char* session_name;
f277dbb7 1479
a52c2257 1480 setlocale(LC_CTYPE, "");
f277dbb7 1481
a52c2257
HJYP
1482 gtk_init(&argc, &argv);
1483
1484#ifdef ENABLE_NLS
1485 bindtextdomain ( GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR );
1486 bind_textdomain_codeset ( GETTEXT_PACKAGE, "UTF-8" );
1487 textdomain ( GETTEXT_PACKAGE );
1488#endif
1489
1490 XSetLocaleModifiers("");
2918994e 1491 XSetErrorHandler((XErrorHandler) panel_handle_x_error);
8110399f 1492
a52c2257 1493 resolve_atoms();
8110399f 1494
95095259
HJYP
1495 session_name = g_getenv("DESKTOP_SESSION");
1496 is_in_lxde = session_name && (0 == strcmp(session_name, "LXDE"));
1497
a52c2257
HJYP
1498 for (i = 1; i < argc; i++) {
1499 if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
1500 usage();
1501 exit(0);
1502 } else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
1503 printf("lxpanel %s\n", version);
1504 exit(0);
1505 } else if (!strcmp(argv[i], "--log")) {
1506 i++;
1507 if (i == argc) {
1508 ERR( "lxpanel: missing log level\n");
1509 usage();
1510 exit(1);
1511 } else {
1512 log_level = atoi(argv[i]);
1513 }
1514 } else if (!strcmp(argv[i], "--configure") || !strcmp(argv[i], "-C")) {
1515 config = 1;
1516 } else if (!strcmp(argv[i], "--profile") || !strcmp(argv[i], "-p")) {
1517 i++;
1518 if (i == argc) {
1519 ERR( "lxpanel: missing profile name\n");
1520 usage();
1521 exit(1);
1522 } else {
1523 cprofile = g_strdup(argv[i]);
1524 }
1525 } else {
1526 printf("lxpanel: unknown option - %s\n", argv[i]);
1527 usage();
1528 exit(1);
1529 }
1530 }
f277dbb7 1531
8110399f 1532 /* Check for duplicated lxpanel instances */
e68b47dc 1533 if (!check_main_lock() && !config) {
e7a42ecf 1534 printf("There is already an instance of LXPanel. Now to exit\n");
e68b47dc
JH
1535 exit(1);
1536 }
1537
f277dbb7
HJYP
1538 /* Add our own icons to the search path of icon theme */
1539 gtk_icon_theme_append_search_path( gtk_icon_theme_get_default(),
1540 PACKAGE_DATA_DIR "/lxpanel/images" );
1541
cf701cb7 1542 fbev = fb_ev_new();
e2957bd2 1543 win_grp = gtk_window_group_new();
22242ed4 1544
f7cb330e
HJYP
1545restart:
1546 is_restarting = FALSE;
1547
cf701cb7
HJYP
1548 load_global_config();
1549
1d9dc649
HJYP
1550 /* NOTE: StructureNotifyMask is required by XRandR
1551 * See init_randr_support() in gdkscreen-x11.c of gtk+ for detail.
1552 */
1553 XSelectInput (GDK_DISPLAY(), GDK_ROOT_WINDOW(), StructureNotifyMask|SubstructureNotifyMask|PropertyChangeMask);
cf701cb7 1554 gdk_window_add_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, NULL);
8110399f 1555
cf701cb7
HJYP
1556 if( G_UNLIKELY( ! start_all_panels() ) )
1557 g_warning( "Config files are not found.\n" );
8110399f
HJYP
1558/*
1559 * FIXME: configure??
6a6ad54e
HJYP
1560 if (config)
1561 configure();
8110399f 1562*/
6a6ad54e 1563 gtk_main();
8110399f 1564
cf701cb7
HJYP
1565 XSelectInput (GDK_DISPLAY(), GDK_ROOT_WINDOW(), NoEventMask);
1566 gdk_window_remove_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, NULL);
1567
8110399f
HJYP
1568 /* destroy all panels */
1569 g_slist_foreach( all_panels, (GFunc) panel_destroy, NULL );
2a26c0d8
HJYP
1570 g_slist_free( all_panels );
1571 all_panels = NULL;
6a6ad54e 1572 g_free( cfgfile );
5541b8d2 1573
cf701cb7
HJYP
1574 free_global_config();
1575
f7cb330e
HJYP
1576 if( is_restarting )
1577 goto restart;
1578
e2957bd2 1579 g_object_unref(win_grp);
22242ed4
HJYP
1580 g_object_unref(fbev);
1581
5541b8d2 1582 return 0;
a52c2257 1583}