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