[PATCH] Improve performance of list view in single-click mode
- From: Zbigniew Chyla <mail zbigniew chyla pl>
- To: nautilus-list gnome org
- Subject: [PATCH] Improve performance of list view in single-click mode
- Date: Sun, 26 Feb 2006 21:35:24 +0100
The attached patches resolve performance problems and X cursor leak,
both of which occur when using list view in single-click mode.
I've reported this problem as bug #332669
http://bugzilla.gnome.org/show_bug.cgi?id=332669 and attached patches
for Nautilus 2.12 to the bug report.
The attached patches are intended for nautilus 2.13/HEAD.
Regards
Zbigniew
--- ChangeLog 26 Feb 2006 17:01:58 -0000
+++ ChangeLog 26 Feb 2006 17:21:44 -0000
@@ -0,0 +1,30 @@
+2006-02-26 Zbigniew Chyla <mail zbigniew chyla pl>
+
+ Avoid heavy operations inside "motion_notify_event" handler: don't
+ call gtk_tree_model_row_changed (which causes updating the whole
+ view), don't create new mouse cursor, change cursor only when
+ necessary.
+ As a bonus we avoid leaking hand cursor in nautilus and X server.
+
+ * src/file-manager/fm-list-view.c:
+ (hand_cursor): new global variable for storing hand cursor used in
+ single click mode
+ (motion_notify_callback): don't call gtk_tree_model_row_changed on
+ the model when changing rows, it's very slow and unnecessary - GTK+
+ automatically invalidates parts of the view occupied by old and new
+ row; don't leak the hand cursor (GdkCursor), change mouse cursor
+ only when necessary (from GDK_HAND2 to default and the other way
+ round)
+ (leave_notify_callback): don't call gtk_tree_model_row_changed on
+ the current row in the model, GTK+ automatically redraws the current
+ row
+ (enter_notify_callback): in case single click mode is used, update
+ details->hover_path and set hand cursor if necessary
+ (create_and_set_up_tree_view): connect to enter_notify_event signal
+ in addition to leave_notify_event.
+ (fm_list_view_click_policy_changed): unref global hand_cursor and
+ set it to NULL when changing to double click mode, create new cursor
+ and assign it to hand_cursor when changing to single click one.
+ (fm_list_view_finalize): free details->hover_path if necessary
+ (fm_list_view_init): explicitly set details->hover_path to NULL
+
--- src/file-manager/fm-list-view.c 21 Feb 2006 15:10:43 -0000
+++ src/file-manager/fm-list-view.c 26 Feb 2006 17:21:46 -0000
@@ -141,6 +141,7 @@ static gboolean default_sort_reversed_
static NautilusZoomLevel default_zoom_level_auto_value;
static GList * default_visible_columns_auto_value;
static GList * default_column_order_auto_value;
+static GdkCursor * hand_cursor = NULL;
static GList *fm_list_view_get_selection (FMDirectoryView *view);
static GList *fm_list_view_get_selection_for_file_transfer (FMDirectoryView *view);
@@ -457,9 +458,6 @@ motion_notify_callback (GtkWidget *widge
{
FMListView *view;
GdkDragContext *context;
- GdkCursor *cursor;
- GtkTreePath *last_hover_path;
- GtkTreeIter iter;
view = FM_LIST_VIEW (callback_data);
@@ -468,42 +466,25 @@ motion_notify_callback (GtkWidget *widge
}
if (click_policy_auto_value == NAUTILUS_CLICK_POLICY_SINGLE) {
- last_hover_path = view->details->hover_path;
+ GtkTreePath *old_hover_path;
+ old_hover_path = view->details->hover_path;
gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
event->x, event->y,
&view->details->hover_path,
NULL, NULL, NULL);
- if (view->details->hover_path != NULL) {
- cursor = gdk_cursor_new (GDK_HAND2);
- } else {
- cursor = NULL;
- }
-
- gdk_window_set_cursor (widget->window, cursor);
-
- /* only redraw if the hover row has changed */
- if (!(last_hover_path == NULL && view->details->hover_path == NULL) &&
- (!(last_hover_path != NULL && view->details->hover_path != NULL) ||
- gtk_tree_path_compare (last_hover_path, view->details->hover_path))) {
- if (last_hover_path) {
- if (gtk_tree_model_get_iter (GTK_TREE_MODEL (view->details->model),
- &iter, last_hover_path)) {
- gtk_tree_model_row_changed (GTK_TREE_MODEL (view->details->model),
- last_hover_path, &iter);
- }
- }
-
- if (view->details->hover_path) {
- gtk_tree_model_get_iter (GTK_TREE_MODEL (view->details->model),
- &iter, view->details->hover_path);
- gtk_tree_model_row_changed (GTK_TREE_MODEL (view->details->model),
- view->details->hover_path, &iter);
+ if ((old_hover_path != NULL) != (view->details->hover_path != NULL)) {
+ if (view->details->hover_path != NULL) {
+ gdk_window_set_cursor (widget->window, hand_cursor);
+ } else {
+ gdk_window_set_cursor (widget->window, NULL);
}
}
- gtk_tree_path_free (last_hover_path);
+ if (old_hover_path != NULL) {
+ gtk_tree_path_free (old_hover_path);
+ }
}
if (view->details->drag_button != 0) {
@@ -531,22 +512,40 @@ leave_notify_callback (GtkWidget *widget
gpointer callback_data)
{
FMListView *view;
- GtkTreeIter iter;
view = FM_LIST_VIEW (callback_data);
if (click_policy_auto_value == NAUTILUS_CLICK_POLICY_SINGLE &&
view->details->hover_path != NULL) {
- if (gtk_tree_model_get_iter (GTK_TREE_MODEL (view->details->model),
- &iter, view->details->hover_path)) {
- gtk_tree_model_row_changed (GTK_TREE_MODEL (view->details->model),
- view->details->hover_path, &iter);
- }
-
gtk_tree_path_free (view->details->hover_path);
view->details->hover_path = NULL;
+ }
- return TRUE;
+ return FALSE;
+}
+
+static gboolean
+enter_notify_callback (GtkWidget *widget,
+ GdkEventCrossing *event,
+ gpointer callback_data)
+{
+ FMListView *view;
+
+ view = FM_LIST_VIEW (callback_data);
+
+ if (click_policy_auto_value == NAUTILUS_CLICK_POLICY_SINGLE) {
+ if (view->details->hover_path != NULL) {
+ gtk_tree_path_free (view->details->hover_path);
+ }
+
+ gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
+ event->x, event->y,
+ &view->details->hover_path,
+ NULL, NULL, NULL);
+
+ if (view->details->hover_path != NULL) {
+ gdk_window_set_cursor (widget->window, hand_cursor);
+ }
}
return FALSE;
@@ -1234,6 +1233,8 @@ create_and_set_up_tree_view (FMListView
G_CALLBACK (drag_data_get_callback), view, 0);
g_signal_connect_object (view->details->tree_view, "motion_notify_event",
G_CALLBACK (motion_notify_callback), view, 0);
+ g_signal_connect_object (view->details->tree_view, "enter_notify_event",
+ G_CALLBACK (enter_notify_callback), view, 0);
g_signal_connect_object (view->details->tree_view, "leave_notify_event",
G_CALLBACK (leave_notify_callback), view, 0);
g_signal_connect_object (view->details->tree_view, "button_press_event",
@@ -2288,10 +2289,10 @@ fm_list_view_click_policy_changed (FMDir
GtkTreeIter iter;
GtkTreeView *tree;
+ view = FM_LIST_VIEW (directory_view);
+
/* ensure that we unset the hand cursor and refresh underlined rows */
if (click_policy_auto_value == NAUTILUS_CLICK_POLICY_DOUBLE) {
- view = FM_LIST_VIEW (directory_view);
-
if (view->details->hover_path != NULL) {
if (gtk_tree_model_get_iter (GTK_TREE_MODEL (view->details->model),
&iter, view->details->hover_path)) {
@@ -2313,7 +2314,15 @@ fm_list_view_click_policy_changed (FMDir
gdk_display_flush (display);
}
}
-
+
+ if (hand_cursor != NULL) {
+ gdk_cursor_unref(hand_cursor);
+ hand_cursor = NULL;
+ }
+ } else if (click_policy_auto_value == NAUTILUS_CLICK_POLICY_SINGLE) {
+ if (hand_cursor == NULL) {
+ hand_cursor = gdk_cursor_new(GDK_HAND2);
+ }
}
}
@@ -2457,6 +2466,10 @@ fm_list_view_finalize (GObject *object)
g_list_free (list_view->details->cells);
g_hash_table_destroy (list_view->details->columns);
+ if (list_view->details->hover_path != NULL) {
+ gtk_tree_path_free (list_view->details->hover_path);
+ }
+
g_free (list_view->details);
G_OBJECT_CLASS (parent_class)->finalize (object);
@@ -2663,6 +2676,8 @@ fm_list_view_init (FMListView *list_view
/* ensure that the zoom level is always set in begin_loading */
list_view->details->zoom_level = NAUTILUS_ZOOM_LEVEL_SMALLEST - 1;
+
+ list_view->details->hover_path = NULL;
}
static NautilusView *
--- ChangeLog 26 Feb 2006 17:01:58 -0000
+++ ChangeLog 26 Feb 2006 17:23:40 -0000
@@ -0,0 +1,30 @@
+2006-02-26 Zbigniew Chyla <mail zbigniew chyla pl>
+
+ Don't add all possible columns to GtkTreeView when initializing
+ FMListView, only the ones we actually show. This way we avoid getting
+ unnecessary data from the model, e.g. quite heavy "owner" and "group"
+ columns with the default settings.
+
+ * src/file-manager/fm-list-view.c:
+ (apply_visible_columns_foreach, apply_visible_columns,
+ apply_column_order): removed
+ (apply_columns_settings): new function, updates a list of columns in
+ GtkTreeView given both a list of visible columns and their ordering,
+ replacement for apply_visible_columns and apply_column_order.
+ (create_and_set_up_tree_view): when creating details->columns hash
+ table, pass g_object_unref as value_destroy_func; before adding
+ columns to the hash call g_object_ref+gtk_object_sink on them, don't
+ add columns to the view, the appropriate columns will be added when
+ applying the configuration; in order to apply initial columns
+ settings, call apply_columns_settings (instead of
+ apply_visible_columns + apply_column_order).
+ (set_visible_columns_from_metadata_and_preferences,
+ set_column_order_from_metadata_and_preferences): removed
+ (set_columns_settings_from_metadata_and_preferences): new, replacement
+ for set_visible_columns_* and set_column_order_*.
+ (fm_list_view_begin_loading, column_chooser_changed_callback,
+ column_chooser_use_default_callback, fm_list_view_reset_to_defaults,
+ default_visible_columns_changed_callback,
+ default_column_order_changed_callback): use newly added
+ set_columns_settings_from_metadata_and_preferences.
+
--- src/file-manager/fm-list-view.c 21 Feb 2006 15:10:43 -0000
+++ src/file-manager/fm-list-view.c 26 Feb 2006 17:23:41 -0000
@@ -1087,57 +1087,57 @@ move_copy_items_callback (NautilusTreeVi
}
static void
-apply_visible_columns_foreach (gpointer key, gpointer value, gpointer user_data)
+apply_columns_settings (FMListView *list_view, GList *column_order, GList *visible_columns)
{
- gboolean visible;
-
- visible = (eel_g_str_list_index (user_data, key) != -1);
-
- gtk_tree_view_column_set_visible (GTK_TREE_VIEW_COLUMN (value),
- visible);
-}
-
-static void
-apply_visible_columns (FMListView *list_view, GList *visible_columns)
-{
- g_hash_table_foreach (list_view->details->columns,
- apply_visible_columns_foreach,
- visible_columns);
-
-}
-
-static void
-apply_column_order (FMListView *list_view, GList *column_order)
-{
- GList *columns;
- GtkTreeViewColumn *last_view_column;
+ GList *all_columns;
+ GList *old_view_columns, *view_columns;
+ GtkTreeViewColumn *prev_view_column;
GList *l;
-
- columns = nautilus_get_all_columns ();
- columns = nautilus_sort_columns (columns, column_order);
- last_view_column = NULL;
- for (l = columns; l != NULL; l = l->next) {
- GtkTreeViewColumn *view_column;
+ /* prepare ordered list of view columns using column_order and visible_columns */
+ view_columns = NULL;
+ all_columns = nautilus_get_all_columns ();
+ all_columns = nautilus_sort_columns (all_columns, column_order);
+ for (l = all_columns; l != NULL; l = l->next) {
char *name;
g_object_get (G_OBJECT (l->data), "name", &name, NULL);
-
- view_column = g_hash_table_lookup (list_view->details->columns,
- name);
+ if (g_list_find_custom (visible_columns, name, (GCompareFunc) g_ascii_strcasecmp) != NULL) {
+ GtkTreeViewColumn *view_column;
+ view_column = g_hash_table_lookup (list_view->details->columns, name);
+ if (view_column != NULL) {
+ view_columns = g_list_append (view_columns, view_column);
+ }
+ }
g_free (name);
-
- if (view_column) {
- gtk_tree_view_move_column_after
- (list_view->details->tree_view,
- view_column,
- last_view_column);
-
- last_view_column = view_column;
+ }
+ nautilus_column_list_free (all_columns);
+
+ /* remove columns that are not present in the configuration */
+ old_view_columns = gtk_tree_view_get_columns (list_view->details->tree_view);
+ for (l = old_view_columns; l != NULL; l = l->next) {
+ if (g_list_find (view_columns, l->data) == NULL) {
+ gtk_tree_view_remove_column (list_view->details->tree_view, l->data);
}
}
- nautilus_column_list_free (columns);
+ g_list_free (old_view_columns);
+
+ /* append new columns from the configuration */
+ old_view_columns = gtk_tree_view_get_columns (list_view->details->tree_view);
+ for (l = view_columns; l != NULL; l = l->next) {
+ if (g_list_find (old_view_columns, l->data) == NULL) {
+ gtk_tree_view_append_column (list_view->details->tree_view, l->data);
+ }
+ }
+ g_list_free (old_view_columns);
+
+ /* place columns in the correct order */
+ prev_view_column = NULL;
+ for (l = view_columns; l != NULL; l = l->next) {
+ gtk_tree_view_move_column_after (list_view->details->tree_view, l->data, prev_view_column);
+ prev_view_column = l->data;
+ }
}
static void
@@ -1193,7 +1193,7 @@ create_and_set_up_tree_view (FMListView
view->details->columns = g_hash_table_new_full (g_str_hash,
g_str_equal,
(GDestroyNotify)g_free,
- NULL);
+ (GDestroyNotify) g_object_unref);
gtk_tree_view_set_enable_search (view->details->tree_view, TRUE);
/* Don't handle backspace key. It's used to open the parent folder. */
@@ -1295,6 +1295,8 @@ create_and_set_up_tree_view (FMListView
view->details->pixbuf_cell = (GtkCellRendererPixbuf *)cell;
view->details->file_name_column = gtk_tree_view_column_new ();
+ g_object_ref (view->details->file_name_column);
+ gtk_object_sink (GTK_OBJECT (view->details->file_name_column));
view->details->file_name_column_num = column_num;
g_hash_table_insert (view->details->columns,
@@ -1323,7 +1325,6 @@ create_and_set_up_tree_view (FMListView
gtk_tree_view_column_set_cell_data_func (view->details->file_name_column, cell,
(GtkTreeCellDataFunc) filename_cell_data_func,
view, NULL);
- gtk_tree_view_append_column (view->details->tree_view, view->details->file_name_column);
} else {
cell = gtk_cell_renderer_text_new ();
g_object_set (cell, "xalign", xalign, NULL);
@@ -1333,6 +1334,8 @@ create_and_set_up_tree_view (FMListView
cell,
"text", column_num,
NULL);
+ g_object_ref (column);
+ gtk_object_sink (GTK_OBJECT (column));
gtk_tree_view_column_set_sort_column_id (column, column_num);
g_hash_table_insert (view->details->columns,
g_strdup (name),
@@ -1340,7 +1343,6 @@ create_and_set_up_tree_view (FMListView
gtk_tree_view_column_set_resizable (column, TRUE);
gtk_tree_view_column_set_visible (column, TRUE);
- gtk_tree_view_append_column (view->details->tree_view, column);
}
g_free (name);
g_free (label);
@@ -1350,8 +1352,7 @@ create_and_set_up_tree_view (FMListView
/* Apply the default column order and visible columns, to get it
* right most of the time. The metadata will be checked when a
* folder is loaded */
- apply_visible_columns (view, default_visible_columns_auto_value);
- apply_column_order (view, default_column_order_auto_value);
+ apply_columns_settings (view, default_column_order_auto_value, default_visible_columns_auto_value);
gtk_widget_show (GTK_WIDGET (view->details->tree_view));
gtk_container_add (GTK_CONTAINER (view), GTK_WIDGET (view->details->tree_view));
@@ -1390,16 +1391,6 @@ get_visible_columns (FMListView *list_vi
return visible_columns;
}
-static void
-set_visible_columns_from_metadata_and_preferences (FMListView *list_view)
-{
- GList *visible_columns;
-
- visible_columns = get_visible_columns (list_view);
- apply_visible_columns (list_view, visible_columns);
- eel_g_list_free_deep (visible_columns);
-}
-
static GList *
get_column_order (FMListView *list_view)
{
@@ -1421,13 +1412,18 @@ get_column_order (FMListView *list_view)
}
static void
-set_column_order_from_metadata_and_preferences (FMListView *list_view)
+set_columns_settings_from_metadata_and_preferences (FMListView *list_view)
{
GList *column_order;
+ GList *visible_columns;
column_order = get_column_order (list_view);
- apply_column_order (list_view, column_order);
+ visible_columns = get_visible_columns (list_view);
+
+ apply_columns_settings (list_view, column_order, visible_columns);
+
eel_g_list_free_deep (column_order);
+ eel_g_list_free_deep (visible_columns);
}
static void
@@ -1509,8 +1505,7 @@ fm_list_view_begin_loading (FMDirectoryV
set_sort_order_from_metadata_and_preferences (list_view);
set_zoom_level_from_metadata_and_preferences (list_view);
- set_visible_columns_from_metadata_and_preferences (list_view);
- set_column_order_from_metadata_and_preferences (list_view);
+ set_columns_settings_from_metadata_and_preferences (list_view);
}
static void
@@ -1867,8 +1862,7 @@ column_chooser_changed_callback (Nautilu
eel_g_list_free_deep (visible_columns);
eel_g_list_free_deep (column_order);
- set_visible_columns_from_metadata_and_preferences (view);
- set_column_order_from_metadata_and_preferences (view);
+ set_columns_settings_from_metadata_and_preferences (view);
}
static void
@@ -1907,8 +1901,7 @@ column_chooser_use_default_callback (Nau
nautilus_file_set_metadata_list (file, NAUTILUS_METADATA_KEY_LIST_VIEW_COLUMN_ORDER, NAUTILUS_METADATA_SUBKEY_COLUMNS, NULL);
nautilus_file_set_metadata_list (file, NAUTILUS_METADATA_KEY_LIST_VIEW_VISIBLE_COLUMNS, NAUTILUS_METADATA_SUBKEY_COLUMNS, NULL);
- set_visible_columns_from_metadata_and_preferences (FM_LIST_VIEW (view));
- set_column_order_from_metadata_and_preferences (FM_LIST_VIEW (view));
+ set_columns_settings_from_metadata_and_preferences (FM_LIST_VIEW (view));
column_chooser_set_from_settings (chooser, view);
}
@@ -2056,8 +2049,7 @@ fm_list_view_reset_to_defaults (FMDirect
default_sort_reversed_auto_value ? GTK_SORT_DESCENDING : GTK_SORT_ASCENDING);
fm_list_view_set_zoom_level (FM_LIST_VIEW (view), get_default_zoom_level (), FALSE);
- set_visible_columns_from_metadata_and_preferences (FM_LIST_VIEW (view));
- set_column_order_from_metadata_and_preferences (FM_LIST_VIEW (view));
+ set_columns_settings_from_metadata_and_preferences (FM_LIST_VIEW (view));
}
static void
@@ -2356,8 +2348,8 @@ default_visible_columns_changed_callback
FMListView *list_view;
list_view = FM_LIST_VIEW (callback_data);
-
- set_visible_columns_from_metadata_and_preferences (list_view);
+
+ set_columns_settings_from_metadata_and_preferences (list_view);
}
static void
@@ -2366,8 +2358,8 @@ default_column_order_changed_callback (g
FMListView *list_view;
list_view = FM_LIST_VIEW (callback_data);
-
- set_column_order_from_metadata_and_preferences (list_view);
+
+ set_columns_settings_from_metadata_and_preferences (list_view);
}
static void
--- ChangeLog 26 Feb 2006 15:15:36 -0000
+++ ChangeLog 26 Feb 2006 17:02:07 -0000
@@ -0,0 +1,20 @@
+2006-02-26 Zbigniew Chyla <mail zbigniew chyla pl>
+
+ Get users' and groups' names via a cache to avoid calling
+ getpwuid/getgrgid too often (i.e. many times during single redraw).
+
+ * libnautilus-private/Makefile.am: (libnautilus_private_la_SOURCES):
+ Added nautilus-users-groups-cache.[ch]
+ * libnautilus-private/nautilus-users-groups-cache.[ch]: New,
+ implementation of cache for getpwuid/getgrgid results.
+ * libnautilus-private/nautilus-file.c:
+ (get_user_name_from_id): removed
+ (get_real_name): accept "name + gecos" pair instead of "struct passwd".
+ (get_user_and_real_name_from_id): get user name and gecos using
+ nautilus_users_cache_* functions instead of using getpwuid directly.
+ (nautilus_get_user_names): adjusted to changed signature of get_real_name.
+ (nautilus_file_get_group_name): get group name using
+ nautilus_groups_cache_get_name instead of using getgrgid directly.
+ (nautilus_file_get_owner_as_string): get user name using
+ nautilus_users_cache_get_name.
+
--- libnautilus-private/Makefile.am 16 Jan 2006 09:30:58 -0000
+++ libnautilus-private/Makefile.am 26 Feb 2006 17:02:07 -0000
@@ -193,6 +193,8 @@ libnautilus_private_la_SOURCES = \
nautilus-undo-transaction.h \
nautilus-undo.c \
nautilus-undo.h \
+ nautilus-users-groups-cache.c \
+ nautilus-users-groups-cache.h \
nautilus-vfs-directory.c \
nautilus-vfs-directory.h \
nautilus-vfs-file.c \
--- libnautilus-private/nautilus-file.c 21 Feb 2006 22:37:22 -0000
+++ libnautilus-private/nautilus-file.c 26 Feb 2006 17:02:09 -0000
@@ -45,6 +45,7 @@
#include "nautilus-thumbnails.h"
#include "nautilus-trash-directory.h"
#include "nautilus-trash-file.h"
+#include "nautilus-users-groups-cache.h"
#include "nautilus-vfs-file.h"
#include "nautilus-saved-search-file.h"
#include <eel/eel-debug.h>
@@ -3537,30 +3538,15 @@ nautilus_file_set_permissions (NautilusF
}
static char *
-get_user_name_from_id (uid_t uid)
-{
- struct passwd *password_info;
-
- /* No need to free result of getpwuid */
- password_info = getpwuid (uid);
-
- if (password_info == NULL) {
- return NULL;
- }
-
- return g_strdup (password_info->pw_name);
-}
-
-static char *
-get_real_name (struct passwd *user)
+get_real_name (const char *name, const char *gecos)
{
char *locale_string, *part_before_comma, *capitalized_login_name, *real_name;
- if (user->pw_gecos == NULL) {
+ if (gecos == NULL) {
return NULL;
}
- locale_string = eel_str_strip_substring_and_after (user->pw_gecos, ",");
+ locale_string = eel_str_strip_substring_and_after (gecos, ",");
if (!g_utf8_validate (locale_string, -1, NULL)) {
part_before_comma = g_locale_to_utf8 (locale_string, -1, NULL, NULL, NULL);
g_free (locale_string);
@@ -3568,10 +3554,10 @@ get_real_name (struct passwd *user)
part_before_comma = locale_string;
}
- if (!g_utf8_validate (user->pw_name, -1, NULL)) {
- locale_string = g_locale_to_utf8 (user->pw_name, -1, NULL, NULL, NULL);
+ if (!g_utf8_validate (name, -1, NULL)) {
+ locale_string = g_locale_to_utf8 (name, -1, NULL, NULL, NULL);
} else {
- locale_string = g_strdup (user->pw_name);
+ locale_string = g_strdup (name);
}
capitalized_login_name = eel_str_capitalize (locale_string);
@@ -3587,7 +3573,7 @@ get_real_name (struct passwd *user)
if (eel_str_is_empty (real_name)
- || eel_strcmp (user->pw_name, real_name) == 0
+ || eel_strcmp (name, real_name) == 0
|| eel_strcmp (capitalized_login_name, real_name) == 0) {
g_free (real_name);
real_name = NULL;
@@ -3601,25 +3587,23 @@ get_real_name (struct passwd *user)
static char *
get_user_and_real_name_from_id (uid_t uid)
{
+ char *name, *gecos;
char *real_name, *user_and_real_name;
- struct passwd *password_info;
- /* No need to free result of getpwuid */
- password_info = getpwuid (uid);
+ name = nautilus_users_cache_get_name (uid);
+ gecos = nautilus_users_cache_get_gecos (uid);
- if (password_info == NULL) {
- return NULL;
- }
-
- real_name = get_real_name (password_info);
+ real_name = get_real_name (name, gecos);
if (real_name != NULL) {
- user_and_real_name = g_strdup_printf
- ("%s - %s", password_info->pw_name, real_name);
+ user_and_real_name = g_strdup_printf ("%s - %s", name, real_name);
} else {
- user_and_real_name = g_strdup (password_info->pw_name);
+ user_and_real_name = g_strdup (name);
}
g_free (real_name);
+ g_free (name);
+ g_free (gecos);
+
return user_and_real_name;
}
@@ -3893,7 +3877,7 @@ nautilus_get_user_names (void)
setpwent ();
while ((user = getpwent ()) != NULL) {
- real_name = get_real_name (user);
+ real_name = get_real_name (user->pw_name, user->pw_gecos);
if (real_name != NULL) {
name = g_strconcat (user->pw_name, "\n", real_name, NULL);
} else {
@@ -3939,24 +3923,22 @@ nautilus_file_can_get_group (NautilusFil
char *
nautilus_file_get_group_name (NautilusFile *file)
{
- struct group *group_info;
+ char *group_name;
/* Before we have info on a file, the owner is unknown. */
if (nautilus_file_info_missing (file, GNOME_VFS_FILE_INFO_FIELDS_IDS)) {
return NULL;
}
- /* No need to free result of getgrgid */
- group_info = getgrgid ((gid_t) file->details->info->gid);
-
- if (group_info != NULL) {
- return g_strdup (group_info->gr_name);
+ group_name = nautilus_groups_cache_get_name ((gid_t) file->details->info->gid);
+ if (group_name == NULL) {
+ /* In the oddball case that the group name has been set to an id for which
+ * there is no defined group, return the id in string form.
+ */
+ group_name = g_strdup_printf ("%d", file->details->info->gid);
}
-
- /* In the oddball case that the group name has been set to an id for which
- * there is no defined group, return the id in string form.
- */
- return g_strdup_printf ("%d", file->details->info->gid);
+
+ return group_name;
}
/**
@@ -4252,17 +4234,17 @@ nautilus_file_get_owner_as_string (Nauti
if (include_real_name) {
user_name = get_user_and_real_name_from_id (file->details->info->uid);
} else {
- user_name = get_user_name_from_id (file->details->info->uid);
+ user_name = nautilus_users_cache_get_name (file->details->info->uid);
}
- if (user_name != NULL) {
- return user_name;
+ if (user_name == NULL) {
+ /* In the oddball case that the user name has been set to an id for which
+ * there is no defined user, return the id in string form.
+ */
+ user_name = g_strdup_printf ("%d", file->details->info->uid);
}
- /* In the oddball case that the user name has been set to an id for which
- * there is no defined user, return the id in string form.
- */
- return g_strdup_printf ("%d", file->details->info->uid);
+ return user_name;
}
static char *
--- libnautilus-private/nautilus-users-groups-cache.h 1970-01-01 01:00:00.000000000 +0100
+++ libnautilus-private/nautilus-users-groups-cache.h 2006-02-26 17:59:02.000000000 +0100
@@ -0,0 +1,34 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+
+ nautilus-users-groups-cache.h: cache of users' and groups' names.
+
+ Copyright (C) 2006 Zbigniew Chyla
+
+ 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.
+
+ Author: Zbigniew Chyla <mail zbigniew chyla pl>
+*/
+
+#ifndef NAUTILUS_USERS_GROUPS_CACHE_H
+#define NAUTILUS_USERS_GROUPS_CACHE_H
+
+#include <sys/types.h>
+
+char *nautilus_users_cache_get_name (uid_t uid);
+char *nautilus_users_cache_get_gecos (uid_t uid);
+char *nautilus_groups_cache_get_name (gid_t gid);
+
+#endif /* NAUTILUS_USERS_GROUPS_CACHE_H */
--- libnautilus-private/nautilus-users-groups-cache.c 1970-01-01 01:00:00.000000000 +0100
+++ libnautilus-private/nautilus-users-groups-cache.c 2006-02-26 18:43:48.000000000 +0100
@@ -0,0 +1,277 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+
+ nautilus-users-groups-cache.c: cache of users' and groups' names.
+
+ Copyright (C) 2006 Zbigniew Chyla
+
+ 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.
+
+ Author: Zbigniew Chyla <mail zbigniew chyla pl>
+*/
+
+#include <config.h>
+#include "nautilus-users-groups-cache.h"
+
+#include <glib.h>
+#include <grp.h>
+#include <pwd.h>
+
+
+typedef struct _ExpiringCache ExpiringCache;
+
+/* times in milliseconds */
+#define USERS_CACHE_EXPIRE_TIME (60 * 1000)
+#define GROUPS_CACHE_EXPIRE_TIME (60 * 1000)
+
+/* cache of users' names */
+static ExpiringCache *users_cache = NULL;
+
+/* cache of groups' names */
+static ExpiringCache *groups_cache = NULL;
+
+
+/**
+ * Generic implementation of cache with guint keys and values which expire
+ * after specified amount of time.
+ */
+
+typedef gpointer (*ExpiringCacheGetValFunc) (guint key);
+
+
+struct _ExpiringCache
+{
+ /* Expiration time of cached value */
+ time_t expire_time;
+
+ /* Called to obtain a value by a key */
+ ExpiringCacheGetValFunc get_value_func;
+
+ /* Called to destroy a value */
+ GDestroyNotify value_destroy_func;
+
+ /* Stores cached values */
+ GHashTable *cached_values;
+};
+
+
+typedef struct _ExpiringCacheEntry ExpiringCacheEntry;
+
+struct _ExpiringCacheEntry
+{
+ ExpiringCache *cache;
+ guint key;
+ gpointer value;
+};
+
+
+static ExpiringCache *
+expiring_cache_new (time_t expire_time, ExpiringCacheGetValFunc get_value_func,
+ GDestroyNotify value_destroy_func)
+{
+ ExpiringCache *cache;
+
+ g_return_val_if_fail (get_value_func != NULL, NULL);
+
+ cache = g_new (ExpiringCache, 1);
+ cache->expire_time = expire_time;
+ cache->get_value_func = get_value_func;
+ cache->value_destroy_func = value_destroy_func;
+ cache->cached_values = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
+
+ return cache;
+}
+
+static ExpiringCacheEntry *
+expiring_cache_entry_new (ExpiringCache *cache, guint key, gpointer value)
+{
+ ExpiringCacheEntry *entry;
+
+ entry = g_slice_new (ExpiringCacheEntry);
+ entry->cache = cache;
+ entry->key = key;
+ entry->value = value;
+
+ return entry;
+}
+
+static void
+expiring_cache_entry_destroy (ExpiringCacheEntry *entry)
+{
+ if (entry->cache->value_destroy_func != NULL) {
+ entry->cache->value_destroy_func (entry->value);
+ }
+ g_slice_free (ExpiringCacheEntry, entry);
+}
+
+static gboolean
+cb_cache_entry_expired (ExpiringCacheEntry *entry)
+{
+ g_hash_table_remove (entry->cache->cached_values, GSIZE_TO_POINTER (entry->key));
+ expiring_cache_entry_destroy (entry);
+
+ return FALSE;
+}
+
+static gpointer
+expiring_cache_get_value (ExpiringCache *cache, guint key)
+{
+ ExpiringCacheEntry *entry;
+
+ g_return_val_if_fail (cache != NULL, NULL);
+
+ if (!g_hash_table_lookup_extended (cache->cached_values, GSIZE_TO_POINTER (key),
+ NULL, (gpointer) &entry)) {
+ entry = expiring_cache_entry_new (cache, key, cache->get_value_func (key));
+ g_hash_table_insert (cache->cached_values, GSIZE_TO_POINTER (key), entry);
+ g_timeout_add (cache->expire_time, (GSourceFunc) cb_cache_entry_expired, entry);
+ }
+
+ return entry->value;
+}
+
+
+/*
+ * Cache of users' names based on ExpiringCache.
+ */
+
+typedef struct _UserInfo UserInfo;
+
+struct _UserInfo {
+ char *name;
+ char *gecos;
+};
+
+
+static UserInfo *
+user_info_new (struct passwd *password_info)
+{
+ UserInfo *uinfo;
+
+ if (password_info != NULL) {
+ uinfo = g_slice_new (UserInfo);
+ uinfo->name = g_strdup (password_info->pw_name);
+ uinfo->gecos = g_strdup (password_info->pw_gecos);
+ } else {
+ uinfo = NULL;
+ }
+
+ return uinfo;
+}
+
+static void
+user_info_free (UserInfo *uinfo)
+{
+ if (uinfo != NULL) {
+ g_free (uinfo->name);
+ g_free (uinfo->gecos);
+ g_slice_free (UserInfo, uinfo);
+ }
+}
+
+static gpointer
+users_cache_get_value (guint key)
+{
+ return user_info_new (getpwuid (key));
+}
+
+static UserInfo *
+get_cached_user_info(guint uid)
+{
+ if (users_cache == NULL) {
+ users_cache = expiring_cache_new (USERS_CACHE_EXPIRE_TIME, users_cache_get_value,
+ (GDestroyNotify) user_info_free);
+ }
+
+ return expiring_cache_get_value (users_cache, uid);
+}
+
+/**
+ * nautilus_users_cache_get_name:
+ *
+ * Returns name of user with given uid (using cached data if possible) or
+ * NULL in case a user with given uid can't be found.
+ *
+ * Returns: Newly allocated string or NULL.
+ */
+char *
+nautilus_users_cache_get_name (uid_t uid)
+{
+ UserInfo *uinfo;
+
+ uinfo = get_cached_user_info (uid);
+ if (uinfo != NULL) {
+ return g_strdup (uinfo->name);
+ } else {
+ return NULL;
+ }
+}
+
+/**
+ * nautilus_users_cache_get_gecos:
+ *
+ * Returns gecos of user with given uid (using cached data if possible) or
+ * NULL in case a user with given uid can't be found.
+ *
+ * Returns: Newly allocated string or NULL.
+ */
+char *
+nautilus_users_cache_get_gecos (uid_t uid)
+{
+ UserInfo *uinfo;
+
+ uinfo = get_cached_user_info (uid);
+ if (uinfo != NULL) {
+ return g_strdup (uinfo->gecos);
+ } else {
+ return NULL;
+ }
+}
+
+/*
+ * Cache of groups' names based on ExpiringCache.
+ */
+
+static gpointer
+groups_cache_get_value (guint key)
+{
+ struct group *group_info;
+
+ group_info = getgrgid (GPOINTER_TO_SIZE (key));
+ if (group_info != NULL) {
+ return g_strdup (group_info->gr_name);
+ } else {
+ return NULL;
+ }
+}
+
+
+/**
+ * nautilus_groups_cache_get_name:
+ *
+ * Returns name of group with given gid (using cached data if possible) or
+ * NULL in case a group with given gid can't be found.
+ *
+ * Returns: Newly allocated string or NULL.
+ */
+char *
+nautilus_groups_cache_get_name (gid_t gid)
+{
+ if (groups_cache == NULL) {
+ groups_cache = expiring_cache_new (GROUPS_CACHE_EXPIRE_TIME, groups_cache_get_value, g_free);
+ }
+
+ return g_strdup (expiring_cache_get_value (groups_cache, gid));
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]