Compare commits

...

No commits in common. "upstream-4.20.7" and "usagebars" have entirely different histories.

86 changed files with 23543 additions and 35236 deletions

96
NEWS
View file

@ -1,99 +1,3 @@
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) 4.20.2 (2025-02-10)
====== ======
- Fix wrong selection after rename in list view (#1415) - 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_verinfo], [0:0:0])
m4_define([thunarx_version_api], [3]) m4_define([thunarx_version_api], [3])
XDT_VERSION_INIT([4.20.7]) XDT_VERSION_INIT([4.20.2])
dnl *************************** dnl ***************************
dnl *** Initialize autoconf *** dnl *** Initialize autoconf ***
@ -123,7 +123,7 @@ AC_SYS_LARGEFILE()
dnl ********************************** dnl **********************************
dnl *** Check for standard headers *** dnl *** Check for standard headers ***
dnl ********************************** dnl **********************************
AC_CHECK_HEADERS([ctype.h errno.h fcntl.h grp.h limits.h locale.h malloc.h memory.h \ AC_CHECK_HEADERS([ctype.h errno.h fcntl.h grp.h limits.h locale.h memory.h \
paths.h pwd.h sched.h signal.h stdarg.h stdlib.h string.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/mman.h sys/param.h sys/stat.h sys/time.h sys/types.h \
sys/uio.h sys/wait.h time.h]) sys/uio.h sys/wait.h time.h])

View file

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

View file

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

View file

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

1304
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

957
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

1059
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

683
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

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

1179
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

1466
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

File diff suppressed because it is too large Load diff

1207
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

@ -172,6 +172,8 @@ thunar_SOURCES = \
thunar-shortcuts-icon-renderer.h \ thunar-shortcuts-icon-renderer.h \
thunar-shortcuts-model.c \ thunar-shortcuts-model.c \
thunar-shortcuts-model.h \ thunar-shortcuts-model.h \
thunar-shortcuts-progress-renderer.c \
thunar-shortcuts-progress-renderer.h \
thunar-shortcuts-pane.c \ thunar-shortcuts-pane.c \
thunar-shortcuts-pane.h \ thunar-shortcuts-pane.h \
thunar-shortcuts-view.c \ thunar-shortcuts-view.c \

View file

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

View file

@ -1525,17 +1525,7 @@ thunar_action_manager_action_open_location (ThunarActionManager *action_mgr)
return TRUE; return TRUE;
for (lp = action_mgr->files_to_process; lp != NULL; lp = lp->next) 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); thunar_window_open_files_in_location (THUNAR_WINDOW (action_mgr->widget), gfiles);
@ -1668,7 +1658,7 @@ thunar_action_manager_append_menu_item (ThunarActionManager *action_mgr,
case THUNAR_ACTION_MANAGER_ACTION_OPEN_IN_TAB: case THUNAR_ACTION_MANAGER_ACTION_OPEN_IN_TAB:
n = action_mgr->n_files_to_process > 0 ? action_mgr->n_files_to_process : 1; n = action_mgr->n_files_to_process > 0 ? action_mgr->n_files_to_process : 1;
label_text = g_strdup_printf (ngettext ("Open in new _Tab", "Open in %d New _Tabs", n), n); label_text = g_strdup_printf (ngettext (action_entry->menu_item_label_text, "Open in %d New _Tabs", n), n);
tooltip_text = g_strdup_printf (ngettext ("Open the selected directory in new tab", tooltip_text = g_strdup_printf (ngettext ("Open the selected directory in new tab",
"Open the selected directories in %d new tabs", n), "Open the selected directories in %d new tabs", n),
n); n);
@ -1679,7 +1669,7 @@ thunar_action_manager_append_menu_item (ThunarActionManager *action_mgr,
case THUNAR_ACTION_MANAGER_ACTION_OPEN_IN_WINDOW: case THUNAR_ACTION_MANAGER_ACTION_OPEN_IN_WINDOW:
n = action_mgr->n_files_to_process > 0 ? action_mgr->n_files_to_process : 1; n = action_mgr->n_files_to_process > 0 ? action_mgr->n_files_to_process : 1;
label_text = g_strdup_printf (ngettext ("Open in new _Window", "Open in %d New _Windows", n), n); label_text = g_strdup_printf (ngettext (action_entry->menu_item_label_text, "Open in %d New _Windows", n), n);
tooltip_text = g_strdup_printf (ngettext ("Open the selected directory in new window", tooltip_text = g_strdup_printf (ngettext ("Open the selected directory in new window",
"Open the selected directories in %d new windows", n), "Open the selected directories in %d new windows", n),
n); n);
@ -1717,7 +1707,7 @@ thunar_action_manager_append_menu_item (ThunarActionManager *action_mgr,
if (!show_item && !force) if (!show_item && !force)
return NULL; return NULL;
label_text = ngettext ("Ma_ke Link", "Ma_ke Links", action_mgr->n_files_to_process); label_text = ngettext (action_entry->menu_item_label_text, "Ma_ke Links", action_mgr->n_files_to_process);
tooltip_text = ngettext ("Create a symbolic link for the selected file", 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); "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, item = xfce_gtk_menu_item_new (label_text, tooltip_text, action_entry->accel_path, action_entry->callback,
@ -2401,10 +2391,6 @@ 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 (THUNAR_IS_ACTION_MANAGER (action_mgr), FALSE);
_thunar_return_val_if_fail (GTK_IS_MENU (menu), 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 */ /* determine the toplevel window we belong to */
window = gtk_widget_get_toplevel (action_mgr->widget); window = gtk_widget_get_toplevel (action_mgr->widget);
@ -2416,6 +2402,10 @@ thunar_action_manager_append_custom_actions (ThunarActionManager *action_mgr,
if (G_UNLIKELY (providers == NULL)) if (G_UNLIKELY (providers == NULL))
return FALSE; 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 */ /* load the menu items offered by the menu providers */
for (lp_provider = providers; lp_provider != NULL; lp_provider = lp_provider->next) for (lp_provider = providers; lp_provider != NULL; lp_provider = lp_provider->next)
{ {
@ -2676,7 +2666,7 @@ thunar_action_manager_action_trash_delete (ThunarActionManager *action_mgr)
/* when shift modifier is pressed, we delete (as well via context menu) */ /* 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) if (gtk_get_current_event_state (&event_state) && (event_state & GDK_SHIFT_MASK) != 0)
thunar_action_manager_action_delete (action_mgr); thunar_action_manager_action_delete (action_mgr);
else if (thunar_action_manager_show_trash (action_mgr)) else if (thunar_g_vfs_is_uri_scheme_supported ("trash") && !thunar_file_is_trashed (action_mgr->current_directory))
thunar_action_manager_action_move_to_trash (action_mgr); thunar_action_manager_action_move_to_trash (action_mgr);
else else
thunar_action_manager_action_delete (action_mgr); thunar_action_manager_action_delete (action_mgr);

View file

@ -27,9 +27,6 @@
#ifdef HAVE_ERRNO_H #ifdef HAVE_ERRNO_H
#include <errno.h> #include <errno.h>
#endif #endif
#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif
#ifdef HAVE_MEMORY_H #ifdef HAVE_MEMORY_H
#include <memory.h> #include <memory.h>
#endif #endif
@ -2441,7 +2438,7 @@ unlink_stub (GList *source_path_list,
* @parent : a #GdkScreen, a #GtkWidget or %NULL. * @parent : a #GdkScreen, a #GtkWidget or %NULL.
* @file_list : the list of #ThunarFile<!---->s that should be deleted. * @file_list : the list of #ThunarFile<!---->s that should be deleted.
* @permanently : whether to unlink the files permanently. * @permanently : whether to unlink the files permanently.
* @warn : whether to warn the user if moving files to trash. * @warn : whether to warn the user if deleting permanently.
* @log_mode : log mode * @log_mode : log mode
* *
* Deletes all files in the @file_list and takes care of all user interaction. * Deletes all files in the @file_list and takes care of all user interaction.
@ -2462,7 +2459,7 @@ thunar_application_unlink_files (ThunarApplication *application,
{ {
GtkWidget *dialog; GtkWidget *dialog;
GtkWidget *message_area; GtkWidget *message_area;
GtkWindow *window = NULL; GtkWindow *window;
GdkScreen *screen; GdkScreen *screen;
GList *path_list = NULL; GList *path_list = NULL;
GList *lp; GList *lp;
@ -2492,7 +2489,7 @@ thunar_application_unlink_files (ThunarApplication *application,
return FALSE; return FALSE;
/* ask the user to confirm if deleting permanently */ /* ask the user to confirm if deleting permanently */
if (G_UNLIKELY (permanently)) if (G_UNLIKELY (permanently) && warn)
{ {
/* parse the parent pointer */ /* parse the parent pointer */
screen = thunar_util_parse_parent (parent, &window); screen = thunar_util_parse_parent (parent, &window);
@ -2558,6 +2555,12 @@ thunar_application_unlink_files (ThunarApplication *application,
else else
operation_canceled = TRUE; 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) else if (G_LIKELY (!permanently) && warn)
{ {
/* parse the parent pointer */ /* parse the parent pointer */
@ -2757,7 +2760,7 @@ thunar_application_empty_trash (ThunarApplication *application,
const gchar *startup_id) const gchar *startup_id)
{ {
GtkWidget *dialog; GtkWidget *dialog;
GtkWindow *window = NULL; GtkWindow *window;
GdkScreen *screen; GdkScreen *screen;
GList file_list; GList file_list;
gint response; gint response;
@ -2905,28 +2908,3 @@ thunar_application_get_thumbnail_cache (ThunarApplication *application)
return g_object_ref (application->thumbnail_cache); 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,9 +205,6 @@ thunar_application_get_thumbnail_cache (ThunarApplication *application);
gboolean gboolean
thunar_application_accel_map_init (ThunarApplication *application); thunar_application_accel_map_init (ThunarApplication *application);
void
thunar_application_malloc_trim_on_idle (ThunarApplication *application);
G_END_DECLS; G_END_DECLS;
#endif /* !__THUNAR_APPLICATION_H__ */ #endif /* !__THUNAR_APPLICATION_H__ */

View file

@ -1238,9 +1238,6 @@ thunar_details_view_row_activated (GtkTreeView *tree_view,
_thunar_return_if_fail (THUNAR_IS_DETAILS_VIEW (details_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)); window = gtk_widget_get_toplevel (GTK_WIDGET (details_view));
action_mgr = thunar_window_get_action_manager (THUNAR_WINDOW (window)); action_mgr = thunar_window_get_action_manager (THUNAR_WINDOW (window));
thunar_action_manager_activate_selected_files (action_mgr, THUNAR_ACTION_MANAGER_CHANGE_DIRECTORY, NULL); thunar_action_manager_activate_selected_files (action_mgr, THUNAR_ACTION_MANAGER_CHANGE_DIRECTORY, NULL);
@ -1312,7 +1309,7 @@ thunar_details_view_row_changed (GtkTreeView *tree_view,
{ {
_thunar_return_if_fail (THUNAR_IS_DETAILS_VIEW (details_view)); _thunar_return_if_fail (THUNAR_IS_DETAILS_VIEW (details_view));
gtk_widget_queue_draw (GTK_WIDGET (details_view->tree_view)); gtk_widget_queue_draw (GTK_WIDGET (details_view));
} }

View file

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

View file

@ -230,9 +230,8 @@ struct _ThunarFile
/* flags for thumbnail state etc */ /* flags for thumbnail state etc */
ThunarFileFlags flags; ThunarFileFlags flags;
/* event source id and variable to rate-limit file-changed signals */ /* event source id used to rate-limit file-changed signals */
guint signal_changed_source_id; guint signal_changed_source_id;
gboolean signal_change_requested;
/* Number of files in this directory (only used if this #Thunarfile is a directory) */ /* 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 /* Note that this feature was added into #ThunarFile on purpose, because having inside #ThunarFolder caused lag when
@ -1205,7 +1204,6 @@ thunar_file_load (ThunarFile *file,
{ {
GError *err = NULL; GError *err = NULL;
GFileInfo *info = NULL; GFileInfo *info = NULL;
gboolean mounted = TRUE;
_thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE); _thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
_thunar_return_val_if_fail (error == NULL || *error == NULL, FALSE); _thunar_return_val_if_fail (error == NULL || *error == NULL, FALSE);
@ -1223,12 +1221,12 @@ thunar_file_load (ThunarFile *file,
G_FILE_QUERY_INFO_NONE, G_FILE_QUERY_INFO_NONE,
cancellable, &err); cancellable, &err);
/* check if mounted (but do not update the file until after it has been cleared) */ /* update the mounted info */
if (err != NULL if (err != NULL
&& err->domain == G_IO_ERROR && err->domain == G_IO_ERROR
&& err->code == G_IO_ERROR_NOT_MOUNTED) && err->code == G_IO_ERROR_NOT_MOUNTED)
{ {
mounted = FALSE; FLAG_UNSET (file, THUNAR_FILE_FLAG_IS_MOUNTED);
g_clear_error (&err); g_clear_error (&err);
} }
@ -1256,10 +1254,6 @@ thunar_file_load (ThunarFile *file,
/* update the file from the information */ /* update the file from the information */
thunar_file_info_reload (file, cancellable); 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 */ /* (re)insert the file into the cache */
if (file->kind != G_FILE_TYPE_UNKNOWN) if (file->kind != G_FILE_TYPE_UNKNOWN)
{ {
@ -1747,15 +1741,11 @@ thunar_file_execute (ThunarFile *file,
{ {
/* fake the Exec line */ /* fake the Exec line */
escaped_location = g_shell_quote (location); escaped_location = g_shell_quote (location);
exec = g_strconcat (escaped_location, " %F", NULL);
command = xfce_expand_desktop_entry_field_codes (exec, uri_list, NULL, NULL, NULL, FALSE);
if (in_terminal) if (in_terminal)
{ exec = g_strconcat ("exo-open --launch TerminalEmulator ", escaped_location, " %F", NULL);
gchar *escaped = g_shell_quote (command); else
g_free (command); exec = g_strconcat (escaped_location, " %F", NULL);
command = g_strconcat ("exo-open --launch TerminalEmulator ", escaped, NULL); command = xfce_expand_desktop_entry_field_codes (exec, uri_list, NULL, NULL, NULL, FALSE);
g_free (escaped);
}
result = g_shell_parse_argv (command, NULL, &argv, error); result = g_shell_parse_argv (command, NULL, &argv, error);
g_free (escaped_location); g_free (escaped_location);
g_free (exec); g_free (exec);
@ -2117,8 +2107,7 @@ thunar_file_accepts_drop (ThunarFile *file,
{ {
/* default to copy and get outta here */ /* default to copy and get outta here */
suggested_action = GDK_ACTION_COPY; suggested_action = GDK_ACTION_COPY;
if (parent_thunar_file != NULL) g_object_unref (parent_thunar_file);
g_object_unref (parent_thunar_file);
break; break;
} }
if (parent_thunar_file != NULL) if (parent_thunar_file != NULL)
@ -5424,23 +5413,24 @@ thunar_file_reset_thumbnail (ThunarFile *file,
static gboolean static gboolean
thunar_file_changed_signal_timeout (gpointer data) thunar_file_changed_signal_emit (gpointer data)
{ {
ThunarFile *file = THUNAR_FILE (data); /* emit the changed signal on thunarx level */
_thunar_return_val_if_fail (THUNAR_IS_FILE (file), G_SOURCE_REMOVE); thunarx_file_info_changed (THUNARX_FILE_INFO (data));
/* 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; return G_SOURCE_REMOVE;
} }
static void
thunar_file_changed_signal_destroy (gpointer data)
{
THUNAR_FILE (data)->signal_changed_source_id = 0;
}
/** /**
* thunar_file_changed: * thunar_file_changed:
* @file : a #ThunarFile instance. * @file : a #ThunarFile instance.
@ -5451,17 +5441,10 @@ thunar_file_changed_signal_timeout (gpointer data)
void void
thunar_file_changed (ThunarFile *file) thunar_file_changed (ThunarFile *file)
{ {
/* in case a timeout is running, the change-signal will be delayed (performance) */ if (file->signal_changed_source_id == 0)
if (file->signal_changed_source_id != 0)
{ {
file->signal_change_requested = TRUE; file->signal_changed_source_id = g_timeout_add_full (G_PRIORITY_DEFAULT, FILE_CHANGED_SIGNAL_RATE_LIMIT,
return; thunar_file_changed_signal_emit,
file, thunar_file_changed_signal_destroy);
} }
/* 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)*/ /* Files which were changed recently. The key is a ThunarFile; value is NULL (unimportant)*/
GHashTable *changed_files_map; GHashTable *changed_files_map;
/* Files which were added recently. The key is a ThunarFile; value is NULL (unimportant)*/ /* Files which were changed recently. The key is a ThunarFile; value is NULL (unimportant)*/
GHashTable *added_files_map; GHashTable *added_files_map;
/* Files which were removed recently. The key is a ThunarFile; value is NULL (unimportant)*/ /* Files which were changed recently. The key is a ThunarFile; value is NULL (unimportant)*/
GHashTable *removed_files_map; GHashTable *removed_files_map;
/* Files inside this folder. The key is a ThunarFile; value is NULL (unimportant)*/ /* Files inside this folder. The key is a ThunarFile; value is NULL (unimportant)*/
@ -409,10 +409,7 @@ thunar_folder_finalize (GObject *object)
g_source_remove (folder->files_update_timeout_source_id); g_source_remove (folder->files_update_timeout_source_id);
if (folder->monitor != NULL) if (folder->monitor != NULL)
{ g_signal_handlers_disconnect_by_data (folder->monitor, folder);
g_signal_handlers_disconnect_by_data (folder->monitor, folder);
g_object_unref (folder->monitor);
}
if (folder->corresponding_file) if (folder->corresponding_file)
{ {
@ -597,25 +594,17 @@ _thunar_folder_files_update_timeout (gpointer data)
g_hash_table_remove_all (files); g_hash_table_remove_all (files);
g_hash_table_remove_all (folder->added_files_map); g_hash_table_remove_all (folder->added_files_map);
/* reload files which were changed and send a 'changed' signal for them, if required */ /* send a 'changed' signal for all files which changed */
g_hash_table_iter_init (&iter, folder->changed_files_map); g_hash_table_iter_init (&iter, folder->changed_files_map);
while (g_hash_table_iter_next (&iter, &key, NULL)) 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)) if (!g_hash_table_contains (folder->files_map, key))
continue; continue;
g_hash_table_add (files, g_object_ref (key)); 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_signal_emit (G_OBJECT (folder), folder_signals[FILES_CHANGED], 0, files);
g_hash_table_destroy (files); g_hash_table_destroy (files);
g_hash_table_remove_all (folder->changed_files_map); g_hash_table_remove_all (folder->changed_files_map);
@ -656,10 +645,6 @@ thunar_folder_add_file (ThunarFolder *folder,
if (g_hash_table_remove (folder->removed_files_map, file)) if (g_hash_table_remove (folder->removed_files_map, file))
return; 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)); g_hash_table_add (folder->added_files_map, g_object_ref (file));
if (folder->files_update_timeout_source_id == 0) if (folder->files_update_timeout_source_id == 0)
@ -676,10 +661,6 @@ thunar_folder_remove_file (ThunarFolder *folder,
if (g_hash_table_remove (folder->added_files_map, file)) if (g_hash_table_remove (folder->added_files_map, file))
return; 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)); g_hash_table_add (folder->removed_files_map, g_object_ref (file));
if (folder->files_update_timeout_source_id == 0) if (folder->files_update_timeout_source_id == 0)
@ -1087,12 +1068,16 @@ thunar_folder_monitor (GFileMonitor *monitor,
case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED: case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
if (event_file_thunar != NULL) if (event_file_thunar != NULL)
{ {
/* this will trigger a reload if the file and send a 'file-changed' signal */ /* If we have that file in our internal map, reload it and send a file-changed signal */
thunar_folder_file_changed (folder, event_file_thunar); if (g_hash_table_lookup (folder->files_map, event_file_thunar) != NULL)
{
thunar_folder_file_changed (folder, event_file_thunar);
#if DEBUG_FILE_CHANGES #if DEBUG_FILE_CHANGES
thunar_file_infos_equal (event_file_thunar, event_file); thunar_file_infos_equal (event_file_thunar, event_file);
#endif #endif
thunar_file_reload (event_file_thunar);
}
} }
default: default:

View file

@ -194,91 +194,9 @@ 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: * thunar_g_file_resolve_symlink:
* @file : a #GFile * @file : a #GFile.
* *
* Returns the resolved symlink target of @file as a new #GFile. * Returns the resolved symlink target of @file as a new #GFile.
* If @file is not a symlink, @file will just be returned * If @file is not a symlink, @file will just be returned
@ -288,7 +206,27 @@ _thunar_g_file_resolve_symlink_internal (GFile *file, GHashTable *lookup_table)
GFile * GFile *
thunar_g_file_resolve_symlink (GFile *file) thunar_g_file_resolve_symlink (GFile *file)
{ {
return _thunar_g_file_resolve_symlink_internal (file, NULL); 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;
} }
@ -1822,10 +1760,8 @@ thunar_g_file_get_content_type (GFile *gfile)
g_object_unref (info); g_object_unref (info);
info = NULL; info = NULL;
if (link_target != NULL) if (link_target != NULL)
{ info = thunar_g_file_get_content_type_querry_info (link_target, err);
info = thunar_g_file_get_content_type_querry_info (link_target, err); g_object_unref (link_target);
g_object_unref (link_target);
}
} }
} }

View file

@ -151,8 +151,6 @@ thunar_gtk_menu_thunarx_menu_item_new (GObject *thunarx_menu_item,
g_free (icon_name); g_free (icon_name);
if (icon != NULL) if (icon != NULL)
g_object_unref (icon); g_object_unref (icon);
if (thunarx_menu != NULL)
g_object_unref (thunarx_menu);
return gtk_menu_item; return gtk_menu_item;
} }
@ -189,7 +187,7 @@ thunar_gtk_menu_clean (GtkMenu *menu)
* thunar_gtk_menu_run: * thunar_gtk_menu_run:
* @menu : a #GtkMenu. * @menu : a #GtkMenu.
* *
* Convenience wrapper for thunar_gtk_menu_run_at_event() to run a menu for the current event * Conveniance wrapper for thunar_gtk_menu_run_at_event_pointer, to run a menu for the current event
**/ **/
void void
thunar_gtk_menu_run (GtkMenu *menu) thunar_gtk_menu_run (GtkMenu *menu)
@ -209,51 +207,6 @@ 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: * thunar_gtk_menu_run_at_event:
* @menu : a #GtkMenu. * @menu : a #GtkMenu.
@ -268,8 +221,7 @@ thunar_gtk_menu_popup_at_pointer (GtkMenu *menu,
* *
**/ **/
void void
thunar_gtk_menu_run_at_event (GtkMenu *menu, thunar_gtk_menu_run_at_event (GtkMenu *menu, GdkEvent *event)
GdkEvent *event)
{ {
GMainLoop *loop; GMainLoop *loop;
gulong signal_id; gulong signal_id;
@ -282,7 +234,7 @@ thunar_gtk_menu_run_at_event (GtkMenu *menu,
/* run an internal main loop */ /* run an internal main loop */
loop = g_main_loop_new (NULL, FALSE); loop = g_main_loop_new (NULL, FALSE);
signal_id = g_signal_connect_swapped (G_OBJECT (menu), "deactivate", G_CALLBACK (g_main_loop_quit), loop); signal_id = g_signal_connect_swapped (G_OBJECT (menu), "deactivate", G_CALLBACK (g_main_loop_quit), loop);
thunar_gtk_menu_popup_at_pointer (menu, event); gtk_menu_popup_at_pointer (menu, event);
gtk_menu_reposition (menu); gtk_menu_reposition (menu);
gtk_grab_add (GTK_WIDGET (menu)); gtk_grab_add (GTK_WIDGET (menu));
g_main_loop_run (loop); g_main_loop_run (loop);

View file

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

View file

@ -259,7 +259,6 @@ thunar_preferences_dialog_init (ThunarPreferencesDialog *dialog)
GtkWidget *vbox; GtkWidget *vbox;
GtkWidget *infobar; GtkWidget *infobar;
GEnumClass *type; GEnumClass *type;
GList *windows;
gchar *date; gchar *date;
gint row = 0; gint row = 0;
@ -1421,29 +1420,16 @@ thunar_preferences_dialog_init (ThunarPreferencesDialog *dialog)
gtk_widget_show (vbox); gtk_widget_show (vbox);
frame = g_object_new (GTK_TYPE_FRAME, "border-width", 0, "shadow-type", GTK_SHADOW_IN, NULL); frame = g_object_new (GTK_TYPE_FRAME, "border-width", 0, "shadow-type", GTK_SHADOW_IN, NULL);
gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, TRUE, 0);
gtk_widget_show (frame); gtk_widget_show (frame);
/* check if the accel map has been initialized which is done in thunar-window.c */ grid = xfce_shortcuts_editor_new (13,
windows = thunar_application_get_windows (thunar_application_get ()); _("Window"), thunar_window_get_action_entries (), (size_t) THUNAR_WINDOW_N_ACTIONS,
if (windows != NULL) _("View"), thunar_standard_view_get_action_entries (), (size_t) THUNAR_STANDARD_VIEW_N_ACTIONS,
{ _("Launcher"), thunar_action_manager_get_action_entries (), (size_t) THUNAR_ACTION_MANAGER_N_ACTIONS,
grid = xfce_shortcuts_editor_new (13, _("Status Bar"), thunar_statusbar_get_action_entries (), (size_t) THUNAR_STATUS_BAR_N_ACTIONS);
_("Window"), thunar_window_get_action_entries (), (size_t) THUNAR_WINDOW_N_ACTIONS, gtk_container_add (GTK_CONTAINER (frame), grid);
_("View"), thunar_standard_view_get_action_entries (), (size_t) THUNAR_STANDARD_VIEW_N_ACTIONS, gtk_widget_show (grid);
_("Launcher"), thunar_action_manager_get_action_entries (), (size_t) THUNAR_ACTION_MANAGER_N_ACTIONS,
_("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,7 +381,6 @@ thunar_progress_dialog_add_job (ThunarProgressDialog *dialog,
else else
{ {
dialog->views_waiting = g_list_append (dialog->views_waiting, view); dialog->views_waiting = g_list_append (dialog->views_waiting, view);
thunar_job_freeze (job);
} }
g_list_free (job_list); g_list_free (job_list);

View file

@ -658,10 +658,7 @@ thunar_progress_view_new_with_job (ThunarJob *job)
_thunar_return_val_if_fail (job == NULL || THUNAR_IS_JOB (job), NULL); _thunar_return_val_if_fail (job == NULL || THUNAR_IS_JOB (job), NULL);
view = g_object_new (THUNAR_TYPE_PROGRESS_VIEW, "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); return GTK_WIDGET (view);
} }

View file

@ -1705,14 +1705,12 @@ thunar_properties_dialog_update_multiple (ThunarPropertiesDialog *dialog)
} }
else if (tmp_parent != NULL) 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)))
{ {
/* we only display the location if they are all equal */ if (parent_file != NULL)
if (!g_file_equal (thunar_file_get_file (parent_file), thunar_file_get_file (tmp_parent))) g_object_unref (G_OBJECT (parent_file));
{ parent_file = NULL;
g_object_unref (G_OBJECT (parent_file));
parent_file = NULL;
}
} }
g_object_unref (G_OBJECT (tmp_parent)); g_object_unref (G_OBJECT (tmp_parent));

View file

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

View file

@ -217,6 +217,8 @@ struct _ThunarShortcutsModel
guint bookmarks_idle_id; guint bookmarks_idle_id;
guint busy_timeout_id; guint busy_timeout_id;
gboolean bookmark_separator_added;
}; };
struct _ThunarShortcut struct _ThunarShortcut
@ -236,15 +238,9 @@ struct _ThunarShortcut
ThunarDevice *device; ThunarDevice *device;
guint hidden : 1; guint hidden : 1;
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, G_DEFINE_TYPE_WITH_CODE (ThunarShortcutsModel, thunar_shortcuts_model, G_TYPE_OBJECT,
@ -542,6 +538,12 @@ thunar_shortcuts_model_get_column_type (GtkTreeModel *tree_model,
case THUNAR_SHORTCUTS_MODEL_COLUMN_HIDDEN: case THUNAR_SHORTCUTS_MODEL_COLUMN_HIDDEN:
return G_TYPE_BOOLEAN; return G_TYPE_BOOLEAN;
case THUNAR_SHORTCUTS_MODEL_COLUMN_USAGE_PERCENT:
return G_TYPE_INT;
case THUNAR_SHORTCUTS_MODEL_COLUMN_IS_SEPARATOR:
return G_TYPE_BOOLEAN;
} }
_thunar_assert_not_reached (); _thunar_assert_not_reached ();
@ -822,6 +824,45 @@ thunar_shortcuts_model_get_value (GtkTreeModel *tree_model,
g_value_set_boolean (value, FALSE); g_value_set_boolean (value, FALSE);
break; break;
case THUNAR_SHORTCUTS_MODEL_COLUMN_IS_SEPARATOR:
g_value_init (value, G_TYPE_BOOLEAN);
g_value_set_boolean (value, shortcut->is_separator);
break;
case THUNAR_SHORTCUTS_MODEL_COLUMN_USAGE_PERCENT:
g_value_init (value, G_TYPE_INT);
if ((shortcut->group & THUNAR_SHORTCUT_GROUP_DEVICES) != 0
&& (shortcut->group & THUNAR_SHORTCUT_GROUP_HEADER) == 0)
{
guint64 fs_free = 0, fs_total = 0;
if (shortcut->device != NULL)
file = thunar_device_get_root (shortcut->device);
else if (shortcut->file != NULL)
file = g_object_ref (thunar_file_get_file (shortcut->file));
else
file = NULL;
if (file != NULL
&& thunar_g_file_get_free_space (file, &fs_free, &fs_total)
&& fs_total > 0)
{
g_value_set_int (value, (gint) ((fs_total - fs_free) * 100 / fs_total));
}
else
{
g_value_set_int (value, -1);
}
if (file != NULL)
g_object_unref (file);
}
else
{
g_value_set_int (value, -1);
}
break;
default: default:
_thunar_assert_not_reached (); _thunar_assert_not_reached ();
} }
@ -1380,6 +1421,18 @@ thunar_shortcuts_model_load_line (GFile *file_path,
_thunar_return_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model)); _thunar_return_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model));
_thunar_return_if_fail (name == NULL || g_utf8_validate (name, -1, NULL)); _thunar_return_if_fail (name == NULL || g_utf8_validate (name, -1, NULL));
/* insert a separator before the first remote bookmark */
if (!thunar_shortcuts_model_local_file (file_path)
&& !model->bookmark_separator_added)
{
ThunarShortcut *sep = g_slice_new0 (ThunarShortcut);
sep->group = THUNAR_SHORTCUT_GROUP_PLACES_BOOKMARKS;
sep->is_separator = TRUE;
sep->sort_id = row_num;
thunar_shortcuts_model_add_shortcut (model, sep);
model->bookmark_separator_added = TRUE;
}
shortcut = g_slice_new0 (ThunarShortcut); shortcut = g_slice_new0 (ThunarShortcut);
shortcut->group = THUNAR_SHORTCUT_GROUP_PLACES_BOOKMARKS; shortcut->group = THUNAR_SHORTCUT_GROUP_PLACES_BOOKMARKS;
shortcut->location = g_object_ref (file_path); shortcut->location = g_object_ref (file_path);
@ -1441,6 +1494,9 @@ thunar_shortcuts_model_reload_bookmarks (gpointer data)
_thunar_return_val_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model), FALSE); _thunar_return_val_if_fail (THUNAR_IS_SHORTCUTS_MODEL (model), FALSE);
/* reset separator flag */
model->bookmark_separator_added = FALSE;
/* Find all user-defined shortcuts (bookmarks) */ /* Find all user-defined shortcuts (bookmarks) */
for (lp = model->shortcuts; lp != NULL; lp = lp->next) for (lp = model->shortcuts; lp != NULL; lp = lp->next)
{ {
@ -1554,40 +1610,13 @@ 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 static void
thunar_shortcuts_model_device_added (ThunarDeviceMonitor *device_monitor, thunar_shortcuts_model_device_added (ThunarDeviceMonitor *device_monitor,
ThunarDevice *device, ThunarDevice *device,
ThunarShortcutsModel *model) ThunarShortcutsModel *model)
{ {
ThunarShortcut *shortcut; ThunarShortcut *shortcut;
ThunarShortcutFileGetData *data; GFile *mount_point;
GFile *mount_point;
_thunar_return_if_fail (device_monitor == NULL || THUNAR_DEVICE_MONITOR (device_monitor)); _thunar_return_if_fail (device_monitor == NULL || THUNAR_DEVICE_MONITOR (device_monitor));
_thunar_return_if_fail (device_monitor == NULL || model->device_monitor == device_monitor); _thunar_return_if_fail (device_monitor == NULL || model->device_monitor == device_monitor);
@ -1599,6 +1628,13 @@ thunar_shortcuts_model_device_added (ThunarDeviceMonitor *device_monitor,
shortcut->device = g_object_ref (device); shortcut->device = g_object_ref (device);
shortcut->hidden = thunar_device_get_hidden (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)) switch (thunar_device_get_kind (device))
{ {
case THUNAR_DEVICE_KIND_VOLUME: case THUNAR_DEVICE_KIND_VOLUME:
@ -1614,27 +1650,13 @@ thunar_shortcuts_model_device_added (ThunarDeviceMonitor *device_monitor,
break; break;
} }
/* insert in the model */
thunar_shortcuts_model_add_shortcut (model, shortcut);
/* header visibility if call is from monitor */ /* header visibility if call is from monitor */
gboolean update_visibility = (device_monitor != NULL && !shortcut->hidden); if (device_monitor != NULL
&& !shortcut->hidden)
mount_point = thunar_device_get_root (device); thunar_shortcuts_model_header_visibility (model);
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);
if (update_visibility)
thunar_shortcuts_model_header_visibility (model);
}
} }
@ -1657,12 +1679,7 @@ thunar_shortcuts_model_device_removed (ThunarDeviceMonitor *device_monitor,
break; break;
/* something is broken if we don't have a shortcut here */ /* something is broken if we don't have a shortcut here */
if (lp == NULL) _thunar_assert (lp != NULL);
{
g_warning ("No shortcut found for device '%s'", thunar_device_get_name (device));
return;
}
_thunar_assert (THUNAR_SHORTCUT (lp->data)->device == device); _thunar_assert (THUNAR_SHORTCUT (lp->data)->device == device);
/* drop the shortcut from the model */ /* drop the shortcut from the model */
@ -1696,12 +1713,7 @@ thunar_shortcuts_model_device_changed (ThunarDeviceMonitor *device_monitor,
break; break;
/* something is broken if we don't have a shortcut here */ /* something is broken if we don't have a shortcut here */
if (lp == NULL) _thunar_assert (lp != NULL);
{
g_warning ("No shortcut found for device '%s'", thunar_device_get_name (device));
return;
}
_thunar_assert (THUNAR_SHORTCUT (lp->data)->device == device); _thunar_assert (THUNAR_SHORTCUT (lp->data)->device == device);
if (G_LIKELY (lp != NULL)) if (G_LIKELY (lp != NULL))
@ -1774,7 +1786,8 @@ thunar_shortcuts_model_file_destroyed (ThunarFile *file,
/* remove the thunar file from the list and set the g_file instead */ /* remove the thunar file from the list and set the g_file instead */
if (shortcut->location == NULL) if (shortcut->location == NULL)
{ {
shortcut->location = g_object_ref (thunar_file_get_file (file)) ; GFile *g_file = thunar_file_get_file (file);
shortcut->location = g_file;
} }
if (shortcut->gicon == NULL) if (shortcut->gicon == NULL)

View file

@ -53,6 +53,8 @@ typedef enum
THUNAR_SHORTCUTS_MODEL_COLUMN_BUSY, THUNAR_SHORTCUTS_MODEL_COLUMN_BUSY,
THUNAR_SHORTCUTS_MODEL_COLUMN_BUSY_PULSE, THUNAR_SHORTCUTS_MODEL_COLUMN_BUSY_PULSE,
THUNAR_SHORTCUTS_MODEL_COLUMN_HIDDEN, THUNAR_SHORTCUTS_MODEL_COLUMN_HIDDEN,
THUNAR_SHORTCUTS_MODEL_COLUMN_USAGE_PERCENT,
THUNAR_SHORTCUTS_MODEL_COLUMN_IS_SEPARATOR,
THUNAR_SHORTCUTS_MODEL_N_COLUMNS, THUNAR_SHORTCUTS_MODEL_N_COLUMNS,
} ThunarShortcutsModelColumn; } ThunarShortcutsModelColumn;

View file

@ -0,0 +1,332 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2026
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "thunar/thunar-shortcuts-progress-renderer.h"
/* Height of the usage bar in pixels */
#define BAR_HEIGHT 4
/* Gap between the text and the bar */
#define BAR_TOP_PAD 2
/* Corner radius */
#define BAR_RADIUS 2
enum
{
PROP_0,
PROP_USAGE_PERCENT,
PROP_IS_SEPARATOR,
};
struct _ThunarShortcutsProgressRendererClass
{
GtkCellRendererTextClass __parent__;
};
struct _ThunarShortcutsProgressRenderer
{
GtkCellRendererText __parent__;
gint usage_percent; /* -1 = no bar, 0..100 = percent used */
gboolean is_separator;
};
G_DEFINE_TYPE (ThunarShortcutsProgressRenderer,
thunar_shortcuts_progress_renderer,
GTK_TYPE_CELL_RENDERER_TEXT)
static void
thunar_shortcuts_progress_renderer_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
ThunarShortcutsProgressRenderer *renderer = THUNAR_SHORTCUTS_PROGRESS_RENDERER (object);
switch (prop_id)
{
case PROP_USAGE_PERCENT:
g_value_set_int (value, renderer->usage_percent);
break;
case PROP_IS_SEPARATOR:
g_value_set_boolean (value, renderer->is_separator);
break;
default:
G_OBJECT_CLASS (thunar_shortcuts_progress_renderer_parent_class)->get_property (object, prop_id, value, pspec);
break;
}
}
static void
thunar_shortcuts_progress_renderer_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
ThunarShortcutsProgressRenderer *renderer = THUNAR_SHORTCUTS_PROGRESS_RENDERER (object);
switch (prop_id)
{
case PROP_USAGE_PERCENT:
renderer->usage_percent = g_value_get_int (value);
break;
case PROP_IS_SEPARATOR:
renderer->is_separator = g_value_get_boolean (value);
break;
default:
G_OBJECT_CLASS (thunar_shortcuts_progress_renderer_parent_class)->set_property (object, prop_id, value, pspec);
break;
}
}
static void
thunar_shortcuts_progress_renderer_get_preferred_height (GtkCellRenderer *cell,
GtkWidget *widget,
gint *minimum,
gint *natural)
{
ThunarShortcutsProgressRenderer *renderer = THUNAR_SHORTCUTS_PROGRESS_RENDERER (cell);
gint min_h = 0, nat_h = 0;
/* let the parent (GtkCellRendererText) calculate text height */
GTK_CELL_RENDERER_CLASS (thunar_shortcuts_progress_renderer_parent_class)->get_preferred_height (cell, widget, &min_h, &nat_h);
/* add space for the bar if usage data is available */
if (renderer->usage_percent >= 0)
{
min_h += BAR_TOP_PAD + BAR_HEIGHT;
nat_h += BAR_TOP_PAD + BAR_HEIGHT;
}
if (minimum != NULL)
*minimum = min_h;
if (natural != NULL)
*natural = nat_h;
}
static void
thunar_shortcuts_progress_renderer_render (GtkCellRenderer *cell,
cairo_t *cr,
GtkWidget *widget,
const GdkRectangle *background_area,
const GdkRectangle *cell_area,
GtkCellRendererState flags)
{
ThunarShortcutsProgressRenderer *renderer = THUNAR_SHORTCUTS_PROGRESS_RENDERER (cell);
GtkStyleContext *context;
GdkRGBA bar_bg;
GdkRGBA bar_fg;
gint text_height;
gint bar_y;
gint bar_width;
gint xpad, ypad;
GdkRectangle text_area;
gtk_cell_renderer_get_padding (cell, &xpad, &ypad);
/* draw a horizontal line for separator rows */
if (renderer->is_separator)
{
GdkRGBA fg_color;
context = gtk_widget_get_style_context (widget);
gtk_style_context_get_color (context, gtk_style_context_get_state (context), &fg_color);
fg_color.alpha = 0.3;
cairo_save (cr);
gdk_cairo_set_source_rgba (cr, &fg_color);
cairo_set_line_width (cr, 1.0);
cairo_move_to (cr, cell_area->x + xpad, cell_area->y + cell_area->height / 2 + 0.5);
cairo_line_to (cr, cell_area->x + cell_area->width - xpad, cell_area->y + cell_area->height / 2 + 0.5);
cairo_stroke (cr);
cairo_restore (cr);
return;
}
if (renderer->usage_percent >= 0)
{
/* calculate how much space the bar needs, then shrink cell_area for text */
text_area = *cell_area;
text_area.height -= (BAR_TOP_PAD + BAR_HEIGHT);
/* let the parent draw the text in the reduced area */
GTK_CELL_RENDERER_CLASS (thunar_shortcuts_progress_renderer_parent_class)->render (
cell, cr, widget, background_area, &text_area, flags);
/* draw the usage bar below the text */
text_height = text_area.height;
bar_y = cell_area->y + ypad + text_height + BAR_TOP_PAD;
bar_width = cell_area->width - 2 * xpad;
if (bar_width <= 0)
return;
context = gtk_widget_get_style_context (widget);
/* bar background: semi-transparent grey */
bar_bg.red = 0.5;
bar_bg.green = 0.5;
bar_bg.blue = 0.5;
bar_bg.alpha = 0.3;
/* bar foreground color: red for >90%, otherwise theme selection color */
if (renderer->usage_percent > 90)
{
bar_fg.red = 0.85;
bar_fg.green = 0.2;
bar_fg.blue = 0.2;
bar_fg.alpha = 0.9;
}
else
{
if (!gtk_style_context_lookup_color (context, "theme_selected_bg_color", &bar_fg)
|| bar_fg.alpha < 0.01)
{
/* fallback blue */
bar_fg.red = 0.3;
bar_fg.green = 0.5;
bar_fg.blue = 0.8;
}
bar_fg.alpha = 0.9;
}
cairo_save (cr);
/* draw background track (rounded rectangle) */
{
gdouble x = cell_area->x + xpad;
gdouble y = bar_y;
gdouble w = bar_width;
gdouble h = BAR_HEIGHT;
gdouble r = BAR_RADIUS;
cairo_new_sub_path (cr);
cairo_arc (cr, x + w - r, y + r, r, -G_PI_2, 0);
cairo_arc (cr, x + w - r, y + h - r, r, 0, G_PI_2);
cairo_arc (cr, x + r, y + h - r, r, G_PI_2, G_PI);
cairo_arc (cr, x + r, y + r, r, G_PI, 3 * G_PI_2);
cairo_close_path (cr);
}
gdk_cairo_set_source_rgba (cr, &bar_bg);
cairo_fill (cr);
/* draw filled portion (rounded rectangle) */
if (renderer->usage_percent > 0)
{
gdouble x = cell_area->x + xpad;
gdouble y = bar_y;
gdouble w = bar_width * renderer->usage_percent / 100.0;
gdouble h = BAR_HEIGHT;
gdouble r = BAR_RADIUS;
if (w < 2 * r)
r = w / 2.0;
if (r < 0.5)
r = 0;
cairo_new_sub_path (cr);
cairo_arc (cr, x + w - r, y + r, r, -G_PI_2, 0);
cairo_arc (cr, x + w - r, y + h - r, r, 0, G_PI_2);
cairo_arc (cr, x + r, y + h - r, r, G_PI_2, G_PI);
cairo_arc (cr, x + r, y + r, r, G_PI, 3 * G_PI_2);
cairo_close_path (cr);
gdk_cairo_set_source_rgba (cr, &bar_fg);
cairo_fill (cr);
}
cairo_restore (cr);
}
else
{
/* no usage data: just draw the text normally */
GTK_CELL_RENDERER_CLASS (thunar_shortcuts_progress_renderer_parent_class)->render (
cell, cr, widget, background_area, cell_area, flags);
}
}
static void
thunar_shortcuts_progress_renderer_class_init (ThunarShortcutsProgressRendererClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass);
gobject_class->get_property = thunar_shortcuts_progress_renderer_get_property;
gobject_class->set_property = thunar_shortcuts_progress_renderer_set_property;
cell_class->get_preferred_height = thunar_shortcuts_progress_renderer_get_preferred_height;
cell_class->render = thunar_shortcuts_progress_renderer_render;
g_object_class_install_property (gobject_class,
PROP_USAGE_PERCENT,
g_param_spec_int ("usage-percent",
"usage-percent",
"Disk usage percentage (0-100, -1 = hidden)",
-1, 100, -1,
G_PARAM_READWRITE));
g_object_class_install_property (gobject_class,
PROP_IS_SEPARATOR,
g_param_spec_boolean ("is-separator",
"is-separator",
"Draw a horizontal line instead of text",
FALSE,
G_PARAM_READWRITE));
}
static void
thunar_shortcuts_progress_renderer_init (ThunarShortcutsProgressRenderer *renderer)
{
renderer->usage_percent = -1;
/* match the original text renderer settings */
g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
}
GtkCellRenderer *
thunar_shortcuts_progress_renderer_new (void)
{
return g_object_new (THUNAR_TYPE_SHORTCUTS_PROGRESS_RENDERER, NULL);
}

View file

@ -0,0 +1,38 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2026
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __THUNAR_SHORTCUTS_PROGRESS_RENDERER_H__
#define __THUNAR_SHORTCUTS_PROGRESS_RENDERER_H__
#include <gtk/gtk.h>
G_BEGIN_DECLS;
typedef struct _ThunarShortcutsProgressRendererClass ThunarShortcutsProgressRendererClass;
typedef struct _ThunarShortcutsProgressRenderer ThunarShortcutsProgressRenderer;
#define THUNAR_TYPE_SHORTCUTS_PROGRESS_RENDERER (thunar_shortcuts_progress_renderer_get_type ())
#define THUNAR_SHORTCUTS_PROGRESS_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_TYPE_SHORTCUTS_PROGRESS_RENDERER, ThunarShortcutsProgressRenderer))
GType thunar_shortcuts_progress_renderer_get_type (void) G_GNUC_CONST;
GtkCellRenderer *thunar_shortcuts_progress_renderer_new (void);
G_END_DECLS;
#endif /* !__THUNAR_SHORTCUTS_PROGRESS_RENDERER_H__ */

View file

@ -44,6 +44,7 @@
#include "thunar/thunar-private.h" #include "thunar/thunar-private.h"
#include "thunar/thunar-shortcuts-icon-renderer.h" #include "thunar/thunar-shortcuts-icon-renderer.h"
#include "thunar/thunar-shortcuts-model.h" #include "thunar/thunar-shortcuts-model.h"
#include "thunar/thunar-shortcuts-progress-renderer.h"
#include "thunar/thunar-shortcuts-view.h" #include "thunar/thunar-shortcuts-view.h"
#include <gdk/gdkkeysyms.h> #include <gdk/gdkkeysyms.h>
@ -319,6 +320,20 @@ thunar_shortcuts_view_navigator_init (ThunarNavigatorIface *iface)
static gboolean
thunar_shortcuts_view_row_separator (GtkTreeModel *model,
GtkTreeIter *iter,
gpointer data)
{
gboolean is_separator = FALSE;
gtk_tree_model_get (model, iter,
THUNAR_SHORTCUTS_MODEL_COLUMN_IS_SEPARATOR, &is_separator,
-1);
return is_separator;
}
static void static void
thunar_shortcuts_view_init (ThunarShortcutsView *view) thunar_shortcuts_view_init (ThunarShortcutsView *view)
{ {
@ -331,6 +346,8 @@ thunar_shortcuts_view_init (ThunarShortcutsView *view)
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view), FALSE); gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view), FALSE);
gtk_tree_view_set_tooltip_column (GTK_TREE_VIEW (view), THUNAR_SHORTCUTS_MODEL_COLUMN_TOOLTIP); gtk_tree_view_set_tooltip_column (GTK_TREE_VIEW (view), THUNAR_SHORTCUTS_MODEL_COLUMN_TOOLTIP);
/* no row_separator_func — separator is drawn by the progress renderer */
/* grab a reference on the provider factory */ /* grab a reference on the provider factory */
view->provider_factory = thunarx_provider_factory_get_default (); view->provider_factory = thunarx_provider_factory_get_default ();
@ -395,14 +412,14 @@ thunar_shortcuts_view_init (ThunarShortcutsView *view)
g_object_bind_property (G_OBJECT (view->preferences), "misc-symbolic-icons-in-sidepane", G_OBJECT (view->icon_renderer), "use-symbolic-icons", G_BINDING_SYNC_CREATE); g_object_bind_property (G_OBJECT (view->preferences), "misc-symbolic-icons-in-sidepane", G_OBJECT (view->icon_renderer), "use-symbolic-icons", G_BINDING_SYNC_CREATE);
g_signal_connect_swapped (G_OBJECT (view->preferences), "notify::misc-symbolic-icons-in-sidepane", G_CALLBACK (gtk_widget_queue_draw), view); g_signal_connect_swapped (G_OBJECT (view->preferences), "notify::misc-symbolic-icons-in-sidepane", G_CALLBACK (gtk_widget_queue_draw), view);
/* allocate the text renderer (ellipsizing as required, but "File System" must fit) */ /* allocate the text+progress renderer for device names with usage bars */
renderer = g_object_new (GTK_TYPE_CELL_RENDERER_TEXT, renderer = thunar_shortcuts_progress_renderer_new ();
"ellipsize", PANGO_ELLIPSIZE_END,
NULL);
g_signal_connect (G_OBJECT (renderer), "edited", G_CALLBACK (thunar_shortcuts_view_renamed), view); g_signal_connect (G_OBJECT (renderer), "edited", G_CALLBACK (thunar_shortcuts_view_renamed), view);
gtk_tree_view_column_pack_start (column, renderer, TRUE); gtk_tree_view_column_pack_start (column, renderer, TRUE);
gtk_tree_view_column_set_attributes (column, renderer, gtk_tree_view_column_set_attributes (column, renderer,
"text", THUNAR_SHORTCUTS_MODEL_COLUMN_NAME, "text", THUNAR_SHORTCUTS_MODEL_COLUMN_NAME,
"usage-percent", THUNAR_SHORTCUTS_MODEL_COLUMN_USAGE_PERCENT,
"is-separator", THUNAR_SHORTCUTS_MODEL_COLUMN_IS_SEPARATOR,
"visible", THUNAR_SHORTCUTS_MODEL_COLUMN_IS_ITEM, "visible", THUNAR_SHORTCUTS_MODEL_COLUMN_IS_ITEM,
NULL); NULL);

View file

@ -1500,10 +1500,6 @@ thunar_standard_view_update_selected_files (ThunarStandardView *standard_view,
/* determine the tree paths for the given files */ /* determine the tree paths for the given files */
paths = thunar_standard_view_model_get_paths_for_files (standard_view->model, files_to_select); 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)) if (G_LIKELY (paths != NULL))
{ {
/* determine the first path */ /* determine the first path */
@ -2460,7 +2456,6 @@ thunar_standard_view_update_statusbar_text_idle (gpointer data)
GList *selected_items_tree_path_list; GList *selected_items_tree_path_list;
GtkTreeIter iter; GtkTreeIter iter;
ThunarFile *file; ThunarFile *file;
gchar *statusbar_text;
_thunar_return_val_if_fail (THUNAR_IS_STANDARD_VIEW (standard_view), FALSE); _thunar_return_val_if_fail (THUNAR_IS_STANDARD_VIEW (standard_view), FALSE);
@ -2483,22 +2478,6 @@ thunar_standard_view_update_statusbar_text_idle (gpointer data)
if (thunar_standard_view_model_get_folder (standard_view->model) == NULL) if (thunar_standard_view_model_get_folder (standard_view->model) == NULL)
return FALSE; 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)); 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); g_signal_connect (standard_view->priv->statusbar_job, "error", G_CALLBACK (thunar_standard_view_update_statusbar_text_error), standard_view);
@ -2508,6 +2487,8 @@ thunar_standard_view_update_statusbar_text_idle (gpointer data)
} }
else if (selected_items_tree_path_list->next == NULL) /* only one item selected */ else if (selected_items_tree_path_list->next == NULL) /* only one item selected */
{ {
gchar *statusbar_text;
/* resolve the iter for the single path */ /* 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); gtk_tree_model_get_iter (GTK_TREE_MODEL (standard_view->model), &iter, selected_items_tree_path_list->data);
@ -2568,7 +2549,7 @@ thunar_standard_view_update_statusbar_text (ThunarStandardView *standard_view)
/* restart a new one, this way we avoid multiple update when /* restart a new one, this way we avoid multiple update when
* the user is pressing a key to scroll */ * the user is pressing a key to scroll */
standard_view->priv->statusbar_text_idle_id = standard_view->priv->statusbar_text_idle_id =
g_timeout_add_full (G_PRIORITY_DEFAULT, 50, thunar_standard_view_update_statusbar_text_idle, g_timeout_add_full (G_PRIORITY_LOW, 50, thunar_standard_view_update_statusbar_text_idle,
standard_view, NULL); standard_view, NULL);
} }
@ -4818,10 +4799,27 @@ thunar_standard_view_cell_layout_data_func (GtkCellLayout *layout,
/* since this function is being used for both icon & name renderers; /* since this function is being used for both icon & name renderers;
* we need to make sure the right properties are applied to the right renderers */ * we need to make sure the right properties are applied to the right renderers */
if (THUNAR_IS_TEXT_RENDERER (cell)) if (THUNAR_IS_TEXT_RENDERER (cell))
g_object_set (G_OBJECT (cell), {
"foreground-rgba", foreground != NULL ? &foreground_rgba : NULL, /* set disk usage bar for computer:// items */
"highlight-color", background, gint usage = -1;
NULL); GFile *gfile = thunar_file_get_file (file);
if (gfile != NULL && g_file_has_uri_scheme (gfile, "computer"))
{
GFile *root = thunar_file_get_target_location (file);
if (root == NULL)
root = gfile;
guint64 fs_free = 0, fs_total = 0;
if (thunar_g_file_get_free_space (root, &fs_free, &fs_total) && fs_total > 0)
usage = (gint) ((fs_total - fs_free) * 100 / fs_total);
if (root != gfile)
g_object_unref (root);
}
g_object_set (G_OBJECT (cell),
"foreground-rgba", foreground != NULL ? &foreground_rgba : NULL,
"highlight-color", background,
"usage-percent", usage,
NULL);
}
else if (THUNAR_IS_ICON_RENDERER (cell)) else if (THUNAR_IS_ICON_RENDERER (cell))
g_object_set (G_OBJECT (cell), g_object_set (G_OBJECT (cell),

View file

@ -23,6 +23,11 @@
#include "thunar/thunar-text-renderer.h" #include "thunar/thunar-text-renderer.h"
#include "thunar/thunar-util.h" #include "thunar/thunar-util.h"
#include <math.h>
#define USAGE_BAR_HEIGHT 4
#define USAGE_BAR_TOP_PAD 2
#define USAGE_BAR_RADIUS 2
@ -32,6 +37,7 @@ enum
PROP_HIGHLIGHT_COLOR, PROP_HIGHLIGHT_COLOR,
PROP_ROUNDED_CORNERS, PROP_ROUNDED_CORNERS,
PROP_HIGHLIGHTING_ENABLED, PROP_HIGHLIGHTING_ENABLED,
PROP_USAGE_PERCENT,
}; };
@ -55,6 +61,11 @@ thunar_text_renderer_render (GtkCellRenderer *renderer,
const GdkRectangle *background_area, const GdkRectangle *background_area,
const GdkRectangle *cell_area, const GdkRectangle *cell_area,
GtkCellRendererState flags); GtkCellRendererState flags);
static void
thunar_text_renderer_get_preferred_height (GtkCellRenderer *cell,
GtkWidget *widget,
gint *minimum,
gint *natural);
@ -77,6 +88,7 @@ struct _ThunarTextRenderer
gchar *highlight_color; gchar *highlight_color;
gboolean rounded_corners; gboolean rounded_corners;
gboolean highlighting_enabled; gboolean highlighting_enabled;
gint usage_percent; /* -1 = no bar, 0..100 = percent used */
}; };
@ -98,6 +110,7 @@ thunar_text_renderer_class_init (ThunarTextRendererClass *klass)
klass->default_render_function = cell_class->render; klass->default_render_function = cell_class->render;
cell_class->render = thunar_text_renderer_render; cell_class->render = thunar_text_renderer_render;
cell_class->get_preferred_height = thunar_text_renderer_get_preferred_height;
/** /**
* ThunarTextRenderer:highlight-color: * ThunarTextRenderer:highlight-color:
@ -136,6 +149,13 @@ thunar_text_renderer_class_init (ThunarTextRendererClass *klass)
g_param_spec_boolean ("highlighting-enabled", "highlighting-enabled", "highlighting-enabled", g_param_spec_boolean ("highlighting-enabled", "highlighting-enabled", "highlighting-enabled",
FALSE, FALSE,
EXO_PARAM_READWRITE)); EXO_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_USAGE_PERCENT,
g_param_spec_int ("usage-percent", "usage-percent",
"Disk usage percentage (0-100, -1 = hidden)",
-1, 100, -1,
G_PARAM_READWRITE));
} }
@ -144,6 +164,7 @@ static void
thunar_text_renderer_init (ThunarTextRenderer *text_renderer) thunar_text_renderer_init (ThunarTextRenderer *text_renderer)
{ {
text_renderer->highlight_color = NULL; text_renderer->highlight_color = NULL;
text_renderer->usage_percent = -1;
} }
@ -182,6 +203,10 @@ thunar_text_renderer_get_property (GObject *object,
g_value_set_boolean (value, text_renderer->highlighting_enabled); g_value_set_boolean (value, text_renderer->highlighting_enabled);
break; break;
case PROP_USAGE_PERCENT:
g_value_set_int (value, text_renderer->usage_percent);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -213,6 +238,10 @@ thunar_text_renderer_set_property (GObject *object,
text_renderer->highlighting_enabled = g_value_get_boolean (value); text_renderer->highlighting_enabled = g_value_get_boolean (value);
break; break;
case PROP_USAGE_PERCENT:
text_renderer->usage_percent = g_value_get_int (value);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -239,6 +268,91 @@ thunar_text_renderer_new (void)
static void
thunar_text_renderer_get_preferred_height (GtkCellRenderer *cell,
GtkWidget *widget,
gint *minimum,
gint *natural)
{
gint min_h = 0, nat_h = 0;
GTK_CELL_RENDERER_CLASS (thunar_text_renderer_parent_class)->get_preferred_height (cell, widget, &min_h, &nat_h);
if (minimum != NULL) *minimum = min_h;
if (natural != NULL) *natural = nat_h;
}
static void
thunar_text_renderer_draw_usage_bar (cairo_t *cr,
GtkWidget *widget,
const GdkRectangle *cell_area,
gint usage_percent)
{
GtkStyleContext *context;
GdkRGBA bar_bg = { 0.5, 0.5, 0.5, 0.3 };
GdkRGBA bar_fg;
gint xpad = 2;
gdouble x = cell_area->x + xpad;
gdouble y = cell_area->y + cell_area->height - USAGE_BAR_HEIGHT;
gdouble w = cell_area->width - 2 * xpad;
gdouble h = USAGE_BAR_HEIGHT;
gdouble r = USAGE_BAR_RADIUS;
gdouble fw;
if (w <= 0)
return;
context = gtk_widget_get_style_context (widget);
if (usage_percent > 90)
{
bar_fg.red = 0.85; bar_fg.green = 0.2; bar_fg.blue = 0.2; bar_fg.alpha = 0.9;
}
else
{
if (!gtk_style_context_lookup_color (context, "theme_selected_bg_color", &bar_fg)
|| bar_fg.alpha < 0.01)
{ bar_fg.red = 0.3; bar_fg.green = 0.5; bar_fg.blue = 0.8; }
bar_fg.alpha = 0.9;
}
cairo_save (cr);
/* background track */
cairo_new_sub_path (cr);
cairo_arc (cr, x + w - r, y + r, r, -G_PI_2, 0);
cairo_arc (cr, x + w - r, y + h - r, r, 0, G_PI_2);
cairo_arc (cr, x + r, y + h - r, r, G_PI_2, G_PI);
cairo_arc (cr, x + r, y + r, r, G_PI, 3 * G_PI_2);
cairo_close_path (cr);
gdk_cairo_set_source_rgba (cr, &bar_bg);
cairo_fill (cr);
/* filled portion */
if (usage_percent > 0)
{
fw = w * usage_percent / 100.0;
r = USAGE_BAR_RADIUS;
if (fw < 2 * r) r = fw / 2.0;
if (r < 0.5) r = 0;
cairo_new_sub_path (cr);
cairo_arc (cr, x + fw - r, y + r, r, -G_PI_2, 0);
cairo_arc (cr, x + fw - r, y + h - r, r, 0, G_PI_2);
cairo_arc (cr, x + r, y + h - r, r, G_PI_2, G_PI);
cairo_arc (cr, x + r, y + r, r, G_PI, 3 * G_PI_2);
cairo_close_path (cr);
gdk_cairo_set_source_rgba (cr, &bar_fg);
cairo_fill (cr);
}
cairo_restore (cr);
}
static void static void
thunar_text_renderer_render (GtkCellRenderer *cell, thunar_text_renderer_render (GtkCellRenderer *cell,
cairo_t *cr, cairo_t *cr,
@ -247,11 +361,16 @@ thunar_text_renderer_render (GtkCellRenderer *cell,
const GdkRectangle *cell_area, const GdkRectangle *cell_area,
GtkCellRendererState flags) GtkCellRendererState flags)
{ {
if (THUNAR_TEXT_RENDERER (cell)->highlighting_enabled) ThunarTextRenderer *text_renderer = THUNAR_TEXT_RENDERER (cell);
if (text_renderer->highlighting_enabled)
thunar_util_clip_view_background (cell, cr, background_area, widget, flags); thunar_util_clip_view_background (cell, cr, background_area, widget, flags);
/* we only needed to manipulate the background_area, otherwise everything remains the same. /* draw text normally */
Hence, we are simply running the original render function now */ THUNAR_TEXT_RENDERER_GET_CLASS (text_renderer)
THUNAR_TEXT_RENDERER_GET_CLASS (THUNAR_TEXT_RENDERER (cell))
->default_render_function (cell, cr, widget, background_area, cell_area, flags); ->default_render_function (cell, cr, widget, background_area, cell_area, flags);
/* draw usage bar at the very bottom of the cell, overlapping if needed */
if (text_renderer->usage_percent >= 0)
thunar_text_renderer_draw_usage_bar (cr, widget, cell_area, text_renderer->usage_percent);
} }

View file

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

View file

@ -1717,9 +1717,6 @@ _thunar_tree_view_model_cancel_search_job (ThunarTreeViewModel *model)
g_object_unref (model->search_job); g_object_unref (model->search_job);
model->search_job = NULL; model->search_job = NULL;
} }
thunar_g_list_free_full (model->search_files);
model->search_files = NULL;
} }
@ -2519,26 +2516,10 @@ thunar_tree_view_model_sort (ThunarTreeViewModel *model)
static void static void
_thunar_tree_view_model_folder_destroy (Node *node) _thunar_tree_view_model_folder_destroy (Node *node)
{ {
/* if a subdir gets destroyed, it's handled internally, but if the current_folder /* if a subdir get's destroyed it's handled internally but if the current_folder
* gets destroyed, then we simply set_folder to NULL & cleanup things */ * get's destroyed then we simply set_folder to NULL & cleanup things */
if (node->parent == NULL) if (node->parent == NULL)
{ thunar_tree_view_model_set_folder (THUNAR_STANDARD_VIEW_MODEL (node->model), NULL, 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);
}
} }
@ -2549,13 +2530,26 @@ _thunar_tree_view_model_folder_error (Node *node,
{ {
_thunar_return_if_fail (error != NULL); _thunar_return_if_fail (error != NULL);
if (node->parent == NULL) if (node->n_children > 0)
_thunar_tree_view_model_folder_destroy (node); {
else if (node->parent == NULL)
thunar_tree_view_model_dir_remove_file (node->parent, node->file); {
/* 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);
/* forward the error signal */ for (GList *lp = keys; lp != NULL; lp = lp->next)
g_signal_emit_by_name (G_OBJECT (node->model), "error", error); 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);
}
else
thunar_tree_view_model_dir_remove_file (node->parent, node->file);
}
} }
@ -2584,8 +2578,6 @@ _thunar_tree_view_model_dir_files_added (Node *node,
if (!g_hash_table_contains (node->set, file)) if (!g_hash_table_contains (node->set, file))
thunar_tree_view_model_dir_add_file (node, 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]);
} }
@ -2615,8 +2607,6 @@ _thunar_tree_view_model_dir_files_removed (Node *node,
thunar_tree_view_model_dir_remove_file (node, file); thunar_tree_view_model_dir_remove_file (node, file);
} }
g_object_notify_by_pspec (G_OBJECT (node->model), tree_model_props[PROP_NUM_FILES]);
} }
@ -2851,8 +2841,8 @@ thunar_tree_view_model_dir_files_changed (Node *node_parent,
{ {
if (!model->show_hidden) if (!model->show_hidden)
thunar_tree_view_model_dir_remove_file (node_parent, file); thunar_tree_view_model_dir_remove_file (node_parent, file);
g_hash_table_add (node_parent->hidden_files, g_object_ref (file)); g_hash_table_add (node_parent->hidden_files, g_object_ref (file));
continue;
} }
node = thunar_tree_view_model_locate_file (model, file); node = thunar_tree_view_model_locate_file (model, file);
@ -3124,12 +3114,9 @@ thunar_tree_view_model_update_search_files (ThunarTreeViewModel *model)
g_object_unref (file); g_object_unref (file);
} }
g_object_notify_by_pspec (G_OBJECT (model), tree_model_props[PROP_NUM_FILES]);
if (model->search_files != NULL) if (model->search_files != NULL)
thunar_g_list_free_full (model->search_files); g_list_free (model->search_files);
model->search_files = NULL; model->search_files = NULL;
g_mutex_unlock (&model->mutex_add_search_files); g_mutex_unlock (&model->mutex_add_search_files);
return TRUE; return TRUE;

