Merging upstream version 0.3.7.
[debian/lxpanel.git] / src / configurator.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 "plugin.h"
24 #include "panel.h"
25 #include "misc.h"
26 #include "bg.h"
27 #include <stdlib.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <unistd.h>
31 #include <string.h>
32 #include <glib/gi18n.h>
33
34 #include "dbg.h"
35
36 #include "ptk-ui-xml.h"
37
38 enum{
39 COL_NAME,
40 COL_EXPAND,
41 COL_DATA,
42 N_COLS
43 };
44
45 void panel_configure(Panel* p, int sel_page );
46 void restart(void);
47 void gtk_run(void);
48 void panel_config_save(Panel* panel);
49 static void logout(void);
50
51 Command commands[] = {
52 //{ "configure", N_("Preferences"), configure },
53 { "run", N_("Run"), gtk_run },
54 { "restart", N_("Restart"), restart },
55 { "logout", N_("Logout"), logout },
56 { NULL, NULL },
57 };
58
59 static char* file_manager_cmd = NULL;
60 static char* terminal_cmd = NULL;
61 static char* logout_cmd = NULL;
62
63 extern GSList* all_panels;
64 extern gchar *cprofile;
65 extern int config;
66
67 void panel_global_config_save( Panel* p, FILE *fp);
68 void panel_plugin_config_save( Panel* p, FILE *fp);
69
70 static void update_opt_menu(GtkWidget *w, int ind);
71 static void update_toggle_button(GtkWidget *w, gboolean n);
72 static void modify_plugin( GtkTreeView* view );
73 static void on_entry_changed( GtkEditable* edit, gpointer user_data );
74
75 /* older versions of glib don't provde these API */
76 #if ! GLIB_CHECK_VERSION(2, 8, 0)
77 #include <errno.h>
78
79 int g_mkdir_with_parents(const gchar *pathname, int mode)
80 {
81 struct stat statbuf;
82 char *dir, *sep;
83 dir = g_strdup( pathname );
84 sep = dir[0] == '/' ? dir + 1 : dir;
85 do {
86 sep = strchr( sep, '/' );
87 if( G_LIKELY( sep ) )
88 *sep = '\0';
89
90 if( stat( dir, &statbuf) == 0 )
91 {
92 if( ! S_ISDIR(statbuf.st_mode) ) /* parent not dir */
93 goto err;
94 }
95 else /* stat failed */
96 {
97 if( errno == ENOENT ) /* not exists */
98 {
99 if( mkdir( dir, mode ) == -1 )
100 goto err;
101 }
102 else
103 goto err; /* unknown error */
104 }
105
106 if( G_LIKELY( sep ) )
107 {
108 *sep = '/';
109 ++sep;
110 }
111 else
112 break;
113 }while( sep );
114 g_free( dir );
115 return 0;
116 err:
117 g_free( dir );
118 return -1;
119 }
120 #endif
121
122 static void
123 response_event(GtkDialog *widget, gint arg1, Panel* panel )
124 {
125 switch (arg1) {
126 /* FIXME: what will happen if the user exit lxpanel without
127 close this config dialog?
128 Then the config won't be save, I guess. */
129 case GTK_RESPONSE_DELETE_EVENT:
130 case GTK_RESPONSE_CLOSE:
131 case GTK_RESPONSE_NONE:
132 panel_config_save( panel );
133 /* NOTE: NO BREAK HERE*/
134 gtk_widget_destroy(widget);
135 break;
136 }
137 return;
138 }
139
140 static void
141 update_panel_geometry( Panel* p )
142 {
143 calculate_position(p);
144 gdk_window_move_resize(p->topgwin->window, p->ax, p->ay, p->aw, p->ah);
145
146 panel_set_wm_strut( p );
147 }
148
149 static void
150 set_edge( GtkComboBox *widget, Panel* p )
151 {
152 int edge;
153
154 ENTER;
155 edge = gtk_combo_box_get_active(widget) + 1;
156 p->edge = edge;
157 panel_set_orientation( p );
158 update_panel_geometry(p);
159 panel_update_background( p );
160 RET();
161 }
162
163 static void
164 set_allign( GtkComboBox *widget, Panel* p )
165 {
166 int allign;
167 gboolean t;
168
169 ENTER;
170 allign = gtk_combo_box_get_active(widget) + 1;
171 t = (allign != ALLIGN_CENTER);
172 /*
173 gtk_widget_set_sensitive(margin_label, t);
174 gtk_widget_set_sensitive(margin_spinb, t);
175 */
176 p->allign = allign;
177 update_panel_geometry(p);
178 RET();
179 }
180
181 static void
182 set_margin( GtkSpinButton* spin, Panel* p )
183 {
184 p->margin = (int)gtk_spin_button_get_value(spin);
185 update_panel_geometry(p);
186 }
187
188 static void
189 set_width( GtkSpinButton* spin, Panel* p )
190 {
191 p->width = (int)gtk_spin_button_get_value(spin);
192 update_panel_geometry(p);
193 }
194
195 static void
196 set_height( GtkSpinButton* spin, Panel* p )
197 {
198 p->height = (int)gtk_spin_button_get_value(spin);
199 update_panel_geometry(p);
200 }
201
202 static void
203 set_width_type( GtkWidget *item, Panel* p )
204 {
205 GtkWidget* spin;
206 int widthtype;
207 gboolean t;
208 widthtype = gtk_combo_box_get_active(GTK_COMBO_BOX(item)) + 1;
209 p->widthtype = widthtype;
210
211 spin = ptk_ui_xml_get_widget( gtk_widget_get_toplevel(item), "width" );
212 t = (widthtype != WIDTH_REQUEST);
213 gtk_widget_set_sensitive( spin, t );
214 if (widthtype == WIDTH_PERCENT) {
215 gtk_spin_button_set_range( (GtkSpinButton*)spin, 0, 100 );
216 gtk_spin_button_set_value( (GtkSpinButton*)spin, 100 );
217 } else if (widthtype == WIDTH_PIXEL) {
218 gtk_spin_button_set_range( (GtkSpinButton*)spin, 0, gdk_screen_width() );
219 gtk_spin_button_set_value( (GtkSpinButton*)spin, gdk_screen_width() );
220 } else
221 return;
222
223 update_panel_geometry(p);
224 }
225
226 static void
227 transparency_toggle( GtkWidget *b, Panel* p)
228 {
229 GtkWidget* tr = ptk_ui_xml_get_widget( gtk_widget_get_toplevel(b), "tint_clr");
230 gboolean t;
231
232 ENTER;
233
234 t = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b));
235 gtk_widget_set_sensitive(tr, t);
236 /*
237 gtk_widget_set_sensitive(tr_colorl, t);
238 gtk_widget_set_sensitive(tr_colorb, t);
239 */
240 /* Update background immediately. */
241 if (t&&!p->transparent) {
242 p->transparent = 1;
243 p->background = 0;
244 panel_update_background( p );
245 //restart();
246 }
247 RET();
248 }
249
250 static void
251 background_toggle( GtkWidget *b, Panel* p)
252 {
253 GtkWidget* fc = ptk_ui_xml_get_widget( gtk_widget_get_toplevel(b), "img_file" );
254 gtk_widget_set_sensitive( fc, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b)));
255 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b))) {
256 if (!p->background) {
257 p->transparent = 0;
258 p->background = 1;
259 /* Update background immediately. */
260 panel_update_background( p );
261 //restart();
262 }
263 }
264 }
265
266 static void
267 background_changed(GtkFileChooser *file_chooser, Panel* p )
268 {
269 GtkWidget* btn = ptk_ui_xml_get_widget( p->pref_dialog, "bg_image" );
270 char* file;
271
272 file = g_strdup(gtk_file_chooser_get_filename(file_chooser));
273 if( ! file )
274 return;
275
276 if( p->background_file && 0 == strcmp( p->background_file, file ) )
277 {
278 g_free( file );
279 return;
280 }
281
282 p->background_file = file;
283
284 if( gtk_toggle_button_get_active( (GtkToggleButton*)btn ) )
285 {
286 p->transparent = 0;
287 p->background = 1;
288 /* Update background immediately. */
289 panel_update_background( p );
290 }
291 }
292
293 static void
294 background_disable_toggle( GtkWidget *b, Panel* p )
295 {
296 ENTER;
297 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b))) {
298 if (p->background!=0||p->transparent!=0) {
299 p->background = 0;
300 p->transparent = 0;
301 /* Update background immediately. */
302 panel_update_background( p );
303 //restart();
304 }
305 }
306
307 RET();
308 }
309
310 static void
311 on_font_color_set( GtkColorButton* clr, Panel* p )
312 {
313 gtk_color_button_get_color( clr, &p->gfontcolor );
314 /* FIXME: need some better mechanism to update the panel */
315 if( p->usefontcolor )
316 gtk_widget_queue_draw( p->topgwin );
317 }
318
319 static void
320 on_use_font_color_toggled( GtkToggleButton* btn, Panel* p )
321 {
322 GtkWidget* clr = (GtkWidget*)g_object_get_data( btn, "clr" );
323 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(btn)))
324 gtk_widget_set_sensitive( clr, TRUE );
325 else
326 gtk_widget_set_sensitive( clr, FALSE );
327 p->usefontcolor = gtk_toggle_button_get_active( btn );
328 /* FIXME: need some better mechanism to update the panel */
329 gtk_widget_queue_draw( p->topgwin );
330 }
331
332 static void
333 set_dock_type(GtkToggleButton* toggle, Panel* p )
334 {
335 p->setdocktype = gtk_toggle_button_get_active(toggle) ? 1 : 0;
336 panel_set_dock_type( p );
337 update_panel_geometry(p);
338 /* FIXME: apparently, this doesn't work,
339 but we don't know the reason yet! */
340 }
341
342 static void
343 set_struct(GtkToggleButton* toggle, Panel* p )
344 {
345 p->setstrut = gtk_toggle_button_get_active(toggle) ? 1 : 0;
346 update_panel_geometry(p);
347 }
348
349 static void
350 on_sel_plugin_changed( GtkTreeSelection* tree_sel, GtkWidget* label )
351 {
352 GtkTreeIter it;
353 GtkTreeModel* model;
354 Plugin* pl;
355
356 if( gtk_tree_selection_get_selected( tree_sel, &model, &it ) )
357 {
358 GtkTreeView* view = gtk_tree_selection_get_tree_view( tree_sel );
359 GtkWidget *edit_btn = GTK_WIDGET(g_object_get_data( G_OBJECT(view), "edit_btn" ));
360 gtk_tree_model_get( model, &it, COL_DATA, &pl, -1 );
361 gtk_label_set_text( GTK_LABEL(label), _(pl->class->description) );
362 gtk_widget_set_sensitive( edit_btn, pl->class->config != NULL );
363 }
364 }
365
366 static void
367 on_plugin_expand_toggled(GtkCellRendererToggle* render, char* path, GtkTreeView* view)
368 {
369 GtkTreeModel* model;
370 GtkTreeIter it;
371 GtkTreePath* tp = gtk_tree_path_new_from_string( path );
372 model = gtk_tree_view_get_model( view );
373 if( gtk_tree_model_get_iter( model, &it, tp ) )
374 {
375 Plugin* pl;
376 gboolean old_expand, expand, fill;
377 int padding;
378 GtkPackType pack_type;
379
380 gtk_tree_model_get( model, &it, COL_DATA, &pl, COL_EXPAND, &expand, -1 );
381
382 /* query the old packing of the plugin widget */
383 gtk_box_query_child_packing( pl->panel->box, pl->pwid, &old_expand, &fill, &padding, &pack_type );
384
385 expand = ! expand;
386 pl->expand = expand;
387 gtk_list_store_set( (GtkListStore*)model, &it, COL_EXPAND, expand, -1 );
388 /* apply the new packing with only "expand" modified. */
389 gtk_box_set_child_packing( pl->panel->box, pl->pwid, expand, fill, padding, pack_type );
390 }
391 gtk_tree_path_free( tp );
392 }
393
394 static void init_plugin_list( Panel* p, GtkTreeView* view, GtkWidget* label )
395 {
396 GtkListStore* list;
397 GtkTreeViewColumn* col;
398 GtkCellRenderer* render;
399 GtkTreeSelection* tree_sel;
400 GList* l;
401 GtkTreeIter it;
402
403 g_object_set_data( view, "panel", p );
404
405 render = gtk_cell_renderer_text_new();
406 col = gtk_tree_view_column_new_with_attributes(
407 _("Currently loaded plugins"),
408 render, "text", COL_NAME, NULL );
409 gtk_tree_view_column_set_expand( col, TRUE );
410 gtk_tree_view_append_column( view, col );
411
412 render = gtk_cell_renderer_toggle_new();
413 g_object_set( render, "activatable", TRUE, NULL );
414 g_signal_connect( render, "toggled", G_CALLBACK( on_plugin_expand_toggled ), view );
415 col = gtk_tree_view_column_new_with_attributes(
416 _("Stretch"),
417 render, "active", COL_EXPAND, NULL );
418 gtk_tree_view_column_set_expand( col, FALSE );
419 gtk_tree_view_append_column( view, col );
420
421 list = gtk_list_store_new( N_COLS, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_POINTER );
422 for( l = p->plugins; l; l = l->next )
423 {
424 GtkTreeIter it;
425 Plugin* pl = (Plugin*)l->data;
426 gtk_list_store_append( list, &it );
427 gtk_list_store_set( list, &it,
428 COL_NAME, _(pl->class->name),
429 COL_EXPAND, pl->expand,
430 COL_DATA, pl, -1);
431 }
432 gtk_tree_view_set_model( view, GTK_TREE_MODEL( list ) );
433 g_signal_connect( view, "row-activated",
434 G_CALLBACK(modify_plugin), NULL );
435 tree_sel = gtk_tree_view_get_selection( view );
436 gtk_tree_selection_set_mode( tree_sel, GTK_SELECTION_BROWSE );
437 g_signal_connect( tree_sel, "changed",
438 G_CALLBACK(on_sel_plugin_changed), label);
439 if( gtk_tree_model_get_iter_first( GTK_TREE_MODEL(list), &it ) )
440 gtk_tree_selection_select_iter( tree_sel, &it );
441 }
442
443 static void on_add_plugin_response( GtkDialog* dlg,
444 int response,
445 GtkTreeView* _view )
446 {
447 Panel* p = (Panel*) g_object_get_data( _view, "panel" );
448 if( response == GTK_RESPONSE_OK )
449 {
450 GtkTreeView* view;
451 GtkTreeSelection* tree_sel;
452 GtkTreeIter it;
453 GtkTreeModel* model;
454
455 view = (GtkTreeView*)g_object_get_data( G_OBJECT(dlg), "avail-plugins" );
456 tree_sel = gtk_tree_view_get_selection( view );
457 if( gtk_tree_selection_get_selected( tree_sel, &model, &it ) )
458 {
459 char* type = NULL;
460 Plugin* pl;
461 gtk_tree_model_get( model, &it, 1, &type, -1 );
462 if( pl = plugin_load( type ) )
463 {
464 GtkTreePath* tree_path;
465
466 pl->panel = p;
467 plugin_start( pl, NULL );
468 p->plugins = g_list_append(p->plugins, pl);
469 /* FIXME: will show all cause problems? */
470 gtk_widget_show_all( pl->pwid );
471
472 /* update background of the newly added plugin */
473 plugin_widget_set_background( pl->pwid, pl->panel );
474
475 model = gtk_tree_view_get_model( _view );
476 gtk_list_store_append( (GtkListStore*)model, &it );
477 gtk_list_store_set( (GtkListStore*)model, &it,
478 COL_NAME, _(pl->class->name),
479 COL_DATA, pl, -1 );
480 tree_sel = gtk_tree_view_get_selection( _view );
481 gtk_tree_selection_select_iter( tree_sel, &it );
482 if( tree_path = gtk_tree_model_get_path( model, &it ) )
483 {
484 gtk_tree_view_scroll_to_cell( _view, tree_path, NULL, FALSE, 0, 0 );
485 gtk_tree_path_free( tree_path );
486 }
487 }
488 g_free( type );
489 }
490 }
491 /*
492 gtk_widget_set_sensitive( (GtkWidget*)gtk_window_get_transient_for( (GtkWindow*)dlg ),
493 TRUE );
494 */
495 gtk_widget_destroy( (GtkWidget*)dlg );
496 }
497
498 static void on_add_plugin( GtkButton* btn, GtkTreeView* _view )
499 {
500 GtkWidget* dlg, *parent_win, *scroll;
501 GList* classes;
502 GList* tmp;
503 GtkTreeViewColumn* col;
504 GtkCellRenderer* render;
505 GtkTreeView* view;
506 GtkListStore* list;
507 GtkTreeSelection* tree_sel;
508
509 Panel* p = (Panel*) g_object_get_data( _view, "panel" );
510
511 classes = plugin_get_available_classes();
512
513 parent_win = gtk_widget_get_toplevel( (GtkWidget*)_view );
514 dlg = gtk_dialog_new_with_buttons( _("Add plugin to panel"),
515 GTK_WINDOW(parent_win), 0,
516 GTK_STOCK_CANCEL,
517 GTK_RESPONSE_CANCEL,
518 GTK_STOCK_ADD,
519 GTK_RESPONSE_OK, NULL );
520
521 /* fix background */
522 if (p->background)
523 gtk_widget_set_style(dlg, p->defstyle);
524
525 /* gtk_widget_set_sensitive( parent_win, FALSE ); */
526 scroll = gtk_scrolled_window_new( NULL, NULL );
527 gtk_scrolled_window_set_shadow_type( (GtkScrolledWindow*)scroll,
528 GTK_SHADOW_IN );
529 gtk_scrolled_window_set_policy((GtkScrolledWindow*)scroll,
530 GTK_POLICY_AUTOMATIC,
531 GTK_POLICY_AUTOMATIC );
532 gtk_box_pack_start( (GtkBox*)GTK_DIALOG(dlg)->vbox, scroll,
533 TRUE, TRUE, 4 );
534 view = (GtkTreeView*)gtk_tree_view_new();
535 gtk_container_add( (GtkContainer*)scroll, view );
536 tree_sel = gtk_tree_view_get_selection( view );
537 gtk_tree_selection_set_mode( tree_sel, GTK_SELECTION_BROWSE );
538
539 render = gtk_cell_renderer_text_new();
540 col = gtk_tree_view_column_new_with_attributes(
541 _("Available plugins"),
542 render, "text", 0, NULL );
543 gtk_tree_view_append_column( view, col );
544
545 list = gtk_list_store_new( 2,
546 G_TYPE_STRING,
547 G_TYPE_STRING );
548
549 for( tmp = classes; tmp; tmp = tmp->next ) {
550 PluginClass* pc = (PluginClass*)tmp->data;
551 if( ! pc->invisible ) {
552 /* FIXME: should we display invisible plugins? */
553 GtkTreeIter it;
554 gtk_list_store_append( list, &it );
555 gtk_list_store_set( list, &it,
556 0, _(pc->name),
557 1, pc->type, -1 );
558 /* g_debug( "%s (%s)", pc->type, _(pc->name) ); */
559 }
560 }
561
562 gtk_tree_view_set_model( view, GTK_TREE_MODEL(list) );
563 g_object_unref( list );
564
565 g_signal_connect( dlg, "response",
566 on_add_plugin_response, _view );
567 g_object_set_data( dlg, "avail-plugins", view );
568 g_object_weak_ref( dlg, plugin_class_list_free, classes );
569
570 gtk_window_set_default_size( (GtkWindow*)dlg, 320, 400 );
571 gtk_widget_show_all( dlg );
572 }
573
574 static void on_remove_plugin( GtkButton* btn, GtkTreeView* view )
575 {
576 GtkTreeIter it;
577 GtkTreePath* tree_path;
578 GtkTreeModel* model;
579 GtkTreeSelection* tree_sel = gtk_tree_view_get_selection( view );
580 Plugin* pl;
581
582 Panel* p = (Panel*) g_object_get_data( view, "panel" );
583
584 if( gtk_tree_selection_get_selected( tree_sel, &model, &it ) )
585 {
586 tree_path = gtk_tree_model_get_path( model, &it );
587 gtk_tree_model_get( model, &it, COL_DATA, &pl, -1 );
588 if( gtk_tree_path_get_indices(tree_path)[0] >= gtk_tree_model_iter_n_children( model, NULL ) )
589 gtk_tree_path_prev( tree_path );
590 gtk_list_store_remove( GTK_LIST_STORE(model), &it );
591 p->plugins = g_list_remove( p->plugins, pl );
592 plugin_stop( pl ); /* free the plugin widget & its data */
593 plugin_put( pl ); /* free the lib if necessary */
594
595 gtk_tree_selection_select_path( tree_sel, tree_path );
596 gtk_tree_path_free( tree_path );
597 }
598 }
599
600 void modify_plugin( GtkTreeView* view )
601 {
602 GtkTreeSelection* tree_sel = gtk_tree_view_get_selection( view );
603 GtkTreeModel* model;
604 GtkTreeIter it;
605 Plugin* pl;
606
607 if( ! gtk_tree_selection_get_selected( tree_sel, &model, &it ) )
608 return;
609
610 gtk_tree_model_get( model, &it, COL_DATA, &pl, -1 );
611 if( pl->class->config )
612 pl->class->config( pl, (GtkWindow*)gtk_widget_get_toplevel(GTK_WIDGET(view)) );
613 }
614
615 static int get_widget_index( Panel* p, Plugin* pl )
616 {
617 GList* l;
618 int i;
619 for( i = 0, l = p->plugins; l; l = l->next )
620 {
621 Plugin* _pl = (Plugin*)l->data;
622 if( _pl == pl )
623 return i;
624 if( _pl->pwid )
625 ++i;
626 }
627 return -1;
628 }
629
630 static void on_moveup_plugin( GtkButton* btn, GtkTreeView* view )
631 {
632 GList *l;
633 GtkTreeIter it, prev;
634 GtkTreeModel* model = gtk_tree_view_get_model( view );
635 GtkTreeSelection* tree_sel = gtk_tree_view_get_selection( view );
636 int i;
637
638 Panel* panel = (Panel*) g_object_get_data( view, "panel" );
639
640 if( ! gtk_tree_model_get_iter_first( model, &it ) )
641 return;
642 if( gtk_tree_selection_iter_is_selected( tree_sel, &it ) )
643 return;
644 do{
645 if( gtk_tree_selection_iter_is_selected(tree_sel, &it) )
646 {
647 Plugin* pl;
648 gtk_tree_model_get( model, &it, COL_DATA, &pl, -1 );
649 gtk_list_store_move_before( GTK_LIST_STORE( model ),
650 &it, &prev );
651
652 i = 0;
653 for( l = panel->plugins; l; l = l->next, ++i )
654 {
655 if( l->data == pl )
656 {
657 panel->plugins = g_list_insert( panel->plugins, pl, i - 1);
658 panel->plugins = g_list_delete_link( panel->plugins, l);
659 }
660 }
661 if( pl->pwid )
662 {
663 gtk_box_reorder_child( GTK_BOX(panel->box), pl->pwid, get_widget_index( panel, pl ) );
664 }
665 return;
666 }
667 prev = it;
668 }while( gtk_tree_model_iter_next( model, &it ) );
669 }
670
671 static void on_movedown_plugin( GtkButton* btn, GtkTreeView* view )
672 {
673 GList *l;
674 GtkTreeIter it, next;
675 GtkTreeModel* model;
676 GtkTreeSelection* tree_sel = gtk_tree_view_get_selection( view );
677 Plugin* pl;
678 int i;
679
680 Panel* panel = (Panel*) g_object_get_data( view, "panel" );
681
682 if( ! gtk_tree_selection_get_selected( tree_sel, &model, &it ) )
683 return;
684 next = it;
685
686 if( ! gtk_tree_model_iter_next( model, &next) )
687 return;
688
689 gtk_tree_model_get( model, &it, COL_DATA, &pl, -1 );
690
691 gtk_list_store_move_after( GTK_LIST_STORE( model ), &it, &next );
692
693 i = 0;
694 for( l = panel->plugins; l; l = l->next, ++i )
695 {
696 if( l->data == pl )
697 {
698 panel->plugins = g_list_insert( panel->plugins, pl, i + 2);
699 panel->plugins = g_list_delete_link( panel->plugins, l);
700 }
701 }
702 if( pl->pwid )
703 {
704 gtk_box_reorder_child( GTK_BOX(panel->box), pl->pwid, get_widget_index( panel, pl ) );
705 }
706 }
707
708 static void
709 update_opt_menu(GtkWidget *w, int ind)
710 {
711 int i;
712
713 ENTER;
714 /* this trick will trigger "changed" signal even if active entry is
715 * not actually changing */
716 i = gtk_combo_box_get_active(GTK_COMBO_BOX(w));
717 if (i == ind) {
718 i = i ? 0 : 1;
719 gtk_combo_box_set_active(GTK_COMBO_BOX(w), i);
720 }
721 gtk_combo_box_set_active(GTK_COMBO_BOX(w), ind);
722 RET();
723 }
724
725 static void
726 update_toggle_button(GtkWidget *w, gboolean n)
727 {
728 gboolean c;
729
730 ENTER;
731 /* this trick will trigger "changed" signal even if active entry is
732 * not actually changing */
733 c = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w));
734 if (c == n) {
735 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), !n);
736 }
737 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), n);
738 RET();
739 }
740
741 void
742 panel_configure( Panel* p, int sel_page )
743 {
744 PtkUIXml* xml;
745 GtkWidget *w, *w2;
746
747 if( p->pref_dialog )
748 {
749 gtk_window_present((GtkWindow*)p->pref_dialog);
750 return;
751 }
752
753 p->pref_dialog = ptk_ui_xml_create_widget_from_file( PACKAGE_DATA_DIR "/lxpanel/ui/panel-pref.glade" );
754 g_signal_connect(p->pref_dialog, "response", (GCallback) response_event, p);
755 g_object_add_weak_pointer( p->pref_dialog, &p->pref_dialog );
756 gtk_window_set_position( (GtkWindow*)p->pref_dialog, GTK_WIN_POS_CENTER );
757
758 xml = ptk_ui_xml_get( p->pref_dialog );
759 /* position */
760 w = ptk_ui_xml_lookup( xml, "edge" );
761 update_opt_menu( w, p->edge - 1 );
762 g_signal_connect( w, "changed", G_CALLBACK(set_edge), p);
763
764 w = ptk_ui_xml_lookup( xml, "align" );
765 update_opt_menu( w, p->allign - 1 );
766 g_signal_connect( w, "changed", G_CALLBACK(set_allign), p);
767
768 w = ptk_ui_xml_lookup( xml, "margin" );
769 gtk_spin_button_set_value( (GtkSpinButton*)w, p->margin );
770 g_signal_connect( w, "value-changed",
771 G_CALLBACK(set_margin), p);
772
773 /* size */
774 w = ptk_ui_xml_lookup( xml, "width" );
775 gtk_widget_set_sensitive( w, p->widthtype != WIDTH_REQUEST );
776 if( p->widthtype == WIDTH_PERCENT) {
777 gtk_spin_button_set_range( (GtkSpinButton*)w, 0, 100 );
778 } else if( p->widthtype == WIDTH_PIXEL) {
779 gtk_spin_button_set_range( (GtkSpinButton*)w, 0, gdk_screen_width() );
780 }
781 gtk_spin_button_set_value( (GtkSpinButton*)w, p->width );
782 g_signal_connect( w, "value-changed", G_CALLBACK(set_width), p );
783
784 w = ptk_ui_xml_lookup( xml, "width_unit" );
785 update_opt_menu( w, p->widthtype - 1 );
786 g_signal_connect( w, "changed",
787 G_CALLBACK(set_width_type), p);
788
789 w = ptk_ui_xml_lookup( xml, "height" );
790 gtk_spin_button_set_value( (GtkSpinButton*)w, p->height );
791 g_signal_connect( w, "value-changed", G_CALLBACK(set_height), p );
792
793 w = ptk_ui_xml_lookup( xml, "height_unit" );
794 update_opt_menu( w, HEIGHT_PIXEL - 1);
795 //g_signal_connect( w, "changed", G_CALLBACK(set_height_type), NULL);
796
797 /* properties */
798
799 /* Explaination from Ruediger Arp <ruediger@gmx.net>:
800 "Set Dock Type", it is referring to the behaviour of
801 dockable applications such as those found in WindowMaker (e.g.
802 http://www.cs.mun.ca/~gstarkes/wmaker/dockapps ) and other
803 lightweight window managers. These dockapps are probably being
804 treated in some special way.
805 */
806 w = ptk_ui_xml_lookup( xml, "as_dock" );
807 update_toggle_button( w, p->setdocktype );
808 g_signal_connect( w, "toggled",
809 G_CALLBACK(set_dock_type), p );
810
811 /* Explaination from Ruediger Arp <ruediger@gmx.net>:
812 "Set Strut": Reserve panel's space so that it will not be
813 covered by maximazied windows.
814 This is clearly an option to avoid the panel being
815 covered/hidden by other applications so that it always is
816 accessible. The panel "steals" some screen estate which cannot
817 be accessed by other applications.
818 GNOME Panel acts this way, too.
819 */
820 w = ptk_ui_xml_lookup( xml, "reserve_space" );
821 update_toggle_button( w, p->setstrut );
822 g_signal_connect( w, "toggled",
823 G_CALLBACK(set_struct), p );
824
825 /* transparancy */
826 w = ptk_ui_xml_lookup( xml, "tint_clr" );
827 gtk_color_button_set_color((GtkColorButton*)w, &p->gtintcolor);
828 gtk_color_button_set_alpha((GtkColorButton*)w, 256*p->alpha);
829 if ( ! p->transparent )
830 gtk_widget_set_sensitive( w, FALSE );
831
832 /* background */
833 {
834 GtkWidget* none, *trans, *img;
835 GSList* group;
836 none = ptk_ui_xml_lookup( xml, "bg_none" );
837 trans = ptk_ui_xml_lookup( xml, "bg_transparency" );
838 img = ptk_ui_xml_lookup( xml, "bg_image" );
839
840 group = gtk_radio_button_get_group( (GtkRadioButton*)none );
841 gtk_radio_button_set_group( (GtkRadioButton*)trans, group );
842 group = gtk_radio_button_get_group( (GtkRadioButton*)trans );
843 gtk_radio_button_set_group( (GtkRadioButton*)img, group );
844
845 if (p->background)
846 gtk_toggle_button_set_active( (GtkToggleButton*)img, TRUE);
847 else if (p->transparent)
848 gtk_toggle_button_set_active( (GtkToggleButton*)trans, TRUE);
849 else
850 gtk_toggle_button_set_active( (GtkToggleButton*)none, TRUE);
851
852 g_signal_connect(none, "toggled", G_CALLBACK(background_disable_toggle), p);
853 g_signal_connect(trans, "toggled", G_CALLBACK(transparency_toggle), p);
854 g_signal_connect(img, "toggled", G_CALLBACK(background_toggle), p);
855
856 w = ptk_ui_xml_lookup( xml, "img_file" );
857 gtk_file_chooser_set_current_folder( (GtkFileChooser*)w, PACKAGE_DATA_DIR "/lxpanel/images");
858 if (p->background_file)
859 gtk_file_chooser_set_filename( (GtkFileChooser*)w, p->background_file);
860
861 if (!p->background)
862 gtk_widget_set_sensitive( w, FALSE);
863
864 /* NOTE: Important!! */
865 /* "file-set" signal of GtkFileChooserButton is only available in gtk+ >= 2.12 */
866 /* So we use some dirty tricks here and make things more complicated. :-( */
867 g_signal_connect( w, "selection-changed", G_CALLBACK (background_changed), p);
868 /* g_signal_connect( w, "file-set", G_CALLBACK (background_changed), p); */
869 }
870
871 /* font color */
872 w = ptk_ui_xml_lookup( xml, "font_clr" );
873 gtk_color_button_set_color( (GtkColorButton*)w, &p->gfontcolor );
874 g_signal_connect( w, "color-set", G_CALLBACK( on_font_color_set ), p );
875
876 w2 = ptk_ui_xml_lookup( xml, "use_font_clr" );
877 gtk_toggle_button_set_active( (GtkToggleButton*)w2, p->usefontcolor );
878 g_object_set_data( w2, "clr", w );
879 g_signal_connect(w2, "toggled", G_CALLBACK(on_use_font_color_toggled), p);
880 if( ! p->usefontcolor )
881 gtk_widget_set_sensitive( w, FALSE );
882
883 /* plugin list */
884 {
885 GtkWidget* plugin_list = ptk_ui_xml_lookup( xml, "plugin_list" );
886
887 /* buttons used to edit plugin list */
888 w = ptk_ui_xml_lookup( xml, "add_btn" );
889 g_signal_connect( w, "clicked", G_CALLBACK(on_add_plugin), plugin_list );
890
891 w = ptk_ui_xml_lookup( xml, "edit_btn" );
892 g_signal_connect_swapped( w, "clicked", G_CALLBACK(modify_plugin), plugin_list );
893 g_object_set_data( G_OBJECT(plugin_list), "edit_btn", w );
894
895 w = ptk_ui_xml_lookup( xml, "remove_btn" );
896 g_signal_connect( w, "clicked", G_CALLBACK(on_remove_plugin), plugin_list );
897 w = ptk_ui_xml_lookup( xml, "moveup_btn" );
898 g_signal_connect( w, "clicked", G_CALLBACK(on_moveup_plugin), plugin_list );
899 w = ptk_ui_xml_lookup( xml, "movedown_btn" );
900 g_signal_connect( w, "clicked", G_CALLBACK(on_movedown_plugin), plugin_list );
901
902 w = ptk_ui_xml_lookup( xml, "plugin_desc" );
903 init_plugin_list( p, (GtkTreeView*)plugin_list, w );
904 }
905 /* advanced, applications */
906 w = ptk_ui_xml_lookup( xml, "file_manager" );
907 if (file_manager_cmd)
908 gtk_entry_set_text( (GtkEntry*)w, file_manager_cmd );
909 g_signal_connect( w, "changed",
910 G_CALLBACK(on_entry_changed),
911 &file_manager_cmd);
912
913 w = ptk_ui_xml_lookup( xml, "term" );
914 if (terminal_cmd)
915 gtk_entry_set_text( (GtkEntry*)w, terminal_cmd );
916 g_signal_connect( w, "changed",
917 G_CALLBACK(on_entry_changed),
918 &terminal_cmd);
919
920 /* If we are under LXSession, setting logout command is not necessary. */
921 w = ptk_ui_xml_lookup( xml, "logout" );
922 if( getenv("_LXSESSION_PID") ) {
923 gtk_widget_hide( w );
924 w = ptk_ui_xml_lookup( xml, "logout_label" );
925 gtk_widget_hide( w );
926 }
927 else {
928 if(logout_cmd)
929 gtk_entry_set_text( (GtkEntry*)w, logout_cmd );
930 g_signal_connect( w, "changed",
931 G_CALLBACK(on_entry_changed),
932 &logout_cmd);
933 }
934
935 gtk_widget_show((GtkWindow*)p->pref_dialog);
936 w = ptk_ui_xml_get_widget( p->pref_dialog, "notebook" );
937 gtk_notebook_set_current_page( (GtkNotebook*)w, sel_page );
938 }
939
940 void
941 panel_global_config_save( Panel* p, FILE *fp)
942 {
943 GdkColor c;
944
945 fprintf(fp, "# lxpanel <profile> config file. Manually editing is not recommended.\n"
946 "# Use preference dialog in lxpanel to adjust config when you can.\n\n");
947 lxpanel_put_line(fp, "Global {");
948 lxpanel_put_str(fp, "edge", num2str(edge_pair, p->edge, "none"));
949 lxpanel_put_str(fp, "allign", num2str(allign_pair, p->allign, "none"));
950 lxpanel_put_int(fp, "margin", p->margin);
951 lxpanel_put_str(fp, "widthtype", num2str(width_pair, p->widthtype, "none"));
952 lxpanel_put_int(fp, "width", p->width);
953 lxpanel_put_int(fp, "height", p->height);
954 lxpanel_put_bool(fp, "transparent", p->transparent );
955 // gtk_color_button_get_color(GTK_COLOR_BUTTON(tr_colorb), &c);
956 lxpanel_put_line(fp, "tintcolor=#%06x", gcolor2rgb24(&p->gtintcolor));
957 // lxpanel_put_int(fp, "alpha", gtk_color_button_get_alpha(GTK_COLOR_BUTTON(tr_colorb)) * 0xff / 0xffff);
958 /* FIXME: is this correct?? */
959 lxpanel_put_int(fp, "alpha", p->alpha * 0xff / 0xffff);
960 lxpanel_put_bool(fp, "setdocktype", p->setdocktype);
961 lxpanel_put_bool(fp, "setpartialstrut", p->setstrut);
962 lxpanel_put_bool(fp, "usefontcolor", p->usefontcolor);
963 lxpanel_put_line(fp, "fontcolor=#%06x", gcolor2rgb24(&p->gfontcolor));
964 lxpanel_put_bool(fp, "background", p->background );
965 lxpanel_put_str(fp, "backgroundfile", p->background_file );
966 lxpanel_put_line(fp, "}\n");
967 }
968
969 void
970 panel_plugin_config_save( Panel* p, FILE *fp)
971 {
972 GList* l;
973 for( l = p->plugins; l; l = l->next )
974 {
975 Plugin* pl = (Plugin*)l->data;
976 lxpanel_put_line( fp, "Plugin {" );
977 lxpanel_put_line( fp, "type = %s", pl->class->type );
978 if( pl->expand )
979 lxpanel_put_bool( fp, "expand", TRUE );
980 if( pl->padding > 0 )
981 lxpanel_put_int( fp, "padding", pl->padding );
982 if( pl->border > 0 )
983 lxpanel_put_int( fp, "border", pl->border );
984
985 if( pl->class->save )
986 {
987 lxpanel_put_line( fp, "Config {" );
988 pl->class->save( pl, fp );
989 lxpanel_put_line( fp, "}" );
990 }
991 lxpanel_put_line( fp, "}\n" );
992 }
993 }
994
995 void panel_config_save( Panel* p )
996 {
997 gchar *fname, *dir;
998 FILE *fp;
999
1000 dir = get_config_file( cprofile, "panels", FALSE );
1001 fname = g_build_filename( dir, p->name, NULL );
1002
1003 /* ensure the 'panels' dir exists */
1004 if( ! g_file_test( dir, G_FILE_TEST_EXISTS ) )
1005 g_mkdir_with_parents( dir, 0755 );
1006 g_free( dir );
1007
1008 if (!(fp = fopen(fname, "w"))) {
1009 ERR("can't open for write %s:", fname);
1010 g_free( fname );
1011 perror(NULL);
1012 RET();
1013 }
1014 panel_global_config_save(p, fp);
1015 panel_plugin_config_save(p, fp);
1016 fclose(fp);
1017 g_free( fname );
1018
1019 /* save the global config file */
1020 save_global_config();
1021 p->config_changed = 0;
1022 }
1023
1024 void restart(void)
1025 {
1026 /* This is defined in panel.c */
1027 extern gboolean is_restarting;
1028 ENTER;
1029 is_restarting = TRUE;
1030
1031 /* processing any possible idle handlers before we restart */
1032 while (gtk_events_pending ())
1033 gtk_main_iteration ();
1034 gtk_main_quit();
1035 RET();
1036 }
1037
1038 void logout(void)
1039 {
1040 const char* logout_cmd = logout_cmd;
1041 /* If LXSession is running, _LXSESSION_PID will be set */
1042 if( ! logout_cmd && getenv("_LXSESSION_PID") )
1043 logout_cmd = "lxsession-logout";
1044
1045 if( logout_cmd ) {
1046 GError* err = NULL;
1047 if( ! g_spawn_command_line_async( logout_cmd, &err ) ) {
1048 show_error( NULL, err->message );
1049 g_error_free( err );
1050 }
1051 }
1052 else {
1053 show_error( NULL, _("Logout command is not set") );
1054 }
1055 }
1056
1057 static void notify_apply_config( GtkWidget* widget )
1058 {
1059 GSourceFunc apply_func;
1060 GtkWidget* dlg;
1061
1062 dlg = gtk_widget_get_toplevel( widget );
1063 apply_func = g_object_get_data( G_OBJECT(dlg), "apply_func" );
1064 if( apply_func )
1065 apply_func( g_object_get_data(G_OBJECT(dlg), "plugin") );
1066 }
1067
1068 static void on_entry_changed( GtkEditable* edit, gpointer user_data )
1069 {
1070 char** val = (char**)user_data;
1071 const char *new_val;
1072 g_free( *val );
1073 new_val = gtk_entry_get_text(GTK_ENTRY(edit));
1074 *val = (new_val && *new_val) ? g_strdup( new_val ) : NULL;
1075 notify_apply_config( GTK_WIDGET(edit) );
1076 }
1077
1078 static void on_spin_changed( GtkSpinButton* spin, gpointer user_data )
1079 {
1080 int* val = (int*)user_data;
1081 *val = (int)gtk_spin_button_get_value( spin );
1082 notify_apply_config( GTK_WIDGET(spin) );
1083 }
1084
1085 static void on_toggle_changed( GtkToggleButton* btn, gpointer user_data )
1086 {
1087 gboolean* val = (gboolean*)user_data;
1088 *val = gtk_toggle_button_get_active( btn );
1089 notify_apply_config( GTK_WIDGET(btn) );
1090 }
1091
1092 void generic_config_dlg_save(Panel *panel)
1093 {
1094 panel_config_save(panel);
1095 }
1096
1097 /* Parameters: const char* name, gpointer ret_value, GType type, ....NULL */
1098 GtkWidget* create_generic_config_dlg( const char* title, GtkWidget* parent,
1099 GSourceFunc apply_func, gpointer plugin,
1100 const char* name, ... )
1101 {
1102 va_list args;
1103 Panel* p = ((Plugin*)plugin)->panel;
1104 GtkWidget* dlg = gtk_dialog_new_with_buttons( title, GTK_WINDOW(parent), 0,
1105 GTK_STOCK_CLOSE,
1106 GTK_RESPONSE_CLOSE,
1107 NULL );
1108
1109 /* fix background */
1110 // if (p->background)
1111 // gtk_widget_set_style(dlg, p->defstyle);
1112
1113 /* this is a dirty hack. We need to check if this response is GTK_RESPONSE_CLOSE or not. */
1114 g_signal_connect( dlg, "response", G_CALLBACK(gtk_widget_destroy), NULL );
1115 g_object_weak_ref(dlg, generic_config_dlg_save, p);
1116 if( apply_func )
1117 g_object_set_data( G_OBJECT(dlg), "apply_func", apply_func );
1118 if( plugin )
1119 g_object_set_data( G_OBJECT(dlg), "plugin", plugin );
1120
1121 gtk_box_set_spacing( GTK_BOX(GTK_DIALOG(dlg)->vbox), 4 );
1122
1123 va_start( args, name );
1124 while( name )
1125 {
1126 GtkWidget* label = gtk_label_new( name );
1127 GtkWidget* entry = NULL;
1128 gpointer val = va_arg( args, gpointer );
1129 GType type = va_arg( args, GType );
1130 switch( type )
1131 {
1132 case G_TYPE_STRING:
1133 entry = gtk_entry_new();
1134 if( *(char**)val )
1135 gtk_entry_set_text( GTK_ENTRY(entry), *(char**)val );
1136 g_signal_connect( entry, "changed",
1137 G_CALLBACK(on_entry_changed), val );
1138 break;
1139 case G_TYPE_INT:
1140 {
1141 /* FIXME: the range shouldn't be hardcoded */
1142 entry = gtk_spin_button_new_with_range( 0, 1000, 1 );
1143 gtk_spin_button_set_value( GTK_SPIN_BUTTON(entry), *(int*)val );
1144 g_signal_connect( entry, "value-changed",
1145 G_CALLBACK(on_spin_changed), val );
1146 break;
1147 }
1148 case G_TYPE_BOOLEAN:
1149 entry = gtk_check_button_new();
1150 gtk_container_add( GTK_CONTAINER(entry), label );
1151 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(entry), *(gboolean*)val );
1152 g_signal_connect( entry, "toggled",
1153 G_CALLBACK(on_toggle_changed), val );
1154 break;
1155 }
1156 if( entry )
1157 {
1158 if( type == G_TYPE_BOOLEAN )
1159 gtk_box_pack_start( GTK_BOX(GTK_DIALOG(dlg)->vbox), entry, FALSE, FALSE, 2 );
1160 else
1161 {
1162 GtkWidget* hbox = gtk_hbox_new( FALSE, 2 );
1163 gtk_box_pack_start( GTK_BOX(hbox), label, FALSE, FALSE, 2 );
1164 gtk_box_pack_start( GTK_BOX(hbox), entry, TRUE, TRUE, 2 );
1165 gtk_box_pack_start( GTK_BOX(GTK_DIALOG(dlg)->vbox), hbox, FALSE, FALSE, 2 );
1166 }
1167 }
1168 name = va_arg( args, const char* );
1169 }
1170 va_end( args );
1171
1172 /* weird... why this doesn't work? */
1173 /* gtk_container_set_border_width( GTK_CONTAINER(GTK_DIALOG(dlg)->vbox), 12 ); */
1174 gtk_container_set_border_width( GTK_CONTAINER(dlg), 8 );
1175
1176 gtk_widget_show_all( dlg );
1177 return dlg;
1178 }
1179
1180 char* get_config_file( const char* profile, const char* file_name, gboolean is_global )
1181 {
1182 char* path;
1183 if( is_global )
1184 {
1185 path = g_build_filename( PACKAGE_DATA_DIR, "lxpanel/profile", profile, file_name, NULL );
1186 }
1187 else
1188 {
1189 char* dir = g_build_filename( g_get_user_config_dir(), "lxpanel" , profile, NULL);
1190 /* make sure the private profile dir exists */
1191 /* FIXME: Should we do this everytime this func gets called?
1192 * Maybe simply doing this before saving config files is enough. */
1193 g_mkdir_with_parents( dir, 0700 );
1194 path = g_build_filename( dir,file_name, NULL);
1195 g_free( dir );
1196 }
1197 return path;
1198 }
1199
1200 const char command_group[] = "Command";
1201 void load_global_config()
1202 {
1203 GKeyFile* kf = g_key_file_new();
1204 char* file = get_config_file( cprofile, "config", FALSE );
1205 gboolean loaded = g_key_file_load_from_file( kf, file, 0, NULL );
1206 if( ! loaded )
1207 {
1208 g_free( file );
1209 file = get_config_file( cprofile, "config", TRUE ); /* get the system-wide config file */
1210 loaded = g_key_file_load_from_file( kf, file, 0, NULL );
1211 }
1212
1213 if( loaded )
1214 {
1215 file_manager_cmd = g_key_file_get_string( kf, command_group, "FileManager", NULL );
1216 terminal_cmd = g_key_file_get_string( kf, command_group, "Terminal", NULL );
1217 logout_cmd = g_key_file_get_string( kf, command_group, "Logout", NULL );
1218 }
1219 g_key_file_free( kf );
1220 }
1221
1222 void save_global_config()
1223 {
1224 char* file = get_config_file( cprofile, "config", FALSE );
1225 FILE* f = fopen( file, "w" );
1226 if( f )
1227 {
1228 fprintf( f, "[%s]\n", command_group );
1229 if( file_manager_cmd )
1230 fprintf( f, "FileManager=%s\n", file_manager_cmd );
1231 if( terminal_cmd )
1232 fprintf( f, "Terminal=%s\n", terminal_cmd );
1233 if( logout_cmd )
1234 fprintf( f, "Logout=%s\n", logout_cmd );
1235 fclose( f );
1236 }
1237 }
1238
1239 void free_global_config()
1240 {
1241 g_free( file_manager_cmd );
1242 g_free( terminal_cmd );
1243 g_free( logout_cmd );
1244 }
1245
1246 extern const char*
1247 lxpanel_get_file_manager()
1248 {
1249 return file_manager_cmd ? file_manager_cmd : "pcmanfm %s";
1250 }
1251
1252 extern const char*
1253 lxpanel_get_terminal()
1254 {
1255 return terminal_cmd ? terminal_cmd : "xterm -e";
1256 }
1257