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