Commit from LXDE Pootle server by user sotrud_nik.: 217 of 295 strings translated...
[lxde/pcmanfm.git] / src / pref.c
CommitLineData
efb7b23b 1/*
f21b7816 2 * pref.c
e843fc59 3 *
f21b7816 4 * Copyright 2009 PCMan <pcman.tw@gmail.com>
01a3ef5a 5 * Copyright 2012-2014 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua>
e843fc59 6 *
f21b7816
HJYP
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
e843fc59 11 *
f21b7816
HJYP
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
e843fc59 16 *
f21b7816
HJYP
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 * MA 02110-1301, USA.
21 */
22
23#ifdef HAVE_CONFIG_H
24# include <config.h>
25#endif
26
20c0bc9a 27#include <libfm/fm.h>
4d55886c 28
f8f2bfad
HJYP
29#include "pcmanfm.h"
30
f21b7816 31#include "pref.h"
4d55886c 32#include "app-config.h"
8505a8ef 33#include "desktop.h"
2e9d61cd
AG
34#include "main-win.h"
35
9c28c31e 36#include <glib/gi18n.h>
2e9d61cd 37#include <string.h>
f21b7816 38
aadc32a8
AG
39#define INIT_BOOL(b, st, name, changed_notify) init_bool(b, #name, G_STRUCT_OFFSET(st, name), changed_notify, FALSE)
40#define INIT_BOOL_SHOW(b, st, name, changed_notify) init_bool(b, #name, G_STRUCT_OFFSET(st, name), changed_notify, TRUE)
c5fccf1d 41#define INIT_COMBO(b, st, name, changed_notify) init_combo(b, #name, G_STRUCT_OFFSET(st, name), changed_notify)
4d55886c 42#define INIT_ICON_SIZES(b, name) init_icon_sizes(b, #name, G_STRUCT_OFFSET(FmConfig, name))
9902aaea 43#define INIT_SPIN(b, st, name, changed_notify) init_spin(b, #name, G_STRUCT_OFFSET(st, name), changed_notify)
bb31fd59 44#define INIT_ENTRY(b, st, name, changed_notify) init_entry(b, #name, G_STRUCT_OFFSET(st, name), changed_notify)
4d55886c 45
3aebc679
AG
46static GtkWindow* pref_dlg = NULL;
47static GtkNotebook* notebook = NULL;
4d55886c
HJYP
48/*
49static GtkWidget* icon_size_combo[3] = {0};
50static GtkWidget* bookmark_combo = NULL
51static GtkWidget* use_trash;
52*/
53
52f398cc 54static void on_response(GtkDialog* dlg, int res, GtkWindow** pdlg)
f21b7816 55{
c5fccf1d 56 *pdlg = NULL;
3d141640 57 pcmanfm_save_config(TRUE);
52f398cc 58 gtk_widget_destroy(GTK_WIDGET(dlg));
4d55886c 59}
f21b7816 60
4d55886c
HJYP
61static void on_icon_size_changed(GtkComboBox* combo, gpointer _off)
62{
63 GtkTreeIter it;
64 if(gtk_combo_box_get_active_iter(combo, &it))
f21b7816 65 {
4d55886c
HJYP
66 gsize off = GPOINTER_TO_SIZE(_off);
67 int* val = (int*)G_STRUCT_MEMBER_P(fm_config, off);
68 int size;
69 GtkTreeModel* model = gtk_combo_box_get_model(combo);
70 gtk_tree_model_get(model, &it, 1, &size, -1);
71 if(size != *val)
72 {
9902aaea 73 const char* name = gtk_buildable_get_name((GtkBuildable*)combo);
4d55886c
HJYP
74 *val = size;
75 fm_config_emit_changed(fm_config, name);
76 }
77 }
78}
f21b7816 79
4d55886c
HJYP
80static void init_icon_sizes(GtkBuilder* builder, const char* name, gsize off)
81{
82 GtkComboBox* combo = (GtkComboBox*)gtk_builder_get_object(builder, name);
83 GtkTreeModel* model = gtk_combo_box_get_model(combo);
84 GtkTreeIter it;
85 int* val = (int*)G_STRUCT_MEMBER_P(fm_config, off);
4d55886c
HJYP
86 gtk_tree_model_get_iter_first(model, &it);
87 gtk_combo_box_set_active_iter(combo, &it);
88 do{
89 int size;
90 gtk_tree_model_get(model, &it, 1, &size, -1);
91 if(size == *val)
92 {
93 gtk_combo_box_set_active_iter(combo, &it);
94 break;
95 }
96 }while(gtk_tree_model_iter_next(model, &it));
97 g_signal_connect(combo, "changed", G_CALLBACK(on_icon_size_changed), GSIZE_TO_POINTER(off));
98}
99
100static void on_combo_changed(GtkComboBox* combo, gpointer _off)
101{
102 gsize off = GPOINTER_TO_SIZE(_off);
103 int* val = (int*)G_STRUCT_MEMBER_P(fm_config, off);
104 int sel = gtk_combo_box_get_active(combo);
105 if(sel != *val)
106 {
c5fccf1d
HJYP
107 const char* name = g_object_get_data((GObject*)combo, "changed");
108 if(!name)
9902aaea 109 name = gtk_buildable_get_name((GtkBuildable*)combo);
4d55886c
HJYP
110 *val = sel;
111 fm_config_emit_changed(fm_config, name);
f21b7816 112 }
4d55886c
HJYP
113}
114
c5fccf1d 115static void init_combo(GtkBuilder* builder, const char* name, gsize off, const char* changed_notify)
4d55886c
HJYP
116{
117 GtkComboBox* combo = (GtkComboBox*)gtk_builder_get_object(builder, name);
4d55886c 118 int* val = (int*)G_STRUCT_MEMBER_P(fm_config, off);
c5fccf1d 119 if(changed_notify)
f3caa50c 120 g_object_set_data_full(G_OBJECT(combo), "changed", g_strdup(changed_notify), g_free);
4d55886c
HJYP
121 gtk_combo_box_set_active(combo, *val);
122 g_signal_connect(combo, "changed", G_CALLBACK(on_combo_changed), GSIZE_TO_POINTER(off));
123}
124
321cb581
HJYP
125static void on_archiver_combo_changed(GtkComboBox* combo, gpointer user_data)
126{
127 GtkTreeModel* model = gtk_combo_box_get_model(combo);
128 GtkTreeIter it;
129 if(gtk_combo_box_get_active_iter(combo, &it))
130 {
131 FmArchiver* archiver;
132 gtk_tree_model_get(model, &it, 1, &archiver, -1);
133 if(archiver)
134 {
135 g_free(fm_config->archiver);
136 fm_config->archiver = g_strdup(archiver->program);
137 fm_archiver_set_default(archiver);
138 fm_config_emit_changed(fm_config, "archiver");
139 }
140 }
141}
142
143/* archiver integration */
144static void init_archiver_combo(GtkBuilder* builder)
145{
146 GtkListStore* model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
147 GtkComboBox* combo = (GtkComboBox*)gtk_builder_get_object(builder, "archiver");
148 GtkTreeIter it;
5a89e062 149 const GList* archivers = fm_archiver_get_all();
321cb581 150 FmArchiver* default_archiver = fm_archiver_get_default();
5a89e062 151 const GList* l;
321cb581
HJYP
152
153 gtk_combo_box_set_model(combo, GTK_TREE_MODEL(model));
154
155 for(l = archivers; l; l=l->next)
156 {
157 FmArchiver* archiver = (FmArchiver*)l->data;
158 gtk_list_store_insert_with_values(model, &it, -1,
159 0, archiver->program,
160 1, archiver, -1);
161 if(archiver == default_archiver)
162 gtk_combo_box_set_active_iter(combo, &it);
163 }
164 g_object_unref(model);
165 g_signal_connect(combo, "changed", G_CALLBACK(on_archiver_combo_changed), NULL);
166}
167
aadc32a8
AG
168#if FM_CHECK_VERSION(1, 2, 0)
169static void on_auto_sel_changed(GtkRange *scale, gpointer unused)
170{
171 gint new_val = gtk_range_get_value(scale) * 1000;
172
173 if (new_val != fm_config->auto_selection_delay)
174 {
175 fm_config->auto_selection_delay = new_val;
176 fm_config_emit_changed(fm_config, "auto_selection_delay");
177 }
178}
179
180static void init_auto_selection_delay_scale(GtkBuilder* builder)
181{
182 GtkScale *scale;
183 gdouble val = fm_config->auto_selection_delay * 0.001;
184
185 scale = GTK_SCALE(gtk_builder_get_object(builder, "auto_selection_delay"));
186 gtk_range_set_value(GTK_RANGE(scale), val);
187 g_signal_connect(scale, "value-changed", G_CALLBACK(on_auto_sel_changed), NULL);
188 gtk_widget_show(GTK_WIDGET(gtk_builder_get_object(builder, "auto_sel_box")));
189}
4427075b
AG
190
191static void on_drop_default_action_changed(GtkComboBox* combo, gpointer smart_desktop_autodrop)
192{
193 int sel = gtk_combo_box_get_active(combo);
194
ce87d26c
AG
195 /* translate FmDndDestDropAction <- GtkListStore index */
196 switch (sel)
197 {
198 case 0:
199 sel = FM_DND_DEST_DROP_AUTO;
200 break;
201 case 1:
202 sel = FM_DND_DEST_DROP_COPY;
203 break;
204 case 2:
205 sel = FM_DND_DEST_DROP_MOVE;
206 break;
207 default:
208 sel = FM_DND_DEST_DROP_ASK;
209 }
4427075b
AG
210 if (sel != fm_config->drop_default_action)
211 {
212 fm_config->drop_default_action = sel;
213 fm_config_emit_changed(fm_config, "drop_default_action");
214 gtk_widget_set_sensitive(smart_desktop_autodrop, sel == FM_DND_DEST_DROP_AUTO);
215 }
216}
217
218static void init_drop_default_action_combo(GtkBuilder* builder)
219{
220 GObject *combo = gtk_builder_get_object(builder, "drop_default_action");
221 GObject *smart_desktop_autodrop = gtk_builder_get_object(builder, "smart_desktop_autodrop");
ce87d26c 222 gint var;
4427075b 223
ce87d26c
AG
224 /* translate FmDndDestDropAction -> GtkListStore index */
225 switch (fm_config->drop_default_action)
226 {
227 case FM_DND_DEST_DROP_COPY:
228 var = 1;
229 break;
230 case FM_DND_DEST_DROP_MOVE:
231 var = 2;
232 break;
233 case FM_DND_DEST_DROP_ASK:
234 var = 3;
235 break;
236 default:
237 var = 0;
238 }
239 gtk_combo_box_set_active((GtkComboBox*)combo, var);
4427075b
AG
240 gtk_widget_set_sensitive(GTK_WIDGET(smart_desktop_autodrop),
241 fm_config->drop_default_action == FM_DND_DEST_DROP_AUTO);
242 g_signal_connect(combo, "changed", G_CALLBACK(on_drop_default_action_changed),
243 smart_desktop_autodrop);
244}
aadc32a8
AG
245#endif
246
4d55886c
HJYP
247static void on_toggled(GtkToggleButton* btn, gpointer _off)
248{
249 gsize off = GPOINTER_TO_SIZE(_off);
250 gboolean* val = (gboolean*)G_STRUCT_MEMBER_P(fm_config, off);
251 gboolean new_val = gtk_toggle_button_get_active(btn);
252 if(*val != new_val)
253 {
c5fccf1d
HJYP
254 const char* name = g_object_get_data((GObject*)btn, "changed");
255 if(!name)
9902aaea 256 name = gtk_buildable_get_name((GtkBuildable*)btn);
4d55886c
HJYP
257 *val = new_val;
258 fm_config_emit_changed(fm_config, name);
259 }
260}
261
aadc32a8
AG
262static void init_bool(GtkBuilder* b, const char* name, gsize off,
263 const char* changed_notify, gboolean force_show)
4d55886c
HJYP
264{
265 GtkToggleButton* btn = GTK_TOGGLE_BUTTON(gtk_builder_get_object(b, name));
266 gboolean* val = (gboolean*)G_STRUCT_MEMBER_P(fm_config, off);
c5fccf1d 267 if(changed_notify)
f3caa50c 268 g_object_set_data_full(G_OBJECT(btn), "changed", g_strdup(changed_notify), g_free);
aadc32a8
AG
269 if(force_show)
270 gtk_widget_show(GTK_WIDGET(btn));
4d55886c
HJYP
271 gtk_toggle_button_set_active(btn, *val);
272 g_signal_connect(btn, "toggled", G_CALLBACK(on_toggled), GSIZE_TO_POINTER(off));
273}
274
aadc32a8
AG
275static void on_single_click_toggled(GtkToggleButton* btn, gpointer auto_sel_box)
276{
277 gboolean new_val = gtk_toggle_button_get_active(btn);
278
279 if (new_val != fm_config->single_click)
280 {
281 fm_config->single_click = new_val;
282 fm_config_emit_changed(fm_config, "single_click");
283 }
284 if (auto_sel_box)
285 gtk_widget_set_sensitive(auto_sel_box, new_val);
286}
287
288static void on_use_trash_toggled(GtkToggleButton* btn, gpointer vbox_trash)
289{
290 gboolean new_val = gtk_toggle_button_get_active(btn);
291
292 if (new_val != fm_config->use_trash)
293 {
294 fm_config->use_trash = new_val;
295 fm_config_emit_changed(fm_config, "use_trash");
296 }
297 if (vbox_trash)
298 gtk_widget_set_sensitive(vbox_trash, new_val);
299}
300
01a3ef5a
AG
301static void on_close_on_unmount_toggled(GtkToggleButton *btn, FmAppConfig *cfg)
302{
303 gboolean new_val = gtk_toggle_button_get_active(btn);
304
305 if (new_val != cfg->close_on_unmount)
306 {
307 cfg->close_on_unmount = new_val;
308 fm_config_emit_changed(fm_config, "close_on_unmount");
309 }
310}
311
9902aaea
HJYP
312static void on_spin_changed(GtkSpinButton* btn, gpointer _off)
313{
314 gsize off = GPOINTER_TO_SIZE(_off);
315 guint* val = (guint*)G_STRUCT_MEMBER_P(fm_config, off);
6f0f1950 316 guint new_val = gtk_spin_button_get_value(btn);
9902aaea
HJYP
317 if(*val != new_val)
318 {
319 const char* name = g_object_get_data((GObject*)btn, "changed");
320 if(!name)
321 name = gtk_buildable_get_name((GtkBuildable*)btn);
322 *val = new_val;
323 fm_config_emit_changed(fm_config, name);
324 }
325}
326
327static void init_spin(GtkBuilder* b, const char* name, gsize off, const char* changed_notify)
328{
329 GtkSpinButton* btn = GTK_SPIN_BUTTON(gtk_builder_get_object(b, name));
330 guint* val = (guint*)G_STRUCT_MEMBER_P(fm_config, off);
331 if(changed_notify)
f3caa50c 332 g_object_set_data_full(G_OBJECT(btn), "changed", g_strdup(changed_notify), g_free);
9902aaea
HJYP
333 gtk_spin_button_set_value(btn, *val);
334 g_signal_connect(btn, "value-changed", G_CALLBACK(on_spin_changed), GSIZE_TO_POINTER(off));
335}
336
bb31fd59
HJYP
337static void on_entry_changed(GtkEntry* entry, gpointer _off)
338{
339 gsize off = GPOINTER_TO_SIZE(_off);
3aebc679 340 gchar** val = (gchar**)G_STRUCT_MEMBER_P(fm_config, off);
bb31fd59
HJYP
341 const char* new_val = gtk_entry_get_text(entry);
342 if(g_strcmp0(*val, new_val))
343 {
344 const char* name = g_object_get_data((GObject*)entry, "changed");
345 if(!name)
346 name = gtk_buildable_get_name((GtkBuildable*)entry);
347 g_free(*val);
348 *val = *new_val ? g_strdup(new_val) : NULL;
349 fm_config_emit_changed(fm_config, name);
350 }
351}
352
353static void init_entry(GtkBuilder* b, const char* name, gsize off, const char* changed_notify)
354{
012b2962 355 GtkEntry* btn = GTK_ENTRY(gtk_builder_get_object(b, name));
3aebc679 356 gchar** val = (gchar**)G_STRUCT_MEMBER_P(fm_config, off);
bb31fd59 357 if(changed_notify)
f3caa50c 358 g_object_set_data_full(G_OBJECT(btn), "changed", g_strdup(changed_notify), g_free);
bb31fd59 359 if(*val)
012b2962 360 gtk_entry_set_text(btn, *val);
bb31fd59
HJYP
361 g_signal_connect(btn, "changed", G_CALLBACK(on_entry_changed), GSIZE_TO_POINTER(off));
362}
363
35243f05
HJYP
364static void on_tab_label_list_sel_changed(GtkTreeSelection* tree_sel, gpointer user_data)
365{
366 GtkTreePath* tp;
367 GtkTreeIter it;
368 GtkTreeModel* model;
8d3876e1
AG
369 int page;
370 if (!gtk_tree_selection_get_selected(tree_sel, &model, &it))
371 {
372 g_warning("pref: on_tab_label_list_sel_changed() got no selection");
373 return;
374 }
35243f05 375 tp = gtk_tree_model_get_path(model, &it);
8d3876e1
AG
376 page = gtk_tree_path_get_indices(tp)[0];
377 if (gtk_notebook_get_current_page(notebook) != page)
378 gtk_notebook_set_current_page(notebook, page);
35243f05
HJYP
379 gtk_tree_path_free(tp);
380}
381
fbce80c9 382static void on_notebook_page_changed(GtkNotebook *notebook, gpointer page,
8d3876e1 383 guint n, GtkTreeView *view)
fbce80c9 384{
8d3876e1 385 GtkTreePath *tp;
fbce80c9 386
fbce80c9 387 /* g_debug("changed pref page: %u", n); */
8d3876e1
AG
388 gtk_tree_view_get_cursor(view, &tp, NULL);
389 if (gtk_tree_path_get_indices(tp)[0] != (int)n)
390 {
391 gtk_tree_path_free(tp);
392 tp = gtk_tree_path_new_from_indices(n, -1);
393 gtk_tree_view_set_cursor(view, tp, NULL, FALSE);
394 }
395 gtk_tree_path_free(tp);
396}
397
398static gboolean on_key_press(GtkWidget* w, GdkEventKey* evt, GtkNotebook *notebook)
399{
400 int modifier = (evt->state & gtk_accelerator_get_default_mod_mask());
401
402 if (modifier == GDK_MOD1_MASK) /* Alt */
403 {
404 if(evt->keyval >= '1' && evt->keyval <= '9') /* Alt + 1 ~ 9, nth tab */
405 {
406 gtk_notebook_set_current_page(notebook, evt->keyval - '1');
407 return TRUE;
408 }
409 }
410 return FALSE;
fbce80c9
AG
411}
412
04ecf5ec
AG
413static void on_autorun_toggled(GtkToggleButton* btn, GtkWidget *autorun_choices_area)
414{
415 gboolean new_val = gtk_toggle_button_get_active(btn);
416
417 if (new_val != app_config->autorun)
418 {
419 app_config->autorun = new_val;
420 fm_config_emit_changed(fm_config, "autorun");
421 }
422 if (autorun_choices_area)
423 gtk_widget_set_sensitive(autorun_choices_area, new_val);
424}
425
426static void on_remove_autorun_choice_clicked(GtkButton *button, GtkTreeView *view)
427{
428 GtkTreeSelection *tree_sel = gtk_tree_view_get_selection(view);
429 GtkTreeModel *model;
430 GList *rows = gtk_tree_selection_get_selected_rows(tree_sel, &model), *l;
431 GtkTreeIter it;
432
433 /* convert paths to references */
434 for (l = rows; l; l = l->next)
435 {
436 GtkTreePath *tp = l->data;
437 l->data = gtk_tree_row_reference_new(model, tp);
438 gtk_tree_path_free(tp);
439 }
440 /* remove rows from model */
441 for (l = rows; l; l = l->next)
442 {
443 if (gtk_tree_model_get_iter(model, &it, gtk_tree_row_reference_get_path(l->data)))
444 {
445 char *type;
446
447 gtk_tree_model_get(model, &it, 2, &type, -1);
04ecf5ec
AG
448 if (type)
449 g_hash_table_remove(app_config->autorun_choices, type);
450 g_free(type);
451 gtk_list_store_remove(GTK_LIST_STORE(model), &it);
452 }
453 else
454 g_critical("autorun_choice not found in model");
455 gtk_tree_row_reference_free(l->data);
456 }
457 g_list_free(rows);
458}
459
460static void on_choices_sel_changed(GtkTreeSelection *selection, GtkWidget *btn)
461{
462 gtk_widget_set_sensitive(btn, gtk_tree_selection_count_selected_rows(selection) > 0);
463}
464
2e9d61cd
AG
465#if FM_CHECK_VERSION(1, 2, 0)
466static void on_use_home_path_toggled(GtkToggleButton *btn, GtkWidget *home_path_custom)
467{
468 gboolean active = gtk_toggle_button_get_active(btn);
469
470 gtk_widget_set_sensitive(home_path_custom, active);
471}
472
473static void on_use_home_path_toggled2(GtkToggleButton *btn, GtkEntry *home_path)
474{
475 if (!gtk_toggle_button_get_active(btn))
476 gtk_entry_set_text(home_path, fm_get_home_dir());
477}
478
479static void on_home_path_changed(GtkEntry *home_path, FmAppConfig *cfg)
480{
481 const char *path = gtk_entry_get_text(home_path);
482
483 if (path[0] && strcmp(path, fm_get_home_dir()) != 0)
484 {
485 if (g_strcmp0(path, cfg->home_path) == 0) /* not changed */
486 return;
487 g_free(cfg->home_path);
488 cfg->home_path = g_strdup(path);
489 }
490 else
491 {
492 g_free(cfg->home_path);
493 cfg->home_path = NULL;
494 }
495 fm_config_emit_changed(FM_CONFIG(cfg), "home_path");
496}
497
498static void on_home_path_current_clicked(GtkButton *button, GtkEntry *home_path)
499{
500 FmMainWin *win = fm_main_win_get_last_active();
501 FmPath *cwd;
502 char *path;
503
504 if (win == NULL || win->folder_view == NULL)
505 return; /* FIXME: print warning? */
506 cwd = fm_folder_view_get_cwd(win->folder_view);
507 path = fm_path_to_str(cwd);
508 gtk_entry_set_text(home_path, path);
509 g_free(path);
510}
9c28c31e
AG
511
512static void on_add_to_blacklist_clicked(GtkButton *button, GtkListStore *model)
513{
514 char *item = fm_get_user_input(NULL, _("Add to Modules Blacklist"),
515 _("Enter a blacklisted module mask:"), NULL);
516 int i;
517 GtkTreeIter it;
518
519 if (item == NULL || item[0] == '\0') /* cancelled or empty */
520 return;
521 /* add a row to list */
522 gtk_list_store_append(model, &it);
523 gtk_list_store_set(model, &it, 0, item, 1, TRUE, -1);
524 /* rebuild the blacklist */
525 i = fm_config->modules_blacklist ? g_strv_length(fm_config->modules_blacklist) : 0;
526 fm_config->modules_blacklist = g_renew(char *, fm_config->modules_blacklist, i + 2);
527 fm_config->modules_blacklist[i+1] = NULL;
528 fm_config->modules_blacklist[i] = item;
529}
530
531static void on_remove_from_blacklist_clicked(GtkButton *button, GtkTreeView *view)
532{
533 GtkTreeSelection *tree_sel = gtk_tree_view_get_selection(view);
534 GtkTreeModel *model;
535 GList *rows = gtk_tree_selection_get_selected_rows(tree_sel, &model), *l;
536 char *item;
537 char **bl;
538 int i;
539 GtkTreeIter it;
540
541 /* convert paths to references */
542 for (l = rows; l; l = l->next)
543 {
544 GtkTreePath *tp = l->data;
545 l->data = gtk_tree_row_reference_new(model, tp);
546 gtk_tree_path_free(tp);
547 }
548 /* remove rows from model */
549 for (l = rows; l; l = l->next)
550 {
551 if (gtk_tree_model_get_iter(model, &it, gtk_tree_row_reference_get_path(l->data)))
552 gtk_list_store_remove(GTK_LIST_STORE(model), &it);
553 else
554 g_critical("the item not found in model");
555 gtk_tree_row_reference_free(l->data);
556 }
557 g_list_free(rows);
558 /* rebuild the blacklist */
559 if (gtk_tree_model_get_iter_first(model, &it))
560 {
561 bl = g_new(char *, gtk_tree_model_iter_n_children(model, NULL) + 1);
562 i = 0;
563 do
564 {
565 gtk_tree_model_get(model, &it, 0, &item, -1);
566 bl[i++] = item;
567 } while (gtk_tree_model_iter_next(model, &it));
568 bl[i] = NULL;
569 }
570 else
571 bl = NULL;
572 g_strfreev(fm_config->modules_blacklist);
573 fm_config->modules_blacklist = bl;
574}
575
576static void on_blacklist_sel_changed(GtkTreeSelection *selection, GtkWidget *btn)
577{
578 GList *rows, *l;
579 GtkTreeModel *model;
580 GtkTreePath *tp;
581 GtkTreeIter it;
582 gboolean can_del;
583
584 if (gtk_tree_selection_count_selected_rows(selection) == 0)
585 {
586 gtk_widget_set_sensitive(btn, FALSE);
587 return;
588 }
589 rows = gtk_tree_selection_get_selected_rows(selection, &model);
590 can_del = TRUE;
591 for (l = rows; l; l = l->next)
592 {
593 tp = l->data;
594 if (can_del && gtk_tree_model_get_iter(model, &it, tp))
595 gtk_tree_model_get(model, &it, 1, &can_del, -1);
596 else
597 can_del = FALSE;
598 gtk_tree_path_free(tp);
599 }
600 g_list_free(rows);
601 gtk_widget_set_sensitive(btn, can_del);
602}
603
604static void on_add_to_whitelist_clicked(GtkButton *button, GtkListStore *model)
605{
606 char *item = fm_get_user_input(NULL, _("Add to Modules Whitelist"),
607 _("Enter a whitelisted module mask:"), NULL);
608 int i;
609 GtkTreeIter it;
610
611 if (item == NULL || item[0] == '\0') /* cancelled or empty */
612 return;
613 /* add a row to list */
614 gtk_list_store_append(model, &it);
615 gtk_list_store_set(model, &it, 0, item, -1);
616 /* rebuild the blacklist */
617 i = fm_config->modules_whitelist ? g_strv_length(fm_config->modules_whitelist) : 0;
618 fm_config->modules_whitelist = g_renew(char *, fm_config->modules_whitelist, i + 2);
619 fm_config->modules_whitelist[i+1] = NULL;
620 fm_config->modules_whitelist[i] = item;
621}
622
623static void on_remove_from_whitelist_clicked(GtkButton *button, GtkTreeView *view)
624{
625 GtkTreeSelection *tree_sel = gtk_tree_view_get_selection(view);
626 GtkTreeModel *model;
627 GList *rows = gtk_tree_selection_get_selected_rows(tree_sel, &model), *l;
628 char *item;
629 char **wl;
630 int i;
631 GtkTreeIter it;
632
633 /* convert paths to references */
634 for (l = rows; l; l = l->next)
635 {
636 GtkTreePath *tp = l->data;
637 l->data = gtk_tree_row_reference_new(model, tp);
638 gtk_tree_path_free(tp);
639 }
640 /* remove rows from model */
641 for (l = rows; l; l = l->next)
642 {
643 if (gtk_tree_model_get_iter(model, &it, gtk_tree_row_reference_get_path(l->data)))
644 gtk_list_store_remove(GTK_LIST_STORE(model), &it);
645 else
646 g_critical("the item not found in model");
647 gtk_tree_row_reference_free(l->data);
648 }
649 g_list_free(rows);
650 /* rebuild the blacklist */
651 if (gtk_tree_model_get_iter_first(model, &it))
652 {
653 wl = g_new(char *, gtk_tree_model_iter_n_children(model, NULL) + 1);
654 i = 0;
655 do
656 {
657 gtk_tree_model_get(model, &it, 0, &item, -1);
658 wl[i++] = item;
659 } while (gtk_tree_model_iter_next(model, &it));
660 wl[i] = NULL;
661 }
662 else
663 wl = NULL;
664 g_strfreev(fm_config->modules_whitelist);
665 fm_config->modules_whitelist = wl;
666}
667
668static void on_whitelist_sel_changed(GtkTreeSelection *selection, GtkWidget *btn)
669{
670 gtk_widget_set_sensitive(btn, gtk_tree_selection_count_selected_rows(selection) > 0);
671}
2e9d61cd
AG
672#endif
673
c5fccf1d 674void fm_edit_preference( GtkWindow* parent, int page )
4d55886c
HJYP
675{
676 if(!pref_dlg)
677 {
678 GtkBuilder* builder = gtk_builder_new();
35243f05
HJYP
679 GtkTreeView* tab_label_list;
680 GtkTreeSelection* tree_sel;
681 GtkListStore* tab_label_model;
04ecf5ec 682 GObject *obj;
35243f05
HJYP
683 GtkTreeIter it;
684 int i, n;
321cb581 685
4d55886c 686 gtk_builder_add_from_file(builder, PACKAGE_UI_DIR "/pref.ui", NULL);
3aebc679
AG
687 pref_dlg = GTK_WINDOW(gtk_builder_get_object(builder, "dlg"));
688 notebook = GTK_NOTEBOOK(gtk_builder_get_object(builder, "notebook"));
4d55886c 689
aadc32a8
AG
690 /* General tab */
691 /* special handling for single_click */
692 g_signal_connect(gtk_builder_get_object(builder, "single_click"),
693 "toggled", G_CALLBACK(on_single_click_toggled),
694 gtk_builder_get_object(builder, "auto_sel_box"));
695 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "single_click")),
696 fm_config->single_click);
697#if FM_CHECK_VERSION(1, 2, 0)
698 init_auto_selection_delay_scale(builder);
699#endif
c5fccf1d 700 INIT_BOOL(builder, FmConfig, confirm_del, NULL);
aadc32a8
AG
701 /* special handling for use_trash */
702 g_signal_connect(gtk_builder_get_object(builder, "use_trash"),
703 "toggled", G_CALLBACK(on_use_trash_toggled),
704 gtk_builder_get_object(builder, "vbox_trash"));
705 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "use_trash")),
706 fm_config->use_trash);
707#if FM_CHECK_VERSION(1, 0, 2)
708 INIT_BOOL_SHOW(builder, FmConfig, no_usb_trash, NULL);
709#endif
710#if FM_CHECK_VERSION(1, 2, 0)
711 INIT_BOOL_SHOW(builder, FmConfig, confirm_trash, NULL);
712#endif
498fc1df 713
aadc32a8
AG
714#if FM_CHECK_VERSION(1, 2, 0)
715 INIT_BOOL_SHOW(builder, FmConfig, quick_exec, NULL);
716#endif
4d55886c 717
c5fccf1d 718 INIT_COMBO(builder, FmAppConfig, bm_open_method, NULL);
aadc32a8 719#if FM_CHECK_VERSION(1, 2, 0)
4427075b 720 init_drop_default_action_combo(builder);
aadc32a8
AG
721 gtk_widget_show(GTK_WIDGET(gtk_builder_get_object(builder, "drop_default_action")));
722 gtk_widget_show(GTK_WIDGET(gtk_builder_get_object(builder, "drop_default_action_label")));
4427075b 723 INIT_BOOL_SHOW(builder, FmConfig, smart_desktop_autodrop, NULL);
1b553f57 724 INIT_BOOL_SHOW(builder, FmAppConfig, focus_previous, NULL);
aadc32a8 725#endif
d830e211 726 INIT_BOOL_SHOW(builder, FmAppConfig, change_tab_on_drop, NULL);
01a3ef5a
AG
727 gtk_widget_show(GTK_WIDGET(gtk_builder_get_object(builder, "on_unmount_vbox")));
728 if (app_config->close_on_unmount)
729 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "close_on_unmount")), TRUE);
730 else
731 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "go_home_on_unmount")), TRUE);
732 g_signal_connect(gtk_builder_get_object(builder, "close_on_unmount"),
733 "toggled", G_CALLBACK(on_close_on_unmount_toggled), app_config);
8d0091f1 734 INIT_COMBO(builder, FmAppConfig, view_mode, NULL);
aadc32a8 735 /* FIXME: translate FmStandardViewMode <-> GtkListStore index */
4d55886c 736
aadc32a8 737 /* 'Display' tab */
4d55886c
HJYP
738 INIT_ICON_SIZES(builder, big_icon_size);
739 INIT_ICON_SIZES(builder, small_icon_size);
9902aaea 740 INIT_ICON_SIZES(builder, thumbnail_size);
4d55886c
HJYP
741 INIT_ICON_SIZES(builder, pane_icon_size);
742
aadc32a8
AG
743 INIT_BOOL(builder, FmConfig, show_thumbnail, NULL);
744 INIT_BOOL(builder, FmConfig, thumbnail_local, NULL);
745 INIT_SPIN(builder, FmConfig, thumbnail_max, NULL);
746
747 INIT_BOOL(builder, FmConfig, si_unit, NULL);
748 INIT_BOOL(builder, FmConfig, backup_as_hidden, NULL);
749#if FM_CHECK_VERSION(1, 2, 0)
750 INIT_BOOL_SHOW(builder, FmConfig, show_full_names, NULL);
751 INIT_BOOL_SHOW(builder, FmConfig, shadow_hidden, NULL);
752#endif
753
754 /* 'Layout' tab */
755 INIT_BOOL(builder, FmAppConfig, hide_close_btn, NULL);
756 INIT_BOOL(builder, FmAppConfig, always_show_tabs, NULL);
757 INIT_SPIN(builder, FmAppConfig, max_tab_chars, NULL);
758
759#if FM_CHECK_VERSION(1, 0, 2)
760 INIT_BOOL(builder, FmConfig, no_child_non_expandable, NULL);
761 gtk_widget_show(GTK_WIDGET(gtk_builder_get_object(builder, "vbox_dir_tree")));
762#endif
763
764#if FM_CHECK_VERSION(1, 2, 0)
765 INIT_BOOL(builder, FmConfig, places_home, NULL);
766 INIT_BOOL(builder, FmConfig, places_desktop, NULL);
767 INIT_BOOL(builder, FmConfig, places_applications, NULL);
768 INIT_BOOL(builder, FmConfig, places_trash, NULL);
769 INIT_BOOL(builder, FmConfig, places_root, NULL);
770 INIT_BOOL(builder, FmConfig, places_computer, NULL);
771 INIT_BOOL(builder, FmConfig, places_network, NULL);
772 gtk_widget_show(GTK_WIDGET(gtk_builder_get_object(builder, "vbox_places")));
773#endif
774
775 /* 'Volume management' tab */
776 INIT_BOOL(builder, FmAppConfig, mount_on_startup, NULL);
777 INIT_BOOL(builder, FmAppConfig, mount_removable, NULL);
04ecf5ec
AG
778 obj = gtk_builder_get_object(builder, "autorun");
779 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(obj), app_config->autorun);
780 g_signal_connect(obj, "toggled", G_CALLBACK(on_autorun_toggled),
781 gtk_builder_get_object(builder, "autorun_choices_area"));
246e8420
AG
782 INIT_BOOL(builder, FmAppConfig, media_in_new_tab, NULL);
783 gtk_widget_show(GTK_WIDGET(gtk_builder_get_object(builder, "media_in_new_tab")));
aadc32a8
AG
784 //INIT_BOOL(builder, FmAppConfig, close_on_unmount, NULL);
785
04ecf5ec
AG
786 /* autorun choices editing area */
787 obj = gtk_builder_get_object(builder, "autorun_choices_area");
788 if (obj)
789 {
790 GtkTreeView *view = GTK_TREE_VIEW(gtk_builder_get_object(builder, "autorun_choices"));
791 GtkListStore *model = gtk_list_store_new(3, G_TYPE_ICON, G_TYPE_STRING, G_TYPE_STRING);
792 GtkTreeSelection *tree_sel;
793 GtkTreeViewColumn *col;
794 GtkCellRenderer *render;
795 GIcon *icon;
796 char *desc;
797 GList *keys, *l;
798 GtkTreeIter it;
799
800 gtk_widget_set_sensitive(GTK_WIDGET(obj), app_config->autorun);
801 gtk_widget_show(GTK_WIDGET(obj));
802
803 tree_sel = gtk_tree_view_get_selection(view);
804 gtk_tree_selection_set_mode(tree_sel, GTK_SELECTION_MULTIPLE);
805
806 col = gtk_tree_view_column_new();
807 render = gtk_cell_renderer_pixbuf_new();
808 gtk_tree_view_column_pack_start(col, render, FALSE);
809 gtk_tree_view_column_set_attributes(col, render, "gicon", 0, NULL);
810 render = gtk_cell_renderer_text_new();
811 gtk_tree_view_column_pack_start(col, render, FALSE);
812 gtk_tree_view_column_set_attributes(col, render, "text", 1, NULL);
813 gtk_tree_view_append_column(view, col);
814 keys = g_hash_table_get_keys(app_config->autorun_choices);
815 for (l = keys; l; l = l->next)
816 {
817 gtk_list_store_append(model, &it);
818 desc = g_content_type_get_description(l->data);
819 icon = g_content_type_get_icon(l->data);
820 gtk_list_store_set(model, &it, 0, icon, 1, desc, 2, l->data, -1);
821 if (icon)
822 g_object_unref(icon);
823 g_free(desc);
824 }
825 g_list_free(keys);
826 gtk_tree_view_set_model(view, GTK_TREE_MODEL(model));
827 /* handle 'Remove selected' button */
828 obj = gtk_builder_get_object(builder, "remove_autorun_choice");
829 g_signal_connect(obj, "clicked", G_CALLBACK(on_remove_autorun_choice_clicked),
830 view);
831 /* update button sensitivity by tree selection */
832 g_signal_connect(tree_sel, "changed", G_CALLBACK(on_choices_sel_changed),
833 obj);
834 }
835
aadc32a8 836 /* 'Advanced' tab */
bb31fd59 837 INIT_ENTRY(builder, FmConfig, terminal, NULL);
28e69c40 838 /*INIT_ENTRY(builder, FmAppConfig, su_cmd, NULL);*/
aadc32a8 839#if FM_CHECK_VERSION(1, 2, 0)
f3428f6a 840 INIT_ENTRY(builder, FmConfig, format_cmd, NULL);
aadc32a8
AG
841 gtk_widget_show(GTK_WIDGET(gtk_builder_get_object(builder, "hbox_format")));
842#endif
bb31fd59 843
321cb581
HJYP
844 /* archiver integration */
845 init_archiver_combo(builder);
846
aadc32a8
AG
847#if FM_CHECK_VERSION(1, 2, 0)
848 INIT_BOOL(builder, FmConfig, only_user_templates, NULL);
849 INIT_BOOL(builder, FmConfig, template_type_once, NULL);
850 INIT_BOOL(builder, FmConfig, template_run_app, NULL);
851 gtk_widget_show(GTK_WIDGET(gtk_builder_get_object(builder, "vbox_templates")));
852 INIT_BOOL_SHOW(builder, FmConfig, defer_content_test, NULL);
853#endif
854 INIT_BOOL(builder, FmConfig, force_startup_notify, NULL);
855
35243f05
HJYP
856 /* initialize the left side list used for switching among tabs */
857 tab_label_list = GTK_TREE_VIEW(gtk_builder_get_object(builder, "tab_label_list"));
858 tab_label_model = gtk_list_store_new(1, G_TYPE_STRING);
859 n = gtk_notebook_get_n_pages(notebook);
860 for(i = 0; i < n; ++i)
861 {
862 /* this can be less efficient than iterating over a GList obtained by gtk_container_get_children().
863 * However, the order of pages does matter here. So we use get_nth_page. */
864 GtkWidget* page = gtk_notebook_get_nth_page(notebook, i);
865 const char* title = gtk_notebook_get_tab_label_text(notebook, page);
866 gtk_list_store_insert_with_values(tab_label_model, NULL, i, 0, title, -1);
867 }
868 gtk_tree_view_set_model(tab_label_list, GTK_TREE_MODEL(tab_label_model));
8d3876e1 869 gtk_tree_view_set_enable_search(tab_label_list, FALSE);
35243f05 870 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(tab_label_model), &it);
35243f05 871 tree_sel = gtk_tree_view_get_selection(tab_label_list);
8d3876e1 872 gtk_tree_selection_set_mode(tree_sel, GTK_SELECTION_BROWSE);
35243f05 873 gtk_tree_selection_select_iter(tree_sel, &it);
21df32aa 874 g_object_unref(tab_label_model);
2e9d61cd
AG
875#if FM_CHECK_VERSION(1, 2, 0)
876 obj = gtk_builder_get_object(builder, "home_path_vbox");
877 if (obj)
878 {
879 gboolean is_set;
880
881 gtk_widget_show(GTK_WIDGET(obj));
882 obj = gtk_builder_get_object(builder, "use_home_path");
883 g_signal_connect(obj, "toggled", G_CALLBACK(on_use_home_path_toggled),
884 gtk_builder_get_object(builder, "home_path_custom"));
885 g_signal_connect(obj, "toggled", G_CALLBACK(on_use_home_path_toggled2),
886 gtk_builder_get_object(builder, "home_path"));
887 is_set = app_config->home_path &&
888 strcmp(app_config->home_path, fm_get_home_dir()) != 0;
889 if (is_set)
890 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(obj), TRUE);
891 else
892 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "use_home_default")), TRUE);
893 obj = gtk_builder_get_object(builder, "home_path");
894 if (is_set)
895 gtk_entry_set_text(GTK_ENTRY(obj), app_config->home_path);
896 else
897 gtk_entry_set_text(GTK_ENTRY(obj), fm_get_home_dir());
898 g_signal_connect(obj, "changed", G_CALLBACK(on_home_path_changed), app_config);
899 obj = gtk_builder_get_object(builder, "home_path_current");
900 if (parent && IS_FM_MAIN_WIN(parent)) /* is called from menu */
901 g_signal_connect(obj, "clicked", G_CALLBACK(on_home_path_current_clicked),
902 gtk_builder_get_object(builder, "home_path"));
903 else
904 gtk_widget_set_sensitive(GTK_WIDGET(obj), FALSE);
905 }
9c28c31e
AG
906 /* modules blacklist and whitelist */
907 obj = gtk_builder_get_object(builder, "modules_tab");
908 if (obj)
909 {
910 GtkTreeView *view = GTK_TREE_VIEW(gtk_builder_get_object(builder, "modules_blacklist"));
911 GtkListStore *model = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_BOOLEAN);
912 GtkTreeSelection *tree_sel;
913 GtkTreeViewColumn *col;
914 GtkCellRenderer *render;
915 char **item;
916 GtkTreeIter it;
917
918 gtk_widget_show(GTK_WIDGET(obj));
919
920 /* blacklist */
921 tree_sel = gtk_tree_view_get_selection(view);
922 gtk_tree_selection_set_mode(tree_sel, GTK_SELECTION_MULTIPLE);
923
924 col = gtk_tree_view_column_new();
925 render = gtk_cell_renderer_text_new();
926 gtk_tree_view_column_pack_start(col, render, FALSE);
927 gtk_tree_view_column_set_attributes(col, render, "text", 0, NULL);
928 gtk_tree_view_append_column(view, col);
929 for (item = fm_config->system_modules_blacklist; item && *item; item++)
930 {
931 gtk_list_store_append(model, &it);
932 gtk_list_store_set(model, &it, 0, *item, 1, FALSE, -1);
933 }
934 for (item = fm_config->modules_blacklist; item && *item; item++)
935 {
936 gtk_list_store_append(model, &it);
937 gtk_list_store_set(model, &it, 0, *item, 1, TRUE, -1);
938 }
939 gtk_tree_view_set_model(view, GTK_TREE_MODEL(model));
940 /* handle 'Add' button */
941 obj = gtk_builder_get_object(builder, "add_to_blacklist");
942 g_signal_connect(obj, "clicked", G_CALLBACK(on_add_to_blacklist_clicked),
943 model);
944 /* handle 'Remove' button */
945 obj = gtk_builder_get_object(builder, "remove_from_blacklist");
946 g_signal_connect(obj, "clicked", G_CALLBACK(on_remove_from_blacklist_clicked),
947 view);
948 /* update button sensitivity by tree selection */
949 g_signal_connect(tree_sel, "changed", G_CALLBACK(on_blacklist_sel_changed),
950 obj);
951
952 /* whitelist */
953 view = GTK_TREE_VIEW(gtk_builder_get_object(builder, "modules_whitelist"));
954 model = gtk_list_store_new(1, G_TYPE_STRING);
955 tree_sel = gtk_tree_view_get_selection(view);
956 gtk_tree_selection_set_mode(tree_sel, GTK_SELECTION_MULTIPLE);
957
958 col = gtk_tree_view_column_new();
959 render = gtk_cell_renderer_text_new();
960 gtk_tree_view_column_pack_start(col, render, FALSE);
961 gtk_tree_view_column_set_attributes(col, render, "text", 0, NULL);
962 gtk_tree_view_append_column(view, col);
963 for (item = fm_config->modules_whitelist; item && *item; item++)
964 {
965 gtk_list_store_append(model, &it);
966 gtk_list_store_set(model, &it, 0, *item, -1);
967 }
968 gtk_tree_view_set_model(view, GTK_TREE_MODEL(model));
969 /* handle 'Add' button */
970 obj = gtk_builder_get_object(builder, "add_to_whitelist");
971 g_signal_connect(obj, "clicked", G_CALLBACK(on_add_to_whitelist_clicked),
972 model);
973 /* handle 'Remove' button */
974 obj = gtk_builder_get_object(builder, "remove_from_whitelist");
975 g_signal_connect(obj, "clicked", G_CALLBACK(on_remove_from_whitelist_clicked),
976 view);
977 /* update button sensitivity by tree selection */
978 g_signal_connect(tree_sel, "changed", G_CALLBACK(on_whitelist_sel_changed),
979 obj);
980 }
2e9d61cd 981#endif
35243f05 982 g_signal_connect(tree_sel, "changed", G_CALLBACK(on_tab_label_list_sel_changed), notebook);
8d3876e1 983 g_signal_connect(notebook, "switch-page", G_CALLBACK(on_notebook_page_changed), tab_label_list);
35243f05
HJYP
984 gtk_notebook_set_show_tabs(notebook, FALSE);
985
c5fccf1d 986 g_signal_connect(pref_dlg, "response", G_CALLBACK(on_response), &pref_dlg);
8d3876e1 987 g_signal_connect(pref_dlg, "key-press-event", G_CALLBACK(on_key_press), notebook);
4d55886c 988 g_object_unref(builder);
c5fccf1d 989
f8f2bfad
HJYP
990 pcmanfm_ref();
991 g_signal_connect(pref_dlg, "destroy", G_CALLBACK(pcmanfm_unref), NULL);
7d78ab22
AG
992 if(parent)
993 gtk_window_set_transient_for(pref_dlg, parent);
4d55886c 994 }
fbce80c9
AG
995 if(page < 0 || page >= gtk_notebook_get_n_pages(notebook))
996 page = 0;
3aebc679
AG
997 gtk_notebook_set_current_page(notebook, page);
998 gtk_window_present(pref_dlg);
f21b7816 999}