* Use new fm_launch_files_simple API.
[lxde/pcmanfm.git] / src / main-win.c
CommitLineData
b6e3c554
HJYP
1/*
2 * main-win.c
3 *
4 * Copyright 2009 PCMan <pcman.tw@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 * MA 02110-1301, USA.
20 */
21
08e70fea
HJYP
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25
b6e3c554
HJYP
26#include <glib/gi18n.h>
27
f8f2bfad
HJYP
28#include "pcmanfm.h"
29
19fbd668 30#include "app-config.h"
b6e3c554 31#include "main-win.h"
f2a36fcd 32#include "pref.h"
b6e3c554
HJYP
33
34static void fm_main_win_finalize (GObject *object);
35G_DEFINE_TYPE(FmMainWin, fm_main_win, GTK_TYPE_WINDOW);
36
37static GtkWidget* create_tab_label(FmMainWin* win, FmPath* path, FmFolderView* view);
38static void update_tab_label(FmMainWin* win, FmFolderView* fv, const char* title);
f0ce8c37 39static void update_volume_info(FmMainWin* win);
b6e3c554 40
0589ff84
HJYP
41static void on_focus_in(GtkWidget* w, GdkEventFocus* evt);
42
b6e3c554
HJYP
43static void on_new_win(GtkAction* act, FmMainWin* win);
44static void on_new_tab(GtkAction* act, FmMainWin* win);
19fbd668 45static void on_close_tab(GtkAction* act, FmMainWin* win);
b6e3c554
HJYP
46static void on_close_win(GtkAction* act, FmMainWin* win);
47
48static void on_open_in_new_tab(GtkAction* act, FmMainWin* win);
49static void on_open_in_new_win(GtkAction* act, FmMainWin* win);
50
51static void on_cut(GtkAction* act, FmMainWin* win);
52static void on_copy(GtkAction* act, FmMainWin* win);
53static void on_copy_to(GtkAction* act, FmMainWin* win);
54static void on_move_to(GtkAction* act, FmMainWin* win);
55static void on_paste(GtkAction* act, FmMainWin* win);
56static void on_del(GtkAction* act, FmMainWin* win);
57static void on_rename(GtkAction* act, FmMainWin* win);
58
59static void on_select_all(GtkAction* act, FmMainWin* win);
60static void on_invert_select(GtkAction* act, FmMainWin* win);
f2a36fcd 61static void on_preference(GtkAction* act, FmMainWin* win);
b6e3c554
HJYP
62
63static void on_go(GtkAction* act, FmMainWin* win);
64static void on_go_back(GtkAction* act, FmMainWin* win);
65static void on_go_forward(GtkAction* act, FmMainWin* win);
66static void on_go_up(GtkAction* act, FmMainWin* win);
67static void on_go_home(GtkAction* act, FmMainWin* win);
68static void on_go_desktop(GtkAction* act, FmMainWin* win);
69static void on_go_trash(GtkAction* act, FmMainWin* win);
70static void on_go_computer(GtkAction* act, FmMainWin* win);
71static void on_go_network(GtkAction* act, FmMainWin* win);
72static void on_go_apps(GtkAction* act, FmMainWin* win);
73static void on_show_hidden(GtkToggleAction* act, FmMainWin* win);
74static void on_change_mode(GtkRadioAction* act, GtkRadioAction *cur, FmMainWin* win);
75static void on_sort_by(GtkRadioAction* act, GtkRadioAction *cur, FmMainWin* win);
76static void on_sort_type(GtkRadioAction* act, GtkRadioAction *cur, FmMainWin* win);
77static void on_about(GtkAction* act, FmMainWin* win);
78
79static void on_location(GtkAction* act, FmMainWin* win);
80
81static void on_create_new(GtkAction* action, FmMainWin* win);
82static void on_prop(GtkAction* action, FmMainWin* win);
83
84static void on_switch_page(GtkNotebook* nb, GtkNotebookPage* page, guint num, FmMainWin* win);
09710de4 85static void on_page_removed(GtkNotebook* nb, GtkWidget* page, guint num, FmMainWin* win);
b6e3c554
HJYP
86
87#include "main-win-ui.c" /* ui xml definitions and actions */
88
f0ce8c37 89static GQuark nav_history_id = 0;
0589ff84 90static GSList* all_wins = NULL;
b6e3c554
HJYP
91
92static void fm_main_win_class_init(FmMainWinClass *klass)
93{
94 GObjectClass *g_object_class;
0589ff84 95 GtkWidgetClass* widget_class;
b6e3c554
HJYP
96 g_object_class = G_OBJECT_CLASS(klass);
97 g_object_class->finalize = fm_main_win_finalize;
0589ff84
HJYP
98
99 widget_class = (GtkWidgetClass*)klass;
100 widget_class->focus_in_event = on_focus_in;
101
b6e3c554
HJYP
102 fm_main_win_parent_class = (GtkWindowClass*)g_type_class_peek(GTK_TYPE_WINDOW);
103
104 /* special style used by close button */
105 gtk_rc_parse_string(
106 "style \"close-btn-style\" {\n"
107 "GtkWidget::focus-padding = 0\n"
108 "GtkWidget::focus-line-width = 0\n"
109 "xthickness = 0\n"
110 "ythickness = 0\n"
111 "}\n"
112 "widget \"*.close-btn\" style \"close-btn-style\""
113 );
f0ce8c37
HJYP
114
115 nav_history_id = g_quark_from_static_string("nav-history");
b6e3c554
HJYP
116}
117
118static void on_entry_activate(GtkEntry* entry, FmMainWin* self)
119{
120 fm_folder_view_chdir_by_name(self->folder_view, gtk_entry_get_text(entry));
121}
122
123static void on_view_loaded( FmFolderView* view, FmPath* path, gpointer user_data)
124{
125 FmMainWin* win = FM_MAIN_WIN(user_data);
f0ce8c37
HJYP
126 FmIcon* icon;
127 /* FIXME: we shouldn't access private data member directly. */
19abb8bf 128 fm_path_entry_set_model( win->location, path, view->model );
566c23e0 129 if(FM_FOLDER_MODEL(view->model)->dir->dir_fi)
f0ce8c37 130 {
566c23e0
HJYP
131 icon = FM_FOLDER_MODEL(view->model)->dir->dir_fi->icon;
132 if(icon)
133 {
134 icon->gicon;
135 /* FIXME: load icon. we need to change window icon when switching pages. */
136 gtk_window_set_icon_name(win, "folder");
137 }
f0ce8c37
HJYP
138 }
139 update_volume_info(win);
b6e3c554
HJYP
140}
141
df6826e0 142static gboolean open_folder_func(GAppLaunchContext* ctx, GList* folder_infos, gpointer user_data, GError** err)
b6e3c554
HJYP
143{
144 FmMainWin* win = FM_MAIN_WIN(user_data);
df6826e0
HJYP
145 GList* l = folder_infos;
146 FmFileInfo* fi = (FmFileInfo*)l->data;
b6e3c554 147 fm_main_win_chdir(win, fi->path);
df6826e0
HJYP
148 l=l->next;
149 for(; l; l=l->next)
150 {
151 FmFileInfo* fi = (FmFileInfo*)l->data;
152 fm_main_win_add_tab(win, fi->path);
153 }
154 return TRUE;
b6e3c554
HJYP
155}
156
157static void on_file_clicked(FmFolderView* fv, FmFolderViewClickType type, FmFileInfo* fi, FmMainWin* win)
158{
159 char* fpath, *uri;
160 GAppLaunchContext* ctx;
161 switch(type)
162 {
163 case FM_FV_ACTIVATED: /* file activated */
164 if(fm_file_info_is_dir(fi))
165 {
166 fm_main_win_chdir( win, fi->path);
167 }
f77ae5d1 168 else if(!fm_file_info_is_symlink(fi) && fi->target) /* FIXME: use accessor functions. */
b6e3c554 169 {
f77ae5d1 170 /* symlinks also has fi->target, but we only handle shortcuts here. */
b6e3c554
HJYP
171 fm_main_win_chdir_by_name( win, fi->target);
172 }
173 else
174 {
df6826e0 175 fm_launch_file_simple(win, NULL, fi, open_folder_func, win);
b6e3c554
HJYP
176 }
177 break;
178 case FM_FV_CONTEXT_MENU:
179 if(fi)
180 {
181 FmFileMenu* menu;
182 GtkMenu* popup;
183 FmFileInfoList* files = fm_folder_view_get_selected_files(fv);
184 menu = fm_file_menu_new_for_files(files, TRUE);
df6826e0 185 fm_file_menu_set_folder_func(menu, open_folder_func, win);
b6e3c554
HJYP
186 fm_list_unref(files);
187
188 /* merge some specific menu items for folders */
189 if(fm_file_menu_is_single_file_type(menu) && fm_file_info_is_dir(fi))
190 {
191 GtkUIManager* ui = fm_file_menu_get_ui(menu);
192 GtkActionGroup* act_grp = fm_file_menu_get_action_group(menu);
08e70fea 193 gtk_action_group_set_translation_domain(act_grp, NULL);
b6e3c554
HJYP
194 gtk_action_group_add_actions(act_grp, folder_menu_actions, G_N_ELEMENTS(folder_menu_actions), win);
195 gtk_ui_manager_add_ui_from_string(ui, folder_menu_xml, -1, NULL);
196 }
197
198 popup = fm_file_menu_get_menu(menu);
199 gtk_menu_popup(popup, NULL, NULL, NULL, fi, 3, gtk_get_current_event_time());
200 }
201 else /* no files are selected. Show context menu of current folder. */
202 {
203 gtk_menu_popup(win->popup, NULL, NULL, NULL, NULL, 3, gtk_get_current_event_time());
204 }
205 break;
206 case FM_FV_MIDDLE_CLICK:
207 g_debug("middle click!");
208 break;
209 }
210}
211
212static void on_sel_changed(FmFolderView* fv, FmFileInfoList* files, FmMainWin* win)
213{
214 /* popup previous message if there is any */
215 gtk_statusbar_pop(win->statusbar, win->statusbar_ctx2);
216 if(files)
217 {
218 char* msg;
219 /* FIXME: display total size of all selected files. */
220 if(fm_list_get_length(files) == 1) /* only one file is selected */
221 {
222 FmFileInfo* fi = fm_list_peek_head(files);
223 const char* size_str = fm_file_info_get_disp_size(fi);
224 if(size_str)
225 {
226 msg = g_strdup_printf("\"%s\" (%s) %s",
227 fm_file_info_get_disp_name(fi),
228 size_str ? size_str : "",
229 fm_file_info_get_desc(fi));
230 }
231 else
232 {
233 msg = g_strdup_printf("\"%s\" %s",
234 fm_file_info_get_disp_name(fi),
235 fm_file_info_get_desc(fi));
236 }
237 }
238 else
239 msg = g_strdup_printf("%d items selected", fm_list_get_length(files));
240 gtk_statusbar_push(win->statusbar, win->statusbar_ctx2, msg);
241 g_free(msg);
242 }
243}
244
245static void on_status(FmFolderView* fv, const char* msg, FmMainWin* win)
246{
247 gtk_statusbar_pop(win->statusbar, win->statusbar_ctx);
248 gtk_statusbar_push(win->statusbar, win->statusbar_ctx, msg);
249}
250
251static void on_bookmark(GtkMenuItem* mi, FmMainWin* win)
252{
253 FmPath* path = (FmPath*)g_object_get_data(mi, "path");
19fbd668
HJYP
254 switch(FM_APP_CONFIG(fm_config)->bm_open_method)
255 {
256 case 0: /* current tab */
257 fm_main_win_chdir(win, path);
258 break;
259 case 1: /* new tab */
260 fm_main_win_add_tab(win, path);
261 break;
262 case 2: /* new window */
263 fm_main_win_add_win(win, path);
264 break;
265 }
b6e3c554
HJYP
266}
267
268static void create_bookmarks_menu(FmMainWin* win)
269{
270 GList* l;
271 int i = 0;
272 /* FIXME: direct access to data member is not allowed */
273 for(l=win->bookmarks->items;l;l=l->next)
274 {
275 FmBookmarkItem* item = (FmBookmarkItem*)l->data;
276 GtkWidget* mi = gtk_image_menu_item_new_with_label(item->name);
277 gtk_widget_show(mi);
278 // gtk_image_menu_item_set_image(); // FIXME: set icons for menu items
279 g_object_set_data_full(mi, "path", fm_path_ref(item->path), (GDestroyNotify)fm_path_unref);
280 g_signal_connect(mi, "activate", G_CALLBACK(on_bookmark), win);
281 gtk_menu_shell_insert(win->bookmarks_menu, mi, i);
282 ++i;
283 }
284 if(i > 0)
285 gtk_menu_shell_insert(win->bookmarks_menu, gtk_separator_menu_item_new(), i);
286}
287
288static void on_bookmarks_changed(FmBookmarks* bm, FmMainWin* win)
289{
290 /* delete old items first. */
291 GList* mis = gtk_container_get_children(win->bookmarks_menu);
292 GList* l;
293 for(l = mis;l;l=l->next)
294 {
295 GtkWidget* item = (GtkWidget*)l->data;
296 if( GTK_IS_SEPARATOR_MENU_ITEM(item) )
297 break;
298 gtk_widget_destroy(item);
299 }
300 g_list_free(mis);
301
302 create_bookmarks_menu(win);
303}
304
305static void load_bookmarks(FmMainWin* win, GtkUIManager* ui)
306{
307 GtkWidget* mi = gtk_ui_manager_get_widget(ui, "/menubar/BookmarksMenu");
308 win->bookmarks_menu = gtk_menu_item_get_submenu(mi);
309 win->bookmarks = fm_bookmarks_get();
310 g_signal_connect(win->bookmarks, "changed", G_CALLBACK(on_bookmarks_changed), win);
311
312 create_bookmarks_menu(win);
313}
314
315static void on_history_item(GtkMenuItem* mi, FmMainWin* win)
316{
317 GList* l = g_object_get_data(mi, "path");
318 FmPath* path = (FmPath*)l->data;
319 fm_nav_history_jump(win->nav_history, l);
320 /* FIXME: should this be driven by signal emitted on FmNavHistory? */
321 fm_main_win_chdir_without_history(win, path);
322}
323
324static void on_show_history_menu(GtkMenuToolButton* btn, FmMainWin* win)
325{
326 GtkMenuShell* menu = (GtkMenuShell*)gtk_menu_tool_button_get_menu(btn);
327 GList *l;
328 FmPathList* nh = fm_nav_history_list(win->nav_history);
329 GList* cur = fm_nav_history_get_cur_link(win->nav_history);
330
331 /* delete old items */
332 gtk_container_foreach(menu, (GtkCallback)gtk_widget_destroy, NULL);
333
334 for(l = fm_list_peek_head_link(nh); l; l=l->next)
335 {
336 FmPath* path = (FmPath*)l->data;
90d8f009 337 char* str = fm_path_display_name(path, TRUE);
b6e3c554
HJYP
338 GtkMenuItem* mi;
339 if( l == cur )
340 {
341 mi = gtk_check_menu_item_new_with_label(str);
342 gtk_check_menu_item_set_draw_as_radio(mi, TRUE);
343 gtk_check_menu_item_set_active(mi, TRUE);
344 }
345 else
346 mi = gtk_menu_item_new_with_label(str);
347
348 g_object_set_data_full(mi, "path", l, NULL);
349 g_signal_connect(mi, "activate", G_CALLBACK(on_history_item), win);
350 gtk_menu_shell_append(menu, mi);
351 }
352 fm_list_unref(nh);
353
354 gtk_widget_show_all( GTK_WIDGET(menu) );
355}
356
357static void fm_main_win_init(FmMainWin *self)
358{
359 GtkWidget *vbox, *menubar, *toolitem, *next_btn, *scroll;
360 GtkUIManager* ui;
361 GtkActionGroup* act_grp;
362 GtkAction* act;
363 GtkAccelGroup* accel_grp;
f0ce8c37 364 GtkShadowType shadow_type;
b6e3c554 365
f8f2bfad 366 pcmanfm_ref();
0589ff84 367 all_wins = g_slist_prepend(all_wins, self);
b6e3c554
HJYP
368
369 vbox = gtk_vbox_new(FALSE, 0);
370
371 self->hpaned = gtk_hpaned_new();
372 gtk_paned_set_position(self->hpaned, 150);
373
374 /* places left pane */
375 self->places_view = fm_places_view_new();
376 scroll = gtk_scrolled_window_new(NULL, NULL);
377 gtk_scrolled_window_set_policy(scroll, GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
378 gtk_container_add(scroll, self->places_view);
379 gtk_paned_add1(self->hpaned, scroll);
380 g_signal_connect_swapped(self->places_view, "chdir", G_CALLBACK(fm_main_win_chdir), self);
381
382 /* notebook right pane */
383 self->notebook = gtk_notebook_new();
384 gtk_notebook_set_scrollable(self->notebook, TRUE);
385 g_signal_connect(self->notebook, "switch-page", G_CALLBACK(on_switch_page), self);
09710de4 386 g_signal_connect(self->notebook, "page-removed", G_CALLBACK(on_page_removed), self);
b6e3c554
HJYP
387 gtk_paned_add2(self->hpaned, self->notebook);
388
389 /* create menu bar and toolbar */
390 ui = gtk_ui_manager_new();
391 act_grp = gtk_action_group_new("Main");
08e70fea 392 gtk_action_group_set_translation_domain(act_grp, NULL);
b6e3c554
HJYP
393 gtk_action_group_add_actions(act_grp, main_win_actions, G_N_ELEMENTS(main_win_actions), self);
394 gtk_action_group_add_toggle_actions(act_grp, main_win_toggle_actions, G_N_ELEMENTS(main_win_toggle_actions), self);
395 gtk_action_group_add_radio_actions(act_grp, main_win_mode_actions, G_N_ELEMENTS(main_win_mode_actions), FM_FV_ICON_VIEW, on_change_mode, self);
396 gtk_action_group_add_radio_actions(act_grp, main_win_sort_type_actions, G_N_ELEMENTS(main_win_sort_type_actions), GTK_SORT_ASCENDING, on_sort_type, self);
397 gtk_action_group_add_radio_actions(act_grp, main_win_sort_by_actions, G_N_ELEMENTS(main_win_sort_by_actions), 0, on_sort_by, self);
398
399 accel_grp = gtk_ui_manager_get_accel_group(ui);
400 gtk_window_add_accel_group(self, accel_grp);
401
402 gtk_ui_manager_insert_action_group(ui, act_grp, 0);
403 gtk_ui_manager_add_ui_from_string(ui, main_menu_xml, -1, NULL);
404
405 menubar = gtk_ui_manager_get_widget(ui, "/menubar");
b6e3c554
HJYP
406 self->toolbar = gtk_ui_manager_get_widget(ui, "/toolbar");
407 /* FIXME: should make these optional */
408 gtk_toolbar_set_icon_size(self->toolbar, GTK_ICON_SIZE_SMALL_TOOLBAR);
409 gtk_toolbar_set_style(self->toolbar, GTK_TOOLBAR_ICONS);
410
411 /* create 'Next' button manually and add a popup menu to it */
412 toolitem = g_object_new(GTK_TYPE_MENU_TOOL_BUTTON, NULL);
413 gtk_toolbar_insert(GTK_TOOLBAR(self->toolbar), toolitem, 2);
414 gtk_widget_show(GTK_WIDGET(toolitem));
415 act = gtk_ui_manager_get_action(ui, "/menubar/GoMenu/Next");
416 gtk_activatable_set_related_action(toolitem, act);
417
418 /* set up history menu */
b6e3c554
HJYP
419 self->history_menu = gtk_menu_new();
420 gtk_menu_tool_button_set_menu(toolitem, self->history_menu);
421 g_signal_connect(toolitem, "show-menu", G_CALLBACK(on_show_history_menu), self);
422
423 self->popup = gtk_ui_manager_get_widget(ui, "/popup");
424
425 gtk_box_pack_start( (GtkBox*)vbox, menubar, FALSE, TRUE, 0 );
426 gtk_box_pack_start( (GtkBox*)vbox, self->toolbar, FALSE, TRUE, 0 );
427
428 /* load bookmarks menu */
429 load_bookmarks(self, ui);
430
431 /* the location bar */
432 self->location = fm_path_entry_new();
433 g_signal_connect(self->location, "activate", on_entry_activate, self);
434
435 toolitem = gtk_tool_item_new();
436 gtk_container_add( toolitem, self->location );
437 gtk_tool_item_set_expand(toolitem, TRUE);
438 gtk_toolbar_insert((GtkToolbar*)self->toolbar, toolitem, gtk_toolbar_get_n_items(self->toolbar) - 1 );
439
440 gtk_box_pack_start( (GtkBox*)vbox, self->hpaned, TRUE, TRUE, 0 );
441
442 /* status bar */
443 self->statusbar = gtk_statusbar_new();
f0ce8c37
HJYP
444 /* status bar column showing volume free space */
445 gtk_widget_style_get(self->statusbar, "shadow-type", &shadow_type, NULL);
446 self->vol_status = gtk_frame_new(NULL);
447 gtk_frame_set_shadow_type(self->vol_status, shadow_type);
448 gtk_box_pack_start(self->statusbar, self->vol_status, FALSE, TRUE, 0);
449 gtk_container_add(self->vol_status, gtk_label_new(NULL));
450
b6e3c554
HJYP
451 gtk_box_pack_start( (GtkBox*)vbox, self->statusbar, FALSE, TRUE, 0 );
452 self->statusbar_ctx = gtk_statusbar_get_context_id(self->statusbar, "status");
453 self->statusbar_ctx2 = gtk_statusbar_get_context_id(self->statusbar, "status2");
454
455 g_object_unref(act_grp);
456 self->ui = ui;
457
458 gtk_container_add( (GtkContainer*)self, vbox );
459 gtk_widget_show_all(vbox);
460
461 /* create new tab */
462 fm_main_win_add_tab(self, fm_path_get_home());
463 gtk_widget_grab_focus(self->folder_view);
464}
465
466
467GtkWidget* fm_main_win_new(void)
468{
469 return (GtkWidget*)g_object_new(FM_MAIN_WIN_TYPE, NULL);
470}
471
472
473static void fm_main_win_finalize(GObject *object)
474{
475 FmMainWin *self;
476
477 g_return_if_fail(object != NULL);
478 g_return_if_fail(IS_FM_MAIN_WIN(object));
479
b6e3c554
HJYP
480 self = FM_MAIN_WIN(object);
481
f0ce8c37
HJYP
482 if(self->vol_status_cancellable)
483 g_object_unref(self->vol_status_cancellable);
484
b6e3c554
HJYP
485 g_object_unref(self->ui);
486 g_object_unref(self->bookmarks);
487
488 if (G_OBJECT_CLASS(fm_main_win_parent_class)->finalize)
489 (* G_OBJECT_CLASS(fm_main_win_parent_class)->finalize)(object);
495bd587 490
0589ff84 491 all_wins = g_slist_remove(all_wins, self);
495bd587 492 pcmanfm_unref();
b6e3c554
HJYP
493}
494
495void on_about(GtkAction* act, FmMainWin* win)
496{
f2a36fcd
HJYP
497 GtkWidget* dlg;
498 GtkBuilder* builder = gtk_builder_new();
499 gtk_builder_add_from_file(builder, PACKAGE_UI_DIR "/about.ui", NULL);
500 dlg = (GtkWidget*)gtk_builder_get_object(builder, "dlg");
501 g_object_unref(builder);
502 gtk_dialog_run((GtkDialog*)dlg);
b6e3c554
HJYP
503 gtk_widget_destroy(dlg);
504}
505
506void on_show_hidden(GtkToggleAction* act, FmMainWin* win)
507{
508 gboolean active = gtk_toggle_action_get_active(act);
509 fm_folder_view_set_show_hidden( win->folder_view, active );
510}
511
512void on_change_mode(GtkRadioAction* act, GtkRadioAction *cur, FmMainWin* win)
513{
514 int mode = gtk_radio_action_get_current_value(cur);
515 fm_folder_view_set_mode( win->folder_view, mode );
516}
517
518void on_sort_by(GtkRadioAction* act, GtkRadioAction *cur, FmMainWin* win)
519{
520 int val = gtk_radio_action_get_current_value(cur);
521 fm_folder_view_sort(win->folder_view, -1, val);
522}
523
524void on_sort_type(GtkRadioAction* act, GtkRadioAction *cur, FmMainWin* win)
525{
526 int val = gtk_radio_action_get_current_value(cur);
527 fm_folder_view_sort(win->folder_view, val, -1);
528}
529
0589ff84
HJYP
530void on_focus_in(GtkWidget* w, GdkEventFocus* evt)
531{
532 if(all_wins->data != w)
533 {
534 all_wins = g_slist_remove(all_wins, w);
535 all_wins = g_slist_prepend(all_wins, w);
536 }
537 ((GtkWidgetClass*)fm_main_win_parent_class)->focus_in_event(w, evt);
538}
539
b6e3c554
HJYP
540void on_new_win(GtkAction* act, FmMainWin* win)
541{
19fbd668 542 fm_main_win_add_win(win, fm_path_get_home());
b6e3c554
HJYP
543}
544
545void on_new_tab(GtkAction* act, FmMainWin* win)
546{
547 FmPath* path = fm_folder_view_get_cwd(win->folder_view);
548 fm_main_win_add_tab(win, path);
549}
550
551void on_close_win(GtkAction* act, FmMainWin* win)
552{
553 gtk_widget_destroy(win);
554}
555
19fbd668
HJYP
556void on_close_tab(GtkAction* act, FmMainWin* win)
557{
558 /* FIXME: this is a little bit dirty */
559 GtkNotebook* nb = (GtkNotebook*)win->notebook;
560 gtk_widget_destroy(win->folder_view);
561
562 if(win->vol_status_cancellable)
563 {
564 g_cancellable_cancel(win->vol_status_cancellable);
565 g_object_unref(win->vol_status_cancellable);
566 win->vol_status_cancellable = NULL;
567 }
568 if(gtk_notebook_get_n_pages(nb) == 0)
569 {
570 GtkWidget* main_win = gtk_widget_get_toplevel(nb);
571 gtk_widget_destroy(main_win);
572 }
573}
574
b6e3c554
HJYP
575void on_open_in_new_tab(GtkAction* act, FmMainWin* win)
576{
19fbd668
HJYP
577 FmPathList* sels = fm_folder_view_get_selected_file_paths(win->folder_view);
578 GList* l;
579 for( l = fm_list_peek_head_link(sels); l; l=l->next )
580 {
581 FmPath* path = (FmPath*)l->data;
582 fm_main_win_add_tab(win, path);
583 }
584 fm_list_unref(sels);
b6e3c554
HJYP
585}
586
587
588void on_open_in_new_win(GtkAction* act, FmMainWin* win)
589{
590 FmPathList* sels = fm_folder_view_get_selected_file_paths(win->folder_view);
591 GList* l;
592 for( l = fm_list_peek_head_link(sels); l; l=l->next )
593 {
594 FmPath* path = (FmPath*)l->data;
19fbd668 595 fm_main_win_add_win(win, path);
b6e3c554
HJYP
596 }
597 fm_list_unref(sels);
598}
599
600
601void on_go(GtkAction* act, FmMainWin* win)
602{
603 fm_main_win_chdir_by_name( win, gtk_entry_get_text(win->location));
604}
605
606void on_go_back(GtkAction* act, FmMainWin* win)
607{
608 if(fm_nav_history_get_can_back(win->nav_history))
609 {
610 fm_nav_history_back(win->nav_history);
611 /* FIXME: should this be driven by a signal emitted on FmNavHistory? */
612 fm_main_win_chdir_without_history(win, fm_nav_history_get_cur(win->nav_history));
613 }
614}
615
616void on_go_forward(GtkAction* act, FmMainWin* win)
617{
618 if(fm_nav_history_get_can_forward(win->nav_history))
619 {
620 fm_nav_history_forward(win->nav_history);
621 /* FIXME: should this be driven by a signal emitted on FmNavHistory? */
622 fm_main_win_chdir_without_history(win, fm_nav_history_get_cur(win->nav_history));
623 }
624}
625
626void on_go_up(GtkAction* act, FmMainWin* win)
627{
628 FmPath* parent = fm_path_get_parent(fm_folder_view_get_cwd(win->folder_view));
629 if(parent)
b6e3c554 630 fm_main_win_chdir( win, parent);
b6e3c554
HJYP
631}
632
633void on_go_home(GtkAction* act, FmMainWin* win)
634{
635 fm_main_win_chdir_by_name( win, g_get_home_dir());
636}
637
638void on_go_desktop(GtkAction* act, FmMainWin* win)
639{
640 fm_main_win_chdir_by_name( win, g_get_user_special_dir(G_USER_DIRECTORY_DESKTOP));
641}
642
643void on_go_trash(GtkAction* act, FmMainWin* win)
644{
645 fm_main_win_chdir_by_name( win, "trash:///");
646}
647
648void on_go_computer(GtkAction* act, FmMainWin* win)
649{
650 fm_main_win_chdir_by_name( win, "computer:///");
651}
652
653void on_go_network(GtkAction* act, FmMainWin* win)
654{
655 fm_main_win_chdir_by_name( win, "network:///");
656}
657
658void on_go_apps(GtkAction* act, FmMainWin* win)
659{
660 fm_main_win_chdir_by_name( win, "applications:///");
661}
662
663void fm_main_win_chdir_by_name(FmMainWin* win, const char* path_str)
664{
665 FmPath* path = fm_path_new(path_str);
90d8f009 666 fm_main_win_chdir(win, path);
b6e3c554
HJYP
667 fm_path_unref(path);
668}
669
670void fm_main_win_chdir_without_history(FmMainWin* win, FmPath* path)
671{
90d8f009
HJYP
672 /* FIXME: how to handle UTF-8 here? */
673 char* disp_path = fm_path_to_str(path);
674 char* disp_name = fm_path_display_basename(path);
b6e3c554
HJYP
675 gtk_entry_set_text(win->location, disp_path);
676
b6e3c554
HJYP
677 update_tab_label(win, win->folder_view, disp_name);
678 gtk_window_set_title(win, disp_name);
679 g_free(disp_path);
90d8f009 680 g_free(disp_name);
b6e3c554
HJYP
681
682 fm_folder_view_chdir(win->folder_view, path);
683 /* fm_nav_history_set_cur(); */
684}
685
686void fm_main_win_chdir(FmMainWin* win, FmPath* path)
687{
688 fm_main_win_chdir_without_history(win, path);
689 fm_nav_history_chdir(win->nav_history, path);
690}
691
692static void close_btn_style_set(GtkWidget *btn, GtkRcStyle *prev, gpointer data)
693{
694 gint w, h;
695 gtk_icon_size_lookup_for_settings(gtk_widget_get_settings(btn), GTK_ICON_SIZE_MENU, &w, &h);
696 gtk_widget_set_size_request(btn, w + 2, h + 2);
697}
698
19fbd668 699static void on_close_tab_btn(GtkButton* btn, GtkWidget* view)
b6e3c554
HJYP
700{
701 GtkNotebook* nb = GTK_NOTEBOOK(gtk_widget_get_parent(view));
f0ce8c37 702 FmMainWin* win = FM_MAIN_WIN(gtk_widget_get_toplevel(nb));
b6e3c554 703 gtk_widget_destroy(view);
f0ce8c37
HJYP
704
705 if(win->vol_status_cancellable)
706 {
707 g_cancellable_cancel(win->vol_status_cancellable);
708 g_object_unref(win->vol_status_cancellable);
709 win->vol_status_cancellable = NULL;
710 }
b6e3c554
HJYP
711 if(gtk_notebook_get_n_pages(nb) == 0)
712 {
713 GtkWidget* main_win = gtk_widget_get_toplevel(nb);
714 gtk_widget_destroy(main_win);
715 }
716}
717
718GtkWidget* create_tab_label(FmMainWin* win, FmPath* path, FmFolderView* view)
719{
720 GtkWidget * evt_box;
721 GtkWidget* tab_label;
722 GtkWidget* tab_text;
723 GtkWidget* close_btn;
724 char* disp_name;
725 int w, h;
726
727 /* Create tab label */
728 evt_box = gtk_event_box_new();
729 gtk_event_box_set_visible_window(GTK_EVENT_BOX(evt_box), FALSE);
730
731 tab_label = gtk_hbox_new( FALSE, 0 );
732
733 disp_name = g_filename_display_name(path->name);
734 tab_text = gtk_label_new(disp_name);
735 g_free(disp_name);
736 gtk_box_pack_start(GTK_BOX(tab_label), tab_text, FALSE, FALSE, 4 );
737
738 close_btn = gtk_button_new ();
739 gtk_button_set_focus_on_click ( GTK_BUTTON ( close_btn ), FALSE );
740 gtk_button_set_relief( GTK_BUTTON ( close_btn ), GTK_RELIEF_NONE );
741 gtk_container_add ( GTK_CONTAINER ( close_btn ),
742 gtk_image_new_from_stock(GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU));
743 gtk_container_set_border_width(close_btn, 0);
744 gtk_widget_set_name(close_btn, "close-btn");
745 g_signal_connect(close_btn, "style-set", G_CALLBACK(close_btn_style_set), NULL);
746
747 gtk_box_pack_end( GTK_BOX( tab_label ), close_btn, FALSE, FALSE, 0 );
748
19fbd668 749 g_signal_connect(close_btn, "clicked", G_CALLBACK(on_close_tab_btn), view);
b6e3c554
HJYP
750
751 gtk_container_add(GTK_CONTAINER(evt_box), tab_label);
752 gtk_widget_set_events( GTK_WIDGET(evt_box), GDK_ALL_EVENTS_MASK);
753/*
754 gtk_drag_dest_set ( GTK_WIDGET( evt_box ), GTK_DEST_DEFAULT_ALL,
755 drag_targets,
756 sizeof( drag_targets ) / sizeof( GtkTargetEntry ),
757 GDK_ACTION_DEFAULT | GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK );
758 g_signal_connect ( ( gpointer ) evt_box, "drag-motion",
759 G_CALLBACK ( on_tab_drag_motion ),
760 file_browser );
761*/
762 gtk_widget_show_all(GTK_WIDGET(evt_box));
763 return GTK_WIDGET(evt_box);
764}
765
766void update_tab_label(FmMainWin* win, FmFolderView* fv, const char* title)
767{
768 GtkWidget* tab = gtk_notebook_get_tab_label((GtkNotebook*)win->notebook, win->folder_view);
769 GtkWidget* hbox = gtk_bin_get_child((GtkBin*)tab);
770 GList* children = gtk_container_get_children(hbox);
771 GtkWidget* label = (GtkWidget*)children->data;
772 g_list_free(children);
773 gtk_label_set_text((GtkLabel*)label, title);
774}
775
f0ce8c37
HJYP
776/* FIXME: remote filesystems are sometimes regarded as local ones. */
777static void on_vol_info_available(GObject *src, GAsyncResult *res, FmMainWin* win)
778{
779 GFileInfo* inf = g_file_query_filesystem_info_finish((GFile*)src, res, NULL);
780 guint64 total, free;
781 char total_str[ 64 ];
782 char free_str[ 64 ];
783 char buf[128];
784 if(!inf)
785 return;
786 if(g_file_info_has_attribute(inf, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE))
787 {
788 total = g_file_info_get_attribute_uint64(inf, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE);
789 free = g_file_info_get_attribute_uint64(inf, G_FILE_ATTRIBUTE_FILESYSTEM_FREE);
790
791 fm_file_size_to_str(free_str, free, TRUE);
792 fm_file_size_to_str(total_str, total, TRUE);
793 g_snprintf( buf, G_N_ELEMENTS(buf),
f2a36fcd 794 _("Free space: %s (Total: %s )"), free_str, total_str );
f0ce8c37
HJYP
795 gtk_label_set_text(gtk_bin_get_child(win->vol_status), buf);
796 gtk_widget_show(win->vol_status);
797 }
798 else
799 gtk_widget_hide(win->vol_status);
800 g_object_unref(inf);
801}
802
803void update_volume_info(FmMainWin* win)
804{
805 FmFolderModel* model;
806 if(win->vol_status_cancellable)
807 {
808 g_cancellable_cancel(win->vol_status_cancellable);
809 g_object_unref(win->vol_status_cancellable);
810 win->vol_status_cancellable = NULL;
811 }
812 if(win->folder_view && (model = FM_FOLDER_VIEW(win->folder_view)->model))
813 {
814 /* FIXME: we should't access private data member directly. */
815 GFile* gf = model->dir->gf;
816 win->vol_status_cancellable = g_cancellable_new();
817 g_file_query_filesystem_info_async(gf,
818 G_FILE_ATTRIBUTE_FILESYSTEM_SIZE","
819 G_FILE_ATTRIBUTE_FILESYSTEM_FREE,
820 G_PRIORITY_LOW, win->vol_status_cancellable,
821 on_vol_info_available, win);
822 }
823 else
824 gtk_widget_hide(win->vol_status);
825}
826
b6e3c554
HJYP
827gint fm_main_win_add_tab(FmMainWin* win, FmPath* path)
828{
829 /* create label for the tab */
830 GtkWidget* label;
831 GtkWidget* folder_view;
832 gint ret;
833
834 /* create folder view */
835 folder_view = fm_folder_view_new( FM_FV_ICON_VIEW );
836 fm_folder_view_set_show_hidden(folder_view, FALSE);
837 fm_folder_view_sort(folder_view, GTK_SORT_DESCENDING, COL_FILE_NAME);
838 fm_folder_view_set_selection_mode(folder_view, GTK_SELECTION_MULTIPLE);
839 g_signal_connect(folder_view, "clicked", on_file_clicked, win);
840 g_signal_connect(folder_view, "status", on_status, win);
841 g_signal_connect(folder_view, "sel-changed", on_sel_changed, win);
842
843 fm_folder_view_chdir(folder_view, path);
844 gtk_widget_show(folder_view);
845
846 label = create_tab_label(win, path, folder_view);
847
848 ret = gtk_notebook_append_page(win->notebook, folder_view, label);
849 gtk_notebook_set_tab_reorderable(win->notebook, folder_view, TRUE);
850 gtk_notebook_set_current_page(win->notebook, ret);
851
09710de4
HJYP
852 if(gtk_notebook_get_n_pages(win->notebook) > 1)
853 gtk_notebook_set_show_tabs(win->notebook, TRUE);
854 else
855 gtk_notebook_set_show_tabs(win->notebook, FALSE);
856
b6e3c554
HJYP
857 /* set current folder view */
858 win->folder_view = folder_view;
f0ce8c37
HJYP
859 /* create navigation history */
860 win->nav_history = fm_nav_history_new();
861 g_object_set_qdata_full((GObject*)folder_view, nav_history_id, win->nav_history, (GDestroyNotify)g_object_unref);
b6e3c554
HJYP
862 return ret;
863}
864
865
19fbd668
HJYP
866FmMainWin* fm_main_win_add_win(FmMainWin* win, FmPath* path)
867{
868 win = fm_main_win_new();
869 gtk_window_set_default_size(win, 640, 480);
870 fm_main_win_chdir(win, path);
871 gtk_window_present(win);
872 return win;
873}
874
b6e3c554
HJYP
875void on_cut(GtkAction* act, FmMainWin* win)
876{
877 GtkWidget* focus = gtk_window_get_focus((GtkWindow*)win);
878 if(GTK_IS_EDITABLE(focus) &&
879 gtk_editable_get_selection_bounds((GtkEditable*)focus, NULL, NULL) )
880 {
881 gtk_editable_cut_clipboard((GtkEditable*)focus);
882 }
883 else
884 {
885 FmPathList* files = fm_folder_view_get_selected_file_paths(win->folder_view);
886 if(files)
887 {
888 fm_clipboard_cut_files(win, files);
889 fm_list_unref(files);
890 }
891 }
892}
893
894void on_copy(GtkAction* act, FmMainWin* win)
895{
896 GtkWidget* focus = gtk_window_get_focus((GtkWindow*)win);
897 if(GTK_IS_EDITABLE(focus) &&
898 gtk_editable_get_selection_bounds((GtkEditable*)focus, NULL, NULL) )
899 {
900 gtk_editable_copy_clipboard((GtkEditable*)focus);
901 }
902 else
903 {
904 FmPathList* files = fm_folder_view_get_selected_file_paths(win->folder_view);
905 if(files)
906 {
907 fm_clipboard_copy_files(win, files);
908 fm_list_unref(files);
909 }
910 }
911}
912
913void on_copy_to(GtkAction* act, FmMainWin* win)
914{
915 FmPathList* files = fm_folder_view_get_selected_file_paths(win->folder_view);
916 if(files)
917 {
918 fm_copy_files_to(files);
919 fm_list_unref(files);
920 }
921}
922
923void on_move_to(GtkAction* act, FmMainWin* win)
924{
925 FmPathList* files = fm_folder_view_get_selected_file_paths(win->folder_view);
926 if(files)
927 {
928 fm_move_files_to(files);
929 fm_list_unref(files);
930 }
931}
932
933void on_paste(GtkAction* act, FmMainWin* win)
934{
935 GtkWidget* focus = gtk_window_get_focus((GtkWindow*)win);
936 if(GTK_IS_EDITABLE(focus) )
937 {
938 gtk_editable_paste_clipboard((GtkEditable*)focus);
939 }
940 else
941 {
942 FmPath* path = fm_folder_view_get_cwd(win->folder_view);
943 fm_clipboard_paste_files(win->folder_view, path);
944 }
945}
946
947void on_del(GtkAction* act, FmMainWin* win)
948{
949 FmPathList* files = fm_folder_view_get_selected_file_paths(win->folder_view);
950 fm_trash_or_delete_files(files);
951 fm_list_unref(files);
952}
953
954void on_rename(GtkAction* act, FmMainWin* win)
955{
956 FmPathList* files = fm_folder_view_get_selected_file_paths(win->folder_view);
957 if( !fm_list_is_empty(files) )
958 {
959 fm_rename_file(fm_list_peek_head(files));
960 /* FIXME: is it ok to only rename the first selected file here. */
961 }
962 fm_list_unref(files);
963}
964
965void on_select_all(GtkAction* act, FmMainWin* win)
966{
967 fm_folder_view_select_all(win->folder_view);
968}
969
970void on_invert_select(GtkAction* act, FmMainWin* win)
971{
972 fm_folder_view_select_invert(win->folder_view);
973}
974
f2a36fcd
HJYP
975void on_preference(GtkAction* act, FmMainWin* win)
976{
977 fm_edit_preference(win, 0);
978}
979
b6e3c554
HJYP
980void on_location(GtkAction* act, FmMainWin* win)
981{
982 gtk_widget_grab_focus(win->location);
983}
984
985void on_prop(GtkAction* action, FmMainWin* win)
986{
987 FmFolderView* fv = FM_FOLDER_VIEW(win->folder_view);
988 /* FIXME: should prevent directly accessing data members */
989 FmFileInfo* fi = FM_FOLDER_MODEL(fv->model)->dir->dir_fi;
990 FmFileInfoList* files = fm_file_info_list_new();
991 fm_list_push_tail(files, fi);
992 fm_show_file_properties(files);
993 fm_list_unref(files);
994}
995
996void on_switch_page(GtkNotebook* nb, GtkNotebookPage* page, guint num, FmMainWin* win)
997{
998 FmFolderView* fv = FM_FOLDER_VIEW(gtk_notebook_get_nth_page(nb, num));
999
1000 if(win->folder_view)
1001 g_signal_handlers_disconnect_by_func(win->folder_view, on_view_loaded, win);
1002 win->folder_view = fv;
1003
1004 if(fv)
1005 {
1006 FmPath* cwd = fm_folder_view_get_cwd(fv);
f0ce8c37 1007 win->nav_history = (FmNavHistory*)g_object_get_qdata((GObject*)fv, nav_history_id);
b6e3c554 1008
19abb8bf
HJYP
1009 /* FIXME: we shouldn't access private data member. */
1010 fm_path_entry_set_model( win->location, cwd, fv->model );
b6e3c554
HJYP
1011 g_signal_connect(fv, "loaded", G_CALLBACK(on_view_loaded), win);
1012
1013 if(cwd)
1014 {
1015 char* disp_name;
1016 disp_name = g_filename_display_name(cwd->name);
1017 gtk_window_set_title((GtkWindow*)win, disp_name);
1018 g_free(disp_name);
1019 }
f0ce8c37
HJYP
1020
1021 update_volume_info(win);
b6e3c554
HJYP
1022 }
1023}
1024
09710de4
HJYP
1025void on_page_removed(GtkNotebook* nb, GtkWidget* page, guint num, FmMainWin* win)
1026{
1027 if(gtk_notebook_get_n_pages(nb) > 1)
1028 gtk_notebook_set_show_tabs(nb, TRUE);
1029 else
1030 gtk_notebook_set_show_tabs(nb, FALSE);
1031}
1032
b6e3c554
HJYP
1033void on_create_new(GtkAction* action, FmMainWin* win)
1034{
1035 FmFolderView* fv = FM_FOLDER_VIEW(win->folder_view);
1036 const char* name = gtk_action_get_name(action);
1037 GError* err = NULL;
1038 FmPath* cwd = fm_folder_view_get_cwd(fv);
1039 FmPath* dest;
1040 char* basename;
1041_retry:
1042 basename = fm_get_user_input(win, _("Create New..."), _("Enter a name for the newly created file:"), _("New"));
1043 if(!basename)
1044 return;
1045
1046 dest = fm_path_new_child(cwd, basename);
1047 g_free(basename);
1048
1049 if( strcmp(name, "NewFolder") == 0 )
1050 {
1051 GFile* gf = fm_path_to_gfile(dest);
1052 if(!g_file_make_directory(gf, NULL, &err))
1053 {
1054 if(err->domain = G_IO_ERROR && err->code == G_IO_ERROR_EXISTS)
1055 {
1056 fm_path_unref(dest);
1057 g_error_free(err);
1058 g_object_unref(gf);
1059 err = NULL;
1060 goto _retry;
1061 }
1062 fm_show_error(win, err->message);
1063 g_error_free(err);
1064 }
1065
1066 if(!err) /* select the newly created file */
1067 {
1068 /*FIXME: this doesn't work since the newly created file will
1069 * only be shown after file-created event was fired on its
1070 * folder's monitor and after FmFolder handles it in idle
1071 * handler. So, we cannot select it since it's not yet in
1072 * the folder model now. */
1073 /* fm_folder_view_select_file_path(fv, dest); */
1074 }
1075 g_object_unref(gf);
1076 }
1077 else if( strcmp(name, "NewBlank") == 0 )
1078 {
1079 GFile* gf = fm_path_to_gfile(dest);
1080 GFileOutputStream* f = g_file_create(gf, G_FILE_CREATE_NONE, NULL, &err);
1081 if(f)
1082 {
1083 g_output_stream_close(f, NULL, NULL);
1084 g_object_unref(f);
1085 }
1086 else
1087 {
1088 if(err->domain = G_IO_ERROR && err->code == G_IO_ERROR_EXISTS)
1089 {
1090 fm_path_unref(dest);
1091 g_error_free(err);
1092 g_object_unref(gf);
1093 err = NULL;
1094 goto _retry;
1095 }
1096 fm_show_error(win, err->message);
1097 g_error_free(err);
1098 }
1099
1100 if(!err) /* select the newly created file */
1101 {
1102 /*FIXME: this doesn't work since the newly created file will
1103 * only be shown after file-created event was fired on its
1104 * folder's monitor and after FmFolder handles it in idle
1105 * handler. So, we cannot select it since it's not yet in
1106 * the folder model now. */
1107 /* fm_folder_view_select_file_path(fv, dest); */
1108 }
1109 g_object_unref(gf);
1110 }
1111 else /* templates */
1112 {
1113
1114 }
1115 fm_path_unref(dest);
1116}
0589ff84
HJYP
1117
1118FmMainWin* fm_main_win_get_last_active()
1119{
1120 return all_wins ? (FmMainWin*)all_wins->data : NULL;
1121}
1122
1123void fm_main_win_open_in_last_active(FmPath* path)
1124{
1125 FmMainWin* win = fm_main_win_get_last_active();
1126 if(!win)
1127 {
1128 win = fm_main_win_new();
1129 gtk_window_set_default_size(win, app_config->win_width, app_config->win_height);
1130 fm_main_win_chdir(win, path);
1131 }
1132 else
1133 fm_main_win_add_tab(win, path);
1134 gtk_window_present(win);
1135}