Commit from LXDE Pootle server by user sotrud_nik.: 217 of 295 strings translated...
[lxde/pcmanfm.git] / src / tab-page.c
CommitLineData
32f6ac22
HJYP
1// fm-tab-page.c
2//
3// Copyright 2011 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
1b553f57 4// Copyright 2012-2014 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua>
32f6ac22
HJYP
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
18aab7be
AG
21#ifdef HAVE_CONFIG_H
22# include <config.h>
23#endif
24
32f6ac22
HJYP
25#include <libfm/fm-gtk.h>
26#include <glib/gi18n.h>
27
3aebc679
AG
28#include "app-config.h"
29#include "main-win.h"
30#include "tab-page.h"
3f8714d0 31
3d6f154b
AG
32#include "gseal-gtk-compat.h"
33
77bc3087 34#include <stdlib.h>
6b895e11 35#include <fnmatch.h>
77bc3087 36
3f8714d0 37/* Additional entries for FmFileMenu popup */
b22ddcea 38/* it is also used for FmSidePane context menu popup */
3f8714d0
AG
39static const char folder_menu_xml[]=
40"<popup>"
41 "<placeholder name='ph1'>"
42 "<menuitem action='NewTab'/>"
43 "<menuitem action='NewWin'/>"
44 "<menuitem action='Term'/>"
45 /* "<menuitem action='Search'/>" */
46 "</placeholder>"
47"</popup>";
48
49static void on_open_in_new_tab(GtkAction* act, FmMainWin* win);
50static void on_open_in_new_win(GtkAction* act, FmMainWin* win);
51static void on_open_folder_in_terminal(GtkAction* act, FmMainWin* win);
52
53/* Action entries for popup menu entries above */
54static GtkActionEntry folder_menu_actions[]=
55{
56 {"NewTab", GTK_STOCK_NEW, N_("Open in New T_ab"), NULL, NULL, G_CALLBACK(on_open_in_new_tab)},
57 {"NewWin", GTK_STOCK_NEW, N_("Open in New Win_dow"), NULL, NULL, G_CALLBACK(on_open_in_new_win)},
58 {"Search", GTK_STOCK_FIND, NULL, NULL, NULL, NULL},
59 {"Term", "utilities-terminal", N_("Open in Termina_l"), NULL, NULL, G_CALLBACK(on_open_folder_in_terminal)},
60};
3aebc679 61
32f6ac22
HJYP
62#define GET_MAIN_WIN(page) FM_MAIN_WIN(gtk_widget_get_toplevel(GTK_WIDGET(page)))
63
64enum {
9756804e
HJYP
65 CHDIR,
66 OPEN_DIR,
32f6ac22 67 STATUS,
f8a192a0 68 GOT_FOCUS,
32f6ac22
HJYP
69 N_SIGNALS
70};
71
72static guint signals[N_SIGNALS];
73
74static void fm_tab_page_finalize(GObject *object);
9756804e 75static void fm_tab_page_chdir_without_history(FmTabPage* page, FmPath* path);
32f6ac22 76static void on_folder_fs_info(FmFolder* folder, FmTabPage* page);
5dee1d40
HJYP
77static void on_folder_start_loading(FmFolder* folder, FmTabPage* page);
78static void on_folder_finish_loading(FmFolder* folder, FmTabPage* page);
79static void on_folder_removed(FmFolder* folder, FmTabPage* page);
80static void on_folder_unmount(FmFolder* folder, FmTabPage* page);
32f6ac22 81static void on_folder_content_changed(FmFolder* folder, FmTabPage* page);
5dee1d40
HJYP
82static FmJobErrorAction on_folder_error(FmFolder* folder, GError* err, FmJobErrorSeverity severity, FmTabPage* page);
83
b3959427 84static void on_folder_view_sel_changed(FmFolderView* fv, gint n_sel, FmTabPage* page);
77bc3087
AG
85#if FM_CHECK_VERSION(1, 2, 0)
86static void on_folder_view_columns_changed(FmFolderView *fv, FmTabPage *page);
87#endif
f8a192a0 88static gboolean on_folder_view_focus_in(GtkWidget *widget, GdkEvent *event, FmTabPage *page);
9756804e 89static char* format_status_text(FmTabPage* page);
32f6ac22
HJYP
90
91#if GTK_CHECK_VERSION(3, 0, 0)
92static void fm_tab_page_destroy(GtkWidget *page);
93#else
94static void fm_tab_page_destroy(GtkObject *page);
95#endif
96
326e4241
AG
97static void fm_tab_page_realize(GtkWidget *page);
98static void fm_tab_page_unrealize(GtkWidget *page);
99
b22ddcea
AG
100static GQuark popup_qdata;
101
32f6ac22
HJYP
102G_DEFINE_TYPE(FmTabPage, fm_tab_page, GTK_TYPE_HPANED)
103
104static void fm_tab_page_class_init(FmTabPageClass *klass)
105{
106 GObjectClass *g_object_class = G_OBJECT_CLASS(klass);
32f6ac22 107 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
326e4241 108#if GTK_CHECK_VERSION(3, 0, 0)
32f6ac22
HJYP
109 widget_class->destroy = fm_tab_page_destroy;
110#else
111 GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS(klass);
112 gtk_object_class->destroy = fm_tab_page_destroy;
113#endif
114 g_object_class->finalize = fm_tab_page_finalize;
326e4241
AG
115 widget_class->realize = fm_tab_page_realize;
116 widget_class->unrealize = fm_tab_page_unrealize;
32f6ac22 117
9756804e
HJYP
118 /* signals that current working directory is changed. */
119 signals[CHDIR] =
120 g_signal_new("chdir",
121 G_TYPE_FROM_CLASS(klass),
122 G_SIGNAL_RUN_FIRST,
123 G_STRUCT_OFFSET (FmTabPageClass, chdir),
124 NULL, NULL,
125 g_cclosure_marshal_VOID__POINTER,
126 G_TYPE_NONE, 1, G_TYPE_POINTER);
127
128#if 0
129 /* FIXME: is this really needed? */
130 /* signals that the user wants to open a new dir. */
131 signals[OPEN_DIR] =
132 g_signal_new("open-dir",
133 G_TYPE_FROM_CLASS(klass),
134 G_SIGNAL_RUN_FIRST,
135 G_STRUCT_OFFSET (FmTabPageClass, open_dir),
136 NULL, NULL,
137 g_cclosure_marshal_VOID__UINT_POINTER,
138 G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_POINTER);
139#endif
140
141 /* emit when the status bar message is changed */
32f6ac22
HJYP
142 signals[STATUS] =
143 g_signal_new("status",
144 G_TYPE_FROM_CLASS(klass),
145 G_SIGNAL_RUN_FIRST,
146 G_STRUCT_OFFSET (FmTabPageClass, status),
147 NULL, NULL,
148 g_cclosure_marshal_VOID__UINT_POINTER,
149 G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_POINTER);
f8a192a0
AG
150 /* folder view received the focus */
151 signals[GOT_FOCUS] =
152 g_signal_new("got-focus",
153 G_TYPE_FROM_CLASS(klass),
154 G_SIGNAL_RUN_FIRST,
155 G_STRUCT_OFFSET (FmTabPageClass, got_focus),
156 NULL, NULL,
157 g_cclosure_marshal_VOID__VOID,
158 G_TYPE_NONE, 0);
b22ddcea
AG
159
160 popup_qdata = g_quark_from_static_string("tab-page::popup-filelist");
32f6ac22
HJYP
161}
162
163
164static void fm_tab_page_finalize(GObject *object)
165{
166 FmTabPage *page;
167 int i;
168
169 g_return_if_fail(object != NULL);
170 g_return_if_fail(FM_IS_TAB_PAGE(object));
171
3aebc679 172 page = (FmTabPage*)object;
32f6ac22
HJYP
173
174 for(i = 0; i < FM_STATUS_TEXT_NUM; ++i)
175 g_free(page->status_text[i]);
176
87aa3463
AG
177#if FM_CHECK_VERSION(1, 0, 2)
178 g_free(page->filter_pattern);
179#endif
180
32f6ac22
HJYP
181 G_OBJECT_CLASS(fm_tab_page_parent_class)->finalize(object);
182}
183
5dee1d40
HJYP
184static void free_folder(FmTabPage* page)
185{
186 if(page->folder)
187 {
188 g_signal_handlers_disconnect_by_func(page->folder, on_folder_start_loading, page);
189 g_signal_handlers_disconnect_by_func(page->folder, on_folder_finish_loading, page);
190 g_signal_handlers_disconnect_by_func(page->folder, on_folder_fs_info, page);
191 g_signal_handlers_disconnect_by_func(page->folder, on_folder_error, page);
192 g_signal_handlers_disconnect_by_func(page->folder, on_folder_content_changed, page);
193 g_signal_handlers_disconnect_by_func(page->folder, on_folder_removed, page);
194 g_signal_handlers_disconnect_by_func(page->folder, on_folder_unmount, page);
195 g_object_unref(page->folder);
196 page->folder = NULL;
1b553f57
AG
197#if FM_CHECK_VERSION(1, 2, 0)
198 if (page->want_focus)
199 fm_path_unref(page->want_focus);
200 page->want_focus = NULL;
201#endif
5dee1d40
HJYP
202 }
203}
204
f8a192a0
AG
205/* workaround on FmStandardView: it should forward focus-in events but doesn't do */
206static void on_folder_view_add(GtkContainer *container, GtkWidget *child, FmTabPage *page)
207{
208 g_signal_connect(child, "focus-in-event",
209 G_CALLBACK(on_folder_view_focus_in), page);
210}
211
212static void on_folder_view_remove(GtkContainer *container, GtkWidget *child, FmTabPage *page)
213{
214 g_signal_handlers_disconnect_by_func(child, on_folder_view_focus_in, page);
215}
216
217static void _connect_focus_in(FmFolderView *folder_view, FmTabPage *page)
218{
219 GList *children, *l;
220
221 g_signal_connect(folder_view, "focus-in-event",
222 G_CALLBACK(on_folder_view_focus_in), page);
223 g_signal_connect(folder_view, "add",
224 G_CALLBACK(on_folder_view_add), page);
225 g_signal_connect(folder_view, "remove",
226 G_CALLBACK(on_folder_view_remove), page);
227 children = gtk_container_get_children(GTK_CONTAINER(folder_view));
228 for (l = children; l; l = l->next)
229 g_signal_connect(l->data, "focus-in-event",
230 G_CALLBACK(on_folder_view_focus_in), page);
231 g_list_free(children);
232}
233
234static void _disconnect_focus_in(FmFolderView *folder_view, FmTabPage *page)
235{
236 GList *children, *l;
237
238 g_signal_handlers_disconnect_by_func(folder_view, on_folder_view_focus_in, page);
239 g_signal_handlers_disconnect_by_func(folder_view, on_folder_view_add, page);
240 g_signal_handlers_disconnect_by_func(folder_view, on_folder_view_remove, page);
241 children = gtk_container_get_children(GTK_CONTAINER(folder_view));
242 for (l = children; l; l = l->next)
243 g_signal_handlers_disconnect_by_func(l->data, on_folder_view_focus_in, page);
244 g_list_free(children);
245}
246
2e9d61cd
AG
247#if FM_CHECK_VERSION(1, 2, 0)
248static void on_home_path_changed(FmAppConfig *cfg, FmSidePane *sp)
249{
2e9d61cd 250 if (cfg->home_path && cfg->home_path[0])
1b553f57 251 fm_side_pane_set_home_dir(sp, cfg->home_path);
2e9d61cd 252 else
1b553f57 253 fm_side_pane_set_home_dir(sp, fm_get_home_dir());
2e9d61cd
AG
254}
255#endif
256
32f6ac22 257#if GTK_CHECK_VERSION(3, 0, 0)
056dd83b 258void fm_tab_page_destroy(GtkWidget *object)
32f6ac22 259#else
056dd83b 260void fm_tab_page_destroy(GtkObject *object)
32f6ac22
HJYP
261#endif
262{
056dd83b 263 FmTabPage* page = FM_TAB_PAGE(object);
f8a192a0 264
6b071351
AG
265 g_debug("fm_tab_page_destroy, folder: %s",
266 page->folder ? fm_path_get_basename(fm_folder_get_path(page->folder)) : "(none)");
5dee1d40
HJYP
267 free_folder(page);
268 if(page->nav_history)
269 {
270 g_object_unref(page->nav_history);
271 page->nav_history = NULL;
272 }
273 if(page->folder_view)
274 {
f8a192a0
AG
275 /* tab page may be inactive now and there is a chance it does not contain
276 own view therefore we have to destroy own folder view widget manually */
277 GtkWidget *fv = GTK_WIDGET(page->folder_view);
278 GtkWidget *parent = gtk_widget_get_parent(fv);
279 GList *panes, *l;
280
281 if (parent)
282 {
283 panes = gtk_container_get_children(GTK_CONTAINER(page->views));
284 for (l = panes; l; l = l->next)
285 if ((GtkWidget*)l->data == fv)
286 break;
287 if (l == NULL)
288 gtk_container_remove(GTK_CONTAINER(parent), fv);
289 g_list_free(panes);
290 }
291
5dee1d40 292 g_signal_handlers_disconnect_by_func(page->folder_view, on_folder_view_sel_changed, page);
77bc3087
AG
293#if FM_CHECK_VERSION(1, 2, 0)
294 g_signal_handlers_disconnect_by_func(page->folder_view, on_folder_view_columns_changed, page);
295#endif
2e9d61cd
AG
296#if FM_CHECK_VERSION(1, 2, 0)
297 g_signal_handlers_disconnect_by_func(app_config, on_home_path_changed, page->side_pane);
298#endif
f8a192a0 299 _disconnect_focus_in(page->folder_view, page);
aedfc89d 300 g_object_unref(page->folder_view);
5dee1d40
HJYP
301 page->folder_view = NULL;
302 }
9273d81a
AG
303#if FM_CHECK_VERSION(1, 0, 2)
304 g_strfreev(page->columns);
305 page->columns = NULL;
306#endif
14c144bc
AG
307 if(page->update_scroll_id)
308 {
309 g_source_remove(page->update_scroll_id);
310 page->update_scroll_id = 0;
311 }
b22ddcea
AG
312#if FM_CHECK_VERSION(1, 2, 0)
313 fm_side_pane_set_popup_updater(page->side_pane, NULL, NULL);
314#endif
14014af9
AG
315 if (page->dd)
316 {
317 g_object_unref(page->dd);
318 page->dd = NULL;
319 }
056dd83b 320
5dee1d40
HJYP
321#if GTK_CHECK_VERSION(3, 0, 0)
322 if(GTK_WIDGET_CLASS(fm_tab_page_parent_class)->destroy)
323 (*GTK_WIDGET_CLASS(fm_tab_page_parent_class)->destroy)(object);
324#else
325 if(GTK_OBJECT_CLASS(fm_tab_page_parent_class)->destroy)
326 (*GTK_OBJECT_CLASS(fm_tab_page_parent_class)->destroy)(object);
327#endif
32f6ac22
HJYP
328}
329
330static void on_folder_content_changed(FmFolder* folder, FmTabPage* page)
331{
9756804e
HJYP
332 /* update status text */
333 g_free(page->status_text[FM_STATUS_TEXT_NORMAL]);
334 page->status_text[FM_STATUS_TEXT_NORMAL] = format_status_text(page);
335 g_signal_emit(page, signals[STATUS], 0,
0b37d2a5
AG
336 (guint)FM_STATUS_TEXT_NORMAL,
337 page->status_text[FM_STATUS_TEXT_NORMAL]);
32f6ac22
HJYP
338}
339
b3959427 340static void on_folder_view_sel_changed(FmFolderView* fv, gint n_sel, FmTabPage* page)
32f6ac22
HJYP
341{
342 char* msg = page->status_text[FM_STATUS_TEXT_SELECTED_FILES];
b462b7e0 343 GString *str;
32f6ac22
HJYP
344 g_free(msg);
345
b3959427 346 if(n_sel > 0)
32f6ac22 347 {
b462b7e0 348 str = g_string_sized_new(64);
32f6ac22 349 /* FIXME: display total size of all selected files. */
b3959427 350 if(n_sel == 1) /* only one file is selected */
32f6ac22 351 {
b3959427 352 FmFileInfoList* files = fm_folder_view_dup_selected_files(fv);
5a89e062 353 FmFileInfo* fi = fm_file_info_list_peek_head(files);
32f6ac22 354 const char* size_str = fm_file_info_get_disp_size(fi);
b462b7e0
AG
355#if FM_CHECK_VERSION(1, 2, 0)
356 GList *l;
357#endif
32f6ac22
HJYP
358 if(size_str)
359 {
b462b7e0 360 g_string_printf(str, "\"%s\" (%s) %s",
32f6ac22
HJYP
361 fm_file_info_get_disp_name(fi),
362 size_str ? size_str : "",
363 fm_file_info_get_desc(fi));
364 }
365 else
366 {
b462b7e0 367 g_string_printf(str, "\"%s\" %s",
32f6ac22
HJYP
368 fm_file_info_get_disp_name(fi),
369 fm_file_info_get_desc(fi));
370 }
b462b7e0
AG
371#if FM_CHECK_VERSION(1, 2, 0)
372 /* ---- statusbar plugins support ---- */
373 CHECK_MODULES();
374 for (l = _tab_page_modules; l; l = l->next)
375 {
376 FmTabPageStatusInit *module = l->data;
377 char *message = module->sel_message(files, n_sel);
378 if (message && message[0])
379 {
380 g_string_append_c(str, ' ');
381 g_string_append(str, message);
382 }
383 g_free(message);
384 }
385#endif
b3959427 386 fm_file_info_list_unref(files);
32f6ac22
HJYP
387 }
388 else
792cf322 389 {
22d3f0a4 390 FmFileInfoList* files;
b462b7e0 391 goffset sum;
22d3f0a4
AG
392 GList *l;
393 char size_str[128];
394
b462b7e0 395 g_string_printf(str, ngettext("%d item selected", "%d items selected", n_sel), n_sel);
22d3f0a4
AG
396 /* don't count if too many files are selected, that isn't lightweight */
397 if (n_sel < 1000)
398 {
399 sum = 0;
400 files = fm_folder_view_dup_selected_files(fv);
401 for (l = fm_file_info_list_peek_head_link(files); l; l = l->next)
402 {
403 if (fm_file_info_is_dir(l->data))
404 {
405 /* if we got a directory then we cannot tell it's size
406 unless we do deep count but we cannot afford it */
407 sum = -1;
408 break;
409 }
410 sum += fm_file_info_get_size(l->data);
411 }
b462b7e0
AG
412 if (sum >= 0)
413 {
414 fm_file_size_to_str(size_str, sizeof(size_str), sum,
415 fm_config->si_unit);
416 g_string_append_printf(str, " (%s)", size_str);
417 }
418#if FM_CHECK_VERSION(1, 2, 0)
419 /* ---- statusbar plugins support ---- */
420 CHECK_MODULES();
421 for (l = _tab_page_modules; l; l = l->next)
422 {
423 FmTabPageStatusInit *module = l->data;
424 char *message = module->sel_message(files, n_sel);
425 if (message && message[0])
426 {
427 g_string_append_c(str, ' ');
428 g_string_append(str, message);
429 }
430 g_free(message);
431 }
432#endif
22d3f0a4
AG
433 fm_file_info_list_unref(files);
434 }
2e9d61cd
AG
435 /* FIXME: can we show some more info on selection?
436 that isn't lightweight if a lot of files are selected */
792cf322 437 }
b462b7e0 438 msg = g_string_free(str, FALSE);
32f6ac22
HJYP
439 }
440 else
441 msg = NULL;
442 page->status_text[FM_STATUS_TEXT_SELECTED_FILES] = msg;
443 g_signal_emit(page, signals[STATUS], 0,
444 (guint)FM_STATUS_TEXT_SELECTED_FILES, msg);
445}
446
77bc3087
AG
447#if FM_CHECK_VERSION(1, 2, 0)
448static void on_folder_view_columns_changed(FmFolderView *fv, FmTabPage *page)
449{
450 GSList *columns = fm_folder_view_get_columns(fv), *l;
451 char **cols;
452 guint i;
453
454 if (columns == NULL)
455 return;
456 i = g_slist_length(columns);
457 cols = g_new(char *, i+1);
458 for (i = 0, l = columns; l; i++, l = l->next)
459 {
460 FmFolderViewColumnInfo *info = l->data;
461
462 if (info->width > 0)
463 cols[i] = g_strdup_printf("%s:%d",
464 fm_folder_model_col_get_name(info->col_id),
465 info->width);
466 else
467 cols[i] = g_strdup(fm_folder_model_col_get_name(info->col_id));
468 }
5cbe5b94 469 g_slist_free(columns);
77bc3087 470 cols[i] = NULL; /* terminate the list */
9273d81a
AG
471 if (page->own_config)
472 {
473 g_strfreev(page->columns);
474 page->columns = cols;
475 fm_app_config_save_config_for_path(fm_folder_view_get_cwd(fv),
476 page->sort_type, page->sort_by, -1,
477 page->show_hidden, cols);
478 }
479 else
480 {
481 g_strfreev(app_config->columns);
482 app_config->columns = cols;
483 pcmanfm_save_config(FALSE);
484 }
77bc3087
AG
485}
486#endif
487
f8a192a0
AG
488static gboolean on_folder_view_focus_in(GtkWidget *widget, GdkEvent *event, FmTabPage *page)
489{
490 g_signal_emit(page, signals[GOT_FOCUS], 0);
491 return FALSE;
492}
493
5dee1d40
HJYP
494static FmJobErrorAction on_folder_error(FmFolder* folder, GError* err, FmJobErrorSeverity severity, FmTabPage* page)
495{
496 GtkWindow* win = GTK_WINDOW(GET_MAIN_WIN(page));
497 if(err->domain == G_IO_ERROR)
498 {
499 if( err->code == G_IO_ERROR_NOT_MOUNTED && severity < FM_JOB_ERROR_CRITICAL )
500 {
501 FmPath* path = fm_folder_get_path(folder);
502 if(fm_mount_path(win, path, TRUE))
503 return FM_JOB_RETRY;
504 }
505 }
0bf366d5
HJYP
506 if(severity >= FM_JOB_ERROR_MODERATE)
507 {
3aebc679
AG
508 /* Only show more severe errors to the users and
509 * ignore milder errors. Otherwise too many error
510 * message boxes can be annoying.
511 * This fixes bug #3411298- Show "Permission denied" when switching to super user mode.
512 * https://sourceforge.net/tracker/?func=detail&aid=3411298&group_id=156956&atid=801864
513 * */
514 fm_show_error(win, NULL, err->message);
515 }
5dee1d40
HJYP
516 return FM_JOB_CONTINUE;
517}
518
326e4241
AG
519static void fm_tab_page_realize(GtkWidget *page)
520{
521 GTK_WIDGET_CLASS(fm_tab_page_parent_class)->realize(page);
522 if (FM_TAB_PAGE(page)->busy)
523 fm_set_busy_cursor(page);
524}
525
526static void fm_tab_page_unrealize(GtkWidget *page)
527{
528 if (FM_TAB_PAGE(page)->busy)
529 fm_unset_busy_cursor(page);
530 GTK_WIDGET_CLASS(fm_tab_page_parent_class)->unrealize(page);
531}
532
533static void _tab_set_busy_cursor(FmTabPage* page)
534{
535 page->busy = TRUE;
536 if (gtk_widget_get_realized(GTK_WIDGET(page)))
537 fm_set_busy_cursor(GTK_WIDGET(page));
538}
539
540static void _tab_unset_busy_cursor(FmTabPage* page)
541{
542 page->busy = FALSE;
543 if (gtk_widget_get_realized(GTK_WIDGET(page)))
544 fm_unset_busy_cursor(GTK_WIDGET(page));
545}
546
87aa3463 547#if FM_CHECK_VERSION(1, 0, 2)
87aa3463
AG
548static gboolean fm_tab_page_path_filter(FmFileInfo *file, gpointer user_data)
549{
550 FmTabPage *page;
551 const char *disp_name;
552 char *casefold, *key;
553 gboolean result;
554
555 g_return_val_if_fail(FM_IS_TAB_PAGE(user_data), FALSE);
556 page = (FmTabPage*)user_data;
557 if (page->filter_pattern == NULL)
558 return TRUE;
559 disp_name = fm_file_info_get_disp_name(file);
560 casefold = g_utf8_casefold(disp_name, -1);
561 key = g_utf8_normalize(casefold, -1, G_NORMALIZE_ALL);
562 g_free(casefold);
6b895e11 563 result = (fnmatch(page->filter_pattern, key, 0) == 0);
87aa3463
AG
564 g_free(key);
565 return result;
566}
567#endif
568
5dee1d40
HJYP
569static void on_folder_start_loading(FmFolder* folder, FmTabPage* page)
570{
f2e13d08 571 FmFolderView* fv = page->folder_view;
0b997604 572 /* g_debug("start-loading"); */
5dee1d40 573 /* FIXME: this should be set on toplevel parent */
326e4241 574 _tab_set_busy_cursor(page);
48be498f 575
f2e13d08 576#if FM_CHECK_VERSION(1, 0, 2)
48be498f
HJYP
577 if(fm_folder_is_incremental(folder))
578 {
a861bb2a
AG
579 /* create a model for the folder and set it to the view
580 it is delayed for non-incremental folders since adding rows into
581 model is much faster without handlers connected to its signals */
9273d81a 582 FmFolderModel* model = fm_folder_model_new(folder, page->show_hidden);
87aa3463
AG
583 if (page->filter_pattern)
584 {
585 fm_folder_model_add_filter(model, fm_tab_page_path_filter, page);
586 fm_folder_model_apply_filters(model);
587 }
48be498f 588 fm_folder_view_set_model(fv, model);
9273d81a 589 fm_folder_model_set_sort(model, page->sort_by, page->sort_type);
48be498f
HJYP
590 g_object_unref(model);
591 }
592 else
f2e13d08 593#endif
48be498f 594 fm_folder_view_set_model(fv, NULL);
5dee1d40
HJYP
595}
596
14c144bc 597static gboolean update_scroll(gpointer data)
5dee1d40 598{
14c144bc
AG
599 FmTabPage* page = data;
600 GtkScrolledWindow* scroll = GTK_SCROLLED_WINDOW(page->folder_view);
d6eb3bac 601#if !FM_CHECK_VERSION(1, 0, 2)
5dee1d40 602 const FmNavHistoryItem* item;
14c144bc
AG
603
604 item = fm_nav_history_get_cur(page->nav_history);
605 /* scroll to recorded position */
606 gtk_adjustment_set_value(gtk_scrolled_window_get_vadjustment(scroll), item->scroll_pos);
607#else
608 gtk_adjustment_set_value(gtk_scrolled_window_get_vadjustment(scroll),
609 fm_nav_history_get_scroll_pos(page->nav_history));
d6eb3bac 610#endif
1b553f57
AG
611#if FM_CHECK_VERSION(1, 2, 0)
612 if (page->want_focus)
613 {
614 fm_folder_view_select_file_path(page->folder_view, page->want_focus);
615 fm_folder_view_scroll_to_path(page->folder_view, page->want_focus, TRUE);
616 fm_path_unref(page->want_focus);
617 page->want_focus = NULL;
618 }
619#endif
14c144bc
AG
620 page->update_scroll_id = 0;
621 return FALSE;
622}
623
624static void on_folder_finish_loading(FmFolder* folder, FmTabPage* page)
625{
626 FmFolderView* fv = page->folder_view;
5dee1d40 627
48be498f
HJYP
628 /* Note: most of the time, we delay the creation of the
629 * folder model and do it after the whole folder is loaded.
a861bb2a
AG
630 * That is because adding rows into model is much faster when no handlers
631 * are connected to its signals. So we detach the model from folder view
48be498f
HJYP
632 * and create the model again when it's fully loaded.
633 * This optimization, however, is not used for FmFolder objects
634 * with incremental loading (search://) */
635 if(fm_folder_view_get_model(fv) == NULL)
636 {
637 /* create a model for the folder and set it to the view */
9273d81a 638 FmFolderModel* model = fm_folder_model_new(folder, page->show_hidden);
48be498f 639 fm_folder_view_set_model(fv, model);
aabb3121 640#if FM_CHECK_VERSION(1, 0, 2)
87aa3463
AG
641 if (page->filter_pattern)
642 {
643 fm_folder_model_add_filter(model, fm_tab_page_path_filter, page);
644 fm_folder_model_apply_filters(model);
645 }
aabb3121 646 /* since 1.0.2 sorting should be applied on model instead of view */
9273d81a 647 fm_folder_model_set_sort(model, page->sort_by, page->sort_type);
aabb3121 648#endif
48be498f
HJYP
649 g_object_unref(model);
650 }
5dee1d40
HJYP
651 fm_folder_query_filesystem_info(folder); /* FIXME: is this needed? */
652
653 // fm_path_entry_set_path(entry, path);
14c144bc
AG
654 /* delaying scrolling since drawing folder view is delayed */
655 if(!page->update_scroll_id)
26ee30bf 656 page->update_scroll_id = gdk_threads_add_timeout(20, update_scroll, page);
5dee1d40
HJYP
657
658 /* update status bar */
659 /* update status text */
660 g_free(page->status_text[FM_STATUS_TEXT_NORMAL]);
661 page->status_text[FM_STATUS_TEXT_NORMAL] = format_status_text(page);
662 g_signal_emit(page, signals[STATUS], 0,
0b37d2a5
AG
663 (guint)FM_STATUS_TEXT_NORMAL,
664 page->status_text[FM_STATUS_TEXT_NORMAL]);
5dee1d40 665
326e4241 666 _tab_unset_busy_cursor(page);
0b997604 667 /* g_debug("finish-loading"); */
5dee1d40
HJYP
668}
669
670static void on_folder_unmount(FmFolder* folder, FmTabPage* page)
671{
01a3ef5a
AG
672 if (app_config->close_on_unmount)
673 gtk_widget_destroy(GTK_WIDGET(page));
674 else
675#if FM_CHECK_VERSION(1, 2, 0)
676 if (app_config->home_path && app_config->home_path[0])
677 {
678 FmPath *path = fm_path_new_for_str(app_config->home_path);
679
680 fm_tab_page_chdir(page, path);
681 fm_path_unref(path);
682 }
683 else
684#endif
685 fm_tab_page_chdir(page, fm_path_get_home());
5dee1d40
HJYP
686}
687
688static void on_folder_removed(FmFolder* folder, FmTabPage* page)
689{
01a3ef5a
AG
690 if (app_config->close_on_unmount)
691 gtk_widget_destroy(GTK_WIDGET(page));
692 else
693#if FM_CHECK_VERSION(1, 2, 0)
694 if (app_config->home_path && app_config->home_path[0])
695 {
696 FmPath *path = fm_path_new_for_str(app_config->home_path);
697
698 fm_tab_page_chdir(page, path);
699 fm_path_unref(path);
700 }
701 else
702#endif
703 fm_tab_page_chdir(page, fm_path_get_home());
5dee1d40
HJYP
704}
705
32f6ac22
HJYP
706static void on_folder_fs_info(FmFolder* folder, FmTabPage* page)
707{
708 guint64 free, total;
709 char* msg = page->status_text[FM_STATUS_TEXT_FS_INFO];
710 g_free(msg);
711 /* g_debug("%p, fs-info: %d", folder, (int)folder->has_fs_info); */
712 if(fm_folder_get_filesystem_info(folder, &total, &free))
713 {
714 char total_str[ 64 ];
715 char free_str[ 64 ];
8b85c2d6
AG
716 fm_file_size_to_str(free_str, sizeof(free_str), free, fm_config->si_unit);
717 fm_file_size_to_str(total_str, sizeof(total_str), total, fm_config->si_unit);
32f6ac22
HJYP
718 msg = g_strdup_printf(_("Free space: %s (Total: %s)"), free_str, total_str );
719 }
720 else
721 msg = NULL;
722 page->status_text[FM_STATUS_TEXT_FS_INFO] = msg;
723 g_signal_emit(page, signals[STATUS], 0,
724 (guint)FM_STATUS_TEXT_FS_INFO, msg);
725}
726
727static char* format_status_text(FmTabPage* page)
728{
729 FmFolderModel* model = fm_folder_view_get_model(page->folder_view);
730 FmFolder* folder = fm_folder_view_get_folder(page->folder_view);
731 if(model && folder)
732 {
2105792e 733 FmFileInfoList* files = fm_folder_get_files(folder);
32f6ac22 734 GString* msg = g_string_sized_new(128);
5a89e062 735 int total_files = fm_file_info_list_get_length(files);
32f6ac22
HJYP
736 int shown_files = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(model), NULL);
737 int hidden_files = total_files - shown_files;
738 const char* visible_fmt = ngettext("%d item", "%d items", shown_files);
739 const char* hidden_fmt = ngettext(" (%d hidden)", " (%d hidden)", hidden_files);
740
741 g_string_append_printf(msg, visible_fmt, shown_files);
742 if(hidden_files > 0)
743 g_string_append_printf(msg, hidden_fmt, hidden_files);
744 return g_string_free(msg, FALSE);
745 }
746 return NULL;
747}
748
3f8714d0
AG
749static void on_open_in_new_tab(GtkAction* act, FmMainWin* win)
750{
b22ddcea
AG
751 GObject* act_grp;
752 FmFileInfoList* sels;
3f8714d0 753 GList* l;
b22ddcea
AG
754
755 g_object_get(act, "action-group", &act_grp, NULL);
756 sels = g_object_get_qdata(act_grp, popup_qdata);
757 g_object_unref(act_grp);
758 for( l = fm_file_info_list_peek_head_link(sels); l; l=l->next )
3f8714d0 759 {
b22ddcea
AG
760 FmFileInfo* fi = (FmFileInfo*)l->data;
761 fm_main_win_add_tab(win, fm_file_info_get_path(fi));
3f8714d0 762 }
3f8714d0
AG
763}
764
765static void on_open_in_new_win(GtkAction* act, FmMainWin* win)
766{
b22ddcea
AG
767 GObject* act_grp;
768 FmFileInfoList* sels;
3f8714d0 769 GList* l;
b22ddcea
AG
770
771 g_object_get(act, "action-group", &act_grp, NULL);
772 sels = g_object_get_qdata(act_grp, popup_qdata);
773 g_object_unref(act_grp);
774 for( l = fm_file_info_list_peek_head_link(sels); l; l=l->next )
3f8714d0 775 {
b22ddcea
AG
776 FmFileInfo* fi = (FmFileInfo*)l->data;
777 fm_main_win_add_win(win, fm_file_info_get_path(fi));
3f8714d0 778 }
3f8714d0
AG
779}
780
781static void on_open_folder_in_terminal(GtkAction* act, FmMainWin* win)
782{
b22ddcea
AG
783 GObject* act_grp;
784 FmFileInfoList* files;
3f8714d0 785 GList* l;
b22ddcea
AG
786
787 g_object_get(act, "action-group", &act_grp, NULL);
788 files = g_object_get_qdata(act_grp, popup_qdata);
789 g_object_unref(act_grp);
3f8714d0
AG
790 for(l=fm_file_info_list_peek_head_link(files);l;l=l->next)
791 {
792 FmFileInfo* fi = (FmFileInfo*)l->data;
b22ddcea 793 pcmanfm_open_folder_in_terminal(GTK_WINDOW(win), fm_file_info_get_path(fi));
3f8714d0 794 }
3f8714d0
AG
795}
796
797/* folder view popups */
798static void update_files_popup(FmFolderView* fv, GtkWindow* win,
799 GtkUIManager* ui, GtkActionGroup* act_grp,
800 FmFileInfoList* files)
801{
f53d9b1c 802 GList* l;
93469438 803 gboolean all_native = TRUE;
f53d9b1c
AG
804
805 for(l = fm_file_info_list_peek_head_link(files); l; l = l->next)
806 if(!fm_file_info_is_dir(l->data))
807 return; /* actions are valid only if all selected are directories */
93469438
AG
808 else if (!fm_file_info_is_native(l->data))
809 all_native = FALSE;
b22ddcea
AG
810 g_object_set_qdata_full(G_OBJECT(act_grp), popup_qdata,
811 fm_file_info_list_ref(files),
812 (GDestroyNotify)fm_file_info_list_unref);
36f8e152 813 gtk_action_group_set_translation_domain(act_grp, NULL);
3f8714d0
AG
814 gtk_action_group_add_actions(act_grp, folder_menu_actions,
815 G_N_ELEMENTS(folder_menu_actions), win);
816 gtk_ui_manager_add_ui_from_string(ui, folder_menu_xml, -1, NULL);
93469438
AG
817 if (!all_native)
818 gtk_action_set_visible(gtk_action_group_get_action(act_grp, "Term"), FALSE);
3f8714d0
AG
819}
820
821static gboolean open_folder_func(GAppLaunchContext* ctx, GList* folder_infos, gpointer user_data, GError** err)
822{
823 FmMainWin* win = FM_MAIN_WIN(user_data);
824 GList* l = folder_infos;
825 FmFileInfo* fi = (FmFileInfo*)l->data;
826 fm_main_win_chdir(win, fm_file_info_get_path(fi));
827 l=l->next;
828 for(; l; l=l->next)
829 {
830 FmFileInfo* fi = (FmFileInfo*)l->data;
831 fm_main_win_add_tab(win, fm_file_info_get_path(fi));
832 }
833 return TRUE;
834}
835
b22ddcea
AG
836#if FM_CHECK_VERSION(1, 2, 0)
837void _update_sidepane_popup(FmSidePane* sp, GtkUIManager* ui,
838 GtkActionGroup* act_grp,
839 FmFileInfo* file, gpointer user_data)
840{
841 FmMainWin *win = GET_MAIN_WIN(user_data); /* user_data is FmTabPage */
842 FmFileInfoList* files;
843
844 /* bookmark may contain not a directory */
845 if (G_UNLIKELY(!file || !fm_file_info_is_dir(file)))
846 return;
847 /* well, it should be FmMainWin but let safeguard it */
848 if (G_UNLIKELY(!IS_FM_MAIN_WIN(win)))
849 return;
850 files = fm_file_info_list_new();
851 fm_file_info_list_push_tail(files, file);
852 g_object_set_qdata_full(G_OBJECT(act_grp), popup_qdata, files,
853 (GDestroyNotify)fm_file_info_list_unref);
854 gtk_action_group_set_translation_domain(act_grp, NULL);
855 gtk_action_group_add_actions(act_grp, folder_menu_actions,
856 G_N_ELEMENTS(folder_menu_actions), win);
857 /* we use the same XML for simplicity */
858 gtk_ui_manager_add_ui_from_string(ui, folder_menu_xml, -1, NULL);
93469438
AG
859 if (!fm_file_info_is_native(file))
860 gtk_action_set_visible(gtk_action_group_get_action(act_grp, "Term"), FALSE);
b22ddcea
AG
861}
862#endif
863
d830e211
AG
864static gboolean on_drag_motion(FmTabLabel *label, GdkDragContext *drag_context,
865 gint x, gint y, guint time, FmTabPage *page)
866{
867 GdkAtom target;
868 GdkDragAction action = 0;
869 FmFileInfo *file_info = NULL;
870
871 /* if change_tab_on_drop is set then we should ignore it and drop file
872 using classic behavior - drop after it unfolded, so not drop on label */
873 if (!app_config->change_tab_on_drop && page->folder_view)
874 file_info = fm_folder_view_get_cwd_info(page->folder_view);
875 fm_dnd_dest_set_dest_file(page->dd, file_info);
876 if (file_info == NULL)
877 return FALSE; /* not in drop zone */
878 target = fm_dnd_dest_find_target(page->dd, drag_context);
879 if (target != GDK_NONE && fm_dnd_dest_is_target_supported(page->dd, target))
880 action = fm_dnd_dest_get_default_action(page->dd, drag_context, target);
881 if (action == 0)
882 return FALSE; /* cannot drop on that destination */
883 gdk_drag_status(drag_context, action, time);
884 return TRUE;
885}
886
32f6ac22
HJYP
887static void fm_tab_page_init(FmTabPage *page)
888{
889 GtkPaned* paned = GTK_PANED(page);
890 FmTabLabel* tab_label;
9756804e 891 FmFolderView* folder_view;
3c969e76 892 GList* focus_chain = NULL;
ccc07608
AG
893 AtkObject *atk_widget, *atk_label;
894 AtkRelation *relation;
bc132e33 895 FmSidePaneMode mode = app_config->side_pane_mode;
9756804e 896
32f6ac22 897 page->side_pane = fm_side_pane_new();
bc132e33 898 fm_side_pane_set_mode(page->side_pane, (mode & FM_SP_MODE_MASK));
b22ddcea
AG
899#if FM_CHECK_VERSION(1, 2, 0)
900 fm_side_pane_set_popup_updater(page->side_pane, _update_sidepane_popup, page);
2e9d61cd
AG
901 if (app_config->home_path && app_config->home_path[0])
902 fm_side_pane_set_home_dir(page->side_pane, app_config->home_path);
903 g_signal_connect(app_config, "changed::home_path",
904 G_CALLBACK(on_home_path_changed), page->side_pane);
b22ddcea 905#endif
32f6ac22 906 /* TODO: add a close button to side pane */
3aebc679 907 gtk_paned_add1(paned, GTK_WIDGET(page->side_pane));
3c969e76 908 focus_chain = g_list_prepend(focus_chain, page->side_pane);
32f6ac22 909
0662d3cb
AG
910 /* setup initial view mode for the tab from configuration */
911 page->view_mode = app_config->view_mode;
912
3f8714d0
AG
913 /* handlers below will be used when FmMainWin detects new page added */
914 folder_view = (FmFolderView*)fm_standard_view_new(app_config->view_mode,
915 update_files_popup,
916 open_folder_func);
9273d81a
AG
917 /* FIXME: it is inefficient to set view mode to default one then change
918 it per-folder but it will be default in most cases but might it be
919 even more inefficient to add an object property for the mode and set
920 it in fm_tab_page_init() from the property? let make it later */
aedfc89d 921 page->folder_view = g_object_ref_sink(folder_view);
9756804e 922 fm_folder_view_set_selection_mode(folder_view, GTK_SELECTION_MULTIPLE);
32f6ac22 923 page->nav_history = fm_nav_history_new();
f8a192a0
AG
924 page->views = GTK_BOX(gtk_hbox_new(TRUE, 4));
925 gtk_box_pack_start(page->views, GTK_WIDGET(folder_view), TRUE, TRUE, 0);
926 gtk_paned_add2(paned, GTK_WIDGET(page->views));
927 focus_chain = g_list_prepend(focus_chain, page->views);
d185c65b 928
3c969e76 929 /* We need this to change tab order to focus folder view before left pane. */
3aebc679 930 gtk_container_set_focus_chain(GTK_CONTAINER(page), focus_chain);
3c969e76 931 g_list_free(focus_chain);
32f6ac22 932
326e4241
AG
933// gtk_widget_show_all(GTK_WIDGET(page));
934// if(mode & FM_SP_HIDE)
935// gtk_widget_hide(GTK_WIDGET(page->side_pane));
bc99152c 936
32f6ac22
HJYP
937 /* create tab label */
938 tab_label = (FmTabLabel*)fm_tab_label_new("");
939 gtk_label_set_max_width_chars(tab_label->label, app_config->max_tab_chars);
f17ed8cf 940#if ! GTK_CHECK_VERSION(3, 0, 0)
32f6ac22 941 gtk_label_set_ellipsize(tab_label->label, PANGO_ELLIPSIZE_END);
f17ed8cf 942#endif
3aebc679 943 page->tab_label = tab_label;
32f6ac22 944
ccc07608
AG
945 atk_widget = gtk_widget_get_accessible(GTK_WIDGET(folder_view));
946 atk_label = gtk_widget_get_accessible(GTK_WIDGET(tab_label));
947 relation = atk_relation_new(&atk_widget, 1, ATK_RELATION_LABEL_FOR);
948 atk_relation_set_add(atk_object_ref_relation_set(atk_label), relation);
949 g_object_unref(relation);
950
aedfc89d 951 g_signal_connect(folder_view, "sel-changed",
32f6ac22 952 G_CALLBACK(on_folder_view_sel_changed), page);
77bc3087
AG
953#if FM_CHECK_VERSION(1, 2, 0)
954 g_signal_connect(folder_view, "columns-changed",
955 G_CALLBACK(on_folder_view_columns_changed), page);
956#endif
f8a192a0 957 _connect_focus_in(folder_view, page);
5dee1d40
HJYP
958 /*
959 g_signal_connect(page->folder_view, "chdir",
960 G_CALLBACK(on_folder_view_chdir), page);
32f6ac22
HJYP
961 g_signal_connect(page->folder_view, "loaded",
962 G_CALLBACK(on_folder_view_loaded), page);
5dee1d40
HJYP
963 g_signal_connect(page->folder_view, "error",
964 G_CALLBACK(on_folder_view_error), page);
965 */
966
d830e211
AG
967 /* setup D&D on the tab label */
968 page->dd = fm_dnd_dest_new_with_handlers(GTK_WIDGET(tab_label));
969 g_signal_connect(tab_label, "drag-motion", G_CALLBACK(on_drag_motion), page);
970
9756804e 971 /* the folder view is already loded, call the "loaded" callback ourself. */
5dee1d40
HJYP
972 //if(fm_folder_view_is_loaded(folder_view))
973 // on_folder_view_loaded(folder_view, fm_folder_view_get_cwd(folder_view), page);
326e4241 974 page->busy = FALSE;
32f6ac22
HJYP
975}
976
3aebc679 977FmTabPage *fm_tab_page_new(FmPath* path)
32f6ac22
HJYP
978{
979 FmTabPage* page = (FmTabPage*)g_object_new(FM_TYPE_TAB_PAGE, NULL);
32f6ac22 980
9756804e 981 fm_tab_page_chdir(page, path);
32f6ac22
HJYP
982 return page;
983}
984
9756804e 985static void fm_tab_page_chdir_without_history(FmTabPage* page, FmPath* path)
32f6ac22 986{
32f6ac22 987 char* disp_name = fm_path_display_basename(path);
9273d81a
AG
988 char *disp_path;
989 FmStandardViewMode view_mode;
990 gboolean show_hidden;
991 char **columns; /* unused with libfm < 1.0.2 */
1b553f57
AG
992#if FM_CHECK_VERSION(1, 2, 0)
993 FmPath *prev_path = NULL;
994#endif
9273d81a 995
87aa3463
AG
996#if FM_CHECK_VERSION(1, 0, 2)
997 if (page->filter_pattern && page->filter_pattern[0])
998 {
999 /* include pattern into page title */
1000 char *text = g_strdup_printf("%s [%s]", disp_name, page->filter_pattern);
1001 g_free(disp_name);
1002 disp_name = text;
1003 }
1004#endif
3aebc679 1005 fm_tab_label_set_text(page->tab_label, disp_name);
32f6ac22
HJYP
1006 g_free(disp_name);
1007
1b553f57
AG
1008#if FM_CHECK_VERSION(1, 2, 0)
1009 if (app_config->focus_previous && page->folder)
1010 {
1011 prev_path = fm_folder_get_path(page->folder);
1012 if (fm_path_equal(fm_path_get_parent(prev_path), path))
1013 fm_path_ref(prev_path);
1014 else
1015 prev_path = NULL;
1016 }
1017#endif
1018
9273d81a 1019 disp_path = fm_path_display_name(path, FALSE);
1238e8d0
VU
1020 fm_tab_label_set_tooltip_text(FM_TAB_LABEL(page->tab_label), disp_path);
1021 g_free(disp_path);
1022
5dee1d40 1023 free_folder(page);
32f6ac22 1024
5a89e062 1025 page->folder = fm_folder_from_path(path);
5dee1d40
HJYP
1026 g_signal_connect(page->folder, "start-loading", G_CALLBACK(on_folder_start_loading), page);
1027 g_signal_connect(page->folder, "finish-loading", G_CALLBACK(on_folder_finish_loading), page);
1028 g_signal_connect(page->folder, "error", G_CALLBACK(on_folder_error), page);
1029 g_signal_connect(page->folder, "fs-info", G_CALLBACK(on_folder_fs_info), page);
1030 /* destroy the page when the folder is unmounted or deleted. */
1031 g_signal_connect(page->folder, "removed", G_CALLBACK(on_folder_removed), page);
1032 g_signal_connect(page->folder, "unmount", G_CALLBACK(on_folder_unmount), page);
1033 g_signal_connect(page->folder, "content-changed", G_CALLBACK(on_folder_content_changed), page);
1034
1b553f57
AG
1035#if FM_CHECK_VERSION(1, 2, 0)
1036 page->want_focus = prev_path;
1037#endif
1038
9273d81a
AG
1039 /* get sort and view modes for new path */
1040 page->own_config = fm_app_config_get_config_for_path(path, &page->sort_type,
1041 &page->sort_by,
1042 &view_mode,
1043 &show_hidden, &columns);
0662d3cb
AG
1044 if (!page->own_config)
1045 /* bug #3615242: view mode is reset to default when changing directory */
1046 view_mode = page->view_mode;
9273d81a
AG
1047 page->show_hidden = show_hidden;
1048 fm_folder_view_set_show_hidden(page->folder_view, show_hidden);
888407bd
AG
1049#if FM_CHECK_VERSION(1, 2, 0)
1050 fm_side_pane_set_show_hidden(page->side_pane, show_hidden);
1051#endif
9273d81a 1052
5dee1d40 1053 if(fm_folder_is_loaded(page->folder))
32f6ac22 1054 {
48be498f 1055 on_folder_start_loading(page->folder, page);
5dee1d40
HJYP
1056 on_folder_finish_loading(page->folder, page);
1057 on_folder_fs_info(page->folder, page);
32f6ac22 1058 }
5dee1d40
HJYP
1059 else
1060 on_folder_start_loading(page->folder, page);
32f6ac22 1061
9273d81a
AG
1062 /* change view and sort modes according to new path */
1063 fm_standard_view_set_mode(FM_STANDARD_VIEW(page->folder_view), view_mode);
1064#if FM_CHECK_VERSION(1, 0, 2)
1065 /* update columns from config */
1066 if (columns)
1067 {
1068 guint i, n = g_strv_length(columns);
1069 FmFolderViewColumnInfo *infos = g_new(FmFolderViewColumnInfo, n);
1070 GSList *infos_list = NULL;
1071
1072 for (i = 0; i < n; i++)
1073 {
1074 char *name = g_strdup(columns[i]), *delim;
1075
d5ce0128 1076#if FM_CHECK_VERSION(1, 2, 0)
9273d81a 1077 infos[i].width = 0;
d5ce0128 1078#endif
9273d81a
AG
1079 delim = strchr(name, ':');
1080 if (delim)
1081 {
1082 *delim++ = '\0';
d5ce0128 1083#if FM_CHECK_VERSION(1, 2, 0)
9273d81a 1084 infos[i].width = atoi(delim);
d5ce0128 1085#endif
9273d81a
AG
1086 }
1087 infos[i].col_id = fm_folder_model_get_col_by_name(name);
1088 g_free(name);
1089 infos_list = g_slist_append(infos_list, &infos[i]);
1090 }
1091#if FM_CHECK_VERSION(1, 2, 0)
1092 g_signal_handlers_block_by_func(page->folder_view,
1093 on_folder_view_columns_changed, page);
1094#endif
1095 fm_folder_view_set_columns(page->folder_view, infos_list);
1096#if FM_CHECK_VERSION(1, 2, 0)
1097 g_signal_handlers_unblock_by_func(page->folder_view,
1098 on_folder_view_columns_changed, page);
1099#endif
1100 g_slist_free(infos_list);
1101 g_free(infos);
1102 if (page->own_config)
1103 page->columns = g_strdupv(columns);
1104 }
1105#else
1106 /* since 1.0.2 sorting should be applied on model instead */
1107 fm_folder_view_sort(page->folder_view, page->sort_type, page->sort_by);
1108#endif
1109
3aebc679 1110 fm_side_pane_chdir(page->side_pane, path);
32f6ac22 1111
9756804e
HJYP
1112 /* tell the world that our current working directory is changed */
1113 g_signal_emit(page, signals[CHDIR], 0, path);
1114}
1115
1116void fm_tab_page_chdir(FmTabPage* page, FmPath* path)
1117{
5dee1d40
HJYP
1118 FmPath* cwd = fm_tab_page_get_cwd(page);
1119 int scroll_pos;
1120 if(cwd && path && fm_path_equal(cwd, path))
1121 return;
1122 scroll_pos = gtk_adjustment_get_value(gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(page->folder_view)));
9756804e
HJYP
1123 fm_nav_history_chdir(page->nav_history, path, scroll_pos);
1124 fm_tab_page_chdir_without_history(page, path);
32f6ac22
HJYP
1125}
1126
1127void fm_tab_page_set_show_hidden(FmTabPage* page, gboolean show_hidden)
1128{
3aebc679 1129 fm_folder_view_set_show_hidden(page->folder_view, show_hidden);
888407bd
AG
1130#if FM_CHECK_VERSION(1, 2, 0)
1131 fm_side_pane_set_show_hidden(page->side_pane, show_hidden);
1132#endif
32f6ac22
HJYP
1133 /* update status text */
1134 g_free(page->status_text[FM_STATUS_TEXT_NORMAL]);
1135 page->status_text[FM_STATUS_TEXT_NORMAL] = format_status_text(page);
1136 g_signal_emit(page, signals[STATUS], 0,
0b37d2a5
AG
1137 (guint)FM_STATUS_TEXT_NORMAL,
1138 page->status_text[FM_STATUS_TEXT_NORMAL]);
32f6ac22
HJYP
1139}
1140
1141FmPath* fm_tab_page_get_cwd(FmTabPage* page)
1142{
5dee1d40 1143 return page->folder ? fm_folder_get_path(page->folder) : NULL;
32f6ac22
HJYP
1144}
1145
3aebc679 1146FmSidePane* fm_tab_page_get_side_pane(FmTabPage* page)
32f6ac22
HJYP
1147{
1148 return page->side_pane;
1149}
1150
3aebc679 1151FmFolderView* fm_tab_page_get_folder_view(FmTabPage* page)
32f6ac22
HJYP
1152{
1153 return page->folder_view;
1154}
1155
1156FmFolder* fm_tab_page_get_folder(FmTabPage* page)
1157{
3aebc679 1158 return fm_folder_view_get_folder(page->folder_view);
32f6ac22
HJYP
1159}
1160
1161FmNavHistory* fm_tab_page_get_history(FmTabPage* page)
1162{
1163 return page->nav_history;
1164}
1165
1166void fm_tab_page_forward(FmTabPage* page)
1167{
d6eb3bac
AG
1168#if FM_CHECK_VERSION(1, 0, 2)
1169 guint index = fm_nav_history_get_cur_index(page->nav_history);
1170
1171 if (index > 0)
1172#else
5a89e062 1173 if(fm_nav_history_can_forward(page->nav_history))
d6eb3bac 1174#endif
32f6ac22 1175 {
d6eb3bac 1176#if !FM_CHECK_VERSION(1, 0, 2)
3aebc679 1177 const FmNavHistoryItem* item;
d6eb3bac 1178#endif
32f6ac22
HJYP
1179 GtkAdjustment* vadjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(page->folder_view));
1180 int scroll_pos = gtk_adjustment_get_value(vadjustment);
d6eb3bac
AG
1181#if FM_CHECK_VERSION(1, 0, 2)
1182 FmPath *path = fm_nav_history_go_to(page->nav_history, index - 1, scroll_pos);
1183 fm_tab_page_chdir_without_history(page, path);
1184#else
32f6ac22 1185 fm_nav_history_forward(page->nav_history, scroll_pos);
32f6ac22 1186 item = fm_nav_history_get_cur(page->nav_history);
9756804e 1187 fm_tab_page_chdir_without_history(page, item->path);
d6eb3bac 1188#endif
32f6ac22
HJYP
1189 }
1190}
1191
1192void fm_tab_page_back(FmTabPage* page)
1193{
5a89e062 1194 if(fm_nav_history_can_back(page->nav_history))
32f6ac22 1195 {
d6eb3bac 1196#if !FM_CHECK_VERSION(1, 0, 2)
3aebc679 1197 const FmNavHistoryItem* item;
d6eb3bac 1198#endif
32f6ac22
HJYP
1199 GtkAdjustment* vadjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(page->folder_view));
1200 int scroll_pos = gtk_adjustment_get_value(vadjustment);
d6eb3bac
AG
1201#if FM_CHECK_VERSION(1, 0, 2)
1202 guint index = fm_nav_history_get_cur_index(page->nav_history);
1203 FmPath *path = fm_nav_history_go_to(page->nav_history, index + 1, scroll_pos);
1204 fm_tab_page_chdir_without_history(page, path);
1205#else
32f6ac22
HJYP
1206 fm_nav_history_back(page->nav_history, scroll_pos);
1207 item = fm_nav_history_get_cur(page->nav_history);
9756804e 1208 fm_tab_page_chdir_without_history(page, item->path);
d6eb3bac 1209#endif
32f6ac22
HJYP
1210 }
1211}
1212
d6eb3bac
AG
1213#if FM_CHECK_VERSION(1, 0, 2)
1214void fm_tab_page_history(FmTabPage* page, guint history_item)
1215{
1216 GtkAdjustment* vadjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(page->folder_view));
1217 int scroll_pos = gtk_adjustment_get_value(vadjustment);
1218 FmPath *path = fm_nav_history_go_to(page->nav_history, history_item, scroll_pos);
1219 fm_tab_page_chdir_without_history(page, path);
1220}
1221#else
32f6ac22
HJYP
1222void fm_tab_page_history(FmTabPage* page, GList* history_item_link)
1223{
32f6ac22
HJYP
1224 const FmNavHistoryItem* item = (FmNavHistoryItem*)history_item_link->data;
1225 GtkAdjustment* vadjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(page->folder_view));
1226 int scroll_pos = gtk_adjustment_get_value(vadjustment);
1227 fm_nav_history_jump(page->nav_history, history_item_link, scroll_pos);
1228 item = fm_nav_history_get_cur(page->nav_history);
9756804e 1229 fm_tab_page_chdir_without_history(page, item->path);
32f6ac22 1230}
d6eb3bac 1231#endif
32f6ac22
HJYP
1232
1233const char* fm_tab_page_get_title(FmTabPage* page)
1234{
3aebc679 1235 FmTabLabel* label = page->tab_label;
32f6ac22
HJYP
1236 return gtk_label_get_text(label->label);
1237}
1238
1239const char* fm_tab_page_get_status_text(FmTabPage* page, FmStatusTextType type)
1240{
6f0f1950 1241 return (type < FM_STATUS_TEXT_NUM) ? page->status_text[type] : NULL;
32f6ac22 1242}
813241d6
HJYP
1243
1244void fm_tab_page_reload(FmTabPage* page)
1245{
3aebc679 1246 FmFolder* folder = fm_folder_view_get_folder(page->folder_view);
14c144bc 1247
813241d6 1248 if(folder)
a92f65e4
AG
1249 {
1250 GtkAdjustment* vadjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(page->folder_view));
1251 int scroll_pos = gtk_adjustment_get_value(vadjustment);
1252 /* save the scroll position before reload */
1253#if FM_CHECK_VERSION(1, 0, 2)
1254 int idx = fm_nav_history_get_cur_index(page->nav_history);
1255 fm_nav_history_go_to(page->nav_history, idx, scroll_pos);
1256#else
b8c3393f
AG
1257 FmNavHistoryItem* item = (FmNavHistoryItem*)fm_nav_history_get_cur(page->nav_history);
1258 /* NOTE: ignoring const modifier due to invalid pre-1.0.2 design */
a92f65e4
AG
1259 item->scroll_pos = scroll_pos;
1260#endif
813241d6 1261 fm_folder_reload(folder);
a92f65e4 1262 }
813241d6 1263}
f8a192a0
AG
1264
1265/**
1266 * fm_tab_page_take_view_back
1267 * @page: the page instance
1268 *
1269 * If folder view that is bound to page isn't present in the container,
1270 * then moves it from the container where it is currently, to the @page.
1271 * This API should be called only in single-pane mode, for two-pane use
1272 * fm_tab_page_set_passive_view() instead.
1273 *
1274 * Returns: %TRUE if folder was not in @page and moved successfully.
1275 */
1276gboolean fm_tab_page_take_view_back(FmTabPage *page)
1277{
1278 GList *panes, *l;
1279 GtkWidget *folder_view = GTK_WIDGET(page->folder_view);
1280
1281 gtk_widget_set_state(folder_view, GTK_STATE_NORMAL);
1282 panes = gtk_container_get_children(GTK_CONTAINER(page->views));
1283 for (l = panes; l; l = l->next)
1284 if ((GtkWidget*)l->data == folder_view)
1285 break;
1286 g_list_free(panes);
1287 if (l)
1288 return FALSE;
1289 /* we already keep the reference, no need to do it again */
1290 gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(folder_view)),
1291 folder_view);
1292 gtk_box_pack_start(page->views, folder_view, TRUE, TRUE, 0);
1293 return TRUE;
1294}
1295
1296/**
1297 * fm_tab_page_set_passive_view
1298 * @page: the page instance
1299 * @view: the folder view to add
1300 * @on_right: %TRUE if @view should be moved to right pane
1301 *
1302 * If folder @view isn't already in designed place then moves it from the
1303 * container where it is currently, to the @page. If @on_right is %TRUE
1304 * then attempts to move into right pane. If @on_right is %FALSE then
1305 * attempts to move into left pane.
1306 *
1307 * Also if folder view that is bound to @page isn't presented in the
1308 * container, then moves it from the container where it is currently, to
1309 * the @page on the side opposite to @view.
1310 *
1311 * See also: fm_tab_page_take_view_back().
1312 *
1313 * Returns: %TRUE if @view was moved successfully.
1314 */
1315gboolean fm_tab_page_set_passive_view(FmTabPage *page, FmFolderView *view,
1316 gboolean on_right)
1317{
1318 GtkWidget *pane;
1319
1320 g_return_val_if_fail(page != NULL && view != NULL, FALSE);
1321 if (!fm_tab_page_take_view_back(page))
1322 {
f02fd435 1323#if !FM_CHECK_VERSION(1, 2, 0)
f8a192a0
AG
1324 /* workaround on ExoIconView bug - it doesn't follow state change
1325 so we re-add the folder view into our container to force change */
1326 GtkWidget *fv = GTK_WIDGET(page->folder_view);
1327
1328 /* we already keep the reference, no need to do it again */
1329 gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(fv)), fv);
1330 gtk_box_pack_start(page->views, fv, TRUE, TRUE, 0);
1331#endif
1332 }
1333 pane = GTK_WIDGET(view);
1334 gtk_widget_set_state(pane, GTK_STATE_ACTIVE);
1335 g_object_ref(view);
1336 /* gtk_widget_reparent() is buggy so we do it manually */
1337 if (gtk_widget_get_parent(pane))
1338 gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(pane)), pane);
1339 gtk_box_pack_start(page->views, pane, TRUE, TRUE, 0);
1340 g_object_unref(view);
1341 if (!on_right)
1342 gtk_box_reorder_child(page->views, pane, 0);
1343 return TRUE;
1344}
1345
1346/**
1347 * fm_tab_page_get_passive_view
1348 * @page: the page instance
1349 *
1350 * Checks if the @page contains some folder view that was added via call
1351 * to fm_tab_page_set_passive_view() and was not moved out yet.
1352 *
1353 * Returns: (transfer none): the folder view or %NULL.
1354 */
1355FmFolderView *fm_tab_page_get_passive_view(FmTabPage *page)
1356{
1357 GList *panes, *l;
1358 FmFolderView *view;
1359
1360 panes = gtk_container_get_children(GTK_CONTAINER(page->views));
1361 for (l = panes; l; l = l->next)
1362 if ((FmFolderView*)l->data != page->folder_view)
1363 break;
1364 view = l ? l->data : NULL;
1365 g_list_free(panes);
1366 return view;
1367}
87aa3463
AG
1368
1369#if FM_CHECK_VERSION(1, 0, 2)
1370/**
1371 * fm_tab_page_set_filter_pattern
1372 * @page: the page instance
1373 * @pattern: (allow-none): new pattern
1374 *
1375 * Changes filter for the folder view in the @page. If @pattern is %NULL
1376 * then folder contents will be not filtered anymore.
1377 */
1378void fm_tab_page_set_filter_pattern(FmTabPage *page, const char *pattern)
1379{
1380 FmFolderModel *model = NULL;
1381 char *disp_name;
1382
1383 /* validate pattern */
1384 if (pattern && pattern[0] == '\0')
1385 pattern = NULL;
1386 if (page->folder_view != NULL)
1387 model = fm_folder_view_get_model(page->folder_view);
1388 if (page->filter_pattern == NULL && pattern == NULL)
1389 return; /* nothing to change */
1390 /* if we have model then update filter chain in it */
1391 if (model)
1392 {
1393 if (page->filter_pattern == NULL && pattern)
1394 fm_folder_model_add_filter(model, fm_tab_page_path_filter, page);
1395 else if (page->filter_pattern && pattern == NULL)
1396 fm_folder_model_remove_filter(model, fm_tab_page_path_filter, page);
1397 }
1398 /* update page own data */
1399 g_free(page->filter_pattern);
1400 if (pattern)
1401 {
1402 char *casefold = g_utf8_casefold(pattern, -1);
1403 page->filter_pattern = g_utf8_normalize(casefold, -1, G_NORMALIZE_ALL);
1404 g_free(casefold);
1405 }
1406 else
1407 page->filter_pattern = NULL;
1408 /* apply changes if needed */
1409 if (model)
1410 fm_folder_model_apply_filters(model);
1411 /* update tab page title */
1412 disp_name = fm_path_display_basename(fm_folder_view_get_cwd(page->folder_view));
1413 if (page->filter_pattern)
1414 {
1415 /* include pattern into page title */
1416 char *text = g_strdup_printf("%s [%s]", disp_name, page->filter_pattern);
1417 g_free(disp_name);
1418 disp_name = text;
1419 }
1420 fm_tab_label_set_text(page->tab_label, disp_name);
1421 g_free(disp_name);
1422}
1423#endif