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