Commit d87a2ef3 authored by John Sullivan's avatar John Sullivan

Fixed bug 558 (Rubber-band selection of files in icon

	view gradually degrades)

	Now the menu-updating code does not run while rubberband
	selecting (but does run immediately thereafter).

	* libnautilus-extensions/nautilus-icon-container.h:
	* libnautilus-extensions/nautilus-icon-container.c:
	(start_rubberbanding), (stop_rubberbanding),
	(nautilus_icon_container_initialize_class): New signals
	"band_select_started" and "band_select_ended" emitted
	just when you'd expect.

	* src/file-manager/fm-directory-view.h:
	Prototypes for new functions (intended to be called only
	by subclasses) fm_directory_view_start_batching_selection_changes
	and fm_directory_view_stop_batching_selection_changes.

	* src/file-manager/fm-directory-view.c:
	Added details fields: guint batching_selection_level,
	gboolean selection_changed_while_batched, gboolean
	menu_states_untrustworthy
	(selection_contains_one_item_in_menu_callback),
	(selection_not_empty_in_menu_callback): New helper functions
	that report if the selection matches expectations and complain
	via g_warning if they don't match and we trust the menu state.
	(open_callback), (trash_callback), (duplicate_callback),
	(create_link_callback),	(open_properties_window_callback):
	Use these new helper functions to verify that the menu state
	matches expectations but not crash if it doesn't.

	(schedule_update_menus): Change timeout back to idle now that
	it isn't affecting the band-select case; mark menu states as
	untrustworthy.
	(fm_directory_view_update_menus): mark menu states as
	trustworthy.

	(fm_directory_view_notify_selection_changed): If we're batching
	selection changes, remember that the selection changed; otherwise,
	do the work that you only do when not batching (update menus).

	(fm_directory_view_start_batching_selection_changes): Increment
	the batching level counter.
	(fm_directory_view_stop_batching_selection_changes): Decrement
	the batching level counter. If it reaches zero, and the selection
	changed while batched, call notify_selection_changed.

	* src/file-manager/fm-icon-view.c:
	(band_select_started_callback): start batching selection changes.
	(band_select_ended_callback): stop batching selection changes.
	(create_icon_container): connect to band_select_started and
	band_select_ended signals of icon container.
