1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
3 /* nautilus-icon-container.c - Icon container widget.
5 Copyright (C) 1999, 2000 Free Software Foundation
6 Copyright (C) 2000, 2001 Eazel, Inc.
7 Copyright (C) 2002, 2003 Red Hat, Inc.
9 The Gnome Library is free software; you can redistribute it and/or
10 modify it under the terms of the GNU Library General Public License as
11 published by the Free Software Foundation; either version 2 of the
12 License, or (at your option) any later version.
14 The Gnome Library is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Library General Public License for more details.
19 You should have received a copy of the GNU Library General Public
20 License along with the Gnome Library; see the file COPYING.LIB. If not,
21 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 Boston, MA 02111-1307, USA.
24 Authors: Ettore Perazzoli <ettore@gnu.org>,
25 Darin Adler <darin@bentspoon.com>
30 #include "nautilus-icon-container.h"
32 #include "nautilus-debug-log.h"
33 #include "nautilus-global-preferences.h"
34 #include "nautilus-icon-private.h"
35 #include "nautilus-lib-self-check-functions.h"
36 #include "nautilus-marshal.h"
37 #include <atk/atkaction.h>
38 #include <eel/eel-accessibility.h>
39 #include <eel/eel-background.h>
40 #include <eel/eel-vfs-extensions.h>
41 #include <eel/eel-gdk-pixbuf-extensions.h>
42 #include <eel/eel-gnome-extensions.h>
43 #include <eel/eel-gtk-extensions.h>
44 #include <eel/eel-art-extensions.h>
45 #include <eel/eel-editable-label.h>
46 #include <eel/eel-marshal.h>
47 #include <eel/eel-string.h>
48 #include <eel/eel-canvas-rect-ellipse.h>
49 #include <libgnomeui/gnome-icon-item.h>
50 #include <gdk/gdkkeysyms.h>
51 #include <gtk/gtkaccessible.h>
52 #include <gtk/gtklayout.h>
53 #include <gtk/gtkmain.h>
54 #include <gtk/gtksignal.h>
55 #include <glib/gi18n.h>
56 #include <libgnome/gnome-macros.h>
60 #define TAB_NAVIGATION_DISABLED
62 /* Interval for updating the rubberband selection, in milliseconds. */
63 #define RUBBERBAND_TIMEOUT_INTERVAL 10
65 /* Initial unpositioned icon value */
66 #define ICON_UNPOSITIONED_VALUE -1
68 /* Timeout for making the icon currently selected for keyboard operation visible.
69 * If this is 0, you can get into trouble with extra scrolling after holding
70 * down the arrow key for awhile when there are many items.
72 #define KEYBOARD_ICON_REVEAL_TIMEOUT 10
74 #define CONTEXT_MENU_TIMEOUT_INTERVAL 500
76 /* Maximum amount of milliseconds the mouse button is allowed to stay down
77 * and still be considered a click.
79 #define MAX_CLICK_TIME 1500
81 /* Button assignments. */
83 #define RUBBERBAND_BUTTON 1
84 #define MIDDLE_BUTTON 2
85 #define CONTEXTUAL_MENU_BUTTON 3
86 #define DRAG_MENU_BUTTON 2
88 /* Maximum size (pixels) allowed for icons at the standard zoom level. */
89 #define MINIMUM_IMAGE_SIZE 24
90 #define MAXIMUM_IMAGE_SIZE 96
92 #define ICON_PAD_LEFT 4
93 #define ICON_PAD_RIGHT 4
94 #define ICON_BASE_WIDTH 96
96 #define ICON_PAD_TOP 4
97 #define ICON_PAD_BOTTOM 4
99 #define CONTAINER_PAD_LEFT 4
100 #define CONTAINER_PAD_TOP 4
101 #define CONTAINER_PAD_BOTTOM 4
103 #define STANDARD_ICON_GRID_WIDTH 155
105 #define TEXT_BESIDE_ICON_GRID_WIDTH 205
107 /* Desktop layout mode defines */
108 #define DESKTOP_PAD_HORIZONTAL 10
109 #define DESKTOP_PAD_VERTICAL 10
110 #define SNAP_SIZE_X 78
111 #define SNAP_SIZE_Y 20
113 /* Value used to protect against icons being dragged outside of the desktop bounds */
114 #define DESKTOP_ICON_SAFETY_PAD 10
116 #define DEFAULT_SELECTION_BOX_ALPHA 0x40
117 #define DEFAULT_HIGHLIGHT_ALPHA 0xff
118 #define DEFAULT_NORMAL_ALPHA 0xff
119 #define DEFAULT_PRELIGHT_ALPHA 0xff
120 #define DEFAULT_LIGHT_INFO_COLOR 0xAAAAFD
121 #define DEFAULT_DARK_INFO_COLOR 0x33337F
123 #define DEFAULT_NORMAL_ICON_RENDER_MODE 0
124 #define DEFAULT_PRELIGHT_ICON_RENDER_MODE 1
125 #define DEFAULT_NORMAL_ICON_SATURATION 255
126 #define DEFAULT_PRELIGHT_ICON_SATURATION 255
127 #define DEFAULT_NORMAL_ICON_BRIGHTNESS 255
128 #define DEFAULT_PRELIGHT_ICON_BRIGHTNESS 255
129 #define DEFAULT_NORMAL_ICON_LIGHTEN 0
130 #define DEFAULT_PRELIGHT_ICON_LIGHTEN 0
132 #define MINIMUM_EMBEDDED_TEXT_RECT_WIDTH 20
133 #define MINIMUM_EMBEDDED_TEXT_RECT_HEIGHT 20
135 /* If icon size is bigger than this, request large embedded text.
136 * Its selected so that the non-large text should fit in "normal" icon sizes
138 #define ICON_SIZE_FOR_LARGE_EMBEDDED_TEXT 55
140 /* From nautilus-icon-canvas-item.c */
141 #define MAX_TEXT_WIDTH_BESIDE 90
143 #define SNAP_HORIZONTAL(func,x) ((func ((double)((x) - DESKTOP_PAD_HORIZONTAL) / SNAP_SIZE_X) * SNAP_SIZE_X) + DESKTOP_PAD_HORIZONTAL)
144 #define SNAP_VERTICAL(func, y) ((func ((double)((y) - DESKTOP_PAD_VERTICAL) / SNAP_SIZE_Y) * SNAP_SIZE_Y) + DESKTOP_PAD_VERTICAL)
146 #define SNAP_NEAREST_HORIZONTAL(x) SNAP_HORIZONTAL (eel_round, x)
147 #define SNAP_NEAREST_VERTICAL(y) SNAP_VERTICAL (eel_round, y)
149 #define SNAP_CEIL_HORIZONTAL(x) SNAP_HORIZONTAL (ceil, x)
150 #define SNAP_CEIL_VERTICAL(y) SNAP_VERTICAL (ceil, y)
152 /* Copied from NautilusIconContainer */
153 #define NAUTILUS_ICON_CONTAINER_SEARCH_DIALOG_TIMEOUT 5000
163 char *action_descriptions
[LAST_ACTION
];
164 } NautilusIconContainerAccessiblePrivate
;
166 static GType
nautilus_icon_container_accessible_get_type (void);
168 static void activate_selected_items (NautilusIconContainer
*container
);
169 static void activate_selected_items_alternate (NautilusIconContainer
*container
,
171 static void nautilus_icon_container_theme_changed (gpointer user_data
);
172 static void compute_stretch (StretchState
*start
,
173 StretchState
*current
);
174 static NautilusIcon
*get_first_selected_icon (NautilusIconContainer
*container
);
175 static NautilusIcon
*get_nth_selected_icon (NautilusIconContainer
*container
,
177 static gboolean
has_multiple_selection (NautilusIconContainer
*container
);
178 static gboolean
all_selected (NautilusIconContainer
*container
);
179 static gboolean
has_selection (NautilusIconContainer
*container
);
180 static void icon_destroy (NautilusIconContainer
*container
,
182 static void end_renaming_mode (NautilusIconContainer
*container
,
184 static NautilusIcon
*get_icon_being_renamed (NautilusIconContainer
*container
);
185 static void finish_adding_new_icons (NautilusIconContainer
*container
);
186 static void update_label_color (EelBackground
*background
,
187 NautilusIconContainer
*icon_container
);
188 static void icon_get_bounding_box (NautilusIcon
*icon
,
193 static gboolean
is_renaming (NautilusIconContainer
*container
);
194 static gboolean
is_renaming_pending (NautilusIconContainer
*container
);
195 static void process_pending_icon_to_rename (NautilusIconContainer
*container
);
196 static void setup_label_gcs (NautilusIconContainer
*container
);
197 static void nautilus_icon_container_stop_monitor_top_left (NautilusIconContainer
*container
,
198 NautilusIconData
*data
,
199 gconstpointer client
);
200 static void nautilus_icon_container_start_monitor_top_left (NautilusIconContainer
*container
,
201 NautilusIconData
*data
,
202 gconstpointer client
,
203 gboolean large_text
);
204 static void handle_vadjustment_changed (GtkAdjustment
*adjustment
,
205 NautilusIconContainer
*container
);
206 static GList
* nautilus_icon_container_get_selected_icons (NautilusIconContainer
*container
);
207 static void nautilus_icon_container_update_visible_icons (NautilusIconContainer
*container
);
208 static void reveal_icon (NautilusIconContainer
*container
,
211 static void nautilus_icon_container_set_rtl_positions (NautilusIconContainer
*container
);
212 static double get_mirror_x_position (NautilusIconContainer
*container
,
216 static gpointer accessible_parent_class
;
218 static GQuark accessible_private_data_quark
= 0;
220 static const char *nautilus_icon_container_accessible_action_names
[] = {
226 static const char *nautilus_icon_container_accessible_action_descriptions
[] = {
227 "Activate selected items",
228 "Popup context menu",
232 GNOME_CLASS_BOILERPLATE (NautilusIconContainer
, nautilus_icon_container
,
233 EelCanvas
, EEL_TYPE_CANVAS
)
235 /* The NautilusIconContainer signals. */
243 CONTEXT_CLICK_BACKGROUND
,
244 CONTEXT_CLICK_SELECTION
,
248 GET_ICON_DROP_TARGET_URI
,
249 GET_STORED_ICON_POSITION
,
250 ICON_POSITION_CHANGED
,
252 ICON_STRETCH_STARTED
,
265 START_INTERACTIVE_SEARCH
,
277 static guint signals
[LAST_SIGNAL
];
279 /* Functions dealing with NautilusIcons. */
282 icon_free (NautilusIcon
*icon
)
284 /* Destroy this canvas item; the parent will unref it. */
285 gtk_object_destroy (GTK_OBJECT (icon
->item
));
290 icon_is_positioned (const NautilusIcon
*icon
)
292 return icon
->x
!= ICON_UNPOSITIONED_VALUE
&& icon
->y
!= ICON_UNPOSITIONED_VALUE
;
296 icon_set_position (NautilusIcon
*icon
,
299 NautilusIconContainer
*container
;
300 double pixels_per_unit
;
301 int left
, top
, right
, bottom
;
303 int container_x
, container_y
, container_width
, container_height
;
304 EelDRect icon_bounds
;
306 if (icon
->x
== x
&& icon
->y
== y
) {
310 container
= NAUTILUS_ICON_CONTAINER (EEL_CANVAS_ITEM (icon
->item
)->canvas
);
312 if (icon
== get_icon_being_renamed (container
)) {
313 end_renaming_mode (container
, TRUE
);
316 if (nautilus_icon_container_get_is_fixed_size (container
)) {
317 /* FIXME: This should be:
319 container_x = GTK_WIDGET (container)->allocation.x;
320 container_y = GTK_WIDGET (container)->allocation.y;
321 container_width = GTK_WIDGET (container)->allocation.width;
322 container_height = GTK_WIDGET (container)->allocation.height;
324 But for some reason the widget allocation is sometimes not done
325 at startup, and the allocation is then only 45x60. which is
328 For now, we have a cheesy workaround:
332 container_width
= gdk_screen_width ();
333 container_height
= gdk_screen_height ();
334 pixels_per_unit
= EEL_CANVAS (container
)->pixels_per_unit
;
335 /* Clip the position of the icon within our desktop bounds */
336 left
= container_x
/ pixels_per_unit
;
337 top
= container_y
/ pixels_per_unit
;
338 right
= left
+ container_width
/ pixels_per_unit
;
339 bottom
= top
+ container_height
/ pixels_per_unit
;
341 icon_bounds
= nautilus_icon_canvas_item_get_icon_rectangle (icon
->item
);
342 width
= icon_bounds
.x1
- icon_bounds
.x0
;
344 if (nautilus_icon_container_is_layout_rtl(container
)) {
345 if (x
+ width
< left
+ DESKTOP_ICON_SAFETY_PAD
) {
346 x
= left
+ DESKTOP_ICON_SAFETY_PAD
- width
;
348 if (x
+ width
> right
) {
352 if (x
> right
- DESKTOP_ICON_SAFETY_PAD
) {
353 x
= right
- DESKTOP_ICON_SAFETY_PAD
;
359 if (y
> bottom
- DESKTOP_ICON_SAFETY_PAD
) {
360 y
= bottom
- DESKTOP_ICON_SAFETY_PAD
;
367 if (icon
->x
== ICON_UNPOSITIONED_VALUE
) {
370 if (icon
->y
== ICON_UNPOSITIONED_VALUE
) {
374 eel_canvas_item_move (EEL_CANVAS_ITEM (icon
->item
),
383 icon_get_size (NautilusIconContainer
*container
,
388 *size
= MAX (nautilus_get_icon_size_for_zoom_level (container
->details
->zoom_level
)
389 * icon
->scale
, NAUTILUS_ICON_SIZE_SMALLEST
);
393 /* The icon_set_size function is used by the stretching user
394 * interface, which currently stretches in a way that keeps the aspect
395 * ratio. Later we might have a stretching interface that stretches Y
396 * separate from X and we will change this around.
399 icon_set_size (NautilusIconContainer
*container
,
403 gboolean update_position
)
408 icon_get_size (container
, icon
, &old_size
);
409 if (icon_size
== old_size
) {
413 scale
= (double) icon_size
/
414 nautilus_get_icon_size_for_zoom_level
415 (container
->details
->zoom_level
);
416 nautilus_icon_container_move_icon (container
, icon
,
419 snap
, update_position
);
423 icon_raise (NautilusIcon
*icon
)
425 EelCanvasItem
*item
, *band
;
427 item
= EEL_CANVAS_ITEM (icon
->item
);
428 band
= NAUTILUS_ICON_CONTAINER (item
->canvas
)->details
->rubberband_info
.selection_rectangle
;
430 eel_canvas_item_send_behind (item
, band
);
434 emit_stretch_started (NautilusIconContainer
*container
, NautilusIcon
*icon
)
436 g_signal_emit (container
,
437 signals
[ICON_STRETCH_STARTED
], 0,
442 emit_stretch_ended (NautilusIconContainer
*container
, NautilusIcon
*icon
)
444 g_signal_emit (container
,
445 signals
[ICON_STRETCH_ENDED
], 0,
450 icon_toggle_selected (NautilusIconContainer
*container
,
453 end_renaming_mode (container
, TRUE
);
455 icon
->is_selected
= !icon
->is_selected
;
456 eel_canvas_item_set (EEL_CANVAS_ITEM (icon
->item
),
457 "highlighted_for_selection", (gboolean
) icon
->is_selected
,
460 /* If the icon is deselected, then get rid of the stretch handles.
461 * No harm in doing the same if the item is newly selected.
463 if (icon
== container
->details
->stretch_icon
) {
464 container
->details
->stretch_icon
= NULL
;
465 nautilus_icon_canvas_item_set_show_stretch_handles (icon
->item
, FALSE
);
466 /* snap the icon if necessary */
467 if (container
->details
->keep_aligned
) {
468 nautilus_icon_container_move_icon (container
,
475 emit_stretch_ended (container
, icon
);
478 /* Raise each newly-selected icon to the front as it is selected. */
479 if (icon
->is_selected
) {
484 /* Select an icon. Return TRUE if selection has changed. */
486 icon_set_selected (NautilusIconContainer
*container
,
490 g_assert (select
== FALSE
|| select
== TRUE
);
491 g_assert (icon
->is_selected
== FALSE
|| icon
->is_selected
== TRUE
);
493 if (select
== icon
->is_selected
) {
497 icon_toggle_selected (container
, icon
);
498 g_assert (select
== icon
->is_selected
);
503 icon_get_bounding_box (NautilusIcon
*icon
,
504 int *x1_return
, int *y1_return
,
505 int *x2_return
, int *y2_return
)
507 double x1
, y1
, x2
, y2
;
509 eel_canvas_item_get_bounds (EEL_CANVAS_ITEM (icon
->item
),
518 /* Utility functions for NautilusIconContainer. */
521 nautilus_icon_container_scroll (NautilusIconContainer
*container
,
522 int delta_x
, int delta_y
)
524 GtkAdjustment
*hadj
, *vadj
;
525 int old_h_value
, old_v_value
;
527 hadj
= gtk_layout_get_hadjustment (GTK_LAYOUT (container
));
528 vadj
= gtk_layout_get_vadjustment (GTK_LAYOUT (container
));
530 /* Store the old ajustment values so we can tell if we
531 * ended up actually scrolling. We may not have in a case
532 * where the resulting value got pinned to the adjustment
535 old_h_value
= hadj
->value
;
536 old_v_value
= vadj
->value
;
538 eel_gtk_adjustment_set_value (hadj
, hadj
->value
+ delta_x
);
539 eel_gtk_adjustment_set_value (vadj
, vadj
->value
+ delta_y
);
541 /* return TRUE if we did scroll */
542 return hadj
->value
!= old_h_value
|| vadj
->value
!= old_v_value
;
546 pending_icon_to_reveal_destroy_callback (NautilusIconCanvasItem
*item
,
547 NautilusIconContainer
*container
)
549 g_assert (NAUTILUS_IS_ICON_CONTAINER (container
));
550 g_assert (container
->details
->pending_icon_to_reveal
!= NULL
);
551 g_assert (container
->details
->pending_icon_to_reveal
->item
== item
);
553 container
->details
->pending_icon_to_reveal
= NULL
;
557 get_pending_icon_to_reveal (NautilusIconContainer
*container
)
559 return container
->details
->pending_icon_to_reveal
;
563 set_pending_icon_to_reveal (NautilusIconContainer
*container
, NautilusIcon
*icon
)
565 NautilusIcon
*old_icon
;
567 old_icon
= container
->details
->pending_icon_to_reveal
;
569 if (icon
== old_icon
) {
573 if (old_icon
!= NULL
) {
574 g_signal_handlers_disconnect_by_func
576 G_CALLBACK (pending_icon_to_reveal_destroy_callback
),
581 g_signal_connect (icon
->item
, "destroy",
582 G_CALLBACK (pending_icon_to_reveal_destroy_callback
),
586 container
->details
->pending_icon_to_reveal
= icon
;
590 item_get_canvas_bounds (EelCanvasItem
*item
, EelIRect
*bounds
)
594 eel_canvas_item_get_bounds (item
,
599 eel_canvas_item_i2w (item
->parent
,
602 eel_canvas_item_i2w (item
->parent
,
605 eel_canvas_w2c (item
->canvas
,
610 eel_canvas_w2c (item
->canvas
,
618 reveal_icon (NautilusIconContainer
*container
,
621 NautilusIconContainerDetails
*details
;
622 GtkAllocation
*allocation
;
623 GtkAdjustment
*hadj
, *vadj
;
626 if (!icon_is_positioned (icon
)) {
627 set_pending_icon_to_reveal (container
, icon
);
631 set_pending_icon_to_reveal (container
, NULL
);
633 details
= container
->details
;
634 allocation
= >K_WIDGET (container
)->allocation
;
636 hadj
= gtk_layout_get_hadjustment (GTK_LAYOUT (container
));
637 vadj
= gtk_layout_get_vadjustment (GTK_LAYOUT (container
));
639 item_get_canvas_bounds (EEL_CANVAS_ITEM (icon
->item
), &bounds
);
640 if (bounds
.y0
< vadj
->value
) {
641 eel_gtk_adjustment_set_value (vadj
, bounds
.y0
);
642 } else if (bounds
.y1
> vadj
->value
+ allocation
->height
) {
643 eel_gtk_adjustment_set_value
644 (vadj
, bounds
.y1
- allocation
->height
);
647 if (bounds
.x0
< hadj
->value
) {
648 eel_gtk_adjustment_set_value (hadj
, bounds
.x0
);
649 } else if (bounds
.x1
> hadj
->value
+ allocation
->width
) {
650 eel_gtk_adjustment_set_value
651 (hadj
, bounds
.x1
- allocation
->width
);
656 process_pending_icon_to_reveal (NautilusIconContainer
*container
)
658 NautilusIcon
*pending_icon_to_reveal
;
660 pending_icon_to_reveal
= get_pending_icon_to_reveal (container
);
662 if (pending_icon_to_reveal
!= NULL
) {
663 reveal_icon (container
, pending_icon_to_reveal
);
668 keyboard_icon_reveal_timeout_callback (gpointer data
)
670 NautilusIconContainer
*container
;
673 container
= NAUTILUS_ICON_CONTAINER (data
);
674 icon
= container
->details
->keyboard_icon_to_reveal
;
676 g_assert (icon
!= NULL
);
678 /* Only reveal the icon if it's still the keyboard focus or if
679 * it's still selected. Someone originally thought we should
680 * cancel this reveal if the user manages to sneak a direct
681 * scroll in before the timeout fires, but we later realized
682 * this wouldn't actually be an improvement
683 * (see bugzilla.gnome.org 40612).
685 if (icon
== container
->details
->keyboard_focus
686 || icon
->is_selected
) {
687 reveal_icon (container
, icon
);
689 container
->details
->keyboard_icon_reveal_timer_id
= 0;
695 unschedule_keyboard_icon_reveal (NautilusIconContainer
*container
)
697 NautilusIconContainerDetails
*details
;
699 details
= container
->details
;
701 if (details
->keyboard_icon_reveal_timer_id
!= 0) {
702 g_source_remove (details
->keyboard_icon_reveal_timer_id
);
707 schedule_keyboard_icon_reveal (NautilusIconContainer
*container
,
710 NautilusIconContainerDetails
*details
;
712 details
= container
->details
;
714 unschedule_keyboard_icon_reveal (container
);
716 details
->keyboard_icon_to_reveal
= icon
;
717 details
->keyboard_icon_reveal_timer_id
718 = g_timeout_add (KEYBOARD_ICON_REVEAL_TIMEOUT
,
719 keyboard_icon_reveal_timeout_callback
,
724 clear_keyboard_focus (NautilusIconContainer
*container
)
726 if (container
->details
->keyboard_focus
!= NULL
) {
727 eel_canvas_item_set (EEL_CANVAS_ITEM (container
->details
->keyboard_focus
->item
),
728 "highlighted_as_keyboard_focus", 0,
732 container
->details
->keyboard_focus
= NULL
;
735 /* Set @icon as the icon currently selected for keyboard operations. */
737 set_keyboard_focus (NautilusIconContainer
*container
,
740 g_assert (icon
!= NULL
);
742 if (icon
== container
->details
->keyboard_focus
) {
746 clear_keyboard_focus (container
);
748 container
->details
->keyboard_focus
= icon
;
750 eel_canvas_item_set (EEL_CANVAS_ITEM (container
->details
->keyboard_focus
->item
),
751 "highlighted_as_keyboard_focus", 1,
756 set_keyboard_rubberband_start (NautilusIconContainer
*container
,
759 container
->details
->keyboard_rubberband_start
= icon
;
763 clear_keyboard_rubberband_start (NautilusIconContainer
*container
)
765 container
->details
->keyboard_rubberband_start
= NULL
;
769 get_all_icon_bounds (NautilusIconContainer
*container
,
770 double *x1
, double *y1
,
771 double *x2
, double *y2
)
773 /* FIXME bugzilla.gnome.org 42477: Do we have to do something about the rubberband
774 * here? Any other non-icon items?
776 eel_canvas_item_get_bounds
777 (EEL_CANVAS (container
)->root
,
781 /* Don't preserve visible white space the next time the scroll region
782 * is recomputed when the container is not empty. */
784 nautilus_icon_container_reset_scroll_region (NautilusIconContainer
*container
)
786 container
->details
->reset_scroll_region_trigger
= TRUE
;
789 /* Set a new scroll region without eliminating any of the currently-visible area. */
791 canvas_set_scroll_region_include_visible_area (EelCanvas
*canvas
,
792 double x1
, double y1
,
793 double x2
, double y2
)
795 double old_x1
, old_y1
, old_x2
, old_y2
;
796 double old_scroll_x
, old_scroll_y
;
797 double height
, width
;
799 eel_canvas_get_scroll_region (canvas
, &old_x1
, &old_y1
, &old_x2
, &old_y2
);
801 width
= (GTK_WIDGET (canvas
)->allocation
.width
) / canvas
->pixels_per_unit
;
802 height
= (GTK_WIDGET (canvas
)->allocation
.height
) / canvas
->pixels_per_unit
;
804 old_scroll_x
= gtk_layout_get_hadjustment (GTK_LAYOUT (canvas
))->value
;
805 old_scroll_y
= gtk_layout_get_vadjustment (GTK_LAYOUT (canvas
))->value
;
807 x1
= MIN (x1
, old_x1
+ old_scroll_x
);
808 y1
= MIN (y1
, old_y1
+ old_scroll_y
);
809 x2
= MAX (x2
, old_x1
+ old_scroll_x
+ width
);
810 y2
= MAX (y2
, old_y1
+ old_scroll_y
+ height
);
812 eel_canvas_set_scroll_region
813 (canvas
, x1
, y1
, x2
, y2
);
817 nautilus_icon_container_update_scroll_region (NautilusIconContainer
*container
)
819 double x1
, y1
, x2
, y2
;
820 double pixels_per_unit
;
821 GtkAdjustment
*hadj
, *vadj
;
822 float step_increment
;
823 GtkAllocation
*allocation
;
824 gboolean reset_scroll_region
;
826 if (nautilus_icon_container_get_is_fixed_size (container
)) {
827 pixels_per_unit
= EEL_CANVAS (container
)->pixels_per_unit
;
829 /* Set the scroll region to the size of the container allocation */
830 allocation
= >K_WIDGET (container
)->allocation
;
831 eel_canvas_set_scroll_region
832 (EEL_CANVAS (container
),
833 (double) - container
->details
->left_margin
/ pixels_per_unit
,
834 (double) - container
->details
->top_margin
/ pixels_per_unit
,
835 ((double) (allocation
->width
- 1)
836 - container
->details
->left_margin
837 - container
->details
->right_margin
)
839 ((double) (allocation
->height
- 1)
840 - container
->details
->top_margin
841 - container
->details
->bottom_margin
)
846 reset_scroll_region
= container
->details
->reset_scroll_region_trigger
847 || nautilus_icon_container_is_empty (container
)
848 || nautilus_icon_container_is_auto_layout (container
);
850 /* The trigger is only cleared when container is non-empty, so
851 * callers can reliably reset the scroll region when an item
852 * is added even if extraneous relayouts are called when the
853 * window is still empty.
855 if (!nautilus_icon_container_is_empty (container
)) {
856 container
->details
->reset_scroll_region_trigger
= FALSE
;
859 get_all_icon_bounds (container
, &x1
, &y1
, &x2
, &y2
);
861 /* Auto-layout assumes a 0, 0 scroll origin */
862 if (nautilus_icon_container_is_auto_layout (container
)) {
866 x1
-= CONTAINER_PAD_LEFT
;
867 y1
-= CONTAINER_PAD_TOP
;
870 y2
+= CONTAINER_PAD_BOTTOM
;
872 if (reset_scroll_region
) {
873 eel_canvas_set_scroll_region
874 (EEL_CANVAS (container
),
877 canvas_set_scroll_region_include_visible_area
878 (EEL_CANVAS (container
),
882 hadj
= gtk_layout_get_hadjustment (GTK_LAYOUT (container
));
883 vadj
= gtk_layout_get_vadjustment (GTK_LAYOUT (container
));
885 /* Scroll by 1/4 icon each time you click. */
886 step_increment
= nautilus_get_icon_size_for_zoom_level
887 (container
->details
->zoom_level
) / 4;
888 if (hadj
->step_increment
!= step_increment
) {
889 hadj
->step_increment
= step_increment
;
890 gtk_adjustment_changed (hadj
);
892 if (vadj
->step_increment
!= step_increment
) {
893 vadj
->step_increment
= step_increment
;
894 gtk_adjustment_changed (vadj
);
897 /* Now that we have a new scroll region, clamp the
898 * adjustments so we are within the valid scroll area.
900 eel_gtk_adjustment_clamp_value (hadj
);
901 eel_gtk_adjustment_clamp_value (vadj
);
904 * In RTL mode, when displayed force horizontal scrollbar to the
908 if (nautilus_icon_container_is_layout_rtl(container
))
909 gtk_adjustment_set_value (hadj
, hadj
->upper
- hadj
->page_size
);
913 compare_icons (gconstpointer a
, gconstpointer b
, gpointer icon_container
)
915 NautilusIconContainerClass
*klass
;
916 const NautilusIcon
*icon_a
, *icon_b
;
920 klass
= NAUTILUS_ICON_CONTAINER_GET_CLASS (icon_container
);
922 return klass
->compare_icons (icon_container
, icon_a
->data
, icon_b
->data
);
926 sort_icons (NautilusIconContainer
*container
,
929 NautilusIconContainerClass
*klass
;
931 klass
= NAUTILUS_ICON_CONTAINER_GET_CLASS (container
);
932 g_return_if_fail (klass
->compare_icons
!= NULL
);
934 *icons
= g_list_sort_with_data (*icons
, compare_icons
, container
);
938 resort (NautilusIconContainer
*container
)
940 sort_icons (container
, &container
->details
->icons
);
945 get_grid_width (NautilusIconContainer
*container
)
947 if (container
->details
->label_position
== NAUTILUS_ICON_LABEL_POSITION_BESIDE
) {
948 return TEXT_BESIDE_ICON_GRID_WIDTH
;
950 return STANDARD_ICON_GRID_WIDTH
;
962 lay_down_one_line (NautilusIconContainer
*container
,
972 IconPositions
*position
;
976 is_rtl
= nautilus_icon_container_is_layout_rtl (container
);
978 /* Lay out the icons along the baseline. */
981 for (p
= line_start
; p
!= line_end
; p
= p
->next
) {
984 position
= &g_array_index (positions
, IconPositions
, i
++);
986 if (container
->details
->label_position
== NAUTILUS_ICON_LABEL_POSITION_BESIDE
) {
987 y_offset
= (max_height
- position
->height
) / 2;
989 y_offset
= position
->y_offset
;
994 is_rtl
? get_mirror_x_position (container
, icon
, x
+ position
->x_offset
) : x
+ position
->x_offset
,
997 icon
->saved_ltr_x
= is_rtl
? get_mirror_x_position (container
, icon
, icon
->x
) : icon
->x
;
999 x
+= position
->width
;
1004 lay_down_icons_horizontal (NautilusIconContainer
*container
,
1008 GList
*p
, *line_start
;
1010 double canvas_width
, y
, canvas_height
;
1012 IconPositions
*position
;
1014 EelDRect icon_bounds
;
1015 EelDRect text_bounds
;
1016 EelCanvasItem
*item
;
1017 double max_height_above
, max_height_below
;
1018 double height_above
, height_below
;
1020 gboolean gridded_layout
;
1022 double max_text_width
, max_icon_width
;
1026 g_assert (NAUTILUS_IS_ICON_CONTAINER (container
));
1028 if (icons
== NULL
) {
1032 positions
= g_array_new (FALSE
, FALSE
, sizeof (IconPositions
));
1034 /* Lay out icons a line at a time. */
1035 canvas_width
= CANVAS_WIDTH(container
);
1036 canvas_height
= CANVAS_HEIGHT(container
);
1038 max_icon_width
= max_text_width
= 0.0;
1040 if (container
->details
->label_position
== NAUTILUS_ICON_LABEL_POSITION_BESIDE
) {
1041 /* Would it be worth caching these bounds for the next loop? */
1042 for (p
= icons
; p
!= NULL
; p
= p
->next
) {
1045 icon_bounds
= nautilus_icon_canvas_item_get_icon_rectangle (icon
->item
);
1046 max_icon_width
= MAX (max_icon_width
, ceil (icon_bounds
.x1
- icon_bounds
.x0
));
1048 text_bounds
= nautilus_icon_canvas_item_get_text_rectangle (icon
->item
);
1049 max_text_width
= MAX (max_text_width
, ceil (text_bounds
.x1
- text_bounds
.x0
));
1052 grid_width
= max_icon_width
+ max_text_width
+ ICON_PAD_LEFT
+ ICON_PAD_RIGHT
;
1054 grid_width
= STANDARD_ICON_GRID_WIDTH
;
1057 gridded_layout
= !nautilus_icon_container_is_tighter_layout (container
);
1059 line_width
= container
->details
->label_position
== NAUTILUS_ICON_LABEL_POSITION_BESIDE
? ICON_PAD_LEFT
: 0;
1064 max_height_above
= 0;
1065 max_height_below
= 0;
1066 for (p
= icons
; p
!= NULL
; p
= p
->next
) {
1069 /* Get the width of the icon. */
1070 item
= EEL_CANVAS_ITEM (icon
->item
);
1072 /* Assume it's only one level hierarchy to avoid costly affine calculations */
1073 eel_canvas_item_get_bounds (item
,
1074 &bounds
.x0
, &bounds
.y0
,
1075 &bounds
.x1
, &bounds
.y1
);
1077 icon_bounds
= nautilus_icon_canvas_item_get_icon_rectangle (icon
->item
);
1078 text_bounds
= nautilus_icon_canvas_item_get_text_rectangle (icon
->item
);
1080 if (gridded_layout
) {
1081 icon_width
= ceil ((bounds
.x1
- bounds
.x0
)/grid_width
) * grid_width
;
1085 icon_width
= (bounds
.x1
- bounds
.x0
) + ICON_PAD_RIGHT
+ 8; /* 8 pixels extra for fancy selection box */
1088 /* Calculate size above/below baseline */
1089 height_above
= icon_bounds
.y1
- bounds
.y0
;
1090 height_below
= bounds
.y1
- icon_bounds
.y1
;
1092 /* If this icon doesn't fit, it's time to lay out the line that's queued up. */
1093 if (line_start
!= p
&& line_width
+ icon_width
> canvas_width
) {
1094 if (container
->details
->label_position
== NAUTILUS_ICON_LABEL_POSITION_BESIDE
) {
1097 /* Advance to the baseline. */
1098 y
+= ICON_PAD_TOP
+ max_height_above
;
1101 lay_down_one_line (container
, line_start
, p
, y
, max_height_above
, positions
);
1103 if (container
->details
->label_position
== NAUTILUS_ICON_LABEL_POSITION_BESIDE
) {
1104 y
+= max_height_above
+ max_height_below
+ ICON_PAD_BOTTOM
;
1106 /* Advance to next line. */
1107 y
+= max_height_below
+ ICON_PAD_BOTTOM
;
1110 line_width
= container
->details
->label_position
== NAUTILUS_ICON_LABEL_POSITION_BESIDE
? ICON_PAD_LEFT
: 0;
1114 max_height_above
= height_above
;
1115 max_height_below
= height_below
;
1117 if (height_above
> max_height_above
) {
1118 max_height_above
= height_above
;
1120 if (height_below
> max_height_below
) {
1121 max_height_below
= height_below
;
1125 g_array_set_size (positions
, i
+ 1);
1126 position
= &g_array_index (positions
, IconPositions
, i
++);
1127 position
->width
= icon_width
;
1128 position
->height
= icon_bounds
.y1
- icon_bounds
.y0
;
1130 if (container
->details
->label_position
== NAUTILUS_ICON_LABEL_POSITION_BESIDE
) {
1131 if (gridded_layout
) {
1132 position
->x_offset
= max_icon_width
+ ICON_PAD_LEFT
+ ICON_PAD_RIGHT
- (icon_bounds
.x1
- icon_bounds
.x0
);
1134 position
->x_offset
= icon_width
- ((icon_bounds
.x1
- icon_bounds
.x0
) + (text_bounds
.x1
- text_bounds
.x0
));
1136 position
->y_offset
= 0;
1138 position
->x_offset
= (icon_width
- (icon_bounds
.x1
- icon_bounds
.x0
)) / 2;
1139 position
->y_offset
= icon_bounds
.y0
- icon_bounds
.y1
;
1142 /* Add this icon. */
1143 line_width
+= icon_width
;
1146 /* Lay down that last line of icons. */
1147 if (line_start
!= NULL
) {
1148 if (container
->details
->label_position
== NAUTILUS_ICON_LABEL_POSITION_BESIDE
) {
1151 /* Advance to the baseline. */
1152 y
+= ICON_PAD_TOP
+ max_height_above
;
1155 lay_down_one_line (container
, line_start
, NULL
, y
, max_height_above
, positions
);
1157 /* Advance to next line. */
1158 y
+= max_height_below
+ ICON_PAD_BOTTOM
;
1161 g_array_free (positions
, TRUE
);
1165 snap_position (NautilusIconContainer
*container
,
1173 EelDRect icon_position
;
1175 icon_position
= nautilus_icon_canvas_item_get_icon_rectangle (icon
->item
);
1176 icon_width
= icon_position
.x1
- icon_position
.x0
;
1177 icon_height
= icon_position
.y1
- icon_position
.y0
;
1179 if (nautilus_icon_container_is_layout_rtl (container
))
1180 *x
= get_mirror_x_position (container
, icon
, *x
);
1182 if (*x
+ icon_width
/ 2 < DESKTOP_PAD_HORIZONTAL
+ SNAP_SIZE_X
) {
1183 *x
= DESKTOP_PAD_HORIZONTAL
+ SNAP_SIZE_X
- icon_width
/ 2;
1186 if (*y
+ icon_height
< DESKTOP_PAD_VERTICAL
+ SNAP_SIZE_Y
) {
1187 *y
= DESKTOP_PAD_VERTICAL
+ SNAP_SIZE_Y
- icon_height
;
1190 center_x
= *x
+ icon_width
/ 2;
1191 *x
= SNAP_NEAREST_HORIZONTAL (center_x
) - (icon_width
/ 2);
1192 if (nautilus_icon_container_is_layout_rtl (container
)) {
1193 *x
= get_mirror_x_position (container
, icon
, *x
);
1197 /* Find the grid position vertically and place on the proper baseline */
1198 baseline_y
= *y
+ icon_height
;
1199 baseline_y
= SNAP_NEAREST_VERTICAL (baseline_y
);
1200 *y
= baseline_y
- icon_height
;
1204 compare_icons_by_position (gconstpointer a
, gconstpointer b
)
1206 NautilusIcon
*icon_a
, *icon_b
;
1211 icon_a
= (NautilusIcon
*)a
;
1212 icon_b
= (NautilusIcon
*)b
;
1214 icon_get_bounding_box (icon_a
, &x1
, &y1
, &x2
, &y2
);
1215 center_a
= x1
+ (x2
- x1
) / 2;
1216 icon_get_bounding_box (icon_b
, &x1
, &y1
, &x2
, &y2
);
1217 center_b
= x1
+ (x2
- x1
) / 2;
1219 return center_a
== center_b
?
1220 icon_a
->y
- icon_b
->y
:
1221 center_a
- center_b
;
1224 static PlacementGrid
*
1225 placement_grid_new (NautilusIconContainer
*container
, gboolean tight
)
1227 PlacementGrid
*grid
;
1233 /* Get container dimensions */
1234 width
= CANVAS_WIDTH(container
);
1235 height
= CANVAS_HEIGHT(container
);
1237 num_columns
= width
/ SNAP_SIZE_X
;
1238 num_rows
= height
/ SNAP_SIZE_Y
;
1240 if (num_columns
== 0 || num_rows
== 0) {
1244 grid
= g_new0 (PlacementGrid
, 1);
1245 grid
->tight
= tight
;
1246 grid
->num_columns
= num_columns
;
1247 grid
->num_rows
= num_rows
;
1249 grid
->grid_memory
= g_new0 (int, (num_rows
* num_columns
));
1250 grid
->icon_grid
= g_new0 (int *, num_columns
);
1252 for (i
= 0; i
< num_columns
; i
++) {
1253 grid
->icon_grid
[i
] = grid
->grid_memory
+ (i
* num_rows
);
1260 placement_grid_free (PlacementGrid
*grid
)
1262 g_free (grid
->icon_grid
);
1263 g_free (grid
->grid_memory
);
1268 placement_grid_position_is_free (PlacementGrid
*grid
, EelIRect pos
)
1272 g_return_val_if_fail (pos
.x0
>= 0 && pos
.x0
< grid
->num_columns
, TRUE
);
1273 g_return_val_if_fail (pos
.y0
>= 0 && pos
.y0
< grid
->num_rows
, TRUE
);
1274 g_return_val_if_fail (pos
.x1
>= 0 && pos
.x1
< grid
->num_columns
, TRUE
);
1275 g_return_val_if_fail (pos
.y1
>= 0 && pos
.y1
< grid
->num_rows
, TRUE
);
1277 for (x
= pos
.x0
; x
<= pos
.x1
; x
++) {
1278 for (y
= pos
.y0
; y
<= pos
.y1
; y
++) {
1279 if (grid
->icon_grid
[x
][y
] != 0) {
1289 placement_grid_mark (PlacementGrid
*grid
, EelIRect pos
)
1293 g_return_if_fail (pos
.x0
>= 0 && pos
.x0
< grid
->num_columns
);
1294 g_return_if_fail (pos
.y0
>= 0 && pos
.y0
< grid
->num_rows
);
1295 g_return_if_fail (pos
.x1
>= 0 && pos
.x1
< grid
->num_columns
);
1296 g_return_if_fail (pos
.y1
>= 0 && pos
.y1
< grid
->num_rows
);
1298 for (x
= pos
.x0
; x
<= pos
.x1
; x
++) {
1299 for (y
= pos
.y0
; y
<= pos
.y1
; y
++) {
1300 grid
->icon_grid
[x
][y
] = 1;
1306 canvas_position_to_grid_position (PlacementGrid
*grid
,
1307 EelIRect canvas_position
,
1308 EelIRect
*grid_position
)
1310 /* The first causes minimal moving around during a snap, but
1311 * can end up with partially overlapping icons. The second one won't
1312 * allow any overlapping, but can cause more movement to happen
1315 grid_position
->x0
= ceil ((double)(canvas_position
.x0
- DESKTOP_PAD_HORIZONTAL
) / SNAP_SIZE_X
);
1316 grid_position
->y0
= ceil ((double)(canvas_position
.y0
- DESKTOP_PAD_VERTICAL
) / SNAP_SIZE_Y
);
1317 grid_position
->x1
= floor ((double)(canvas_position
.x1
- DESKTOP_PAD_HORIZONTAL
) / SNAP_SIZE_X
);
1318 grid_position
->y1
= floor ((double)(canvas_position
.y1
- DESKTOP_PAD_VERTICAL
) / SNAP_SIZE_Y
);
1320 grid_position
->x0
= floor ((double)(canvas_position
.x0
- DESKTOP_PAD_HORIZONTAL
) / SNAP_SIZE_X
);
1321 grid_position
->y0
= floor ((double)(canvas_position
.y0
- DESKTOP_PAD_VERTICAL
) / SNAP_SIZE_Y
);
1322 grid_position
->x1
= floor ((double)(canvas_position
.x1
- DESKTOP_PAD_HORIZONTAL
) / SNAP_SIZE_X
);
1323 grid_position
->y1
= floor ((double)(canvas_position
.y1
- DESKTOP_PAD_VERTICAL
) / SNAP_SIZE_Y
);
1326 grid_position
->x0
= CLAMP (grid_position
->x0
, 0, grid
->num_columns
- 1);
1327 grid_position
->y0
= CLAMP (grid_position
->y0
, 0, grid
->num_rows
- 1);
1328 grid_position
->x1
= CLAMP (grid_position
->x1
, grid_position
->x0
, grid
->num_columns
- 1);
1329 grid_position
->y1
= CLAMP (grid_position
->y1
, grid_position
->y0
, grid
->num_rows
- 1);
1333 placement_grid_mark_icon (PlacementGrid
*grid
, NautilusIcon
*icon
)
1338 icon_get_bounding_box (icon
,
1339 &icon_pos
.x0
, &icon_pos
.y0
,
1340 &icon_pos
.x1
, &icon_pos
.y1
);
1341 canvas_position_to_grid_position (grid
,
1344 placement_grid_mark (grid
, grid_pos
);
1348 find_empty_location (NautilusIconContainer
*container
,
1349 PlacementGrid
*grid
,
1356 double icon_width
, icon_height
;
1359 EelIRect icon_position
;
1360 EelDRect pixbuf_rect
;
1363 /* Get container dimensions */
1364 canvas_width
= CANVAS_WIDTH(container
);
1365 canvas_height
= CANVAS_HEIGHT(container
);
1367 icon_get_bounding_box (icon
,
1368 &icon_position
.x0
, &icon_position
.y0
,
1369 &icon_position
.x1
, &icon_position
.y1
);
1370 icon_width
= icon_position
.x1
- icon_position
.x0
;
1371 icon_height
= icon_position
.y1
- icon_position
.y0
;
1373 pixbuf_rect
= nautilus_icon_canvas_item_get_icon_rectangle (icon
->item
);
1375 /* Start the icon on a grid location */
1376 snap_position (container
, icon
, &start_x
, &start_y
);
1378 icon_position
.x0
= start_x
;
1379 icon_position
.y0
= start_y
;
1380 icon_position
.x1
= icon_position
.x0
+ icon_width
;
1381 icon_position
.y1
= icon_position
.y0
+ icon_height
;
1384 EelIRect grid_position
;
1388 canvas_position_to_grid_position (grid
,
1392 if (!placement_grid_position_is_free (grid
, grid_position
)) {
1393 icon_position
.y0
+= SNAP_SIZE_Y
;
1394 icon_position
.y1
= icon_position
.y0
+ icon_height
;
1396 if (icon_position
.y1
+ DESKTOP_PAD_VERTICAL
> canvas_height
) {
1397 /* Move to the next column */
1398 icon_position
.y0
= DESKTOP_PAD_VERTICAL
+ SNAP_SIZE_Y
- (pixbuf_rect
.y1
- pixbuf_rect
.y0
);
1399 while (icon_position
.y0
< DESKTOP_PAD_VERTICAL
) {
1400 icon_position
.y0
+= SNAP_SIZE_Y
;
1402 icon_position
.y1
= icon_position
.y0
+ icon_height
;
1404 icon_position
.x0
+= SNAP_SIZE_X
;
1405 icon_position
.x1
= icon_position
.x0
+ icon_width
;
1410 } while (collision
&& (icon_position
.x1
< canvas_width
));
1412 *x
= icon_position
.x0
;
1413 *y
= icon_position
.y0
;
1417 align_icons (NautilusIconContainer
*container
)
1419 GList
*unplaced_icons
;
1421 PlacementGrid
*grid
;
1423 unplaced_icons
= g_list_copy (container
->details
->icons
);
1425 unplaced_icons
= g_list_sort (unplaced_icons
,
1426 compare_icons_by_position
);
1428 if (nautilus_icon_container_is_layout_rtl (container
)) {
1429 unplaced_icons
= g_list_reverse (unplaced_icons
);
1432 grid
= placement_grid_new (container
, TRUE
);
1438 for (l
= unplaced_icons
; l
!= NULL
; l
= l
->next
) {
1443 x
= icon
->saved_ltr_x
;
1445 find_empty_location (container
, grid
,
1446 icon
, x
, y
, &x
, &y
);
1448 icon_set_position (icon
, x
, y
);
1449 icon
->saved_ltr_x
= icon
->x
;
1450 placement_grid_mark_icon (grid
, icon
);
1453 g_list_free (unplaced_icons
);
1455 placement_grid_free (grid
);
1457 if (nautilus_icon_container_is_layout_rtl (container
)) {
1458 nautilus_icon_container_set_rtl_positions (container
);
1463 get_mirror_x_position (NautilusIconContainer
*container
, NautilusIcon
*icon
, double x
)
1465 EelDRect icon_bounds
;
1467 icon_bounds
= nautilus_icon_canvas_item_get_icon_rectangle (icon
->item
);
1469 return CANVAS_WIDTH(container
) - x
- (icon_bounds
.x1
- icon_bounds
.x0
);
1473 nautilus_icon_container_set_rtl_positions (NautilusIconContainer
*container
)
1479 if (!container
->details
->icons
) {
1483 for (l
= container
->details
->icons
; l
!= NULL
; l
= l
->next
) {
1485 x
= get_mirror_x_position (container
, icon
, icon
->saved_ltr_x
);
1486 icon_set_position (icon
, x
, icon
->y
);
1491 lay_down_icons_vertical (NautilusIconContainer
*container
, GList
*icons
)
1493 GList
*p
, *placed_icons
, *unplaced_icons
;
1494 int total
, new_length
, placed
;
1496 int width
, height
, max_width
, column_width
, icon_width
, icon_height
;
1497 int x
, y
, x1
, x2
, y1
, y2
;
1500 /* Get container dimensions */
1501 width
= CANVAS_WIDTH(container
);
1502 height
= CANVAS_HEIGHT(container
);
1504 /* Determine which icons have and have not been placed */
1505 placed_icons
= NULL
;
1506 unplaced_icons
= NULL
;
1508 total
= g_list_length (container
->details
->icons
);
1509 new_length
= g_list_length (icons
);
1510 placed
= total
- new_length
;
1512 PlacementGrid
*grid
;
1513 /* Add only placed icons in list */
1514 for (p
= container
->details
->icons
; p
!= NULL
; p
= p
->next
) {
1516 if (icon_is_positioned (icon
)) {
1517 icon_set_position(icon
, icon
->saved_ltr_x
, icon
->y
);
1518 placed_icons
= g_list_prepend (placed_icons
, icon
);
1522 unplaced_icons
= g_list_prepend (unplaced_icons
, icon
);
1525 placed_icons
= g_list_reverse (placed_icons
);
1526 unplaced_icons
= g_list_reverse (unplaced_icons
);
1528 grid
= placement_grid_new (container
, FALSE
);
1531 for (p
= placed_icons
; p
!= NULL
; p
= p
->next
) {
1532 placement_grid_mark_icon
1533 (grid
, (NautilusIcon
*)p
->data
);
1536 /* Place unplaced icons in the best locations */
1537 for (p
= unplaced_icons
; p
!= NULL
; p
= p
->next
) {
1540 icon_rect
= nautilus_icon_canvas_item_get_icon_rectangle (icon
->item
);
1542 /* Start the icon in the first column */
1543 x
= DESKTOP_PAD_HORIZONTAL
+ (SNAP_SIZE_X
/ 2) - ((icon_rect
.x1
- icon_rect
.x0
) / 2);
1544 y
= DESKTOP_PAD_VERTICAL
+ SNAP_SIZE_Y
- (icon_rect
.y1
- icon_rect
.y0
);
1546 find_empty_location (container
,
1552 icon_set_position (icon
, x
, y
);
1553 icon
->saved_ltr_x
= x
;
1554 placement_grid_mark_icon (grid
, icon
);
1557 placement_grid_free (grid
);
1560 g_list_free (placed_icons
);
1561 g_list_free (unplaced_icons
);
1563 /* There are no placed icons. Just lay them down using our rules */
1564 x
= DESKTOP_PAD_HORIZONTAL
;
1566 while (icons
!= NULL
) {
1569 gboolean should_snap
;
1571 should_snap
= !(container
->details
->tighter_layout
&& !container
->details
->keep_aligned
);
1573 y
= DESKTOP_PAD_VERTICAL
;
1577 /* Calculate max width for column */
1578 for (p
= icons
; p
!= NULL
; p
= p
->next
) {
1580 icon_get_bounding_box (icon
, &x1
, &y1
, &x2
, &y2
);
1582 icon_width
= x2
- x1
;
1583 icon_height
= y2
- y1
;
1586 /* Snap the baseline to a grid position */
1587 icon_rect
= nautilus_icon_canvas_item_get_icon_rectangle (icon
->item
);
1588 baseline
= y
+ (icon_rect
.y1
- icon_rect
.y0
);
1589 baseline
= SNAP_CEIL_VERTICAL (baseline
);
1590 y
= baseline
- (icon_rect
.y1
- icon_rect
.y0
);
1593 /* Check and see if we need to move to a new column */
1594 if (y
!= DESKTOP_PAD_VERTICAL
&& y
> height
- icon_height
) {
1598 if (max_width
< icon_width
) {
1599 max_width
= icon_width
;
1602 y
+= icon_height
+ DESKTOP_PAD_VERTICAL
;
1605 y
= DESKTOP_PAD_VERTICAL
;
1607 center_x
= x
+ max_width
/ 2;
1608 column_width
= max_width
;
1610 /* Find the grid column to center on */
1611 center_x
= SNAP_CEIL_HORIZONTAL (center_x
);
1612 column_width
= (center_x
- x
) + (max_width
/ 2);
1615 /* Lay out column */
1616 for (p
= icons
; p
!= NULL
; p
= p
->next
) {
1618 icon_get_bounding_box (icon
, &x1
, &y1
, &x2
, &y2
);
1620 icon_height
= y2
- y1
;
1622 icon_rect
= nautilus_icon_canvas_item_get_icon_rectangle (icon
->item
);
1625 baseline
= y
+ (icon_rect
.y1
- icon_rect
.y0
);
1626 baseline
= SNAP_CEIL_VERTICAL (baseline
);
1627 y
= baseline
- (icon_rect
.y1
- icon_rect
.y0
);
1630 /* Check and see if we need to move to a new column */
1631 if (y
!= DESKTOP_PAD_VERTICAL
&& y
> height
- icon_height
&&
1632 /* Make sure we lay out at least one icon per column, to make progress */
1634 x
+= column_width
+ DESKTOP_PAD_HORIZONTAL
;
1638 icon_set_position (icon
,
1639 center_x
- (icon_rect
.x1
- icon_rect
.x0
) / 2,
1642 icon
->saved_ltr_x
= icon
->x
;
1643 y
+= icon_height
+ DESKTOP_PAD_VERTICAL
;
1649 /* These modes are special. We freeze all of our positions
1650 * after we do the layout.
1652 /* FIXME bugzilla.gnome.org 42478:
1653 * This should not be tied to the direction of layout.
1654 * It should be a separate switch.
1656 nautilus_icon_container_freeze_icon_positions (container
);
1661 lay_down_icons (NautilusIconContainer
*container
, GList
*icons
, double start_y
)
1663 switch (container
->details
->layout_mode
)
1665 case NAUTILUS_ICON_LAYOUT_L_R_T_B
:
1666 case NAUTILUS_ICON_LAYOUT_R_L_T_B
:
1667 lay_down_icons_horizontal (container
, icons
, start_y
);
1670 case NAUTILUS_ICON_LAYOUT_T_B_L_R
:
1671 case NAUTILUS_ICON_LAYOUT_T_B_R_L
:
1672 lay_down_icons_vertical (container
, icons
);
1676 g_assert_not_reached ();
1681 redo_layout_internal (NautilusIconContainer
*container
)
1683 finish_adding_new_icons (container
);
1685 /* Don't do any re-laying-out during stretching. Later we
1686 * might add smart logic that does this and leaves room for
1687 * the stretched icon, but if we do it we want it to be fast
1688 * and only re-lay-out when it's really needed.
1690 if (container
->details
->auto_layout
1691 && container
->details
->drag_state
!= DRAG_STATE_STRETCH
) {
1693 lay_down_icons (container
, container
->details
->icons
, 0);
1696 if (nautilus_icon_container_is_layout_rtl (container
)) {
1697 nautilus_icon_container_set_rtl_positions (container
);
1700 nautilus_icon_container_update_scroll_region (container
);
1702 process_pending_icon_to_reveal (container
);
1703 process_pending_icon_to_rename (container
);
1704 nautilus_icon_container_update_visible_icons (container
);
1708 redo_layout_callback (gpointer callback_data
)
1710 NautilusIconContainer
*container
;
1712 container
= NAUTILUS_ICON_CONTAINER (callback_data
);
1713 redo_layout_internal (container
);
1714 container
->details
->idle_id
= 0;
1720 unschedule_redo_layout (NautilusIconContainer
*container
)
1722 if (container
->details
->idle_id
!= 0) {
1723 g_source_remove (container
->details
->idle_id
);
1724 container
->details
->idle_id
= 0;
1729 schedule_redo_layout (NautilusIconContainer
*container
)
1731 if (container
->details
->idle_id
== 0
1732 && container
->details
->has_been_allocated
) {
1733 container
->details
->idle_id
= g_idle_add
1734 (redo_layout_callback
, container
);
1739 redo_layout (NautilusIconContainer
*container
)
1741 unschedule_redo_layout (container
);
1742 redo_layout_internal (container
);
1746 reload_icon_positions (NautilusIconContainer
*container
)
1748 GList
*p
, *no_position_icons
;
1750 gboolean have_stored_position
;
1751 NautilusIconPosition position
;
1754 EelCanvasItem
*item
;
1756 g_assert (!container
->details
->auto_layout
);
1760 no_position_icons
= NULL
;
1762 /* Place all the icons with positions. */
1764 for (p
= container
->details
->icons
; p
!= NULL
; p
= p
->next
) {
1767 have_stored_position
= FALSE
;
1768 g_signal_emit (container
,
1769 signals
[GET_STORED_ICON_POSITION
], 0,
1772 &have_stored_position
);
1773 if (have_stored_position
) {
1774 icon_set_position (icon
, position
.x
, position
.y
);
1775 item
= EEL_CANVAS_ITEM (icon
->item
);
1776 eel_canvas_item_get_bounds (item
,
1781 eel_canvas_item_i2w (item
->parent
,
1784 eel_canvas_item_i2w (item
->parent
,
1787 if (bounds
.y1
> bottom
) {
1791 no_position_icons
= g_list_prepend (no_position_icons
, icon
);
1794 no_position_icons
= g_list_reverse (no_position_icons
);
1796 /* Place all the other icons. */
1797 lay_down_icons (container
, no_position_icons
, bottom
+ ICON_PAD_BOTTOM
);
1798 g_list_free (no_position_icons
);
1801 /* Container-level icon handling functions. */
1804 button_event_modifies_selection (GdkEventButton
*event
)
1806 return (event
->state
& (GDK_CONTROL_MASK
| GDK_SHIFT_MASK
)) != 0;
1809 /* invalidate the cached label sizes for all the icons */
1811 invalidate_label_sizes (NautilusIconContainer
*container
)
1816 for (p
= container
->details
->icons
; p
!= NULL
; p
= p
->next
) {
1819 nautilus_icon_canvas_item_invalidate_label_size (icon
->item
);
1824 select_range (NautilusIconContainer
*container
,
1825 NautilusIcon
*icon1
,
1826 NautilusIcon
*icon2
)
1828 gboolean selection_changed
;
1831 NautilusIcon
*unmatched_icon
;
1834 selection_changed
= FALSE
;
1836 unmatched_icon
= NULL
;
1838 for (p
= container
->details
->icons
; p
!= NULL
; p
= p
->next
) {
1841 if (unmatched_icon
== NULL
) {
1842 if (icon
== icon1
) {
1843 unmatched_icon
= icon2
;
1845 } else if (icon
== icon2
) {
1846 unmatched_icon
= icon1
;
1851 selection_changed
|= icon_set_selected
1852 (container
, icon
, select
);
1854 if (unmatched_icon
!= NULL
&& icon
== unmatched_icon
) {
1860 if (selection_changed
&& icon2
!= NULL
) {
1861 AtkObject
*atk_object
= eel_accessibility_for_object (icon2
->item
);
1862 atk_focus_tracker_notify (atk_object
);
1864 return selection_changed
;
1869 select_one_unselect_others (NautilusIconContainer
*container
,
1870 NautilusIcon
*icon_to_select
)
1872 gboolean selection_changed
;
1876 selection_changed
= FALSE
;
1878 for (p
= container
->details
->icons
; p
!= NULL
; p
= p
->next
) {
1881 selection_changed
|= icon_set_selected
1882 (container
, icon
, icon
== icon_to_select
);
1885 if (selection_changed
&& icon_to_select
!= NULL
) {
1886 AtkObject
*atk_object
= eel_accessibility_for_object (icon_to_select
->item
);
1887 atk_focus_tracker_notify (atk_object
);
1888 reveal_icon (container
, icon_to_select
);
1890 return selection_changed
;
1894 unselect_all (NautilusIconContainer
*container
)
1896 return select_one_unselect_others (container
, NULL
);
1900 nautilus_icon_container_move_icon (NautilusIconContainer
*container
,
1906 gboolean update_position
)
1908 NautilusIconContainerDetails
*details
;
1909 gboolean emit_signal
;
1910 NautilusIconPosition position
;
1912 details
= container
->details
;
1914 emit_signal
= FALSE
;
1916 if (icon
== get_icon_being_renamed (container
)) {
1917 end_renaming_mode (container
, TRUE
);
1920 if (scale
!= icon
->scale
) {
1921 icon
->scale
= scale
;
1922 nautilus_icon_container_update_icon (container
, icon
);
1923 if (update_position
) {
1924 redo_layout (container
);
1929 if (!details
->auto_layout
) {
1930 if (details
->keep_aligned
&& snap
) {
1931 snap_position (container
, icon
, &x
, &y
);
1934 if (x
!= icon
->x
|| y
!= icon
->y
) {
1935 icon_set_position (icon
, x
, y
);
1936 emit_signal
= update_position
;
1939 icon
->saved_ltr_x
= nautilus_icon_container_is_layout_rtl (container
) ? get_mirror_x_position (container
, icon
, icon
->x
) : icon
->x
;
1943 position
.x
= icon
->saved_ltr_x
;
1944 position
.y
= icon
->y
;
1945 position
.scale
= scale
;
1946 g_signal_emit (container
,
1947 signals
[ICON_POSITION_CHANGED
], 0,
1948 icon
->data
, &position
);
1955 /* FIXME bugzilla.gnome.org 42474:
1956 * Handling of the scroll region is inconsistent here. In
1957 * the scale-changing case, redo_layout is called, which updates the
1958 * scroll region appropriately. In other cases, it's up to the
1959 * caller to make sure the scroll region is updated. This could
1960 * lead to hard-to-track-down bugs.
1964 /* Implementation of rubberband selection. */
1966 rubberband_select (NautilusIconContainer
*container
,
1967 const EelDRect
*previous_rect
,
1968 const EelDRect
*current_rect
)
1971 gboolean selection_changed
, is_in
, canvas_rect_calculated
;
1973 EelIRect canvas_rect
;
1976 selection_changed
= FALSE
;
1977 canvas_rect_calculated
= FALSE
;
1979 for (p
= container
->details
->icons
; p
!= NULL
; p
= p
->next
) {
1982 if (!canvas_rect_calculated
) {
1983 /* Only do this calculation once, since all the canvas items
1984 * we are interating are in the same coordinate space
1986 canvas
= EEL_CANVAS_ITEM (icon
->item
)->canvas
;
1987 eel_canvas_w2c (canvas
,
1992 eel_canvas_w2c (canvas
,
1997 canvas_rect_calculated
= TRUE
;
2000 is_in
= nautilus_icon_canvas_item_hit_test_rectangle (icon
->item
, canvas_rect
);
2002 g_assert (icon
->was_selected_before_rubberband
== FALSE
2003 || icon
->was_selected_before_rubberband
== TRUE
);
2004 selection_changed
|= icon_set_selected
2006 is_in
^ icon
->was_selected_before_rubberband
);
2009 if (selection_changed
) {
2010 g_signal_emit (container
,
2011 signals
[SELECTION_CHANGED
], 0);
2016 rubberband_timeout_callback (gpointer data
)
2018 NautilusIconContainer
*container
;
2020 NautilusIconRubberbandInfo
*band_info
;
2022 double x1
, y1
, x2
, y2
;
2023 double world_x
, world_y
;
2024 int x_scroll
, y_scroll
;
2026 gboolean adj_changed
;
2028 EelDRect selection_rect
;
2030 widget
= GTK_WIDGET (data
);
2031 container
= NAUTILUS_ICON_CONTAINER (data
);
2032 band_info
= &container
->details
->rubberband_info
;
2034 g_assert (band_info
->timer_id
!= 0);
2035 g_assert (EEL_IS_CANVAS_RECT (band_info
->selection_rectangle
) ||
2036 EEL_IS_CANVAS_RECT (band_info
->selection_rectangle
));
2038 adj_changed
= FALSE
;
2039 adj_y
= gtk_adjustment_get_value (gtk_layout_get_vadjustment (GTK_LAYOUT (container
)));
2040 if (adj_y
!= band_info
->last_adj_y
) {
2041 band_info
->last_adj_y
= adj_y
;
2045 gtk_widget_get_pointer (widget
, &x
, &y
);
2050 } else if (x
>= widget
->allocation
.width
) {
2051 x_scroll
= x
- widget
->allocation
.width
+ 1;
2052 x
= widget
->allocation
.width
- 1;
2060 } else if (y
>= widget
->allocation
.height
) {
2061 y_scroll
= y
- widget
->allocation
.height
+ 1;
2062 y
= widget
->allocation
.height
- 1;
2067 if (y_scroll
== 0 && x_scroll
== 0
2068 && (int) band_info
->prev_x
== x
&& (int) band_info
->prev_y
== y
&& !adj_changed
) {
2072 nautilus_icon_container_scroll (container
, x_scroll
, y_scroll
);
2074 /* Remember to convert from widget to scrolled window coords */
2075 eel_canvas_window_to_world (EEL_CANVAS (container
),
2076 x
+ gtk_adjustment_get_value (gtk_layout_get_hadjustment (GTK_LAYOUT (container
))),
2077 y
+ gtk_adjustment_get_value (gtk_layout_get_vadjustment (GTK_LAYOUT (container
))),
2078 &world_x
, &world_y
);
2080 if (world_x
< band_info
->start_x
) {
2082 x2
= band_info
->start_x
;
2084 x1
= band_info
->start_x
;
2088 if (world_y
< band_info
->start_y
) {
2090 y2
= band_info
->start_y
;
2092 y1
= band_info
->start_y
;
2096 /* Don't let the area of the selection rectangle be empty.
2097 * Aside from the fact that it would be funny when the rectangle disappears,
2098 * this also works around a crash in libart that happens sometimes when a
2099 * zero height rectangle is passed.
2101 x2
= MAX (x1
+ 1, x2
);
2102 y2
= MAX (y1
+ 1, y2
);
2105 (band_info
->selection_rectangle
,
2110 selection_rect
.x0
= x1
;
2111 selection_rect
.y0
= y1
;
2112 selection_rect
.x1
= x2
;
2113 selection_rect
.y1
= y2
;
2115 rubberband_select (container
,
2116 &band_info
->prev_rect
,
2119 band_info
->prev_x
= x
;
2120 band_info
->prev_y
= y
;
2122 band_info
->prev_rect
= selection_rect
;
2128 start_rubberbanding (NautilusIconContainer
*container
,
2129 GdkEventButton
*event
)
2131 AtkObject
*accessible
;
2132 NautilusIconContainerDetails
*details
;
2133 NautilusIconRubberbandInfo
*band_info
;
2134 guint fill_color
, outline_color
;
2135 GdkColor
*fill_color_gdk
;
2136 guchar fill_color_alpha
;
2140 details
= container
->details
;
2141 band_info
= &details
->rubberband_info
;
2143 g_signal_emit (container
,
2144 signals
[BAND_SELECT_STARTED
], 0);
2146 for (p
= details
->icons
; p
!= NULL
; p
= p
->next
) {
2148 icon
->was_selected_before_rubberband
= icon
->is_selected
;
2151 eel_canvas_window_to_world
2152 (EEL_CANVAS (container
), event
->x
, event
->y
,
2153 &band_info
->start_x
, &band_info
->start_y
);
2155 gtk_widget_style_get (GTK_WIDGET (container
),
2156 "selection_box_color", &fill_color_gdk
,
2157 "selection_box_alpha", &fill_color_alpha
,
2160 if (!fill_color_gdk
) {
2161 fill_color_gdk
= gdk_color_copy (>K_WIDGET (container
)->style
->base
[GTK_STATE_SELECTED
]);
2164 fill_color
= eel_gdk_color_to_rgb (fill_color_gdk
) << 8 | fill_color_alpha
;
2166 gdk_color_free (fill_color_gdk
);
2168 outline_color
= fill_color
| 255;
2170 band_info
->selection_rectangle
= eel_canvas_item_new
2172 (EEL_CANVAS (container
)),
2173 EEL_TYPE_CANVAS_RECT
,
2174 "x1", band_info
->start_x
,
2175 "y1", band_info
->start_y
,
2176 "x2", band_info
->start_x
,
2177 "y2", band_info
->start_y
,
2178 "fill_color_rgba", fill_color
,
2179 "outline_color_rgba", outline_color
,
2183 accessible
= atk_gobject_accessible_for_object
2184 (G_OBJECT (band_info
->selection_rectangle
));
2185 atk_object_set_name (accessible
, "selection");
2186 atk_object_set_description (accessible
, _("The selection rectangle"));
2188 band_info
->prev_x
= event
->x
- gtk_adjustment_get_value (gtk_layout_get_hadjustment (GTK_LAYOUT (container
)));
2189 band_info
->prev_y
= event
->y
- gtk_adjustment_get_value (gtk_layout_get_vadjustment (GTK_LAYOUT (container
)));
2191 band_info
->active
= TRUE
;
2193 if (band_info
->timer_id
== 0) {
2194 band_info
->timer_id
= g_timeout_add
2195 (RUBBERBAND_TIMEOUT_INTERVAL
,
2196 rubberband_timeout_callback
,
2200 eel_canvas_item_grab (band_info
->selection_rectangle
,
2201 (GDK_POINTER_MOTION_MASK
2202 | GDK_BUTTON_RELEASE_MASK
2208 stop_rubberbanding (NautilusIconContainer
*container
,
2211 NautilusIconRubberbandInfo
*band_info
;
2214 band_info
= &container
->details
->rubberband_info
;
2216 g_assert (band_info
->timer_id
!= 0);
2217 g_source_remove (band_info
->timer_id
);
2218 band_info
->timer_id
= 0;
2220 band_info
->active
= FALSE
;
2222 /* Destroy this canvas item; the parent will unref it. */
2223 eel_canvas_item_ungrab (band_info
->selection_rectangle
, time
);
2224 gtk_object_destroy (GTK_OBJECT (band_info
->selection_rectangle
));
2225 band_info
->selection_rectangle
= NULL
;
2227 /* if only one item has been selected, use it as range
2228 * selection base (cf. handle_icon_button_press) */
2229 icons
= nautilus_icon_container_get_selected_icons (container
);
2230 if (g_list_length (icons
) == 1) {
2231 container
->details
->range_selection_base_icon
= icons
->data
;
2233 g_list_free (icons
);
2235 g_signal_emit (container
,
2236 signals
[BAND_SELECT_ENDED
], 0);
2239 /* Keyboard navigation. */
2241 typedef gboolean (* IsBetterIconFunction
) (NautilusIconContainer
*container
,
2242 NautilusIcon
*start_icon
,
2243 NautilusIcon
*best_so_far
,
2244 NautilusIcon
*candidate
,
2247 static NautilusIcon
*
2248 find_best_icon (NautilusIconContainer
*container
,
2249 NautilusIcon
*start_icon
,
2250 IsBetterIconFunction function
,
2254 NautilusIcon
*best
, *candidate
;
2257 for (p
= container
->details
->icons
; p
!= NULL
; p
= p
->next
) {
2258 candidate
= p
->data
;
2260 if (candidate
!= start_icon
) {
2261 if ((* function
) (container
, start_icon
, best
, candidate
, data
)) {
2269 static NautilusIcon
*
2270 find_best_selected_icon (NautilusIconContainer
*container
,
2271 NautilusIcon
*start_icon
,
2272 IsBetterIconFunction function
,
2276 NautilusIcon
*best
, *candidate
;
2279 for (p
= container
->details
->icons
; p
!= NULL
; p
= p
->next
) {
2280 candidate
= p
->data
;
2282 if (candidate
!= start_icon
&& candidate
->is_selected
) {
2283 if ((* function
) (container
, start_icon
, best
, candidate
, data
)) {
2292 compare_icons_by_uri (NautilusIconContainer
*container
,
2293 NautilusIcon
*icon_a
,
2294 NautilusIcon
*icon_b
)
2296 char *uri_a
, *uri_b
;
2299 g_assert (NAUTILUS_IS_ICON_CONTAINER (container
));
2300 g_assert (icon_a
!= NULL
);
2301 g_assert (icon_b
!= NULL
);
2302 g_assert (icon_a
!= icon_b
);
2304 uri_a
= nautilus_icon_container_get_icon_uri (container
, icon_a
);
2305 uri_b
= nautilus_icon_container_get_icon_uri (container
, icon_b
);
2306 result
= strcmp (uri_a
, uri_b
);
2307 g_assert (result
!= 0);
2315 get_cmp_point_x (NautilusIconContainer
*container
,
2318 if (container
->details
->label_position
== NAUTILUS_ICON_LABEL_POSITION_BESIDE
) {
2319 if (gtk_widget_get_direction (GTK_WIDGET (container
)) == GTK_TEXT_DIR_RTL
) {
2320 return icon_rect
.x0
;
2322 return icon_rect
.x1
;
2325 return (icon_rect
.x0
+ icon_rect
.x1
) / 2;
2330 get_cmp_point_y (NautilusIconContainer
*container
,
2333 if (container
->details
->label_position
== NAUTILUS_ICON_LABEL_POSITION_BESIDE
) {
2334 return (icon_rect
.y0
+ icon_rect
.y1
)/2;
2336 return icon_rect
.y1
;
2341 compare_icons_horizontal_first (NautilusIconContainer
*container
,
2342 NautilusIcon
*icon_a
,
2343 NautilusIcon
*icon_b
)
2345 EelDRect world_rect
;
2348 world_rect
= nautilus_icon_canvas_item_get_icon_rectangle (icon_a
->item
);
2350 (EEL_CANVAS (container
),
2351 get_cmp_point_x (container
, world_rect
),
2352 get_cmp_point_y (container
, world_rect
),
2355 world_rect
= nautilus_icon_canvas_item_get_icon_rectangle (icon_b
->item
);
2357 (EEL_CANVAS (container
),
2358 get_cmp_point_x (container
, world_rect
),
2359 get_cmp_point_y (container
, world_rect
),
2375 return compare_icons_by_uri (container
, icon_a
, icon_b
);
2379 compare_icons_vertical_first_reverse_horizontal (NautilusIconContainer
*container
,
2380 NautilusIcon
*icon_a
,
2381 NautilusIcon
*icon_b
)
2383 EelDRect world_rect
;
2386 world_rect
= nautilus_icon_canvas_item_get_icon_rectangle (icon_a
->item
);
2388 (EEL_CANVAS (container
),
2389 get_cmp_point_x (container
, world_rect
),
2390 get_cmp_point_y (container
, world_rect
),
2393 world_rect
= nautilus_icon_canvas_item_get_icon_rectangle (icon_b
->item
);
2395 (EEL_CANVAS (container
),
2396 get_cmp_point_x (container
, world_rect
),
2397 get_cmp_point_y (container
, world_rect
),
2408 return (nautilus_icon_container_is_layout_rtl (container
) ? -1 : +1);
2411 return (nautilus_icon_container_is_layout_rtl (container
) ? +1 : -1);
2413 return compare_icons_by_uri (container
, icon_a
, icon_b
);
2418 compare_icons_vertical_first (NautilusIconContainer
*container
,
2419 NautilusIcon
*icon_a
,
2420 NautilusIcon
*icon_b
)
2422 EelDRect world_rect
;
2425 world_rect
= nautilus_icon_canvas_item_get_icon_rectangle (icon_a
->item
);
2427 (EEL_CANVAS (container
),
2428 get_cmp_point_x (container
, world_rect
),
2429 get_cmp_point_y (container
, world_rect
),
2432 world_rect
= nautilus_icon_canvas_item_get_icon_rectangle (icon_b
->item
);
2434 (EEL_CANVAS (container
),
2435 get_cmp_point_x (container
, world_rect
),
2436 get_cmp_point_y (container
, world_rect
),
2452 return compare_icons_by_uri (container
, icon_a
, icon_b
);
2456 leftmost_in_top_row (NautilusIconContainer
*container
,
2457 NautilusIcon
*start_icon
,
2458 NautilusIcon
*best_so_far
,
2459 NautilusIcon
*candidate
,
2462 if (best_so_far
== NULL
) {
2465 return compare_icons_vertical_first (container
, best_so_far
, candidate
) > 0;
2469 rightmost_in_bottom_row (NautilusIconContainer
*container
,
2470 NautilusIcon
*start_icon
,
2471 NautilusIcon
*best_so_far
,
2472 NautilusIcon
*candidate
,
2475 if (best_so_far
== NULL
) {
2478 return compare_icons_vertical_first (container
, best_so_far
, candidate
) < 0;
2482 compare_with_start_row (NautilusIconContainer
*container
,
2485 EelCanvasItem
*item
;
2487 item
= EEL_CANVAS_ITEM (icon
->item
);
2489 if (container
->details
->arrow_key_start
< item
->y1
) {
2492 if (container
->details
->arrow_key_start
> item
->y2
) {
2499 compare_with_start_column (NautilusIconContainer
*container
,
2502 EelCanvasItem
*item
;
2504 item
= EEL_CANVAS_ITEM (icon
->item
);
2506 if (container
->details
->arrow_key_start
< item
->x1
) {
2509 if (container
->details
->arrow_key_start
> item
->x2
) {
2516 same_row_right_side_leftmost (NautilusIconContainer
*container
,
2517 NautilusIcon
*start_icon
,
2518 NautilusIcon
*best_so_far
,
2519 NautilusIcon
*candidate
,
2522 /* Candidates not on the start row do not qualify. */
2523 if (compare_with_start_row (container
, candidate
) != 0) {
2527 /* Candidates that are farther right lose out. */
2528 if (best_so_far
!= NULL
) {
2529 if (compare_icons_horizontal_first (container
,
2536 /* Candidate to the left of the start do not qualify. */
2537 if (compare_icons_horizontal_first (container
,
2547 same_row_left_side_rightmost (NautilusIconContainer
*container
,
2548 NautilusIcon
*start_icon
,
2549 NautilusIcon
*best_so_far
,
2550 NautilusIcon
*candidate
,
2553 /* Candidates not on the start row do not qualify. */
2554 if (compare_with_start_row (container
, candidate
) != 0) {
2558 /* Candidates that are farther left lose out. */
2559 if (best_so_far
!= NULL
) {
2560 if (compare_icons_horizontal_first (container
,
2567 /* Candidate to the right of the start do not qualify. */
2568 if (compare_icons_horizontal_first (container
,
2578 same_column_above_lowest (NautilusIconContainer
*container
,
2579 NautilusIcon
*start_icon
,
2580 NautilusIcon
*best_so_far
,
2581 NautilusIcon
*candidate
,
2584 /* Candidates not on the start column do not qualify. */
2585 if (compare_with_start_column (container
, candidate
) != 0) {
2589 /* Candidates that are higher lose out. */
2590 if (best_so_far
!= NULL
) {
2591 if (compare_icons_vertical_first (container
,
2598 /* Candidates below the start do not qualify. */
2599 if (compare_icons_vertical_first (container
,
2609 same_column_below_highest (NautilusIconContainer
*container
,
2610 NautilusIcon
*start_icon
,
2611 NautilusIcon
*best_so_far
,
2612 NautilusIcon
*candidate
,
2615 EelCanvasItem
*item
;
2617 item
= EEL_CANVAS_ITEM (candidate
->item
);
2619 /* Candidates above or on the start row do not qualify. */
2620 if (container
->details
->arrow_key_start_y
>= item
->y1
) {
2624 if (best_so_far
!= NULL
) {
2625 /* Candidates on the start column are preferred. */
2626 if (compare_with_start_column (container
, candidate
) != 0 &&
2627 compare_with_start_column (container
, best_so_far
) == 0) {
2630 /* Candidates that are lower or to the left lose out. */
2631 if (compare_icons_vertical_first_reverse_horizontal (container
, best_so_far
, candidate
) <= 0) {
2640 closest_in_90_degrees (NautilusIconContainer
*container
,
2641 NautilusIcon
*start_icon
,
2642 NautilusIcon
*best_so_far
,
2643 NautilusIcon
*candidate
,
2646 EelDRect world_rect
;
2653 world_rect
= nautilus_icon_canvas_item_get_icon_rectangle (candidate
->item
);
2655 (EEL_CANVAS (container
),
2656 get_cmp_point_x (container
, world_rect
),
2657 get_cmp_point_y (container
, world_rect
),
2661 dx
= x
- container
->details
->arrow_key_start_x
;
2662 dy
= y
- container
->details
->arrow_key_start_y
;
2664 switch (container
->details
->arrow_key_direction
) {
2667 ABS(dx
) > ABS(dy
)) {
2673 ABS(dx
) > ABS(dy
)) {
2679 ABS(dy
) > ABS(dx
)) {
2685 ABS(dy
) > ABS(dx
)) {
2690 g_assert_not_reached();
2693 dist
= dx
*dx
+ dy
*dy
;
2696 if (best_so_far
== NULL
) {
2701 if (dist
< *best_dist
) {
2710 get_rubberband (NautilusIcon
*icon1
,
2711 NautilusIcon
*icon2
)
2717 eel_canvas_item_get_bounds (EEL_CANVAS_ITEM (icon1
->item
),
2718 &rect1
.x0
, &rect1
.y0
,
2719 &rect1
.x1
, &rect1
.y1
);
2720 eel_canvas_item_get_bounds (EEL_CANVAS_ITEM (icon2
->item
),
2721 &rect2
.x0
, &rect2
.y0
,
2722 &rect2
.x1
, &rect2
.y1
);
2724 eel_drect_union (&ret
, &rect1
, &rect2
);
2730 keyboard_move_to (NautilusIconContainer
*container
,
2739 if ((event
->state
& GDK_CONTROL_MASK
) != 0) {
2740 /* Move the keyboard focus. Use Control modifier
2741 * rather than Alt to avoid Sawfish conflict.
2743 set_keyboard_focus (container
, icon
);
2744 container
->details
->keyboard_rubberband_start
= NULL
;
2745 } else if ((event
->state
& GDK_SHIFT_MASK
) != 0) {
2746 /* Do rubberband selection */
2749 if (from
&& !container
->details
->keyboard_rubberband_start
) {
2750 set_keyboard_rubberband_start (container
, from
);
2753 select_one_unselect_others (container
, icon
);
2754 set_keyboard_focus (container
, icon
);
2756 if (icon
&& container
->details
->keyboard_rubberband_start
&& container
->details
->keyboard_rubberband_start
!= icon
) {
2757 rect
= get_rubberband (container
->details
->keyboard_rubberband_start
,
2759 rubberband_select (container
, NULL
, &rect
);
2762 /* Select icons and get rid of the special keyboard focus. */
2763 clear_keyboard_focus (container
);
2764 clear_keyboard_rubberband_start (container
);
2766 container
->details
->range_selection_base_icon
= icon
;
2767 if (select_one_unselect_others (container
, icon
)) {
2768 g_signal_emit (container
,
2769 signals
[SELECTION_CHANGED
], 0);
2772 schedule_keyboard_icon_reveal (container
, icon
);
2776 keyboard_home (NautilusIconContainer
*container
,
2782 /* Home selects the first icon.
2783 * Control-Home sets the keyboard focus to the first icon.
2786 from
= find_best_selected_icon (container
, NULL
,
2787 rightmost_in_bottom_row
,
2789 to
= find_best_icon (container
, NULL
, leftmost_in_top_row
, NULL
);
2791 container
->details
->arrow_key_axis
= AXIS_NONE
;
2792 keyboard_move_to (container
, to
, from
, event
);
2796 keyboard_end (NautilusIconContainer
*container
,
2802 /* End selects the last icon.
2803 * Control-End sets the keyboard focus to the last icon.
2805 from
= find_best_selected_icon (container
, NULL
,
2806 leftmost_in_top_row
,
2808 to
= find_best_icon (container
, NULL
, rightmost_in_bottom_row
, NULL
);
2810 container
->details
->arrow_key_axis
= AXIS_NONE
;
2811 keyboard_move_to (container
, to
, from
, event
);
2815 record_arrow_key_start (NautilusIconContainer
*container
,
2817 GtkDirectionType direction
)
2819 EelDRect world_rect
;
2821 world_rect
= nautilus_icon_canvas_item_get_icon_rectangle (icon
->item
);
2823 (EEL_CANVAS (container
),
2824 get_cmp_point_x (container
, world_rect
),
2825 get_cmp_point_y (container
, world_rect
),
2826 &container
->details
->arrow_key_start_x
,
2827 &container
->details
->arrow_key_start_y
);
2829 container
->details
->arrow_key_direction
= direction
;
2831 switch (container
->details
->arrow_key_direction
) {
2834 container
->details
->arrow_key_axis
= AXIS_VERTICAL
;
2835 container
->details
->arrow_key_start
= container
->details
->arrow_key_start_x
;
2839 container
->details
->arrow_key_axis
= AXIS_HORIZONTAL
;
2840 container
->details
->arrow_key_start
= container
->details
->arrow_key_start_y
;
2843 g_assert_not_reached();
2848 keyboard_arrow_key (NautilusIconContainer
*container
,
2850 GtkDirectionType direction
,
2851 IsBetterIconFunction better_start
,
2852 IsBetterIconFunction empty_start
,
2853 IsBetterIconFunction better_destination
,
2854 IsBetterIconFunction better_destination_manual
)
2860 /* Chose the icon to start with.
2861 * If we have a keyboard focus, start with it.
2862 * Otherwise, use the single selected icon.
2863 * If there's multiple selection, use the icon farthest toward the end.
2866 from
= container
->details
->keyboard_focus
;
2869 if (has_multiple_selection (container
)) {
2870 if (all_selected (container
)) {
2871 from
= find_best_selected_icon
2875 from
= find_best_selected_icon
2877 better_start
, NULL
);
2880 from
= get_first_selected_icon (container
);
2884 /* If there's no icon, select the icon farthest toward the end.
2885 * If there is an icon, select the next icon based on the arrow direction.
2888 container
->details
->arrow_key_axis
= AXIS_NONE
;
2889 to
= from
= find_best_icon
2894 record_arrow_key_start (container
, from
, direction
);
2898 container
->details
->auto_layout
? better_destination
: better_destination_manual
,
2902 keyboard_move_to (container
, to
, from
, event
);
2906 keyboard_right (NautilusIconContainer
*container
,
2909 /* Right selects the next icon in the same row.
2910 * Control-Right sets the keyboard focus to the next icon in the same row.
2912 keyboard_arrow_key (container
,
2915 rightmost_in_bottom_row
,
2916 leftmost_in_top_row
,
2917 same_row_right_side_leftmost
,
2918 closest_in_90_degrees
);
2922 keyboard_left (NautilusIconContainer
*container
,
2925 /* Left selects the next icon in the same row.
2926 * Control-Left sets the keyboard focus to the next icon in the same row.
2928 keyboard_arrow_key (container
,
2931 leftmost_in_top_row
,
2932 rightmost_in_bottom_row
,
2933 same_row_left_side_rightmost
,
2934 closest_in_90_degrees
);
2938 keyboard_down (NautilusIconContainer
*container
,
2941 /* Down selects the next icon in the same column.
2942 * Control-Down sets the keyboard focus to the next icon in the same column.
2944 keyboard_arrow_key (container
,
2947 rightmost_in_bottom_row
,
2948 leftmost_in_top_row
,
2949 same_column_below_highest
,
2950 closest_in_90_degrees
);
2954 keyboard_up (NautilusIconContainer
*container
,
2957 /* Up selects the next icon in the same column.
2958 * Control-Up sets the keyboard focus to the next icon in the same column.
2960 keyboard_arrow_key (container
,
2963 leftmost_in_top_row
,
2964 rightmost_in_bottom_row
,
2965 same_column_above_lowest
,
2966 closest_in_90_degrees
);
2970 keyboard_space (NautilusIconContainer
*container
,
2975 /* Control-space toggles the selection state of the current icon. */
2976 if ((event
->state
& GDK_CONTROL_MASK
) != 0) {
2977 if (container
->details
->keyboard_focus
!= NULL
) {
2978 icon_toggle_selected (container
, container
->details
->keyboard_focus
);
2979 g_signal_emit (container
, signals
[SELECTION_CHANGED
], 0);
2980 if (container
->details
->keyboard_focus
->is_selected
) {
2981 container
->details
->range_selection_base_icon
= container
->details
->keyboard_focus
;
2984 icon
= find_best_selected_icon (container
,
2986 leftmost_in_top_row
,
2989 icon
= find_best_icon (container
,
2991 leftmost_in_top_row
,
2995 set_keyboard_focus (container
, icon
);
2998 } else if ((event
->state
& GDK_SHIFT_MASK
) != 0) {
2999 activate_selected_items_alternate (container
, NULL
);
3001 activate_selected_items (container
);
3005 /* look for the first icon that matches the longest part of a given
3010 int last_match_length
;
3013 #ifndef TAB_NAVIGATION_DISABLED
3015 select_previous_or_next_icon (NautilusIconContainer
*container
,
3023 /* Chose the icon to start with.
3024 * If we have a keyboard focus, start with it.
3025 * Otherwise, use the single selected icon.
3027 icon
= container
->details
->keyboard_focus
;
3029 icon
= get_first_selected_icon (container
);
3033 /* must have at least @icon in the list */
3034 g_assert (container
->details
->icons
!= NULL
);
3035 item
= g_list_find (container
->details
->icons
, icon
);
3036 g_assert (item
!= NULL
);
3038 item
= next
? item
->next
: item
->prev
;
3040 item
= next
? g_list_first (container
->details
->icons
) : g_list_last (container
->details
->icons
);
3043 } else if (container
->details
->icons
!= NULL
) {
3044 /* no selection yet, pick the first or last item to select */
3045 item
= next
? g_list_first (container
->details
->icons
) : g_list_last (container
->details
->icons
);
3048 icon
= (item
!= NULL
) ? item
->data
: NULL
;
3051 keyboard_move_to (container
, icon
, NULL
, event
);
3056 /* GtkObject methods. */
3059 destroy (GtkObject
*object
)
3061 NautilusIconContainer
*container
;
3063 container
= NAUTILUS_ICON_CONTAINER (object
);
3065 nautilus_icon_container_clear (container
);
3067 if (container
->details
->rubberband_info
.timer_id
!= 0) {
3068 g_source_remove (container
->details
->rubberband_info
.timer_id
);
3069 container
->details
->rubberband_info
.timer_id
= 0;
3072 if (container
->details
->idle_id
!= 0) {
3073 g_source_remove (container
->details
->idle_id
);
3074 container
->details
->idle_id
= 0;
3077 if (container
->details
->stretch_idle_id
!= 0) {
3078 g_source_remove (container
->details
->stretch_idle_id
);
3079 container
->details
->stretch_idle_id
= 0;
3082 if (container
->details
->align_idle_id
!= 0) {
3083 g_source_remove (container
->details
->align_idle_id
);
3084 container
->details
->align_idle_id
= 0;
3088 /* destroy interactive search dialog */
3089 if (container
->details
->search_window
) {
3090 gtk_widget_destroy (container
->details
->search_window
);
3091 container
->details
->search_window
= NULL
;
3092 container
->details
->search_entry
= NULL
;
3093 if (container
->details
->typeselect_flush_timeout
) {
3094 g_source_remove (container
->details
->typeselect_flush_timeout
);
3095 container
->details
->typeselect_flush_timeout
= 0;
3100 GTK_OBJECT_CLASS (parent_class
)->destroy (object
);
3104 finalize (GObject
*object
)
3106 NautilusIconContainerDetails
*details
;
3108 details
= NAUTILUS_ICON_CONTAINER (object
)->details
;
3110 eel_preferences_remove_callback (NAUTILUS_PREFERENCES_THEME
,
3111 nautilus_icon_container_theme_changed
,
3114 g_hash_table_destroy (details
->icon_set
);
3115 details
->icon_set
= NULL
;
3117 g_free (details
->font
);
3119 if (details
->a11y_item_action_queue
!= NULL
) {
3120 while (!g_queue_is_empty (details
->a11y_item_action_queue
)) {
3121 g_free (g_queue_pop_head (details
->a11y_item_action_queue
));
3123 g_queue_free (details
->a11y_item_action_queue
);
3125 if (details
->a11y_item_action_idle_handler
!= 0) {
3126 g_source_remove (details
->a11y_item_action_idle_handler
);
3131 G_OBJECT_CLASS (parent_class
)->finalize (object
);
3134 /* GtkWidget methods. */
3137 size_request (GtkWidget
*widget
,
3138 GtkRequisition
*requisition
)
3140 GTK_WIDGET_CLASS (parent_class
)->size_request (widget
, requisition
);
3141 requisition
->width
= 1;
3142 requisition
->height
= 1;
3146 size_allocate (GtkWidget
*widget
,
3147 GtkAllocation
*allocation
)
3149 NautilusIconContainer
*container
;
3150 gboolean need_layout_redone
;
3152 container
= NAUTILUS_ICON_CONTAINER (widget
);
3154 need_layout_redone
= !container
->details
->has_been_allocated
;
3156 if (allocation
->width
!= widget
->allocation
.width
) {
3157 need_layout_redone
= TRUE
;
3160 GTK_WIDGET_CLASS (parent_class
)->size_allocate (widget
, allocation
);
3162 container
->details
->has_been_allocated
= TRUE
;
3164 if (need_layout_redone
) {
3165 redo_layout (container
);
3170 realize (GtkWidget
*widget
)
3174 GtkAdjustment
*vadj
;
3176 GTK_WIDGET_CLASS (parent_class
)->realize (widget
);
3179 nautilus_icon_dnd_init (NAUTILUS_ICON_CONTAINER (widget
), NULL
);
3181 setup_label_gcs (NAUTILUS_ICON_CONTAINER (widget
));
3183 /* make us the focused widget */
3184 g_assert (GTK_IS_WINDOW (gtk_widget_get_toplevel (widget
)));
3185 window
= GTK_WINDOW (gtk_widget_get_toplevel (widget
));
3186 gtk_window_set_focus (window
, widget
);
3188 stipple
= eel_stipple_bitmap_for_screen (
3189 gdk_drawable_get_screen (GDK_DRAWABLE (widget
->window
)));
3191 nautilus_icon_dnd_set_stipple (NAUTILUS_ICON_CONTAINER (widget
), stipple
);
3193 vadj
= gtk_layout_get_vadjustment (GTK_LAYOUT (widget
));
3194 g_signal_connect (vadj
, "value_changed",
3195 G_CALLBACK (handle_vadjustment_changed
), widget
);
3200 unrealize (GtkWidget
*widget
)
3204 NautilusIconContainer
*container
;
3206 container
= NAUTILUS_ICON_CONTAINER (widget
);
3208 g_assert (GTK_IS_WINDOW (gtk_widget_get_toplevel (widget
)));
3209 window
= GTK_WINDOW (gtk_widget_get_toplevel (widget
));
3210 gtk_window_set_focus (window
, NULL
);
3212 for (i
= 0; i
< LAST_LABEL_COLOR
; i
++) {
3213 if (container
->details
->label_gcs
[i
]) {
3214 g_object_unref (container
->details
->label_gcs
[i
]);
3215 container
->details
->label_gcs
[i
] = NULL
;
3219 nautilus_icon_dnd_fini (container
);
3221 if (container
->details
->typeselect_flush_timeout
) {
3222 g_source_remove (container
->details
->typeselect_flush_timeout
);
3223 container
->details
->typeselect_flush_timeout
= 0;
3226 GTK_WIDGET_CLASS (parent_class
)->unrealize (widget
);
3230 style_set (GtkWidget
*widget
,
3231 GtkStyle
*previous_style
)
3233 NautilusIconContainer
*container
;
3234 gboolean frame_text
;
3236 container
= NAUTILUS_ICON_CONTAINER (widget
);
3238 gtk_widget_style_get (GTK_WIDGET (container
),
3239 "frame_text", &frame_text
,
3242 container
->details
->use_drop_shadows
= container
->details
->drop_shadows_requested
&& !frame_text
;
3244 nautilus_icon_container_theme_changed (NAUTILUS_ICON_CONTAINER (widget
));
3246 if (GTK_WIDGET_REALIZED (widget
)) {
3247 invalidate_label_sizes (container
);
3248 nautilus_icon_container_request_update_all (container
);
3251 GTK_WIDGET_CLASS (parent_class
)->style_set (widget
, previous_style
);
3255 button_press_event (GtkWidget
*widget
,
3256 GdkEventButton
*event
)
3258 NautilusIconContainer
*container
;
3259 gboolean selection_changed
;
3260 gboolean return_value
;
3261 gboolean clicked_on_icon
;
3263 container
= NAUTILUS_ICON_CONTAINER (widget
);
3264 container
->details
->button_down_time
= event
->time
;
3266 /* Forget about the old keyboard selection now that we've started mousing. */
3267 clear_keyboard_focus (container
);
3268 clear_keyboard_rubberband_start (container
);
3270 /* Forget about where we began with the arrow keys now that we're mousing. */
3271 container
->details
->arrow_key_axis
= AXIS_NONE
;
3273 /* Invoke the canvas event handler and see if an item picks up the event. */
3274 clicked_on_icon
= GTK_WIDGET_CLASS (parent_class
)->button_press_event (widget
, event
);
3276 /* Move focus to icon container, unless we're still renaming (to avoid exiting
3279 if (!GTK_WIDGET_HAS_FOCUS (widget
) && !(is_renaming (container
) || is_renaming_pending (container
))) {
3280 gtk_widget_grab_focus (widget
);
3283 if (clicked_on_icon
) {
3287 /* An item didn't take the press, so it's a background press.
3288 * We ignore double clicks on the desktop for now.
3290 if (event
->type
== GDK_2BUTTON_PRESS
|| event
->type
== GDK_3BUTTON_PRESS
) {
3294 if ((event
->button
== DRAG_BUTTON
|| event
->button
== MIDDLE_BUTTON
) &&
3295 event
->type
== GDK_BUTTON_PRESS
) {
3296 /* Clear the last click icon for double click */
3297 container
->details
->double_click_icon
[1] = container
->details
->double_click_icon
[0];
3298 container
->details
->double_click_icon
[0] = NULL
;
3301 /* Button 1 does rubber banding. */
3302 if (event
->button
== RUBBERBAND_BUTTON
) {
3303 if (! button_event_modifies_selection (event
)) {
3304 selection_changed
= unselect_all (container
);
3305 if (selection_changed
) {
3306 g_signal_emit (container
,
3307 signals
[SELECTION_CHANGED
], 0);
3311 start_rubberbanding (container
, event
);
3315 /* Prevent multi-button weirdness such as bug 6181 */
3316 if (container
->details
->rubberband_info
.active
) {
3320 /* Button 2 may be passed to the window manager. */
3321 if (event
->button
== MIDDLE_BUTTON
) {
3322 selection_changed
= unselect_all (container
);
3323 if (selection_changed
) {
3324 g_signal_emit (container
, signals
[SELECTION_CHANGED
], 0);
3326 g_signal_emit (widget
, signals
[MIDDLE_CLICK
], 0, event
);
3330 /* Button 3 does a contextual menu. */
3331 if (event
->button
== CONTEXTUAL_MENU_BUTTON
) {
3332 end_renaming_mode (container
, TRUE
);
3333 selection_changed
= unselect_all (container
);
3334 if (selection_changed
) {
3335 g_signal_emit (container
, signals
[SELECTION_CHANGED
], 0);
3337 g_signal_emit (widget
, signals
[CONTEXT_CLICK_BACKGROUND
], 0, event
);
3341 /* Otherwise, we emit a button_press message. */
3342 g_signal_emit (widget
,
3343 signals
[BUTTON_PRESS
], 0, event
,
3345 return return_value
;
3349 nautilus_icon_container_did_not_drag (NautilusIconContainer
*container
,
3350 GdkEventButton
*event
)
3352 NautilusIconContainerDetails
*details
;
3353 gboolean selection_changed
;
3354 static gint64 last_click_time
= 0;
3355 static gint click_count
= 0;
3356 gint double_click_time
;
3357 gint64 current_time
;
3359 details
= container
->details
;
3361 if (details
->icon_selected_on_button_down
&&
3362 ((event
->state
& GDK_CONTROL_MASK
) != 0 ||
3363 (event
->state
& GDK_SHIFT_MASK
) == 0)) {
3364 if (button_event_modifies_selection (event
)) {
3365 details
->range_selection_base_icon
= NULL
;
3366 icon_toggle_selected (container
, details
->drag_icon
);
3367 g_signal_emit (container
,
3368 signals
[SELECTION_CHANGED
], 0);
3370 details
->range_selection_base_icon
= details
->drag_icon
;
3371 selection_changed
= select_one_unselect_others
3372 (container
, details
->drag_icon
);
3374 if (selection_changed
) {
3375 g_signal_emit (container
,
3376 signals
[SELECTION_CHANGED
], 0);
3381 if (details
->drag_icon
!= NULL
&&
3382 details
->single_click_mode
) {
3383 /* Determine click count */
3384 g_object_get (G_OBJECT (gtk_widget_get_settings (GTK_WIDGET (container
))),
3385 "gtk-double-click-time", &double_click_time
,
3387 current_time
= eel_get_system_time ();
3388 if (current_time
- last_click_time
< double_click_time
* 1000) {
3394 /* Stash time for next compare */
3395 last_click_time
= current_time
;
3397 /* If single-click mode, activate the selected icons, unless modifying
3398 * the selection or pressing for a very long time, or double clicking.
3402 if (click_count
== 0 &&
3403 event
->time
- details
->button_down_time
< MAX_CLICK_TIME
&&
3404 ! button_event_modifies_selection (event
)) {
3406 /* It's a tricky UI issue whether this should activate
3407 * just the clicked item (as if it were a link), or all
3408 * the selected items (as if you were issuing an "activate
3409 * selection" command). For now, we're trying the activate
3410 * entire selection version to see how it feels. Note that
3411 * NautilusList goes the other way because its "links" seem
3412 * much more link-like.
3414 if (event
->button
== MIDDLE_BUTTON
) {
3415 activate_selected_items_alternate (container
, NULL
);
3417 activate_selected_items (container
);
3424 clear_drag_state (NautilusIconContainer
*container
)
3426 container
->details
->drag_icon
= NULL
;
3427 container
->details
->drag_state
= DRAG_STATE_INITIAL
;
3431 start_stretching (NautilusIconContainer
*container
)
3433 NautilusIconContainerDetails
*details
;
3435 EelDPoint world_point
;
3436 GtkWidget
*toplevel
;
3437 GtkCornerType corner
;
3440 details
= container
->details
;
3441 icon
= details
->stretch_icon
;
3443 /* Check if we hit the stretch handles. */
3444 world_point
.x
= details
->drag_x
;
3445 world_point
.y
= details
->drag_y
;
3446 if (!nautilus_icon_canvas_item_hit_test_stretch_handles (icon
->item
, world_point
, &corner
)) {
3451 case GTK_CORNER_TOP_LEFT
:
3452 cursor
= gdk_cursor_new (GDK_TOP_LEFT_CORNER
);
3454 case GTK_CORNER_BOTTOM_LEFT
:
3455 cursor
= gdk_cursor_new (GDK_BOTTOM_LEFT_CORNER
);
3457 case GTK_CORNER_TOP_RIGHT
:
3458 cursor
= gdk_cursor_new (GDK_TOP_RIGHT_CORNER
);
3460 case GTK_CORNER_BOTTOM_RIGHT
:
3461 cursor
= gdk_cursor_new (GDK_BOTTOM_RIGHT_CORNER
);
3467 /* Set up the dragging. */
3468 details
->drag_state
= DRAG_STATE_STRETCH
;
3469 eel_canvas_w2c (EEL_CANVAS (container
),
3472 &details
->stretch_start
.pointer_x
,
3473 &details
->stretch_start
.pointer_y
);
3474 eel_canvas_w2c (EEL_CANVAS (container
),
3476 &details
->stretch_start
.icon_x
,
3477 &details
->stretch_start
.icon_y
);
3478 icon_get_size (container
, icon
,
3479 &details
->stretch_start
.icon_size
);
3481 eel_canvas_item_grab (EEL_CANVAS_ITEM (icon
->item
),
3482 (GDK_POINTER_MOTION_MASK
3483 | GDK_BUTTON_RELEASE_MASK
),
3487 gdk_cursor_unref (cursor
);
3489 /* Ensure the window itself is focused.. */
3490 toplevel
= gtk_widget_get_toplevel (GTK_WIDGET (container
));
3491 if (toplevel
!= NULL
&& GTK_WIDGET_REALIZED (toplevel
)) {
3492 eel_gdk_window_focus (toplevel
->window
, GDK_CURRENT_TIME
);
3499 update_stretch_at_idle (NautilusIconContainer
*container
)
3501 NautilusIconContainerDetails
*details
;
3503 double world_x
, world_y
;
3504 StretchState stretch_state
;
3506 details
= container
->details
;
3507 icon
= details
->stretch_icon
;
3510 container
->details
->stretch_idle_id
= 0;
3514 eel_canvas_w2c (EEL_CANVAS (container
),
3515 details
->world_x
, details
->world_y
,
3516 &stretch_state
.pointer_x
, &stretch_state
.pointer_y
);
3518 compute_stretch (&details
->stretch_start
,
3521 eel_canvas_c2w (EEL_CANVAS (container
),
3522 stretch_state
.icon_x
, stretch_state
.icon_y
,
3523 &world_x
, &world_y
);
3525 icon_set_position (icon
, world_x
, world_y
);
3526 icon_set_size (container
, icon
, stretch_state
.icon_size
, FALSE
, FALSE
);
3528 container
->details
->stretch_idle_id
= 0;
3534 continue_stretching (NautilusIconContainer
*container
,
3535 double world_x
, double world_y
)
3538 g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
));
3540 container
->details
->world_x
= world_x
;
3541 container
->details
->world_y
= world_y
;
3543 if (container
->details
->stretch_idle_id
== 0) {
3544 container
->details
->stretch_idle_id
= g_idle_add ((GtkFunction
) update_stretch_at_idle
, container
);
3549 keyboard_stretching (NautilusIconContainer
*container
,
3555 icon
= container
->details
->stretch_icon
;
3557 if (icon
== NULL
|| !icon
->is_selected
) {
3561 icon_get_size (container
, icon
, &size
);
3563 switch (event
->keyval
) {
3567 icon_set_size (container
, icon
, size
+ 5, FALSE
, FALSE
);
3570 case GDK_KP_Subtract
:
3571 icon_set_size (container
, icon
, size
- 5, FALSE
, FALSE
);
3575 nautilus_icon_container_move_icon (container
, icon
,
3586 ungrab_stretch_icon (NautilusIconContainer
*container
)
3588 eel_canvas_item_ungrab (EEL_CANVAS_ITEM (container
->details
->stretch_icon
->item
),
3593 end_stretching (NautilusIconContainer
*container
,
3594 double world_x
, double world_y
)
3596 NautilusIconPosition position
;
3599 continue_stretching (container
, world_x
, world_y
);
3600 ungrab_stretch_icon (container
);
3602 /* now that we're done stretching, update the icon's position */
3604 icon
= container
->details
->drag_icon
;
3605 if (nautilus_icon_container_is_layout_rtl (container
)) {
3606 position
.x
= icon
->saved_ltr_x
= get_mirror_x_position (container
, icon
, icon
->x
);
3608 position
.x
= icon
->x
;
3610 position
.y
= icon
->y
;
3611 position
.scale
= icon
->scale
;
3612 g_signal_emit (container
,
3613 signals
[ICON_POSITION_CHANGED
], 0,
3614 icon
->data
, &position
);
3616 clear_drag_state (container
);
3617 redo_layout (container
);
3621 undo_stretching (NautilusIconContainer
*container
)
3623 NautilusIcon
*stretched_icon
;
3625 stretched_icon
= container
->details
->stretch_icon
;
3627 if (stretched_icon
== NULL
) {
3631 if (container
->details
->drag_state
== DRAG_STATE_STRETCH
) {
3632 ungrab_stretch_icon (container
);
3633 clear_drag_state (container
);
3635 nautilus_icon_canvas_item_set_show_stretch_handles
3636 (stretched_icon
->item
, FALSE
);
3638 icon_set_position (stretched_icon
,
3639 container
->details
->stretch_initial_x
,
3640 container
->details
->stretch_initial_y
);
3641 icon_set_size (container
,
3643 container
->details
->stretch_initial_size
,
3647 container
->details
->stretch_icon
= NULL
;
3648 emit_stretch_ended (container
, stretched_icon
);
3649 redo_layout (container
);
3655 button_release_event (GtkWidget
*widget
,
3656 GdkEventButton
*event
)
3658 NautilusIconContainer
*container
;
3659 NautilusIconContainerDetails
*details
;
3660 double world_x
, world_y
;
3662 container
= NAUTILUS_ICON_CONTAINER (widget
);
3663 details
= container
->details
;
3665 if (event
->button
== RUBBERBAND_BUTTON
&& details
->rubberband_info
.active
) {
3666 stop_rubberbanding (container
, event
->time
);
3670 if (event
->button
== details
->drag_button
) {
3671 details
->drag_button
= 0;
3673 switch (details
->drag_state
) {
3674 case DRAG_STATE_MOVE_OR_COPY
:
3675 if (!details
->drag_started
) {
3676 nautilus_icon_container_did_not_drag (container
, event
);
3678 nautilus_icon_dnd_end_drag (container
);
3679 nautilus_debug_log (FALSE
, NAUTILUS_DEBUG_LOG_DOMAIN_USER
,
3680 "end drag from icon container");
3683 case DRAG_STATE_STRETCH
:
3684 eel_canvas_window_to_world
3685 (EEL_CANVAS (container
), event
->x
, event
->y
, &world_x
, &world_y
);
3686 end_stretching (container
, world_x
, world_y
);
3692 clear_drag_state (container
);
3696 return GTK_WIDGET_CLASS (parent_class
)->button_release_event (widget
, event
);
3700 motion_notify_event (GtkWidget
*widget
,
3701 GdkEventMotion
*event
)
3703 NautilusIconContainer
*container
;
3704 NautilusIconContainerDetails
*details
;
3705 double world_x
, world_y
;
3706 int canvas_x
, canvas_y
;
3707 GdkDragAction actions
;
3709 container
= NAUTILUS_ICON_CONTAINER (widget
);
3710 details
= container
->details
;
3712 if (details
->drag_button
!= 0) {
3713 switch (details
->drag_state
) {
3714 case DRAG_STATE_MOVE_OR_COPY
:
3715 if (details
->drag_started
) {
3719 eel_canvas_window_to_world
3720 (EEL_CANVAS (container
), event
->x
, event
->y
, &world_x
, &world_y
);
3722 if (gtk_drag_check_threshold (widget
,
3727 details
->drag_started
= TRUE
;
3728 details
->drag_state
= DRAG_STATE_MOVE_OR_COPY
;
3730 end_renaming_mode (container
, TRUE
);
3732 eel_canvas_w2c (EEL_CANVAS (container
),
3738 actions
= GDK_ACTION_COPY
3742 if (container
->details
->drag_allow_moves
) {
3743 actions
|= GDK_ACTION_MOVE
;
3746 nautilus_icon_dnd_begin_drag (container
,
3748 details
->drag_button
,
3752 nautilus_debug_log (FALSE
, NAUTILUS_DEBUG_LOG_DOMAIN_USER
,
3753 "begin drag from icon container");
3756 case DRAG_STATE_STRETCH
:
3757 eel_canvas_window_to_world
3758 (EEL_CANVAS (container
), event
->x
, event
->y
, &world_x
, &world_y
);
3759 continue_stretching (container
, world_x
, world_y
);
3766 return GTK_WIDGET_CLASS (parent_class
)->motion_notify_event (widget
, event
);
3770 nautilus_icon_container_search_position_func (NautilusIconContainer
*container
,
3771 GtkWidget
*search_dialog
)
3774 gint cont_x
, cont_y
;
3775 gint cont_width
, cont_height
;
3776 GdkWindow
*cont_window
;
3778 GtkRequisition requisition
;
3780 GdkRectangle monitor
;
3783 cont_window
= GTK_WIDGET (container
)->window
;
3784 screen
= gdk_drawable_get_screen (cont_window
);
3786 monitor_num
= gdk_screen_get_monitor_at_window (screen
, cont_window
);
3787 gdk_screen_get_monitor_geometry (screen
, monitor_num
, &monitor
);
3789 gtk_widget_realize (search_dialog
);
3791 gdk_window_get_origin (cont_window
, &cont_x
, &cont_y
);
3792 gdk_drawable_get_size (cont_window
, &cont_width
, &cont_height
);
3793 gtk_widget_size_request (search_dialog
, &requisition
);
3795 if (cont_x
+ cont_width
- requisition
.width
> gdk_screen_get_width (screen
)) {
3796 x
= gdk_screen_get_width (screen
) - requisition
.width
;
3797 } else if (cont_x
+ cont_width
- requisition
.width
< 0) {
3800 x
= cont_x
+ cont_width
- requisition
.width
;
3803 if (cont_y
+ cont_height
> gdk_screen_get_height (screen
)) {
3804 y
= gdk_screen_get_height (screen
) - requisition
.height
;
3805 } else if (cont_y
+ cont_height
< 0) { /* isn't really possible ... */
3808 y
= cont_y
+ cont_height
;
3811 gtk_window_move (GTK_WINDOW (search_dialog
), x
, y
);
3815 nautilus_icon_container_real_search_enable_popdown (gpointer data
)
3817 NautilusIconContainer
*container
= (NautilusIconContainer
*)data
;
3819 container
->details
->disable_popdown
= FALSE
;
3821 g_object_unref (container
);
3827 nautilus_icon_container_search_enable_popdown (GtkWidget
*widget
,
3830 NautilusIconContainer
*container
= (NautilusIconContainer
*) data
;
3832 g_object_ref (container
);
3833 g_timeout_add (200, nautilus_icon_container_real_search_enable_popdown
, data
);
3837 nautilus_icon_container_search_disable_popdown (GtkEntry
*entry
,
3841 NautilusIconContainer
*container
= (NautilusIconContainer
*) data
;
3843 container
->details
->disable_popdown
= TRUE
;
3844 g_signal_connect (menu
, "hide",
3845 G_CALLBACK (nautilus_icon_container_search_enable_popdown
),
3849 /* Cut and paste from gtkwindow.c */
3851 send_focus_change (GtkWidget
*widget
, gboolean in
)
3855 fevent
= gdk_event_new (GDK_FOCUS_CHANGE
);
3857 g_object_ref (widget
);
3860 GTK_WIDGET_SET_FLAGS (widget
, GTK_HAS_FOCUS
);
3862 GTK_WIDGET_UNSET_FLAGS (widget
, GTK_HAS_FOCUS
);
3865 fevent
->focus_change
.type
= GDK_FOCUS_CHANGE
;
3866 fevent
->focus_change
.window
= g_object_ref (widget
->window
);
3867 fevent
->focus_change
.in
= in
;
3869 gtk_widget_event (widget
, fevent
);
3871 g_object_notify (G_OBJECT (widget
), "has-focus");
3873 g_object_unref (widget
);
3874 gdk_event_free (fevent
);
3878 nautilus_icon_container_search_dialog_hide (GtkWidget
*search_dialog
,
3879 NautilusIconContainer
*container
)
3881 if (container
->details
->disable_popdown
) {
3885 if (container
->details
->search_entry_changed_id
) {
3886 g_signal_handler_disconnect (container
->details
->search_entry
,
3887 container
->details
->search_entry_changed_id
);
3888 container
->details
->search_entry_changed_id
= 0;
3890 if (container
->details
->typeselect_flush_timeout
) {
3891 g_source_remove (container
->details
->typeselect_flush_timeout
);
3892 container
->details
->typeselect_flush_timeout
= 0;
3895 /* send focus-in event */
3896 send_focus_change (GTK_WIDGET (container
->details
->search_entry
), FALSE
);
3897 gtk_widget_hide (search_dialog
);
3898 gtk_entry_set_text (GTK_ENTRY (container
->details
->search_entry
), "");
3902 nautilus_icon_container_search_entry_flush_timeout (NautilusIconContainer
*container
)
3904 nautilus_icon_container_search_dialog_hide (container
->details
->search_window
, container
);
3909 /* Because we're visible but offscreen, we just set a flag in the preedit
3913 nautilus_icon_container_search_preedit_changed (GtkIMContext
*im_context
,
3914 NautilusIconContainer
*container
)
3916 container
->details
->imcontext_changed
= 1;
3917 if (container
->details
->typeselect_flush_timeout
) {
3918 g_source_remove (container
->details
->typeselect_flush_timeout
);
3919 container
->details
->typeselect_flush_timeout
=
3920 g_timeout_add (NAUTILUS_ICON_CONTAINER_SEARCH_DIALOG_TIMEOUT
,
3921 (GSourceFunc
) nautilus_icon_container_search_entry_flush_timeout
,
3927 nautilus_icon_container_search_activate (GtkEntry
*entry
,
3928 NautilusIconContainer
*container
)
3930 nautilus_icon_container_search_dialog_hide (container
->details
->search_window
,
3933 activate_selected_items (container
);
3937 nautilus_icon_container_search_delete_event (GtkWidget
*widget
,
3939 NautilusIconContainer
*container
)
3941 g_return_val_if_fail (GTK_IS_WIDGET (widget
), FALSE
);
3943 nautilus_icon_container_search_dialog_hide (widget
, container
);
3949 nautilus_icon_container_search_button_press_event (GtkWidget
*widget
,
3950 GdkEventButton
*event
,
3951 NautilusIconContainer
*container
)
3953 g_return_val_if_fail (GTK_IS_WIDGET (widget
), FALSE
);
3955 nautilus_icon_container_search_dialog_hide (widget
, container
);
3957 if (event
->window
== GTK_LAYOUT (container
)->bin_window
) {
3958 button_press_event (GTK_WIDGET (container
), event
);
3965 nautilus_icon_container_search_iter (NautilusIconContainer
*container
,
3966 const char *key
, gint n
)
3972 char *normalized_key
, *case_normalized_key
;
3973 char *normalized_name
, *case_normalized_name
;
3975 g_return_val_if_fail (key
!= NULL
, FALSE
);
3976 g_return_val_if_fail (n
>= 1, FALSE
);
3978 normalized_key
= g_utf8_normalize (key
, -1, G_NORMALIZE_ALL
);
3979 if (!normalized_key
) {
3982 case_normalized_key
= g_utf8_casefold (normalized_key
, -1);
3983 g_free (normalized_key
);
3984 if (!case_normalized_key
) {
3990 for (p
= container
->details
->icons
; p
!= NULL
&& count
!= n
; p
= p
->next
) {
3992 name
= nautilus_icon_canvas_item_get_editable_text (icon
->item
);
3994 /* This can happen if a key event is handled really early while
3995 * loading the icon container, before the items have all been
4002 normalized_name
= g_utf8_normalize (name
, -1, G_NORMALIZE_ALL
);
4003 if (!normalized_name
) {
4006 case_normalized_name
= g_utf8_casefold (normalized_name
, -1);
4007 g_free (normalized_name
);
4008 if (!case_normalized_name
) {
4012 if (strncmp (case_normalized_key
, case_normalized_name
,
4013 strlen (case_normalized_key
)) == 0) {
4017 g_free (case_normalized_name
);
4020 g_free (case_normalized_key
);
4023 if (select_one_unselect_others (container
, icon
)) {
4024 g_signal_emit (container
, signals
[SELECTION_CHANGED
], 0);
4026 schedule_keyboard_icon_reveal (container
, icon
);
4035 nautilus_icon_container_search_move (GtkWidget
*window
,
4036 NautilusIconContainer
*container
,
4044 text
= gtk_entry_get_text (GTK_ENTRY (container
->details
->search_entry
));
4046 g_return_if_fail (text
!= NULL
);
4048 if (container
->details
->selected_iter
== 0) {
4052 if (up
&& container
->details
->selected_iter
== 1) {
4056 len
= strlen (text
);
4063 unselect_all (container
);
4065 ret
= nautilus_icon_container_search_iter (container
, text
,
4066 up
?((container
->details
->selected_iter
) - 1):((container
->details
->selected_iter
+ 1)));
4070 container
->details
->selected_iter
+= up
?(-1):(1);
4072 /* return to old iter */
4074 nautilus_icon_container_search_iter (container
, text
,
4075 container
->details
->selected_iter
);
4080 nautilus_icon_container_search_scroll_event (GtkWidget
*widget
,
4081 GdkEventScroll
*event
,
4082 NautilusIconContainer
*container
)
4084 gboolean retval
= FALSE
;
4086 if (event
->direction
== GDK_SCROLL_UP
) {
4087 nautilus_icon_container_search_move (widget
, container
, TRUE
);
4089 } else if (event
->direction
== GDK_SCROLL_DOWN
) {
4090 nautilus_icon_container_search_move (widget
, container
, FALSE
);
4094 /* renew the flush timeout */
4095 if (retval
&& container
->details
->typeselect_flush_timeout
) {
4096 g_source_remove (container
->details
->typeselect_flush_timeout
);
4097 container
->details
->typeselect_flush_timeout
=
4098 g_timeout_add (NAUTILUS_ICON_CONTAINER_SEARCH_DIALOG_TIMEOUT
,
4099 (GSourceFunc
) nautilus_icon_container_search_entry_flush_timeout
,
4107 nautilus_icon_container_search_key_press_event (GtkWidget
*widget
,
4109 NautilusIconContainer
*container
)
4111 gboolean retval
= FALSE
;
4113 g_return_val_if_fail (GTK_IS_WIDGET (widget
), FALSE
);
4114 g_return_val_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
), FALSE
);
4116 /* close window and cancel the search */
4117 if (event
->keyval
== GDK_Escape
|| event
->keyval
== GDK_Tab
) {
4118 nautilus_icon_container_search_dialog_hide (widget
, container
);
4122 /* select previous matching iter */
4123 if (event
->keyval
== GDK_Up
|| event
->keyval
== GDK_KP_Up
) {
4124 nautilus_icon_container_search_move (widget
, container
, TRUE
);
4128 if (((event
->state
& (GDK_CONTROL_MASK
| GDK_SHIFT_MASK
)) == (GDK_CONTROL_MASK
| GDK_SHIFT_MASK
))
4129 && (event
->keyval
== GDK_g
|| event
->keyval
== GDK_G
)) {
4130 nautilus_icon_container_search_move (widget
, container
, TRUE
);
4134 /* select next matching iter */
4135 if (event
->keyval
== GDK_Down
|| event
->keyval
== GDK_KP_Down
) {
4136 nautilus_icon_container_search_move (widget
, container
, FALSE
);
4140 if (((event
->state
& (GDK_CONTROL_MASK
| GDK_SHIFT_MASK
)) == GDK_CONTROL_MASK
)
4141 && (event
->keyval
== GDK_g
|| event
->keyval
== GDK_G
)) {
4142 nautilus_icon_container_search_move (widget
, container
, FALSE
);
4146 /* renew the flush timeout */
4147 if (retval
&& container
->details
->typeselect_flush_timeout
) {
4148 g_source_remove (container
->details
->typeselect_flush_timeout
);
4149 container
->details
->typeselect_flush_timeout
=
4150 g_timeout_add (NAUTILUS_ICON_CONTAINER_SEARCH_DIALOG_TIMEOUT
,
4151 (GSourceFunc
) nautilus_icon_container_search_entry_flush_timeout
,
4159 nautilus_icon_container_search_init (GtkWidget
*entry
,
4160 NautilusIconContainer
*container
)
4166 g_return_if_fail (GTK_IS_ENTRY (entry
));
4167 g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
));
4169 text
= gtk_entry_get_text (GTK_ENTRY (entry
));
4170 len
= strlen (text
);
4173 unselect_all (container
);
4174 if (container
->details
->typeselect_flush_timeout
)
4176 g_source_remove (container
->details
->typeselect_flush_timeout
);
4177 container
->details
->typeselect_flush_timeout
=
4178 g_timeout_add (NAUTILUS_ICON_CONTAINER_SEARCH_DIALOG_TIMEOUT
,
4179 (GSourceFunc
) nautilus_icon_container_search_entry_flush_timeout
,
4187 ret
= nautilus_icon_container_search_iter (container
, text
, 1);
4190 container
->details
->selected_iter
= 1;
4195 nautilus_icon_container_ensure_interactive_directory (NautilusIconContainer
*container
)
4197 GtkWidget
*frame
, *vbox
, *toplevel
;
4199 toplevel
= gtk_widget_get_toplevel (GTK_WIDGET (container
));
4201 if (container
->details
->search_window
!= NULL
) {
4205 container
->details
->search_window
= gtk_window_new (GTK_WINDOW_POPUP
);
4207 gtk_window_set_modal (GTK_WINDOW (container
->details
->search_window
), TRUE
);
4208 g_signal_connect (container
->details
->search_window
, "delete_event",
4209 G_CALLBACK (nautilus_icon_container_search_delete_event
),
4211 g_signal_connect (container
->details
->search_window
, "key_press_event",
4212 G_CALLBACK (nautilus_icon_container_search_key_press_event
),
4214 g_signal_connect (container
->details
->search_window
, "button_press_event",
4215 G_CALLBACK (nautilus_icon_container_search_button_press_event
),
4217 g_signal_connect (container
->details
->search_window
, "scroll_event",
4218 G_CALLBACK (nautilus_icon_container_search_scroll_event
),
4221 frame
= gtk_frame_new (NULL
);
4222 gtk_frame_set_shadow_type (GTK_FRAME (frame
), GTK_SHADOW_ETCHED_IN
);
4223 gtk_widget_show (frame
);
4224 gtk_container_add (GTK_CONTAINER (container
->details
->search_window
), frame
);
4226 vbox
= gtk_vbox_new (FALSE
, 0);
4227 gtk_widget_show (vbox
);
4228 gtk_container_add (GTK_CONTAINER (frame
), vbox
);
4229 gtk_container_set_border_width (GTK_CONTAINER (vbox
), 3);
4232 container
->details
->search_entry
= gtk_entry_new ();
4233 gtk_widget_show (container
->details
->search_entry
);
4234 g_signal_connect (container
->details
->search_entry
, "populate_popup",
4235 G_CALLBACK (nautilus_icon_container_search_disable_popdown
),
4237 g_signal_connect (container
->details
->search_entry
, "activate",
4238 G_CALLBACK (nautilus_icon_container_search_activate
),
4240 g_signal_connect (GTK_ENTRY (container
->details
->search_entry
)->im_context
,
4242 G_CALLBACK (nautilus_icon_container_search_preedit_changed
),
4244 gtk_container_add (GTK_CONTAINER (vbox
), container
->details
->search_entry
);
4246 gtk_widget_realize (container
->details
->search_entry
);
4249 /* Pops up the interactive search entry. If keybinding is TRUE then the user
4250 * started this by typing the start_interactive_search keybinding. Otherwise, it came from
4253 nautilus_icon_container_real_start_interactive_search (NautilusIconContainer
*container
,
4254 gboolean keybinding
)
4256 /* We only start interactive search if we have focus. If one of our
4257 * children have focus, we don't want to start the search.
4259 GtkWidgetClass
*entry_parent_class
;
4261 if (container
->details
->search_window
!= NULL
&&
4262 GTK_WIDGET_VISIBLE (container
->details
->search_window
)) {
4266 if (!GTK_WIDGET_HAS_FOCUS (container
)) {
4270 nautilus_icon_container_ensure_interactive_directory (container
);
4273 gtk_entry_set_text (GTK_ENTRY (container
->details
->search_entry
), "");
4277 nautilus_icon_container_search_position_func (container
, container
->details
->search_window
);
4278 gtk_widget_show (container
->details
->search_window
);
4279 if (container
->details
->search_entry_changed_id
== 0) {
4280 container
->details
->search_entry_changed_id
=
4281 g_signal_connect (container
->details
->search_entry
, "changed",
4282 G_CALLBACK (nautilus_icon_container_search_init
),
4286 container
->details
->typeselect_flush_timeout
=
4287 g_timeout_add (NAUTILUS_ICON_CONTAINER_SEARCH_DIALOG_TIMEOUT
,
4288 (GSourceFunc
) nautilus_icon_container_search_entry_flush_timeout
,
4291 /* Grab focus will select all the text. We don't want that to happen, so we
4292 * call the parent instance and bypass the selection change. This is probably
4293 * really non-kosher. */
4294 entry_parent_class
= g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (container
->details
->search_entry
));
4295 (entry_parent_class
->grab_focus
) (container
->details
->search_entry
);
4297 /* send focus-in event */
4298 send_focus_change (container
->details
->search_entry
, TRUE
);
4300 /* search first matching iter */
4301 nautilus_icon_container_search_init (container
->details
->search_entry
, container
);
4307 nautilus_icon_container_start_interactive_search (NautilusIconContainer
*container
)
4309 return nautilus_icon_container_real_start_interactive_search (container
, TRUE
);
4313 handle_popups (NautilusIconContainer
*container
,
4317 GdkEventButton button_event
= { 0 };
4319 g_signal_emit_by_name (container
, signal
, &button_event
);
4325 key_press_event (GtkWidget
*widget
,
4328 NautilusIconContainer
*container
;
4331 container
= NAUTILUS_ICON_CONTAINER (widget
);
4334 if (is_renaming (container
) || is_renaming_pending (container
)) {
4335 switch (event
->keyval
) {
4338 end_renaming_mode (container
, TRUE
);
4342 end_renaming_mode (container
, FALSE
);
4349 switch (event
->keyval
) {
4352 keyboard_home (container
, event
);
4357 keyboard_end (container
, event
);
4362 keyboard_left (container
, event
);
4367 /* Don't eat Alt-Up, as that is used for alt-shift-Up */
4368 if ((event
->state
& GDK_MOD1_MASK
) == 0) {
4369 keyboard_up (container
, event
);
4375 keyboard_right (container
, event
);
4380 /* Don't eat Alt-Down, as that is used for Open */
4381 if ((event
->state
& GDK_MOD1_MASK
) == 0) {
4382 keyboard_down (container
, event
);
4387 keyboard_space (container
, event
);
4390 #ifndef TAB_NAVIGATION_DISABLED
4392 case GDK_ISO_Left_Tab
:
4393 select_previous_or_next_icon (container
,
4394 (event
->state
& GDK_SHIFT_MASK
) == 0, event
);
4400 if ((event
->state
& GDK_SHIFT_MASK
) != 0) {
4401 activate_selected_items_alternate (container
, NULL
);
4403 activate_selected_items (container
);
4409 handled
= undo_stretching (container
);
4415 case GDK_KP_Subtract
:
4418 if (event
->state
& GDK_CONTROL_MASK
) {
4419 handled
= keyboard_stretching (container
, event
);
4423 /* handle Ctrl+F10 because we want to display the
4424 * background popup even if something is selected.
4425 * The other cases are handled by popup_menu().
4427 if (event
->state
& GDK_CONTROL_MASK
) {
4428 handled
= handle_popups (container
, event
,
4429 "context_click_background");
4438 handled
= GTK_WIDGET_CLASS (parent_class
)->key_press_event (widget
, event
);
4441 /* We pass the event to the search_entry. If its text changes, then we
4442 * start the typeahead find capabilities.
4443 * Copied from NautilusIconContainer */
4445 event
->keyval
!= GDK_slash
/* don't steal slash key event, used for "go to" */ &&
4446 event
->keyval
!= GDK_BackSpace
&&
4447 event
->keyval
!= GDK_Delete
) {
4448 GdkEvent
*new_event
;
4450 const char *new_text
;
4453 gboolean text_modified
;
4454 gulong popup_menu_id
;
4456 nautilus_icon_container_ensure_interactive_directory (container
);
4458 /* Make a copy of the current text */
4459 old_text
= g_strdup (gtk_entry_get_text (GTK_ENTRY (container
->details
->search_entry
)));
4460 new_event
= gdk_event_copy ((GdkEvent
*) event
);
4461 ((GdkEventKey
*) new_event
)->window
= container
->details
->search_entry
->window
;
4462 gtk_widget_realize (container
->details
->search_window
);
4464 popup_menu_id
= g_signal_connect (container
->details
->search_entry
,
4465 "popup_menu", G_CALLBACK (gtk_true
), NULL
);
4467 /* Move the entry off screen */
4468 screen
= gtk_widget_get_screen (GTK_WIDGET (container
));
4469 gtk_window_move (GTK_WINDOW (container
->details
->search_window
),
4470 gdk_screen_get_width (screen
) + 1,
4471 gdk_screen_get_height (screen
) + 1);
4472 gtk_widget_show (container
->details
->search_window
);
4474 /* Send the event to the window. If the preedit_changed signal is emitted
4475 * during this event, we will set priv->imcontext_changed */
4476 container
->details
->imcontext_changed
= FALSE
;
4477 retval
= gtk_widget_event (container
->details
->search_entry
, new_event
);
4478 gtk_widget_hide (container
->details
->search_window
);
4480 g_signal_handler_disconnect (container
->details
->search_entry
,
4483 /* We check to make sure that the entry tried to handle the text, and that
4484 * the text has changed. */
4485 new_text
= gtk_entry_get_text (GTK_ENTRY (container
->details
->search_entry
));
4486 text_modified
= strcmp (old_text
, new_text
) != 0;
4488 if (container
->details
->imcontext_changed
|| /* we're in a preedit */
4489 (retval
&& text_modified
)) { /* ...or the text was modified */
4490 if (nautilus_icon_container_real_start_interactive_search (container
, FALSE
)) {
4491 gtk_widget_grab_focus (GTK_WIDGET (container
));
4494 gtk_entry_set_text (GTK_ENTRY (container
->details
->search_entry
), "");
4504 popup_menu (GtkWidget
*widget
)
4506 NautilusIconContainer
*container
;
4508 container
= NAUTILUS_ICON_CONTAINER (widget
);
4510 if (has_selection (container
)) {
4511 handle_popups (container
, NULL
,
4512 "context_click_selection");
4514 handle_popups (container
, NULL
,
4515 "context_click_background");
4522 draw_canvas_background (EelCanvas
*canvas
,
4523 int x
, int y
, int width
, int height
)
4525 /* Don't chain up to the parent to avoid clearing and redrawing */
4530 expose_event (GtkWidget
*widget
,
4531 GdkEventExpose
*event
)
4533 /* g_warning ("Expose Icon Container %p '%d,%d: %d,%d'",
4535 event->area.x, event->area.y,
4536 event->area.width, event->area.height); */
4538 return GTK_WIDGET_CLASS (parent_class
)->expose_event (widget
, event
);
4542 get_accessible (GtkWidget
*widget
)
4544 AtkObject
*accessible
;
4546 if ((accessible
= eel_accessibility_get_atk_object (widget
))) {
4550 accessible
= g_object_new
4551 (nautilus_icon_container_accessible_get_type (), NULL
);
4553 return eel_accessibility_set_atk_object_return (widget
, accessible
);
4557 grab_notify_cb (GtkWidget
*widget
,
4558 gboolean was_grabbed
)
4560 NautilusIconContainer
*container
;
4562 container
= NAUTILUS_ICON_CONTAINER (widget
);
4564 if (container
->details
->rubberband_info
.active
&&
4566 /* we got a (un)grab-notify during rubberband.
4567 * This happens when a new modal dialog shows
4568 * up (e.g. authentication or an error). Stop
4569 * the rubberbanding so that we can handle the
4571 stop_rubberbanding (container
,
4577 /* Initialization. */
4580 nautilus_icon_container_class_init (NautilusIconContainerClass
*class)
4582 GtkWidgetClass
*widget_class
;
4583 EelCanvasClass
*canvas_class
;
4584 GtkBindingSet
*binding_set
;
4586 G_OBJECT_CLASS (class)->finalize
= finalize
;
4587 GTK_OBJECT_CLASS (class)->destroy
= destroy
;
4591 signals
[SELECTION_CHANGED
]
4592 = g_signal_new ("selection_changed",
4593 G_TYPE_FROM_CLASS (class),
4595 G_STRUCT_OFFSET (NautilusIconContainerClass
,
4598 g_cclosure_marshal_VOID__VOID
,
4600 signals
[BUTTON_PRESS
]
4601 = g_signal_new ("button_press",
4602 G_TYPE_FROM_CLASS (class),
4604 G_STRUCT_OFFSET (NautilusIconContainerClass
,
4607 nautilus_marshal_BOOLEAN__POINTER
,
4611 = g_signal_new ("activate",
4612 G_TYPE_FROM_CLASS (class),
4614 G_STRUCT_OFFSET (NautilusIconContainerClass
,
4617 g_cclosure_marshal_VOID__POINTER
,
4620 signals
[ACTIVATE_ALTERNATE
]
4621 = g_signal_new ("activate_alternate",
4622 G_TYPE_FROM_CLASS (class),
4624 G_STRUCT_OFFSET (NautilusIconContainerClass
,
4625 activate_alternate
),
4627 g_cclosure_marshal_VOID__POINTER
,
4630 signals
[CONTEXT_CLICK_SELECTION
]
4631 = g_signal_new ("context_click_selection",
4632 G_TYPE_FROM_CLASS (class),
4634 G_STRUCT_OFFSET (NautilusIconContainerClass
,
4635 context_click_selection
),
4637 g_cclosure_marshal_VOID__POINTER
,
4640 signals
[CONTEXT_CLICK_BACKGROUND
]
4641 = g_signal_new ("context_click_background",
4642 G_TYPE_FROM_CLASS (class),
4644 G_STRUCT_OFFSET (NautilusIconContainerClass
,
4645 context_click_background
),
4647 g_cclosure_marshal_VOID__POINTER
,
4650 signals
[MIDDLE_CLICK
]
4651 = g_signal_new ("middle_click",
4652 G_TYPE_FROM_CLASS (class),
4654 G_STRUCT_OFFSET (NautilusIconContainerClass
,
4657 g_cclosure_marshal_VOID__POINTER
,
4660 signals
[ICON_POSITION_CHANGED
]
4661 = g_signal_new ("icon_position_changed",
4662 G_TYPE_FROM_CLASS (class),
4664 G_STRUCT_OFFSET (NautilusIconContainerClass
,
4665 icon_position_changed
),
4667 nautilus_marshal_VOID__POINTER_POINTER
,
4671 signals
[ICON_TEXT_CHANGED
]
4672 = g_signal_new ("icon_text_changed",
4673 G_TYPE_FROM_CLASS (class),
4675 G_STRUCT_OFFSET (NautilusIconContainerClass
,
4678 nautilus_marshal_VOID__POINTER_STRING
,
4682 signals
[ICON_STRETCH_STARTED
]
4683 = g_signal_new ("icon_stretch_started",
4684 G_TYPE_FROM_CLASS (class),
4686 G_STRUCT_OFFSET (NautilusIconContainerClass
,
4687 icon_stretch_started
),
4689 g_cclosure_marshal_VOID__POINTER
,
4692 signals
[ICON_STRETCH_ENDED
]
4693 = g_signal_new ("icon_stretch_ended",
4694 G_TYPE_FROM_CLASS (class),
4696 G_STRUCT_OFFSET (NautilusIconContainerClass
,
4697 icon_stretch_ended
),
4699 g_cclosure_marshal_VOID__POINTER
,
4702 signals
[RENAMING_ICON
]
4703 = g_signal_new ("renaming_icon",
4704 G_TYPE_FROM_CLASS (class),
4706 G_STRUCT_OFFSET (NautilusIconContainerClass
,
4709 g_cclosure_marshal_VOID__POINTER
,
4712 signals
[GET_ICON_URI
]
4713 = g_signal_new ("get_icon_uri",
4714 G_TYPE_FROM_CLASS (class),
4716 G_STRUCT_OFFSET (NautilusIconContainerClass
,
4719 eel_marshal_STRING__POINTER
,
4722 signals
[GET_ICON_DROP_TARGET_URI
]
4723 = g_signal_new ("get_icon_drop_target_uri",
4724 G_TYPE_FROM_CLASS (class),
4726 G_STRUCT_OFFSET (NautilusIconContainerClass
,
4727 get_icon_drop_target_uri
),
4729 eel_marshal_STRING__POINTER
,
4732 signals
[MOVE_COPY_ITEMS
]
4733 = g_signal_new ("move_copy_items",
4734 G_TYPE_FROM_CLASS (class),
4736 G_STRUCT_OFFSET (NautilusIconContainerClass
,
4739 nautilus_marshal_VOID__POINTER_POINTER_POINTER_ENUM_INT_INT
,
4744 GDK_TYPE_DRAG_ACTION
,
4747 signals
[HANDLE_NETSCAPE_URL
]
4748 = g_signal_new ("handle_netscape_url",
4749 G_TYPE_FROM_CLASS (class),
4751 G_STRUCT_OFFSET (NautilusIconContainerClass
,
4752 handle_netscape_url
),
4754 nautilus_marshal_VOID__STRING_STRING_ENUM_INT_INT
,
4758 GDK_TYPE_DRAG_ACTION
,
4761 signals
[HANDLE_URI_LIST
]
4762 = g_signal_new ("handle_uri_list",
4763 G_TYPE_FROM_CLASS (class),
4765 G_STRUCT_OFFSET (NautilusIconContainerClass
,
4768 nautilus_marshal_VOID__STRING_STRING_ENUM_INT_INT
,
4772 GDK_TYPE_DRAG_ACTION
,
4775 signals
[HANDLE_TEXT
]
4776 = g_signal_new ("handle_text",
4777 G_TYPE_FROM_CLASS (class),
4779 G_STRUCT_OFFSET (NautilusIconContainerClass
,
4782 nautilus_marshal_VOID__STRING_STRING_ENUM_INT_INT
,
4786 GDK_TYPE_DRAG_ACTION
,
4789 signals
[GET_CONTAINER_URI
]
4790 = g_signal_new ("get_container_uri",
4791 G_TYPE_FROM_CLASS (class),
4793 G_STRUCT_OFFSET (NautilusIconContainerClass
,
4796 eel_marshal_STRING__VOID
,
4798 signals
[CAN_ACCEPT_ITEM
]
4799 = g_signal_new ("can_accept_item",
4800 G_TYPE_FROM_CLASS (class),
4802 G_STRUCT_OFFSET (NautilusIconContainerClass
,
4805 eel_marshal_INT__POINTER_STRING
,
4809 signals
[GET_STORED_ICON_POSITION
]
4810 = g_signal_new ("get_stored_icon_position",
4811 G_TYPE_FROM_CLASS (class),
4813 G_STRUCT_OFFSET (NautilusIconContainerClass
,
4814 get_stored_icon_position
),
4816 eel_marshal_BOOLEAN__POINTER_POINTER
,
4820 signals
[LAYOUT_CHANGED
]
4821 = g_signal_new ("layout_changed",
4822 G_TYPE_FROM_CLASS (class),
4824 G_STRUCT_OFFSET (NautilusIconContainerClass
,
4827 g_cclosure_marshal_VOID__VOID
,
4830 = g_signal_new ("preview",
4831 G_TYPE_FROM_CLASS (class),
4833 G_STRUCT_OFFSET (NautilusIconContainerClass
,
4836 nautilus_marshal_INT__POINTER_BOOLEAN
,
4840 signals
[BAND_SELECT_STARTED
]
4841 = g_signal_new ("band_select_started",
4842 G_TYPE_FROM_CLASS (class),
4844 G_STRUCT_OFFSET (NautilusIconContainerClass
,
4845 band_select_started
),
4847 g_cclosure_marshal_VOID__VOID
,
4849 signals
[BAND_SELECT_ENDED
]
4850 = g_signal_new ("band_select_ended",
4851 G_TYPE_FROM_CLASS (class),
4853 G_STRUCT_OFFSET (NautilusIconContainerClass
,
4856 g_cclosure_marshal_VOID__VOID
,
4859 = g_signal_new ("icon_added",
4860 G_TYPE_FROM_CLASS (class),
4862 G_STRUCT_OFFSET (NautilusIconContainerClass
,
4865 g_cclosure_marshal_VOID__POINTER
,
4866 G_TYPE_NONE
, 1, G_TYPE_POINTER
);
4867 signals
[ICON_REMOVED
]
4868 = g_signal_new ("icon_removed",
4869 G_TYPE_FROM_CLASS (class),
4871 G_STRUCT_OFFSET (NautilusIconContainerClass
,
4874 g_cclosure_marshal_VOID__POINTER
,
4875 G_TYPE_NONE
, 1, G_TYPE_POINTER
);
4878 = g_signal_new ("cleared",
4879 G_TYPE_FROM_CLASS (class),
4881 G_STRUCT_OFFSET (NautilusIconContainerClass
,
4884 g_cclosure_marshal_VOID__VOID
,
4887 signals
[START_INTERACTIVE_SEARCH
]
4888 = g_signal_new ("start_interactive_search",
4889 G_TYPE_FROM_CLASS (class),
4890 G_SIGNAL_RUN_LAST
| G_SIGNAL_ACTION
,
4891 G_STRUCT_OFFSET (NautilusIconContainerClass
,
4892 start_interactive_search
),
4894 nautilus_marshal_BOOLEAN__VOID
,
4897 /* GtkWidget class. */
4899 widget_class
= GTK_WIDGET_CLASS (class);
4900 widget_class
->size_request
= size_request
;
4901 widget_class
->size_allocate
= size_allocate
;
4902 widget_class
->realize
= realize
;
4903 widget_class
->unrealize
= unrealize
;
4904 widget_class
->button_press_event
= button_press_event
;
4905 widget_class
->button_release_event
= button_release_event
;
4906 widget_class
->motion_notify_event
= motion_notify_event
;
4907 widget_class
->key_press_event
= key_press_event
;
4908 widget_class
->popup_menu
= popup_menu
;
4909 widget_class
->get_accessible
= get_accessible
;
4910 widget_class
->style_set
= style_set
;
4911 widget_class
->expose_event
= expose_event
;
4912 widget_class
->grab_notify
= grab_notify_cb
;
4914 canvas_class
= EEL_CANVAS_CLASS (class);
4915 canvas_class
->draw_background
= draw_canvas_background
;
4917 class->start_interactive_search
= nautilus_icon_container_start_interactive_search
;
4919 gtk_widget_class_install_style_property (widget_class
,
4920 g_param_spec_boolean ("frame_text",
4922 "Draw a frame around unselected text",
4926 gtk_widget_class_install_style_property (widget_class
,
4927 g_param_spec_boxed ("selection_box_color",
4928 "Selection Box Color",
4929 "Color of the selection box",
4932 gtk_widget_class_install_style_property (widget_class
,
4933 g_param_spec_uchar ("selection_box_alpha",
4934 "Selection Box Alpha",
4935 "Opacity of the selection box",
4937 DEFAULT_SELECTION_BOX_ALPHA
,
4940 gtk_widget_class_install_style_property (widget_class
,
4941 g_param_spec_uchar ("highlight_alpha",
4943 "Opacity of the highlight for selected icons",
4945 DEFAULT_HIGHLIGHT_ALPHA
,
4947 gtk_widget_class_install_style_property (widget_class
,
4948 g_param_spec_uchar ("normal_alpha",
4950 "Opacity of the normal icons if frame_text is set",
4952 DEFAULT_NORMAL_ALPHA
,
4954 gtk_widget_class_install_style_property (widget_class
,
4955 g_param_spec_uchar ("prelight_alpha",
4957 "Opacity of the prelight icons if frame_text is set",
4959 DEFAULT_PRELIGHT_ALPHA
,
4961 gtk_widget_class_install_style_property (widget_class
,
4962 g_param_spec_boxed ("light_info_color",
4964 "Color used for information text against a dark background",
4967 gtk_widget_class_install_style_property (widget_class
,
4968 g_param_spec_boxed ("dark_info_color",
4970 "Color used for information text against a light background",
4974 gtk_widget_class_install_style_property (widget_class
,
4975 g_param_spec_uint ("normal_icon_render_mode",
4976 "Normal Icon Render Mode",
4977 "Mode of normal icons being rendered (0=normal, 1=spotlight, 2=colorize, 3=colorize-monochromely)",
4979 DEFAULT_NORMAL_ICON_RENDER_MODE
,
4981 gtk_widget_class_install_style_property (widget_class
,
4982 g_param_spec_uint ("prelight_icon_render_mode",
4983 "Prelight Icon Render Mode",
4984 "Mode of prelight icons being rendered (0=normal, 1=spotlight, 2=colorize, 3=colorize-monochromely)",
4986 DEFAULT_PRELIGHT_ICON_RENDER_MODE
,
4988 gtk_widget_class_install_style_property (widget_class
,
4989 g_param_spec_boxed ("normal_icon_color",
4990 "Icon Normal Color",
4991 "Color used for colorizing icons in normal state (default base[NORMAL])",
4994 gtk_widget_class_install_style_property (widget_class
,
4995 g_param_spec_boxed ("prelight_icon_color",
4996 "Icon Prelight Color",
4997 "Color used for colorizing prelighted icons (default base[PRELIGHT])",
5000 gtk_widget_class_install_style_property (widget_class
,
5001 g_param_spec_uint ("normal_icon_saturation",
5002 "Normal Icon Saturation",
5003 "Saturation of icons in normal state",
5005 DEFAULT_NORMAL_ICON_SATURATION
,
5007 gtk_widget_class_install_style_property (widget_class
,
5008 g_param_spec_uint ("prelight_icon_saturation",
5009 "Prelight Icon Saturation",
5010 "Saturation of icons in prelight state",
5012 DEFAULT_PRELIGHT_ICON_SATURATION
,
5014 gtk_widget_class_install_style_property (widget_class
,
5015 g_param_spec_uint ("normal_icon_brightness",
5016 "Normal Icon Brightness",
5017 "Brightness of icons in normal state",
5019 DEFAULT_NORMAL_ICON_BRIGHTNESS
,
5021 gtk_widget_class_install_style_property (widget_class
,
5022 g_param_spec_uint ("prelight_icon_brightness",
5023 "Prelight Icon Brightness",
5024 "Brightness of icons in prelight state",
5026 DEFAULT_PRELIGHT_ICON_BRIGHTNESS
,
5028 gtk_widget_class_install_style_property (widget_class
,
5029 g_param_spec_uint ("normal_icon_lighten",
5030 "Normal Icon Lighten",
5031 "Lighten icons in normal state",
5033 DEFAULT_NORMAL_ICON_LIGHTEN
,
5035 gtk_widget_class_install_style_property (widget_class
,
5036 g_param_spec_uint ("prelight_icon_lighten",
5037 "Prelight Icon Lighten",
5038 "Lighten icons in prelight state",
5040 DEFAULT_PRELIGHT_ICON_LIGHTEN
,
5042 gtk_widget_class_install_style_property (widget_class
,
5043 g_param_spec_boolean ("activate_prelight_icon_label",
5044 "Activate Prelight Icon Label",
5045 "Whether icon labels should make use of its prelight color in prelight state",
5050 binding_set
= gtk_binding_set_by_class (class);
5052 gtk_binding_entry_add_signal (binding_set
, GDK_f
, GDK_CONTROL_MASK
, "start_interactive_search", 0);
5053 gtk_binding_entry_add_signal (binding_set
, GDK_F
, GDK_CONTROL_MASK
, "start_interactive_search", 0);
5057 update_selected (NautilusIconContainer
*container
)
5062 for (node
= container
->details
->icons
; node
!= NULL
; node
= node
->next
) {
5064 if (icon
->is_selected
) {
5065 eel_canvas_item_request_update (EEL_CANVAS_ITEM (icon
->item
));
5071 handle_focus_in_event (GtkWidget
*widget
, GdkEventFocus
*event
, gpointer user_data
)
5073 update_selected (NAUTILUS_ICON_CONTAINER (widget
));
5079 handle_focus_out_event (GtkWidget
*widget
, GdkEventFocus
*event
, gpointer user_data
)
5081 /* End renaming and commit change. */
5082 end_renaming_mode (NAUTILUS_ICON_CONTAINER (widget
), TRUE
);
5083 update_selected (NAUTILUS_ICON_CONTAINER (widget
));
5089 nautilus_icon_container_instance_init (NautilusIconContainer
*container
)
5091 NautilusIconContainerDetails
*details
;
5092 EelBackground
*background
;
5094 details
= g_new0 (NautilusIconContainerDetails
, 1);
5096 details
->icon_set
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
5098 details
->zoom_level
= NAUTILUS_ZOOM_LEVEL_STANDARD
;
5100 details
->font_size_table
[NAUTILUS_ZOOM_LEVEL_SMALLEST
] = -2 * PANGO_SCALE
;
5101 details
->font_size_table
[NAUTILUS_ZOOM_LEVEL_SMALLER
] = -2 * PANGO_SCALE
;
5102 details
->font_size_table
[NAUTILUS_ZOOM_LEVEL_SMALL
] = -0 * PANGO_SCALE
;
5103 details
->font_size_table
[NAUTILUS_ZOOM_LEVEL_STANDARD
] = 0 * PANGO_SCALE
;
5104 details
->font_size_table
[NAUTILUS_ZOOM_LEVEL_LARGE
] = 0 * PANGO_SCALE
;
5105 details
->font_size_table
[NAUTILUS_ZOOM_LEVEL_LARGER
] = 0 * PANGO_SCALE
;
5106 details
->font_size_table
[NAUTILUS_ZOOM_LEVEL_LARGEST
] = 0 * PANGO_SCALE
;
5108 container
->details
= details
;
5110 /* when the background changes, we must set up the label text color */
5111 background
= eel_get_widget_background (GTK_WIDGET (container
));
5113 g_signal_connect_object (background
, "appearance_changed",
5114 G_CALLBACK (update_label_color
), container
, 0);
5116 g_signal_connect (container
, "focus-in-event",
5117 G_CALLBACK (handle_focus_in_event
), NULL
);
5118 g_signal_connect (container
, "focus-out-event",
5119 G_CALLBACK (handle_focus_out_event
), NULL
);
5121 eel_background_set_use_base (background
, TRUE
);
5123 /* read in theme-dependent data */
5124 nautilus_icon_container_theme_changed (container
);
5125 eel_preferences_add_callback (NAUTILUS_PREFERENCES_THEME
,
5126 nautilus_icon_container_theme_changed
,
5131 NautilusIconContainer
*container
;
5132 GdkEventButton
*event
;
5133 } ContextMenuParameters
;
5135 /* NautilusIcon event handling. */
5137 /* Conceptually, pressing button 1 together with CTRL or SHIFT toggles
5138 * selection of a single icon without affecting the other icons;
5139 * without CTRL or SHIFT, it selects a single icon and un-selects all
5140 * the other icons. But in this latter case, the de-selection should
5141 * only happen when the button is released if the icon is already
5142 * selected, because the user might select multiple icons and drag all
5143 * of them by doing a simple click-drag.
5147 handle_icon_button_press (NautilusIconContainer
*container
,
5149 GdkEventButton
*event
)
5151 NautilusIconContainerDetails
*details
;
5153 details
= container
->details
;
5155 if (event
->type
== GDK_3BUTTON_PRESS
) {
5159 if (details
->single_click_mode
&&
5160 event
->type
== GDK_2BUTTON_PRESS
) {
5161 /* Don't care about double clicks in single click mode */
5165 if (event
->button
!= DRAG_BUTTON
5166 && event
->button
!= CONTEXTUAL_MENU_BUTTON
5167 && event
->button
!= DRAG_MENU_BUTTON
) {
5171 if ((event
->button
== DRAG_BUTTON
|| event
->button
== MIDDLE_BUTTON
) &&
5172 event
->type
== GDK_BUTTON_PRESS
) {
5173 /* The next double click has to be on this icon */
5174 details
->double_click_icon
[1] = details
->double_click_icon
[0];
5175 details
->double_click_icon
[0] = icon
;
5177 if (event
->type
== GDK_2BUTTON_PRESS
&&
5178 (event
->button
== DRAG_BUTTON
|| event
->button
== MIDDLE_BUTTON
)) {
5179 /* Double clicking does not trigger a D&D action. */
5180 details
->drag_button
= 0;
5181 details
->drag_icon
= NULL
;
5183 if (icon
== details
->double_click_icon
[1]) {
5184 if (!button_event_modifies_selection (event
)) {
5185 if (event
->button
== MIDDLE_BUTTON
) {
5186 activate_selected_items_alternate (container
, NULL
);
5188 activate_selected_items (container
);
5190 } else if (event
->button
== DRAG_BUTTON
&&
5191 (event
->state
& GDK_SHIFT_MASK
) != 0) {
5192 activate_selected_items_alternate (container
, icon
);
5197 if (event
->button
== DRAG_BUTTON
5198 || event
->button
== DRAG_MENU_BUTTON
) {
5199 details
->drag_button
= event
->button
;
5200 details
->drag_icon
= icon
;
5201 details
->drag_x
= event
->x
;
5202 details
->drag_y
= event
->y
;
5203 details
->drag_state
= DRAG_STATE_MOVE_OR_COPY
;
5204 details
->drag_started
= FALSE
;
5206 /* Check to see if this is a click on the stretch handles.
5207 * If so, it won't modify the selection.
5209 if (icon
== container
->details
->stretch_icon
) {
5210 if (start_stretching (container
)) {
5216 /* Modify the selection as appropriate. Selection is modified
5217 * the same way for contextual menu as it would be without.
5219 details
->icon_selected_on_button_down
= icon
->is_selected
;
5221 if ((event
->button
== DRAG_BUTTON
|| event
->button
== MIDDLE_BUTTON
) &&
5222 (event
->state
& GDK_CONTROL_MASK
) == 0 &&
5223 (event
->state
& GDK_SHIFT_MASK
) != 0) {
5224 NautilusIcon
*start_icon
;
5226 start_icon
= details
->range_selection_base_icon
;
5227 if (start_icon
== NULL
|| !start_icon
->is_selected
) {
5229 details
->range_selection_base_icon
= icon
;
5231 if (select_range (container
, start_icon
, icon
)) {
5232 g_signal_emit (container
,
5233 signals
[SELECTION_CHANGED
], 0);
5235 } else if (!details
->icon_selected_on_button_down
) {
5236 details
->range_selection_base_icon
= icon
;
5237 if (button_event_modifies_selection (event
)) {
5238 icon_toggle_selected (container
, icon
);
5239 g_signal_emit (container
,
5240 signals
[SELECTION_CHANGED
], 0);
5242 select_one_unselect_others (container
, icon
);
5243 g_signal_emit (container
,
5244 signals
[SELECTION_CHANGED
], 0);
5248 if (event
->button
== CONTEXTUAL_MENU_BUTTON
) {
5249 g_signal_emit (container
,
5250 signals
[CONTEXT_CLICK_SELECTION
], 0,
5259 item_event_callback (EelCanvasItem
*item
,
5263 NautilusIconContainer
*container
;
5264 NautilusIconContainerDetails
*details
;
5267 container
= NAUTILUS_ICON_CONTAINER (data
);
5268 details
= container
->details
;
5270 icon
= NAUTILUS_ICON_CANVAS_ITEM (item
)->user_data
;
5271 g_return_val_if_fail (icon
!= NULL
, FALSE
);
5273 switch (event
->type
) {
5274 case GDK_BUTTON_PRESS
:
5275 case GDK_2BUTTON_PRESS
:
5276 if (handle_icon_button_press (container
, icon
, &event
->button
)) {
5277 /* Stop the event from being passed along further. Returning
5278 * TRUE ain't enough.
5289 nautilus_icon_container_new (void)
5291 return gtk_widget_new (nautilus_icon_container_get_type (), NULL
);
5294 /* Clear all of the icons in the container. */
5296 nautilus_icon_container_clear (NautilusIconContainer
*container
)
5298 NautilusIconContainerDetails
*details
;
5302 g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
));
5304 details
= container
->details
;
5306 if (details
->icons
== NULL
) {
5310 end_renaming_mode (container
, TRUE
);
5312 clear_keyboard_focus (container
);
5313 clear_keyboard_rubberband_start (container
);
5314 unschedule_keyboard_icon_reveal (container
);
5315 set_pending_icon_to_reveal (container
, NULL
);
5316 details
->stretch_icon
= NULL
;
5317 details
->drop_target
= NULL
;
5319 for (p
= details
->icons
; p
!= NULL
; p
= p
->next
) {
5321 if (icon
->is_monitored
) {
5322 nautilus_icon_container_stop_monitor_top_left (container
,
5326 icon_free (p
->data
);
5328 g_list_free (details
->icons
);
5329 details
->icons
= NULL
;
5330 g_list_free (details
->new_icons
);
5331 details
->new_icons
= NULL
;
5333 g_hash_table_destroy (details
->icon_set
);
5334 details
->icon_set
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
5336 nautilus_icon_container_update_scroll_region (container
);
5340 nautilus_icon_container_is_empty (NautilusIconContainer
*container
)
5342 return container
->details
->icons
== NULL
;
5346 nautilus_icon_container_get_first_visible_icon (NautilusIconContainer
*container
)
5349 NautilusIcon
*icon
, *best_icon
;
5350 GtkAdjustment
*vadj
;
5352 double x1
, y1
, x2
, y2
;
5355 vadj
= gtk_layout_get_vadjustment (GTK_LAYOUT (container
));
5357 eel_canvas_c2w (EEL_CANVAS (container
),
5361 l
= container
->details
->icons
;
5367 if (icon_is_positioned (icon
)) {
5368 eel_canvas_item_get_bounds (EEL_CANVAS_ITEM (icon
->item
),
5369 &x1
, &y1
, &x2
, &y2
);
5371 if (best_icon
!= NULL
) {
5386 return best_icon
? best_icon
->data
: NULL
;
5389 /* puts the icon at the top of the screen */
5391 nautilus_icon_container_scroll_to_icon (NautilusIconContainer
*container
,
5392 NautilusIconData
*data
)
5396 GtkAdjustment
*vadj
;
5398 double x1
, y1
, x2
, y2
;
5399 EelCanvasItem
*item
;
5401 vadj
= gtk_layout_get_vadjustment (GTK_LAYOUT (container
));
5403 /* We need to force a relayout now if there are updates queued
5404 * since we need the final positions */
5405 nautilus_icon_container_layout_now (container
);
5407 l
= container
->details
->icons
;
5411 if (icon
->data
== data
&&
5412 icon_is_positioned (icon
)) {
5413 item
= EEL_CANVAS_ITEM (icon
->item
);
5414 eel_canvas_item_get_bounds (item
,
5415 &x1
, &y1
, &x2
, &y2
);
5416 eel_canvas_item_i2w (item
->parent
,
5419 eel_canvas_w2c (item
->canvas
,
5427 eel_gtk_adjustment_set_value (vadj
, y
);
5434 /* Call a function for all the icons. */
5436 NautilusIconCallback callback
;
5437 gpointer callback_data
;
5441 call_icon_callback (gpointer data
, gpointer callback_data
)
5444 CallbackAndData
*callback_and_data
;
5447 callback_and_data
= callback_data
;
5448 (* callback_and_data
->callback
) (icon
->data
, callback_and_data
->callback_data
);
5452 nautilus_icon_container_for_each (NautilusIconContainer
*container
,
5453 NautilusIconCallback callback
,
5454 gpointer callback_data
)
5456 CallbackAndData callback_and_data
;
5458 g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
));
5460 callback_and_data
.callback
= callback
;
5461 callback_and_data
.callback_data
= callback_data
;
5463 g_list_foreach (container
->details
->icons
,
5464 call_icon_callback
, &callback_and_data
);
5467 /* utility routine to remove a single icon from the container */
5470 icon_destroy (NautilusIconContainer
*container
,
5473 NautilusIconContainerDetails
*details
;
5474 gboolean was_selected
;
5475 NautilusIcon
*icon_to_focus
;
5478 details
= container
->details
;
5480 item
= g_list_find (details
->icons
, icon
);
5481 item
= item
->next
? item
->next
: item
->prev
;
5482 icon_to_focus
= (item
!= NULL
) ? item
->data
: NULL
;
5484 details
->icons
= g_list_remove (details
->icons
, icon
);
5485 details
->new_icons
= g_list_remove (details
->new_icons
, icon
);
5486 g_hash_table_remove (details
->icon_set
, icon
->data
);
5488 was_selected
= icon
->is_selected
;
5490 if (details
->keyboard_focus
== icon
||
5491 details
->keyboard_focus
== NULL
) {
5492 if (icon_to_focus
!= NULL
) {
5493 set_keyboard_focus (container
, icon_to_focus
);
5495 clear_keyboard_focus (container
);
5499 if (details
->keyboard_rubberband_start
== icon
) {
5500 clear_keyboard_rubberband_start (container
);
5503 if (details
->keyboard_icon_to_reveal
== icon
) {
5504 unschedule_keyboard_icon_reveal (container
);
5506 if (details
->drag_icon
== icon
) {
5507 clear_drag_state (container
);
5509 if (details
->drop_target
== icon
) {
5510 details
->drop_target
= NULL
;
5512 if (details
->range_selection_base_icon
== icon
) {
5513 details
->range_selection_base_icon
= NULL
;
5515 if (details
->pending_icon_to_reveal
== icon
) {
5516 set_pending_icon_to_reveal (container
, NULL
);
5518 if (details
->stretch_icon
== icon
) {
5519 details
->stretch_icon
= NULL
;
5522 if (icon
->is_monitored
) {
5523 nautilus_icon_container_stop_monitor_top_left (container
,
5530 g_signal_emit (container
,
5531 signals
[SELECTION_CHANGED
], 0);
5535 /* activate any selected items in the container */
5537 activate_selected_items (NautilusIconContainer
*container
)
5541 g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
));
5543 selection
= nautilus_icon_container_get_selection (container
);
5544 if (selection
!= NULL
) {
5545 g_signal_emit (container
,
5546 signals
[ACTIVATE
], 0,
5549 g_list_free (selection
);
5553 activate_selected_items_alternate (NautilusIconContainer
*container
,
5558 g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
));
5561 selection
= g_list_prepend (NULL
, icon
->data
);
5563 selection
= nautilus_icon_container_get_selection (container
);
5565 if (selection
!= NULL
) {
5566 g_signal_emit (container
,
5567 signals
[ACTIVATE_ALTERNATE
], 0,
5570 g_list_free (selection
);
5573 static NautilusIcon
*
5574 get_icon_being_renamed (NautilusIconContainer
*container
)
5576 NautilusIcon
*rename_icon
;
5578 if (!is_renaming (container
)) {
5582 g_assert (!has_multiple_selection (container
));
5584 rename_icon
= get_first_selected_icon (container
);
5585 g_assert (rename_icon
!= NULL
);
5590 static NautilusIconInfo
*
5591 nautilus_icon_container_get_icon_images (NautilusIconContainer
*container
,
5592 NautilusIconData
*data
,
5594 GList
**emblem_pixbufs
,
5595 char **embedded_text
,
5596 gboolean for_drag_accept
,
5597 gboolean need_large_embeddded_text
,
5598 gboolean
*embedded_text_needs_loading
,
5599 gboolean
*has_open_window
)
5601 NautilusIconContainerClass
*klass
;
5603 klass
= NAUTILUS_ICON_CONTAINER_GET_CLASS (container
);
5604 g_return_val_if_fail (klass
->get_icon_images
!= NULL
, NULL
);
5606 return klass
->get_icon_images (container
, data
, size
, emblem_pixbufs
, embedded_text
, for_drag_accept
, need_large_embeddded_text
, embedded_text_needs_loading
, has_open_window
);
5611 nautilus_icon_container_get_icon_text (NautilusIconContainer
*container
,
5612 NautilusIconData
*data
,
5613 char **editable_text
,
5614 char **additional_text
)
5616 NautilusIconContainerClass
*klass
;
5618 klass
= NAUTILUS_ICON_CONTAINER_GET_CLASS (container
);
5619 g_return_if_fail (klass
->get_icon_text
!= NULL
);
5621 klass
->get_icon_text (container
, data
, editable_text
, additional_text
);
5625 nautilus_icon_container_freeze_updates (NautilusIconContainer
*container
)
5627 NautilusIconContainerClass
*klass
;
5629 klass
= NAUTILUS_ICON_CONTAINER_GET_CLASS (container
);
5630 g_return_if_fail (klass
->freeze_updates
!= NULL
);
5632 klass
->freeze_updates (container
);
5636 nautilus_icon_container_unfreeze_updates (NautilusIconContainer
*container
)
5638 NautilusIconContainerClass
*klass
;
5640 klass
= NAUTILUS_ICON_CONTAINER_GET_CLASS (container
);
5641 g_return_if_fail (klass
->unfreeze_updates
!= NULL
);
5643 klass
->unfreeze_updates (container
);
5647 nautilus_icon_container_start_monitor_top_left (NautilusIconContainer
*container
,
5648 NautilusIconData
*data
,
5649 gconstpointer client
,
5650 gboolean large_text
)
5652 NautilusIconContainerClass
*klass
;
5654 klass
= NAUTILUS_ICON_CONTAINER_GET_CLASS (container
);
5655 g_return_if_fail (klass
->start_monitor_top_left
!= NULL
);
5657 klass
->start_monitor_top_left (container
, data
, client
, large_text
);
5661 nautilus_icon_container_stop_monitor_top_left (NautilusIconContainer
*container
,
5662 NautilusIconData
*data
,
5663 gconstpointer client
)
5665 NautilusIconContainerClass
*klass
;
5667 klass
= NAUTILUS_ICON_CONTAINER_GET_CLASS (container
);
5668 g_return_if_fail (klass
->stop_monitor_top_left
!= NULL
);
5670 klass
->stop_monitor_top_left (container
, data
, client
);
5675 nautilus_icon_container_prioritize_thumbnailing (NautilusIconContainer
*container
,
5678 NautilusIconContainerClass
*klass
;
5680 klass
= NAUTILUS_ICON_CONTAINER_GET_CLASS (container
);
5681 g_return_if_fail (klass
->prioritize_thumbnailing
!= NULL
);
5683 klass
->prioritize_thumbnailing (container
, icon
->data
);
5687 nautilus_icon_container_update_visible_icons (NautilusIconContainer
*container
)
5689 GtkAdjustment
*vadj
;
5690 double min_y
, max_y
;
5691 double x0
, y0
, x1
, y1
;
5696 vadj
= gtk_layout_get_vadjustment (GTK_LAYOUT (container
));
5698 min_y
= vadj
->value
;
5699 max_y
= min_y
+ GTK_WIDGET (container
)->allocation
.height
;
5701 eel_canvas_c2w (EEL_CANVAS (container
),
5702 0, min_y
, NULL
, &min_y
);
5703 eel_canvas_c2w (EEL_CANVAS (container
),
5704 0, max_y
, NULL
, &max_y
);
5706 /* Do the iteration in reverse to get the render-order from top to
5707 * bottom for the prioritized thumbnails.
5709 for (node
= g_list_last (container
->details
->icons
); node
!= NULL
; node
= node
->prev
) {
5712 if (icon_is_positioned (icon
)) {
5713 eel_canvas_item_get_bounds (EEL_CANVAS_ITEM (icon
->item
),
5718 eel_canvas_item_i2w (EEL_CANVAS_ITEM (icon
->item
)->parent
,
5721 eel_canvas_item_i2w (EEL_CANVAS_ITEM (icon
->item
)->parent
,
5724 if (y1
>= min_y
&& y0
<= max_y
) {
5725 nautilus_icon_canvas_item_set_is_visible (icon
->item
, TRUE
);
5726 nautilus_icon_container_prioritize_thumbnailing (container
,
5729 nautilus_icon_canvas_item_set_is_visible (icon
->item
, FALSE
);
5736 handle_vadjustment_changed (GtkAdjustment
*adjustment
,
5737 NautilusIconContainer
*container
)
5739 nautilus_icon_container_update_visible_icons (container
);
5743 nautilus_icon_container_update_icon (NautilusIconContainer
*container
,
5746 NautilusIconContainerDetails
*details
;
5748 guint min_image_size
, max_image_size
;
5749 NautilusIconInfo
*icon_info
;
5750 GdkPoint
*attach_points
;
5751 int n_attach_points
;
5752 gboolean has_embedded_text_rect
;
5754 GList
*emblem_pixbufs
;
5755 char *editable_text
, *additional_text
;
5756 char *embedded_text
;
5757 GdkRectangle embedded_text_rect
;
5758 gboolean large_embedded_text
;
5759 gboolean embedded_text_needs_loading
;
5760 gboolean has_open_window
;
5766 details
= container
->details
;
5768 /* compute the maximum size based on the scale factor */
5769 min_image_size
= MINIMUM_IMAGE_SIZE
* EEL_CANVAS (container
)->pixels_per_unit
;
5770 max_image_size
= MAX (MAXIMUM_IMAGE_SIZE
* EEL_CANVAS (container
)->pixels_per_unit
, NAUTILUS_ICON_MAXIMUM_SIZE
);
5772 /* Get the appropriate images for the file. */
5773 icon_get_size (container
, icon
, &icon_size
);
5775 icon_size
= MAX (icon_size
, min_image_size
);
5776 icon_size
= MIN (icon_size
, max_image_size
);
5778 /* Get the icons. */
5779 emblem_pixbufs
= NULL
;
5780 embedded_text
= NULL
;
5781 large_embedded_text
= icon_size
> ICON_SIZE_FOR_LARGE_EMBEDDED_TEXT
;
5782 icon_info
= nautilus_icon_container_get_icon_images (container
, icon
->data
, icon_size
,
5785 icon
== details
->drop_target
,
5786 large_embedded_text
, &embedded_text_needs_loading
,
5790 pixbuf
= nautilus_icon_info_get_pixbuf (icon_info
);
5791 nautilus_icon_info_get_attach_points (icon_info
, &attach_points
, &n_attach_points
);
5792 has_embedded_text_rect
= nautilus_icon_info_get_embedded_rect (icon_info
,
5793 &embedded_text_rect
);
5795 if (has_embedded_text_rect
&& embedded_text_needs_loading
) {
5796 icon
->is_monitored
= TRUE
;
5797 nautilus_icon_container_start_monitor_top_left (container
, icon
->data
, icon
, large_embedded_text
);
5800 icon_size
= MAX (nautilus_get_icon_size_for_zoom_level (container
->details
->zoom_level
)
5801 * icon
->scale
, NAUTILUS_ICON_SIZE_SMALLEST
);
5803 nautilus_icon_container_get_icon_text (container
,
5808 /* If name of icon being renamed was changed from elsewhere, end renaming mode.
5809 * Alternatively, we could replace the characters in the editable text widget
5810 * with the new name, but that could cause timing problems if the user just
5811 * happened to be typing at that moment.
5813 if (icon
== get_icon_being_renamed (container
) &&
5814 eel_strcmp (editable_text
,
5815 nautilus_icon_canvas_item_get_editable_text (icon
->item
)) != 0) {
5816 end_renaming_mode (container
, FALSE
);
5819 eel_canvas_item_set (EEL_CANVAS_ITEM (icon
->item
),
5820 "editable_text", editable_text
,
5821 "additional_text", additional_text
,
5822 "highlighted_for_drop", icon
== details
->drop_target
,
5825 nautilus_icon_canvas_item_set_image (icon
->item
, pixbuf
);
5826 nautilus_icon_canvas_item_set_attach_points (icon
->item
, attach_points
, n_attach_points
);
5827 nautilus_icon_canvas_item_set_emblems (icon
->item
, emblem_pixbufs
);
5828 nautilus_icon_canvas_item_set_embedded_text_rect (icon
->item
, &embedded_text_rect
);
5829 nautilus_icon_canvas_item_set_embedded_text (icon
->item
, embedded_text
);
5831 /* Let the pixbufs go. */
5832 g_object_unref (pixbuf
);
5833 eel_gdk_pixbuf_list_free (emblem_pixbufs
);
5835 g_free (editable_text
);
5836 g_free (additional_text
);
5838 g_object_unref (icon_info
);
5842 assign_icon_position (NautilusIconContainer
*container
,
5845 gboolean have_stored_position
;
5846 NautilusIconPosition position
;
5848 /* Get the stored position. */
5849 have_stored_position
= FALSE
;
5850 position
.scale
= 1.0;
5851 g_signal_emit (container
,
5852 signals
[GET_STORED_ICON_POSITION
], 0,
5855 &have_stored_position
);
5856 icon
->scale
= position
.scale
;
5857 if (!container
->details
->auto_layout
) {
5858 if (have_stored_position
) {
5859 icon_set_position (icon
, position
.x
, position
.y
);
5860 icon
->saved_ltr_x
= icon
->x
;
5869 finish_adding_icon (NautilusIconContainer
*container
,
5872 nautilus_icon_container_update_icon (container
, icon
);
5873 eel_canvas_item_show (EEL_CANVAS_ITEM (icon
->item
));
5875 g_signal_connect_object (icon
->item
, "event",
5876 G_CALLBACK (item_event_callback
), container
, 0);
5878 g_signal_emit (container
, signals
[ICON_ADDED
], 0, icon
->data
);
5882 finish_adding_new_icons (NautilusIconContainer
*container
)
5884 GList
*p
, *new_icons
, *no_position_icons
, *semi_position_icons
;
5888 new_icons
= container
->details
->new_icons
;
5889 container
->details
->new_icons
= NULL
;
5891 /* Position most icons (not unpositioned manual-layout icons). */
5892 new_icons
= g_list_reverse (new_icons
);
5893 no_position_icons
= semi_position_icons
= NULL
;
5894 for (p
= new_icons
; p
!= NULL
; p
= p
->next
) {
5896 if (assign_icon_position (container
, icon
)) {
5897 if (!container
->details
->auto_layout
&& icon
->has_lazy_position
) {
5898 semi_position_icons
= g_list_prepend (semi_position_icons
, icon
);
5901 no_position_icons
= g_list_prepend (no_position_icons
, icon
);
5904 finish_adding_icon (container
, icon
);
5906 g_list_free (new_icons
);
5908 if (semi_position_icons
!= NULL
) {
5909 PlacementGrid
*grid
;
5911 g_assert (!container
->details
->auto_layout
);
5913 semi_position_icons
= g_list_reverse (semi_position_icons
);
5915 /* This is currently only used on the desktop.
5916 * Thus, we pass FALSE for tight, like lay_down_icons_tblr */
5917 grid
= placement_grid_new (container
, FALSE
);
5919 for (p
= container
->details
->icons
; p
!= NULL
; p
= p
->next
) {
5922 if (icon_is_positioned (icon
) && !icon
->has_lazy_position
) {
5923 placement_grid_mark_icon (grid
, icon
);
5927 for (p
= semi_position_icons
; p
!= NULL
; p
= p
->next
) {
5935 find_empty_location (container
, grid
,
5936 icon
, x
, y
, &x
, &y
);
5938 icon_set_position (icon
, x
, y
);
5940 placement_grid_mark_icon (grid
, icon
);
5942 /* ensure that next time we run this code, the formerly semi-positioned
5943 * icons are treated as being positioned. */
5944 icon
->has_lazy_position
= FALSE
;
5947 placement_grid_free (grid
);
5949 g_list_free (semi_position_icons
);
5952 /* Position the unpositioned manual layout icons. */
5953 if (no_position_icons
!= NULL
) {
5954 g_assert (!container
->details
->auto_layout
);
5956 sort_icons (container
, &no_position_icons
);
5957 get_all_icon_bounds (container
, NULL
, NULL
, NULL
, &bottom
);
5958 lay_down_icons (container
, no_position_icons
, bottom
+ ICON_PAD_BOTTOM
);
5959 g_list_free (no_position_icons
);
5964 * nautilus_icon_container_add:
5965 * @container: A NautilusIconContainer
5967 * @has_lazy_position: Whether the saved icon position should only be used
5968 * if the previous icon position is free. If the position
5969 * is occupied, another position near the last one will
5972 * Add icon to represent @data to container.
5973 * Returns FALSE if there was already such an icon.
5976 nautilus_icon_container_add (NautilusIconContainer
*container
,
5977 NautilusIconData
*data
,
5978 gboolean has_lazy_position
)
5980 NautilusIconContainerDetails
*details
;
5982 EelCanvasItem
*band
, *item
;
5984 g_return_val_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
), FALSE
);
5985 g_return_val_if_fail (data
!= NULL
, FALSE
);
5987 details
= container
->details
;
5989 if (g_hash_table_lookup (details
->icon_set
, data
) != NULL
) {
5993 /* Create the new icon, including the canvas item. */
5994 icon
= g_new0 (NautilusIcon
, 1);
5996 icon
->x
= ICON_UNPOSITIONED_VALUE
;
5997 icon
->y
= ICON_UNPOSITIONED_VALUE
;
5998 icon
->has_lazy_position
= has_lazy_position
;
6000 icon
->item
= NAUTILUS_ICON_CANVAS_ITEM
6001 (eel_canvas_item_new (EEL_CANVAS_GROUP (EEL_CANVAS (container
)->root
),
6002 nautilus_icon_canvas_item_get_type (),
6005 icon
->item
->user_data
= icon
;
6007 /* Make sure the icon is under the selection_rectangle */
6008 item
= EEL_CANVAS_ITEM (icon
->item
);
6009 band
= NAUTILUS_ICON_CONTAINER (item
->canvas
)->details
->rubberband_info
.selection_rectangle
;
6011 eel_canvas_item_send_behind (item
, band
);
6014 /* Put it on both lists. */
6015 details
->icons
= g_list_prepend (details
->icons
, icon
);
6016 details
->new_icons
= g_list_prepend (details
->new_icons
, icon
);
6018 g_hash_table_insert (details
->icon_set
, data
, icon
);
6020 /* Run an idle function to add the icons. */
6021 schedule_redo_layout (container
);
6027 nautilus_icon_container_layout_now (NautilusIconContainer
*container
)
6029 if (container
->details
->idle_id
!= 0) {
6030 unschedule_redo_layout (container
);
6031 redo_layout_internal (container
);
6036 * nautilus_icon_container_remove:
6037 * @container: A NautilusIconContainer.
6040 * Remove the icon with this data.
6043 nautilus_icon_container_remove (NautilusIconContainer
*container
,
6044 NautilusIconData
*data
)
6048 g_return_val_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
), FALSE
);
6049 g_return_val_if_fail (data
!= NULL
, FALSE
);
6051 end_renaming_mode (container
, FALSE
);
6053 icon
= g_hash_table_lookup (container
->details
->icon_set
, data
);
6059 icon_destroy (container
, icon
);
6060 schedule_redo_layout (container
);
6062 g_signal_emit (container
, signals
[ICON_REMOVED
], 0, icon
);
6068 * nautilus_icon_container_request_update:
6069 * @container: A NautilusIconContainer.
6072 * Update the icon with this data.
6075 nautilus_icon_container_request_update (NautilusIconContainer
*container
,
6076 NautilusIconData
*data
)
6080 g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
));
6081 g_return_if_fail (data
!= NULL
);
6083 icon
= g_hash_table_lookup (container
->details
->icon_set
, data
);
6086 nautilus_icon_container_update_icon (container
, icon
);
6087 schedule_redo_layout (container
);
6094 nautilus_icon_container_get_zoom_level (NautilusIconContainer
*container
)
6096 return container
->details
->zoom_level
;
6100 nautilus_icon_container_set_zoom_level (NautilusIconContainer
*container
, int new_level
)
6102 NautilusIconContainerDetails
*details
;
6104 double pixels_per_unit
;
6106 details
= container
->details
;
6108 end_renaming_mode (container
, TRUE
);
6110 pinned_level
= new_level
;
6111 if (pinned_level
< NAUTILUS_ZOOM_LEVEL_SMALLEST
) {
6112 pinned_level
= NAUTILUS_ZOOM_LEVEL_SMALLEST
;
6113 } else if (pinned_level
> NAUTILUS_ZOOM_LEVEL_LARGEST
) {
6114 pinned_level
= NAUTILUS_ZOOM_LEVEL_LARGEST
;
6117 if (pinned_level
== details
->zoom_level
) {
6121 details
->zoom_level
= pinned_level
;
6123 pixels_per_unit
= (double) nautilus_get_icon_size_for_zoom_level (pinned_level
)
6124 / NAUTILUS_ICON_SIZE_STANDARD
;
6125 eel_canvas_set_pixels_per_unit (EEL_CANVAS (container
), pixels_per_unit
);
6127 invalidate_label_sizes (container
);
6128 nautilus_icon_container_request_update_all (container
);
6132 * nautilus_icon_container_request_update_all:
6133 * For each icon, synchronizes the displayed information (image, text) with the
6134 * information from the model.
6136 * @container: An icon container.
6139 nautilus_icon_container_request_update_all (NautilusIconContainer
*container
)
6144 g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
));
6146 for (node
= container
->details
->icons
; node
!= NULL
; node
= node
->next
) {
6148 nautilus_icon_container_update_icon (container
, icon
);
6151 redo_layout (container
);
6155 * nautilus_icon_container_reveal:
6156 * Change scroll position as necessary to reveal the specified item.
6159 nautilus_icon_container_reveal (NautilusIconContainer
*container
, NautilusIconData
*data
)
6163 g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
));
6164 g_return_if_fail (data
!= NULL
);
6166 icon
= g_hash_table_lookup (container
->details
->icon_set
, data
);
6169 reveal_icon (container
, icon
);
6174 * nautilus_icon_container_get_selection:
6175 * @container: An icon container.
6177 * Get a list of the icons currently selected in @container.
6179 * Return value: A GList of the programmer-specified data associated to each
6180 * selected icon, or NULL if no icon is selected. The caller is expected to
6181 * free the list when it is not needed anymore.
6184 nautilus_icon_container_get_selection (NautilusIconContainer
*container
)
6188 g_return_val_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
), NULL
);
6191 for (p
= container
->details
->icons
; p
!= NULL
; p
= p
->next
) {
6195 if (icon
->is_selected
) {
6196 list
= g_list_prepend (list
, icon
->data
);
6200 return g_list_reverse (list
);
6204 nautilus_icon_container_get_selected_icons (NautilusIconContainer
*container
)
6208 g_return_val_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
), NULL
);
6211 for (p
= container
->details
->icons
; p
!= NULL
; p
= p
->next
) {
6215 if (icon
->is_selected
) {
6216 list
= g_list_prepend (list
, icon
);
6220 return g_list_reverse (list
);
6223 /* Returns an array of GdkPoints of locations of the icons. */
6225 nautilus_icon_container_get_icon_locations (NautilusIconContainer
*container
,
6232 result
= g_array_new (FALSE
, TRUE
, sizeof (GdkPoint
));
6233 result
= g_array_set_size (result
, g_list_length (icons
));
6235 for (index
= 0, node
= icons
; node
!= NULL
; index
++, node
= node
->next
) {
6236 g_array_index (result
, GdkPoint
, index
).x
=
6237 ((NautilusIcon
*)node
->data
)->x
;
6238 g_array_index (result
, GdkPoint
, index
).y
=
6239 ((NautilusIcon
*)node
->data
)->y
;
6246 * nautilus_icon_container_get_selected_icon_locations:
6247 * @container: An icon container widget.
6249 * Returns an array of GdkPoints of locations of the selected icons.
6252 nautilus_icon_container_get_selected_icon_locations (NautilusIconContainer
*container
)
6257 g_return_val_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
), NULL
);
6259 icons
= nautilus_icon_container_get_selected_icons (container
);
6260 result
= nautilus_icon_container_get_icon_locations (container
, icons
);
6261 g_list_free (icons
);
6267 * nautilus_icon_container_select_all:
6268 * @container: An icon container widget.
6270 * Select all the icons in @container at once.
6273 nautilus_icon_container_select_all (NautilusIconContainer
*container
)
6275 gboolean selection_changed
;
6279 g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
));
6281 selection_changed
= FALSE
;
6283 for (p
= container
->details
->icons
; p
!= NULL
; p
= p
->next
) {
6286 selection_changed
|= icon_set_selected (container
, icon
, TRUE
);
6289 if (selection_changed
) {
6290 g_signal_emit (container
,
6291 signals
[SELECTION_CHANGED
], 0);
6296 * nautilus_icon_container_set_selection:
6297 * @container: An icon container widget.
6298 * @selection: A list of NautilusIconData *.
6300 * Set the selection to exactly the icons in @container which have
6301 * programmer data matching one of the items in @selection.
6304 nautilus_icon_container_set_selection (NautilusIconContainer
*container
,
6307 gboolean selection_changed
;
6312 g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
));
6314 selection_changed
= FALSE
;
6316 hash
= g_hash_table_new (NULL
, NULL
);
6317 for (p
= selection
; p
!= NULL
; p
= p
->next
) {
6318 g_hash_table_insert (hash
, p
->data
, p
->data
);
6320 for (p
= container
->details
->icons
; p
!= NULL
; p
= p
->next
) {
6323 selection_changed
|= icon_set_selected
6325 g_hash_table_lookup (hash
, icon
->data
) != NULL
);
6327 g_hash_table_destroy (hash
);
6329 if (selection_changed
) {
6330 g_signal_emit (container
,
6331 signals
[SELECTION_CHANGED
], 0);
6336 * nautilus_icon_container_select_list_unselect_others.
6337 * @container: An icon container widget.
6338 * @selection: A list of NautilusIcon *.
6340 * Set the selection to exactly the icons in @selection.
6343 nautilus_icon_container_select_list_unselect_others (NautilusIconContainer
*container
,
6346 gboolean selection_changed
;
6351 g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
));
6353 selection_changed
= FALSE
;
6355 hash
= g_hash_table_new (NULL
, NULL
);
6356 for (p
= selection
; p
!= NULL
; p
= p
->next
) {
6357 g_hash_table_insert (hash
, p
->data
, p
->data
);
6359 for (p
= container
->details
->icons
; p
!= NULL
; p
= p
->next
) {
6362 selection_changed
|= icon_set_selected
6364 g_hash_table_lookup (hash
, icon
) != NULL
);
6366 g_hash_table_destroy (hash
);
6368 if (selection_changed
) {
6369 g_signal_emit (container
,
6370 signals
[SELECTION_CHANGED
], 0);
6375 * nautilus_icon_container_unselect_all:
6376 * @container: An icon container widget.
6378 * Deselect all the icons in @container.
6381 nautilus_icon_container_unselect_all (NautilusIconContainer
*container
)
6383 if (unselect_all (container
)) {
6384 g_signal_emit (container
,
6385 signals
[SELECTION_CHANGED
], 0);
6390 * nautilus_icon_container_get_icon_by_uri:
6391 * @container: An icon container widget.
6392 * @uri: The uri of an icon to find.
6394 * Locate an icon, given the URI. The URI must match exactly.
6395 * Later we may have to have some way of figuring out if the
6396 * URI specifies the same object that does not require an exact match.
6399 nautilus_icon_container_get_icon_by_uri (NautilusIconContainer
*container
,
6402 NautilusIconContainerDetails
*details
;
6405 /* Eventually, we must avoid searching the entire icon list,
6406 but it's OK for now.
6407 A hash table mapping uri to icon is one possibility.
6410 details
= container
->details
;
6412 for (p
= details
->icons
; p
!= NULL
; p
= p
->next
) {
6419 icon_uri
= nautilus_icon_container_get_icon_uri
6421 is_match
= strcmp (uri
, icon_uri
) == 0;
6432 static NautilusIcon
*
6433 get_nth_selected_icon (NautilusIconContainer
*container
, int index
)
6437 int selection_count
;
6439 g_return_val_if_fail (index
> 0, NULL
);
6441 /* Find the nth selected icon. */
6442 selection_count
= 0;
6443 for (p
= container
->details
->icons
; p
!= NULL
; p
= p
->next
) {
6445 if (icon
->is_selected
) {
6446 if (++selection_count
== index
) {
6454 static NautilusIcon
*
6455 get_first_selected_icon (NautilusIconContainer
*container
)
6457 return get_nth_selected_icon (container
, 1);
6461 has_multiple_selection (NautilusIconContainer
*container
)
6463 return get_nth_selected_icon (container
, 2) != NULL
;
6467 all_selected (NautilusIconContainer
*container
)
6472 for (p
= container
->details
->icons
; p
!= NULL
; p
= p
->next
) {
6474 if (!icon
->is_selected
) {
6482 has_selection (NautilusIconContainer
*container
)
6484 return get_nth_selected_icon (container
, 1) != NULL
;
6488 * nautilus_icon_container_show_stretch_handles:
6489 * @container: An icon container widget.
6491 * Makes stretch handles visible on the first selected icon.
6494 nautilus_icon_container_show_stretch_handles (NautilusIconContainer
*container
)
6496 NautilusIconContainerDetails
*details
;
6500 icon
= get_first_selected_icon (container
);
6505 /* Check if it already has stretch handles. */
6506 details
= container
->details
;
6507 if (details
->stretch_icon
== icon
) {
6511 /* Get rid of the existing stretch handles and put them on the new icon. */
6512 if (details
->stretch_icon
!= NULL
) {
6513 nautilus_icon_canvas_item_set_show_stretch_handles
6514 (details
->stretch_icon
->item
, FALSE
);
6515 ungrab_stretch_icon (container
);
6516 emit_stretch_ended (container
, details
->stretch_icon
);
6518 nautilus_icon_canvas_item_set_show_stretch_handles (icon
->item
, TRUE
);
6519 details
->stretch_icon
= icon
;
6521 icon_get_size (container
, icon
, &initial_size
);
6523 /* only need to keep size in one dimension, since they are constrained to be the same */
6524 container
->details
->stretch_initial_x
= icon
->x
;
6525 container
->details
->stretch_initial_y
= icon
->y
;
6526 container
->details
->stretch_initial_size
= initial_size
;
6528 emit_stretch_started (container
, icon
);
6532 * nautilus_icon_container_has_stretch_handles
6533 * @container: An icon container widget.
6535 * Returns true if the first selected item has stretch handles.
6538 nautilus_icon_container_has_stretch_handles (NautilusIconContainer
*container
)
6542 icon
= get_first_selected_icon (container
);
6547 return icon
== container
->details
->stretch_icon
;
6551 * nautilus_icon_container_is_stretched
6552 * @container: An icon container widget.
6554 * Returns true if the any selected item is stretched to a size other than 1.0.
6557 nautilus_icon_container_is_stretched (NautilusIconContainer
*container
)
6562 for (p
= container
->details
->icons
; p
!= NULL
; p
= p
->next
) {
6564 if (icon
->is_selected
&& icon
->scale
!= 1.0) {
6572 * nautilus_icon_container_unstretch
6573 * @container: An icon container widget.
6575 * Gets rid of any icon stretching.
6578 nautilus_icon_container_unstretch (NautilusIconContainer
*container
)
6583 for (p
= container
->details
->icons
; p
!= NULL
; p
= p
->next
) {
6585 if (icon
->is_selected
) {
6586 nautilus_icon_container_move_icon (container
, icon
,
6595 compute_stretch (StretchState
*start
,
6596 StretchState
*current
)
6598 gboolean right
, bottom
;
6599 int x_stretch
, y_stretch
;
6601 /* FIXME bugzilla.gnome.org 45390: This doesn't correspond to
6602 * the way the handles are drawn.
6604 /* Figure out which handle we are dragging. */
6605 right
= start
->pointer_x
> start
->icon_x
+ (int) start
->icon_size
/ 2;
6606 bottom
= start
->pointer_y
> start
->icon_y
+ (int) start
->icon_size
/ 2;
6608 /* Figure out how big we should stretch. */
6609 x_stretch
= start
->pointer_x
- current
->pointer_x
;
6610 y_stretch
= start
->pointer_y
- current
->pointer_y
;
6612 x_stretch
= - x_stretch
;
6615 y_stretch
= - y_stretch
;
6617 current
->icon_size
= MAX ((int) start
->icon_size
+ MIN (x_stretch
, y_stretch
),
6618 (int) NAUTILUS_ICON_SIZE_SMALLEST
);
6620 /* Figure out where the corner of the icon should be. */
6621 current
->icon_x
= start
->icon_x
;
6623 current
->icon_x
+= start
->icon_size
- current
->icon_size
;
6625 current
->icon_y
= start
->icon_y
;
6627 current
->icon_y
+= start
->icon_size
- current
->icon_size
;
6632 nautilus_icon_container_get_icon_uri (NautilusIconContainer
*container
,
6638 g_signal_emit (container
,
6639 signals
[GET_ICON_URI
], 0,
6646 nautilus_icon_container_get_icon_drop_target_uri (NautilusIconContainer
*container
,
6652 g_signal_emit (container
,
6653 signals
[GET_ICON_DROP_TARGET_URI
], 0,
6659 /* Call to reset the scroll region only if the container is not empty,
6660 * to avoid having the flag linger until the next file is added.
6663 reset_scroll_region_if_not_empty (NautilusIconContainer
*container
)
6665 if (!nautilus_icon_container_is_empty (container
)) {
6666 nautilus_icon_container_reset_scroll_region (container
);
6670 /* Switch from automatic layout to manual or vice versa.
6671 * If we switch to manual layout, we restore the icon positions from the
6672 * last manual layout.
6675 nautilus_icon_container_set_auto_layout (NautilusIconContainer
*container
,
6676 gboolean auto_layout
)
6678 g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
));
6679 g_return_if_fail (auto_layout
== FALSE
|| auto_layout
== TRUE
);
6681 if (container
->details
->auto_layout
== auto_layout
) {
6685 reset_scroll_region_if_not_empty (container
);
6686 container
->details
->auto_layout
= auto_layout
;
6689 reload_icon_positions (container
);
6690 nautilus_icon_container_freeze_icon_positions (container
);
6693 redo_layout (container
);
6695 g_signal_emit (container
, signals
[LAYOUT_CHANGED
], 0);
6699 /* Toggle the tighter layout boolean. */
6701 nautilus_icon_container_set_tighter_layout (NautilusIconContainer
*container
,
6702 gboolean tighter_layout
)
6704 g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
));
6705 g_return_if_fail (tighter_layout
== FALSE
|| tighter_layout
== TRUE
);
6707 if (container
->details
->tighter_layout
== tighter_layout
) {
6711 container
->details
->tighter_layout
= tighter_layout
;
6713 if (container
->details
->auto_layout
) {
6714 invalidate_label_sizes (container
);
6715 redo_layout (container
);
6717 g_signal_emit (container
, signals
[LAYOUT_CHANGED
], 0);
6719 /* in manual layout, label sizes still change, even though
6720 * the icons don't move.
6722 invalidate_label_sizes (container
);
6723 nautilus_icon_container_request_update_all (container
);
6728 nautilus_icon_container_is_keep_aligned (NautilusIconContainer
*container
)
6730 return container
->details
->keep_aligned
;
6734 align_icons_callback (gpointer callback_data
)
6736 NautilusIconContainer
*container
;
6738 container
= NAUTILUS_ICON_CONTAINER (callback_data
);
6739 align_icons (container
);
6740 container
->details
->align_idle_id
= 0;
6746 unschedule_align_icons (NautilusIconContainer
*container
)
6748 if (container
->details
->align_idle_id
!= 0) {
6749 g_source_remove (container
->details
->align_idle_id
);
6750 container
->details
->align_idle_id
= 0;
6755 schedule_align_icons (NautilusIconContainer
*container
)
6757 if (container
->details
->align_idle_id
== 0
6758 && container
->details
->has_been_allocated
) {
6759 container
->details
->align_idle_id
= g_idle_add
6760 (align_icons_callback
, container
);
6765 nautilus_icon_container_set_keep_aligned (NautilusIconContainer
*container
,
6766 gboolean keep_aligned
)
6768 if (container
->details
->keep_aligned
!= keep_aligned
) {
6769 container
->details
->keep_aligned
= keep_aligned
;
6771 if (keep_aligned
&& !container
->details
->auto_layout
) {
6772 schedule_align_icons (container
);
6774 unschedule_align_icons (container
);
6780 nautilus_icon_container_set_layout_mode (NautilusIconContainer
*container
,
6781 NautilusIconLayoutMode mode
)
6783 g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
));
6785 container
->details
->layout_mode
= mode
;
6787 redo_layout (container
);
6789 g_signal_emit (container
, signals
[LAYOUT_CHANGED
], 0);
6793 nautilus_icon_container_set_label_position (NautilusIconContainer
*container
,
6794 NautilusIconLabelPosition position
)
6796 g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
));
6798 if (container
->details
->label_position
!= position
) {
6799 container
->details
->label_position
= position
;
6801 invalidate_label_sizes (container
);
6802 nautilus_icon_container_request_update_all (container
);
6804 schedule_redo_layout (container
);
6808 /* Switch from automatic to manual layout, freezing all the icons in their
6809 * current positions instead of restoring icon positions from the last manual
6810 * layout as set_auto_layout does.
6813 nautilus_icon_container_freeze_icon_positions (NautilusIconContainer
*container
)
6818 NautilusIconPosition position
;
6820 changed
= container
->details
->auto_layout
;
6821 container
->details
->auto_layout
= FALSE
;
6823 for (p
= container
->details
->icons
; p
!= NULL
; p
= p
->next
) {
6826 position
.x
= icon
->saved_ltr_x
;
6827 position
.y
= icon
->y
;
6828 position
.scale
= icon
->scale
;
6829 g_signal_emit (container
, signals
[ICON_POSITION_CHANGED
], 0,
6830 icon
->data
, &position
);
6834 g_signal_emit (container
, signals
[LAYOUT_CHANGED
], 0);
6838 /* Re-sort, switching to automatic layout if it was in manual layout. */
6840 nautilus_icon_container_sort (NautilusIconContainer
*container
)
6844 changed
= !container
->details
->auto_layout
;
6845 container
->details
->auto_layout
= TRUE
;
6847 reset_scroll_region_if_not_empty (container
);
6848 redo_layout (container
);
6851 g_signal_emit (container
, signals
[LAYOUT_CHANGED
], 0);
6856 nautilus_icon_container_is_auto_layout (NautilusIconContainer
*container
)
6858 g_return_val_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
), FALSE
);
6860 return container
->details
->auto_layout
;
6864 nautilus_icon_container_is_tighter_layout (NautilusIconContainer
*container
)
6866 g_return_val_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
), FALSE
);
6868 return container
->details
->tighter_layout
;
6872 pending_icon_to_rename_destroy_callback (NautilusIconCanvasItem
*item
, NautilusIconContainer
*container
)
6874 g_assert (container
->details
->pending_icon_to_rename
!= NULL
);
6875 g_assert (container
->details
->pending_icon_to_rename
->item
== item
);
6876 container
->details
->pending_icon_to_rename
= NULL
;
6879 static NautilusIcon
*
6880 get_pending_icon_to_rename (NautilusIconContainer
*container
)
6882 return container
->details
->pending_icon_to_rename
;
6886 set_pending_icon_to_rename (NautilusIconContainer
*container
, NautilusIcon
*icon
)
6888 NautilusIcon
*old_icon
;
6890 old_icon
= container
->details
->pending_icon_to_rename
;
6892 if (icon
== old_icon
) {
6896 if (old_icon
!= NULL
) {
6897 g_signal_handlers_disconnect_by_func
6899 G_CALLBACK (pending_icon_to_rename_destroy_callback
),
6904 g_signal_connect (icon
->item
, "destroy",
6905 G_CALLBACK (pending_icon_to_rename_destroy_callback
), container
);
6908 container
->details
->pending_icon_to_rename
= icon
;
6912 process_pending_icon_to_rename (NautilusIconContainer
*container
)
6914 NautilusIcon
*pending_icon_to_rename
;
6916 pending_icon_to_rename
= get_pending_icon_to_rename (container
);
6918 if (pending_icon_to_rename
!= NULL
) {
6919 if (pending_icon_to_rename
->is_selected
&& !has_multiple_selection (container
)) {
6920 nautilus_icon_container_start_renaming_selected_item (container
, FALSE
);
6922 set_pending_icon_to_rename (container
, NULL
);
6928 is_renaming_pending (NautilusIconContainer
*container
)
6930 return get_pending_icon_to_rename (container
) != NULL
;
6934 is_renaming (NautilusIconContainer
*container
)
6936 return container
->details
->renaming
;
6940 * nautilus_icon_container_start_renaming_selected_item
6941 * @container: An icon container widget.
6942 * @select_all: Whether the whole file should initially be selected, or
6943 * only its basename (i.e. everything except its extension).
6945 * Displays the edit name widget on the first selected icon
6948 nautilus_icon_container_start_renaming_selected_item (NautilusIconContainer
*container
,
6949 gboolean select_all
)
6951 NautilusIconContainerDetails
*details
;
6955 PangoContext
*context
;
6956 PangoFontDescription
*desc
;
6957 const char *editable_text
;
6959 int start_offset
, end_offset
;
6961 /* Check if it already in renaming mode. */
6962 details
= container
->details
;
6963 if (details
->renaming
) {
6967 /* Find selected icon */
6968 icon
= get_first_selected_icon (container
);
6973 g_assert (!has_multiple_selection (container
));
6976 if (!icon_is_positioned (icon
)) {
6977 set_pending_icon_to_rename (container
, icon
);
6981 set_pending_icon_to_rename (container
, NULL
);
6983 /* Make a copy of the original editable text for a later compare */
6984 editable_text
= nautilus_icon_canvas_item_get_editable_text (icon
->item
);
6986 /* This could conceivably be NULL if a rename was triggered really early. */
6987 if (editable_text
== NULL
) {
6991 details
->original_text
= g_strdup (editable_text
);
6993 /* Freeze updates so files added while renaming don't cause rename to loose focus, bug #318373 */
6994 nautilus_icon_container_freeze_updates (container
);
6996 /* Create text renaming widget, if it hasn't been created already.
6997 * We deal with the broken icon text item widget by keeping it around
6998 * so its contents can still be cut and pasted as part of the clipboard
7000 if (details
->rename_widget
== NULL
) {
7001 details
->rename_widget
= eel_editable_label_new ("Test text");
7002 eel_editable_label_set_line_wrap (EEL_EDITABLE_LABEL (details
->rename_widget
), TRUE
);
7003 eel_editable_label_set_line_wrap_mode (EEL_EDITABLE_LABEL (details
->rename_widget
), PANGO_WRAP_WORD_CHAR
);
7004 eel_editable_label_set_draw_outline (EEL_EDITABLE_LABEL (details
->rename_widget
), TRUE
);
7005 eel_editable_label_set_justify (EEL_EDITABLE_LABEL (details
->rename_widget
), GTK_JUSTIFY_CENTER
);
7006 gtk_misc_set_padding (GTK_MISC (details
->rename_widget
), 1, 1);
7007 gtk_layout_put (GTK_LAYOUT (container
),
7008 details
->rename_widget
, 0, 0);
7011 /* Set the right font */
7012 if (details
->font
) {
7013 desc
= pango_font_description_from_string (details
->font
);
7015 context
= gtk_widget_get_pango_context (GTK_WIDGET (container
));
7016 desc
= pango_font_description_copy (pango_context_get_font_description (context
));
7017 pango_font_description_set_size (desc
,
7018 pango_font_description_get_size (desc
) +
7019 container
->details
->font_size_table
[container
->details
->zoom_level
]);
7021 eel_editable_label_set_font_description (EEL_EDITABLE_LABEL (details
->rename_widget
),
7023 pango_font_description_free (desc
);
7025 icon_rect
= nautilus_icon_canvas_item_get_icon_rectangle (icon
->item
);
7026 text_rect
= nautilus_icon_canvas_item_get_text_rectangle (icon
->item
);
7028 width
= nautilus_icon_canvas_item_get_max_text_width (icon
->item
);
7031 if (details
->label_position
== NAUTILUS_ICON_LABEL_POSITION_BESIDE
) {
7032 eel_canvas_w2c (EEL_CANVAS_ITEM (icon
->item
)->canvas
,
7037 eel_canvas_w2c (EEL_CANVAS_ITEM (icon
->item
)->canvas
,
7038 (icon_rect
.x0
+ icon_rect
.x1
) / 2,
7041 x
= x
- width
/ 2 - 1;
7044 gtk_layout_move (GTK_LAYOUT (container
),
7045 details
->rename_widget
,
7048 gtk_widget_set_size_request (details
->rename_widget
,
7050 eel_editable_label_set_text (EEL_EDITABLE_LABEL (details
->rename_widget
),
7056 eel_filename_get_rename_region (editable_text
, &start_offset
, &end_offset
);
7058 eel_editable_label_select_region (EEL_EDITABLE_LABEL (details
->rename_widget
),
7061 gtk_widget_show (details
->rename_widget
);
7063 gtk_widget_grab_focus (details
->rename_widget
);
7065 g_signal_emit (container
,
7066 signals
[RENAMING_ICON
], 0,
7067 GTK_EDITABLE (details
->rename_widget
));
7069 nautilus_icon_container_update_icon (container
, icon
);
7071 /* We are in renaming mode */
7072 details
->renaming
= TRUE
;
7073 nautilus_icon_canvas_item_set_renaming (icon
->item
, TRUE
);
7077 end_renaming_mode (NautilusIconContainer
*container
, gboolean commit
)
7080 const char *changed_text
;
7082 set_pending_icon_to_rename (container
, NULL
);
7084 icon
= get_icon_being_renamed (container
);
7089 /* We are not in renaming mode */
7090 container
->details
->renaming
= FALSE
;
7091 nautilus_icon_canvas_item_set_renaming (icon
->item
, FALSE
);
7093 nautilus_icon_container_unfreeze_updates (container
);
7096 set_pending_icon_to_reveal (container
, icon
);
7099 gtk_widget_grab_focus (GTK_WIDGET (container
));
7102 /* Verify that text has been modified before signalling change. */
7103 changed_text
= eel_editable_label_get_text (EEL_EDITABLE_LABEL (container
->details
->rename_widget
));
7104 if (strcmp (container
->details
->original_text
, changed_text
) != 0) {
7105 g_signal_emit (container
,
7106 signals
[ICON_TEXT_CHANGED
], 0,
7112 gtk_widget_hide (container
->details
->rename_widget
);
7114 g_free (container
->details
->original_text
);
7118 /* emit preview signal, called by the canvas item */
7120 nautilus_icon_container_emit_preview_signal (NautilusIconContainer
*icon_container
,
7122 gboolean start_flag
)
7126 g_return_val_if_fail (NAUTILUS_IS_ICON_CONTAINER (icon_container
), FALSE
);
7127 g_return_val_if_fail (icon
!= NULL
, FALSE
);
7128 g_return_val_if_fail (start_flag
== FALSE
|| start_flag
== TRUE
, FALSE
);
7131 g_signal_emit (icon_container
,
7132 signals
[PREVIEW
], 0,
7141 nautilus_icon_container_has_stored_icon_positions (NautilusIconContainer
*container
)
7145 gboolean have_stored_position
;
7146 NautilusIconPosition position
;
7148 for (p
= container
->details
->icons
; p
!= NULL
; p
= p
->next
) {
7151 have_stored_position
= FALSE
;
7152 g_signal_emit (container
,
7153 signals
[GET_STORED_ICON_POSITION
], 0,
7156 &have_stored_position
);
7157 if (have_stored_position
) {
7165 nautilus_icon_container_set_single_click_mode (NautilusIconContainer
*container
,
7166 gboolean single_click_mode
)
7168 g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
));
7170 container
->details
->single_click_mode
= single_click_mode
;
7174 /* update the label color when the background changes */
7177 nautilus_icon_container_get_label_color_and_gc (NautilusIconContainer
*container
,
7180 gboolean is_highlight
,
7187 if (GTK_WIDGET_HAS_FOCUS (GTK_WIDGET (container
))) {
7188 idx
= LABEL_COLOR_HIGHLIGHT
;
7190 idx
= LABEL_COLOR_ACTIVE
;
7194 idx
= LABEL_COLOR_PRELIGHT
;
7201 if (GTK_WIDGET_HAS_FOCUS (GTK_WIDGET (container
))) {
7202 idx
= LABEL_INFO_COLOR_HIGHLIGHT
;
7204 idx
= LABEL_INFO_COLOR_ACTIVE
;
7207 idx
= LABEL_INFO_COLOR
;
7212 *color
= &container
->details
->label_colors
[idx
];
7215 return container
->details
->label_gcs
[idx
];
7219 setup_gc_with_fg (NautilusIconContainer
*container
, int idx
, guint32 color
)
7224 gcolor
= eel_gdk_rgb_to_color (color
);
7225 container
->details
->label_colors
[idx
] = gcolor
;
7227 gc
= gdk_gc_new (GTK_LAYOUT (container
)->bin_window
);
7228 gdk_gc_set_rgb_fg_color (gc
, &gcolor
);
7230 if (container
->details
->label_gcs
[idx
]) {
7231 g_object_unref (container
->details
->label_gcs
[idx
]);
7234 container
->details
->label_gcs
[idx
] = gc
;
7238 setup_label_gcs (NautilusIconContainer
*container
)
7240 EelBackground
*background
;
7242 GdkColor
*light_info_color
, *dark_info_color
;
7243 guint light_info_value
, dark_info_value
;
7244 gboolean frame_text
;
7246 if (!GTK_WIDGET_REALIZED (container
))
7249 widget
= GTK_WIDGET (container
);
7251 g_assert (NAUTILUS_IS_ICON_CONTAINER (container
));
7253 background
= eel_get_widget_background (GTK_WIDGET (container
));
7255 /* read the info colors from the current theme; use a reasonable default if undefined */
7256 gtk_widget_style_get (GTK_WIDGET (container
),
7257 "light_info_color", &light_info_color
,
7258 "dark_info_color", &dark_info_color
,
7261 if (light_info_color
) {
7262 light_info_value
= eel_gdk_color_to_rgb (light_info_color
);
7263 gdk_color_free (light_info_color
);
7265 light_info_value
= DEFAULT_LIGHT_INFO_COLOR
;
7268 if (dark_info_color
) {
7269 dark_info_value
= eel_gdk_color_to_rgb (dark_info_color
);
7270 gdk_color_free (dark_info_color
);
7272 dark_info_value
= DEFAULT_DARK_INFO_COLOR
;
7275 setup_gc_with_fg (container
, LABEL_COLOR_HIGHLIGHT
, eel_gdk_color_to_rgb (&widget
->style
->text
[GTK_STATE_SELECTED
]));
7276 setup_gc_with_fg (container
, LABEL_COLOR_ACTIVE
, eel_gdk_color_to_rgb (&widget
->style
->text
[GTK_STATE_ACTIVE
]));
7277 setup_gc_with_fg (container
, LABEL_COLOR_PRELIGHT
, eel_gdk_color_to_rgb (&widget
->style
->text
[GTK_STATE_PRELIGHT
]));
7278 setup_gc_with_fg (container
,
7279 LABEL_INFO_COLOR_HIGHLIGHT
,
7280 eel_gdk_color_is_dark (>K_WIDGET (container
)->style
->base
[GTK_STATE_SELECTED
]) ? light_info_value
: dark_info_value
);
7281 setup_gc_with_fg (container
,
7282 LABEL_INFO_COLOR_ACTIVE
,
7283 eel_gdk_color_is_dark (>K_WIDGET (container
)->style
->base
[GTK_STATE_ACTIVE
]) ? light_info_value
: dark_info_value
);
7285 /* If NautilusIconContainer::frame_text is set, we can safely
7286 * use the foreground color from the theme, because it will
7287 * always be displayed against the gtk background */
7288 gtk_widget_style_get (widget
,
7289 "frame_text", &frame_text
,
7292 if (frame_text
|| !eel_background_is_set(background
)) {
7293 setup_gc_with_fg (container
, LABEL_COLOR
,
7294 eel_gdk_color_to_rgb (&widget
->style
->text
[GTK_STATE_NORMAL
]));
7295 setup_gc_with_fg (container
,
7297 eel_gdk_color_is_dark (&widget
->style
->base
[GTK_STATE_NORMAL
]) ? light_info_value
: dark_info_value
);
7299 if (container
->details
->use_drop_shadows
|| eel_background_is_dark (background
)) {
7300 setup_gc_with_fg (container
, LABEL_COLOR
, 0xEFEFEF);
7301 setup_gc_with_fg (container
,
7304 } else { /* converse */
7305 setup_gc_with_fg (container
, LABEL_COLOR
, 0x000000);
7306 setup_gc_with_fg (container
,
7314 update_label_color (EelBackground
*background
,
7315 NautilusIconContainer
*container
)
7317 g_assert (EEL_IS_BACKGROUND (background
));
7319 setup_label_gcs (container
);
7323 /* Return if the icon container is a fixed size */
7325 nautilus_icon_container_get_is_fixed_size (NautilusIconContainer
*container
)
7327 g_return_val_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
), FALSE
);
7329 return container
->details
->is_fixed_size
;
7332 /* Set the icon container to be a fixed size */
7334 nautilus_icon_container_set_is_fixed_size (NautilusIconContainer
*container
,
7335 gboolean is_fixed_size
)
7337 g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
));
7339 container
->details
->is_fixed_size
= is_fixed_size
;
7343 nautilus_icon_container_get_is_desktop (NautilusIconContainer
*container
)
7345 g_return_val_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
), FALSE
);
7347 return container
->details
->is_desktop
;
7351 nautilus_icon_container_set_is_desktop (NautilusIconContainer
*container
,
7352 gboolean is_desktop
)
7354 g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
));
7356 container
->details
->is_desktop
= is_desktop
;
7360 nautilus_icon_container_set_margins (NautilusIconContainer
*container
,
7366 g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
));
7368 container
->details
->left_margin
= left_margin
;
7369 container
->details
->right_margin
= right_margin
;
7370 container
->details
->top_margin
= top_margin
;
7371 container
->details
->bottom_margin
= bottom_margin
;
7373 /* redo layout of icons as the margins have changed */
7374 schedule_redo_layout (container
);
7378 nautilus_icon_container_set_use_drop_shadows (NautilusIconContainer
*container
,
7379 gboolean use_drop_shadows
)
7381 gboolean frame_text
;
7383 gtk_widget_style_get (GTK_WIDGET (container
),
7384 "frame_text", &frame_text
,
7387 if (container
->details
->drop_shadows_requested
== use_drop_shadows
) {
7391 container
->details
->drop_shadows_requested
= use_drop_shadows
;
7392 container
->details
->use_drop_shadows
= use_drop_shadows
&& !frame_text
;
7393 gtk_widget_queue_draw (GTK_WIDGET (container
));
7396 /* handle theme changes */
7399 nautilus_icon_container_theme_changed (gpointer user_data
)
7401 NautilusIconContainer
*container
;
7403 GdkColor
*prelight_icon_color
, *normal_icon_color
;
7404 guchar highlight_alpha
, normal_alpha
, prelight_alpha
;
7406 container
= NAUTILUS_ICON_CONTAINER (user_data
);
7408 /* load the highlight color */
7409 gtk_widget_style_get (GTK_WIDGET (container
),
7410 "highlight_alpha", &highlight_alpha
,
7413 style
= GTK_WIDGET (container
)->style
;
7415 container
->details
->highlight_color_rgba
=
7416 EEL_RGBA_COLOR_PACK (style
->base
[GTK_STATE_SELECTED
].red
>> 8,
7417 style
->base
[GTK_STATE_SELECTED
].green
>> 8,
7418 style
->base
[GTK_STATE_SELECTED
].blue
>> 8,
7420 container
->details
->active_color_rgba
=
7421 EEL_RGBA_COLOR_PACK (style
->base
[GTK_STATE_ACTIVE
].red
>> 8,
7422 style
->base
[GTK_STATE_ACTIVE
].green
>> 8,
7423 style
->base
[GTK_STATE_ACTIVE
].blue
>> 8,
7426 /* load the prelight icon color */
7427 gtk_widget_style_get (GTK_WIDGET (container
),
7428 "prelight_icon_color", &prelight_icon_color
,
7431 if (prelight_icon_color
) {
7432 container
->details
->prelight_icon_color_rgba
=
7433 EEL_RGBA_COLOR_PACK (prelight_icon_color
->red
>> 8,
7434 prelight_icon_color
->green
>> 8,
7435 prelight_icon_color
->blue
>> 8,
7437 } else { /* if not defined by rc, set to default value */
7438 container
->details
->prelight_icon_color_rgba
=
7439 EEL_RGBA_COLOR_PACK (style
->base
[GTK_STATE_PRELIGHT
].red
>> 8,
7440 style
->base
[GTK_STATE_PRELIGHT
].green
>> 8,
7441 style
->base
[GTK_STATE_PRELIGHT
].blue
>> 8,
7446 /* load the normal icon color */
7447 gtk_widget_style_get (GTK_WIDGET (container
),
7448 "normal_icon_color", &normal_icon_color
,
7451 if (normal_icon_color
) {
7452 container
->details
->normal_icon_color_rgba
=
7453 EEL_RGBA_COLOR_PACK (normal_icon_color
->red
>> 8,
7454 normal_icon_color
->green
>> 8,
7455 normal_icon_color
->blue
>> 8,
7457 } else { /* if not defined by rc, set to default value */
7458 container
->details
->normal_icon_color_rgba
=
7459 EEL_RGBA_COLOR_PACK (style
->base
[GTK_STATE_NORMAL
].red
>> 8,
7460 style
->base
[GTK_STATE_NORMAL
].green
>> 8,
7461 style
->base
[GTK_STATE_NORMAL
].blue
>> 8,
7466 /* load the normal color */
7467 gtk_widget_style_get (GTK_WIDGET (container
),
7468 "normal_alpha", &normal_alpha
,
7471 container
->details
->normal_color_rgba
=
7472 EEL_RGBA_COLOR_PACK (style
->base
[GTK_STATE_NORMAL
].red
>> 8,
7473 style
->base
[GTK_STATE_NORMAL
].green
>> 8,
7474 style
->base
[GTK_STATE_NORMAL
].blue
>> 8,
7478 /* load the prelight color */
7479 gtk_widget_style_get (GTK_WIDGET (container
),
7480 "prelight_alpha", &prelight_alpha
,
7483 container
->details
->prelight_color_rgba
=
7484 EEL_RGBA_COLOR_PACK (style
->base
[GTK_STATE_PRELIGHT
].red
>> 8,
7485 style
->base
[GTK_STATE_PRELIGHT
].green
>> 8,
7486 style
->base
[GTK_STATE_PRELIGHT
].blue
>> 8,
7490 setup_label_gcs (container
);
7494 nautilus_icon_container_set_font (NautilusIconContainer
*container
,
7497 g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
));
7499 if (eel_strcmp (container
->details
->font
, font
) == 0) {
7503 g_free (container
->details
->font
);
7504 container
->details
->font
= g_strdup (font
);
7506 invalidate_label_sizes (container
);
7507 nautilus_icon_container_request_update_all (container
);
7508 gtk_widget_queue_draw (GTK_WIDGET (container
));
7512 nautilus_icon_container_set_font_size_table (NautilusIconContainer
*container
,
7513 const int font_size_table
[NAUTILUS_ZOOM_LEVEL_LARGEST
+ 1])
7518 g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
));
7519 g_return_if_fail (font_size_table
!= NULL
);
7521 old_font_size
= container
->details
->font_size_table
[container
->details
->zoom_level
];
7523 for (i
= 0; i
<= NAUTILUS_ZOOM_LEVEL_LARGEST
; i
++) {
7524 if (container
->details
->font_size_table
[i
] != font_size_table
[i
]) {
7525 container
->details
->font_size_table
[i
] = font_size_table
[i
];
7529 if (old_font_size
!= container
->details
->font_size_table
[container
->details
->zoom_level
]) {
7530 invalidate_label_sizes (container
);
7531 nautilus_icon_container_request_update_all (container
);
7536 * nautilus_icon_container_get_icon_description
7537 * @container: An icon container widget.
7540 * Gets the description for the icon. This function may return NULL.
7543 nautilus_icon_container_get_icon_description (NautilusIconContainer
*container
,
7544 NautilusIconData
*data
)
7546 NautilusIconContainerClass
*klass
;
7548 klass
= NAUTILUS_ICON_CONTAINER_GET_CLASS (container
);
7550 if (klass
->get_icon_description
) {
7551 return klass
->get_icon_description (container
, data
);
7558 nautilus_icon_container_get_allow_moves (NautilusIconContainer
*container
)
7560 g_return_val_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
), FALSE
);
7562 return container
->details
->drag_allow_moves
;
7566 nautilus_icon_container_set_allow_moves (NautilusIconContainer
*container
,
7567 gboolean allow_moves
)
7569 g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
));
7571 container
->details
->drag_allow_moves
= allow_moves
;
7574 /* NautilusIconContainerAccessible */
7576 static NautilusIconContainerAccessiblePrivate
*
7577 accessible_get_priv (AtkObject
*accessible
)
7579 NautilusIconContainerAccessiblePrivate
*priv
;
7581 priv
= g_object_get_qdata (G_OBJECT (accessible
),
7582 accessible_private_data_quark
);
7587 /* AtkAction interface */
7590 nautilus_icon_container_accessible_do_action (AtkAction
*accessible
, int i
)
7593 NautilusIconContainer
*container
;
7596 g_return_val_if_fail (i
< LAST_ACTION
, FALSE
);
7598 widget
= GTK_ACCESSIBLE (accessible
)->widget
;
7603 container
= NAUTILUS_ICON_CONTAINER (widget
);
7605 case ACTION_ACTIVATE
:
7606 selection
= nautilus_icon_container_get_selection (container
);
7609 g_signal_emit_by_name (container
, "activate", selection
);
7610 g_list_free (selection
);
7614 handle_popups (container
, NULL
,"context_click_background");
7617 g_warning ("Invalid action passed to NautilusIconContainerAccessible::do_action");
7624 nautilus_icon_container_accessible_get_n_actions (AtkAction
*accessible
)
7630 nautilus_icon_container_accessible_action_get_description (AtkAction
*accessible
,
7633 NautilusIconContainerAccessiblePrivate
*priv
;
7635 g_return_val_if_fail (i
< LAST_ACTION
, NULL
);
7637 priv
= accessible_get_priv (ATK_OBJECT (accessible
));
7639 if (priv
->action_descriptions
[i
]) {
7640 return priv
->action_descriptions
[i
];
7642 return nautilus_icon_container_accessible_action_descriptions
[i
];
7647 nautilus_icon_container_accessible_action_get_name (AtkAction
*accessible
, int i
)
7649 g_return_val_if_fail (i
< LAST_ACTION
, NULL
);
7651 return nautilus_icon_container_accessible_action_names
[i
];
7655 nautilus_icon_container_accessible_action_get_keybinding (AtkAction
*accessible
,
7658 g_return_val_if_fail (i
< LAST_ACTION
, NULL
);
7664 nautilus_icon_container_accessible_action_set_description (AtkAction
*accessible
,
7666 const char *description
)
7668 NautilusIconContainerAccessiblePrivate
*priv
;
7670 g_return_val_if_fail (i
< LAST_ACTION
, FALSE
);
7672 priv
= accessible_get_priv (ATK_OBJECT (accessible
));
7674 if (priv
->action_descriptions
[i
]) {
7675 g_free (priv
->action_descriptions
[i
]);
7677 priv
->action_descriptions
[i
] = g_strdup (description
);
7683 nautilus_icon_container_accessible_action_interface_init (AtkActionIface
*iface
)
7685 iface
->do_action
= nautilus_icon_container_accessible_do_action
;
7686 iface
->get_n_actions
= nautilus_icon_container_accessible_get_n_actions
;
7687 iface
->get_description
= nautilus_icon_container_accessible_action_get_description
;
7688 iface
->get_name
= nautilus_icon_container_accessible_action_get_name
;
7689 iface
->get_keybinding
= nautilus_icon_container_accessible_action_get_keybinding
;
7690 iface
->set_description
= nautilus_icon_container_accessible_action_set_description
;
7693 /* AtkSelection interface */
7696 nautilus_icon_container_accessible_update_selection (AtkObject
*accessible
)
7698 NautilusIconContainer
*container
;
7699 NautilusIconContainerAccessiblePrivate
*priv
;
7703 container
= NAUTILUS_ICON_CONTAINER (GTK_ACCESSIBLE (accessible
)->widget
);
7705 priv
= accessible_get_priv (accessible
);
7707 if (priv
->selection
) {
7708 g_list_free (priv
->selection
);
7709 priv
->selection
= NULL
;
7712 for (l
= container
->details
->icons
; l
!= NULL
; l
= l
->next
) {
7714 if (icon
->is_selected
) {
7715 priv
->selection
= g_list_prepend (priv
->selection
,
7720 priv
->selection
= g_list_reverse (priv
->selection
);
7724 nautilus_icon_container_accessible_selection_changed_cb (NautilusIconContainer
*container
,
7727 g_signal_emit_by_name (data
, "selection_changed");
7731 nautilus_icon_container_accessible_icon_added_cb (NautilusIconContainer
*container
,
7732 NautilusIconData
*icon_data
,
7736 AtkObject
*atk_parent
;
7737 AtkObject
*atk_child
;
7740 icon
= g_hash_table_lookup (container
->details
->icon_set
, icon_data
);
7742 atk_parent
= ATK_OBJECT (data
);
7743 atk_child
= atk_gobject_accessible_for_object
7744 (G_OBJECT (icon
->item
));
7745 index
= g_list_index (container
->details
->icons
, icon
);
7747 g_signal_emit_by_name (atk_parent
, "children_changed::add",
7748 index
, atk_child
, NULL
);
7753 nautilus_icon_container_accessible_icon_removed_cb (NautilusIconContainer
*container
,
7754 NautilusIconData
*icon_data
,
7758 AtkObject
*atk_parent
;
7759 AtkObject
*atk_child
;
7762 icon
= g_hash_table_lookup (container
->details
->icon_set
, icon_data
);
7764 atk_parent
= ATK_OBJECT (data
);
7765 atk_child
= atk_gobject_accessible_for_object
7766 (G_OBJECT (icon
->item
));
7767 index
= g_list_index (container
->details
->icons
, icon
);
7769 g_signal_emit_by_name (atk_parent
, "children_changed::remove",
7770 index
, atk_child
, NULL
);
7775 nautilus_icon_container_accessible_cleared_cb (NautilusIconContainer
*container
,
7778 g_signal_emit_by_name (data
, "children_changed", 0, NULL
, NULL
);
7783 nautilus_icon_container_accessible_add_selection (AtkSelection
*accessible
,
7787 NautilusIconContainer
*container
;
7792 widget
= GTK_ACCESSIBLE (accessible
)->widget
;
7797 container
= NAUTILUS_ICON_CONTAINER (widget
);
7799 l
= g_list_nth (container
->details
->icons
, i
);
7803 selection
= nautilus_icon_container_get_selection (container
);
7804 selection
= g_list_prepend (selection
,
7806 nautilus_icon_container_set_selection (container
, selection
);
7808 g_list_free (selection
);
7816 nautilus_icon_container_accessible_clear_selection (AtkSelection
*accessible
)
7819 NautilusIconContainer
*container
;
7821 widget
= GTK_ACCESSIBLE (accessible
)->widget
;
7826 container
= NAUTILUS_ICON_CONTAINER (widget
);
7828 nautilus_icon_container_unselect_all (container
);
7834 nautilus_icon_container_accessible_ref_selection (AtkSelection
*accessible
,
7837 AtkObject
*atk_object
;
7838 NautilusIconContainerAccessiblePrivate
*priv
;
7842 nautilus_icon_container_accessible_update_selection (ATK_OBJECT (accessible
));
7843 priv
= accessible_get_priv (ATK_OBJECT (accessible
));
7845 item
= (g_list_nth (priv
->selection
, i
));
7849 atk_object
= atk_gobject_accessible_for_object (G_OBJECT (icon
->item
));
7851 g_object_ref (atk_object
);
7861 nautilus_icon_container_accessible_get_selection_count (AtkSelection
*accessible
)
7864 NautilusIconContainerAccessiblePrivate
*priv
;
7866 nautilus_icon_container_accessible_update_selection (ATK_OBJECT (accessible
));
7867 priv
= accessible_get_priv (ATK_OBJECT (accessible
));
7869 count
= g_list_length (priv
->selection
);
7875 nautilus_icon_container_accessible_is_child_selected (AtkSelection
*accessible
,
7878 NautilusIconContainer
*container
;
7883 widget
= GTK_ACCESSIBLE (accessible
)->widget
;
7888 container
= NAUTILUS_ICON_CONTAINER (widget
);
7890 l
= g_list_nth (container
->details
->icons
, i
);
7893 return icon
->is_selected
;
7899 nautilus_icon_container_accessible_remove_selection (AtkSelection
*accessible
,
7902 NautilusIconContainer
*container
;
7903 NautilusIconContainerAccessiblePrivate
*priv
;
7909 widget
= GTK_ACCESSIBLE (accessible
)->widget
;
7914 nautilus_icon_container_accessible_update_selection (ATK_OBJECT (accessible
));
7915 priv
= accessible_get_priv (ATK_OBJECT (accessible
));
7917 container
= NAUTILUS_ICON_CONTAINER (widget
);
7919 l
= g_list_nth (priv
->selection
, i
);
7923 selection
= nautilus_icon_container_get_selection (container
);
7924 selection
= g_list_remove (selection
, icon
->data
);
7925 nautilus_icon_container_set_selection (container
, selection
);
7927 g_list_free (selection
);
7935 nautilus_icon_container_accessible_select_all_selection (AtkSelection
*accessible
)
7937 NautilusIconContainer
*container
;
7940 widget
= GTK_ACCESSIBLE (accessible
)->widget
;
7945 container
= NAUTILUS_ICON_CONTAINER (widget
);
7947 nautilus_icon_container_select_all (container
);
7953 nautilus_icon_container_widget_to_file_operation_position (NautilusIconContainer
*container
,
7958 g_return_if_fail (position
!= NULL
);
7963 eel_canvas_window_to_world (EEL_CANVAS (container
), x
, y
, &x
, &y
);
7965 position
->x
= (int) x
;
7966 position
->y
= (int) y
;
7968 /* ensure that we end up in the middle of the icon */
7969 position
->x
-= nautilus_get_icon_size_for_zoom_level (container
->details
->zoom_level
) / 2;
7970 position
->y
-= nautilus_get_icon_size_for_zoom_level (container
->details
->zoom_level
) / 2;
7974 nautilus_icon_container_accessible_selection_interface_init (AtkSelectionIface
*iface
)
7976 iface
->add_selection
= nautilus_icon_container_accessible_add_selection
;
7977 iface
->clear_selection
= nautilus_icon_container_accessible_clear_selection
;
7978 iface
->ref_selection
= nautilus_icon_container_accessible_ref_selection
;
7979 iface
->get_selection_count
= nautilus_icon_container_accessible_get_selection_count
;
7980 iface
->is_child_selected
= nautilus_icon_container_accessible_is_child_selected
;
7981 iface
->remove_selection
= nautilus_icon_container_accessible_remove_selection
;
7982 iface
->select_all_selection
= nautilus_icon_container_accessible_select_all_selection
;
7987 nautilus_icon_container_accessible_get_n_children (AtkObject
*accessible
)
7989 NautilusIconContainer
*container
;
7993 widget
= GTK_ACCESSIBLE (accessible
)->widget
;
7998 container
= NAUTILUS_ICON_CONTAINER (widget
);
8000 i
= g_hash_table_size (container
->details
->icon_set
);
8001 if (container
->details
->rename_widget
) {
8008 nautilus_icon_container_accessible_ref_child (AtkObject
*accessible
, int i
)
8010 AtkObject
*atk_object
;
8011 NautilusIconContainer
*container
;
8016 widget
= GTK_ACCESSIBLE (accessible
)->widget
;
8021 container
= NAUTILUS_ICON_CONTAINER (widget
);
8023 item
= (g_list_nth (container
->details
->icons
, i
));
8028 atk_object
= atk_gobject_accessible_for_object (G_OBJECT (icon
->item
));
8029 g_object_ref (atk_object
);
8033 if (i
== g_list_length (container
->details
->icons
)) {
8034 if (container
->details
->rename_widget
) {
8035 atk_object
= gtk_widget_get_accessible (container
->details
->rename_widget
);
8036 g_object_ref (atk_object
);
8046 nautilus_icon_container_accessible_initialize (AtkObject
*accessible
,
8049 NautilusIconContainer
*container
;
8050 NautilusIconContainerAccessiblePrivate
*priv
;
8052 if (ATK_OBJECT_CLASS (accessible_parent_class
)->initialize
) {
8053 ATK_OBJECT_CLASS (accessible_parent_class
)->initialize (accessible
, data
);
8056 priv
= g_new0 (NautilusIconContainerAccessiblePrivate
, 1);
8057 g_object_set_qdata (G_OBJECT (accessible
),
8058 accessible_private_data_quark
,
8061 if (GTK_IS_ACCESSIBLE (accessible
)) {
8062 nautilus_icon_container_accessible_update_selection
8063 (ATK_OBJECT (accessible
));
8065 container
= NAUTILUS_ICON_CONTAINER (GTK_ACCESSIBLE (accessible
)->widget
);
8066 g_signal_connect (G_OBJECT (container
), "selection_changed",
8067 G_CALLBACK (nautilus_icon_container_accessible_selection_changed_cb
),
8069 g_signal_connect (G_OBJECT (container
), "icon_added",
8070 G_CALLBACK (nautilus_icon_container_accessible_icon_added_cb
),
8072 g_signal_connect (G_OBJECT (container
), "icon_removed",
8073 G_CALLBACK (nautilus_icon_container_accessible_icon_removed_cb
),
8075 g_signal_connect (G_OBJECT (container
), "cleared",
8076 G_CALLBACK (nautilus_icon_container_accessible_cleared_cb
),
8082 nautilus_icon_container_accessible_finalize (GObject
*object
)
8084 NautilusIconContainerAccessiblePrivate
*priv
;
8087 priv
= accessible_get_priv (ATK_OBJECT (object
));
8088 if (priv
->selection
) {
8089 g_list_free (priv
->selection
);
8092 for (i
= 0; i
< LAST_ACTION
; i
++) {
8093 if (priv
->action_descriptions
[i
]) {
8094 g_free (priv
->action_descriptions
[i
]);
8100 G_OBJECT_CLASS (accessible_parent_class
)->finalize (object
);
8104 nautilus_icon_container_accessible_class_init (AtkObjectClass
*klass
)
8106 GObjectClass
*gobject_class
= G_OBJECT_CLASS (klass
);
8108 accessible_parent_class
= g_type_class_peek_parent (klass
);
8110 gobject_class
->finalize
= nautilus_icon_container_accessible_finalize
;
8112 klass
->get_n_children
= nautilus_icon_container_accessible_get_n_children
;
8113 klass
->ref_child
= nautilus_icon_container_accessible_ref_child
;
8114 klass
->initialize
= nautilus_icon_container_accessible_initialize
;
8116 accessible_private_data_quark
= g_quark_from_static_string ("icon-container-accessible-private-data");
8120 nautilus_icon_container_accessible_get_type (void)
8122 static GType type
= 0;
8125 static GInterfaceInfo atk_action_info
= {
8126 (GInterfaceInitFunc
) nautilus_icon_container_accessible_action_interface_init
,
8127 (GInterfaceFinalizeFunc
) NULL
,
8131 static GInterfaceInfo atk_selection_info
= {
8132 (GInterfaceInitFunc
) nautilus_icon_container_accessible_selection_interface_init
,
8133 (GInterfaceFinalizeFunc
) NULL
,
8137 type
= eel_accessibility_create_derived_type
8138 ("NautilusIconContainerAccessible",
8140 nautilus_icon_container_accessible_class_init
);
8142 g_type_add_interface_static (type
, ATK_TYPE_ACTION
,
8144 g_type_add_interface_static (type
, ATK_TYPE_SELECTION
,
8145 &atk_selection_info
);
8151 #if ! defined (NAUTILUS_OMIT_SELF_CHECK)
8154 check_compute_stretch (int icon_x
, int icon_y
, int icon_size
,
8155 int start_pointer_x
, int start_pointer_y
,
8156 int end_pointer_x
, int end_pointer_y
)
8158 StretchState start
, current
;
8160 start
.icon_x
= icon_x
;
8161 start
.icon_y
= icon_y
;
8162 start
.icon_size
= icon_size
;
8163 start
.pointer_x
= start_pointer_x
;
8164 start
.pointer_y
= start_pointer_y
;
8165 current
.pointer_x
= end_pointer_x
;
8166 current
.pointer_y
= end_pointer_y
;
8168 compute_stretch (&start
, ¤t
);
8170 return g_strdup_printf ("%d,%d:%d",
8177 nautilus_self_check_icon_container (void)
8179 EEL_CHECK_STRING_RESULT (check_compute_stretch (0, 0, 16, 0, 0, 0, 0), "0,0:16");
8180 EEL_CHECK_STRING_RESULT (check_compute_stretch (0, 0, 16, 16, 16, 17, 17), "0,0:17");
8181 EEL_CHECK_STRING_RESULT (check_compute_stretch (0, 0, 16, 16, 16, 17, 16), "0,0:16");
8182 EEL_CHECK_STRING_RESULT (check_compute_stretch (100, 100, 64, 105, 105, 40, 40), "35,35:129");
8186 nautilus_icon_container_is_layout_rtl (NautilusIconContainer
*container
)
8188 g_return_val_if_fail (NAUTILUS_IS_ICON_CONTAINER (container
), 0);
8190 return container
->details
->layout_mode
== NAUTILUS_ICON_LAYOUT_T_B_R_L
||
8191 container
->details
->layout_mode
== NAUTILUS_ICON_LAYOUT_R_L_T_B
;
8194 #endif /* ! NAUTILUS_OMIT_SELF_CHECK */