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