parent 49e4d7aa
2000-09-18 John Sullivan <sullivan@eazel.com>
Fixed bug 558 (Rubber-band selection of files in icon
view gradually degrades)
Now the menu-updating code does not run while rubberband
selecting (but does run immediately thereafter).
* libnautilus-extensions/nautilus-icon-container.h:
* libnautilus-extensions/nautilus-icon-container.c:
(start_rubberbanding), (stop_rubberbanding),
(nautilus_icon_container_initialize_class): New signals
"band_select_started" and "band_select_ended" emitted
just when you'd expect.
* src/file-manager/fm-directory-view.h:
Prototypes for new functions (intended to be called only
by subclasses) fm_directory_view_start_batching_selection_changes
and fm_directory_view_stop_batching_selection_changes.
* src/file-manager/fm-directory-view.c:
Added details fields: guint batching_selection_level,
gboolean selection_changed_while_batched, gboolean
menu_states_untrustworthy
(selection_contains_one_item_in_menu_callback),
(selection_not_empty_in_menu_callback): New helper functions
that report if the selection matches expectations and complain
via g_warning if they don't match and we trust the menu state.
(open_callback), (trash_callback), (duplicate_callback),
(create_link_callback), (open_properties_window_callback):
Use these new helper functions to verify that the menu state
matches expectations but not crash if it doesn't.
(schedule_update_menus): Change timeout back to idle now that
it isn't affecting the band-select case; mark menu states as
untrustworthy.
(fm_directory_view_update_menus): mark menu states as
trustworthy.
(fm_directory_view_notify_selection_changed): If we're batching
selection changes, remember that the selection changed; otherwise,
do the work that you only do when not batching (update menus).
(fm_directory_view_start_batching_selection_changes): Increment
the batching level counter.
(fm_directory_view_stop_batching_selection_changes): Decrement
the batching level counter. If it reaches zero, and the selection
changed while batched, call notify_selection_changed.
* src/file-manager/fm-icon-view.c:
(band_select_started_callback): start batching selection changes.
(band_select_ended_callback): stop batching selection changes.
(create_icon_container): connect to band_select_started and
band_select_ended signals of icon container.
2000-09-18 Gene Z. Ragan <gzr@eazel.com>
Fixed bug 3018, should not be able to remove special icons
......
......@@ -132,6 +132,8 @@ NAUTILUS_DEFINE_CLASS_BOILERPLATE (NautilusIconContainer,
/* The NautilusIconContainer signals. */
enum {
ACTIVATE,
BAND_SELECT_STARTED,
BAND_SELECT_ENDED,
BUTTON_PRESS,
CAN_ACCEPT_ITEM,
COMPARE_ICONS,
......@@ -1236,6 +1238,9 @@ start_rubberbanding (NautilusIconContainer *container,
details = container->details;
band_info = &details->rubberband_info;
gtk_signal_emit (GTK_OBJECT (container),
signals[BAND_SELECT_STARTED]);
for (p = details->icons; p != NULL; p = p->next) {
NautilusIcon *icon;
......@@ -1330,6 +1335,10 @@ stop_rubberbanding (NautilusIconContainer *container,
/* FIXME bugzilla.eazel.com 2475: Is a destroy really sufficient here? Who does the unref? */
gtk_object_destroy (GTK_OBJECT (band_info->selection_rectangle));
band_info->selection_rectangle = NULL;
gtk_signal_emit (GTK_OBJECT (container),
signals[BAND_SELECT_ENDED]);
}
/* Keyboard navigation. */
......@@ -2810,6 +2819,22 @@ nautilus_icon_container_initialize_class (NautilusIconContainerClass *class)
GTK_TYPE_INT, 2,
GTK_TYPE_POINTER,
GTK_TYPE_BOOL);
signals[BAND_SELECT_STARTED]
= gtk_signal_new ("band_select_started",
GTK_RUN_LAST,
object_class->type,
GTK_SIGNAL_OFFSET (NautilusIconContainerClass,
band_select_started),
gtk_marshal_NONE__NONE,
GTK_TYPE_NONE, 0);
signals[BAND_SELECT_ENDED]
= gtk_signal_new ("band_select_ended",
GTK_RUN_LAST,
object_class->type,
GTK_SIGNAL_OFFSET (NautilusIconContainerClass,
band_select_ended),
gtk_marshal_NONE__NONE,
GTK_TYPE_NONE, 0);
gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL);
......
......@@ -119,6 +119,8 @@ typedef struct {
NautilusIconData *icon_b);
/* Notifications for the whole container. */
void (* band_select_started) (NautilusIconContainer *container);
void (* band_select_ended) (NautilusIconContainer *container);
void (* selection_changed) (NautilusIconContainer *container);
void (* layout_changed) (NautilusIconContainer *container);
......
......@@ -132,6 +132,8 @@ NAUTILUS_DEFINE_CLASS_BOILERPLATE (NautilusIconContainer,
/* The NautilusIconContainer signals. */
enum {
ACTIVATE,
BAND_SELECT_STARTED,
BAND_SELECT_ENDED,
BUTTON_PRESS,
CAN_ACCEPT_ITEM,
COMPARE_ICONS,
......@@ -1236,6 +1238,9 @@ start_rubberbanding (NautilusIconContainer *container,
details = container->details;
band_info = &details->rubberband_info;
gtk_signal_emit (GTK_OBJECT (container),
signals[BAND_SELECT_STARTED]);
for (p = details->icons; p != NULL; p = p->next) {
NautilusIcon *icon;
......@@ -1330,6 +1335,10 @@ stop_rubberbanding (NautilusIconContainer *container,
/* FIXME bugzilla.eazel.com 2475: Is a destroy really sufficient here? Who does the unref? */
gtk_object_destroy (GTK_OBJECT (band_info->selection_rectangle));
band_info->selection_rectangle = NULL;
gtk_signal_emit (GTK_OBJECT (container),
signals[BAND_SELECT_ENDED]);
}
/* Keyboard navigation. */
......@@ -2810,6 +2819,22 @@ nautilus_icon_container_initialize_class (NautilusIconContainerClass *class)
GTK_TYPE_INT, 2,
GTK_TYPE_POINTER,
GTK_TYPE_BOOL);
signals[BAND_SELECT_STARTED]
= gtk_signal_new ("band_select_started",
GTK_RUN_LAST,
object_class->type,
GTK_SIGNAL_OFFSET (NautilusIconContainerClass,
band_select_started),
gtk_marshal_NONE__NONE,
GTK_TYPE_NONE, 0);
signals[BAND_SELECT_ENDED]
= gtk_signal_new ("band_select_ended",
GTK_RUN_LAST,
object_class->type,
GTK_SIGNAL_OFFSET (NautilusIconContainerClass,
band_select_ended),
gtk_marshal_NONE__NONE,
GTK_TYPE_NONE, 0);
gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL);
......
......@@ -119,6 +119,8 @@ typedef struct {
NautilusIconData *icon_b);
/* Notifications for the whole container. */
void (* band_select_started) (NautilusIconContainer *container);
void (* band_select_ended) (NautilusIconContainer *container);
void (* selection_changed) (NautilusIconContainer *container);
void (* layout_changed) (NautilusIconContainer *container);
......
......@@ -115,9 +115,13 @@ struct FMDirectoryViewDetails
gboolean loading;
gboolean menus_merged;
gboolean menu_states_untrustworthy;
gboolean show_hidden_files;
gboolean show_backup_files;
gboolean batching_selection_level;
gboolean selection_changed_while_batched;
};
/* forward declarations */
......@@ -405,6 +409,43 @@ fm_directory_view_confirm_multiple_windows (FMDirectoryView *view, int count)
return gnome_dialog_run (dialog) == GNOME_OK;
}
static gboolean
selection_contains_one_item_in_menu_callback (FMDirectoryView *view, GList *selection)
{
if (nautilus_g_list_exactly_one_item (selection)) {
return TRUE;
}
/* If we've requested a menu update that hasn't yet occurred, then
* the mismatch here doesn't surprise us, and we won't complain.
* Otherwise, we will complain.
*/
if (!view->details->menu_states_untrustworthy) {
g_warning ("expected one selected item, found %d",
g_list_length (selection));
}
return FALSE;
}
static gboolean
selection_not_empty_in_menu_callback (FMDirectoryView *view, GList *selection)
{
if (selection != NULL) {
return TRUE;
}
/* If we've requested a menu update that hasn't yet occurred, then
* the mismatch here doesn't surprise us, and we won't complain.
* Otherwise, we will complain.
*/
if (!view->details->menu_states_untrustworthy) {
g_warning ("empty selection found when selection was expected");
}
return FALSE;
}
/**
* Note that this is used both as a Bonobo menu callback and a signal callback.
* The first parameter is different in these cases, but we just ignore it anyway.
......@@ -423,11 +464,11 @@ open_callback (gpointer ignored, gpointer callback_data)
/* UI should have prevented this from being called unless exactly
* one item is selected.
*/
g_assert (nautilus_g_list_exactly_one_item (selection));
fm_directory_view_activate_file (view,
NAUTILUS_FILE (selection->data),
FALSE);
if (selection_contains_one_item_in_menu_callback (view, selection)) {
fm_directory_view_activate_file (view,
NAUTILUS_FILE (selection->data),
FALSE);
}
nautilus_file_list_free (selection);
}
......@@ -634,14 +675,6 @@ other_viewer_callback (gpointer ignored, gpointer callback_data)
GNOME_VFS_MIME_ACTION_TYPE_COMPONENT);
}
static void
check_selection_not_empty (GList *selection)
{
if (selection == NULL) {
g_warning ("UI should have prevented this from being called with empty selection");
}
}
/**
* Note that this is used both as a Bonobo menu callback and a signal callback.
* The first parameter is different in these cases, but we just ignore it anyway.
......@@ -654,8 +687,9 @@ trash_callback (gpointer *ignored, gpointer callback_data)
view = FM_DIRECTORY_VIEW (callback_data);
selection = fm_directory_view_get_selection (view);
check_selection_not_empty (selection);
fm_directory_view_trash_or_delete_files (view, selection);
if (selection_not_empty_in_menu_callback (view, selection)) {
fm_directory_view_trash_or_delete_files (view, selection);
}
nautilus_file_list_free (selection);
}
......@@ -674,8 +708,9 @@ duplicate_callback (gpointer *ignored, gpointer callback_data)
view = FM_DIRECTORY_VIEW (callback_data);
selection = fm_directory_view_get_selection (view);
check_selection_not_empty (selection);
fm_directory_view_duplicate_selection (view, selection);
if (selection_not_empty_in_menu_callback (view, selection)) {
fm_directory_view_duplicate_selection (view, selection);
}
nautilus_file_list_free (selection);
}
......@@ -694,8 +729,9 @@ create_link_callback (gpointer ignored, gpointer callback_data)
view = FM_DIRECTORY_VIEW (callback_data);
selection = fm_directory_view_get_selection (view);
check_selection_not_empty (selection);
fm_directory_view_create_links_for_files (view, selection);
if (selection_not_empty_in_menu_callback (view, selection)) {
fm_directory_view_create_links_for_files (view, selection);
}
nautilus_file_list_free (selection);
}
......@@ -742,9 +778,10 @@ open_properties_window_callback (gpointer ignored, gpointer callback_data)
view = FM_DIRECTORY_VIEW (callback_data);
selection = fm_directory_view_get_selection (view);
check_selection_not_empty (selection);
if (fm_directory_view_confirm_multiple_windows (view, g_list_length (selection))) {
g_list_foreach (selection, open_one_properties_window, view);
if (selection_not_empty_in_menu_callback (view, selection)) {
if (fm_directory_view_confirm_multiple_windows (view, g_list_length (selection))) {
g_list_foreach (selection, open_one_properties_window, view);
}
}
nautilus_file_list_free (selection);
......@@ -3465,16 +3502,13 @@ static void
schedule_update_menus (FMDirectoryView *view)
{
g_assert (FM_IS_DIRECTORY_VIEW (view));
view->details->menu_states_untrustworthy = TRUE;
if (view->details->menus_merged
&& view->details->update_menus_idle_id == 0) {
/* Using a gtk_timeout_add instead of gtk_idle_add here to partially work
* around a performance problem where bonobo set sensitivity calls
* take too long to execute.
*/
view->details->update_menus_idle_id
= gtk_timeout_add (300,
update_menus_idle_callback, view);
= gtk_idle_add (update_menus_idle_callback, view);
}
}
......@@ -3491,14 +3525,24 @@ fm_directory_view_notify_selection_changed (FMDirectoryView *view)
{
g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
/* Schedule an update of menu item states to match selection */
schedule_update_menus (view);
/* Schedule a display of the new selection. */
if (view->details->display_selection_idle_id == 0)
view->details->display_selection_idle_id
= gtk_idle_add (display_selection_info_idle_callback,
view);
if (view->details->batching_selection_level != 0) {
view->details->selection_changed_while_batched = TRUE;
} else {
/* Here is the work we do only when we're not
* batching selection changes. In other words, it's the slower
* stuff that we don't want to slow down selection techniques
* such as rubberband-selecting in icon view.
*/
/* Schedule an update of menu item states to match selection */
schedule_update_menus (view);
}
}
static gboolean
......@@ -4121,6 +4165,8 @@ fm_directory_view_update_menus (FMDirectoryView *view)
NAUTILUS_CALL_VIRTUAL
(FM_DIRECTORY_VIEW_CLASS, view,
update_menus, (view));
view->details->menu_states_untrustworthy = FALSE;
}
static void
......@@ -4260,3 +4306,25 @@ fm_directory_view_trash_state_changed_callback (NautilusTrashMonitor *trash_moni
schedule_update_menus (view);
}
void
fm_directory_view_start_batching_selection_changes (FMDirectoryView *view)
{
g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
++view->details->batching_selection_level;
view->details->selection_changed_while_batched = FALSE;
}
void
fm_directory_view_stop_batching_selection_changes (FMDirectoryView *view)
{
g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
g_return_if_fail (view->details->batching_selection_level > 0);
if (--view->details->batching_selection_level == 0) {
if (view->details->selection_changed_while_batched) {
fm_directory_view_notify_selection_changed (view);
}
}
}
......@@ -330,6 +330,8 @@ void fm_directory_view_begin_loading (FMDirectory
*/
void fm_directory_view_activate_files (FMDirectoryView *view,
GList *files);
void fm_directory_view_start_batching_selection_changes (FMDirectoryView *view);
void fm_directory_view_stop_batching_selection_changes (FMDirectoryView *view);
gboolean fm_directory_view_confirm_multiple_windows (FMDirectoryView *view,
int window_count);
void fm_directory_view_queue_file_change (FMDirectoryView *view,
......
......@@ -1685,6 +1685,26 @@ icon_container_activate_callback (NautilusIconContainer *container,
fm_directory_view_activate_files (FM_DIRECTORY_VIEW (icon_view), file_list);
}
static void
band_select_started_callback (NautilusIconContainer *container,
FMIconView *icon_view)
{
g_assert (FM_IS_ICON_VIEW (icon_view));
g_assert (container == get_icon_container (icon_view));
fm_directory_view_start_batching_selection_changes (FM_DIRECTORY_VIEW (icon_view));
}
static void
band_select_ended_callback (NautilusIconContainer *container,
FMIconView *icon_view)
{
g_assert (FM_IS_ICON_VIEW (icon_view));
g_assert (container == get_icon_container (icon_view));
fm_directory_view_stop_batching_selection_changes (FM_DIRECTORY_VIEW (icon_view));
}
/* handle the preview signal by inspecting the mime type. For now, we only preview sound files. */
/* here's the timer task that actually plays the file using mpg123. */
......@@ -2310,6 +2330,14 @@ create_icon_container (FMIconView *icon_view)
"activate",
GTK_SIGNAL_FUNC (icon_container_activate_callback),
icon_view);
gtk_signal_connect (GTK_OBJECT (icon_container),
"band_select_started",
GTK_SIGNAL_FUNC (band_select_started_callback),
icon_view);
gtk_signal_connect (GTK_OBJECT (icon_container),
"band_select_ended",
GTK_SIGNAL_FUNC (band_select_ended_callback),
icon_view);
gtk_signal_connect (GTK_OBJECT (icon_container),
"compare_icons",
GTK_SIGNAL_FUNC (icon_container_compare_icons_callback),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment