Fix compatibility with LIBFM 1.0.1 and 1.1.0.
[lxde/pcmanfm.git] / src / main-win.c
1 /*
2 * main-win.c
3 *
4 * Copyright 2009 - 2012 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
5 * Copyright 2012-2014 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua>
6 *
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.
11 *
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.
16 *
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
27 #include <glib/gi18n.h>
28 #include <gdk/gdkkeysyms.h>
29
30 #include <unistd.h> /* for get euid */
31 #include <sys/types.h>
32 #include <ctype.h>
33
34 #include "pcmanfm.h"
35
36 #include "app-config.h"
37 #include "main-win.h"
38 #include "pref.h"
39 #include "tab-page.h"
40 #include "connect-server.h"
41
42 #include "gseal-gtk-compat.h"
43
44 #if GTK_CHECK_VERSION(3, 0, 0)
45 static void fm_main_win_destroy(GtkWidget *object);
46 #else
47 static void fm_main_win_destroy(GtkObject *object);
48 #endif
49
50 static void fm_main_win_finalize(GObject *object);
51 G_DEFINE_TYPE(FmMainWin, fm_main_win, GTK_TYPE_WINDOW);
52
53 static void update_statusbar(FmMainWin* win);
54
55 static gboolean on_focus_in(GtkWidget* w, GdkEventFocus* evt);
56 static gboolean on_key_press_event(GtkWidget* w, GdkEventKey* evt);
57 static gboolean on_button_press_event(GtkWidget* w, GdkEventButton* evt);
58 static gboolean on_scroll_event(GtkWidget* w, GdkEventScroll* evt);
59 static void on_unrealize(GtkWidget* widget);
60
61 static void bounce_action(GtkAction* act, FmMainWin* win);
62
63 static void on_new_win(GtkAction* act, FmMainWin* win);
64 static void on_new_tab(GtkAction* act, FmMainWin* win);
65 static void on_close_tab(GtkAction* act, FmMainWin* win);
66 static void on_close_win(GtkAction* act, FmMainWin* win);
67 static void on_open(GtkAction* act, FmMainWin* win);
68
69 static void on_link(GtkAction* act, FmMainWin* win);
70 static void on_copy_to(GtkAction* act, FmMainWin* win);
71 static void on_move_to(GtkAction* act, FmMainWin* win);
72 static void on_rename(GtkAction* act, FmMainWin* win);
73 static void on_trash(GtkAction* act, FmMainWin* win);
74 static void on_del(GtkAction* act, FmMainWin* win);
75 static void on_copy_path(GtkAction* act, FmMainWin* win);
76 static void on_preference(GtkAction* act, FmMainWin* win);
77
78 static void on_add_bookmark(GtkAction* act, FmMainWin* win);
79
80 static void on_go(GtkAction* act, FmMainWin* win);
81 static void on_go_back(GtkAction* act, FmMainWin* win);
82 static void on_go_forward(GtkAction* act, FmMainWin* win);
83 static void on_go_up(GtkAction* act, FmMainWin* win);
84 static void on_go_home(GtkAction* act, FmMainWin* win);
85 static void on_go_desktop(GtkAction* act, FmMainWin* win);
86 static void on_go_trash(GtkAction* act, FmMainWin* win);
87 static void on_go_computer(GtkAction* act, FmMainWin* win);
88 static void on_go_network(GtkAction* act, FmMainWin* win);
89 static void on_go_apps(GtkAction* act, FmMainWin* win);
90 static void on_go_connect(GtkAction* act, FmMainWin* win);
91 static void on_reload(GtkAction* act, FmMainWin* win);
92 #if FM_CHECK_VERSION(1, 0, 2)
93 static void on_filter(GtkAction* act, FmMainWin* win);
94 #endif
95 static void on_show_hidden(GtkToggleAction* act, FmMainWin* win);
96 #if FM_CHECK_VERSION(1, 2, 0)
97 static void on_mingle_dirs(GtkToggleAction* act, FmMainWin* win);
98 #endif
99 #if FM_CHECK_VERSION(1, 0, 2)
100 static void on_sort_ignore_case(GtkToggleAction* act, FmMainWin* win);
101 #endif
102 static void on_save_per_folder(GtkToggleAction* act, FmMainWin* win);
103 static void on_show_side_pane(GtkToggleAction* act, FmMainWin* win);
104 static void on_dual_pane(GtkToggleAction* act, FmMainWin* win);
105 static void on_show_toolbar(GtkToggleAction *action, FmMainWin *win);
106 static void on_toolbar_new_win(GtkToggleAction *act, FmMainWin *win);
107 static void on_toolbar_new_tab(GtkToggleAction *act, FmMainWin *win);
108 static void on_toolbar_nav(GtkToggleAction *act, FmMainWin *win);
109 static void on_toolbar_home(GtkToggleAction *act, FmMainWin *win);
110 static void on_show_status(GtkToggleAction *action, FmMainWin *win);
111 static void on_change_mode(GtkRadioAction* act, GtkRadioAction *cur, FmMainWin* win);
112 static void on_sort_by(GtkRadioAction* act, GtkRadioAction *cur, FmMainWin* win);
113 static void on_sort_type(GtkRadioAction* act, GtkRadioAction *cur, FmMainWin* win);
114 static void on_side_pane_mode(GtkRadioAction* act, GtkRadioAction *cur, FmMainWin* win);
115 static void on_about(GtkAction* act, FmMainWin* win);
116 static void on_key_nav_list(GtkAction* act, FmMainWin* win);
117 static void on_open_in_terminal(GtkAction* act, FmMainWin* win);
118 /*static void on_open_as_root(GtkAction* act, FmMainWin* win);*/
119 #if FM_CHECK_VERSION(1, 0, 2)
120 static void on_search(GtkAction* act, FmMainWin* win);
121 #endif
122 #if FM_CHECK_VERSION(1, 2, 0)
123 static void on_launch(GtkAction* act, FmMainWin* win);
124 #endif
125 static void on_fullscreen(GtkToggleAction* act, FmMainWin* win);
126
127 static void on_location(GtkAction* act, FmMainWin* win);
128
129 static void on_size_increment(GtkAction *act, FmMainWin *win);
130 static void on_size_decrement(GtkAction *act, FmMainWin *win);
131 static void on_size_default(GtkAction *act, FmMainWin *win);
132
133 static void on_notebook_switch_page(GtkNotebook* nb, gpointer* page, guint num, FmMainWin* win);
134 static void on_notebook_page_added(GtkNotebook* nb, GtkWidget* page, guint num, FmMainWin* win);
135 static void on_notebook_page_removed(GtkNotebook* nb, GtkWidget* page, guint num, FmMainWin* win);
136
137 #include "main-win-ui.c" /* ui xml definitions and actions */
138
139 static GSList* all_wins = NULL;
140 static GtkAboutDialog* about_dlg = NULL;
141 static GtkWidget* key_nav_list_dlg = NULL;
142
143 static GQuark main_win_qdata;
144
145 static void fm_main_win_class_init(FmMainWinClass *klass)
146 {
147 GObjectClass *g_object_class = G_OBJECT_CLASS(klass);
148 GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass);
149 #if GTK_CHECK_VERSION(3, 0, 0)
150 widget_class->destroy = fm_main_win_destroy;
151 #else
152 GtkObjectClass* gtk_object_class = GTK_OBJECT_CLASS(klass);
153 gtk_object_class->destroy = fm_main_win_destroy;
154 #endif
155 g_object_class = G_OBJECT_CLASS(klass);
156 g_object_class->finalize = fm_main_win_finalize;
157
158 widget_class = (GtkWidgetClass*)klass;
159 widget_class->focus_in_event = on_focus_in;
160 widget_class->key_press_event = on_key_press_event;
161 widget_class->button_press_event = on_button_press_event;
162 widget_class->scroll_event = on_scroll_event;
163 widget_class->unrealize = on_unrealize;
164
165 fm_main_win_parent_class = (GtkWindowClass*)g_type_class_peek(GTK_TYPE_WINDOW);
166
167 main_win_qdata = g_quark_from_static_string("FmMainWin::data");
168 }
169
170 static gboolean idle_focus_view(gpointer user_data)
171 {
172 FmMainWin* win = (FmMainWin*)user_data;
173 /* window might be destroyed already */
174 if(g_source_is_destroyed(g_main_current_source()))
175 return FALSE;
176 if(win->folder_view)
177 gtk_widget_grab_focus(GTK_WIDGET(win->folder_view));
178 win->idle_handler = 0;
179 return FALSE;
180 }
181
182 static void on_location_activate(GtkEntry* entry, FmMainWin* win)
183 {
184 FmPath* path = fm_path_entry_get_path(FM_PATH_ENTRY(entry));
185
186 /* bug #3615243 seems to be thread-related issue, let ref the path now */
187 fm_path_ref(path);
188 fm_main_win_chdir(win, path);
189 fm_path_unref(path);
190 if (app_config->pathbar_mode_buttons)
191 {
192 /* recover from Ctrl+L mode change */
193 gtk_widget_hide(GTK_WIDGET(win->location));
194 gtk_widget_hide(gtk_ui_manager_get_widget(win->ui, "/toolbar/Go"));
195 gtk_widget_show(GTK_WIDGET(win->path_bar));
196 }
197
198 /* FIXME: due to bug #650114 in GTK+, GtkEntry still call a
199 * idle function for GtkEntryCompletion even if the completion
200 * is set to NULL. This causes crash in pcmanfm since libfm
201 * set GtkCompletition to NULL when FmPathEntry loses its
202 * focus. Hence the bug is triggered when we set focus to
203 * the folder view, which in turns causes FmPathEntry to lose focus.
204 *
205 * See related bug reports for more info:
206 * https://bugzilla.gnome.org/show_bug.cgi?id=650114
207 * https://sourceforge.net/tracker/?func=detail&aid=3308324&group_id=156956&atid=801864
208 *
209 * So here is a quick fix:
210 * Set the focus to folder view in our own idle handler with lower
211 * priority than GtkEntry's idle function (They use G_PRIORITY_HIGH).
212 */
213 if(win->idle_handler == 0)
214 win->idle_handler = gdk_threads_add_idle_full(G_PRIORITY_LOW, idle_focus_view, win, NULL);
215 }
216
217 static void on_path_bar_chdir(FmPathBar *bar, FmPath *path, FmMainWin *win)
218 {
219 fm_main_win_chdir(win, path);
220 }
221
222 static void on_path_bar_mode(GtkRadioAction *act, GtkRadioAction *cur, FmMainWin *win)
223 {
224 int mode = gtk_radio_action_get_current_value(cur);
225
226 if (app_config->pathbar_mode_buttons != mode)
227 {
228 app_config->pathbar_mode_buttons = mode;
229 pcmanfm_save_config(FALSE);
230 }
231 gtk_widget_set_visible(GTK_WIDGET(win->location), mode == 0);
232 gtk_widget_set_visible(gtk_ui_manager_get_widget(win->ui, "/toolbar/Go"), mode == 0);
233 gtk_widget_set_visible(GTK_WIDGET(win->path_bar), mode);
234 }
235
236 static void update_sort_menu(FmMainWin* win)
237 {
238 GtkAction* act;
239 FmFolderView* fv = win->folder_view;
240 GtkSortType type;
241 #if FM_CHECK_VERSION(1, 0, 2)
242 FmFolderModelCol by;
243 FmSortMode mode;
244
245 if (win->in_update)
246 return;
247 win->in_update = TRUE;
248 /* we have to update this any time */
249 act = gtk_ui_manager_get_action(win->ui, "/menubar/ViewMenu/SavePerFolder");
250 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), win->current_page->own_config);
251 win->in_update = FALSE;
252 if(fv == NULL || fm_folder_view_get_model(fv) == NULL)
253 /* since 1.0.2 libfm have sorting only in FmFolderModel therefore
254 if there is no model then we cannot get last sorting from it */
255 return;
256 if(!fm_folder_model_get_sort(fm_folder_view_get_model(fv), &by, &mode))
257 return;
258 type = FM_SORT_IS_ASCENDING(mode) ? GTK_SORT_ASCENDING : GTK_SORT_DESCENDING;
259 /* we don't handle extended modes in radio actions so do that here */
260 if(mode != win->current_page->sort_type)
261 {
262 win->current_page->sort_type = mode;
263 if (win->current_page->own_config)
264 {
265 fm_app_config_save_config_for_path(fm_folder_view_get_cwd(fv),
266 mode, by, -1,
267 win->current_page->show_hidden,
268 NULL);
269 }
270 else
271 {
272 app_config->sort_type = mode;
273 pcmanfm_save_config(FALSE);
274 }
275 }
276 #else
277 FmFolderModelViewCol by = fm_folder_view_get_sort_by(fv);
278
279 type = fm_folder_view_get_sort_type(fv);
280 #endif
281 win->in_update = TRUE;
282 act = gtk_ui_manager_get_action(win->ui, "/menubar/ViewMenu/Sort/Asc");
283 gtk_radio_action_set_current_value(GTK_RADIO_ACTION(act), type);
284 act = gtk_ui_manager_get_action(win->ui, "/menubar/ViewMenu/Sort/ByName");
285 #if FM_CHECK_VERSION(1, 0, 2)
286 if(by == FM_FOLDER_MODEL_COL_DEFAULT)
287 by = FM_FOLDER_MODEL_COL_NAME;
288 #endif
289 gtk_radio_action_set_current_value(GTK_RADIO_ACTION(act), by);
290 #if FM_CHECK_VERSION(1, 0, 2)
291 act = gtk_ui_manager_get_action(win->ui, "/menubar/ViewMenu/Sort/SortIgnoreCase");
292 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act),
293 (mode & FM_SORT_CASE_SENSITIVE) == 0);
294 #endif
295 #if FM_CHECK_VERSION(1, 2, 0)
296 act = gtk_ui_manager_get_action(win->ui, "/menubar/ViewMenu/Sort/MingleDirs");
297 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act),
298 (mode & FM_SORT_NO_FOLDER_FIRST) != 0);
299 #endif
300 win->in_update = FALSE;
301 }
302
303 static void update_view_menu(FmMainWin* win)
304 {
305 GtkAction* act;
306 FmFolderView* fv = win->folder_view;
307
308 act = gtk_ui_manager_get_action(win->ui, "/menubar/ViewMenu/ShowHidden");
309 if (win->in_update)
310 return;
311 win->in_update = TRUE;
312 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), fm_folder_view_get_show_hidden(fv));
313 gtk_radio_action_set_current_value(win->first_view_mode,
314 fm_standard_view_get_mode(FM_STANDARD_VIEW(fv)));
315 win->in_update = FALSE;
316 }
317
318 static void update_file_menu(FmMainWin* win, FmPath *path)
319 {
320 GtkAction *act;
321 /* FmFolderView *fv = win->folder_view; */
322
323 act = gtk_ui_manager_get_action(win->ui, "/menubar/ToolMenu/Term");
324 gtk_action_set_sensitive(act, path && fm_path_is_native(path));
325 #if FM_CHECK_VERSION(1, 2, 0)
326 act = gtk_ui_manager_get_action(win->ui, "/menubar/ToolMenu/Launch");
327 gtk_action_set_sensitive(act, path && fm_path_is_native(path));
328 #endif
329 act = gtk_ui_manager_get_action(win->ui, "/menubar/GoMenu/Up");
330 gtk_action_set_sensitive(act, path && fm_path_get_parent(path));
331 }
332
333 static void on_folder_view_sort_changed(FmFolderView* fv, FmMainWin* win)
334 {
335 if(fv != win->folder_view)
336 return;
337 update_sort_menu(win);
338 }
339
340 static void on_folder_view_sel_changed(FmFolderView* fv, gint n_sel, FmMainWin* win)
341 {
342 GtkAction* act;
343 gboolean has_selected = n_sel > 0;
344
345 if(fv != win->folder_view)
346 return;
347 act = gtk_ui_manager_get_action(win->ui, "/menubar/EditMenu/Open");
348 gtk_action_set_sensitive(act, has_selected);
349 act = gtk_ui_manager_get_action(win->ui, "/menubar/EditMenu/Cut");
350 gtk_action_set_sensitive(act, has_selected);
351 act = gtk_ui_manager_get_action(win->ui, "/menubar/EditMenu/Copy");
352 gtk_action_set_sensitive(act, has_selected);
353 act = gtk_ui_manager_get_action(win->ui, "/menubar/EditMenu/ToTrash");
354 gtk_action_set_sensitive(act, has_selected);
355 act = gtk_ui_manager_get_action(win->ui, "/menubar/EditMenu/Del");
356 gtk_action_set_sensitive(act, has_selected);
357 act = gtk_ui_manager_get_action(win->ui, "/menubar/EditMenu/CopyPath");
358 gtk_action_set_sensitive(act, has_selected);
359 act = gtk_ui_manager_get_action(win->ui, "/menubar/EditMenu/Rename");
360 gtk_action_set_sensitive(act, n_sel == 1); /* can rename only single file */
361 act = gtk_ui_manager_get_action(win->ui, "/menubar/EditMenu/Link");
362 gtk_action_set_sensitive(act, has_selected);
363 act = gtk_ui_manager_get_action(win->ui, "/menubar/EditMenu/CopyTo");
364 gtk_action_set_sensitive(act, has_selected);
365 act = gtk_ui_manager_get_action(win->ui, "/menubar/EditMenu/MoveTo");
366 gtk_action_set_sensitive(act, has_selected);
367 act = gtk_ui_manager_get_action(win->ui, "/menubar/EditMenu/FileProp");
368 gtk_action_set_sensitive(act, has_selected);
369 }
370
371 static gboolean on_view_key_press_event(FmFolderView* fv, GdkEventKey* evt, FmMainWin* win)
372 {
373 switch(evt->keyval)
374 {
375 case GDK_KEY_BackSpace:
376 on_go_up(NULL, win);
377 break;
378 }
379 return FALSE;
380 }
381
382 static void on_bookmark(GtkMenuItem* mi, FmMainWin* win)
383 {
384 FmPath* path = (FmPath*)g_object_get_qdata(G_OBJECT(mi), main_win_qdata);
385 switch(app_config->bm_open_method)
386 {
387 case FM_OPEN_IN_CURRENT_TAB: /* current tab */
388 fm_main_win_chdir(win, path);
389 break;
390 case FM_OPEN_IN_NEW_TAB: /* new tab */
391 fm_main_win_add_tab(win, path);
392 break;
393 case FM_OPEN_IN_NEW_WINDOW: /* new window */
394 fm_main_win_add_win(win, path);
395 break;
396 }
397 }
398
399 static void create_bookmarks_menu(FmMainWin* win)
400 {
401 const GList *list, *l;
402 GtkWidget* mi;
403 int i = 0;
404
405 #if FM_CHECK_VERSION(1, 0, 2)
406 list = fm_bookmarks_get_all(win->bookmarks);
407 #else
408 list = fm_bookmarks_list_all(win->bookmarks);
409 #endif
410 for(l=list;l;l=l->next)
411 {
412 FmBookmarkItem* item = (FmBookmarkItem*)l->data;
413 mi = gtk_image_menu_item_new_with_label(item->name);
414 gtk_widget_show(mi);
415 g_object_set_qdata_full(G_OBJECT(mi), main_win_qdata,
416 fm_path_ref(item->path), (GDestroyNotify)fm_path_unref);
417 g_signal_connect(mi, "activate", G_CALLBACK(on_bookmark), win);
418 gtk_menu_shell_insert(win->bookmarks_menu, mi, i);
419 ++i;
420 }
421 #if FM_CHECK_VERSION(1, 0, 2)
422 g_list_free_full((GList*)list, (GDestroyNotify)fm_bookmark_item_unref);
423 #endif
424 if(i > 0)
425 {
426 mi = gtk_separator_menu_item_new();
427 gtk_widget_show(mi);
428 gtk_menu_shell_insert(win->bookmarks_menu, mi, i);
429 }
430 }
431
432 static void on_bookmarks_changed(FmBookmarks* bm, FmMainWin* win)
433 {
434 /* delete old items first. */
435 GList* mis = gtk_container_get_children(GTK_CONTAINER(win->bookmarks_menu));
436 GList* l;
437 for(l = mis;l;l=l->next)
438 {
439 GtkWidget* item = (GtkWidget*)l->data;
440 if( g_object_get_qdata(G_OBJECT(item), main_win_qdata) )
441 {
442 g_signal_handlers_disconnect_by_func(item, on_bookmark, win);
443 gtk_widget_destroy(item);
444 }
445 else
446 {
447 if(GTK_IS_SEPARATOR_MENU_ITEM(item))
448 gtk_widget_destroy(item);
449 break;
450 }
451 }
452 g_list_free(mis);
453
454 create_bookmarks_menu(win);
455 }
456
457 static void load_bookmarks(FmMainWin* win, GtkUIManager* ui)
458 {
459 GtkWidget* mi = gtk_ui_manager_get_widget(ui, "/menubar/BookmarksMenu");
460 win->bookmarks_menu = GTK_MENU_SHELL(gtk_menu_item_get_submenu(GTK_MENU_ITEM(mi)));
461 win->bookmarks = fm_bookmarks_dup();
462 g_signal_connect(win->bookmarks, "changed", G_CALLBACK(on_bookmarks_changed), win);
463
464 create_bookmarks_menu(win);
465 }
466
467 static void _update_hist_buttons(FmMainWin* win);
468
469 static void on_history_item(GtkMenuItem* mi, FmMainWin* win)
470 {
471 FmTabPage* page = win->current_page;
472 #if FM_CHECK_VERSION(1, 0, 2)
473 guint l = GPOINTER_TO_UINT(g_object_get_qdata(G_OBJECT(mi), main_win_qdata));
474 #else
475 GList* l = g_object_get_qdata(G_OBJECT(mi), main_win_qdata);
476 #endif
477 fm_tab_page_history(page, l);
478 /* update folder popup */
479 fm_folder_view_set_active(win->folder_view, FALSE);
480 fm_folder_view_add_popup(win->folder_view, GTK_WINDOW(win), NULL);
481 _update_hist_buttons(win);
482 }
483
484 static void disconnect_history_item(GtkWidget* mi, gpointer win)
485 {
486 g_signal_handlers_disconnect_by_func(mi, on_history_item, win);
487 gtk_widget_destroy(mi);
488 }
489
490 static void on_history_selection_done(GtkMenuShell* menu, gpointer win)
491 {
492 g_debug("history selection done");
493 g_signal_handlers_disconnect_by_func(menu, on_history_selection_done, NULL);
494 /* cleanup: disconnect and delete all items */
495 gtk_container_foreach(GTK_CONTAINER(menu), disconnect_history_item, win);
496 }
497
498 #if FM_CHECK_VERSION(1, 2, 0)
499 static void on_show_history_menu(FmMenuToolItem* btn, FmMainWin* win)
500 #else
501 static void on_show_history_menu(GtkMenuToolButton* btn, FmMainWin* win)
502 #endif
503 {
504 #if FM_CHECK_VERSION(1, 2, 0)
505 GtkMenuShell* menu = (GtkMenuShell*)fm_menu_tool_item_get_menu(btn);
506 #else
507 GtkMenuShell* menu = (GtkMenuShell*)gtk_menu_tool_button_get_menu(btn);
508 #endif
509 #if FM_CHECK_VERSION(1, 0, 2)
510 guint i, cur = fm_nav_history_get_cur_index(win->nav_history);
511 FmPath* path;
512 #else
513 const GList* l;
514 const GList* cur = fm_nav_history_get_cur_link(win->nav_history);
515 #endif
516
517 /* delete old items */
518 gtk_container_foreach(GTK_CONTAINER(menu), (GtkCallback)gtk_widget_destroy, NULL);
519
520 #if FM_CHECK_VERSION(1, 0, 2)
521 for (i = 0; (path = fm_nav_history_get_nth_path(win->nav_history, i)); i++)
522 {
523 #else
524 for(l = fm_nav_history_list(win->nav_history); l; l=l->next)
525 {
526 const FmNavHistoryItem* item = (FmNavHistoryItem*)l->data;
527 FmPath* path = item->path;
528 #endif
529 char* str = fm_path_display_name(path, TRUE);
530 GtkWidget* mi;
531 #if FM_CHECK_VERSION(1, 0, 2)
532 if (i == cur)
533 #else
534 if( l == cur )
535 #endif
536 {
537 mi = gtk_check_menu_item_new_with_label(str);
538 gtk_check_menu_item_set_draw_as_radio(GTK_CHECK_MENU_ITEM(mi), TRUE);
539 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mi), TRUE);
540 }
541 else
542 mi = gtk_menu_item_new_with_label(str);
543 g_free(str);
544
545 #if FM_CHECK_VERSION(1, 0, 2)
546 g_object_set_qdata(G_OBJECT(mi), main_win_qdata, GUINT_TO_POINTER(i));
547 #else
548 g_object_set_qdata_full(G_OBJECT(mi), main_win_qdata, (gpointer)l, NULL);
549 #endif
550 g_signal_connect(mi, "activate", G_CALLBACK(on_history_item), win);
551 gtk_menu_shell_append(menu, mi);
552 }
553 g_signal_connect(menu, "selection-done", G_CALLBACK(on_history_selection_done), NULL);
554 gtk_widget_show_all( GTK_WIDGET(menu) );
555 }
556
557 static void on_tab_page_splitter_pos_changed(GtkPaned* paned, GParamSpec* ps, FmMainWin* win)
558 {
559 GList *children, *child;
560 if(paned != (GtkPaned*)win->current_page)
561 return;
562
563 app_config->splitter_pos = gtk_paned_get_position(paned);
564 pcmanfm_save_config(FALSE);
565
566 /* apply the pos to all other pages as well */
567 /* TODO: maybe we should allow different splitter pos for different pages later? */
568 children = gtk_container_get_children(GTK_CONTAINER(win->notebook));
569 for(child = children; child; child = child->next)
570 {
571 FmTabPage* page = FM_TAB_PAGE(child->data);
572 if((GtkPaned*)page != paned)
573 gtk_paned_set_position(GTK_PANED(page), app_config->splitter_pos);
574 }
575 g_list_free(children);
576 }
577
578 /* This callback is only connected to side pane of current active tab page. */
579 static void on_side_pane_chdir(FmSidePane* sp, guint button, FmPath* path, FmMainWin* win)
580 {
581 if(sp != win->side_pane)
582 return;
583
584 if(button == 2) /* middle click */
585 {
586 FmPath *prev_path = NULL;
587
588 if (win->folder_view)
589 prev_path = fm_folder_view_get_cwd(win->folder_view);
590 fm_main_win_add_tab(win, path);
591 /* the side pane activates row itself so let reset it back */
592 if (prev_path)
593 fm_side_pane_chdir(sp, prev_path);
594 }
595 else
596 fm_main_win_chdir(win, path);
597
598 /* bug #3531696: search is done on side pane instead of folder view */
599 if(win->folder_view)
600 gtk_widget_grab_focus(GTK_WIDGET(win->folder_view));
601 }
602
603 /* This callback is only connected to side pane of current active tab page. */
604 static void on_side_pane_mode_changed(FmSidePane* sp, FmMainWin* win)
605 {
606 FmSidePaneMode mode;
607
608 if(sp != win->side_pane)
609 return;
610
611 mode = fm_side_pane_get_mode(sp);
612
613 /* update menu */
614 gtk_radio_action_set_current_value(win->first_side_pane_mode, mode);
615
616 if(mode != (app_config->side_pane_mode & FM_SP_MODE_MASK))
617 {
618 app_config->side_pane_mode = mode | (app_config->side_pane_mode & ~FM_SP_MODE_MASK);
619 fm_config_emit_changed(FM_CONFIG(app_config), "side_pane_mode");
620 pcmanfm_save_config(FALSE);
621 }
622 }
623
624 static void on_always_show_tabs_changed(FmAppConfig *cfg, FmMainWin *win)
625 {
626 /* it will affect only the case when window has exactly 1 tab,
627 all other cases will be handled when tab is added or removed */
628 if (gtk_notebook_get_n_pages(win->notebook) == 1)
629 gtk_notebook_set_show_tabs(win->notebook, app_config->always_show_tabs);
630 }
631
632 static void on_toolsbar_changed(FmAppConfig *cfg, FmMainWin *win)
633 {
634 GtkAction* act;
635 GtkWidget *toolitem;
636 int n;
637 gboolean active;
638
639 if (win->in_update)
640 return;
641 win->in_update = TRUE; /* avoid recursion */
642 active = cfg->tb.visible;
643 gtk_widget_set_visible(GTK_WIDGET(win->toolbar), active);
644 act = gtk_ui_manager_get_action(win->ui, "/menubar/ViewMenu/Toolbar/ToolbarNewWin");
645 gtk_action_set_sensitive(act, active);
646 if (gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(act)) != app_config->tb.new_win)
647 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), app_config->tb.new_win);
648 act = gtk_ui_manager_get_action(win->ui, "/menubar/ViewMenu/Toolbar/ToolbarNewTab");
649 gtk_action_set_sensitive(act, active);
650 if (gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(act)) != app_config->tb.new_tab)
651 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), app_config->tb.new_tab);
652 act = gtk_ui_manager_get_action(win->ui, "/menubar/ViewMenu/Toolbar/ToolbarNav");
653 gtk_action_set_sensitive(act, active);
654 if (gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(act)) != app_config->tb.nav)
655 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), app_config->tb.nav);
656 act = gtk_ui_manager_get_action(win->ui, "/menubar/ViewMenu/Toolbar/ToolbarHome");
657 gtk_action_set_sensitive(act, active);
658 if (gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(act)) != app_config->tb.home)
659 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), app_config->tb.home);
660 if (active) /* it it's hidden no reason to update its children */
661 {
662 toolitem = gtk_ui_manager_get_widget(win->ui, "/toolbar/New");
663 gtk_widget_set_visible(toolitem, cfg->tb.new_win);
664 toolitem = gtk_ui_manager_get_widget(win->ui, "/toolbar/NewTab");
665 gtk_widget_set_visible(toolitem, cfg->tb.new_tab);
666 active = cfg->tb.nav;
667 toolitem = gtk_ui_manager_get_widget(win->ui, "/toolbar/Next");
668 gtk_widget_set_visible(toolitem, active);
669 n = gtk_toolbar_get_item_index(win->toolbar, GTK_TOOL_ITEM(toolitem));
670 toolitem = GTK_WIDGET(gtk_toolbar_get_nth_item(win->toolbar, n-1));
671 gtk_widget_set_visible(toolitem, active); /* Hist */
672 #if FM_CHECK_VERSION(1, 2, 0)
673 toolitem = GTK_WIDGET(gtk_toolbar_get_nth_item(win->toolbar, n-2));
674 gtk_widget_set_visible(toolitem, active); /* Prev */
675 #endif
676 toolitem = GTK_WIDGET(gtk_toolbar_get_nth_item(win->toolbar, n+1));
677 gtk_widget_set_visible(toolitem, active); /* Up */
678 toolitem = gtk_ui_manager_get_widget(win->ui, "/toolbar/Home");
679 gtk_widget_set_visible(toolitem, cfg->tb.home);
680 }
681 win->in_update = FALSE;
682 }
683
684 static void on_statusbar_changed(FmAppConfig *cfg, FmMainWin *win)
685 {
686 gtk_widget_set_visible(GTK_WIDGET(win->statusbar), cfg->show_statusbar);
687 update_statusbar(win);
688 }
689
690 static gboolean settings_hack_done = FALSE;
691 static gint old_gtk_timeout_expand = 0;
692
693 static void on_change_tab_on_drop_changed(FmAppConfig *cfg, gpointer _unused)
694 {
695 if (app_config->change_tab_on_drop)
696 g_object_set(gtk_settings_get_default(), "gtk-timeout-expand",
697 old_gtk_timeout_expand, NULL);
698 else
699 g_object_set(gtk_settings_get_default(), "gtk-timeout-expand", 600000, NULL);
700 }
701
702 static void _do_settings_hack(void)
703 {
704 GtkSettings *settings;
705
706 if (settings_hack_done)
707 return;
708 settings_hack_done = TRUE;
709 settings = gtk_settings_get_default();
710 g_object_get(settings, "gtk-timeout-expand", &old_gtk_timeout_expand, NULL);
711 if (!app_config->change_tab_on_drop)
712 /* dirty hack to override crazy GtkNotebook which unfolds tab
713 each time it sees some drag comes to the tab label */
714 g_object_set(settings, "gtk-timeout-expand", 600000, NULL);
715 g_signal_connect(app_config, "changed::change_tab_on_drop",
716 G_CALLBACK(on_change_tab_on_drop_changed), NULL);
717 }
718
719 static void fm_main_win_init(FmMainWin *win)
720 {
721 GtkBox *vbox;
722 GtkWidget *menubar;
723 GtkBox *pathbox;
724 GtkToolItem *toolitem;
725 GtkUIManager* ui;
726 GtkActionGroup* act_grp;
727 GtkAction* act;
728 GtkAccelGroup* accel_grp;
729 AtkObject *atk_obj, *atk_view;
730 AtkRelation *relation;
731 #if FM_CHECK_VERSION(1, 2, 0)
732 GtkRadioAction *mode_action;
733 GSList *radio_group;
734 GString *str, *xml;
735 static char accel_str[] = "<Ctrl>1";
736 int i;
737 gboolean is_first;
738 #endif
739 GtkShadowType shadow_type;
740
741 pcmanfm_ref();
742 all_wins = g_slist_prepend(all_wins, win);
743
744 /* every window should have its own window group.
745 * So model dialogs opened for the window does not lockup
746 * other windows.
747 * This is related to bug #3439056 - Pcman is frozen renaming files. */
748 win->win_group = gtk_window_group_new();
749 gtk_window_group_add_window(win->win_group, GTK_WINDOW(win));
750
751 gtk_window_set_icon_name(GTK_WINDOW(win), "folder");
752
753 vbox = (GtkBox*)gtk_vbox_new(FALSE, 0);
754
755 /* create menu bar and toolbar */
756 ui = gtk_ui_manager_new();
757 act_grp = gtk_action_group_new("Main");
758 gtk_action_group_set_translation_domain(act_grp, NULL);
759 gtk_action_group_add_actions(act_grp, main_win_actions, G_N_ELEMENTS(main_win_actions), win);
760 gtk_action_group_add_toggle_actions(act_grp, main_win_toggle_actions,
761 G_N_ELEMENTS(main_win_toggle_actions), win);
762 #if FM_CHECK_VERSION(1, 2, 0)
763 /* generate list of modes dynamically from FmStandardView widget data */
764 radio_group = NULL;
765 is_first = TRUE;
766 str = g_string_new("ViewMode:");
767 xml = g_string_new("<menubar><menu action='ViewMenu'><menu action='FolderView'><placeholder name='ViewModes'>");
768 accel_str[6] = '1';
769 for(i = 0; i < fm_standard_view_get_n_modes(); i++)
770 {
771 if(fm_standard_view_get_mode_label(i))
772 {
773 g_string_append(str, fm_standard_view_mode_to_str(i));
774 mode_action = gtk_radio_action_new(str->str,
775 fm_standard_view_get_mode_label(i),
776 fm_standard_view_get_mode_tooltip(i),
777 fm_standard_view_get_mode_icon(i),
778 i);
779 gtk_radio_action_set_group(mode_action, radio_group);
780 radio_group = gtk_radio_action_get_group(mode_action);
781 gtk_action_group_add_action_with_accel(act_grp,
782 GTK_ACTION(mode_action),
783 accel_str);
784 if (is_first) /* work on first one only */
785 {
786 win->first_view_mode = mode_action;
787 g_signal_connect(mode_action, "changed", G_CALLBACK(on_change_mode), win);
788 }
789 is_first = FALSE;
790 g_object_unref(mode_action);
791 g_string_append_printf(xml, "<menuitem action='%s'/>", str->str);
792 accel_str[6]++; /* <Ctrl>2 and so on */
793 g_string_truncate(str, 9); /* reset it to just "ViewMode:" */
794 }
795 }
796 g_string_append(xml, "</placeholder></menu>"); /* it will be continued below */
797 #else
798 gtk_action_group_add_radio_actions(act_grp, main_win_mode_actions,
799 G_N_ELEMENTS(main_win_mode_actions),
800 app_config->view_mode,
801 G_CALLBACK(on_change_mode), win);
802 #endif
803 gtk_action_group_add_radio_actions(act_grp, main_win_sort_type_actions,
804 G_N_ELEMENTS(main_win_sort_type_actions),
805 #if FM_CHECK_VERSION(1, 0, 2)
806 FM_SORT_IS_ASCENDING(app_config->sort_type) ? GTK_SORT_ASCENDING : GTK_SORT_DESCENDING,
807 #else
808 app_config->sort_type,
809 #endif
810 G_CALLBACK(on_sort_type), win);
811 gtk_action_group_add_radio_actions(act_grp, main_win_sort_by_actions,
812 G_N_ELEMENTS(main_win_sort_by_actions),
813 app_config->sort_by,
814 G_CALLBACK(on_sort_by), win);
815 #if FM_CHECK_VERSION(1, 2, 0)
816 /* generate list of modes dynamically from FmSidePane widget data */
817 radio_group = NULL;
818 is_first = TRUE;
819 g_string_assign(str, "SidePaneMode:");
820 g_string_append(xml, "<menu action='SidePane'><placeholder name='SidePaneModes'>");
821 accel_str[6] = '6';
822 for(i = 1; i <= fm_side_pane_get_n_modes(); i++)
823 {
824 if(fm_side_pane_get_mode_label(i))
825 {
826 g_string_append(str, fm_side_pane_get_mode_name(i));
827 mode_action = gtk_radio_action_new(str->str,
828 fm_side_pane_get_mode_label(i),
829 fm_side_pane_get_mode_tooltip(i),
830 NULL,
831 i);
832 gtk_radio_action_set_group(mode_action, radio_group);
833 radio_group = gtk_radio_action_get_group(mode_action);
834 gtk_action_group_add_action_with_accel(act_grp,
835 GTK_ACTION(mode_action),
836 accel_str);
837 if (is_first) /* work on first one only */
838 {
839 win->first_side_pane_mode = mode_action;
840 g_signal_connect(mode_action, "changed", G_CALLBACK(on_side_pane_mode), win);
841 }
842 is_first = FALSE;
843 g_object_unref(mode_action);
844 g_string_append_printf(xml, "<menuitem action='%s'/>", str->str);
845 accel_str[6]++; /* <Ctrl>7 and so on */
846 g_string_truncate(str, 13); /* reset it to just "SidePaneMode:" */
847 }
848 }
849 gtk_radio_action_set_current_value(win->first_side_pane_mode,
850 (app_config->side_pane_mode & FM_SP_MODE_MASK));
851 g_string_append(xml, "</placeholder></menu></menu></menubar>");
852 g_string_free(str, TRUE);
853 #else
854 gtk_action_group_add_radio_actions(act_grp, main_win_side_bar_mode_actions,
855 G_N_ELEMENTS(main_win_side_bar_mode_actions),
856 (app_config->side_pane_mode & FM_SP_MODE_MASK),
857 G_CALLBACK(on_side_pane_mode), win);
858 #endif
859 gtk_action_group_add_radio_actions(act_grp, main_win_path_bar_mode_actions,
860 G_N_ELEMENTS(main_win_path_bar_mode_actions),
861 0, G_CALLBACK(on_path_bar_mode), win);
862
863 accel_grp = gtk_ui_manager_get_accel_group(ui);
864 gtk_window_add_accel_group(GTK_WINDOW(win), accel_grp);
865
866 gtk_ui_manager_insert_action_group(ui, act_grp, 0);
867 gtk_ui_manager_add_ui_from_string(ui, main_menu_xml, -1, NULL);
868 #if FM_CHECK_VERSION(1, 2, 0)
869 /* add ui generated above */
870 gtk_ui_manager_add_ui_from_string(ui, xml->str, xml->len, NULL);
871 g_string_free(xml, TRUE);
872 #else
873 act = gtk_ui_manager_get_action(ui, "/menubar/ViewMenu/FolderView/IconView");
874 win->first_view_mode = GTK_RADIO_ACTION(act);
875 act = gtk_ui_manager_get_action(ui, "/menubar/ViewMenu/SidePane/Places");
876 win->first_side_pane_mode = GTK_RADIO_ACTION(act);
877 #endif
878 #if !FM_CHECK_VERSION(1, 0, 2)
879 act = gtk_ui_manager_get_action(ui, "/menubar/ViewMenu/ShowHidden");
880 /* we cannot keep it in sync without callback from folder view which
881 is available only in 1.0.2 so just hide it */
882 gtk_action_set_visible(act, FALSE);
883 #endif
884 act = gtk_ui_manager_get_action(ui, "/menubar/ViewMenu/SidePane/ShowSidePane");
885 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act),
886 (app_config->side_pane_mode & FM_SP_HIDE) == 0);
887
888 #if FM_CHECK_VERSION(1, 2, 0)
889 /* disable "Find Files" button if module isn't available */
890 if (!fm_module_is_in_use("vfs", "search"))
891 {
892 act = gtk_ui_manager_get_action(ui, "/menubar/ToolMenu/Search");
893 gtk_action_set_sensitive(act, FALSE);
894 }
895 /* disable "Applications" button if module isn't available */
896 if (!fm_module_is_in_use("vfs", "menu"))
897 {
898 act = gtk_ui_manager_get_action(ui, "/menubar/GoMenu/Apps");
899 gtk_action_set_sensitive(act, FALSE);
900 }
901 #endif
902
903 menubar = gtk_ui_manager_get_widget(ui, "/menubar");
904 win->toolbar = GTK_TOOLBAR(gtk_ui_manager_get_widget(ui, "/toolbar"));
905 /* FIXME: should make these optional */
906 gtk_toolbar_set_icon_size(win->toolbar, GTK_ICON_SIZE_SMALL_TOOLBAR);
907 gtk_toolbar_set_style(win->toolbar, GTK_TOOLBAR_ICONS);
908
909 #if FM_CHECK_VERSION(1, 2, 0)
910 /* create history button after 'Prev' and add a popup menu to it */
911 toolitem = fm_menu_tool_item_new();
912 gtk_toolbar_insert(win->toolbar, toolitem, 3);
913 #else
914 /* create 'Prev' button manually and add a popup menu to it */
915 toolitem = (GtkToolItem*)g_object_new(GTK_TYPE_MENU_TOOL_BUTTON, NULL);
916 gtk_toolbar_insert(win->toolbar, toolitem, 1);
917 act = gtk_ui_manager_get_action(ui, "/menubar/GoMenu/Prev");
918 gtk_activatable_set_related_action(GTK_ACTIVATABLE(toolitem), act);
919 #endif
920
921 /* set up history menu */
922 win->history_menu = gtk_menu_new();
923 #if FM_CHECK_VERSION(1, 2, 0)
924 fm_menu_tool_item_set_menu(FM_MENU_TOOL_ITEM(toolitem), win->history_menu);
925 gtk_tool_item_set_tooltip_text(toolitem, _("Show history of visited folders"));
926 #else
927 gtk_menu_tool_button_set_menu(GTK_MENU_TOOL_BUTTON(toolitem), win->history_menu);
928 gtk_menu_tool_button_set_arrow_tooltip_text(GTK_MENU_TOOL_BUTTON(toolitem),
929 _("Show history of visited folders"));
930 #endif
931 g_signal_connect(toolitem, "show-menu", G_CALLBACK(on_show_history_menu), win);
932 atk_obj = gtk_widget_get_accessible(GTK_WIDGET(toolitem));
933 if (atk_obj)
934 atk_object_set_name(atk_obj, _("History"));
935
936 gtk_box_pack_start( vbox, menubar, FALSE, TRUE, 0 );
937 gtk_box_pack_start( vbox, GTK_WIDGET(win->toolbar), FALSE, TRUE, 0 );
938
939 /* load bookmarks menu */
940 load_bookmarks(win, ui);
941
942 /* the location bar */
943 win->location = fm_path_entry_new();
944 g_signal_connect(win->location, "activate", G_CALLBACK(on_location_activate), win);
945 if(geteuid() == 0) /* if we're using root, Give the user some warnings */
946 {
947 GtkWidget* warning = gtk_image_new_from_stock(GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
948 gtk_widget_set_tooltip_markup(warning, _("You are in super user mode"));
949
950 toolitem = gtk_tool_item_new();
951 gtk_container_add( GTK_CONTAINER(toolitem), warning );
952 gtk_toolbar_insert(win->toolbar, gtk_separator_tool_item_new(), 0);
953
954 gtk_toolbar_insert(win->toolbar, toolitem, 0);
955 }
956
957 win->path_bar = fm_path_bar_new();
958 g_signal_connect(win->path_bar, "chdir", G_CALLBACK(on_path_bar_chdir), win);
959 pathbox = (GtkBox*)gtk_hbox_new(FALSE, 0);
960 gtk_box_pack_start(pathbox, GTK_WIDGET(win->location), TRUE, TRUE, 0);
961 gtk_box_pack_start(pathbox, GTK_WIDGET(win->path_bar), TRUE, TRUE, 0);
962
963 toolitem = (GtkToolItem*)gtk_tool_item_new();
964 gtk_container_add(GTK_CONTAINER(toolitem), GTK_WIDGET(pathbox));
965 gtk_tool_item_set_expand(toolitem, TRUE);
966 gtk_toolbar_insert(win->toolbar, toolitem, gtk_toolbar_get_n_items(win->toolbar) - 1);
967 g_signal_connect(app_config, "changed::toolsbar",
968 G_CALLBACK(on_toolsbar_changed), win);
969
970 /* notebook - it contains both side pane and folder view(s) */
971 win->notebook = (GtkNotebook*)gtk_notebook_new();
972 gtk_notebook_set_scrollable(win->notebook, TRUE);
973 gtk_container_set_border_width(GTK_CONTAINER(win->notebook), 0);
974 gtk_notebook_set_show_border(win->notebook, FALSE);
975 _do_settings_hack(); /* do it after GtkNotebook initialized */
976
977 /* We need to use connect_after here.
978 * GtkNotebook handles the real page switching stuff in default
979 * handler of 'switch-page' signal. The current page is changed to the new one
980 * after the signal is emitted. So before the default handler is finished,
981 * current page is still the old one. */
982 g_signal_connect_after(win->notebook, "switch-page", G_CALLBACK(on_notebook_switch_page), win);
983 g_signal_connect(win->notebook, "page-added", G_CALLBACK(on_notebook_page_added), win);
984 g_signal_connect(win->notebook, "page-removed", G_CALLBACK(on_notebook_page_removed), win);
985
986 gtk_box_pack_start(vbox, GTK_WIDGET(win->notebook), TRUE, TRUE, 0);
987 g_signal_connect(app_config, "changed::always_show_tabs",
988 G_CALLBACK(on_always_show_tabs_changed), win);
989
990 /* status bar */
991 win->statusbar = (GtkStatusbar*)gtk_statusbar_new();
992 /* status bar column showing volume free space */
993 gtk_widget_style_get(GTK_WIDGET(win->statusbar), "shadow-type", &shadow_type, NULL);
994 win->vol_status = (GtkFrame*)gtk_frame_new(NULL);
995 gtk_frame_set_shadow_type(win->vol_status, shadow_type);
996 gtk_box_pack_start(GTK_BOX(win->statusbar), GTK_WIDGET(win->vol_status), FALSE, TRUE, 0);
997 gtk_container_add(GTK_CONTAINER(win->vol_status), gtk_label_new(NULL));
998
999 gtk_box_pack_start( vbox, GTK_WIDGET(win->statusbar), FALSE, TRUE, 0 );
1000 win->statusbar_ctx = gtk_statusbar_get_context_id(win->statusbar, "status");
1001 win->statusbar_ctx2 = gtk_statusbar_get_context_id(win->statusbar, "status2");
1002 g_signal_connect(app_config, "changed::statusbar",
1003 G_CALLBACK(on_statusbar_changed), win);
1004
1005 g_object_unref(act_grp);
1006 win->ui = ui;
1007
1008 /* accessibility: setup relation for Go button and location entry */
1009 atk_obj = gtk_widget_get_accessible(GTK_WIDGET(win->location));
1010 relation = atk_relation_new(&atk_obj, 1, ATK_RELATION_LABEL_FOR);
1011 /* use atk_view for button temporarily */
1012 atk_view = gtk_widget_get_accessible(gtk_ui_manager_get_widget(ui, "/toolbar/Go"));
1013 atk_relation_set_add(atk_object_ref_relation_set(atk_view), relation);
1014 g_object_unref(relation);
1015 /* setup relations with view */
1016 atk_view = gtk_widget_get_accessible(GTK_WIDGET(win->notebook));
1017 relation = atk_relation_new(&atk_obj, 1, ATK_RELATION_CONTROLLED_BY);
1018 atk_relation_set_add(atk_object_ref_relation_set(atk_view), relation);
1019 g_object_unref(relation);
1020 atk_obj = gtk_widget_get_accessible(GTK_WIDGET(win->statusbar));
1021 relation = atk_relation_new(&atk_obj, 1, ATK_RELATION_DESCRIBED_BY);
1022 atk_relation_set_add(atk_object_ref_relation_set(atk_view), relation);
1023 g_object_unref(relation);
1024
1025 gtk_container_add(GTK_CONTAINER(win), GTK_WIDGET(vbox));
1026 }
1027
1028
1029 FmMainWin* fm_main_win_new(void)
1030 {
1031 FmMainWin* win = (FmMainWin*)g_object_new(FM_MAIN_WIN_TYPE, NULL);
1032 return win;
1033 }
1034
1035 #if GTK_CHECK_VERSION(3, 0, 0)
1036 static void fm_main_win_destroy(GtkWidget *object)
1037 #else
1038 static void fm_main_win_destroy(GtkObject *object)
1039 #endif
1040 {
1041 FmMainWin *win;
1042
1043 g_return_if_fail(object != NULL);
1044 g_return_if_fail(IS_FM_MAIN_WIN(object));
1045 win = (FmMainWin*)object;
1046
1047 /* Gtk+ runs destroy method twice */
1048 if(win->win_group)
1049 {
1050 g_signal_handlers_disconnect_by_func(win->location, on_location_activate, win);
1051 g_signal_handlers_disconnect_by_func(win->notebook, on_notebook_switch_page, win);
1052 g_signal_handlers_disconnect_by_func(win->notebook, on_notebook_page_added, win);
1053 g_signal_handlers_disconnect_by_func(win->notebook, on_notebook_page_removed, win);
1054 g_signal_handlers_disconnect_by_func(app_config, on_toolsbar_changed, win);
1055 g_signal_handlers_disconnect_by_func(app_config, on_statusbar_changed, win);
1056 g_signal_handlers_disconnect_by_func(app_config, on_always_show_tabs_changed, win);
1057
1058 gtk_window_group_remove_window(win->win_group, GTK_WINDOW(win));
1059 g_object_unref(win->win_group);
1060 win->win_group = NULL;
1061 g_object_unref(win->ui);
1062 win->ui = NULL;
1063 if(win->bookmarks)
1064 {
1065 g_signal_handlers_disconnect_by_func(win->bookmarks, on_bookmarks_changed, win);
1066 g_object_unref(win->bookmarks);
1067 win->bookmarks = NULL;
1068 }
1069 /* This is for removing idle_focus_view() */
1070 if(win->idle_handler)
1071 {
1072 g_source_remove(win->idle_handler);
1073 win->idle_handler = 0;
1074 }
1075
1076 all_wins = g_slist_remove(all_wins, win);
1077
1078 while(gtk_notebook_get_n_pages(win->notebook) > 0)
1079 gtk_notebook_remove_page(win->notebook, 0);
1080 }
1081
1082 #if GTK_CHECK_VERSION(3, 0, 0)
1083 (*GTK_WIDGET_CLASS(fm_main_win_parent_class)->destroy)(object);
1084 #else
1085 (*GTK_OBJECT_CLASS(fm_main_win_parent_class)->destroy)(object);
1086 #endif
1087 }
1088
1089 static void fm_main_win_finalize(GObject *object)
1090 {
1091 g_return_if_fail(object != NULL);
1092 g_return_if_fail(IS_FM_MAIN_WIN(object));
1093
1094 if (G_OBJECT_CLASS(fm_main_win_parent_class)->finalize)
1095 (* G_OBJECT_CLASS(fm_main_win_parent_class)->finalize)(object);
1096
1097 pcmanfm_unref();
1098 }
1099
1100 static void on_unrealize(GtkWidget* widget)
1101 {
1102 int w, h;
1103 FmMainWin *win = FM_MAIN_WIN(widget);
1104
1105 gtk_window_get_size(GTK_WINDOW(widget), &w, &h);
1106 if(!win->fullscreen && !win->maximized &&
1107 (w != app_config->win_width || h != app_config->win_height))
1108 {
1109 app_config->win_width = w;
1110 app_config->win_height = h;
1111 pcmanfm_save_config(FALSE);
1112 }
1113 if (win->maximized != app_config->maximized)
1114 {
1115 app_config->maximized = win->maximized;
1116 pcmanfm_save_config(FALSE);
1117 }
1118 (*GTK_WIDGET_CLASS(fm_main_win_parent_class)->unrealize)(widget);
1119 }
1120
1121 static void on_about_response(GtkDialog* dlg, gint response, GtkAboutDialog **dlgptr)
1122 {
1123 g_signal_handlers_disconnect_by_func(dlg, on_about_response, dlgptr);
1124 *dlgptr = NULL;
1125 pcmanfm_unref();
1126 gtk_widget_destroy(GTK_WIDGET(dlg));
1127 }
1128
1129 static void on_about(GtkAction* act, FmMainWin* win)
1130 {
1131 if(!about_dlg)
1132 {
1133 GtkBuilder* builder = gtk_builder_new();
1134 GString *comments = g_string_new(_("Lightweight file manager\n"));
1135
1136 gtk_builder_add_from_file(builder, PACKAGE_UI_DIR "/about.ui", NULL);
1137 about_dlg = GTK_ABOUT_DIALOG(gtk_builder_get_object(builder, "dlg"));
1138 #if FM_CHECK_VERSION(1, 2, 0)
1139 g_string_append_printf(comments, _("using LibFM ver. %s\n"), fm_version());
1140 #endif
1141 g_string_append(comments, _("\nDeveloped by Hon Jen Yee (PCMan)"));
1142 gtk_about_dialog_set_comments(about_dlg, comments->str);
1143 g_string_free(comments, TRUE);
1144 g_object_unref(builder);
1145 g_signal_connect(about_dlg, "response", G_CALLBACK(on_about_response), (gpointer)&about_dlg);
1146 pcmanfm_ref();
1147 }
1148 gtk_window_present(GTK_WINDOW(about_dlg));
1149 }
1150
1151 static void on_key_nav_list_response(GtkDialog* dlg, gint response, GtkDialog **dlgptr)
1152 {
1153 g_signal_handlers_disconnect_by_func(dlg, on_key_nav_list_response, dlgptr);
1154 *dlgptr = NULL;
1155 gtk_widget_destroy(GTK_WIDGET(dlg));
1156 }
1157
1158 static void on_key_nav_list(GtkAction* act, FmMainWin* win)
1159 {
1160 if(!key_nav_list_dlg)
1161 {
1162 g_debug("creating key_nav_list_dlg");
1163 key_nav_list_dlg = gtk_message_dialog_new(NULL, 0,
1164 GTK_MESSAGE_INFO,
1165 GTK_BUTTONS_OK,
1166 _("Keyboard Navigation"));
1167 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(key_nav_list_dlg),
1168 _("Tab: cycle focus Folder View -> Side Pane -> Tools Bar\n"
1169 "Shift+Tab: cycle focus Tools Bar -> Side Pane -> Folder View\n"
1170 "F6: change focus Side pane <-> Folder view\n"
1171 "F8: focus divider between Side pane and Folder view\n"
1172 "F10: activate main menu\n"
1173 "Ctrl+L or Alt+D: jump focus to Path Bar"));
1174 gtk_window_set_title(GTK_WINDOW(key_nav_list_dlg), _("Keyboard Navigation"));
1175 g_signal_connect(key_nav_list_dlg, "response", G_CALLBACK(on_key_nav_list_response), (gpointer)&key_nav_list_dlg);
1176 }
1177 gtk_window_present(GTK_WINDOW(key_nav_list_dlg));
1178 }
1179
1180 static void on_open_in_terminal(GtkAction* act, FmMainWin* win)
1181 {
1182 FmPath *path;
1183 #if FM_CHECK_VERSION(1, 0, 2)
1184 path = fm_nav_history_get_nth_path(win->nav_history,
1185 fm_nav_history_get_cur_index(win->nav_history));
1186 if (path)
1187 #else
1188 const FmNavHistoryItem* item = fm_nav_history_get_cur(win->nav_history);
1189 if(item && (path = item->path))
1190 #endif
1191 pcmanfm_open_folder_in_terminal(GTK_WINDOW(win), path);
1192 }
1193
1194 #if FM_CHECK_VERSION(1, 0, 2)
1195 static void on_search(GtkAction* act, FmMainWin* win)
1196 {
1197 FmTabPage* page = win->current_page;
1198 FmPath* cwd = fm_tab_page_get_cwd(page);
1199 GList* l = g_list_append(NULL, cwd);
1200 fm_launch_search_simple(GTK_WINDOW(win), NULL, l, pcmanfm_open_folder, NULL);
1201 g_list_free(l);
1202 }
1203 #endif
1204
1205 #if FM_CHECK_VERSION(1, 2, 0)
1206 static void on_launch(GtkAction* act, FmMainWin* win)
1207 {
1208 char *cmd, *cwd, *def;
1209
1210 cmd = fm_get_user_input(GTK_WINDOW(win), _("Run a command"),
1211 _("Enter a command to run:"), NULL);
1212 if (cmd == NULL || cmd[0] == '\0') /* cancelled or empty */
1213 return;
1214 cwd = fm_path_to_str(fm_tab_page_get_cwd(win->current_page));
1215 def = g_get_current_dir();
1216 if (chdir(cwd) != 0) /* error */
1217 {
1218 fm_show_error(GTK_WINDOW(win), _("Current folder is inaccessible"), NULL);
1219 }
1220 else /* run and return back */
1221 {
1222 fm_launch_command_simple(GTK_WINDOW(win), NULL, 0, cmd, NULL);
1223 /* return back */
1224 if(chdir(def) != 0)
1225 g_warning("on_launch(): chdir() failed");
1226 }
1227 g_free(cmd);
1228 g_free(cwd);
1229 g_free(def);
1230 }
1231 #endif
1232
1233 static void on_show_hidden(GtkToggleAction* act, FmMainWin* win)
1234 {
1235 FmTabPage* page = win->current_page;
1236 gboolean active;
1237
1238 if(!page)
1239 return; /* it's fresh created window, do nothing */
1240
1241 active = gtk_toggle_action_get_active(act);
1242 fm_tab_page_set_show_hidden(page, active);
1243 }
1244
1245 static void on_fullscreen(GtkToggleAction* act, FmMainWin* win)
1246 {
1247 gboolean active = gtk_toggle_action_get_active(act);
1248
1249 if(win->fullscreen == active)
1250 return;
1251 win->fullscreen = active;
1252 if(win->fullscreen)
1253 {
1254 int w, h;
1255
1256 gtk_window_get_size(GTK_WINDOW(win), &w, &h);
1257 if(w != app_config->win_width || h != app_config->win_height)
1258 {
1259 app_config->win_width = w;
1260 app_config->win_height = h;
1261 pcmanfm_save_config(FALSE);
1262 }
1263 gtk_window_fullscreen(GTK_WINDOW(win));
1264 }
1265 else
1266 gtk_window_unfullscreen(GTK_WINDOW(win));
1267 }
1268
1269 static void on_change_mode(GtkRadioAction* act, GtkRadioAction *cur, FmMainWin* win)
1270 {
1271 int mode = gtk_radio_action_get_current_value(cur);
1272 if (win->in_update)
1273 return;
1274 fm_standard_view_set_mode(FM_STANDARD_VIEW(win->folder_view), mode);
1275 if (win->current_page->own_config)
1276 fm_app_config_save_config_for_path(fm_folder_view_get_cwd(win->folder_view),
1277 win->current_page->sort_type,
1278 win->current_page->sort_by, mode,
1279 win->current_page->show_hidden, NULL);
1280 else
1281 win->current_page->view_mode = mode;
1282 }
1283
1284 static void on_sort_by(GtkRadioAction* act, GtkRadioAction *cur, FmMainWin* win)
1285 {
1286 int val = gtk_radio_action_get_current_value(cur);
1287 FmFolderView *fv = win->folder_view;
1288 #if FM_CHECK_VERSION(1, 0, 2)
1289 FmFolderModel *model = fm_folder_view_get_model(fv);
1290
1291 if (model)
1292 fm_folder_model_set_sort(model, val, FM_SORT_DEFAULT);
1293 #else
1294 fm_folder_view_sort(fv, -1, val);
1295 #endif
1296 if(val != (int)win->current_page->sort_by)
1297 {
1298 win->current_page->sort_by = val;
1299 if (win->current_page->own_config)
1300 {
1301 fm_app_config_save_config_for_path(fm_folder_view_get_cwd(fv),
1302 win->current_page->sort_type,
1303 val, -1,
1304 win->current_page->show_hidden,
1305 NULL);
1306 }
1307 else
1308 {
1309 app_config->sort_by = val;
1310 pcmanfm_save_config(FALSE);
1311 }
1312 }
1313 }
1314
1315 #if FM_CHECK_VERSION(1, 0, 2)
1316 static inline void update_sort_type_for_page(FmTabPage *page, FmFolderView *fv, FmSortMode mode)
1317 #else
1318 static inline void update_sort_type_for_page(FmTabPage *page, FmFolderView *fv, guint mode)
1319 #endif
1320 {
1321 if(mode != page->sort_type)
1322 {
1323 page->sort_type = mode;
1324 if (page->own_config)
1325 {
1326 fm_app_config_save_config_for_path(fm_folder_view_get_cwd(fv), mode,
1327 page->sort_by, -1,
1328 page->show_hidden, NULL);
1329 }
1330 else
1331 {
1332 app_config->sort_type = mode;
1333 pcmanfm_save_config(FALSE);
1334 }
1335 }
1336 }
1337
1338 static void on_sort_type(GtkRadioAction* act, GtkRadioAction *cur, FmMainWin* win)
1339 {
1340 guint val = gtk_radio_action_get_current_value(cur);
1341 FmFolderView *fv = win->folder_view;
1342 #if FM_CHECK_VERSION(1, 0, 2)
1343 FmFolderModel *model = fm_folder_view_get_model(fv);
1344 FmSortMode mode;
1345
1346 if (model)
1347 {
1348 fm_folder_model_get_sort(model, NULL, &mode);
1349 mode &= ~FM_SORT_ORDER_MASK;
1350 mode |= (val == GTK_SORT_ASCENDING) ? FM_SORT_ASCENDING : FM_SORT_DESCENDING;
1351 fm_folder_model_set_sort(model, -1, mode);
1352 update_sort_type_for_page(win->current_page, fv, mode);
1353 }
1354 #else
1355 fm_folder_view_sort(win->folder_view, val, -1);
1356 update_sort_type_for_page(win->current_page, fv, val);
1357 #endif
1358 }
1359
1360 #if FM_CHECK_VERSION(1, 2, 0)
1361 static void on_mingle_dirs(GtkToggleAction* act, FmMainWin* win)
1362 {
1363 FmFolderView *fv = win->folder_view;
1364 FmFolderModel *model = fm_folder_view_get_model(fv);
1365 FmSortMode mode;
1366 gboolean active;
1367
1368 if (model)
1369 {
1370 fm_folder_model_get_sort(model, NULL, &mode);
1371 active = gtk_toggle_action_get_active(act);
1372 mode &= ~FM_SORT_NO_FOLDER_FIRST;
1373 if (active)
1374 mode |= FM_SORT_NO_FOLDER_FIRST;
1375 fm_folder_model_set_sort(model, -1, mode);
1376 update_sort_type_for_page(win->current_page, fv, mode);
1377 }
1378 }
1379 #endif
1380
1381 #if FM_CHECK_VERSION(1, 0, 2)
1382 static void on_sort_ignore_case(GtkToggleAction* act, FmMainWin* win)
1383 {
1384 FmFolderView *fv = win->folder_view;
1385 FmFolderModel *model = fm_folder_view_get_model(fv);
1386 FmSortMode mode;
1387 gboolean active;
1388
1389 if (model)
1390 {
1391 fm_folder_model_get_sort(model, NULL, &mode);
1392 active = gtk_toggle_action_get_active(act);
1393 mode &= ~FM_SORT_CASE_SENSITIVE;
1394 if (!active)
1395 mode |= FM_SORT_CASE_SENSITIVE;
1396 fm_folder_model_set_sort(model, -1, mode);
1397 update_sort_type_for_page(win->current_page, fv, mode);
1398 }
1399 }
1400 #endif
1401
1402 static void on_save_per_folder(GtkToggleAction* act, FmMainWin* win)
1403 {
1404 gboolean active = gtk_toggle_action_get_active(act);
1405 FmTabPage *page = win->current_page;
1406 FmFolderView *fv = win->folder_view;
1407
1408 if (active)
1409 {
1410 if (page->own_config) /* not changed */
1411 return;
1412 page->own_config = TRUE;
1413 #if FM_CHECK_VERSION(1, 0, 2)
1414 page->columns = g_strdupv(app_config->columns);
1415 #endif
1416 fm_app_config_save_config_for_path(fm_folder_view_get_cwd(fv),
1417 page->sort_type, page->sort_by,
1418 fm_standard_view_get_mode(FM_STANDARD_VIEW(fv)),
1419 #if FM_CHECK_VERSION(1, 0, 2)
1420 page->show_hidden, page->columns);
1421 #else
1422 page->show_hidden, NULL);
1423 #endif
1424 }
1425 else if (page->own_config) /* attribute removed */
1426 {
1427 page->own_config = FALSE;
1428 #if FM_CHECK_VERSION(1, 0, 2)
1429 g_strfreev(page->columns);
1430 page->columns = NULL;
1431 #endif
1432 fm_app_config_clear_config_for_path(fm_folder_view_get_cwd(fv));
1433 }
1434 }
1435
1436 static void on_side_pane_mode(GtkRadioAction* act, GtkRadioAction *cur, FmMainWin* win)
1437 {
1438 FmTabPage* cur_page = win->current_page;
1439 FmSidePane* sp;
1440
1441 if (cur_page == NULL) /* it can be NULL if we are in FmMainWin setup */
1442 return;
1443 sp = fm_tab_page_get_side_pane(cur_page);
1444 int val = gtk_radio_action_get_current_value(cur);
1445 fm_side_pane_set_mode(sp, val);
1446 }
1447
1448 static gboolean on_focus_in(GtkWidget* w, GdkEventFocus* evt)
1449 {
1450 if(all_wins->data != w)
1451 {
1452 all_wins = g_slist_remove(all_wins, w);
1453 all_wins = g_slist_prepend(all_wins, w);
1454 }
1455 return GTK_WIDGET_CLASS(fm_main_win_parent_class)->focus_in_event(w, evt);
1456 }
1457
1458 static void on_new_win(GtkAction* act, FmMainWin* win)
1459 {
1460 FmPath* path = fm_tab_page_get_cwd(win->current_page);
1461 fm_main_win_add_win(win, path);
1462 }
1463
1464 static void on_new_tab(GtkAction* act, FmMainWin* win)
1465 {
1466 FmPath* path = fm_tab_page_get_cwd(win->current_page);
1467 fm_main_win_add_tab(win, path);
1468 /* FR #1967725: focus location bar for newly created tab */
1469 gtk_window_set_focus(GTK_WINDOW(win), GTK_WIDGET(win->location));
1470 if (win->idle_handler)
1471 {
1472 /* it will steal focus so cancel it */
1473 g_source_remove(win->idle_handler);
1474 win->idle_handler = 0;
1475 }
1476 }
1477
1478 static void on_close_win(GtkAction* act, FmMainWin* win)
1479 {
1480 gtk_widget_destroy(GTK_WIDGET(win));
1481 }
1482
1483 static void on_close_tab(GtkAction* act, FmMainWin* win)
1484 {
1485 GtkNotebook* nb = win->notebook;
1486 /* remove current page */
1487 gtk_notebook_remove_page(nb, gtk_notebook_get_current_page(nb));
1488 }
1489
1490
1491 static void on_go(GtkAction* act, FmMainWin* win)
1492 {
1493 /* fm_main_win_chdir_by_name(win, gtk_entry_get_text(GTK_ENTRY(win->location))); */
1494 g_signal_emit_by_name(win->location, "activate");
1495 }
1496
1497 static void _update_hist_buttons(FmMainWin* win)
1498 {
1499 GtkAction* act;
1500 FmNavHistory *nh = fm_tab_page_get_history(win->current_page);
1501
1502 act = gtk_ui_manager_get_action(win->ui, "/menubar/GoMenu/Next");
1503 #if FM_CHECK_VERSION(1, 0, 2)
1504 gtk_action_set_sensitive(act, fm_nav_history_get_cur_index(nh) > 0);
1505 #else
1506 gtk_action_set_sensitive(act, fm_nav_history_can_forward(nh));
1507 #endif
1508 act = gtk_ui_manager_get_action(win->ui, "/menubar/GoMenu/Prev");
1509 gtk_action_set_sensitive(act, fm_nav_history_can_back(nh));
1510 update_file_menu(win, fm_tab_page_get_cwd(win->current_page));
1511 }
1512
1513 static void on_go_back(GtkAction* act, FmMainWin* win)
1514 {
1515 fm_tab_page_back(win->current_page);
1516 /* update folder popup */
1517 fm_folder_view_set_active(win->folder_view, FALSE);
1518 fm_folder_view_add_popup(win->folder_view, GTK_WINDOW(win), NULL);
1519 _update_hist_buttons(win);
1520 }
1521
1522 static void on_go_forward(GtkAction* act, FmMainWin* win)
1523 {
1524 fm_tab_page_forward(win->current_page);
1525 /* update folder popup */
1526 fm_folder_view_set_active(win->folder_view, FALSE);
1527 fm_folder_view_add_popup(win->folder_view, GTK_WINDOW(win), NULL);
1528 _update_hist_buttons(win);
1529 }
1530
1531 static void on_go_up(GtkAction* act, FmMainWin* win)
1532 {
1533 FmPath* parent = fm_path_get_parent(fm_tab_page_get_cwd(win->current_page));
1534 if(parent)
1535 fm_main_win_chdir( win, parent);
1536 }
1537
1538 static void on_go_home(GtkAction* act, FmMainWin* win)
1539 {
1540 #if FM_CHECK_VERSION(1, 2, 0)
1541 if (app_config->home_path && app_config->home_path[0])
1542 fm_main_win_chdir_by_name(win, app_config->home_path);
1543 else
1544 #endif
1545 fm_main_win_chdir( win, fm_path_get_home());
1546 }
1547
1548 static void on_go_desktop(GtkAction* act, FmMainWin* win)
1549 {
1550 fm_main_win_chdir(win, fm_path_get_desktop());
1551 }
1552
1553 static void on_go_trash(GtkAction* act, FmMainWin* win)
1554 {
1555 fm_main_win_chdir(win, fm_path_get_trash());
1556 }
1557
1558 static void on_go_computer(GtkAction* act, FmMainWin* win)
1559 {
1560 fm_main_win_chdir_by_name( win, "computer:///");
1561 }
1562
1563 static void on_go_network(GtkAction* act, FmMainWin* win)
1564 {
1565 fm_main_win_chdir_by_name( win, "network:///");
1566 }
1567
1568 static void on_go_apps(GtkAction* act, FmMainWin* win)
1569 {
1570 fm_main_win_chdir(win, fm_path_get_apps_menu());
1571 }
1572
1573 static void on_go_connect(GtkAction* act, FmMainWin* win)
1574 {
1575 open_connect_dialog(GTK_WINDOW(win));
1576 }
1577
1578 void fm_main_win_chdir_by_name(FmMainWin* win, const char* path_str)
1579 {
1580 FmPath* path = fm_path_new_for_str(path_str);
1581 fm_main_win_chdir(win, path);
1582 fm_path_unref(path);
1583 }
1584
1585 void fm_main_win_chdir(FmMainWin* win, FmPath* path)
1586 {
1587 /* NOTE: fm_tab_page_chdir() calls fm_side_pane_chdir(), which can
1588 * trigger on_side_pane_chdir() callback. So we need to block it here. */
1589 g_signal_handlers_block_by_func(win->side_pane, on_side_pane_chdir, win);
1590 fm_tab_page_chdir(win->current_page, path);
1591 /* update folder popup */
1592 fm_folder_view_set_active(win->folder_view, FALSE);
1593 fm_folder_view_add_popup(win->folder_view, GTK_WINDOW(win), NULL);
1594 g_signal_handlers_unblock_by_func(win->side_pane, on_side_pane_chdir, win);
1595 _update_hist_buttons(win);
1596 update_view_menu(win);
1597 update_file_menu(win, path);
1598 }
1599
1600 #if 0
1601 static void close_btn_style_set(GtkWidget *btn, GtkRcStyle *prev, gpointer data)
1602 {
1603 gint w, h;
1604 gtk_icon_size_lookup_for_settings(gtk_widget_get_settings(btn), GTK_ICON_SIZE_MENU, &w, &h);
1605 gtk_widget_set_size_request(btn, w + 2, h + 2);
1606 }
1607 #endif
1608
1609 static gboolean on_tab_label_button_pressed(GtkWidget* tab_label, GdkEventButton* evt, FmTabPage* tab_page)
1610 {
1611 if(evt->button == 2) /* middle click */
1612 {
1613 gtk_widget_destroy(GTK_WIDGET(tab_page));
1614 return TRUE;
1615 }
1616 return FALSE;
1617 }
1618
1619 static void update_statusbar(FmMainWin* win)
1620 {
1621 FmTabPage* page = win->current_page;
1622 const char* text;
1623 if (!app_config->show_statusbar)
1624 return; /* don't waste time on it */
1625 gtk_statusbar_pop(win->statusbar, win->statusbar_ctx);
1626 text = fm_tab_page_get_status_text(page, FM_STATUS_TEXT_NORMAL);
1627 if(text)
1628 gtk_statusbar_push(win->statusbar, win->statusbar_ctx, text);
1629
1630 text = fm_tab_page_get_status_text(page, FM_STATUS_TEXT_SELECTED_FILES);
1631 if(text)
1632 gtk_statusbar_push(win->statusbar, win->statusbar_ctx2, text);
1633
1634 text = fm_tab_page_get_status_text(page, FM_STATUS_TEXT_FS_INFO);
1635 if(text)
1636 {
1637 GtkLabel* label = GTK_LABEL(gtk_bin_get_child(GTK_BIN(win->vol_status)));
1638 gtk_label_set_text(label, text);
1639 gtk_widget_show(GTK_WIDGET(win->vol_status));
1640 }
1641 else
1642 gtk_widget_hide(GTK_WIDGET(win->vol_status));
1643 }
1644
1645 gint fm_main_win_add_tab(FmMainWin* win, FmPath* path)
1646 {
1647 FmTabPage* page = fm_tab_page_new(path);
1648 GtkWidget* gpage = GTK_WIDGET(page);
1649 FmTabLabel* label = page->tab_label;
1650 FmFolderView* folder_view = fm_tab_page_get_folder_view(page);
1651 gint ret;
1652
1653 gtk_paned_set_position(GTK_PANED(page), app_config->splitter_pos);
1654
1655 g_signal_connect(folder_view, "key-press-event", G_CALLBACK(on_view_key_press_event), win);
1656
1657 g_signal_connect_swapped(label->close_btn, "clicked", G_CALLBACK(gtk_widget_destroy), page);
1658 g_signal_connect(label, "button-press-event", G_CALLBACK(on_tab_label_button_pressed), page);
1659
1660 /* add the tab */
1661 ret = gtk_notebook_append_page(win->notebook, gpage, GTK_WIDGET(page->tab_label));
1662 gtk_widget_show_all(gpage);
1663 gtk_notebook_set_tab_reorderable(win->notebook, gpage, TRUE);
1664 gtk_notebook_set_current_page(win->notebook, ret);
1665
1666 return ret;
1667 }
1668
1669 static gboolean on_window_state_event(GtkWidget *widget, GdkEventWindowState *evt, FmMainWin *win)
1670 {
1671 if (evt->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
1672 win->fullscreen = ((evt->new_window_state & GDK_WINDOW_STATE_FULLSCREEN) != 0);
1673 if (evt->changed_mask & GDK_WINDOW_STATE_MAXIMIZED)
1674 win->maximized = ((evt->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) != 0);
1675 return FALSE;
1676 }
1677
1678 FmMainWin* fm_main_win_add_win(FmMainWin* win, FmPath* path)
1679 {
1680 GtkAction *act;
1681
1682 win = fm_main_win_new();
1683 gtk_window_set_default_size(GTK_WINDOW(win),
1684 app_config->win_width,
1685 app_config->win_height);
1686 if (app_config->maximized)
1687 gtk_window_maximize(GTK_WINDOW(win));
1688 gtk_widget_show_all(GTK_WIDGET(win));
1689 g_signal_connect(win, "window-state-event", G_CALLBACK(on_window_state_event), win);
1690 /* create new tab */
1691 fm_main_win_add_tab(win, path);
1692 gtk_window_present(GTK_WINDOW(win));
1693 /* set toolbar visibility and menu toggleables from config */
1694 act = gtk_ui_manager_get_action(win->ui, "/menubar/ViewMenu/Toolbar/ShowToolbar");
1695 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), app_config->tb.visible);
1696 act = gtk_ui_manager_get_action(win->ui, "/menubar/ViewMenu/Toolbar/ToolbarNewWin");
1697 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), app_config->tb.new_win);
1698 act = gtk_ui_manager_get_action(win->ui, "/menubar/ViewMenu/Toolbar/ToolbarNewTab");
1699 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), app_config->tb.new_tab);
1700 act = gtk_ui_manager_get_action(win->ui, "/menubar/ViewMenu/Toolbar/ToolbarNav");
1701 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), app_config->tb.nav);
1702 act = gtk_ui_manager_get_action(win->ui, "/menubar/ViewMenu/Toolbar/ToolbarHome");
1703 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), app_config->tb.home);
1704 /* the same for statusbar */
1705 act = gtk_ui_manager_get_action(win->ui, "/menubar/ViewMenu/ShowStatus");
1706 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), app_config->show_statusbar);
1707 gtk_widget_set_visible(GTK_WIDGET(win->statusbar), app_config->show_statusbar);
1708 /* the same for path bar mode */
1709 gtk_widget_hide(GTK_WIDGET(win->path_bar));
1710 act = gtk_ui_manager_get_action(win->ui, "/menubar/ViewMenu/PathMode/PathEntry");
1711 gtk_radio_action_set_current_value(GTK_RADIO_ACTION(act), app_config->pathbar_mode_buttons);
1712 return win;
1713 }
1714
1715 static void on_open(GtkAction* act, FmMainWin* win)
1716 {
1717 FmFileInfoList *files = fm_folder_view_dup_selected_files(win->folder_view);
1718 if(files)
1719 {
1720 GList* l = fm_file_info_list_peek_head_link(files);
1721 if (g_list_length(l) > 0)
1722 fm_launch_files_simple(GTK_WINDOW(win), NULL, l, pcmanfm_open_folder, NULL);
1723 fm_file_info_list_unref(files);
1724 }
1725 }
1726
1727 static void on_link(GtkAction *act, FmMainWin *win)
1728 {
1729 FmPathList *files = fm_folder_view_dup_selected_file_paths(win->folder_view);
1730 if (files)
1731 {
1732 FmPath *dest = fm_select_folder(GTK_WINDOW(win), NULL);
1733 if (dest)
1734 {
1735 fm_link_files(GTK_WINDOW(win), files, dest);
1736 fm_path_unref(dest);
1737 }
1738 fm_path_list_unref(files);
1739 }
1740 }
1741
1742 static void on_copy_to(GtkAction* act, FmMainWin* win)
1743 {
1744 FmPathList* files = fm_folder_view_dup_selected_file_paths(win->folder_view);
1745 if(files)
1746 {
1747 fm_copy_files_to(GTK_WINDOW(win), files);
1748 fm_path_list_unref(files);
1749 }
1750 }
1751
1752 static void on_move_to(GtkAction* act, FmMainWin* win)
1753 {
1754 FmPathList* files = fm_folder_view_dup_selected_file_paths(win->folder_view);
1755 if(files)
1756 {
1757 fm_move_files_to(GTK_WINDOW(win), files);
1758 fm_path_list_unref(files);
1759 }
1760 }
1761
1762 static void on_rename(GtkAction* act, FmMainWin* win)
1763 {
1764 FmPathList* files = fm_folder_view_dup_selected_file_paths(win->folder_view);
1765 if(files)
1766 {
1767 fm_rename_file(GTK_WINDOW(win), fm_path_list_peek_head(files));
1768 /* FIXME: is it ok to only rename the first selected file here? */
1769 fm_path_list_unref(files);
1770 }
1771 }
1772
1773 static void on_trash(GtkAction* act, FmMainWin* win)
1774 {
1775 FmPathList* files = fm_folder_view_dup_selected_file_paths(win->folder_view);
1776 if(files)
1777 {
1778 fm_trash_files(GTK_WINDOW(win), files);
1779 fm_path_list_unref(files);
1780 }
1781 }
1782
1783 static void on_del(GtkAction* act, FmMainWin* win)
1784 {
1785 FmPathList* files = fm_folder_view_dup_selected_file_paths(win->folder_view);
1786 if(files)
1787 {
1788 fm_delete_files(GTK_WINDOW(win), files);
1789 fm_path_list_unref(files);
1790 }
1791 }
1792
1793 static void on_copy_path(GtkAction* action, FmMainWin* win)
1794 {
1795 GdkDisplay *dpy = gtk_widget_get_display(GTK_WIDGET(win));
1796 GtkClipboard *clipboard = gtk_clipboard_get_for_display(dpy, GDK_SELECTION_CLIPBOARD);
1797 GString *str = g_string_sized_new(128);
1798 FmPathList *files = fm_folder_view_dup_selected_file_paths(win->folder_view);
1799 GList *fl;
1800
1801 for (fl = fm_path_list_peek_head_link(files); fl; fl = fl->next)
1802 {
1803 char *path = fm_path_to_str(fl->data);
1804 if (str->len > 0)
1805 g_string_append_c(str, '\n');
1806 g_string_append(str, path);
1807 g_free(path);
1808 }
1809 gtk_clipboard_set_text(clipboard, str->str, str->len);
1810 g_string_free(str, TRUE);
1811 }
1812
1813 static void on_preference(GtkAction* act, FmMainWin* win)
1814 {
1815 fm_edit_preference(GTK_WINDOW(win), 0);
1816 }
1817
1818 static void on_add_bookmark(GtkAction* act, FmMainWin* win)
1819 {
1820 FmPath* cwd = fm_tab_page_get_cwd(win->current_page);
1821 char* disp_path = fm_path_display_name(cwd, TRUE);
1822 char* msg = g_strdup_printf(_("Add following folder to bookmarks:\n\'%s\'\nEnter a name for the new bookmark item:"), disp_path);
1823 char* disp_name = fm_path_display_basename(cwd);
1824 char* name;
1825 g_free(disp_path);
1826 name = fm_get_user_input(GTK_WINDOW(win), _("Add to Bookmarks"), msg, disp_name);
1827 g_free(disp_name);
1828 g_free(msg);
1829 if(name)
1830 {
1831 fm_bookmarks_append(win->bookmarks, cwd, name);
1832 g_free(name);
1833 }
1834 }
1835
1836 static void on_show_toolbar(GtkToggleAction *action, FmMainWin *win)
1837 {
1838 gboolean active = gtk_toggle_action_get_active(action);
1839
1840 app_config->tb.visible = active;
1841 fm_config_emit_changed(fm_config, "toolsbar");
1842 pcmanfm_save_config(FALSE);
1843 }
1844
1845 /* toolbar items: NewWin NewTab Prev (Hist) Next Up Home (Location) Go */
1846 static void on_toolbar_new_win(GtkToggleAction *act, FmMainWin *win)
1847 {
1848 gboolean active = gtk_toggle_action_get_active(act);
1849
1850 app_config->tb.new_win = active;
1851 fm_config_emit_changed(fm_config, "toolsbar");
1852 pcmanfm_save_config(FALSE);
1853 }
1854
1855 static void on_toolbar_new_tab(GtkToggleAction *act, FmMainWin *win)
1856 {
1857 gboolean active = gtk_toggle_action_get_active(act);
1858
1859 app_config->tb.new_tab = active;
1860 fm_config_emit_changed(fm_config, "toolsbar");
1861 pcmanfm_save_config(FALSE);
1862 }
1863
1864 static void on_toolbar_nav(GtkToggleAction *act, FmMainWin *win)
1865 {
1866 gboolean active = gtk_toggle_action_get_active(act);
1867
1868 app_config->tb.nav = active;
1869 fm_config_emit_changed(fm_config, "toolsbar");
1870 pcmanfm_save_config(FALSE);
1871 }
1872
1873 static void on_toolbar_home(GtkToggleAction *act, FmMainWin *win)
1874 {
1875 gboolean active = gtk_toggle_action_get_active(act);
1876
1877 app_config->tb.home = active;
1878 fm_config_emit_changed(fm_config, "toolsbar");
1879 pcmanfm_save_config(FALSE);
1880 }
1881
1882 static void on_location(GtkAction* act, FmMainWin* win)
1883 {
1884 gtk_widget_grab_focus(GTK_WIDGET(win->location));
1885 if (app_config->pathbar_mode_buttons)
1886 {
1887 gtk_widget_show(GTK_WIDGET(win->location));
1888 gtk_widget_show(gtk_ui_manager_get_widget(win->ui, "/toolbar/Go"));
1889 gtk_widget_hide(GTK_WIDGET(win->path_bar));
1890 }
1891 }
1892
1893 static void bounce_action(GtkAction* act, FmMainWin* win)
1894 {
1895 GtkWindow *window = GTK_WINDOW(win);
1896 GtkWidget *current_focus;
1897
1898 g_debug("bouncing action %s to popup", gtk_action_get_name(act));
1899 /* save current focus */
1900 current_focus = gtk_window_get_focus(window);
1901 /* bug #3615003: if folder view does not have the focus, action will not work */
1902 gtk_window_set_focus(window, GTK_WIDGET(win->folder_view));
1903 fm_folder_view_bounce_action(act, win->folder_view);
1904 /* restore focus back */
1905 gtk_window_set_focus(window, current_focus);
1906 }
1907
1908 static guint icon_sizes[] =
1909 {
1910 8,
1911 10,
1912 12,
1913 16,
1914 20,
1915 24,
1916 32, /* 30 would be better */
1917 40, /* 38 */
1918 48,
1919 64, /* 60 */
1920 80, /* 76 */
1921 96,
1922 128, /* 120 */
1923 160, /* 152 */
1924 192,
1925 224,
1926 256
1927 };
1928
1929 static void on_size_decrement(GtkAction *act, FmMainWin *win)
1930 {
1931 FmStandardViewMode mode;
1932 guint size, i;
1933
1934 if (win->folder_view == NULL)
1935 return;
1936 mode = fm_standard_view_get_mode(FM_STANDARD_VIEW(win->folder_view));
1937 switch (mode)
1938 {
1939 case FM_FV_ICON_VIEW:
1940 size = fm_config->big_icon_size;
1941 if (size < 24)
1942 return;
1943 break;
1944 case FM_FV_COMPACT_VIEW:
1945 case FM_FV_LIST_VIEW:
1946 size = fm_config->small_icon_size;
1947 if (size < 10)
1948 return;
1949 break;
1950 case FM_FV_THUMBNAIL_VIEW:
1951 size = fm_config->thumbnail_size;
1952 if (size < 80)
1953 return;
1954 break;
1955 default:
1956 return;
1957 }
1958 for (i = G_N_ELEMENTS(icon_sizes); i > 0; )
1959 {
1960 i--;
1961 if (icon_sizes[i] < size)
1962 break;
1963 }
1964 switch (mode)
1965 {
1966 case FM_FV_ICON_VIEW:
1967 fm_config->big_icon_size = icon_sizes[i];
1968 fm_config_emit_changed(fm_config, "big_icon_size");
1969 break;
1970 case FM_FV_COMPACT_VIEW:
1971 case FM_FV_LIST_VIEW:
1972 fm_config->small_icon_size = icon_sizes[i];
1973 fm_config_emit_changed(fm_config, "small_icon_size");
1974 break;
1975 case FM_FV_THUMBNAIL_VIEW:
1976 fm_config->thumbnail_size = icon_sizes[i];
1977 fm_config_emit_changed(fm_config, "thumbnail_size");
1978 }
1979 pcmanfm_save_config(FALSE);
1980 }
1981
1982 static void on_size_increment(GtkAction *act, FmMainWin *win)
1983 {
1984 FmStandardViewMode mode;
1985 guint size, i;
1986
1987 if (win->folder_view == NULL)
1988 return;
1989 mode = fm_standard_view_get_mode(FM_STANDARD_VIEW(win->folder_view));
1990 switch (mode)
1991 {
1992 case FM_FV_ICON_VIEW:
1993 size = fm_config->big_icon_size;
1994 if (size > 80)
1995 return;
1996 break;
1997 case FM_FV_COMPACT_VIEW:
1998 case FM_FV_LIST_VIEW:
1999 size = fm_config->small_icon_size;
2000 if (size > 40)
2001 return;
2002 break;
2003 case FM_FV_THUMBNAIL_VIEW:
2004 size = fm_config->thumbnail_size;
2005 if (size > 224)
2006 return;
2007 break;
2008 default:
2009 return;
2010 }
2011 for (i = 1; i < G_N_ELEMENTS(icon_sizes); i++)
2012 {
2013 if (icon_sizes[i] > size)
2014 break;
2015 }
2016 switch (mode)
2017 {
2018 case FM_FV_ICON_VIEW:
2019 fm_config->big_icon_size = icon_sizes[i];
2020 fm_config_emit_changed(fm_config, "big_icon_size");
2021 break;
2022 case FM_FV_COMPACT_VIEW:
2023 case FM_FV_LIST_VIEW:
2024 fm_config->small_icon_size = icon_sizes[i];
2025 fm_config_emit_changed(fm_config, "small_icon_size");
2026 break;
2027 case FM_FV_THUMBNAIL_VIEW:
2028 fm_config->thumbnail_size = icon_sizes[i];
2029 fm_config_emit_changed(fm_config, "thumbnail_size");
2030 }
2031 pcmanfm_save_config(FALSE);
2032 }
2033
2034 static void on_size_default(GtkAction *act, FmMainWin *win)
2035 {
2036 FmStandardViewMode mode;
2037
2038 if (win->folder_view == NULL)
2039 return;
2040 mode = fm_standard_view_get_mode(FM_STANDARD_VIEW(win->folder_view));
2041 switch (mode)
2042 {
2043 case FM_FV_ICON_VIEW:
2044 if (fm_config->big_icon_size == 48)
2045 return;
2046 fm_config->big_icon_size = 48;
2047 fm_config_emit_changed(fm_config, "big_icon_size");
2048 break;
2049 case FM_FV_COMPACT_VIEW:
2050 case FM_FV_LIST_VIEW:
2051 if (fm_config->small_icon_size == 20)
2052 return;
2053 fm_config->small_icon_size = 20;
2054 fm_config_emit_changed(fm_config, "small_icon_size");
2055 break;
2056 case FM_FV_THUMBNAIL_VIEW:
2057 if (fm_config->thumbnail_size == 128)
2058 return;
2059 fm_config->thumbnail_size = 128;
2060 fm_config_emit_changed(fm_config, "thumbnail_size");
2061 break;
2062 default:
2063 return;
2064 }
2065 pcmanfm_save_config(FALSE);
2066 }
2067
2068 /* This callback is only connected to folder view of current active tab page. */
2069 static void on_folder_view_clicked(FmFolderView* fv, FmFolderViewClickType type, FmFileInfo* fi, FmMainWin* win)
2070 {
2071 if(fv != win->folder_view)
2072 return;
2073
2074 switch(type)
2075 {
2076 case FM_FV_ACTIVATED: /* file activated */
2077 break; /* handled by FmFolderView */
2078 case FM_FV_CONTEXT_MENU:
2079 break; /* handled by FmFolderView */
2080 case FM_FV_MIDDLE_CLICK:
2081 if(fi && fm_file_info_is_dir(fi))
2082 fm_main_win_add_tab(win, fm_file_info_get_path(fi));
2083 break;
2084 case FM_FV_CLICK_NONE: ;
2085 }
2086 }
2087
2088 /* This callback is only connected to current active tab page. */
2089 static void on_tab_page_status_text(FmTabPage* page, guint type, const char* status_text, FmMainWin* win)
2090 {
2091 if(page != win->current_page)
2092 return;
2093 if (!app_config->show_statusbar)
2094 return;
2095
2096 switch(type)
2097 {
2098 case FM_STATUS_TEXT_NORMAL:
2099 gtk_statusbar_pop(win->statusbar, win->statusbar_ctx);
2100 if(status_text)
2101 gtk_statusbar_push(win->statusbar, win->statusbar_ctx, status_text);
2102 break;
2103 case FM_STATUS_TEXT_SELECTED_FILES:
2104 gtk_statusbar_pop(win->statusbar, win->statusbar_ctx2);
2105 if(status_text)
2106 gtk_statusbar_push(win->statusbar, win->statusbar_ctx2, status_text);
2107 break;
2108 case FM_STATUS_TEXT_FS_INFO:
2109 if(status_text)
2110 {
2111 GtkLabel* label = GTK_LABEL(gtk_bin_get_child(GTK_BIN(win->vol_status)));
2112 gtk_label_set_text(label, status_text);
2113 gtk_widget_show(GTK_WIDGET(win->vol_status));
2114 }
2115 else
2116 gtk_widget_hide(GTK_WIDGET(win->vol_status));
2117 break;
2118 }
2119 }
2120
2121 static void on_tab_page_chdir(FmTabPage* page, FmPath* path, FmMainWin* win)
2122 {
2123 if(page != win->current_page)
2124 return;
2125
2126 fm_path_entry_set_path(win->location, path);
2127 fm_path_bar_set_path(win->path_bar, path);
2128 gtk_window_set_title(GTK_WINDOW(win), fm_tab_page_get_title(page));
2129 }
2130
2131 static void on_tab_page_got_focus(FmTabPage* page, FmMainWin* win)
2132 {
2133 int n = gtk_notebook_page_num(win->notebook, GTK_WIDGET(page));
2134 /* sometimes views receive focus while they are in rendering process
2135 therefore click on tab will not always work as expected because it
2136 is reset in this callback, the trick is if notebook is in update
2137 process then focus change is pending (see on_notebook_switch_page()
2138 for details) therefore we can check that and do nothing */
2139 if (n >= 0 && win->idle_handler == 0) /* don't change page if it is pending */
2140 gtk_notebook_set_current_page(win->notebook, n);
2141 }
2142
2143 #if FM_CHECK_VERSION(1, 0, 2)
2144 static void on_folder_view_filter_changed(FmFolderView* fv, FmMainWin* win)
2145 {
2146 GtkAction* act;
2147 gboolean active;
2148
2149 if (fv != win->folder_view)
2150 return;
2151
2152 active = fm_folder_view_get_show_hidden(fv);
2153
2154 act = gtk_ui_manager_get_action(win->ui, "/menubar/ViewMenu/ShowHidden");
2155 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), active);
2156 if(active != win->current_page->show_hidden)
2157 {
2158 win->current_page->show_hidden = active;
2159 if (win->current_page->own_config)
2160 {
2161 fm_app_config_save_config_for_path(fm_folder_view_get_cwd(fv),
2162 win->current_page->sort_type,
2163 win->current_page->sort_by, -1,
2164 active, NULL);
2165 }
2166 else
2167 {
2168 app_config->show_hidden = active;
2169 pcmanfm_save_config(FALSE);
2170 }
2171 }
2172 }
2173 #endif
2174
2175 static inline FmTabPage *_find_tab_page(FmMainWin *win, FmFolderView *fv)
2176 {
2177 FmTabPage *page;
2178 gint n;
2179
2180 for (n = gtk_notebook_get_n_pages(win->notebook); n > 0; )
2181 {
2182 page = FM_TAB_PAGE(gtk_notebook_get_nth_page(win->notebook, --n));
2183 if (fv == fm_tab_page_get_folder_view(page))
2184 return page;
2185 }
2186 return NULL;
2187 }
2188
2189 static void on_notebook_switch_page(GtkNotebook* nb, gpointer* new_page, guint num, FmMainWin* win)
2190 {
2191 GtkWidget* sw_page = gtk_notebook_get_nth_page(nb, num);
2192 FmTabPage* page;
2193 FmFolderView *old_view = NULL, *passive_view = NULL;
2194
2195 g_return_if_fail(FM_IS_TAB_PAGE(sw_page));
2196 page = (FmTabPage*)sw_page;
2197 /* deactivate gestures from old view first */
2198 if(win->folder_view)
2199 {
2200 fm_folder_view_set_active(win->folder_view, FALSE);
2201 g_object_unref(win->folder_view);
2202 }
2203
2204 /* remember old views for checks below */
2205 if (win->current_page)
2206 {
2207 passive_view = fm_tab_page_get_passive_view(win->current_page);
2208 old_view = fm_tab_page_get_folder_view(win->current_page);
2209 }
2210
2211 /* connect to the new active page */
2212 win->current_page = page;
2213 win->folder_view = fm_tab_page_get_folder_view(page);
2214 if(win->folder_view)
2215 g_object_ref(win->folder_view);
2216 win->nav_history = fm_tab_page_get_history(page);
2217 win->side_pane = fm_tab_page_get_side_pane(page);
2218
2219 /* set active and passive panes */
2220 if (win->enable_passive_view && passive_view)
2221 {
2222 FmTabPage *psv_page;
2223
2224 if (win->folder_view == passive_view)
2225 {
2226 /* we moved to other pane so we have to set passive_view_on_right */
2227 win->passive_view_on_right = win->passive_view_on_right ? FALSE : TRUE;
2228 passive_view = old_view;
2229 }
2230 /* now set it */
2231 fm_tab_page_set_passive_view(page, passive_view, win->passive_view_on_right);
2232 /* FIXME: log errors */
2233 /* ok, passive view was just changed, we have to update the button */
2234 psv_page = _find_tab_page(win, passive_view);
2235 if (psv_page)
2236 gtk_widget_set_state(GTK_WIDGET(psv_page->tab_label), GTK_STATE_SELECTED);
2237 }
2238 else
2239 {
2240 /* FIXME: log errors */
2241 fm_tab_page_take_view_back(page);
2242 }
2243
2244 /* reactivate gestures */
2245 fm_folder_view_set_active(win->folder_view, TRUE);
2246 g_debug("reactivated gestures to page %u", num);
2247 /* update Cut/Copy/Del status */
2248 on_folder_view_sel_changed(win->folder_view,
2249 fm_folder_view_get_n_selected_files(win->folder_view),
2250 win);
2251 _update_hist_buttons(win);
2252 #if FM_CHECK_VERSION(1, 0, 2)
2253 on_folder_view_filter_changed(win->folder_view, win);
2254 #endif
2255
2256 /* update side pane state */
2257 if(app_config->side_pane_mode & FM_SP_HIDE) /* hidden */
2258 {
2259 gtk_widget_hide(GTK_WIDGET(win->side_pane));
2260 }
2261 else
2262 {
2263 fm_side_pane_set_mode(win->side_pane,
2264 (app_config->side_pane_mode & FM_SP_MODE_MASK));
2265 gtk_widget_show_all(GTK_WIDGET(win->side_pane));
2266 }
2267
2268 fm_path_entry_set_path(win->location, fm_tab_page_get_cwd(page));
2269 fm_path_bar_set_path(win->path_bar, fm_tab_page_get_cwd(page));
2270 gtk_window_set_title((GtkWindow*)win, fm_tab_page_get_title(page));
2271
2272 update_sort_menu(win);
2273 update_view_menu(win);
2274 update_file_menu(win, fm_tab_page_get_cwd(page));
2275 update_statusbar(win);
2276
2277 if(win->idle_handler == 0)
2278 win->idle_handler = gdk_threads_add_idle_full(G_PRIORITY_LOW,
2279 idle_focus_view, win, NULL);
2280 }
2281
2282 static void on_notebook_page_added(GtkNotebook* nb, GtkWidget* page, guint num, FmMainWin* win)
2283 {
2284 FmTabPage* tab_page = FM_TAB_PAGE(page);
2285
2286 g_signal_connect(tab_page, "notify::position",
2287 G_CALLBACK(on_tab_page_splitter_pos_changed), win);
2288 g_signal_connect(tab_page, "chdir",
2289 G_CALLBACK(on_tab_page_chdir), win);
2290 g_signal_connect(tab_page, "status",
2291 G_CALLBACK(on_tab_page_status_text), win);
2292 g_signal_connect(tab_page, "got-focus",
2293 G_CALLBACK(on_tab_page_got_focus), win);
2294 /* FIXME: remove direct access */
2295 g_signal_connect(tab_page->folder_view, "sort-changed",
2296 G_CALLBACK(on_folder_view_sort_changed), win);
2297 g_signal_connect(tab_page->folder_view, "sel-changed",
2298 G_CALLBACK(on_folder_view_sel_changed), win);
2299 #if FM_CHECK_VERSION(1, 0, 2)
2300 /* connect to "filter-changed" to get ShowHidden state */
2301 g_signal_connect(tab_page->folder_view, "filter-changed",
2302 G_CALLBACK(on_folder_view_filter_changed), win);
2303 #endif
2304 g_signal_connect(tab_page->folder_view, "clicked",
2305 G_CALLBACK(on_folder_view_clicked), win);
2306 g_signal_connect(tab_page->side_pane, "mode-changed",
2307 G_CALLBACK(on_side_pane_mode_changed), win);
2308 g_signal_connect(tab_page->side_pane, "chdir",
2309 G_CALLBACK(on_side_pane_chdir), win);
2310
2311 /* create folder popup and apply shortcuts from it */
2312 fm_folder_view_add_popup(tab_page->folder_view, GTK_WINDOW(win), NULL);
2313 /* disable it yet - it will be enabled in on_notebook_switch_page() */
2314 fm_folder_view_set_active(tab_page->folder_view, FALSE);
2315
2316 if(gtk_notebook_get_n_pages(nb) > 1
2317 || app_config->always_show_tabs)
2318 gtk_notebook_set_show_tabs(nb, TRUE);
2319 else
2320 gtk_notebook_set_show_tabs(nb, FALSE);
2321 }
2322
2323
2324 static void on_notebook_page_removed(GtkNotebook* nb, GtkWidget* page, guint num, FmMainWin* win)
2325 {
2326 FmTabPage* tab_page = FM_TAB_PAGE(page);
2327 FmFolderView* folder_view = fm_tab_page_get_folder_view(tab_page);
2328 GtkAction* act;
2329
2330 g_debug("FmMainWin[%p]: removed page %u[%p](view=%p); %u pages left", win,
2331 num, page, folder_view, gtk_notebook_get_n_pages(nb));
2332 /* disconnect from previous active page */
2333 g_signal_handlers_disconnect_by_func(tab_page,
2334 on_tab_page_splitter_pos_changed, win);
2335 g_signal_handlers_disconnect_by_func(tab_page,
2336 on_tab_page_chdir, win);
2337 g_signal_handlers_disconnect_by_func(tab_page,
2338 on_tab_page_status_text, win);
2339 g_signal_handlers_disconnect_by_func(tab_page,
2340 on_tab_page_got_focus, win);
2341 if(folder_view)
2342 {
2343 g_signal_handlers_disconnect_by_func(folder_view,
2344 on_view_key_press_event, win);
2345 g_signal_handlers_disconnect_by_func(folder_view,
2346 on_folder_view_sort_changed, win);
2347 g_signal_handlers_disconnect_by_func(folder_view,
2348 on_folder_view_sel_changed, win);
2349 #if FM_CHECK_VERSION(1, 0, 2)
2350 g_signal_handlers_disconnect_by_func(folder_view,
2351 on_folder_view_filter_changed, win);
2352 #endif
2353 g_signal_handlers_disconnect_by_func(folder_view,
2354 on_folder_view_clicked, win);
2355 /* update menu if passive pane was removed */
2356 if (win->current_page && win->enable_passive_view &&
2357 fm_tab_page_get_passive_view(win->current_page) == folder_view)
2358 {
2359 win->enable_passive_view = FALSE;
2360 act = gtk_ui_manager_get_action(win->ui, "/menubar/ViewMenu/DualPane");
2361 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), FALSE);
2362 }
2363 }
2364 g_signal_handlers_disconnect_by_func(tab_page->side_pane,
2365 on_side_pane_mode_changed, win);
2366 g_signal_handlers_disconnect_by_func(tab_page->side_pane,
2367 on_side_pane_chdir, win);
2368
2369 if(tab_page == win->current_page)
2370 {
2371 win->current_page = NULL;
2372 if (win->folder_view)
2373 g_object_unref(win->folder_view);
2374 win->folder_view = NULL;
2375 win->nav_history = NULL;
2376 win->side_pane = NULL;
2377 }
2378
2379 if(gtk_notebook_get_n_pages(nb) > 1 || app_config->always_show_tabs)
2380 gtk_notebook_set_show_tabs(nb, TRUE);
2381 else
2382 gtk_notebook_set_show_tabs(nb, FALSE);
2383
2384 /* all notebook pages are removed, let's destroy the main window */
2385 if(gtk_notebook_get_n_pages(nb) == 0)
2386 gtk_widget_destroy(GTK_WIDGET(win));
2387 }
2388
2389 FmMainWin* fm_main_win_get_last_active(void)
2390 {
2391 return all_wins ? (FmMainWin*)all_wins->data : NULL;
2392 }
2393
2394 void fm_main_win_open_in_last_active(FmPath* path)
2395 {
2396 FmMainWin* win = fm_main_win_get_last_active();
2397 if(!win)
2398 win = fm_main_win_add_win(NULL, path);
2399 else
2400 fm_main_win_add_tab(win, path);
2401 gtk_window_present(GTK_WINDOW(win));
2402 }
2403
2404 static void switch_to_next_tab(FmMainWin* win)
2405 {
2406 int n = gtk_notebook_get_current_page(win->notebook);
2407 if(n < gtk_notebook_get_n_pages(win->notebook) - 1)
2408 ++n;
2409 else
2410 n = 0;
2411 gtk_notebook_set_current_page(win->notebook, n);
2412 }
2413
2414 static void switch_to_prev_tab(FmMainWin* win)
2415 {
2416 int n = gtk_notebook_get_current_page(win->notebook);
2417 if(n > 0)
2418 --n;
2419 else
2420 n = gtk_notebook_get_n_pages(win->notebook) - 1;
2421 gtk_notebook_set_current_page(win->notebook, n);
2422 }
2423
2424 static gboolean on_key_press_event(GtkWidget* w, GdkEventKey* evt)
2425 {
2426 FmMainWin* win = FM_MAIN_WIN(w);
2427 int modifier = evt->state & gtk_accelerator_get_default_mod_mask();
2428
2429 if(modifier == GDK_MOD1_MASK) /* Alt */
2430 {
2431 if(isdigit(evt->keyval)) /* Alt + 0 ~ 9, nth tab */
2432 {
2433 int n;
2434 if(evt->keyval == '0')
2435 n = 9;
2436 else
2437 n = evt->keyval - '1';
2438 gtk_notebook_set_current_page(win->notebook, n);
2439 return TRUE;
2440 }
2441 }
2442 else if(modifier == GDK_CONTROL_MASK) /* Ctrl */
2443 {
2444 if(evt->keyval == GDK_KEY_Tab
2445 || evt->keyval == GDK_KEY_ISO_Left_Tab
2446 || evt->keyval == GDK_KEY_Page_Down) /* Ctrl + Tab or PageDown, next tab */
2447 {
2448 switch_to_next_tab(win);
2449 return TRUE;
2450 }
2451 else if(evt->keyval == GDK_KEY_Page_Up)
2452 {
2453 switch_to_prev_tab(win);
2454 return TRUE;
2455 }
2456 }
2457 else if(modifier == (GDK_CONTROL_MASK|GDK_SHIFT_MASK)) /* Ctrl + Shift */
2458 {
2459 if(evt->keyval == GDK_KEY_Tab
2460 || evt->keyval == GDK_KEY_ISO_Left_Tab) /* Ctrl + Shift + Tab or PageUp, previous tab */
2461 {
2462 switch_to_prev_tab(win);
2463 return TRUE;
2464 }
2465 }
2466 else if(evt->keyval == '/' || evt->keyval == '~')
2467 {
2468 if (!gtk_widget_is_focus(GTK_WIDGET(win->location)))
2469 {
2470 gtk_widget_grab_focus(GTK_WIDGET(win->location));
2471 char path[] = {evt->keyval, 0};
2472 gtk_entry_set_text(GTK_ENTRY(win->location), path);
2473 gtk_editable_set_position(GTK_EDITABLE(win->location), -1);
2474 return TRUE;
2475 }
2476 }
2477 else if(evt->keyval == GDK_KEY_Escape)
2478 {
2479 if (gtk_widget_is_focus(GTK_WIDGET(win->location)))
2480 {
2481 gtk_widget_grab_focus(GTK_WIDGET(win->folder_view));
2482 fm_path_entry_set_path(win->location,
2483 fm_tab_page_get_cwd(win->current_page));
2484 fm_path_bar_set_path(win->path_bar,
2485 fm_tab_page_get_cwd(win->current_page));
2486 return TRUE;
2487 }
2488 }
2489 return GTK_WIDGET_CLASS(fm_main_win_parent_class)->key_press_event(w, evt);
2490 }
2491
2492 static gboolean on_button_press_event(GtkWidget* w, GdkEventButton* evt)
2493 {
2494 FmMainWin* win = FM_MAIN_WIN(w);
2495 GtkAction* act;
2496
2497 if(evt->button == 8) /* back */
2498 {
2499 act = gtk_ui_manager_get_action(win->ui, "/Prev2");
2500 gtk_action_activate(act);
2501 }
2502 else if(evt->button == 9) /* forward */
2503 {
2504 act = gtk_ui_manager_get_action(win->ui, "/Next2");
2505 gtk_action_activate(act);
2506 }
2507 if(GTK_WIDGET_CLASS(fm_main_win_parent_class)->button_press_event)
2508 return GTK_WIDGET_CLASS(fm_main_win_parent_class)->button_press_event(w, evt);
2509 else
2510 return FALSE;
2511 }
2512
2513 static gboolean on_scroll_event(GtkWidget* w, GdkEventScroll* evt)
2514 {
2515 FmMainWin* win = FM_MAIN_WIN(w);
2516 GtkAction* act;
2517 int modifier = evt->state & gtk_accelerator_get_default_mod_mask();
2518
2519 /* g_debug("on_scroll_event:%d", evt->direction); */
2520 if (modifier != GDK_CONTROL_MASK) ;
2521 else if(evt->direction == GDK_SCROLL_UP)
2522 {
2523 act = gtk_ui_manager_get_action(win->ui, "/menubar/ViewMenu/SizeBigger");
2524 gtk_action_activate(act);
2525 return TRUE;
2526 }
2527 else if(evt->direction == GDK_SCROLL_DOWN)
2528 {
2529 act = gtk_ui_manager_get_action(win->ui, "/menubar/ViewMenu/SizeSmaller");
2530 gtk_action_activate(act);
2531 return TRUE;
2532 }
2533 if(GTK_WIDGET_CLASS(fm_main_win_parent_class)->scroll_event)
2534 return GTK_WIDGET_CLASS(fm_main_win_parent_class)->scroll_event(w, evt);
2535 else
2536 return FALSE;
2537 }
2538
2539 static void on_reload(GtkAction* act, FmMainWin* win)
2540 {
2541 FmTabPage* page = win->current_page;
2542 fm_tab_page_reload(page);
2543 }
2544
2545 #if FM_CHECK_VERSION(1, 0, 2)
2546 static void on_filter(GtkAction* act, FmMainWin* win)
2547 {
2548 FmTabPage *page = win->current_page;
2549 const char *old_filter = page->filter_pattern ? page->filter_pattern : "*";
2550 char *new_filter;
2551
2552 new_filter = fm_get_user_input(GTK_WINDOW(win), _("Select filter"),
2553 _("Choose a new shell pattern to show files:"),
2554 old_filter);
2555 if (!new_filter) /* cancelled */
2556 return;
2557 if (strcmp(new_filter, "*"))
2558 fm_tab_page_set_filter_pattern(page, new_filter);
2559 else
2560 fm_tab_page_set_filter_pattern(page, NULL);
2561 g_free(new_filter);
2562 gtk_window_set_title(GTK_WINDOW(win), fm_tab_page_get_title(page));
2563 }
2564 #endif
2565
2566 static void on_show_side_pane(GtkToggleAction* act, FmMainWin* win)
2567 {
2568 gboolean active;
2569
2570 active = gtk_toggle_action_get_active(act);
2571 if(active)
2572 {
2573 app_config->side_pane_mode &= ~FM_SP_HIDE;
2574 gtk_widget_show_all(GTK_WIDGET(win->side_pane));
2575 }
2576 else
2577 {
2578 app_config->side_pane_mode |= FM_SP_HIDE;
2579 gtk_widget_hide(GTK_WIDGET(win->side_pane));
2580 }
2581 /* FIXME: propagate the event to other windows? */
2582 pcmanfm_save_config(FALSE);
2583 }
2584
2585 static void on_dual_pane(GtkToggleAction* act, FmMainWin* win)
2586 {
2587 gboolean active;
2588 int num;
2589 FmFolderView *fv;
2590 GtkWidget *page;
2591
2592 active = gtk_toggle_action_get_active(act);
2593 /* g_debug("on_dual_pane: %d", active); */
2594 if (active && !win->enable_passive_view)
2595 {
2596 num = gtk_notebook_get_n_pages(win->notebook);
2597 if (num < 2) /* single page yet */
2598 {
2599 win->passive_view_on_right = FALSE;
2600 num = fm_main_win_add_tab(win, fm_tab_page_get_cwd(win->current_page));
2601 g_debug("on_dual_pane: added duplicate of the single page");
2602 page = gtk_notebook_get_nth_page(win->notebook, 0);
2603 }
2604 else if (gtk_notebook_get_current_page(win->notebook) < num - 1)
2605 {
2606 /* there is a page on right */
2607 win->passive_view_on_right = TRUE;
2608 num = gtk_notebook_get_current_page(win->notebook) + 1;
2609 g_debug("on_dual_pane: adding passive page %d to right pane", num);
2610 page = gtk_notebook_get_nth_page(win->notebook, num);
2611 }
2612 else
2613 {
2614 /* it is the most right page */
2615 win->passive_view_on_right = FALSE;
2616 g_debug("on_dual_pane: adding passive page %d to left pane", num - 2);
2617 page = gtk_notebook_get_nth_page(win->notebook, num - 2);
2618 }
2619 fv = fm_tab_page_get_folder_view(FM_TAB_PAGE(page));
2620 fm_tab_page_set_passive_view(win->current_page, fv,
2621 win->passive_view_on_right);
2622 /* ok, passive view was just changed, we have to update the button */
2623 gtk_widget_set_state(GTK_WIDGET(FM_TAB_PAGE(page)->tab_label), GTK_STATE_SELECTED);
2624 win->enable_passive_view = TRUE;
2625 }
2626 else if (!active && win->enable_passive_view)
2627 {
2628 /* take passive pane back to the tab page */
2629 fv = fm_tab_page_get_passive_view(win->current_page);
2630 if (fv)
2631 {
2632 FmTabPage *tp = _find_tab_page(win, fv);
2633 if (tp)
2634 {
2635 fm_tab_page_take_view_back(tp);
2636 gtk_widget_set_state(GTK_WIDGET(FM_TAB_PAGE(tp)->tab_label), GTK_STATE_ACTIVE);
2637 }
2638 }
2639 win->enable_passive_view = FALSE;
2640 }
2641 }
2642
2643 static void on_show_status(GtkToggleAction *action, FmMainWin *win)
2644 {
2645 gboolean active;
2646
2647 active = gtk_toggle_action_get_active(action);
2648 if (active != app_config->show_statusbar)
2649 {
2650 app_config->show_statusbar = active;
2651 fm_config_emit_changed(fm_config, "statusbar");
2652 pcmanfm_save_config(FALSE);
2653 }
2654 }