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