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