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