Merge with master
[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 <glib/gstdio.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 "lxpanelctl.h"
40 #include "dbg.h"
41
42 static gchar *cfgfile = NULL;
43 static gchar version[] = VERSION;
44 gchar *cprofile = "default";
45
46 static GtkWindowGroup* win_grp; /* window group used to limit the scope of model dialog. */
47
48 static int config = 0;
49 FbEv *fbev = NULL;
50
51 GSList* all_panels = NULL; /* a single-linked list storing all panels */
52
53 gboolean is_restarting = FALSE;
54
55 static int panel_start( Panel *p, char **fp );
56 static void panel_start_gui(Panel *p);
57 void panel_config_save(Panel* panel); /* defined in configurator.c */
58
59 gboolean is_in_lxde = FALSE;
60
61 /* A hack used to be compatible with Gnome panel for gtk+ themes.
62 * Some gtk+ themes define special styles for desktop panels.
63 * http://live.gnome.org/GnomeArt/Tutorials/GtkThemes/GnomePanel
64 * So we make a derived class from GtkWindow named PanelToplevel
65 * and create the panels with it to be compatible with Gnome themes.
66 */
67 #define PANEL_TOPLEVEL_TYPE (panel_toplevel_get_type())
68
69 typedef struct _PanelToplevel PanelToplevel;
70 typedef struct _PanelToplevelClass PanelToplevelClass;
71 struct _PanelToplevel
72 {
73 GtkWindow parent;
74 };
75 struct _PanelToplevelClass
76 {
77 GtkWindowClass parent_class;
78 };
79 G_DEFINE_TYPE(PanelToplevel, panel_toplevel, GTK_TYPE_WINDOW);
80 static void panel_toplevel_class_init(PanelToplevelClass *klass)
81 {
82 }
83 static void panel_toplevel_init(PanelToplevel *self)
84 {
85 }
86
87 /* Allocate and initialize new Panel structure. */
88 static Panel* panel_allocate(void)
89 {
90 Panel* p = g_new0(Panel, 1);
91 p->allign = ALLIGN_CENTER;
92 p->edge = EDGE_NONE;
93 p->widthtype = WIDTH_PERCENT;
94 p->width = 100;
95 p->heighttype = HEIGHT_PIXEL;
96 p->height = PANEL_HEIGHT_DEFAULT;
97 p->monitor = 0;
98 p->setdocktype = 1;
99 p->setstrut = 1;
100 p->round_corners = 0;
101 p->autohide = 0;
102 p->visible = TRUE;
103 p->height_when_hidden = 2;
104 p->transparent = 0;
105 p->alpha = 255;
106 gdk_color_parse("white", &p->gtintcolor);
107 p->tintcolor = gcolor2rgb24(&p->gtintcolor);
108 p->usefontcolor = 0;
109 p->fontcolor = 0x00000000;
110 p->usefontsize = 0;
111 p->fontsize = 10;
112 p->spacing = 0;
113 p->icon_size = PANEL_ICON_SIZE;
114 p->icon_theme = gtk_icon_theme_get_default();
115 return p;
116 }
117
118 /* Normalize panel configuration after load from file or reconfiguration. */
119 static void panel_normalize_configuration(Panel* p)
120 {
121 panel_set_panel_configuration_changed( p );
122 if (p->width < 0)
123 p->width = 100;
124 if (p->widthtype == WIDTH_PERCENT && p->width > 100)
125 p->width = 100;
126 p->heighttype = HEIGHT_PIXEL;
127 if (p->heighttype == HEIGHT_PIXEL) {
128 if (p->height < PANEL_HEIGHT_MIN)
129 p->height = PANEL_HEIGHT_MIN;
130 else if (p->height > PANEL_HEIGHT_MAX)
131 p->height = PANEL_HEIGHT_MAX;
132 }
133 if (p->monitor < 0)
134 p->monitor = 0;
135 if (p->background)
136 p->transparent = 0;
137 }
138
139 /****************************************************
140 * panel's handlers for WM events *
141 ****************************************************/
142
143 void panel_set_wm_strut(Panel *p)
144 {
145 int index;
146 gulong strut_size;
147 gulong strut_lower;
148 gulong strut_upper;
149
150 /* Dispatch on edge to set up strut parameters. */
151 switch (p->edge)
152 {
153 case EDGE_LEFT:
154 index = 0;
155 strut_size = p->aw;
156 strut_lower = p->ay;
157 strut_upper = p->ay + p->ah;
158 break;
159 case EDGE_RIGHT:
160 index = 1;
161 strut_size = p->aw;
162 strut_lower = p->ay;
163 strut_upper = p->ay + p->ah;
164 break;
165 case EDGE_TOP:
166 index = 2;
167 strut_size = p->ah;
168 strut_lower = p->ax;
169 strut_upper = p->ax + p->aw;
170 break;
171 case EDGE_BOTTOM:
172 index = 3;
173 strut_size = p->ah;
174 strut_lower = p->ax;
175 strut_upper = p->ax + p->aw;
176 break;
177 default:
178 return;
179 }
180
181 /* Handle autohide case. EWMH recommends having the strut be the minimized size. */
182 if (p->autohide)
183 strut_size = p->height_when_hidden;
184
185 /* Set up strut value in property format. */
186 gulong desired_strut[12];
187 memset(desired_strut, 0, sizeof(desired_strut));
188 if (p->setstrut)
189 {
190 desired_strut[index] = strut_size;
191 desired_strut[4 + index * 2] = strut_lower;
192 desired_strut[5 + index * 2] = strut_upper;
193 }
194 else
195 {
196 strut_size = 0;
197 strut_lower = 0;
198 strut_upper = 0;
199 }
200
201 /* If strut value changed, set the property value on the panel window.
202 * This avoids property change traffic when the panel layout is recalculated but strut geometry hasn't changed. */
203 if ((GTK_WIDGET_MAPPED(p->topgwin))
204 && ((p->strut_size != strut_size) || (p->strut_lower != strut_lower) || (p->strut_upper != strut_upper) || (p->strut_edge != p->edge)))
205 {
206 p->strut_size = strut_size;
207 p->strut_lower = strut_lower;
208 p->strut_upper = strut_upper;
209 p->strut_edge = p->edge;
210
211 /* If window manager supports STRUT_PARTIAL, it will ignore STRUT.
212 * Set STRUT also for window managers that do not support STRUT_PARTIAL. */
213 if (strut_size != 0)
214 {
215 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT_PARTIAL,
216 XA_CARDINAL, 32, PropModeReplace, (unsigned char *) desired_strut, 12);
217 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT,
218 XA_CARDINAL, 32, PropModeReplace, (unsigned char *) desired_strut, 4);
219 }
220 else
221 {
222 XDeleteProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT);
223 XDeleteProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT_PARTIAL);
224 }
225 }
226 }
227
228 /* defined in plugins/menu.c */
229 gboolean show_system_menu( gpointer system_menu );
230
231 /* defined in configurator.c */
232 void panel_configure(Panel* p, int sel_page );
233 gboolean panel_edge_available(Panel* p, int edge, gint monitor);
234
235 /* built-in commands, defined in configurator.c */
236 void restart(void);
237 void gtk_run(void);
238 void panel_destroy(Panel *p);
239
240 static void process_client_msg ( XClientMessageEvent* ev )
241 {
242 int cmd = ev->data.b[0];
243 switch( cmd )
244 {
245 #ifndef DISABLE_MENU
246 case LXPANEL_CMD_SYS_MENU:
247 {
248 GSList* l;
249 for( l = all_panels; l; l = l->next )
250 {
251 Panel* p = (Panel*)l->data;
252 if( p->system_menus )
253 {
254 /* show_system_menu( p->system_menus->data ); */
255 /* FIXME: I've no idea why this doesn't work without timeout
256 under some WMs, like icewm. */
257 g_timeout_add( 200, (GSourceFunc)show_system_menu,
258 p->system_menus->data );
259 }
260 }
261 break;
262 }
263 #endif
264 #ifndef DISABLE_MENU
265 case LXPANEL_CMD_RUN:
266 gtk_run();
267 break;
268 #endif
269 case LXPANEL_CMD_CONFIG:
270 {
271 Panel * p = ((all_panels != NULL) ? all_panels->data : NULL);
272 if (p != NULL)
273 panel_configure(p, 0);
274 }
275 break;
276 case LXPANEL_CMD_RESTART:
277 restart();
278 break;
279 case LXPANEL_CMD_EXIT:
280 gtk_main_quit();
281 break;
282 }
283 }
284
285 static GdkFilterReturn
286 panel_event_filter(GdkXEvent *xevent, GdkEvent *event, gpointer not_used)
287 {
288 Atom at;
289 Window win;
290 XEvent *ev = (XEvent *) xevent;
291
292 ENTER;
293 DBG("win = 0x%x\n", ev->xproperty.window);
294 if (ev->type != PropertyNotify )
295 {
296 /* private client message from lxpanelctl */
297 if( ev->type == ClientMessage && ev->xproperty.atom == a_LXPANEL_CMD )
298 {
299 process_client_msg( (XClientMessageEvent*)ev );
300 }
301 else if( ev->type == DestroyNotify )
302 {
303 fb_ev_emit_destroy( fbev, ((XDestroyWindowEvent*)ev)->window );
304 }
305 RET(GDK_FILTER_CONTINUE);
306 }
307
308 at = ev->xproperty.atom;
309 win = ev->xproperty.window;
310 if (win == GDK_ROOT_WINDOW())
311 {
312 if (at == a_NET_CLIENT_LIST)
313 {
314 fb_ev_emit(fbev, EV_CLIENT_LIST);
315 }
316 else if (at == a_NET_CURRENT_DESKTOP)
317 {
318 GSList* l;
319 for( l = all_panels; l; l = l->next )
320 ((Panel*)l->data)->curdesk = get_net_current_desktop();
321 fb_ev_emit(fbev, EV_CURRENT_DESKTOP);
322 }
323 else if (at == a_NET_NUMBER_OF_DESKTOPS)
324 {
325 GSList* l;
326 for( l = all_panels; l; l = l->next )
327 ((Panel*)l->data)->desknum = get_net_number_of_desktops();
328 fb_ev_emit(fbev, EV_NUMBER_OF_DESKTOPS);
329 }
330 else if (at == a_NET_DESKTOP_NAMES)
331 {
332 fb_ev_emit(fbev, EV_DESKTOP_NAMES);
333 }
334 else if (at == a_NET_ACTIVE_WINDOW)
335 {
336 fb_ev_emit(fbev, EV_ACTIVE_WINDOW );
337 }
338 else if (at == a_NET_CLIENT_LIST_STACKING)
339 {
340 fb_ev_emit(fbev, EV_CLIENT_LIST_STACKING);
341 }
342 else if (at == a_XROOTPMAP_ID)
343 {
344 GSList* l;
345 for( l = all_panels; l; l = l->next )
346 {
347 Panel* p = (Panel*)l->data;
348 if (p->transparent) {
349 fb_bg_notify_changed_bg(p->bg);
350 }
351 }
352 }
353 else if (at == a_NET_WORKAREA)
354 {
355 GSList* l;
356 for( l = all_panels; l; l = l->next )
357 {
358 Panel* p = (Panel*)l->data;
359 g_free( p->workarea );
360 p->workarea = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_WORKAREA, XA_CARDINAL, &p->wa_len);
361 /* print_wmdata(p); */
362 }
363 }
364 else
365 return GDK_FILTER_CONTINUE;
366
367 return GDK_FILTER_REMOVE;
368 }
369 return GDK_FILTER_CONTINUE;
370 }
371
372 /****************************************************
373 * panel's handlers for GTK events *
374 ****************************************************/
375
376
377 static gint
378 panel_delete_event(GtkWidget * widget, GdkEvent * event, gpointer data)
379 {
380 ENTER;
381 RET(FALSE);
382 }
383
384 static gint
385 panel_destroy_event(GtkWidget * widget, GdkEvent * event, gpointer data)
386 {
387 //Panel *p = (Panel *) data;
388 //if (!p->self_destroy)
389 gtk_main_quit();
390 RET(FALSE);
391 }
392
393 static void
394 on_root_bg_changed(FbBg *bg, Panel* p)
395 {
396 panel_update_background( p );
397 }
398
399 void panel_determine_background_pixmap(Panel * p, GtkWidget * widget, GdkWindow * window)
400 {
401 GdkPixmap * pixmap = NULL;
402
403 /* Free p->bg if it is not going to be used. */
404 if (( ! p->transparent) && (p->bg != NULL))
405 {
406 g_signal_handlers_disconnect_by_func(G_OBJECT(p->bg), on_root_bg_changed, p);
407 g_object_unref(p->bg);
408 p->bg = NULL;
409 }
410
411 if (p->background)
412 {
413 /* User specified background pixmap. */
414 if (p->background_file != NULL)
415 pixmap = fb_bg_get_pix_from_file(widget, p->background_file);
416 }
417
418 else if (p->transparent)
419 {
420 /* Transparent. Determine the appropriate value from the root pixmap. */
421 if (p->bg == NULL)
422 {
423 p->bg = fb_bg_get_for_display();
424 g_signal_connect(G_OBJECT(p->bg), "changed", G_CALLBACK(on_root_bg_changed), p);
425 }
426 pixmap = fb_bg_get_xroot_pix_for_win(p->bg, widget);
427 if ((pixmap != NULL) && (pixmap != GDK_NO_BG) && (p->alpha != 0))
428 fb_bg_composite(pixmap, &p->gtintcolor, p->alpha);
429 }
430
431 if (pixmap != NULL)
432 {
433 gtk_widget_set_app_paintable(widget, TRUE );
434 gdk_window_set_back_pixmap(window, pixmap, FALSE);
435 g_object_unref(pixmap);
436 }
437 else
438 gtk_widget_set_app_paintable(widget, FALSE);
439 }
440
441 /* Update the background of the entire panel.
442 * This function should only be called after the panel has been realized. */
443 void panel_update_background(Panel * p)
444 {
445 /* Redraw the top level widget. */
446 panel_determine_background_pixmap(p, p->topgwin, p->topgwin->window);
447 gdk_window_clear(p->topgwin->window);
448 gtk_widget_queue_draw(p->topgwin);
449
450 /* Loop over all plugins redrawing each plugin. */
451 GList * l;
452 for (l = p->plugins; l != NULL; l = l->next)
453 {
454 Plugin * pl = (Plugin *) l->data;
455 if (pl->pwid != NULL)
456 plugin_widget_set_background(pl->pwid, p);
457 }
458
459 }
460
461 static gboolean delay_update_background( Panel* p )
462 {
463 /* Panel could be destroyed while background update scheduled */
464 if ( p->topgwin && GTK_WIDGET_REALIZED ( p->topgwin ) ) {
465 gdk_display_sync( gtk_widget_get_display(p->topgwin) );
466 panel_update_background( p );
467 }
468
469 return FALSE;
470 }
471
472 static void
473 panel_realize(GtkWidget *widget, Panel *p)
474 {
475 g_idle_add_full( G_PRIORITY_LOW,
476 (GSourceFunc)delay_update_background, p, NULL );
477 }
478
479 static void
480 panel_style_set(GtkWidget *widget, GtkStyle* prev, Panel *p)
481 {
482 /* FIXME: This dirty hack is used to fix the background of systray... */
483 if( GTK_WIDGET_REALIZED( widget ) )
484 g_idle_add_full( G_PRIORITY_LOW,
485 (GSourceFunc)delay_update_background, p, NULL );
486 }
487
488 static gint
489 panel_size_req(GtkWidget *widget, GtkRequisition *req, Panel *p)
490 {
491 ENTER;
492
493 if (p->autohide && !p->visible)
494 /* When the panel is in invisible state, the content box also got hidden, thus always
495 * report 0 size. Ask the content box instead for its size. */
496 gtk_widget_size_request(p->box, req);
497
498 if (p->widthtype == WIDTH_REQUEST)
499 p->width = (p->orientation == ORIENT_HORIZ) ? req->width : req->height;
500 if (p->heighttype == HEIGHT_REQUEST)
501 p->height = (p->orientation == ORIENT_HORIZ) ? req->height : req->width;
502
503 calculate_position(p);
504
505 gtk_widget_set_size_request( widget, p->aw, p->ah );
506
507 RET( TRUE );
508 }
509
510 static gint
511 panel_size_alloc(GtkWidget *widget, GtkAllocation *a, Panel *p)
512 {
513 ENTER;
514 if (p->widthtype == WIDTH_REQUEST)
515 p->width = (p->orientation == ORIENT_HORIZ) ? a->width : a->height;
516 if (p->heighttype == HEIGHT_REQUEST)
517 p->height = (p->orientation == ORIENT_HORIZ) ? a->height : a->width;
518 calculate_position(p);
519
520 if (a->width == p->aw && a->height == p->ah && a->x == p->ax && a->y == p ->ay) {
521 RET(TRUE);
522 }
523
524 gtk_window_move(GTK_WINDOW(p->topgwin), p->ax, p->ay);
525 panel_set_wm_strut(p);
526 RET(TRUE);
527 }
528
529 static gboolean
530 panel_configure_event (GtkWidget *widget, GdkEventConfigure *e, Panel *p)
531 {
532 ENTER;
533 if (e->width == p->cw && e->height == p->ch && e->x == p->cx && e->y == p->cy)
534 RET(TRUE);
535 p->cw = e->width;
536 p->ch = e->height;
537 p->cx = e->x;
538 p->cy = e->y;
539
540 if (p->transparent)
541 fb_bg_notify_changed_bg(p->bg);
542
543 RET(FALSE);
544 }
545
546 static gint
547 panel_popupmenu_configure(GtkWidget *widget, gpointer user_data)
548 {
549 panel_configure( (Panel*)user_data, 0 );
550 return TRUE;
551 }
552
553 /* Handler for "button_press_event" signal with Panel as parameter. */
554 static gboolean panel_button_press_event_with_panel(GtkWidget *widget, GdkEventButton *event, Panel *panel)
555 {
556 if (event->button == 3) /* right button */
557 {
558 GtkMenu* popup = (GtkMenu*) lxpanel_get_panel_menu(panel, NULL, FALSE);
559 gtk_menu_popup(popup, NULL, NULL, NULL, NULL, event->button, event->time);
560 return TRUE;
561 }
562 return FALSE;
563 }
564
565 static void panel_popupmenu_config_plugin( GtkMenuItem* item, Plugin* plugin )
566 {
567 plugin->class->config( plugin, GTK_WINDOW(plugin->panel->topgwin) );
568
569 /* FIXME: this should be more elegant */
570 plugin->panel->config_changed = TRUE;
571 }
572
573 static void panel_popupmenu_add_item( GtkMenuItem* item, Panel* panel )
574 {
575 /* panel_add_plugin( panel, panel->topgwin ); */
576 panel_configure( panel, 2 );
577 }
578
579 static void panel_popupmenu_remove_item( GtkMenuItem* item, Plugin* plugin )
580 {
581 Panel* panel = plugin->panel;
582
583 /* If the configuration dialog is open, there will certainly be a crash if the
584 * user manipulates the Configured Plugins list, after we remove this entry.
585 * Close the configuration dialog if it is open. */
586 if (panel->pref_dialog != NULL)
587 {
588 gtk_widget_destroy(panel->pref_dialog);
589 panel->pref_dialog = NULL;
590 }
591
592 panel->plugins = g_list_remove( panel->plugins, plugin );
593 panel_config_save( plugin->panel );
594 plugin_delete(plugin);
595 }
596
597 /* FIXME: Potentially we can support multiple panels at the same edge,
598 * but currently this cannot be done due to some positioning problems. */
599 static char* gen_panel_name( int edge, gint monitor )
600 {
601 const char* edge_str = num2str( edge_pair, edge, "" );
602 char* name = NULL;
603 char* dir = get_config_file( cprofile, "panels", FALSE );
604 int i;
605 for( i = 0; i < G_MAXINT; ++i )
606 {
607 char* f;
608 if(monitor != 0)
609 name = g_strdup_printf( "%s-m%d-%d", edge_str, monitor, i );
610 else if( G_LIKELY( i > 0 ) )
611 name = g_strdup_printf( "%s%d", edge_str, i );
612 else
613 name = g_strdup( edge_str );
614
615 f = g_build_filename( dir, name, NULL );
616 if( ! g_file_test( f, G_FILE_TEST_EXISTS ) )
617 {
618 g_free( f );
619 break;
620 }
621 g_free( name );
622 g_free( f );
623 }
624 g_free( dir );
625 return name;
626 }
627
628 /* FIXME: Potentially we can support multiple panels at the same edge,
629 * but currently this cannot be done due to some positioning problems. */
630 static void panel_popupmenu_create_panel( GtkMenuItem* item, Panel* panel )
631 {
632 gint m, e, monitors;
633 GdkScreen *screen;
634 GtkWidget *err;
635 Panel* new_panel = panel_allocate();
636
637 /* Allocate the edge. */
638 screen = gdk_screen_get_default();
639 g_assert(screen);
640 monitors = gdk_screen_get_n_monitors(screen);
641 for(m=0; m<monitors; ++m)
642 {
643 /* try each of the four edges */
644 for(e=1; e<5; ++e)
645 {
646 if(panel_edge_available(new_panel,e,m)) {
647 new_panel->edge = e;
648 new_panel->monitor = m;
649 goto found_edge;
650 }
651 }
652 }
653
654 ERR("Error adding panel: There is no room for another panel. All the edges are taken.\n");
655 err = gtk_message_dialog_new(NULL,0,GTK_MESSAGE_ERROR,GTK_BUTTONS_OK,_("There is no room for another panel. All the edges are taken."));
656 gtk_dialog_run(GTK_DIALOG(err));
657 gtk_widget_destroy(err);
658 return;
659
660 found_edge:
661 new_panel->name = gen_panel_name(new_panel->edge,new_panel->monitor);
662
663 panel_configure(new_panel, 0);
664 panel_normalize_configuration(new_panel);
665 panel_start_gui(new_panel);
666 gtk_widget_show_all(new_panel->topgwin);
667
668 panel_config_save(new_panel);
669 all_panels = g_slist_prepend(all_panels, new_panel);
670 }
671
672 static void panel_popupmenu_delete_panel( GtkMenuItem* item, Panel* panel )
673 {
674 GtkWidget* dlg;
675 gboolean ok;
676 dlg = gtk_message_dialog_new_with_markup( GTK_WINDOW(panel->topgwin),
677 GTK_DIALOG_MODAL,
678 GTK_MESSAGE_QUESTION,
679 GTK_BUTTONS_OK_CANCEL,
680 _("Really delete this panel?\n<b>Warning: This can not be recovered.</b>") );
681 panel_apply_icon(GTK_WINDOW(dlg));
682 gtk_window_set_title( (GtkWindow*)dlg, _("Confirm") );
683 ok = ( gtk_dialog_run( (GtkDialog*)dlg ) == GTK_RESPONSE_OK );
684 gtk_widget_destroy( dlg );
685 if( ok )
686 {
687 gchar *fname, *dir;
688 all_panels = g_slist_remove( all_panels, panel );
689
690 /* delete the config file of this panel */
691 dir = get_config_file( cprofile, "panels", FALSE );
692 fname = g_build_filename( dir, panel->name, NULL );
693 g_free( dir );
694 g_unlink( fname );
695 panel->config_changed = 0;
696 panel_destroy( panel );
697 }
698 }
699
700 static void panel_popupmenu_about( GtkMenuItem* item, Panel* panel )
701 {
702 GtkWidget *about;
703 const gchar* authors[] = {
704 "Hong Jen Yee (PCMan) <pcman.tw@gmail.com>",
705 "Jim Huang <jserv.tw@gmail.com>",
706 "Greg McNew <gmcnew@gmail.com> (battery plugin)",
707 "Fred Chien <cfsghost@gmail.com>",
708 "Daniel Kesler <kesler.daniel@gmail.com>",
709 "Juergen Hoetzel <juergen@archlinux.org>",
710 "Marty Jack <martyj19@comcast.net>",
711 "Martin Bagge <brother@bsnet.se>",
712 NULL
713 };
714 /* TRANSLATORS: Replace this string with your names, one name per line. */
715 gchar *translators = _( "translator-credits" );
716
717 about = gtk_about_dialog_new();
718 panel_apply_icon(GTK_WINDOW(about));
719 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about), VERSION);
720 gtk_about_dialog_set_name(GTK_ABOUT_DIALOG(about), _("LXPanel"));
721
722 if(gtk_icon_theme_has_icon(panel->icon_theme, "video-display"))
723 {
724 gtk_about_dialog_set_logo( GTK_ABOUT_DIALOG(about),
725 gtk_icon_theme_load_icon(panel->icon_theme, "video-display", 48, 0, NULL));
726 }
727 else if (gtk_icon_theme_has_icon(panel->icon_theme, "start-here"))
728 {
729 gtk_about_dialog_set_logo( GTK_ABOUT_DIALOG(about),
730 gtk_icon_theme_load_icon(panel->icon_theme, "start-here", 48, 0, NULL));
731 }
732 else
733 {
734 gtk_about_dialog_set_logo( GTK_ABOUT_DIALOG(about),
735 gdk_pixbuf_new_from_file(PACKAGE_DATA_DIR "/lxpanel/images/my-computer.png", NULL));
736 }
737
738 gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(about), _("Copyright (C) 2008-2013"));
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
748 void panel_apply_icon( GtkWindow *w )
749 {
750 GdkPixbuf* window_icon;
751
752 if(gtk_icon_theme_has_icon(gtk_icon_theme_get_default(), "video-display"))
753 {
754 window_icon = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "video-display", 24, 0, NULL);
755 }
756 else if(gtk_icon_theme_has_icon(gtk_icon_theme_get_default(), "start-here"))
757 {
758 window_icon = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "start-here", 24, 0, NULL);
759 }
760 else
761 {
762 window_icon = gdk_pixbuf_new_from_file(PACKAGE_DATA_DIR "/lxpanel/images/my-computer.png", NULL);
763 }
764 gtk_window_set_icon(w, window_icon);
765 }
766
767 GtkMenu* lxpanel_get_panel_menu( Panel* panel, Plugin* plugin, gboolean use_sub_menu )
768 {
769 GtkWidget *menu_item, *img;
770 GtkMenu *ret,*menu;
771
772 char* tmp;
773 ret = menu = GTK_MENU(gtk_menu_new());
774
775 img = gtk_image_new_from_stock( GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU );
776 menu_item = gtk_image_menu_item_new_with_label(_("Add / Remove Panel Items"));
777 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
778 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
779 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_add_item), panel );
780
781 if( plugin )
782 {
783 img = gtk_image_new_from_stock( GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU );
784 tmp = g_strdup_printf( _("Remove \"%s\" From Panel"), _(plugin->class->name) );
785 menu_item = gtk_image_menu_item_new_with_label( tmp );
786 g_free( tmp );
787 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
788 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
789 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_remove_item), plugin );
790 }
791
792 menu_item = gtk_separator_menu_item_new();
793 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
794
795 img = gtk_image_new_from_stock( GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU );
796 menu_item = gtk_image_menu_item_new_with_label(_("Panel Settings"));
797 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
798 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
799 g_signal_connect(G_OBJECT(menu_item), "activate", G_CALLBACK(panel_popupmenu_configure), panel );
800
801 img = gtk_image_new_from_stock( GTK_STOCK_NEW, GTK_ICON_SIZE_MENU );
802 menu_item = gtk_image_menu_item_new_with_label(_("Create New Panel"));
803 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
804 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
805 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_create_panel), panel );
806
807 img = gtk_image_new_from_stock( GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU );
808 menu_item = gtk_image_menu_item_new_with_label(_("Delete This Panel"));
809 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
810 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
811 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_delete_panel), panel );
812 if( ! all_panels->next ) /* if this is the only panel */
813 gtk_widget_set_sensitive( menu_item, FALSE );
814
815 menu_item = gtk_separator_menu_item_new();
816 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
817
818 img = gtk_image_new_from_stock( GTK_STOCK_ABOUT, GTK_ICON_SIZE_MENU );
819 menu_item = gtk_image_menu_item_new_with_label(_("About"));
820 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
821 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
822 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_about), panel );
823
824 if( use_sub_menu )
825 {
826 ret = GTK_MENU(gtk_menu_new());
827 menu_item = gtk_image_menu_item_new_with_label(_("Panel"));
828 gtk_menu_shell_append(GTK_MENU_SHELL(ret), menu_item);
829 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), GTK_WIDGET(menu) );
830
831 gtk_widget_show_all(GTK_WIDGET(ret));
832 }
833
834 if( plugin )
835 {
836 menu_item = gtk_separator_menu_item_new();
837 gtk_menu_shell_prepend(GTK_MENU_SHELL(ret), menu_item);
838
839 img = gtk_image_new_from_stock( GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU );
840 tmp = g_strdup_printf( _("\"%s\" Settings"), _(plugin->class->name) );
841 menu_item = gtk_image_menu_item_new_with_label( tmp );
842 g_free( tmp );
843 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
844 gtk_menu_shell_prepend(GTK_MENU_SHELL(ret), menu_item);
845 if( plugin->class->config )
846 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_config_plugin), plugin );
847 else
848 gtk_widget_set_sensitive( menu_item, FALSE );
849 }
850
851 gtk_widget_show_all(GTK_WIDGET(menu));
852
853 g_signal_connect( ret, "selection-done", G_CALLBACK(gtk_widget_destroy), NULL );
854 return ret;
855 }
856
857 /****************************************************
858 * panel creation *
859 ****************************************************/
860
861 static void
862 make_round_corners(Panel *p)
863 {
864 /* FIXME: This should be re-written with shape extension of X11 */
865 /* gdk_window_shape_combine_mask() can be used */
866 }
867
868 void panel_set_dock_type(Panel *p)
869 {
870 if (p->setdocktype) {
871 Atom state = a_NET_WM_WINDOW_TYPE_DOCK;
872 XChangeProperty(GDK_DISPLAY(), p->topxwin,
873 a_NET_WM_WINDOW_TYPE, XA_ATOM, 32,
874 PropModeReplace, (unsigned char *) &state, 1);
875 }
876 else {
877 XDeleteProperty( GDK_DISPLAY(), p->topxwin, a_NET_WM_WINDOW_TYPE );
878 }
879 }
880
881 static void panel_set_visibility(Panel *p, gboolean visible)
882 {
883 if ( ! visible) gtk_widget_hide(p->box);
884 p->visible = visible;
885 calculate_position(p);
886 gtk_widget_set_size_request(p->topgwin, p->aw, p->ah);
887 gdk_window_move(p->topgwin->window, p->ax, p->ay);
888 if (visible) gtk_widget_show(p->box);
889 panel_set_wm_strut(p);
890 }
891
892 static gboolean panel_leave_real(Panel *p)
893 {
894 /* If the pointer is grabbed by this application, leave the panel displayed.
895 * There is no way to determine if it is grabbed by another application, such as an application that has a systray icon. */
896 if (gdk_display_pointer_is_grabbed(p->display))
897 return TRUE;
898
899 /* If the pointer is inside the panel, leave the panel displayed. */
900 gint x, y;
901 gdk_display_get_pointer(p->display, NULL, &x, &y, NULL);
902 if ((p->cx <= x) && (x <= (p->cx + p->cw)) && (p->cy <= y) && (y <= (p->cy + p->ch)))
903 return TRUE;
904
905 /* If the panel is configured to autohide and if it is visible, hide the panel. */
906 if ((p->autohide) && (p->visible))
907 panel_set_visibility(p, FALSE);
908
909 /* Clear the timer. */
910 p->hide_timeout = 0;
911 return FALSE;
912 }
913
914 static gboolean panel_enter(GtkImage *widget, GdkEventCrossing *event, Panel *p)
915 {
916 /* We may receive multiple enter-notify events when the pointer crosses into the panel.
917 * Do extra tests to make sure this does not cause misoperation such as blinking.
918 * If the pointer is inside the panel, unhide it. */
919 gint x, y;
920 gdk_display_get_pointer(p->display, NULL, &x, &y, NULL);
921 if ((p->cx <= x) && (x <= (p->cx + p->cw)) && (p->cy <= y) && (y <= (p->cy + p->ch)))
922 {
923 /* If the pointer is inside the panel and we have not already unhidden it, do so and
924 * set a timer to recheck it in a half second. */
925 if (p->hide_timeout == 0)
926 {
927 p->hide_timeout = g_timeout_add(500, (GSourceFunc) panel_leave_real, p);
928 panel_set_visibility(p, TRUE);
929 }
930 }
931 else
932 {
933 /* If the pointer is not inside the panel, simulate a timer expiration. */
934 panel_leave_real(p);
935 }
936 return TRUE;
937 }
938
939 static gboolean panel_drag_motion(GtkWidget *widget, GdkDragContext *drag_context, gint x,
940 gint y, guint time, Panel *p)
941 {
942 panel_enter(NULL, NULL, p);
943 return TRUE;
944 }
945
946 void panel_establish_autohide(Panel *p)
947 {
948 if (p->autohide)
949 {
950 gtk_widget_add_events(p->topgwin, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
951 g_signal_connect(G_OBJECT(p->topgwin), "enter-notify-event", G_CALLBACK(panel_enter), p);
952 g_signal_connect(G_OBJECT(p->topgwin), "drag-motion", (GCallback) panel_drag_motion, p);
953 gtk_drag_dest_set(p->topgwin, GTK_DEST_DEFAULT_MOTION, NULL, 0, 0);
954 gtk_drag_dest_set_track_motion(p->topgwin, TRUE);
955 panel_enter(NULL, NULL, p);
956 }
957 else if ( ! p->visible)
958 {
959 gtk_widget_show(p->box);
960 p->visible = TRUE;
961 }
962 }
963
964 /* Set an image from a file with scaling to the panel icon size. */
965 void panel_image_set_from_file(Panel * p, GtkWidget * image, const char * file)
966 {
967 GdkPixbuf * pixbuf = gdk_pixbuf_new_from_file_at_scale(file, p->icon_size, p->icon_size, TRUE, NULL);
968 if (pixbuf != NULL)
969 {
970 gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf);
971 g_object_unref(pixbuf);
972 }
973 }
974
975 /* Set an image from a icon theme with scaling to the panel icon size. */
976 gboolean panel_image_set_icon_theme(Panel * p, GtkWidget * image, const gchar * icon)
977 {
978 if (gtk_icon_theme_has_icon(p->icon_theme, icon))
979 {
980 GdkPixbuf * pixbuf = gtk_icon_theme_load_icon(p->icon_theme, icon, p->icon_size, 0, NULL);
981 gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf);
982 g_object_unref(pixbuf);
983 return TRUE;
984 }
985 return FALSE;
986 }
987
988 static void
989 panel_start_gui(Panel *p)
990 {
991 Atom state[3];
992 XWMHints wmhints;
993 guint32 val;
994
995 ENTER;
996
997 p->curdesk = get_net_current_desktop();
998 p->desknum = get_net_number_of_desktops();
999 p->workarea = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_WORKAREA, XA_CARDINAL, &p->wa_len);
1000
1001 /* main toplevel window */
1002 /* p->topgwin = gtk_window_new(GTK_WINDOW_TOPLEVEL); */
1003 p->topgwin = (GtkWidget*)g_object_new(PANEL_TOPLEVEL_TYPE, NULL);
1004 gtk_widget_set_name(p->topgwin, "PanelToplevel");
1005 p->display = gdk_display_get_default();
1006 gtk_container_set_border_width(GTK_CONTAINER(p->topgwin), 0);
1007 gtk_window_set_resizable(GTK_WINDOW(p->topgwin), FALSE);
1008 gtk_window_set_wmclass(GTK_WINDOW(p->topgwin), "panel", "lxpanel");
1009 gtk_window_set_title(GTK_WINDOW(p->topgwin), "panel");
1010 gtk_window_set_position(GTK_WINDOW(p->topgwin), GTK_WIN_POS_NONE);
1011 gtk_window_set_decorated(GTK_WINDOW(p->topgwin), FALSE);
1012
1013 gtk_window_group_add_window( win_grp, (GtkWindow*)p->topgwin );
1014
1015 g_signal_connect(G_OBJECT(p->topgwin), "delete-event",
1016 G_CALLBACK(panel_delete_event), p);
1017 g_signal_connect(G_OBJECT(p->topgwin), "destroy-event",
1018 G_CALLBACK(panel_destroy_event), p);
1019 g_signal_connect (G_OBJECT (p->topgwin), "size-request",
1020 (GCallback) panel_size_req, p);
1021 g_signal_connect (G_OBJECT (p->topgwin), "size-allocate",
1022 (GCallback) panel_size_alloc, p);
1023 g_signal_connect (G_OBJECT (p->topgwin), "configure-event",
1024 (GCallback) panel_configure_event, p);
1025
1026 gtk_widget_add_events( p->topgwin, GDK_BUTTON_PRESS_MASK );
1027 g_signal_connect(G_OBJECT (p->topgwin), "button_press_event",
1028 (GCallback) panel_button_press_event_with_panel, p);
1029
1030 g_signal_connect (G_OBJECT (p->topgwin), "realize",
1031 (GCallback) panel_realize, p);
1032
1033 g_signal_connect (G_OBJECT (p->topgwin), "style-set",
1034 (GCallback)panel_style_set, p);
1035 gtk_widget_realize(p->topgwin);
1036 //gdk_window_set_decorations(p->topgwin->window, 0);
1037
1038 // main layout manager as a single child of panel
1039 p->box = p->my_box_new(FALSE, 0);
1040 gtk_container_set_border_width(GTK_CONTAINER(p->box), 0);
1041 // gtk_container_add(GTK_CONTAINER(p->bbox), p->box);
1042 gtk_container_add(GTK_CONTAINER(p->topgwin), p->box);
1043 gtk_widget_show(p->box);
1044 if (p->round_corners)
1045 make_round_corners(p);
1046
1047 p->topxwin = GDK_WINDOW_XWINDOW(GTK_WIDGET(p->topgwin)->window);
1048 DBG("topxwin = %x\n", p->topxwin);
1049
1050 /* the settings that should be done before window is mapped */
1051 wmhints.flags = InputHint;
1052 wmhints.input = 0;
1053 XSetWMHints (GDK_DISPLAY(), p->topxwin, &wmhints);
1054 #define WIN_HINTS_SKIP_FOCUS (1<<0) /* "alt-tab" skips this win */
1055 val = WIN_HINTS_SKIP_FOCUS;
1056 XChangeProperty(GDK_DISPLAY(), p->topxwin,
1057 XInternAtom(GDK_DISPLAY(), "_WIN_HINTS", False), XA_CARDINAL, 32,
1058 PropModeReplace, (unsigned char *) &val, 1);
1059
1060 panel_set_dock_type(p);
1061
1062 /* window mapping point */
1063 gtk_widget_show_all(p->topgwin);
1064
1065 /* the settings that should be done after window is mapped */
1066 panel_establish_autohide(p);
1067
1068 /* send it to running wm */
1069 Xclimsg(p->topxwin, a_NET_WM_DESKTOP, 0xFFFFFFFF, 0, 0, 0, 0);
1070 /* and assign it ourself just for case when wm is not running */
1071 val = 0xFFFFFFFF;
1072 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_DESKTOP, XA_CARDINAL, 32,
1073 PropModeReplace, (unsigned char *) &val, 1);
1074
1075 state[0] = a_NET_WM_STATE_SKIP_PAGER;
1076 state[1] = a_NET_WM_STATE_SKIP_TASKBAR;
1077 state[2] = a_NET_WM_STATE_STICKY;
1078 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STATE, XA_ATOM,
1079 32, PropModeReplace, (unsigned char *) state, 3);
1080
1081 calculate_position(p);
1082 gdk_window_move_resize(p->topgwin->window, p->ax, p->ay, p->aw, p->ah);
1083 panel_set_wm_strut(p);
1084
1085 RET();
1086 }
1087
1088 /* Exchange the "width" and "height" terminology for vertical and horizontal panels. */
1089 void panel_adjust_geometry_terminology(Panel * p)
1090 {
1091 if ((p->height_label != NULL) && (p->width_label != NULL)
1092 && (p->alignment_left_label != NULL) && (p->alignment_right_label != NULL))
1093 {
1094 if ((p->edge == EDGE_TOP) || (p->edge == EDGE_BOTTOM))
1095 {
1096 gtk_label_set_text(GTK_LABEL(p->height_label), _("Height:"));
1097 gtk_label_set_text(GTK_LABEL(p->width_label), _("Width:"));
1098 gtk_button_set_label(GTK_BUTTON(p->alignment_left_label), _("Left"));
1099 gtk_button_set_label(GTK_BUTTON(p->alignment_right_label), _("Right"));
1100 }
1101 else
1102 {
1103 gtk_label_set_text(GTK_LABEL(p->height_label), _("Width:"));
1104 gtk_label_set_text(GTK_LABEL(p->width_label), _("Height:"));
1105 gtk_button_set_label(GTK_BUTTON(p->alignment_left_label), _("Top"));
1106 gtk_button_set_label(GTK_BUTTON(p->alignment_right_label), _("Bottom"));
1107 }
1108 }
1109 }
1110
1111 /* Draw text into a label, with the user preference color and optionally bold. */
1112 void panel_draw_label_text(Panel * p, GtkWidget * label, char * text, gboolean bold,
1113 float custom_size_factor, gboolean custom_color)
1114 {
1115 if (text == NULL)
1116 {
1117 /* Null string. */
1118 gtk_label_set_text(GTK_LABEL(label), NULL);
1119 return;
1120 }
1121
1122 /* Compute an appropriate size so the font will scale with the panel's icon size. */
1123 int font_desc;
1124 if (p->usefontsize)
1125 font_desc = p->fontsize;
1126 else
1127 {
1128 if (p->icon_size < 20)
1129 font_desc = 9;
1130 else if (p->icon_size >= 20 && p->icon_size < 36)
1131 font_desc = 10;
1132 else
1133 font_desc = 12;
1134 }
1135 font_desc *= custom_size_factor;
1136
1137 /* Check the string for characters that need to be escaped.
1138 * If any are found, create the properly escaped string and use it instead. */
1139 char * valid_markup = text;
1140 char * escaped_text = NULL;
1141 char * q;
1142 for (q = text; *q != '\0'; q += 1)
1143 {
1144 if ((*q == '<') || (*q == '>') || (*q == '&'))
1145 {
1146 escaped_text = g_markup_escape_text(text, -1);
1147 valid_markup = escaped_text;
1148 break;
1149 }
1150 }
1151
1152 gchar * formatted_text;
1153 if ((custom_color) && (p->usefontcolor))
1154 {
1155 /* Color, optionally bold. */
1156 formatted_text = g_strdup_printf("<span font_desc=\"%d\" color=\"#%06x\">%s%s%s</span>",
1157 font_desc,
1158 gcolor2rgb24(&p->gfontcolor),
1159 ((bold) ? "<b>" : ""),
1160 valid_markup,
1161 ((bold) ? "</b>" : ""));
1162 }
1163 else
1164 {
1165 /* No color, optionally bold. */
1166 formatted_text = g_strdup_printf("<span font_desc=\"%d\">%s%s%s</span>",
1167 font_desc,
1168 ((bold) ? "<b>" : ""),
1169 valid_markup,
1170 ((bold) ? "</b>" : ""));
1171 }
1172
1173 gtk_label_set_markup(GTK_LABEL(label), formatted_text);
1174 g_free(formatted_text);
1175 g_free(escaped_text);
1176 }
1177
1178 void panel_set_panel_configuration_changed(Panel *p)
1179 {
1180 GList* l;
1181
1182 int previous_orientation = p->orientation;
1183 p->orientation = (p->edge == EDGE_TOP || p->edge == EDGE_BOTTOM)
1184 ? ORIENT_HORIZ : ORIENT_VERT;
1185
1186 if (previous_orientation != p->orientation)
1187 {
1188 panel_adjust_geometry_terminology(p);
1189 if (previous_orientation != ORIENT_NONE)
1190 p->height = ((p->orientation == ORIENT_HORIZ) ? PANEL_HEIGHT_DEFAULT : PANEL_WIDTH_DEFAULT);
1191 if (p->height_control != NULL)
1192 gtk_spin_button_set_value(GTK_SPIN_BUTTON(p->height_control), p->height);
1193 if ((p->widthtype == WIDTH_PIXEL) && (p->width_control != NULL))
1194 {
1195 int value = ((p->orientation == ORIENT_HORIZ) ? gdk_screen_width() : gdk_screen_height());
1196 gtk_spin_button_set_range(GTK_SPIN_BUTTON(p->width_control), 0, value);
1197 gtk_spin_button_set_value(GTK_SPIN_BUTTON(p->width_control), value);
1198 }
1199
1200 }
1201
1202 if (p->orientation == ORIENT_HORIZ) {
1203 p->my_box_new = gtk_hbox_new;
1204 p->my_separator_new = gtk_vseparator_new;
1205 } else {
1206 p->my_box_new = gtk_vbox_new;
1207 p->my_separator_new = gtk_hseparator_new;
1208 }
1209
1210 /* recreate the main layout box */
1211 if (p->box != NULL)
1212 {
1213 #if GTK_CHECK_VERSION(2,16,0)
1214 GtkOrientation bo = (p->orientation == ORIENT_HORIZ) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1215 gtk_orientable_set_orientation(GTK_ORIENTABLE(p->box), bo);
1216 #else
1217 GtkBox * newbox = GTK_BOX(recreate_box(GTK_BOX(p->box), p->orientation));
1218 if (GTK_WIDGET(newbox) != p->box)
1219 {
1220 p->box = GTK_WIDGET(newbox);
1221 gtk_container_add(GTK_CONTAINER(p->topgwin), GTK_WIDGET(newbox));
1222 }
1223 #endif
1224 }
1225
1226 /* NOTE: This loop won't be executed when panel started since
1227 plugins are not loaded at that time.
1228 This is used when the orientation of the panel is changed
1229 from the config dialog, and plugins should be re-layout.
1230 */
1231 for( l = p->plugins; l; l = l->next ) {
1232 Plugin* pl = (Plugin*)l->data;
1233 if( pl->class->panel_configuration_changed ) {
1234 pl->class->panel_configuration_changed( pl );
1235 }
1236 }
1237 }
1238
1239 static int
1240 panel_parse_global(Panel *p, char **fp)
1241 {
1242 line s;
1243 s.len = 256;
1244
1245 if( G_LIKELY( fp ) )
1246 {
1247 while (lxpanel_get_line(fp, &s) != LINE_NONE) {
1248 if (s.type == LINE_VAR) {
1249 if (!g_ascii_strcasecmp(s.t[0], "edge")) {
1250 p->edge = str2num(edge_pair, s.t[1], EDGE_NONE);
1251 } else if (!g_ascii_strcasecmp(s.t[0], "allign")) {
1252 p->allign = str2num(allign_pair, s.t[1], ALLIGN_NONE);
1253 } else if (!g_ascii_strcasecmp(s.t[0], "monitor")) {
1254 p->monitor = atoi(s.t[1]);
1255 } else if (!g_ascii_strcasecmp(s.t[0], "margin")) {
1256 p->margin = atoi(s.t[1]);
1257 } else if (!g_ascii_strcasecmp(s.t[0], "widthtype")) {
1258 p->widthtype = str2num(width_pair, s.t[1], WIDTH_NONE);
1259 } else if (!g_ascii_strcasecmp(s.t[0], "width")) {
1260 p->width = atoi(s.t[1]);
1261 } else if (!g_ascii_strcasecmp(s.t[0], "heighttype")) {
1262 p->heighttype = str2num(height_pair, s.t[1], HEIGHT_NONE);
1263 } else if (!g_ascii_strcasecmp(s.t[0], "height")) {
1264 p->height = atoi(s.t[1]);
1265 } else if (!g_ascii_strcasecmp(s.t[0], "spacing")) {
1266 p->spacing = atoi(s.t[1]);
1267 } else if (!g_ascii_strcasecmp(s.t[0], "SetDockType")) {
1268 p->setdocktype = str2num(bool_pair, s.t[1], 0);
1269 } else if (!g_ascii_strcasecmp(s.t[0], "SetPartialStrut")) {
1270 p->setstrut = str2num(bool_pair, s.t[1], 0);
1271 } else if (!g_ascii_strcasecmp(s.t[0], "RoundCorners")) {
1272 p->round_corners = str2num(bool_pair, s.t[1], 0);
1273 } else if (!g_ascii_strcasecmp(s.t[0], "Transparent")) {
1274 p->transparent = str2num(bool_pair, s.t[1], 0);
1275 } else if (!g_ascii_strcasecmp(s.t[0], "Alpha")) {
1276 p->alpha = atoi(s.t[1]);
1277 if (p->alpha > 255)
1278 p->alpha = 255;
1279 } else if (!g_ascii_strcasecmp(s.t[0], "AutoHide")) {
1280 p->autohide = str2num(bool_pair, s.t[1], 0);
1281 } else if (!g_ascii_strcasecmp(s.t[0], "HeightWhenHidden")) {
1282 p->height_when_hidden = atoi(s.t[1]);
1283 } else if (!g_ascii_strcasecmp(s.t[0], "TintColor")) {
1284 if (!gdk_color_parse (s.t[1], &p->gtintcolor))
1285 gdk_color_parse ("white", &p->gtintcolor);
1286 p->tintcolor = gcolor2rgb24(&p->gtintcolor);
1287 DBG("tintcolor=%x\n", p->tintcolor);
1288 } else if (!g_ascii_strcasecmp(s.t[0], "useFontColor")) {
1289 p->usefontcolor = str2num(bool_pair, s.t[1], 0);
1290 } else if (!g_ascii_strcasecmp(s.t[0], "FontColor")) {
1291 if (!gdk_color_parse (s.t[1], &p->gfontcolor))
1292 gdk_color_parse ("black", &p->gfontcolor);
1293 p->fontcolor = gcolor2rgb24(&p->gfontcolor);
1294 DBG("fontcolor=%x\n", p->fontcolor);
1295 } else if (!g_ascii_strcasecmp(s.t[0], "useFontSize")) {
1296 p->usefontsize = str2num(bool_pair, s.t[1], 0);
1297 } else if (!g_ascii_strcasecmp(s.t[0], "FontSize")) {
1298 p->fontsize = atoi(s.t[1]);
1299 } else if (!g_ascii_strcasecmp(s.t[0], "Background")) {
1300 p->background = str2num(bool_pair, s.t[1], 0);
1301 } else if( !g_ascii_strcasecmp(s.t[0], "BackgroundFile") ) {
1302 p->background_file = g_strdup( s.t[1] );
1303 } else if (!g_ascii_strcasecmp(s.t[0], "IconSize")) {
1304 p->icon_size = atoi(s.t[1]);
1305 } else if (!g_ascii_strcasecmp(s.t[0], "LogLevel")) {
1306 configured_log_level = atoi(s.t[1]);
1307 if (!log_level_set_on_commandline)
1308 log_level = configured_log_level;
1309 } else {
1310 ERR( "lxpanel: %s - unknown var in Global section\n", s.t[0]);
1311 }
1312 } else if (s.type == LINE_BLOCK_END) {
1313 break;
1314 } else {
1315 ERR( "lxpanel: illegal in this context %s\n", s.str);
1316 RET(0);
1317 }
1318 }
1319 }
1320
1321 panel_normalize_configuration(p);
1322
1323 return 1;
1324 }
1325
1326 static int
1327 panel_parse_plugin(Panel *p, char **fp)
1328 {
1329 line s;
1330 Plugin *plug = NULL;
1331 gchar *type = NULL;
1332 int expand , padding, border;
1333 char* pconfig = NULL;
1334
1335 ENTER;
1336 s.len = 256;
1337 border = expand = padding = 0;
1338 while (lxpanel_get_line(fp, &s) != LINE_BLOCK_END) {
1339 if (s.type == LINE_NONE) {
1340 ERR( "lxpanel: bad line %s\n", s.str);
1341 goto error;
1342 }
1343 if (s.type == LINE_VAR) {
1344 if (!g_ascii_strcasecmp(s.t[0], "type")) {
1345 type = g_strdup(s.t[1]);
1346 DBG("plug %s\n", type);
1347 } else if (!g_ascii_strcasecmp(s.t[0], "expand"))
1348 expand = str2num(bool_pair, s.t[1], 0);
1349 else if (!g_ascii_strcasecmp(s.t[0], "padding"))
1350 padding = atoi(s.t[1]);
1351 else if (!g_ascii_strcasecmp(s.t[0], "border"))
1352 border = atoi(s.t[1]);
1353 else {
1354 ERR( "lxpanel: unknown var %s\n", s.t[0]);
1355 }
1356 } else if (s.type == LINE_BLOCK_START) {
1357 if (!g_ascii_strcasecmp(s.t[0], "Config")) {
1358 pconfig = *fp;
1359 int pno = 1;
1360 while (pno) {
1361 get_line_as_is(fp, &s);
1362 if (s.type == LINE_NONE) {
1363 ERR( "lxpanel: unexpected eof\n");
1364 goto error;
1365 } else if (s.type == LINE_BLOCK_START) {
1366 pno++;
1367 } else if (s.type == LINE_BLOCK_END) {
1368 pno--;
1369 }
1370 }
1371 } else {
1372 ERR( "lxpanel: unknown block %s\n", s.t[0]);
1373 goto error;
1374 }
1375 } else {
1376 ERR( "lxpanel: illegal in this context %s\n", s.str);
1377 goto error;
1378 }
1379 }
1380
1381 if (!type || !(plug = plugin_load(type))) {
1382 ERR( "lxpanel: can't load %s plugin\n", type);
1383 goto error;
1384 }
1385
1386 plug->panel = p;
1387 if (plug->class->expand_available) plug->expand = expand;
1388 plug->padding = padding;
1389 plug->border = border;
1390 DBG("starting\n");
1391 if (!plugin_start(plug, pconfig ? &pconfig : NULL)) {
1392 ERR( "lxpanel: can't start plugin %s\n", type);
1393 goto error;
1394 }
1395 DBG("plug %s\n", type);
1396 p->plugins = g_list_append(p->plugins, plug);
1397
1398 g_free( type );
1399 RET(1);
1400
1401 error:
1402 if (plug != NULL)
1403 plugin_unload(plug);
1404 g_free(type);
1405 RET(0);
1406 }
1407
1408 int panel_start( Panel *p, char **fp )
1409 {
1410 line s;
1411
1412 /* parse global section */
1413 ENTER;
1414 s.len = 256;
1415
1416 if ((lxpanel_get_line(fp, &s) != LINE_BLOCK_START) || g_ascii_strcasecmp(s.t[0], "Global")) {
1417 ERR( "lxpanel: config file must start from Global section\n");
1418 RET(0);
1419 }
1420 if (!panel_parse_global(p, fp))
1421 RET(0);
1422
1423 panel_start_gui(p);
1424
1425 while (lxpanel_get_line(fp, &s) != LINE_NONE) {
1426 if ((s.type != LINE_BLOCK_START) || g_ascii_strcasecmp(s.t[0], "Plugin")) {
1427 ERR( "lxpanel: expecting Plugin section\n");
1428 RET(0);
1429 }
1430 panel_parse_plugin(p, fp);
1431 }
1432
1433 /* update backgrond of panel and all plugins */
1434 panel_update_background( p );
1435 return 1;
1436 }
1437
1438 static void
1439 delete_plugin(gpointer data, gpointer udata)
1440 {
1441 plugin_delete((Plugin *)data);
1442 }
1443
1444 void panel_destroy(Panel *p)
1445 {
1446 ENTER;
1447
1448 if (p->pref_dialog != NULL)
1449 gtk_widget_destroy(p->pref_dialog);
1450 if (p->plugin_pref_dialog != NULL)
1451 {
1452 gtk_widget_destroy(p->plugin_pref_dialog);
1453 p->plugin_pref_dialog = NULL;
1454 }
1455
1456 if (p->bg != NULL)
1457 {
1458 g_signal_handlers_disconnect_by_func(G_OBJECT(p->bg), on_root_bg_changed, p);
1459 g_object_unref(p->bg);
1460 }
1461
1462 if( p->config_changed )
1463 panel_config_save( p );
1464
1465 g_list_foreach(p->plugins, delete_plugin, NULL);
1466 g_list_free(p->plugins);
1467 p->plugins = NULL;
1468
1469 if( p->system_menus ){
1470 do{
1471 } while ( g_source_remove_by_user_data( p->system_menus ) );
1472 }
1473
1474 gtk_window_group_remove_window( win_grp, GTK_WINDOW( p->topgwin ) );
1475
1476 if( p->topgwin )
1477 gtk_widget_destroy(p->topgwin);
1478 g_free(p->workarea);
1479 g_free( p->background_file );
1480 g_slist_free( p->system_menus );
1481 gdk_flush();
1482 XFlush(GDK_DISPLAY());
1483 XSync(GDK_DISPLAY(), True);
1484
1485 g_free( p->name );
1486 g_free(p);
1487 RET();
1488 }
1489
1490 Panel* panel_new( const char* config_file, const char* config_name )
1491 {
1492 char *fp, *pfp; /* point to current position of profile data in memory */
1493 Panel* panel = NULL;
1494
1495 if( G_LIKELY(config_file) )
1496 {
1497 g_file_get_contents( config_file, &fp, NULL, NULL );
1498 if( fp )
1499 {
1500 panel = panel_allocate();
1501 panel->orientation = ORIENT_NONE;
1502 panel->name = g_strdup( config_name );
1503 pfp = fp;
1504
1505 if (! panel_start( panel, &pfp )) {
1506 ERR( "lxpanel: can't start panel\n");
1507 panel_destroy( panel );
1508 panel = NULL;
1509 }
1510
1511 g_free( fp );
1512 }
1513 }
1514 return panel;
1515 }
1516
1517 static void
1518 usage()
1519 {
1520 g_print(_("lxpanel %s - lightweight GTK2+ panel for UNIX desktops\n"), version);
1521 g_print(_("Command line options:\n"));
1522 g_print(_(" --help -- print this help and exit\n"));
1523 g_print(_(" --version -- print version and exit\n"));
1524 g_print(_(" --log <number> -- set log level 0-5. 0 - none 5 - chatty\n"));
1525 // g_print(_(" --configure -- launch configuration utility\n"));
1526 g_print(_(" --profile name -- use specified profile\n"));
1527 g_print("\n");
1528 g_print(_(" -h -- same as --help\n"));
1529 g_print(_(" -p -- same as --profile\n"));
1530 g_print(_(" -v -- same as --version\n"));
1531 // g_print(_(" -C -- same as --configure\n"));
1532 g_print(_("\nVisit http://lxde.org/ for detail.\n\n"));
1533 }
1534
1535 int panel_handle_x_error(Display * d, XErrorEvent * ev)
1536 {
1537 char buf[256];
1538
1539 if (log_level >= LOG_WARN) {
1540 XGetErrorText(GDK_DISPLAY(), ev->error_code, buf, 256);
1541 LOG(LOG_WARN, "lxpanel : X error: %s\n", buf);
1542 }
1543 return 0; /* Ignored */
1544 }
1545
1546 int panel_handle_x_error_swallow_BadWindow_BadDrawable(Display * d, XErrorEvent * ev)
1547 {
1548 if ((ev->error_code != BadWindow) && (ev->error_code != BadDrawable))
1549 panel_handle_x_error(d, ev);
1550 return 0; /* Ignored */
1551 }
1552
1553 /* Lightweight lock related functions - X clipboard hacks */
1554
1555 #define CLIPBOARD_NAME "LXPANEL_SELECTION"
1556
1557 /*
1558 * clipboard_get_func - dummy get_func for gtk_clipboard_set_with_data ()
1559 */
1560 static void
1561 clipboard_get_func(
1562 GtkClipboard *clipboard G_GNUC_UNUSED,
1563 GtkSelectionData *selection_data G_GNUC_UNUSED,
1564 guint info G_GNUC_UNUSED,
1565 gpointer user_data_or_owner G_GNUC_UNUSED)
1566 {
1567 }
1568
1569 /*
1570 * clipboard_clear_func - dummy clear_func for gtk_clipboard_set_with_data ()
1571 */
1572 static void clipboard_clear_func(
1573 GtkClipboard *clipboard G_GNUC_UNUSED,
1574 gpointer user_data_or_owner G_GNUC_UNUSED)
1575 {
1576 }
1577
1578 /*
1579 * Lightweight version for checking single instance.
1580 * Try and get the CLIPBOARD_NAME clipboard instead of using file manipulation.
1581 *
1582 * Returns TRUE if successfully retrieved and FALSE otherwise.
1583 */
1584 static gboolean check_main_lock()
1585 {
1586 static const GtkTargetEntry targets[] = { { CLIPBOARD_NAME, 0, 0 } };
1587 gboolean retval = FALSE;
1588 GtkClipboard *clipboard;
1589 Atom atom;
1590
1591 atom = gdk_x11_get_xatom_by_name(CLIPBOARD_NAME);
1592
1593 XGrabServer(GDK_DISPLAY());
1594
1595 if (XGetSelectionOwner(GDK_DISPLAY(), atom) != None)
1596 goto out;
1597
1598 clipboard = gtk_clipboard_get(gdk_atom_intern(CLIPBOARD_NAME, FALSE));
1599
1600 if (gtk_clipboard_set_with_data(clipboard, targets,
1601 G_N_ELEMENTS (targets),
1602 clipboard_get_func,
1603 clipboard_clear_func, NULL))
1604 retval = TRUE;
1605
1606 out:
1607 XUngrabServer (GDK_DISPLAY ());
1608 gdk_flush ();
1609
1610 return retval;
1611 }
1612 #undef CLIPBOARD_NAME
1613
1614 static gboolean start_all_panels( )
1615 {
1616 gboolean is_global;
1617 for( is_global = 0; ! all_panels && is_global < 2; ++is_global )
1618 {
1619 char* panel_dir = get_config_file( cprofile, "panels", is_global );
1620 GDir* dir = g_dir_open( panel_dir, 0, NULL );
1621 const gchar* name;
1622
1623 if( ! dir )
1624 {
1625 g_free( panel_dir );
1626 continue;
1627 }
1628
1629 while((name = g_dir_read_name(dir)) != NULL)
1630 {
1631 char* panel_config = g_build_filename( panel_dir, name, NULL );
1632 if (strchr(panel_config, '~') == NULL) /* Skip editor backup files in case user has hand edited in this directory */
1633 {
1634 Panel* panel = panel_new( panel_config, name );
1635 if( panel )
1636 all_panels = g_slist_prepend( all_panels, panel );
1637 }
1638 g_free( panel_config );
1639 }
1640 g_dir_close( dir );
1641 g_free( panel_dir );
1642 }
1643 return all_panels != NULL;
1644 }
1645
1646 void load_global_config();
1647 void free_global_config();
1648
1649 int main(int argc, char *argv[], char *env[])
1650 {
1651 int i;
1652 const char* desktop_name;
1653
1654 setlocale(LC_CTYPE, "");
1655
1656 g_thread_init(NULL);
1657 gdk_threads_init();
1658 gdk_threads_enter();
1659
1660 gtk_init(&argc, &argv);
1661
1662 #ifdef ENABLE_NLS
1663 bindtextdomain ( GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR );
1664 bind_textdomain_codeset ( GETTEXT_PACKAGE, "UTF-8" );
1665 textdomain ( GETTEXT_PACKAGE );
1666 #endif
1667
1668 XSetLocaleModifiers("");
1669 XSetErrorHandler((XErrorHandler) panel_handle_x_error);
1670
1671 resolve_atoms();
1672
1673 desktop_name = g_getenv("XDG_CURRENT_DESKTOP");
1674 is_in_lxde = desktop_name && (0 == strcmp(desktop_name, "LXDE"));
1675
1676 for (i = 1; i < argc; i++) {
1677 if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
1678 usage();
1679 exit(0);
1680 } else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
1681 printf("lxpanel %s\n", version);
1682 exit(0);
1683 } else if (!strcmp(argv[i], "--log")) {
1684 i++;
1685 if (i == argc) {
1686 ERR( "lxpanel: missing log level\n");
1687 usage();
1688 exit(1);
1689 } else {
1690 log_level = atoi(argv[i]);
1691 log_level_set_on_commandline = true;
1692 }
1693 } else if (!strcmp(argv[i], "--configure") || !strcmp(argv[i], "-C")) {
1694 config = 1;
1695 } else if (!strcmp(argv[i], "--profile") || !strcmp(argv[i], "-p")) {
1696 i++;
1697 if (i == argc) {
1698 ERR( "lxpanel: missing profile name\n");
1699 usage();
1700 exit(1);
1701 } else {
1702 cprofile = g_strdup(argv[i]);
1703 }
1704 } else {
1705 printf("lxpanel: unknown option - %s\n", argv[i]);
1706 usage();
1707 exit(1);
1708 }
1709 }
1710
1711 /* Check for duplicated lxpanel instances */
1712 if (!check_main_lock() && !config) {
1713 printf("There is already an instance of LXPanel. Now to exit\n");
1714 exit(1);
1715 }
1716
1717 /* Add our own icons to the search path of icon theme */
1718 gtk_icon_theme_append_search_path( gtk_icon_theme_get_default(), PACKAGE_DATA_DIR "/lxpanel/images" );
1719
1720 fbev = fb_ev_new();
1721 win_grp = gtk_window_group_new();
1722
1723 restart:
1724 is_restarting = FALSE;
1725
1726 load_global_config();
1727
1728 /* NOTE: StructureNotifyMask is required by XRandR
1729 * See init_randr_support() in gdkscreen-x11.c of gtk+ for detail.
1730 */
1731 gdk_window_set_events(gdk_get_default_root_window(), GDK_STRUCTURE_MASK |
1732 GDK_SUBSTRUCTURE_MASK | GDK_PROPERTY_CHANGE_MASK);
1733 gdk_window_add_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, NULL);
1734
1735 if( G_UNLIKELY( ! start_all_panels() ) )
1736 g_warning( "Config files are not found.\n" );
1737 /*
1738 * FIXME: configure??
1739 if (config)
1740 configure();
1741 */
1742 gtk_main();
1743
1744 XSelectInput (GDK_DISPLAY(), GDK_ROOT_WINDOW(), NoEventMask);
1745 gdk_window_remove_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, NULL);
1746
1747 /* destroy all panels */
1748 g_slist_foreach( all_panels, (GFunc) panel_destroy, NULL );
1749 g_slist_free( all_panels );
1750 all_panels = NULL;
1751 g_free( cfgfile );
1752
1753 free_global_config();
1754
1755 if( is_restarting )
1756 goto restart;
1757
1758 gdk_threads_leave();
1759
1760 g_object_unref(win_grp);
1761 g_object_unref(fbev);
1762
1763 return 0;
1764 }