Adding upstream version 0.3.5.2+svn20080509.
[debian/lxpanel.git] / src / configurator.c
CommitLineData
6cc5e1a6
DB
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
38enum{
39 COL_NAME,
40 COL_EXPAND,
41 COL_DATA,
42 N_COLS
43};
44
45void panel_configure(Panel* p, int sel_page );
46void restart(void);
47void gtk_run(void);
48void panel_config_save(Panel* panel);
49static void logout(void);
50
51Command 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
59static char* file_manager_cmd = NULL;
60static char* terminal_cmd = NULL;
61static char* logout_cmd = NULL;
62
63extern GSList* all_panels;
64extern gchar *cprofile;
65extern int config;
66
67void panel_global_config_save( Panel* p, FILE *fp);
68void panel_plugin_config_save( Panel* p, FILE *fp);
69
70static void update_opt_menu(GtkWidget *w, int ind);
71static void update_toggle_button(GtkWidget *w, gboolean n);
72static void modify_plugin( GtkTreeView* view );
73static 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
79int 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;
116err:
117 g_free( dir );
118 return -1;
119}
120#endif
121
122static void
123response_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
140static void
141update_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
149static void
150set_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
163static void
164set_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
181static void
182set_margin( GtkSpinButton* spin, Panel* p )
183{
184 p->margin = (int)gtk_spin_button_get_value(spin);
185 update_panel_geometry(p);
186}
187
188static void
189set_width( GtkSpinButton* spin, Panel* p )
190{
191 p->width = (int)gtk_spin_button_get_value(spin);
192 update_panel_geometry(p);
193}
194
195static void
196set_height( GtkSpinButton* spin, Panel* p )
197{
198 p->height = (int)gtk_spin_button_get_value(spin);
199 update_panel_geometry(p);
200}
201
202static void
203set_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
226static void
227transparency_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
250static void
251background_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
266static void
267background_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
293static void
294background_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
310static void
311on_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
319static void
320on_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
332static void
333set_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
342static void
343set_struct(GtkToggleButton* toggle, Panel* p )
344{
345 p->setstrut = gtk_toggle_button_get_active(toggle) ? 1 : 0;
346 update_panel_geometry(p);
347}
348
349static void
350on_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
366static void
367on_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
394static 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
443static 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
498static 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
574static 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
600void 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
615static 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
630static 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
671static 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
708static void
709update_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
725static void
726update_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
741void
742panel_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
940void
941panel_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
969void
970panel_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
995void 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}
1022
1023void restart(void)
1024{
1025 /* This is defined in panel.c */
1026 extern gboolean is_restarting;
1027 ENTER;
1028 is_restarting = TRUE;
1029
1030 /* processing any possible idle handlers before we restart */
1031 while (gtk_events_pending ())
1032 gtk_main_iteration ();
1033 gtk_main_quit();
1034 RET();
1035}
1036
1037void logout(void)
1038{
1039 const char* logout_cmd = logout_cmd;
1040 /* If LXSession is running, _LXSESSION_PID will be set */
1041 if( ! logout_cmd && getenv("_LXSESSION_PID") )
1042 logout_cmd = "lxsession-logout";
1043
1044 if( logout_cmd ) {
1045 GError* err = NULL;
1046 if( ! g_spawn_command_line_async( logout_cmd, &err ) ) {
1047 show_error( NULL, err->message );
1048 g_error_free( err );
1049 }
1050 }
1051 else {
1052 show_error( NULL, _("Logout command is not set") );
1053 }
1054}
1055
1056static void notify_apply_config( GtkWidget* widget )
1057{
1058 GSourceFunc apply_func;
1059 GtkWidget* dlg;
1060
1061 dlg = gtk_widget_get_toplevel( widget );
1062 apply_func = g_object_get_data( G_OBJECT(dlg), "apply_func" );
1063 if( apply_func )
1064 apply_func( g_object_get_data(G_OBJECT(dlg), "plugin") );
1065}
1066
1067static void on_entry_changed( GtkEditable* edit, gpointer user_data )
1068{
1069 char** val = (char**)user_data;
1070 const char *new_val;
1071 g_free( *val );
1072 new_val = gtk_entry_get_text(GTK_ENTRY(edit));
1073 *val = (new_val && *new_val) ? g_strdup( new_val ) : NULL;
1074 notify_apply_config( GTK_WIDGET(edit) );
1075}
1076
1077static void on_spin_changed( GtkSpinButton* spin, gpointer user_data )
1078{
1079 int* val = (int*)user_data;
1080 *val = (int)gtk_spin_button_get_value( spin );
1081 notify_apply_config( GTK_WIDGET(spin) );
1082}
1083
1084static void on_toggle_changed( GtkToggleButton* btn, gpointer user_data )
1085{
1086 gboolean* val = (gboolean*)user_data;
1087 *val = gtk_toggle_button_get_active( btn );
1088 notify_apply_config( GTK_WIDGET(btn) );
1089}
1090
1091/* Parameters: const char* name, gpointer ret_value, GType type, ....NULL */
1092GtkWidget* create_generic_config_dlg( const char* title, GtkWidget* parent,
1093 GSourceFunc apply_func, gpointer plugin,
1094 const char* name, ... )
1095{
1096 va_list args;
1097 Panel* p = ((Plugin*)plugin)->panel;
1098 GtkWidget* dlg = gtk_dialog_new_with_buttons( title, GTK_WINDOW(parent), 0,
1099 GTK_STOCK_CLOSE,
1100 GTK_RESPONSE_CLOSE,
1101 NULL );
1102
1103 /* fix background */
1104 if (p->background)
1105 gtk_widget_set_style(dlg, p->defstyle);
1106
1107 /* this is a dirty hack. We need to check if this response is GTK_RESPONSE_CLOSE or not. */
1108 g_signal_connect( dlg, "response", G_CALLBACK(gtk_widget_destroy), NULL );
1109 if( apply_func )
1110 g_object_set_data( G_OBJECT(dlg), "apply_func", apply_func );
1111 if( plugin )
1112 g_object_set_data( G_OBJECT(dlg), "plugin", plugin );
1113
1114 gtk_box_set_spacing( GTK_BOX(GTK_DIALOG(dlg)->vbox), 4 );
1115
1116 va_start( args, name );
1117 while( name )
1118 {
1119 GtkWidget* label = gtk_label_new( name );
1120 GtkWidget* entry = NULL;
1121 gpointer val = va_arg( args, gpointer );
1122 GType type = va_arg( args, GType );
1123 switch( type )
1124 {
1125 case G_TYPE_STRING:
1126 entry = gtk_entry_new();
1127 if( *(char**)val )
1128 gtk_entry_set_text( GTK_ENTRY(entry), *(char**)val );
1129 g_signal_connect( entry, "changed",
1130 G_CALLBACK(on_entry_changed), val );
1131 break;
1132 case G_TYPE_INT:
1133 {
1134 /* FIXME: the range shouldn't be hardcoded */
1135 entry = gtk_spin_button_new_with_range( 0, 1000, 1 );
1136 gtk_spin_button_set_value( GTK_SPIN_BUTTON(entry), *(int*)val );
1137 g_signal_connect( entry, "value-changed",
1138 G_CALLBACK(on_spin_changed), val );
1139 break;
1140 }
1141 case G_TYPE_BOOLEAN:
1142 entry = gtk_check_button_new();
1143 gtk_container_add( GTK_CONTAINER(entry), label );
1144 gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(entry), *(gboolean*)val );
1145 g_signal_connect( entry, "toggled",
1146 G_CALLBACK(on_toggle_changed), val );
1147 break;
1148 }
1149 if( entry )
1150 {
1151 if( type == G_TYPE_BOOLEAN )
1152 gtk_box_pack_start( GTK_BOX(GTK_DIALOG(dlg)->vbox), entry, FALSE, FALSE, 2 );
1153 else
1154 {
1155 GtkWidget* hbox = gtk_hbox_new( FALSE, 2 );
1156 gtk_box_pack_start( GTK_BOX(hbox), label, FALSE, FALSE, 2 );
1157 gtk_box_pack_start( GTK_BOX(hbox), entry, TRUE, TRUE, 2 );
1158 gtk_box_pack_start( GTK_BOX(GTK_DIALOG(dlg)->vbox), hbox, FALSE, FALSE, 2 );
1159 }
1160 }
1161 name = va_arg( args, const char* );
1162 }
1163 va_end( args );
1164
1165 /* weird... why this doesn't work? */
1166 /* gtk_container_set_border_width( GTK_CONTAINER(GTK_DIALOG(dlg)->vbox), 12 ); */
1167 gtk_container_set_border_width( GTK_CONTAINER(dlg), 8 );
1168
1169 gtk_widget_show_all( dlg );
1170 return dlg;
1171}
1172
1173char* get_config_file( const char* profile, const char* file_name, gboolean is_global )
1174{
1175 char* path;
1176 if( is_global )
1177 {
1178 path = g_build_filename( PACKAGE_DATA_DIR, "lxpanel/profile", profile, file_name, NULL );
1179 }
1180 else
1181 {
1182 char* dir = g_build_filename( g_get_user_config_dir(), "lxpanel" , profile, NULL);
1183 /* make sure the private profile dir exists */
1184 /* FIXME: Should we do this everytime this func gets called?
1185 * Maybe simply doing this before saving config files is enough. */
1186 g_mkdir_with_parents( dir, 0700 );
1187 path = g_build_filename( dir,file_name, NULL);
1188 g_free( dir );
1189 }
1190 return path;
1191}
1192
1193const char command_group[] = "Command";
1194void load_global_config()
1195{
1196 GKeyFile* kf = g_key_file_new();
1197 char* file = get_config_file( cprofile, "config", FALSE );
1198 gboolean loaded = g_key_file_load_from_file( kf, file, 0, NULL );
1199 if( ! loaded )
1200 {
1201 g_free( file );
1202 file = get_config_file( cprofile, "config", TRUE ); /* get the system-wide config file */
1203 loaded = g_key_file_load_from_file( kf, file, 0, NULL );
1204 }
1205
1206 if( loaded )
1207 {
1208 file_manager_cmd = g_key_file_get_string( kf, command_group, "FileManager", NULL );
1209 terminal_cmd = g_key_file_get_string( kf, command_group, "Terminal", NULL );
1210 logout_cmd = g_key_file_get_string( kf, command_group, "Logout", NULL );
1211 }
1212 g_key_file_free( kf );
1213}
1214
1215void save_global_config()
1216{
1217 char* file = get_config_file( cprofile, "config", FALSE );
1218 FILE* f = fopen( file, "w" );
1219 if( f )
1220 {
1221 fprintf( f, "[%s]\n", command_group );
1222 if( file_manager_cmd )
1223 fprintf( f, "FileManager=%s\n", file_manager_cmd );
1224 if( terminal_cmd )
1225 fprintf( f, "Terminal=%s\n", terminal_cmd );
1226 if( logout_cmd )
1227 fprintf( f, "Logout=%s\n", logout_cmd );
1228 fclose( f );
1229 }
1230}
1231
1232void free_global_config()
1233{
1234 g_free( file_manager_cmd );
1235 g_free( terminal_cmd );
1236 g_free( logout_cmd );
1237}
1238
1239extern const char*
1240lxpanel_get_file_manager()
1241{
1242 return file_manager_cmd ? file_manager_cmd : "pcmanfm %s";
1243}
1244
1245extern const char*
1246lxpanel_get_terminal()
1247{
1248 return terminal_cmd ? terminal_cmd : "xterm -e";
1249}
1250