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