Compare commits

...

No commits in common. "usagebars" and "d5e2daaf6a880ba25253b89e1999df91428a8cbb" have entirely different histories.

80 changed files with 35245 additions and 22962 deletions

96
NEWS
View file

@ -1,3 +1,99 @@
4.20.7 (2026-01-23)
======
- Prevent crash on unmount (#1778)
- Show 'queued' message only for queued jobs (#1755)
- Prevent crash on non-local symlinks (#1757)
- Fix g_object_unref warning
- Fix symlink resolve for desktop files (#1757)
- Translation Updates:
Albanian, Arabic, Asturian, Basque, Belarusian, Bulgarian, Catalan,
Chinese (China), Chinese (Taiwan), Czech, Danish, Dutch, English
(United Kingdom), Estonian, Finnish, French, Galician, Georgian,
German, Greek, Hungarian, Indonesian, Interlingue, Japanese, Kazakh,
Malay, Norwegian Bokmål, Occitan (post 1500), Polish, Portuguese
(Brazil), Romanian, Serbian, Slovenian, Spanish, Swedish, Thai,
Turkish, Venetian, Vietnamese
4.20.6 (2025-10-16)
======
- Prevent GFileMonitor leak for folders (#1724)
- Don't use variables for strings in ngettext (#1683)
- Fix 'MOUNTED' state on file reload when unmounted
- Translation Updates:
Albanian, Arabic, Armenian, Armenian (Armenia), Asturian, Basque,
Belarusian, Bengali, Bulgarian, Catalan, Chinese (China), Chinese
(Hong Kong), Chinese (Taiwan), Czech, Danish, Dutch, Eastern
Armenian, English (Australia), English (United Kingdom), Esperanto,
Estonian, Finnish, French, Galician, Georgian, German, Greek,
Hungarian, Icelandic, Indonesian, Interlingue, Japanese, Kazakh,
Latvian, Malay, Norwegian Bokmål, Norwegian Nynorsk, Occitan (post
1500), Persian (Iran), Polish, Portuguese (Brazil), Romanian,
Serbian, Slovenian, Spanish, Swedish, Thai, Turkish, Venetian,
Vietnamese
4.20.5 (2025-09-14)
======
- Update shortcut model asynchronously (#1022)
- Clear selection before updating selection (#1672)
- Translation Updates:
Chinese (China), English (United Kingdom), Finnish, Greek, Occitan
(post 1500), Polish
4.20.4 (2025-07-18)
======
- Improve file add/remove checks for ThunarFolder (#1649)
- Properly update view after hidden file rename
- Always reload ThunarFiles on change notice (#1650)
- Directly notify after file reload (#1650)
- Set initial state of "Restore" button (#1663)
- Fix quoting when running shell scripts in a terminal (#1661)
- Avoid use-after-free in rename dialog when file is changed
- Fix a GFile leak in _thunar_io_jobs_rename
- Fix use-after-free in thunar_renamer_dialog_response (#1458)
- Fix use-after-free on exit with search tabs open (#1593)
- Translation Updates:
Arabic, Chinese (China), Danish, Esperanto, Galician, Georgian,
Greek, Norwegian Bokmål, Polish, Portuguese (Brazil), Spanish,
Turkish, Vietnamese
4.20.3 (2025-05-16)
======
- Always warn user before permanent deleting files
- Fix string leaks when UCAs use a submenu
- Fix a potential leak in thunar_action_manager_append_custom_actions
- Fix leaks with ThunarMenuX objects
- Fix thunarx_menu_get_items leak when multiple UCAs share a submenu
- Fix refresh on row-changed in list view (#1584)
- Add missing NULL check before unref call
- Fix popup position of DnD menu
- UCA: Correct PWD for submenu folder items (#1615)
- wayland: Fix popup menu not closing (#1592)
- Check if file exists before opening its location (#1257)
- Prevent popups on wrong window (#1591)
- thunar-tpa: Fix libxfce4panel include
- Prevent criticals when switching current view (#1344)
- Fix crash when destroying folder in list view (#1330) (#1568)
- Fix crash in properties dialog (#1585)
- Fix invalid filenames when copying to exFAT FS (#1570)
- Fix typo in preferences dialog
- Hide shortcuts editor when accel map uninitialized (#1488)
- Update statusbar when searching (#1560)
- Always update statusbar on file changes in list view (#1560)
- Fix item activation on double-click in list view (#1567)
- Use 'malloc_trim' after search (#1552)
- Fix missing dialog for folder errors in list view (#1538)
- Fix leak on search update
- Fix missing dialog window icon (#1506)
- Fix leaks on "cancel search"
- Properly check if a file can be trashed (#1554)
- Translation Updates:
Albanian, Bulgarian, Catalan, Chinese (China), Chinese (Taiwan),
Czech, Danish, Dutch, English (United Kingdom), Estonian, Finnish,
French, Georgian, German, Greek, Hebrew, Hungarian, Italian,
Japanese, Norwegian Bokmål, Occitan (post 1500), Polish, Portuguese,
Portuguese (Brazil), Romanian, Serbian, Slovenian, Swedish, Turkish,
Venetian, Vietnamese
4.20.2 (2025-02-10)
======
- Fix wrong selection after rename in list view (#1415)

View file

@ -25,7 +25,7 @@ dnl ***************************
m4_define([thunarx_verinfo], [0:0:0])
m4_define([thunarx_version_api], [3])
XDT_VERSION_INIT([4.20.2])
XDT_VERSION_INIT([4.20.7])
dnl ***************************
dnl *** Initialize autoconf ***
@ -123,7 +123,7 @@ AC_SYS_LARGEFILE()
dnl **********************************
dnl *** Check for standard headers ***
dnl **********************************
AC_CHECK_HEADERS([ctype.h errno.h fcntl.h grp.h limits.h locale.h memory.h \
AC_CHECK_HEADERS([ctype.h errno.h fcntl.h grp.h limits.h locale.h malloc.h memory.h \
paths.h pwd.h sched.h signal.h stdarg.h stdlib.h string.h \
sys/mman.h sys/param.h sys/stat.h sys/time.h sys/types.h \
sys/uio.h sys/wait.h time.h])

View file

@ -25,7 +25,6 @@
#include <glib.h>
#include <gtk/gtk.h>
#include <libxfce4panel/libxfce4panel.h>
#include <libxfce4panel/xfce-panel-macros.h>
#include <libxfce4ui/libxfce4ui.h>
#include <libxfce4util/libxfce4util.h>
#include <thunar-tpa/thunar-tpa-bindings.h>

View file

@ -222,12 +222,13 @@ find_submenu_by_name (gchar *name, GList *items)
if (thunarx_menu_items != NULL)
{
ThunarxMenu *submenu = find_submenu_by_name (name, thunarx_menu_items);
thunarx_menu_item_list_free (thunarx_menu_items);
if (submenu != NULL)
{
g_free (item_name);
return submenu;
}
thunarx_menu_item_list_free (thunarx_menu_items);
}
}
g_free (item_name);
@ -297,7 +298,11 @@ thunar_uca_provider_get_file_menu_items (ThunarxMenuProvider *menu_provider,
gchar *sub_menu_path = g_strdup (sub_menus_as_array[0]);
for (int j = 1; j <= i; j++)
sub_menu_path = g_strconcat (sub_menu_path, "/", sub_menus_as_array[j], NULL);
{
gchar *prev_path = sub_menu_path;
sub_menu_path = g_strconcat (prev_path, "/", sub_menus_as_array[j], NULL);
g_free (prev_path);
}
/* Check if the full path already exists */
submenu = find_submenu_by_name (sub_menu_path, items);
@ -381,6 +386,36 @@ thunar_uca_provider_get_file_menu_items (ThunarxMenuProvider *menu_provider,
static void
set_folder_quarks_recursive (GList *items)
{
GList *lp;
GList *thunarx_menu_items;
for (lp = g_list_first (items); lp != NULL; lp = lp->next)
{
ThunarxMenu *item_menu = NULL;
g_object_get (G_OBJECT (lp->data), "menu", &item_menu, NULL);
if (item_menu != NULL)
{
/* Some submenu found .. lets walk through it recursively */
thunarx_menu_items = thunarx_menu_get_items (item_menu);
g_object_unref (item_menu);
if (thunarx_menu_items != NULL)
{
set_folder_quarks_recursive (thunarx_menu_items);
thunarx_menu_item_list_free (thunarx_menu_items);
}
}
else
g_object_set_qdata (G_OBJECT (lp->data), thunar_uca_folder_quark, GUINT_TO_POINTER (TRUE));
}
}
static GList *
thunar_uca_provider_get_folder_menu_items (ThunarxMenuProvider *menu_provider,
GtkWidget *window,
@ -388,7 +423,6 @@ thunar_uca_provider_get_folder_menu_items (ThunarxMenuProvider *menu_provider,
{
GList *items;
GList files;
GList *lp;
/* fake a file list... */
files.data = folder;
@ -398,9 +432,8 @@ thunar_uca_provider_get_folder_menu_items (ThunarxMenuProvider *menu_provider,
/* ...and use the get_file_menu_items() method */
items = thunarx_menu_provider_get_file_menu_items (menu_provider, window, &files);
/* mark the menu items, so we can properly detect the working directory */
for (lp = items; lp != NULL; lp = lp->next)
g_object_set_qdata (G_OBJECT (lp->data), thunar_uca_folder_quark, GUINT_TO_POINTER (TRUE));
/* mark the menu items as folder menu items, so we can properly detect the working directory */
set_folder_quarks_recursive (items);
return items;
}

View file

@ -31,6 +31,7 @@ ie
is
it
ja
ka
kk
ko
lt
@ -59,6 +60,7 @@ ug
uk
ur_PK
ur
vec
vi
zh_CN
zh_HK

1308
po/ar.po

File diff suppressed because it is too large Load diff

887
po/ast.po

File diff suppressed because it is too large Load diff

896
po/be.po

File diff suppressed because it is too large Load diff

889
po/bg.po

File diff suppressed because it is too large Load diff

862
po/bn.po

File diff suppressed because it is too large Load diff

943
po/ca.po

File diff suppressed because it is too large Load diff

897
po/cs.po

File diff suppressed because it is too large Load diff

887
po/da.po

File diff suppressed because it is too large Load diff

888
po/de.po

File diff suppressed because it is too large Load diff

926
po/el.po

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

882
po/eo.po

File diff suppressed because it is too large Load diff

895
po/es.po

File diff suppressed because it is too large Load diff

961
po/et.po

File diff suppressed because it is too large Load diff

888
po/eu.po

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

889
po/fi.po

File diff suppressed because it is too large Load diff

895
po/fr.po

File diff suppressed because it is too large Load diff

885
po/gl.po

File diff suppressed because it is too large Load diff

559
po/he.po

File diff suppressed because it is too large Load diff

891
po/hu.po

File diff suppressed because it is too large Load diff

879
po/hy.po

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

879
po/hye.po

File diff suppressed because it is too large Load diff

1061
po/id.po

File diff suppressed because it is too large Load diff

888
po/ie.po

File diff suppressed because it is too large Load diff

880
po/is.po

File diff suppressed because it is too large Load diff

685
po/it.po

File diff suppressed because it is too large Load diff

880
po/ja.po

File diff suppressed because it is too large Load diff

5017
po/ka.po Normal file

File diff suppressed because it is too large Load diff

892
po/kk.po

File diff suppressed because it is too large Load diff

866
po/lv.po

File diff suppressed because it is too large Load diff

884
po/ms.po

File diff suppressed because it is too large Load diff

887
po/nb.po

File diff suppressed because it is too large Load diff

889
po/nl.po

File diff suppressed because it is too large Load diff

880
po/nn.po

File diff suppressed because it is too large Load diff

1127
po/oc.po

File diff suppressed because it is too large Load diff

1193
po/pl.po

File diff suppressed because it is too large Load diff

671
po/pt.po

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

895
po/ro.po

File diff suppressed because it is too large Load diff

962
po/sl.po

File diff suppressed because it is too large Load diff

889
po/sq.po

File diff suppressed because it is too large Load diff

892
po/sr.po

File diff suppressed because it is too large Load diff

887
po/sv.po

File diff suppressed because it is too large Load diff

1474
po/th.po

File diff suppressed because it is too large Load diff

892
po/tr.po

File diff suppressed because it is too large Load diff

5148
po/vec.po Normal file

File diff suppressed because it is too large Load diff

1211
po/vi.po

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -80,7 +80,7 @@ main (int argc, char **argv)
application = thunar_application_get ();
/* use the Thunar icon as default for new windows */
gtk_window_set_default_icon_name ("Thunar");
gtk_window_set_default_icon_name ("org.xfce.thunar");
/* do further processing inside gapplication */
g_application_run (G_APPLICATION (application), argc, argv);

View file

@ -1525,7 +1525,17 @@ thunar_action_manager_action_open_location (ThunarActionManager *action_mgr)
return TRUE;
for (lp = action_mgr->files_to_process; lp != NULL; lp = lp->next)
gfiles = g_list_prepend (gfiles, thunar_file_get_file (THUNAR_FILE (lp->data)));
{
ThunarFile *file = lp->data;
if (thunar_file_exists (file))
gfiles = g_list_prepend (gfiles, thunar_file_get_file (file));
else
{
GError *error = g_error_new (G_FILE_ERROR, G_FILE_ERROR_NOENT, _("File does not exist anymore"));
thunar_dialogs_show_error (action_mgr->widget, error, _("Failed to open location for \"%s\""), thunar_file_get_display_name (file));
g_error_free (error);
}
}
thunar_window_open_files_in_location (THUNAR_WINDOW (action_mgr->widget), gfiles);
@ -1658,7 +1668,7 @@ thunar_action_manager_append_menu_item (ThunarActionManager *action_mgr,
case THUNAR_ACTION_MANAGER_ACTION_OPEN_IN_TAB:
n = action_mgr->n_files_to_process > 0 ? action_mgr->n_files_to_process : 1;
label_text = g_strdup_printf (ngettext (action_entry->menu_item_label_text, "Open in %d New _Tabs", n), n);
label_text = g_strdup_printf (ngettext ("Open in new _Tab", "Open in %d New _Tabs", n), n);
tooltip_text = g_strdup_printf (ngettext ("Open the selected directory in new tab",
"Open the selected directories in %d new tabs", n),
n);
@ -1669,7 +1679,7 @@ thunar_action_manager_append_menu_item (ThunarActionManager *action_mgr,
case THUNAR_ACTION_MANAGER_ACTION_OPEN_IN_WINDOW:
n = action_mgr->n_files_to_process > 0 ? action_mgr->n_files_to_process : 1;
label_text = g_strdup_printf (ngettext (action_entry->menu_item_label_text, "Open in %d New _Windows", n), n);
label_text = g_strdup_printf (ngettext ("Open in new _Window", "Open in %d New _Windows", n), n);
tooltip_text = g_strdup_printf (ngettext ("Open the selected directory in new window",
"Open the selected directories in %d new windows", n),
n);
@ -1707,7 +1717,7 @@ thunar_action_manager_append_menu_item (ThunarActionManager *action_mgr,
if (!show_item && !force)
return NULL;
label_text = ngettext (action_entry->menu_item_label_text, "Ma_ke Links", action_mgr->n_files_to_process);
label_text = ngettext ("Ma_ke Link", "Ma_ke Links", action_mgr->n_files_to_process);
tooltip_text = ngettext ("Create a symbolic link for the selected file",
"Create a symbolic link for each selected file", action_mgr->n_files_to_process);
item = xfce_gtk_menu_item_new (label_text, tooltip_text, action_entry->accel_path, action_entry->callback,
@ -2391,6 +2401,10 @@ thunar_action_manager_append_custom_actions (ThunarActionManager *action_mgr,
_thunar_return_val_if_fail (THUNAR_IS_ACTION_MANAGER (action_mgr), FALSE);
_thunar_return_val_if_fail (GTK_IS_MENU (menu), FALSE);
/* This may occur when the thunar-window is build */
if (G_UNLIKELY (action_mgr->files_to_process == NULL))
return FALSE;
/* determine the toplevel window we belong to */
window = gtk_widget_get_toplevel (action_mgr->widget);
@ -2402,10 +2416,6 @@ thunar_action_manager_append_custom_actions (ThunarActionManager *action_mgr,
if (G_UNLIKELY (providers == NULL))
return FALSE;
/* This may occur when the thunar-window is build */
if (G_UNLIKELY (action_mgr->files_to_process == NULL))
return FALSE;
/* load the menu items offered by the menu providers */
for (lp_provider = providers; lp_provider != NULL; lp_provider = lp_provider->next)
{
@ -2666,7 +2676,7 @@ thunar_action_manager_action_trash_delete (ThunarActionManager *action_mgr)
/* when shift modifier is pressed, we delete (as well via context menu) */
if (gtk_get_current_event_state (&event_state) && (event_state & GDK_SHIFT_MASK) != 0)
thunar_action_manager_action_delete (action_mgr);
else if (thunar_g_vfs_is_uri_scheme_supported ("trash") && !thunar_file_is_trashed (action_mgr->current_directory))
else if (thunar_action_manager_show_trash (action_mgr))
thunar_action_manager_action_move_to_trash (action_mgr);
else
thunar_action_manager_action_delete (action_mgr);

View file

@ -27,6 +27,9 @@
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif
#ifdef HAVE_MEMORY_H
#include <memory.h>
#endif
@ -2438,7 +2441,7 @@ unlink_stub (GList *source_path_list,
* @parent : a #GdkScreen, a #GtkWidget or %NULL.
* @file_list : the list of #ThunarFile<!---->s that should be deleted.
* @permanently : whether to unlink the files permanently.
* @warn : whether to warn the user if deleting permanently.
* @warn : whether to warn the user if moving files to trash.
* @log_mode : log mode
*
* Deletes all files in the @file_list and takes care of all user interaction.
@ -2459,7 +2462,7 @@ thunar_application_unlink_files (ThunarApplication *application,
{
GtkWidget *dialog;
GtkWidget *message_area;
GtkWindow *window;
GtkWindow *window = NULL;
GdkScreen *screen;
GList *path_list = NULL;
GList *lp;
@ -2489,7 +2492,7 @@ thunar_application_unlink_files (ThunarApplication *application,
return FALSE;
/* ask the user to confirm if deleting permanently */
if (G_UNLIKELY (permanently) && warn)
if (G_UNLIKELY (permanently))
{
/* parse the parent pointer */
screen = thunar_util_parse_parent (parent, &window);
@ -2555,12 +2558,6 @@ thunar_application_unlink_files (ThunarApplication *application,
else
operation_canceled = TRUE;
}
else if (G_UNLIKELY (permanently))
{
thunar_application_launch (application, parent, "edit-delete",
_("Deleting files..."), unlink_stub,
path_list, path_list, TRUE, FALSE, log_mode, NULL);
}
else if (G_LIKELY (!permanently) && warn)
{
/* parse the parent pointer */
@ -2760,7 +2757,7 @@ thunar_application_empty_trash (ThunarApplication *application,
const gchar *startup_id)
{
GtkWidget *dialog;
GtkWindow *window;
GtkWindow *window = NULL;
GdkScreen *screen;
GList file_list;
gint response;
@ -2908,3 +2905,28 @@ thunar_application_get_thumbnail_cache (ThunarApplication *application)
return g_object_ref (application->thumbnail_cache);
}
static gboolean
thunar_application_malloc_trim_idle (gpointer application_ptr)
{
#ifdef __GLIBC__
/* Workaround to make the kernel reclaim memory which is not used by Thunar anymore. */
/* When using glibc, after releasing bigger memory chunks it seems to be required to call that 'garbage collector'. */
/* More information here: https://gitlab.xfce.org/xfce/thunar/-/issues/1552#note_101990 */
malloc_trim (0);
#endif
return FALSE;
}
void
thunar_application_malloc_trim_on_idle (ThunarApplication *application)
{
_thunar_return_if_fail (THUNAR_IS_APPLICATION (application));
g_idle_add (thunar_application_malloc_trim_idle, application);
}

View file

@ -205,6 +205,9 @@ thunar_application_get_thumbnail_cache (ThunarApplication *application);
gboolean
thunar_application_accel_map_init (ThunarApplication *application);
void
thunar_application_malloc_trim_on_idle (ThunarApplication *application);
G_END_DECLS;
#endif /* !__THUNAR_APPLICATION_H__ */

View file

@ -1238,6 +1238,9 @@ thunar_details_view_row_activated (GtkTreeView *tree_view,
_thunar_return_if_fail (THUNAR_IS_DETAILS_VIEW (details_view));
/* ensure that the path is selected on double-click events */
thunar_details_view_select_path (THUNAR_STANDARD_VIEW (details_view), path);
window = gtk_widget_get_toplevel (GTK_WIDGET (details_view));
action_mgr = thunar_window_get_action_manager (THUNAR_WINDOW (window));
thunar_action_manager_activate_selected_files (action_mgr, THUNAR_ACTION_MANAGER_CHANGE_DIRECTORY, NULL);
@ -1309,7 +1312,7 @@ thunar_details_view_row_changed (GtkTreeView *tree_view,
{
_thunar_return_if_fail (THUNAR_IS_DETAILS_VIEW (details_view));
gtk_widget_queue_draw (GTK_WIDGET (details_view));
gtk_widget_queue_draw (GTK_WIDGET (details_view->tree_view));
}

View file

@ -90,7 +90,7 @@ thunar_dialogs_show_create (gpointer parent,
const gchar *title)
{
GtkWidget *dialog;
GtkWindow *window;
GtkWindow *window = NULL;
GdkScreen *screen;
GError *error = NULL;
gchar *name = NULL;
@ -225,7 +225,7 @@ thunar_dialogs_show_rename_file (gpointer parent,
ThunarIconFactory *icon_factory;
GtkIconTheme *icon_theme;
gint scale_factor;
const gchar *filename;
gchar *filename;
const gchar *text;
ThunarJob *job = NULL;
GtkWidget *dialog;
@ -234,7 +234,7 @@ thunar_dialogs_show_rename_file (gpointer parent,
GtkWidget *grid;
XfceFilenameInput *filename_input;
GtkEntry *filename_input_entry;
GtkWindow *window;
GtkWindow *window = NULL;
GdkScreen *screen;
GdkPixbuf *icon;
cairo_surface_t *surface;
@ -250,8 +250,9 @@ thunar_dialogs_show_rename_file (gpointer parent,
/* parse the parent window and screen */
screen = thunar_util_parse_parent (parent, &window);
/* get the filename of the file */
filename = thunar_file_get_basename (file);
/* Get the filename of the file. We create our own copy of the string because the file
* can be modified by another process while the rename is in progress (issue #1639). */
filename = g_strdup (thunar_file_get_basename (file));
/* create a new dialog window */
title = g_strdup_printf (_("Rename \"%s\""), filename);
@ -385,6 +386,8 @@ thunar_dialogs_show_rename_file (gpointer parent,
gtk_widget_destroy (dialog);
}
g_free (filename);
return job;
}
@ -482,7 +485,7 @@ thunar_dialogs_show_error (gpointer parent,
...)
{
GtkWidget *dialog;
GtkWindow *window;
GtkWindow *window = NULL;
GdkScreen *screen;
va_list args;
gchar *primary_text;
@ -1041,7 +1044,7 @@ thunar_dialogs_show_insecure_program (gpointer parent,
const gchar *command)
{
GdkScreen *screen;
GtkWindow *window;
GtkWindow *window = NULL;
gint response;
GtkWidget *dialog;
GString *secondary;
@ -1236,7 +1239,7 @@ thunar_dialog_ask_execute (const ThunarFile *file,
gboolean single_file)
{
GtkWidget *dialog;
GtkWindow *window;
GtkWindow *window = NULL;
GdkScreen *screen;
gint response;
gchar *dialog_text;

View file

@ -230,8 +230,9 @@ struct _ThunarFile
/* flags for thumbnail state etc */
ThunarFileFlags flags;
/* event source id used to rate-limit file-changed signals */
/* event source id and variable to rate-limit file-changed signals */
guint signal_changed_source_id;
gboolean signal_change_requested;
/* Number of files in this directory (only used if this #Thunarfile is a directory) */
/* Note that this feature was added into #ThunarFile on purpose, because having inside #ThunarFolder caused lag when
@ -1204,6 +1205,7 @@ thunar_file_load (ThunarFile *file,
{
GError *err = NULL;
GFileInfo *info = NULL;
gboolean mounted = TRUE;
_thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
_thunar_return_val_if_fail (error == NULL || *error == NULL, FALSE);
@ -1221,12 +1223,12 @@ thunar_file_load (ThunarFile *file,
G_FILE_QUERY_INFO_NONE,
cancellable, &err);
/* update the mounted info */
/* check if mounted (but do not update the file until after it has been cleared) */
if (err != NULL
&& err->domain == G_IO_ERROR
&& err->code == G_IO_ERROR_NOT_MOUNTED)
{
FLAG_UNSET (file, THUNAR_FILE_FLAG_IS_MOUNTED);
mounted = FALSE;
g_clear_error (&err);
}
@ -1254,6 +1256,10 @@ thunar_file_load (ThunarFile *file,
/* update the file from the information */
thunar_file_info_reload (file, cancellable);
/* update the mounted info */
if (mounted == FALSE)
FLAG_UNSET (file, THUNAR_FILE_FLAG_IS_MOUNTED);
/* (re)insert the file into the cache */
if (file->kind != G_FILE_TYPE_UNKNOWN)
{
@ -1741,11 +1747,15 @@ thunar_file_execute (ThunarFile *file,
{
/* fake the Exec line */
escaped_location = g_shell_quote (location);
if (in_terminal)
exec = g_strconcat ("exo-open --launch TerminalEmulator ", escaped_location, " %F", NULL);
else
exec = g_strconcat (escaped_location, " %F", NULL);
command = xfce_expand_desktop_entry_field_codes (exec, uri_list, NULL, NULL, NULL, FALSE);
if (in_terminal)
{
gchar *escaped = g_shell_quote (command);
g_free (command);
command = g_strconcat ("exo-open --launch TerminalEmulator ", escaped, NULL);
g_free (escaped);
}
result = g_shell_parse_argv (command, NULL, &argv, error);
g_free (escaped_location);
g_free (exec);
@ -2107,6 +2117,7 @@ thunar_file_accepts_drop (ThunarFile *file,
{
/* default to copy and get outta here */
suggested_action = GDK_ACTION_COPY;
if (parent_thunar_file != NULL)
g_object_unref (parent_thunar_file);
break;
}
@ -5413,24 +5424,23 @@ thunar_file_reset_thumbnail (ThunarFile *file,
static gboolean
thunar_file_changed_signal_emit (gpointer data)
thunar_file_changed_signal_timeout (gpointer data)
{
/* emit the changed signal on thunarx level */
thunarx_file_info_changed (THUNARX_FILE_INFO (data));
ThunarFile *file = THUNAR_FILE (data);
_thunar_return_val_if_fail (THUNAR_IS_FILE (file), G_SOURCE_REMOVE);
/* emit the changed signal on thunarx level, if required */
if (file->signal_change_requested)
thunarx_file_info_changed (THUNARX_FILE_INFO (file));
file->signal_change_requested = FALSE;
file->signal_changed_source_id = 0;
return G_SOURCE_REMOVE;
}
static void
thunar_file_changed_signal_destroy (gpointer data)
{
THUNAR_FILE (data)->signal_changed_source_id = 0;
}
/**
* thunar_file_changed:
* @file : a #ThunarFile instance.
@ -5441,10 +5451,17 @@ thunar_file_changed_signal_destroy (gpointer data)
void
thunar_file_changed (ThunarFile *file)
{
if (file->signal_changed_source_id == 0)
/* in case a timeout is running, the change-signal will be delayed (performance) */
if (file->signal_changed_source_id != 0)
{
file->signal_changed_source_id = g_timeout_add_full (G_PRIORITY_DEFAULT, FILE_CHANGED_SIGNAL_RATE_LIMIT,
thunar_file_changed_signal_emit,
file, thunar_file_changed_signal_destroy);
file->signal_change_requested = TRUE;
return;
}
/* handle the first request directly */
thunarx_file_info_changed (THUNARX_FILE_INFO (file));
/* start a timer to throttle consecutive calls */
file->signal_changed_source_id = g_timeout_add (FILE_CHANGED_SIGNAL_RATE_LIMIT,
thunar_file_changed_signal_timeout, file);
}

View file

@ -149,10 +149,10 @@ struct _ThunarFolder
/* Files which were changed recently. The key is a ThunarFile; value is NULL (unimportant)*/
GHashTable *changed_files_map;
/* Files which were changed recently. The key is a ThunarFile; value is NULL (unimportant)*/
/* Files which were added recently. The key is a ThunarFile; value is NULL (unimportant)*/
GHashTable *added_files_map;
/* Files which were changed recently. The key is a ThunarFile; value is NULL (unimportant)*/
/* Files which were removed recently. The key is a ThunarFile; value is NULL (unimportant)*/
GHashTable *removed_files_map;
/* Files inside this folder. The key is a ThunarFile; value is NULL (unimportant)*/
@ -409,7 +409,10 @@ thunar_folder_finalize (GObject *object)
g_source_remove (folder->files_update_timeout_source_id);
if (folder->monitor != NULL)
{
g_signal_handlers_disconnect_by_data (folder->monitor, folder);
g_object_unref (folder->monitor);
}
if (folder->corresponding_file)
{
@ -594,17 +597,25 @@ _thunar_folder_files_update_timeout (gpointer data)
g_hash_table_remove_all (files);
g_hash_table_remove_all (folder->added_files_map);
/* send a 'changed' signal for all files which changed */
/* reload files which were changed and send a 'changed' signal for them, if required */
g_hash_table_iter_init (&iter, folder->changed_files_map);
while (g_hash_table_iter_next (&iter, &key, NULL))
{
/* block 'changed' signals for this file until reload is done, in order to prevent recursion */
g_signal_handlers_block_by_func (G_OBJECT (key), G_CALLBACK (thunar_folder_file_changed), folder);
thunar_file_reload (THUNAR_FILE (key));
g_signal_handlers_unblock_by_func (G_OBJECT (key), G_CALLBACK (thunar_folder_file_changed), folder);
/* only send the 'changed' signal for files which are already part of this folder */
if (!g_hash_table_contains (folder->files_map, key))
continue;
g_hash_table_add (files, g_object_ref (key));
}
if (g_hash_table_size (files) > 0)
g_signal_emit (G_OBJECT (folder), folder_signals[FILES_CHANGED], 0, files);
g_hash_table_destroy (files);
g_hash_table_remove_all (folder->changed_files_map);
@ -645,6 +656,10 @@ thunar_folder_add_file (ThunarFolder *folder,
if (g_hash_table_remove (folder->removed_files_map, file))
return;
/* Do nothing if we already have the file in our internal map */
if (g_hash_table_lookup (folder->files_map, file) != NULL)
return;
g_hash_table_add (folder->added_files_map, g_object_ref (file));
if (folder->files_update_timeout_source_id == 0)
@ -661,6 +676,10 @@ thunar_folder_remove_file (ThunarFolder *folder,
if (g_hash_table_remove (folder->added_files_map, file))
return;
/* Do nothing if we dont have the file yet in our internal map */
if (g_hash_table_lookup (folder->files_map, file) == NULL)
return;
g_hash_table_add (folder->removed_files_map, g_object_ref (file));
if (folder->files_update_timeout_source_id == 0)
@ -1068,16 +1087,12 @@ thunar_folder_monitor (GFileMonitor *monitor,
case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
if (event_file_thunar != NULL)
{
/* If we have that file in our internal map, reload it and send a file-changed signal */
if (g_hash_table_lookup (folder->files_map, event_file_thunar) != NULL)
{
/* this will trigger a reload if the file and send a 'file-changed' signal */
thunar_folder_file_changed (folder, event_file_thunar);
#if DEBUG_FILE_CHANGES
thunar_file_infos_equal (event_file_thunar, event_file);
#endif
thunar_file_reload (event_file_thunar);
}
}
default:

View file

@ -194,9 +194,91 @@ thunar_g_file_new_for_bookmarks (void)
/* internal version, for recursive execution. Uses a lookup table to detect loops */
static GFile *
_thunar_g_file_resolve_symlink_internal (GFile *file, GHashTable *lookup_table)
{
g_autoptr (GHashTable) lookup_history = NULL;
g_autoptr (GFileInfo) info = NULL;
_thunar_return_val_if_fail (G_IS_FILE (file), NULL);
if (lookup_table == NULL)
lookup_history = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
else
lookup_history = g_hash_table_ref (lookup_table);
/* Resolve the link target recursively, until the target we found is not a symlink */
char *file_uri = g_file_get_uri (file);
if (file_uri == NULL)
return NULL;
/* Detect loops: if the file already was seen, return NULL */
if (g_hash_table_contains (lookup_history, file_uri))
{
g_info ("Symlink loop detected - resolve symlink aborted");
g_free (file_uri);
return NULL;
}
g_hash_table_add (lookup_history, file_uri);
info = g_file_query_info (file,
G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET "," G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK,
G_FILE_QUERY_INFO_NONE, NULL, NULL);
if (info == NULL)
return NULL;
/* Not a symlink: return that file */
if (!g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK))
return g_object_ref (file);
/* It's a symlink, get the target */
const char *target_path = g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET);
if (target_path == NULL)
{
g_info ("Failed to read target path from symlink: %s", g_file_get_path (file));
return NULL;
}
/* The target is as well a symlink ... so lets use recursion */
if (g_path_is_absolute (target_path))
{
g_autoptr (GFile) target = g_file_new_for_path (target_path);
return _thunar_g_file_resolve_symlink_internal (target, lookup_history);
}
else
{
/* Resolve a possible relative path */
g_autoptr (GFile) target = NULL;
g_autoptr (GFile) parent = NULL;
g_autoptr (GFile) parent_resolved = NULL;
parent = g_file_get_parent (file);
if (parent == NULL)
return NULL;
/* Resolve parent, if required */
parent_resolved = _thunar_g_file_resolve_symlink_internal (parent, lookup_history);
if (parent_resolved == NULL)
{
g_info ("Failed to resolve parent of symlink");
return NULL;
}
target = g_file_resolve_relative_path (parent_resolved, target_path);
/* Sicne as well that file might be a symlink, we need to use recursion */
return _thunar_g_file_resolve_symlink_internal (target, lookup_history);
}
}
/**
* thunar_g_file_resolve_symlink:
* @file : a #GFile.
* @file : a #GFile
*
* Returns the resolved symlink target of @file as a new #GFile.
* If @file is not a symlink, @file will just be returned
@ -206,27 +288,7 @@ thunar_g_file_new_for_bookmarks (void)
GFile *
thunar_g_file_resolve_symlink (GFile *file)
{
gchar *basename;
GFile *parent = NULL;
GFile *target = NULL;
_thunar_return_val_if_fail (G_IS_FILE (file), NULL);
parent = g_file_get_parent (file);
if (parent == NULL)
return NULL;
basename = g_file_get_basename (file);
if (basename == NULL)
{
g_object_unref (parent);
return NULL;
}
target = g_file_resolve_relative_path (parent, basename);
g_object_unref (parent);
g_free (basename);
return target;
return _thunar_g_file_resolve_symlink_internal (file, NULL);
}
@ -1760,10 +1822,12 @@ thunar_g_file_get_content_type (GFile *gfile)
g_object_unref (info);
info = NULL;
if (link_target != NULL)
{
info = thunar_g_file_get_content_type_querry_info (link_target, err);
g_object_unref (link_target);
}
}
}
if (G_LIKELY (info != NULL))
{

View file

@ -151,6 +151,8 @@ thunar_gtk_menu_thunarx_menu_item_new (GObject *thunarx_menu_item,
g_free (icon_name);
if (icon != NULL)
g_object_unref (icon);
if (thunarx_menu != NULL)
g_object_unref (thunarx_menu);
return gtk_menu_item;
}
@ -187,7 +189,7 @@ thunar_gtk_menu_clean (GtkMenu *menu)
* thunar_gtk_menu_run:
* @menu : a #GtkMenu.
*
* Conveniance wrapper for thunar_gtk_menu_run_at_event_pointer, to run a menu for the current event
* Convenience wrapper for thunar_gtk_menu_run_at_event() to run a menu for the current event
**/
void
thunar_gtk_menu_run (GtkMenu *menu)
@ -207,6 +209,51 @@ thunar_gtk_menu_run (GtkMenu *menu)
static void
thunar_gtk_menu_popup_at_pointer (GtkMenu *menu,
GdkEvent *event)
{
GdkWindow *window;
GdkWindow *toplevel = NULL;
GdkDevice *device;
GdkRectangle rect = { 0, 0, 1, 1 };
/* fallback if event not set or event not holding a GdkSeat */
if (event == NULL || gdk_event_get_seat (event) == NULL)
{
gtk_menu_popup_at_pointer (menu, event);
return;
}
/* create popup rect */
window = gdk_event_get_window (event);
if (window != NULL)
{
/* Ensure that we have the toplevel window to work around a GTK3 Wayland bug
* that prevents the user from dismissing the menu via click in some cases.
* See: https://gitlab.xfce.org/xfce/thunar/-/issues/1592 */
toplevel = gdk_window_get_toplevel (window);
device = gdk_event_get_device (event);
if (device != NULL && gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
device = gdk_device_get_associated_device (device);
if (device != NULL)
gdk_window_get_device_position (toplevel, device, &rect.x, &rect.y, NULL);
}
/* pop up the menu at the rectangle below the mouse cursor */
gtk_menu_popup_at_rect (menu,
toplevel,
&rect,
GDK_GRAVITY_NORTH_WEST,
GDK_GRAVITY_NORTH_WEST,
event);
}
/**
* thunar_gtk_menu_run_at_event:
* @menu : a #GtkMenu.
@ -221,7 +268,8 @@ thunar_gtk_menu_run (GtkMenu *menu)
*
**/
void
thunar_gtk_menu_run_at_event (GtkMenu *menu, GdkEvent *event)
thunar_gtk_menu_run_at_event (GtkMenu *menu,
GdkEvent *event)
{
GMainLoop *loop;
gulong signal_id;
@ -234,7 +282,7 @@ thunar_gtk_menu_run_at_event (GtkMenu *menu, GdkEvent *event)
/* run an internal main loop */
loop = g_main_loop_new (NULL, FALSE);
signal_id = g_signal_connect_swapped (G_OBJECT (menu), "deactivate", G_CALLBACK (g_main_loop_quit), loop);
gtk_menu_popup_at_pointer (menu, event);
thunar_gtk_menu_popup_at_pointer (menu, event);
gtk_menu_reposition (menu);
gtk_grab_add (GTK_WIDGET (menu));
g_main_loop_run (loop);

View file

@ -1371,6 +1371,7 @@ _thunar_io_jobs_rename (ThunarJob *job,
const gchar *display_name;
ThunarFile *file;
GError *err = NULL;
GFile *old_file;
gchar *old_file_uri;
ThunarOperationLogMode log_mode;
ThunarJobOperation *operation;
@ -1403,8 +1404,10 @@ _thunar_io_jobs_rename (ThunarJob *job,
if (log_mode == THUNAR_OPERATION_LOG_OPERATIONS)
{
operation = thunar_job_operation_new (THUNAR_JOB_OPERATION_KIND_RENAME);
thunar_job_operation_add (operation, g_file_new_for_uri (old_file_uri), thunar_file_get_file (file));
old_file = g_file_new_for_uri (old_file_uri);
thunar_job_operation_add (operation, old_file, thunar_file_get_file (file));
thunar_job_operation_history_commit (operation);
g_object_unref (old_file);
g_object_unref (operation);
}
}
@ -1605,7 +1608,7 @@ _thunar_search_folder (ThunarStandardViewModel *model,
if (exo_job_is_cancelled (EXO_JOB (job)))
{
g_list_free (files_found);
thunar_g_list_free_full (files_found);
return;
}

View file

@ -259,6 +259,7 @@ thunar_preferences_dialog_init (ThunarPreferencesDialog *dialog)
GtkWidget *vbox;
GtkWidget *infobar;
GEnumClass *type;
GList *windows;
gchar *date;
gint row = 0;
@ -1420,9 +1421,13 @@ thunar_preferences_dialog_init (ThunarPreferencesDialog *dialog)
gtk_widget_show (vbox);
frame = g_object_new (GTK_TYPE_FRAME, "border-width", 0, "shadow-type", GTK_SHADOW_IN, NULL);
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, TRUE, 0);
gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
gtk_widget_show (frame);
/* check if the accel map has been initialized which is done in thunar-window.c */
windows = thunar_application_get_windows (thunar_application_get ());
if (windows != NULL)
{
grid = xfce_shortcuts_editor_new (13,
_("Window"), thunar_window_get_action_entries (), (size_t) THUNAR_WINDOW_N_ACTIONS,
_("View"), thunar_standard_view_get_action_entries (), (size_t) THUNAR_STANDARD_VIEW_N_ACTIONS,
@ -1430,6 +1435,15 @@ thunar_preferences_dialog_init (ThunarPreferencesDialog *dialog)
_("Status Bar"), thunar_statusbar_get_action_entries (), (size_t) THUNAR_STATUS_BAR_N_ACTIONS);
gtk_container_add (GTK_CONTAINER (frame), grid);
gtk_widget_show (grid);
g_list_free (windows);
}
else
{
label = gtk_label_new (_("The Shortcuts Editor requires a Thunar window to be present."));
gtk_container_add (GTK_CONTAINER (frame), label);
gtk_widget_show (label);
}
}

View file

@ -381,6 +381,7 @@ thunar_progress_dialog_add_job (ThunarProgressDialog *dialog,
else
{
dialog->views_waiting = g_list_append (dialog->views_waiting, view);
thunar_job_freeze (job);
}
g_list_free (job_list);

View file

@ -658,7 +658,10 @@ thunar_progress_view_new_with_job (ThunarJob *job)
_thunar_return_val_if_fail (job == NULL || THUNAR_IS_JOB (job), NULL);
view = g_object_new (THUNAR_TYPE_PROGRESS_VIEW, "job", job, NULL);
gtk_label_set_text (GTK_LABEL (view->progress_label), _("Queued"));
/* 'thunar_progress_dialog_add_job' will instantly overwrite with "queued", if required */
thunar_progress_view_percent (view, 0.0, EXO_JOB (job));
return GTK_WIDGET (view);
}

View file

@ -1704,14 +1704,16 @@ thunar_properties_dialog_update_multiple (ThunarPropertiesDialog *dialog)
parent_file = tmp_parent;
}
else if (tmp_parent != NULL)
{
if (parent_file != NULL)
{
/* we only display the location if they are all equal */
if (!g_file_equal (thunar_file_get_file (parent_file), thunar_file_get_file (tmp_parent)))
{
if (parent_file != NULL)
g_object_unref (G_OBJECT (parent_file));
parent_file = NULL;
}
}
g_object_unref (G_OBJECT (tmp_parent));
}

View file

@ -853,7 +853,8 @@ thunar_renamer_dialog_response (GtkDialog *dialog,
/* perform the rename (returns when done) */
thunar_renamer_progress_run (THUNAR_RENAMER_PROGRESS (renamer_dialog->progress), pair_list);
/* hide the rename progress bar */
/* hide the rename progress bar (unless the dialog has been destroyed) */
if (gtk_widget_get_realized (GTK_WIDGET (renamer_dialog)))
gtk_widget_hide (renamer_dialog->progress);
/* release the pairs */

View file

@ -241,6 +241,13 @@ struct _ThunarShortcut
guint is_separator : 1;
};
typedef struct
{
ThunarShortcutsModel *model;
ThunarShortcut *shortcut;
gboolean update_visibility;
} ThunarShortcutFileGetData;
G_DEFINE_TYPE_WITH_CODE (ThunarShortcutsModel, thunar_shortcuts_model, G_TYPE_OBJECT,
@ -1610,12 +1617,39 @@ thunar_shortcuts_model_save_bookmarks (ThunarShortcutsModel *model)
static void
thunar_shortcuts_model_device_added_callback (GFile *location,
ThunarFile *file,
GError *error,
gpointer user_data)
{
ThunarShortcutFileGetData *data = user_data;
ThunarShortcut *shortcut = data->shortcut;
ThunarShortcutsModel *model = data->model;
if (error == NULL)
{
shortcut->file = g_object_ref (file);
/* insert in the model */
thunar_shortcuts_model_add_shortcut (model, shortcut);
if (data->update_visibility)
thunar_shortcuts_model_header_visibility (model);
}
g_object_unref (location);
g_object_unref (model);
g_slice_free (ThunarShortcutFileGetData, data);
}
static void
thunar_shortcuts_model_device_added (ThunarDeviceMonitor *device_monitor,
ThunarDevice *device,
ThunarShortcutsModel *model)
{
ThunarShortcut *shortcut;
ThunarShortcutFileGetData *data;
GFile *mount_point;
_thunar_return_if_fail (device_monitor == NULL || THUNAR_DEVICE_MONITOR (device_monitor));
@ -1628,13 +1662,6 @@ thunar_shortcuts_model_device_added (ThunarDeviceMonitor *device_monitor,
shortcut->device = g_object_ref (device);
shortcut->hidden = thunar_device_get_hidden (device);
mount_point = thunar_device_get_root (device);
if (mount_point != NULL)
{
shortcut->file = thunar_file_get (mount_point, NULL);
g_object_unref (mount_point);
}
switch (thunar_device_get_kind (device))
{
case THUNAR_DEVICE_KIND_VOLUME:
@ -1650,13 +1677,27 @@ thunar_shortcuts_model_device_added (ThunarDeviceMonitor *device_monitor,
break;
}
/* header visibility if call is from monitor */
gboolean update_visibility = (device_monitor != NULL && !shortcut->hidden);
mount_point = thunar_device_get_root (device);
if (mount_point != NULL)
{
data = g_slice_new0 (ThunarShortcutFileGetData);
data->shortcut = shortcut;
data->model = g_object_ref (model);
data->update_visibility = update_visibility;
thunar_file_get_async (mount_point, NULL, &thunar_shortcuts_model_device_added_callback, data);
}
else /* some devices (e.g. usb flashdrives) do not have a mountpoint */
{
/* insert in the model */
thunar_shortcuts_model_add_shortcut (model, shortcut);
/* header visibility if call is from monitor */
if (device_monitor != NULL
&& !shortcut->hidden)
if (update_visibility)
thunar_shortcuts_model_header_visibility (model);
}
}
@ -1679,7 +1720,12 @@ thunar_shortcuts_model_device_removed (ThunarDeviceMonitor *device_monitor,
break;
/* something is broken if we don't have a shortcut here */
_thunar_assert (lp != NULL);
if (lp == NULL)
{
g_warning ("No shortcut found for device '%s'", thunar_device_get_name (device));
return;
}
_thunar_assert (THUNAR_SHORTCUT (lp->data)->device == device);
/* drop the shortcut from the model */
@ -1713,7 +1759,12 @@ thunar_shortcuts_model_device_changed (ThunarDeviceMonitor *device_monitor,
break;
/* something is broken if we don't have a shortcut here */
_thunar_assert (lp != NULL);
if (lp == NULL)
{
g_warning ("No shortcut found for device '%s'", thunar_device_get_name (device));
return;
}
_thunar_assert (THUNAR_SHORTCUT (lp->data)->device == device);
if (G_LIKELY (lp != NULL))
@ -1786,8 +1837,7 @@ thunar_shortcuts_model_file_destroyed (ThunarFile *file,
/* remove the thunar file from the list and set the g_file instead */
if (shortcut->location == NULL)
{
GFile *g_file = thunar_file_get_file (file);
shortcut->location = g_file;
shortcut->location = g_object_ref (thunar_file_get_file (file)) ;
}
if (shortcut->gicon == NULL)

View file

@ -1500,6 +1500,10 @@ thunar_standard_view_update_selected_files (ThunarStandardView *standard_view,
/* determine the tree paths for the given files */
paths = thunar_standard_view_model_get_paths_for_files (standard_view->model, files_to_select);
/* unselect all previously selected files - this may free the "files_to_select" list */
(*THUNAR_STANDARD_VIEW_GET_CLASS (standard_view)->unselect_all) (standard_view);
if (G_LIKELY (paths != NULL))
{
/* determine the first path */
@ -2456,6 +2460,7 @@ thunar_standard_view_update_statusbar_text_idle (gpointer data)
GList *selected_items_tree_path_list;
GtkTreeIter iter;
ThunarFile *file;
gchar *statusbar_text;
_thunar_return_val_if_fail (THUNAR_IS_STANDARD_VIEW (standard_view), FALSE);
@ -2478,6 +2483,22 @@ thunar_standard_view_update_statusbar_text_idle (gpointer data)
if (thunar_standard_view_model_get_folder (standard_view->model) == NULL)
return FALSE;
/* search mode active */
if (standard_view->priv->search_query != NULL)
{
gint num_files;
g_object_get (G_OBJECT (standard_view->model), "num-files", &num_files, NULL);
statusbar_text = g_strdup_printf (ngettext ("%d file found", "%d files found", num_files), num_files);
thunar_standard_view_set_statusbar_text (standard_view, statusbar_text);
g_free (statusbar_text);
g_object_notify_by_pspec (G_OBJECT (standard_view), standard_view_props[PROP_STATUSBAR_TEXT]);
return FALSE;
}
standard_view->priv->statusbar_job = thunar_io_jobs_load_statusbar_text_for_folder (standard_view, thunar_standard_view_model_get_folder (standard_view->model));
g_signal_connect (standard_view->priv->statusbar_job, "error", G_CALLBACK (thunar_standard_view_update_statusbar_text_error), standard_view);
@ -2487,8 +2508,6 @@ thunar_standard_view_update_statusbar_text_idle (gpointer data)
}
else if (selected_items_tree_path_list->next == NULL) /* only one item selected */
{
gchar *statusbar_text;
/* resolve the iter for the single path */
gtk_tree_model_get_iter (GTK_TREE_MODEL (standard_view->model), &iter, selected_items_tree_path_list->data);
@ -2549,7 +2568,7 @@ thunar_standard_view_update_statusbar_text (ThunarStandardView *standard_view)
/* restart a new one, this way we avoid multiple update when
* the user is pressing a key to scroll */
standard_view->priv->statusbar_text_idle_id =
g_timeout_add_full (G_PRIORITY_LOW, 50, thunar_standard_view_update_statusbar_text_idle,
g_timeout_add_full (G_PRIORITY_DEFAULT, 50, thunar_standard_view_update_statusbar_text_idle,
standard_view, NULL);
}

View file

@ -868,6 +868,7 @@ thunar_transfer_job_copy_node (ThunarTransferJob *job,
use_fat_name_scheme =
!g_strcmp0 (fs_type, "fat") ||
!g_strcmp0 (fs_type, "vfat") ||
!g_strcmp0 (fs_type, "exfat") ||
!g_strcmp0 (fs_type, "fuse") ||
!g_strcmp0 (fs_type, "ntfs") ||
!g_strcmp0 (fs_type, "msdos") ||

View file

@ -1717,6 +1717,9 @@ _thunar_tree_view_model_cancel_search_job (ThunarTreeViewModel *model)
g_object_unref (model->search_job);
model->search_job = NULL;
}
thunar_g_list_free_full (model->search_files);
model->search_files = NULL;
}
@ -2516,10 +2519,26 @@ thunar_tree_view_model_sort (ThunarTreeViewModel *model)
static void
_thunar_tree_view_model_folder_destroy (Node *node)
{
/* if a subdir get's destroyed it's handled internally but if the current_folder
* get's destroyed then we simply set_folder to NULL & cleanup things */
/* if a subdir gets destroyed, it's handled internally, but if the current_folder
* gets destroyed, then we simply set_folder to NULL & cleanup things */
if (node->parent == NULL)
{
if (node->n_children > 0)
{
/* set_folder func does not emit row-deleted signal, but instead relies on
* ThunarStandardView to disconnect & reconnect the view to quickly update
* changes in current_directory. */
GList *keys = g_hash_table_get_keys (node->set);
for (GList *lp = keys; lp != NULL; lp = lp->next)
thunar_tree_view_model_dir_remove_file (node, THUNAR_FILE (lp->data));
g_list_free (keys);
}
/* reset the model */
thunar_tree_view_model_set_folder (THUNAR_STANDARD_VIEW_MODEL (node->model), NULL, NULL);
}
}
@ -2530,26 +2549,13 @@ _thunar_tree_view_model_folder_error (Node *node,
{
_thunar_return_if_fail (error != NULL);
if (node->n_children > 0)
{
if (node->parent == NULL)
{
/* set_folder func does not emit row-deleted signal instead relies
* on ThunarStandardView to disconnect & reconnect the view quickly update
* changes in current_directory. */
GList *keys = g_hash_table_get_keys (node->set);
for (GList *lp = keys; lp != NULL; lp = lp->next)
thunar_tree_view_model_dir_remove_file (node, THUNAR_FILE (lp->data));
g_list_free (keys);
/* reset the model if error is with the current directory */
thunar_tree_view_model_set_folder (THUNAR_STANDARD_VIEW_MODEL (node->model), NULL, NULL);
}
_thunar_tree_view_model_folder_destroy (node);
else
thunar_tree_view_model_dir_remove_file (node->parent, node->file);
}
/* forward the error signal */
g_signal_emit_by_name (G_OBJECT (node->model), "error", error);
}
@ -2578,6 +2584,8 @@ _thunar_tree_view_model_dir_files_added (Node *node,
if (!g_hash_table_contains (node->set, file))
thunar_tree_view_model_dir_add_file (node, file);
}
g_object_notify_by_pspec (G_OBJECT (node->model), tree_model_props[PROP_NUM_FILES]);
}
@ -2607,6 +2615,8 @@ _thunar_tree_view_model_dir_files_removed (Node *node,
thunar_tree_view_model_dir_remove_file (node, file);
}
g_object_notify_by_pspec (G_OBJECT (node->model), tree_model_props[PROP_NUM_FILES]);
}
@ -2841,8 +2851,8 @@ thunar_tree_view_model_dir_files_changed (Node *node_parent,
{
if (!model->show_hidden)
thunar_tree_view_model_dir_remove_file (node_parent, file);
g_hash_table_add (node_parent->hidden_files, g_object_ref (file));
continue;
}
node = thunar_tree_view_model_locate_file (model, file);
@ -3114,9 +3124,12 @@ thunar_tree_view_model_update_search_files (ThunarTreeViewModel *model)
g_object_unref (file);
}
g_object_notify_by_pspec (G_OBJECT (model), tree_model_props[PROP_NUM_FILES]);
if (model->search_files != NULL)
g_list_free (model->search_files);
thunar_g_list_free_full (model->search_files);
model->search_files = NULL;
g_mutex_unlock (&model->mutex_add_search_files);
return TRUE;

View file

@ -560,8 +560,8 @@ thunar_util_parse_parent (gpointer parent,
}
else if (GDK_IS_SCREEN (parent))
{
/* yep, that's a screen */
screen = GDK_SCREEN (parent);
/* A specific screen was requested. Just return it, without filling 'window_return' */
return GDK_SCREEN (parent);
}
else
{

View file

@ -1256,6 +1256,7 @@ thunar_window_init (ThunarWindow *window)
window->trash_infobar = gtk_info_bar_new ();
gtk_grid_attach (GTK_GRID (window->view_box), window->trash_infobar, 0, 2, 1, 1);
window->trash_infobar_restore_button = gtk_info_bar_add_button (GTK_INFO_BAR (window->trash_infobar), _("Restore Selected Items"), RESTORE);
gtk_widget_set_sensitive (window->trash_infobar_restore_button, FALSE);
window->trash_infobar_empty_button = gtk_info_bar_add_button (GTK_INFO_BAR (window->trash_infobar), _("Empty Trash"), EMPTY);
g_signal_connect (window->trash_infobar,
"response",
@ -2405,6 +2406,7 @@ thunar_window_switch_current_view (ThunarWindow *window,
GSList *view_bindings;
ThunarFile *current_directory;
ThunarHistory *history;
gchar *search_query;
_thunar_return_if_fail (THUNAR_IS_WINDOW (window));
_thunar_return_if_fail (THUNAR_IS_VIEW (new_view));
@ -2436,6 +2438,11 @@ thunar_window_switch_current_view (ThunarWindow *window,
window->view_bindings = NULL;
g_slist_free_full (view_bindings, g_object_unref);
/* exit search mode (if the view has no ongoing search operation) */
search_query = thunar_standard_view_get_search_query (THUNAR_STANDARD_VIEW (new_view));
if (search_query == NULL && window->search_query != NULL)
thunar_window_action_cancel_search (window);
/* update the directory of the current window */
current_directory = thunar_navigator_get_current_directory (THUNAR_NAVIGATOR (new_view));
thunar_window_set_current_directory (window, current_directory);
@ -2511,15 +2518,13 @@ thunar_window_switch_current_view (ThunarWindow *window,
if (thunar_file_is_trash (window->current_directory))
gtk_widget_set_sensitive (window->trash_infobar_empty_button, thunar_file_get_item_count (window->current_directory) > 0);
/* if the view has an ongoing search operation take that into account, otherwise cancel the current search (if there is one) */
if (thunar_standard_view_get_search_query (THUNAR_STANDARD_VIEW (window->view)) != NULL)
/* if the view has an ongoing search operation, take that into account */
if (search_query != NULL)
{
gchar *str = g_strjoin (NULL, thunar_util_get_search_prefix (), thunar_standard_view_get_search_query (THUNAR_STANDARD_VIEW (window->view)), NULL);
gchar *str = g_strjoin (NULL, thunar_util_get_search_prefix (), search_query, NULL);
thunar_window_resume_search (window, str);
g_free (str);
}
else if (window->search_query != NULL)
thunar_window_action_cancel_search (window);
/* switch to the new view */
thunar_window_notebook_set_current_tab (window, gtk_notebook_page_num (GTK_NOTEBOOK (window->notebook_selected), window->view));
@ -2643,6 +2648,10 @@ thunar_window_notebook_page_removed (GtkWidget *notebook,
/* drop connected signals */
g_signal_handlers_disconnect_by_data (page, window);
/* skip selecting another page when the window is being destroyed */
if (gtk_widget_in_destruction (GTK_WIDGET (window)))
return;
n_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook));
if (n_pages == 0)
{
@ -2665,8 +2674,7 @@ thunar_window_notebook_page_removed (GtkWidget *notebook,
/* page from the other notebook was removed */
if (notebook != window->notebook_selected)
thunar_window_paned_notebooks_switch (window);
else
/* this page removed -> select next page */
else /* this page removed -> select next page */
thunar_window_notebook_select_current_page (window);
/* update tab visibility */
@ -3670,6 +3678,8 @@ thunar_window_reset_view_type_idle_destroyed (gpointer data)
gboolean
thunar_window_action_cancel_search (ThunarWindow *window)
{
ThunarApplication *application;
_thunar_return_val_if_fail (THUNAR_IS_LOCATION_BAR (window->location_bar), FALSE);
_thunar_return_val_if_fail (THUNAR_IS_WINDOW (window), FALSE);
@ -3708,6 +3718,11 @@ thunar_window_action_cancel_search (ThunarWindow *window)
/* bring back the original location bar style (relevant if the bar is hidden) */
thunar_window_update_location_bar_visible (window);
/* tidy up memory after a search */
application = thunar_application_get ();
thunar_application_malloc_trim_on_idle (application);
g_object_unref (application);
/* required in case of shortcut activation, in order to signal that the accel key got handled */
return TRUE;
}