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