View file

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

View file

@ -1256,7 +1256,6 @@ thunar_window_init (ThunarWindow *window)
window->trash_infobar = gtk_info_bar_new (); window->trash_infobar = gtk_info_bar_new ();
gtk_grid_attach (GTK_GRID (window->view_box), window->trash_infobar, 0, 2, 1, 1); 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); 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); 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, g_signal_connect (window->trash_infobar,
"response", "response",
@ -2406,7 +2405,6 @@ thunar_window_switch_current_view (ThunarWindow *window,
GSList *view_bindings; GSList *view_bindings;
ThunarFile *current_directory; ThunarFile *current_directory;
ThunarHistory *history; ThunarHistory *history;
gchar *search_query;
_thunar_return_if_fail (THUNAR_IS_WINDOW (window)); _thunar_return_if_fail (THUNAR_IS_WINDOW (window));
_thunar_return_if_fail (THUNAR_IS_VIEW (new_view)); _thunar_return_if_fail (THUNAR_IS_VIEW (new_view));
@ -2438,11 +2436,6 @@ thunar_window_switch_current_view (ThunarWindow *window,
window->view_bindings = NULL; window->view_bindings = NULL;
g_slist_free_full (view_bindings, g_object_unref); 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 */ /* update the directory of the current window */
current_directory = thunar_navigator_get_current_directory (THUNAR_NAVIGATOR (new_view)); current_directory = thunar_navigator_get_current_directory (THUNAR_NAVIGATOR (new_view));
thunar_window_set_current_directory (window, current_directory); thunar_window_set_current_directory (window, current_directory);
@ -2518,13 +2511,15 @@ thunar_window_switch_current_view (ThunarWindow *window,
if (thunar_file_is_trash (window->current_directory)) 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); 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 */ /* if the view has an ongoing search operation take that into account, otherwise cancel the current search (if there is one) */
if (search_query != NULL) if (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); gchar *str = g_strjoin (NULL, thunar_util_get_search_prefix (), thunar_standard_view_get_search_query (THUNAR_STANDARD_VIEW (window->view)), NULL);
thunar_window_resume_search (window, str); thunar_window_resume_search (window, str);
g_free (str); g_free (str);
} }
else if (window->search_query != NULL)
thunar_window_action_cancel_search (window);
/* switch to the new view */ /* switch to the new view */
thunar_window_notebook_set_current_tab (window, gtk_notebook_page_num (GTK_NOTEBOOK (window->notebook_selected), window->view)); thunar_window_notebook_set_current_tab (window, gtk_notebook_page_num (GTK_NOTEBOOK (window->notebook_selected), window->view));
@ -2648,10 +2643,6 @@ thunar_window_notebook_page_removed (GtkWidget *notebook,
/* drop connected signals */ /* drop connected signals */
g_signal_handlers_disconnect_by_data (page, window); 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)); n_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook));
if (n_pages == 0) if (n_pages == 0)
{ {
@ -2674,7 +2665,8 @@ thunar_window_notebook_page_removed (GtkWidget *notebook,
/* page from the other notebook was removed */ /* page from the other notebook was removed */
if (notebook != window->notebook_selected) if (notebook != window->notebook_selected)
thunar_window_paned_notebooks_switch (window); 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); thunar_window_notebook_select_current_page (window);
/* update tab visibility */ /* update tab visibility */
@ -3678,8 +3670,6 @@ thunar_window_reset_view_type_idle_destroyed (gpointer data)
gboolean gboolean
thunar_window_action_cancel_search (ThunarWindow *window) 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_LOCATION_BAR (window->location_bar), FALSE);
_thunar_return_val_if_fail (THUNAR_IS_WINDOW (window), FALSE); _thunar_return_val_if_fail (THUNAR_IS_WINDOW (window), FALSE);
@ -3718,11 +3708,6 @@ thunar_window_action_cancel_search (ThunarWindow *window)
/* bring back the original location bar style (relevant if the bar is hidden) */ /* bring back the original location bar style (relevant if the bar is hidden) */
thunar_window_update_location_bar_visible (window); 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 */ /* required in case of shortcut activation, in order to signal that the accel key got handled */
return TRUE; return TRUE;
} }