~ubuntu-branches/ubuntu/oneiric/nautilus/oneiric-proposed

« back to all changes in this revision

Viewing changes to .pc/git-0001-Workaround-focus-issues-on-typeahead.patch/libnautilus-private/nautilus-icon-container.c

  • Committer: Package Import Robot
  • Author(s): Didier Roche, Chow Loong Jin, Dmitry Shachnev, Didier Roche
  • Date: 2011-11-10 09:37:51 UTC
  • Revision ID: package-import@ubuntu.com-20111110093751-codkce7esfqkrt50
Tags: 1:3.2.1-0ubuntu3.1
[ Chow Loong Jin ]
* Add upstream patch to workaround focus issues on typeahead:
  - debian/patches/git-0001-Workaround-focus-issues-on-typeahead.patch
  (LP: #879456)

[ Dmitry Shachnev ]
 * debian/patches/05_desktop_menu_export.patch (updated):
  - Stop hiding actions that do not exist anymore (LP: #880938)

[ Didier Roche ]
* Remove duplicate files icon, still workarounding missing desktop file:
  (LP: #886428)
  - removed debian/nautilus.links
  - ship debian/nautilus-folder-handler.desktop, copy of nautilus.desktop
    with NoDisplay=true
  - debian/nautilus.install: install the new file
* debian/patches/11_copy_skipping_pager.patch:
  - only skip pager for the unity session (LP: #868032)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
 
2
 
 
3
/* nautilus-icon-container.c - Icon container widget.
 
4
 
 
5
   Copyright (C) 1999, 2000 Free Software Foundation
 
6
   Copyright (C) 2000, 2001 Eazel, Inc.
 
7
   Copyright (C) 2002, 2003 Red Hat, Inc.
 
8
   
 
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.
 
13
 
 
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.
 
18
 
 
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.
 
23
 
 
24
   Authors: Ettore Perazzoli <ettore@gnu.org>,
 
25
   Darin Adler <darin@bentspoon.com>
 
26
*/
 
27
 
 
28
#include <config.h>
 
29
#include <math.h>
 
30
#include "nautilus-icon-container.h"
 
31
 
 
32
#include "nautilus-global-preferences.h"
 
33
#include "nautilus-icon-private.h"
 
34
#include "nautilus-lib-self-check-functions.h"
 
35
#include "nautilus-selection-canvas-item.h"
 
36
#include <atk/atkaction.h>
 
37
#include <eel/eel-accessibility.h>
 
38
#include <eel/eel-vfs-extensions.h>
 
39
#include <eel/eel-gtk-extensions.h>
 
40
#include <eel/eel-art-extensions.h>
 
41
#include <eel/eel-editable-label.h>
 
42
 
 
43
#include <gdk/gdkkeysyms.h>
 
44
#include <gtk/gtk.h>
 
45
#include <gdk/gdkx.h>
 
46
#include <glib/gi18n.h>
 
47
#include <stdio.h>
 
48
#include <string.h>
 
49
 
 
50
#define DEBUG_FLAG NAUTILUS_DEBUG_ICON_CONTAINER
 
51
#include "nautilus-debug.h"
 
52
 
 
53
#define TAB_NAVIGATION_DISABLED
 
54
 
 
55
/* Interval for updating the rubberband selection, in milliseconds.  */
 
56
#define RUBBERBAND_TIMEOUT_INTERVAL 10
 
57
 
 
58
/* Initial unpositioned icon value */
 
59
#define ICON_UNPOSITIONED_VALUE -1
 
60
 
 
61
/* Timeout for making the icon currently selected for keyboard operation visible.
 
62
 * If this is 0, you can get into trouble with extra scrolling after holding
 
63
 * down the arrow key for awhile when there are many items.
 
64
 */
 
65
#define KEYBOARD_ICON_REVEAL_TIMEOUT 10
 
66
 
 
67
#define CONTEXT_MENU_TIMEOUT_INTERVAL 500
 
68
 
 
69
/* Maximum amount of milliseconds the mouse button is allowed to stay down
 
70
 * and still be considered a click.
 
71
 */
 
72
#define MAX_CLICK_TIME 1500
 
73
 
 
74
/* Button assignments. */
 
75
#define DRAG_BUTTON 1
 
76
#define RUBBERBAND_BUTTON 1
 
77
#define MIDDLE_BUTTON 2
 
78
#define CONTEXTUAL_MENU_BUTTON 3
 
79
#define DRAG_MENU_BUTTON 2
 
80
 
 
81
/* Maximum size (pixels) allowed for icons at the standard zoom level. */
 
82
#define MINIMUM_IMAGE_SIZE 24
 
83
#define MAXIMUM_IMAGE_SIZE 96
 
84
 
 
85
#define ICON_PAD_LEFT 4
 
86
#define ICON_PAD_RIGHT 4
 
87
#define ICON_BASE_WIDTH 96
 
88
 
 
89
#define ICON_PAD_TOP 4
 
90
#define ICON_PAD_BOTTOM 4
 
91
 
 
92
#define CONTAINER_PAD_LEFT 4
 
93
#define CONTAINER_PAD_RIGHT 4
 
94
#define CONTAINER_PAD_TOP 4
 
95
#define CONTAINER_PAD_BOTTOM 4
 
96
 
 
97
#define STANDARD_ICON_GRID_WIDTH 155
 
98
 
 
99
#define TEXT_BESIDE_ICON_GRID_WIDTH 205
 
100
 
 
101
/* Desktop layout mode defines */
 
102
#define DESKTOP_PAD_HORIZONTAL  10
 
103
#define DESKTOP_PAD_VERTICAL    10
 
104
#define SNAP_SIZE_X             78
 
105
#define SNAP_SIZE_Y             20
 
106
 
 
107
#define MINIMUM_EMBEDDED_TEXT_RECT_WIDTH       20
 
108
#define MINIMUM_EMBEDDED_TEXT_RECT_HEIGHT      20
 
109
 
 
110
/* If icon size is bigger than this, request large embedded text.
 
111
 * Its selected so that the non-large text should fit in "normal" icon sizes
 
112
 */
 
113
#define ICON_SIZE_FOR_LARGE_EMBEDDED_TEXT 55
 
114
 
 
115
/* From nautilus-icon-canvas-item.c */
 
116
#define MAX_TEXT_WIDTH_BESIDE 90
 
117
 
 
118
#define SNAP_HORIZONTAL(func,x) ((func ((double)((x) - DESKTOP_PAD_HORIZONTAL) / SNAP_SIZE_X) * SNAP_SIZE_X) + DESKTOP_PAD_HORIZONTAL)
 
119
#define SNAP_VERTICAL(func, y) ((func ((double)((y) - DESKTOP_PAD_VERTICAL) / SNAP_SIZE_Y) * SNAP_SIZE_Y) + DESKTOP_PAD_VERTICAL)
 
120
 
 
121
#define SNAP_NEAREST_HORIZONTAL(x) SNAP_HORIZONTAL (eel_round, x)
 
122
#define SNAP_NEAREST_VERTICAL(y) SNAP_VERTICAL (eel_round, y)
 
123
 
 
124
#define SNAP_CEIL_HORIZONTAL(x) SNAP_HORIZONTAL (ceil, x)
 
125
#define SNAP_CEIL_VERTICAL(y) SNAP_VERTICAL (ceil, y)
 
126
 
 
127
/* Copied from NautilusIconContainer */
 
128
#define NAUTILUS_ICON_CONTAINER_SEARCH_DIALOG_TIMEOUT 5
 
129
 
 
130
/* Copied from NautilusFile */
 
131
#define UNDEFINED_TIME ((time_t) (-1))
 
132
 
 
133
enum {
 
134
        ACTION_ACTIVATE,
 
135
        ACTION_MENU,
 
136
        LAST_ACTION
 
137
};
 
138
 
 
139
typedef struct {
 
140
        GList *selection;
 
141
        char *action_descriptions[LAST_ACTION];
 
142
} NautilusIconContainerAccessiblePrivate;
 
143
 
 
144
static GType         nautilus_icon_container_accessible_get_type (void);
 
145
 
 
146
static void          preview_selected_items                         (NautilusIconContainer *container);
 
147
static void          activate_selected_items                        (NautilusIconContainer *container);
 
148
static void          activate_selected_items_alternate              (NautilusIconContainer *container,
 
149
                                                                     NautilusIcon          *icon);
 
150
static void          compute_stretch                                (StretchState          *start,
 
151
                                                                     StretchState          *current);
 
152
static NautilusIcon *get_first_selected_icon                        (NautilusIconContainer *container);
 
153
static NautilusIcon *get_nth_selected_icon                          (NautilusIconContainer *container,
 
154
                                                                     int                    index);
 
155
static gboolean      has_multiple_selection                         (NautilusIconContainer *container);
 
156
static gboolean      all_selected                                   (NautilusIconContainer *container);
 
157
static gboolean      has_selection                                  (NautilusIconContainer *container);
 
158
static void          icon_destroy                                   (NautilusIconContainer *container,
 
159
                                                                     NautilusIcon          *icon);
 
160
static void          end_renaming_mode                              (NautilusIconContainer *container,
 
161
                                                                     gboolean               commit);
 
162
static NautilusIcon *get_icon_being_renamed                         (NautilusIconContainer *container);
 
163
static void          finish_adding_new_icons                        (NautilusIconContainer *container);
 
164
static inline void   icon_get_bounding_box                          (NautilusIcon          *icon,
 
165
                                                                     int                   *x1_return,
 
166
                                                                     int                   *y1_return,
 
167
                                                                     int                   *x2_return,
 
168
                                                                     int                   *y2_return,
 
169
                                                                     NautilusIconCanvasItemBoundsUsage usage);
 
170
static gboolean      is_renaming                                    (NautilusIconContainer *container);
 
171
static gboolean      is_renaming_pending                            (NautilusIconContainer *container);
 
172
static void          process_pending_icon_to_rename                 (NautilusIconContainer *container);
 
173
static void          nautilus_icon_container_stop_monitor_top_left  (NautilusIconContainer *container,
 
174
                                                                     NautilusIconData      *data,
 
175
                                                                     gconstpointer          client);
 
176
static void          nautilus_icon_container_start_monitor_top_left (NautilusIconContainer *container,
 
177
                                                                     NautilusIconData      *data,
 
178
                                                                     gconstpointer          client,
 
179
                                                                     gboolean               large_text);
 
180
static void          handle_hadjustment_changed                     (GtkAdjustment         *adjustment,
 
181
                                                                     NautilusIconContainer *container);
 
182
static void          handle_vadjustment_changed                     (GtkAdjustment         *adjustment,
 
183
                                                                     NautilusIconContainer *container);
 
184
static GList *       nautilus_icon_container_get_selected_icons (NautilusIconContainer *container);
 
185
static void          nautilus_icon_container_update_visible_icons   (NautilusIconContainer *container);
 
186
static void          reveal_icon                                    (NautilusIconContainer *container,
 
187
                                                                     NautilusIcon *icon);
 
188
 
 
189
static void          nautilus_icon_container_set_rtl_positions (NautilusIconContainer *container);
 
190
static double        get_mirror_x_position                     (NautilusIconContainer *container,
 
191
                                                                NautilusIcon *icon,
 
192
                                                                double x);
 
193
static void         text_ellipsis_limit_changed_container_callback  (gpointer callback_data);
 
194
 
 
195
static int compare_icons_horizontal (NautilusIconContainer *container,
 
196
                                     NautilusIcon *icon_a,
 
197
                                     NautilusIcon *icon_b);
 
198
 
 
199
static int compare_icons_vertical (NautilusIconContainer *container,
 
200
                                   NautilusIcon *icon_a,
 
201
                                   NautilusIcon *icon_b);
 
202
 
 
203
static void store_layout_timestamps_now (NautilusIconContainer *container);
 
204
static void remove_search_entry_timeout (NautilusIconContainer *container);
 
205
 
 
206
static gpointer accessible_parent_class;
 
207
 
 
208
static GQuark accessible_private_data_quark = 0;
 
209
 
 
210
static const char *nautilus_icon_container_accessible_action_names[] = {
 
211
        "activate",
 
212
        "menu",
 
213
        NULL
 
214
};
 
215
 
 
216
static const char *nautilus_icon_container_accessible_action_descriptions[] = {
 
217
        "Activate selected items",
 
218
        "Popup context menu",
 
219
        NULL
 
220
};
 
221
 
 
222
G_DEFINE_TYPE (NautilusIconContainer, nautilus_icon_container, EEL_TYPE_CANVAS);
 
223
 
 
224
/* The NautilusIconContainer signals.  */
 
225
enum {
 
226
        ACTIVATE,
 
227
        ACTIVATE_ALTERNATE,
 
228
        ACTIVATE_PREVIEWER,
 
229
        BAND_SELECT_STARTED,
 
230
        BAND_SELECT_ENDED,
 
231
        BUTTON_PRESS,
 
232
        CAN_ACCEPT_ITEM,
 
233
        CONTEXT_CLICK_BACKGROUND,
 
234
        CONTEXT_CLICK_SELECTION,
 
235
        MIDDLE_CLICK,
 
236
        GET_CONTAINER_URI,
 
237
        GET_ICON_URI,
 
238
        GET_ICON_DROP_TARGET_URI,
 
239
        GET_STORED_ICON_POSITION,
 
240
        ICON_POSITION_CHANGED,
 
241
        GET_STORED_LAYOUT_TIMESTAMP,
 
242
        STORE_LAYOUT_TIMESTAMP,
 
243
        ICON_RENAME_STARTED,
 
244
        ICON_RENAME_ENDED,
 
245
        ICON_STRETCH_STARTED,
 
246
        ICON_STRETCH_ENDED,
 
247
        LAYOUT_CHANGED,
 
248
        MOVE_COPY_ITEMS,
 
249
        HANDLE_NETSCAPE_URL,
 
250
        HANDLE_URI_LIST,
 
251
        HANDLE_TEXT,
 
252
        HANDLE_RAW,
 
253
        SELECTION_CHANGED,
 
254
        ICON_ADDED,
 
255
        ICON_REMOVED,
 
256
        CLEARED,
 
257
        LAST_SIGNAL
 
258
};
 
259
 
 
260
typedef struct {
 
261
        int **icon_grid;
 
262
        int *grid_memory;
 
263
        int num_rows;
 
264
        int num_columns;
 
265
        gboolean tight;
 
266
} PlacementGrid;
 
267
 
 
268
static guint signals[LAST_SIGNAL];
 
269
 
 
270
/* Functions dealing with NautilusIcons.  */
 
271
 
 
272
static void
 
273
icon_free (NautilusIcon *icon)
 
274
{
 
275
        /* Destroy this canvas item; the parent will unref it. */
 
276
        eel_canvas_item_destroy (EEL_CANVAS_ITEM (icon->item));
 
277
        g_free (icon);
 
278
}
 
279
 
 
280
static gboolean
 
281
icon_is_positioned (const NautilusIcon *icon)
 
282
{
 
283
        return icon->x != ICON_UNPOSITIONED_VALUE && icon->y != ICON_UNPOSITIONED_VALUE;
 
284
}
 
285
 
 
286
 
 
287
/* x, y are the top-left coordinates of the icon. */
 
288
static void
 
289
icon_set_position (NautilusIcon *icon,
 
290
                   double x, double y)
 
291
{       
 
292
        NautilusIconContainer *container;
 
293
        double pixels_per_unit; 
 
294
        int container_left, container_top, container_right, container_bottom;
 
295
        int x1, x2, y1, y2;
 
296
        int container_x, container_y, container_width, container_height;
 
297
        EelDRect icon_bounds;
 
298
        int item_width, item_height;
 
299
        int height_above, width_left;
 
300
        int min_x, max_x, min_y, max_y;
 
301
 
 
302
        if (icon->x == x && icon->y == y) {
 
303
                return;
 
304
        }
 
305
 
 
306
        container = NAUTILUS_ICON_CONTAINER (EEL_CANVAS_ITEM (icon->item)->canvas);
 
307
 
 
308
        if (icon == get_icon_being_renamed (container)) {
 
309
                end_renaming_mode (container, TRUE);
 
310
        }
 
311
 
 
312
        if (nautilus_icon_container_get_is_fixed_size (container)) {
 
313
                /*  FIXME: This should be:
 
314
                    
 
315
                container_x = GTK_WIDGET (container)->allocation.x;
 
316
                container_y = GTK_WIDGET (container)->allocation.y;
 
317
                container_width = GTK_WIDGET (container)->allocation.width;
 
318
                container_height = GTK_WIDGET (container)->allocation.height;
 
319
 
 
320
                But for some reason the widget allocation is sometimes not done
 
321
                at startup, and the allocation is then only 45x60. which is
 
322
                really bad.
 
323
 
 
324
                For now, we have a cheesy workaround:
 
325
                */
 
326
                container_x = 0;
 
327
                container_y = 0;
 
328
                container_width = gdk_screen_width () - container_x
 
329
                        - container->details->left_margin
 
330
                        - container->details->right_margin;
 
331
                container_height = gdk_screen_height () - container_y
 
332
                        - container->details->top_margin
 
333
                        - container->details->bottom_margin;
 
334
                pixels_per_unit = EEL_CANVAS (container)->pixels_per_unit;
 
335
                /* Clip the position of the icon within our desktop bounds */
 
336
                container_left = container_x / pixels_per_unit;
 
337
                container_top =  container_y / pixels_per_unit;
 
338
                container_right = container_left + container_width / pixels_per_unit;
 
339
                container_bottom = container_top + container_height / pixels_per_unit;
 
340
 
 
341
                icon_get_bounding_box (icon, &x1, &y1, &x2, &y2,
 
342
                                       BOUNDS_USAGE_FOR_ENTIRE_ITEM);
 
343
                item_width = x2 - x1;
 
344
                item_height = y2 - y1;
 
345
 
 
346
                icon_bounds = nautilus_icon_canvas_item_get_icon_rectangle (icon->item);
 
347
 
 
348
                /* determine icon rectangle relative to item rectangle */
 
349
                height_above = icon_bounds.y0 - y1;
 
350
                width_left = icon_bounds.x0 - x1;
 
351
 
 
352
                min_x = container_left + DESKTOP_PAD_HORIZONTAL + width_left;
 
353
                max_x = container_right - DESKTOP_PAD_HORIZONTAL - item_width + width_left;
 
354
                x = CLAMP (x, min_x, max_x);
 
355
 
 
356
                min_y = container_top + height_above + DESKTOP_PAD_VERTICAL;
 
357
                max_y = container_bottom - DESKTOP_PAD_VERTICAL - item_height + height_above;
 
358
                y = CLAMP (y, min_y, max_y);
 
359
        }
 
360
 
 
361
        if (icon->x == ICON_UNPOSITIONED_VALUE) {
 
362
                icon->x = 0;
 
363
        }
 
364
        if (icon->y == ICON_UNPOSITIONED_VALUE) {
 
365
                icon->y = 0;
 
366
        }
 
367
        
 
368
        eel_canvas_item_move (EEL_CANVAS_ITEM (icon->item),
 
369
                                x - icon->x,
 
370
                                y - icon->y);
 
371
 
 
372
        icon->x = x;
 
373
        icon->y = y;
 
374
}
 
375
 
 
376
static void
 
377
icon_get_size (NautilusIconContainer *container,
 
378
               NautilusIcon *icon,
 
379
               guint *size)
 
380
{
 
381
        if (size != NULL) {
 
382
                *size = MAX (nautilus_get_icon_size_for_zoom_level (container->details->zoom_level)
 
383
                               * icon->scale, NAUTILUS_ICON_SIZE_SMALLEST);
 
384
        }
 
385
}
 
386
 
 
387
/* The icon_set_size function is used by the stretching user
 
388
 * interface, which currently stretches in a way that keeps the aspect
 
389
 * ratio. Later we might have a stretching interface that stretches Y
 
390
 * separate from X and we will change this around.
 
391
 */
 
392
static void
 
393
icon_set_size (NautilusIconContainer *container,
 
394
               NautilusIcon *icon,
 
395
               guint icon_size,
 
396
               gboolean snap,
 
397
               gboolean update_position)
 
398
{
 
399
        guint old_size;
 
400
        double scale;
 
401
 
 
402
        icon_get_size (container, icon, &old_size);
 
403
        if (icon_size == old_size) {
 
404
                return;
 
405
        }
 
406
 
 
407
        scale = (double) icon_size /
 
408
                nautilus_get_icon_size_for_zoom_level
 
409
                (container->details->zoom_level);
 
410
        nautilus_icon_container_move_icon (container, icon,
 
411
                                           icon->x, icon->y,
 
412
                                           scale, FALSE,
 
413
                                           snap, update_position);
 
414
}
 
415
 
 
416
static void
 
417
icon_raise (NautilusIcon *icon)
 
418
{
 
419
        EelCanvasItem *item, *band;
 
420
        
 
421
        item = EEL_CANVAS_ITEM (icon->item);
 
422
        band = NAUTILUS_ICON_CONTAINER (item->canvas)->details->rubberband_info.selection_rectangle;
 
423
        
 
424
        eel_canvas_item_send_behind (item, band);
 
425
}
 
426
 
 
427
static void
 
428
emit_stretch_started (NautilusIconContainer *container, NautilusIcon *icon)
 
429
{
 
430
        g_signal_emit (container,
 
431
                         signals[ICON_STRETCH_STARTED], 0,
 
432
                         icon->data);
 
433
}
 
434
 
 
435
static void
 
436
emit_stretch_ended (NautilusIconContainer *container, NautilusIcon *icon)
 
437
{
 
438
        g_signal_emit (container,
 
439
                         signals[ICON_STRETCH_ENDED], 0,
 
440
                         icon->data);
 
441
}
 
442
 
 
443
static void
 
444
icon_toggle_selected (NautilusIconContainer *container,
 
445
                      NautilusIcon *icon)
 
446
{               
 
447
        end_renaming_mode (container, TRUE);
 
448
 
 
449
        icon->is_selected = !icon->is_selected;
 
450
        eel_canvas_item_set (EEL_CANVAS_ITEM (icon->item),
 
451
                             "highlighted_for_selection", (gboolean) icon->is_selected,
 
452
                             NULL);
 
453
 
 
454
        /* If the icon is deselected, then get rid of the stretch handles.
 
455
         * No harm in doing the same if the item is newly selected.
 
456
         */
 
457
        if (icon == container->details->stretch_icon) {
 
458
                container->details->stretch_icon = NULL;
 
459
                nautilus_icon_canvas_item_set_show_stretch_handles (icon->item, FALSE);
 
460
                /* snap the icon if necessary */
 
461
                if (container->details->keep_aligned) {
 
462
                        nautilus_icon_container_move_icon (container,
 
463
                                                           icon,
 
464
                                                           icon->x, icon->y,
 
465
                                                           icon->scale,
 
466
                                                           FALSE, TRUE, TRUE);
 
467
                }
 
468
                
 
469
                emit_stretch_ended (container, icon);
 
470
        }
 
471
 
 
472
        /* Raise each newly-selected icon to the front as it is selected. */
 
473
        if (icon->is_selected) {
 
474
                icon_raise (icon);
 
475
        }
 
476
}
 
477
 
 
478
/* Select an icon. Return TRUE if selection has changed. */
 
479
static gboolean
 
480
icon_set_selected (NautilusIconContainer *container,
 
481
                   NautilusIcon *icon,
 
482
                   gboolean select)
 
483
{
 
484
        g_assert (select == FALSE || select == TRUE);
 
485
        g_assert (icon->is_selected == FALSE || icon->is_selected == TRUE);
 
486
 
 
487
        if (select == icon->is_selected) {
 
488
                return FALSE;
 
489
        }
 
490
 
 
491
        icon_toggle_selected (container, icon);
 
492
        g_assert (select == icon->is_selected);
 
493
        return TRUE;
 
494
}
 
495
 
 
496
static inline void
 
497
icon_get_bounding_box (NautilusIcon *icon,
 
498
                       int *x1_return, int *y1_return,
 
499
                       int *x2_return, int *y2_return,
 
500
                       NautilusIconCanvasItemBoundsUsage usage)
 
501
{
 
502
        double x1, y1, x2, y2;
 
503
 
 
504
        if (usage == BOUNDS_USAGE_FOR_DISPLAY) {
 
505
                eel_canvas_item_get_bounds (EEL_CANVAS_ITEM (icon->item),
 
506
                                            &x1, &y1, &x2, &y2);
 
507
        } else if (usage == BOUNDS_USAGE_FOR_LAYOUT) {
 
508
                nautilus_icon_canvas_item_get_bounds_for_layout (icon->item,
 
509
                                                                 &x1, &y1, &x2, &y2);
 
510
        } else if (usage == BOUNDS_USAGE_FOR_ENTIRE_ITEM) {
 
511
                nautilus_icon_canvas_item_get_bounds_for_entire_item (icon->item,
 
512
                                                                      &x1, &y1, &x2, &y2);
 
513
        } else {
 
514
                g_assert_not_reached ();
 
515
        }
 
516
 
 
517
        if (x1_return != NULL) {
 
518
                *x1_return = x1;
 
519
        }
 
520
 
 
521
        if (y1_return != NULL) {
 
522
                *y1_return = y1;
 
523
        }
 
524
 
 
525
        if (x2_return != NULL) {
 
526
                *x2_return = x2;
 
527
        }
 
528
 
 
529
        if (y2_return != NULL) {
 
530
                *y2_return = y2;
 
531
        }
 
532
}
 
533
 
 
534
/* Utility functions for NautilusIconContainer.  */
 
535
 
 
536
gboolean
 
537
nautilus_icon_container_scroll (NautilusIconContainer *container,
 
538
                                int delta_x, int delta_y)
 
539
{
 
540
        GtkAdjustment *hadj, *vadj;
 
541
        int old_h_value, old_v_value;
 
542
 
 
543
        hadj = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (container));
 
544
        vadj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (container));
 
545
 
 
546
        /* Store the old ajustment values so we can tell if we
 
547
         * ended up actually scrolling. We may not have in a case
 
548
         * where the resulting value got pinned to the adjustment
 
549
         * min or max.
 
550
         */
 
551
        old_h_value = gtk_adjustment_get_value (hadj);
 
552
        old_v_value = gtk_adjustment_get_value (vadj);
 
553
        
 
554
        gtk_adjustment_set_value (hadj, gtk_adjustment_get_value (hadj) + delta_x);
 
555
        gtk_adjustment_set_value (vadj, gtk_adjustment_get_value (vadj) + delta_y);
 
556
 
 
557
        /* return TRUE if we did scroll */
 
558
        return gtk_adjustment_get_value (hadj) != old_h_value || gtk_adjustment_get_value (vadj) != old_v_value;
 
559
}
 
560
 
 
561
static void
 
562
pending_icon_to_reveal_destroy_callback (NautilusIconCanvasItem *item,
 
563
                                         NautilusIconContainer *container)
 
564
{
 
565
        g_assert (NAUTILUS_IS_ICON_CONTAINER (container));
 
566
        g_assert (container->details->pending_icon_to_reveal != NULL);
 
567
        g_assert (container->details->pending_icon_to_reveal->item == item);
 
568
 
 
569
        container->details->pending_icon_to_reveal = NULL;
 
570
}
 
571
 
 
572
static NautilusIcon*
 
573
get_pending_icon_to_reveal (NautilusIconContainer *container)
 
574
{
 
575
        return container->details->pending_icon_to_reveal;
 
576
}
 
577
 
 
578
static void
 
579
set_pending_icon_to_reveal (NautilusIconContainer *container, NautilusIcon *icon)
 
580
{
 
581
        NautilusIcon *old_icon;
 
582
        
 
583
        old_icon = container->details->pending_icon_to_reveal;
 
584
        
 
585
        if (icon == old_icon) {
 
586
                return;
 
587
        }
 
588
        
 
589
        if (old_icon != NULL) {
 
590
                g_signal_handlers_disconnect_by_func
 
591
                        (old_icon->item,
 
592
                         G_CALLBACK (pending_icon_to_reveal_destroy_callback),
 
593
                         container);
 
594
        }
 
595
        
 
596
        if (icon != NULL) {
 
597
                g_signal_connect (icon->item, "destroy",
 
598
                                  G_CALLBACK (pending_icon_to_reveal_destroy_callback),
 
599
                                  container);
 
600
        }
 
601
        
 
602
        container->details->pending_icon_to_reveal = icon;
 
603
}
 
604
 
 
605
static void
 
606
item_get_canvas_bounds (EelCanvasItem *item,
 
607
                        EelIRect *bounds,
 
608
                        gboolean safety_pad)
 
609
{
 
610
        EelDRect world_rect;
 
611
        
 
612
        eel_canvas_item_get_bounds (item,
 
613
                                    &world_rect.x0,
 
614
                                    &world_rect.y0,
 
615
                                    &world_rect.x1,
 
616
                                    &world_rect.y1);
 
617
        eel_canvas_item_i2w (item->parent,
 
618
                             &world_rect.x0,
 
619
                             &world_rect.y0);
 
620
        eel_canvas_item_i2w (item->parent,
 
621
                             &world_rect.x1,
 
622
                             &world_rect.y1);
 
623
        if (safety_pad) {
 
624
                world_rect.x0 -= ICON_PAD_LEFT + ICON_PAD_RIGHT;
 
625
                world_rect.x1 += ICON_PAD_LEFT + ICON_PAD_RIGHT;
 
626
 
 
627
                world_rect.y0 -= ICON_PAD_TOP + ICON_PAD_BOTTOM;
 
628
                world_rect.y1 += ICON_PAD_TOP + ICON_PAD_BOTTOM;
 
629
        }
 
630
 
 
631
        eel_canvas_w2c (item->canvas,
 
632
                        world_rect.x0,
 
633
                        world_rect.y0,
 
634
                        &bounds->x0,
 
635
                        &bounds->y0);
 
636
        eel_canvas_w2c (item->canvas,
 
637
                        world_rect.x1,
 
638
                        world_rect.y1,
 
639
                        &bounds->x1,
 
640
                        &bounds->y1);
 
641
}
 
642
 
 
643
static void
 
644
icon_get_row_and_column_bounds (NautilusIconContainer *container,
 
645
                                NautilusIcon *icon,
 
646
                                EelIRect *bounds,
 
647
                                gboolean safety_pad)
 
648
{
 
649
        GList *p;
 
650
        NautilusIcon *one_icon;
 
651
        EelIRect one_bounds;
 
652
 
 
653
        item_get_canvas_bounds (EEL_CANVAS_ITEM (icon->item), bounds, safety_pad);
 
654
 
 
655
        for (p = container->details->icons; p != NULL; p = p->next) {
 
656
                one_icon = p->data;
 
657
 
 
658
                if (icon == one_icon) {
 
659
                        continue;
 
660
                }
 
661
 
 
662
                if (compare_icons_horizontal (container, icon, one_icon) == 0) {
 
663
                        item_get_canvas_bounds (EEL_CANVAS_ITEM (one_icon->item), &one_bounds, safety_pad);
 
664
                        bounds->x0 = MIN (bounds->x0, one_bounds.x0);
 
665
                        bounds->x1 = MAX (bounds->x1, one_bounds.x1);
 
666
                }
 
667
 
 
668
                if (compare_icons_vertical (container, icon, one_icon) == 0) {
 
669
                        item_get_canvas_bounds (EEL_CANVAS_ITEM (one_icon->item), &one_bounds, safety_pad);
 
670
                        bounds->y0 = MIN (bounds->y0, one_bounds.y0);
 
671
                        bounds->y1 = MAX (bounds->y1, one_bounds.y1);
 
672
                }
 
673
        }
 
674
 
 
675
 
 
676
}
 
677
 
 
678
static void
 
679
reveal_icon (NautilusIconContainer *container,
 
680
             NautilusIcon *icon)
 
681
{
 
682
        GtkAllocation allocation;
 
683
        GtkAdjustment *hadj, *vadj;
 
684
        EelIRect bounds;
 
685
 
 
686
        if (!icon_is_positioned (icon)) {
 
687
                set_pending_icon_to_reveal (container, icon);
 
688
                return;
 
689
        }
 
690
        
 
691
        set_pending_icon_to_reveal (container, NULL);
 
692
 
 
693
        gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
 
694
 
 
695
        hadj = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (container));
 
696
        vadj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (container));
 
697
 
 
698
        if (nautilus_icon_container_is_auto_layout (container)) {
 
699
                /* ensure that we reveal the entire row/column */
 
700
                icon_get_row_and_column_bounds (container, icon, &bounds, TRUE);
 
701
        } else {
 
702
                item_get_canvas_bounds (EEL_CANVAS_ITEM (icon->item), &bounds, TRUE);
 
703
        }
 
704
        if (bounds.y0 < gtk_adjustment_get_value (vadj)) {
 
705
                gtk_adjustment_set_value (vadj, bounds.y0);
 
706
        } else if (bounds.y1 > gtk_adjustment_get_value (vadj) + allocation.height) {
 
707
                gtk_adjustment_set_value
 
708
                        (vadj, bounds.y1 - allocation.height);
 
709
        }
 
710
 
 
711
        if (bounds.x0 < gtk_adjustment_get_value (hadj)) {
 
712
                gtk_adjustment_set_value (hadj, bounds.x0);
 
713
        } else if (bounds.x1 > gtk_adjustment_get_value (hadj) + allocation.width) {
 
714
                gtk_adjustment_set_value
 
715
                        (hadj, bounds.x1 - allocation.width);
 
716
        }
 
717
}
 
718
 
 
719
static void
 
720
process_pending_icon_to_reveal (NautilusIconContainer *container)
 
721
{
 
722
        NautilusIcon *pending_icon_to_reveal;
 
723
        
 
724
        pending_icon_to_reveal = get_pending_icon_to_reveal (container);
 
725
        
 
726
        if (pending_icon_to_reveal != NULL) {
 
727
                reveal_icon (container, pending_icon_to_reveal);
 
728
        }
 
729
}
 
730
 
 
731
static gboolean
 
732
keyboard_icon_reveal_timeout_callback (gpointer data)
 
733
{
 
734
        NautilusIconContainer *container;
 
735
        NautilusIcon *icon;
 
736
 
 
737
        container = NAUTILUS_ICON_CONTAINER (data);
 
738
        icon = container->details->keyboard_icon_to_reveal;
 
739
 
 
740
        g_assert (icon != NULL);
 
741
 
 
742
        /* Only reveal the icon if it's still the keyboard focus or if
 
743
         * it's still selected. Someone originally thought we should
 
744
         * cancel this reveal if the user manages to sneak a direct
 
745
         * scroll in before the timeout fires, but we later realized
 
746
         * this wouldn't actually be an improvement 
 
747
         * (see bugzilla.gnome.org 40612).
 
748
         */
 
749
        if (icon == container->details->keyboard_focus
 
750
            || icon->is_selected) {
 
751
                reveal_icon (container, icon);
 
752
        }
 
753
        container->details->keyboard_icon_reveal_timer_id = 0;
 
754
 
 
755
        return FALSE;
 
756
}
 
757
 
 
758
static void
 
759
unschedule_keyboard_icon_reveal (NautilusIconContainer *container)
 
760
{
 
761
        NautilusIconContainerDetails *details;
 
762
 
 
763
        details = container->details;
 
764
 
 
765
        if (details->keyboard_icon_reveal_timer_id != 0) {
 
766
                g_source_remove (details->keyboard_icon_reveal_timer_id);
 
767
        }
 
768
}
 
769
 
 
770
static void
 
771
schedule_keyboard_icon_reveal (NautilusIconContainer *container,
 
772
                               NautilusIcon *icon)
 
773
{
 
774
        NautilusIconContainerDetails *details;
 
775
 
 
776
        details = container->details;
 
777
 
 
778
        unschedule_keyboard_icon_reveal (container);
 
779
 
 
780
        details->keyboard_icon_to_reveal = icon;
 
781
        details->keyboard_icon_reveal_timer_id
 
782
                = g_timeout_add (KEYBOARD_ICON_REVEAL_TIMEOUT,
 
783
                                 keyboard_icon_reveal_timeout_callback,
 
784
                                 container);
 
785
}
 
786
 
 
787
static void
 
788
clear_keyboard_focus (NautilusIconContainer *container)
 
789
{
 
790
        if (container->details->keyboard_focus != NULL) {
 
791
                eel_canvas_item_set (EEL_CANVAS_ITEM (container->details->keyboard_focus->item),
 
792
                                       "highlighted_as_keyboard_focus", 0,
 
793
                                       NULL);
 
794
        }
 
795
        
 
796
        container->details->keyboard_focus = NULL;
 
797
}
 
798
 
 
799
static void inline
 
800
emit_atk_focus_tracker_notify (NautilusIcon *icon)
 
801
{
 
802
        AtkObject *atk_object = atk_gobject_accessible_for_object (G_OBJECT (icon->item));
 
803
        atk_focus_tracker_notify (atk_object);
 
804
}
 
805
 
 
806
/* Set @icon as the icon currently selected for keyboard operations. */
 
807
static void
 
808
set_keyboard_focus (NautilusIconContainer *container,
 
809
                    NautilusIcon *icon)
 
810
{
 
811
        g_assert (icon != NULL);
 
812
 
 
813
        if (icon == container->details->keyboard_focus) {
 
814
                return;
 
815
        }
 
816
 
 
817
        clear_keyboard_focus (container);
 
818
 
 
819
        container->details->keyboard_focus = icon;
 
820
 
 
821
        eel_canvas_item_set (EEL_CANVAS_ITEM (container->details->keyboard_focus->item),
 
822
                               "highlighted_as_keyboard_focus", 1,
 
823
                               NULL);
 
824
 
 
825
        emit_atk_focus_tracker_notify (icon);
 
826
}
 
827
 
 
828
static void
 
829
set_keyboard_rubberband_start (NautilusIconContainer *container,
 
830
                               NautilusIcon *icon)
 
831
{
 
832
        container->details->keyboard_rubberband_start = icon;
 
833
}
 
834
 
 
835
static void
 
836
clear_keyboard_rubberband_start (NautilusIconContainer *container)
 
837
{
 
838
        container->details->keyboard_rubberband_start = NULL;
 
839
}
 
840
 
 
841
/* carbon-copy of eel_canvas_group_bounds(), but
 
842
 * for NautilusIconContainerItems it returns the
 
843
 * bounds for the “entire item”.
 
844
 */
 
845
static void
 
846
get_icon_bounds_for_canvas_bounds (EelCanvasGroup *group,
 
847
                                   double *x1, double *y1,
 
848
                                   double *x2, double *y2,
 
849
                                   NautilusIconCanvasItemBoundsUsage usage)
 
850
{
 
851
        EelCanvasItem *child;
 
852
        GList *list;
 
853
        double tx1, ty1, tx2, ty2;
 
854
        double minx, miny, maxx, maxy;
 
855
        int set;
 
856
 
 
857
        /* Get the bounds of the first visible item */
 
858
 
 
859
        child = NULL; /* Unnecessary but eliminates a warning. */
 
860
 
 
861
        set = FALSE;
 
862
 
 
863
        for (list = group->item_list; list; list = list->next) {
 
864
                child = list->data;
 
865
 
 
866
                if (!NAUTILUS_IS_ICON_CANVAS_ITEM (child)) {
 
867
                        continue;
 
868
                }
 
869
 
 
870
                if (child->flags & EEL_CANVAS_ITEM_VISIBLE) {
 
871
                        set = TRUE;
 
872
                        if (!NAUTILUS_IS_ICON_CANVAS_ITEM (child) ||
 
873
                            usage == BOUNDS_USAGE_FOR_DISPLAY) {
 
874
                                eel_canvas_item_get_bounds (child, &minx, &miny, &maxx, &maxy);
 
875
                        } else if (usage == BOUNDS_USAGE_FOR_LAYOUT) {
 
876
                                nautilus_icon_canvas_item_get_bounds_for_layout (NAUTILUS_ICON_CANVAS_ITEM (child),
 
877
                                                                                 &minx, &miny, &maxx, &maxy);
 
878
                        } else if (usage == BOUNDS_USAGE_FOR_ENTIRE_ITEM) {
 
879
                                nautilus_icon_canvas_item_get_bounds_for_entire_item (NAUTILUS_ICON_CANVAS_ITEM (child),
 
880
                                                                                      &minx, &miny, &maxx, &maxy);
 
881
                        } else {
 
882
                                g_assert_not_reached ();
 
883
                        }
 
884
                        break;
 
885
                }
 
886
        }
 
887
 
 
888
        /* If there were no visible items, return an empty bounding box */
 
889
 
 
890
        if (!set) {
 
891
                *x1 = *y1 = *x2 = *y2 = 0.0;
 
892
                return;
 
893
        }
 
894
 
 
895
        /* Now we can grow the bounds using the rest of the items */
 
896
 
 
897
        list = list->next;
 
898
 
 
899
        for (; list; list = list->next) {
 
900
                child = list->data;
 
901
 
 
902
                if (!NAUTILUS_IS_ICON_CANVAS_ITEM (child)) {
 
903
                        continue;
 
904
                }
 
905
 
 
906
                if (!(child->flags & EEL_CANVAS_ITEM_VISIBLE))
 
907
                        continue;
 
908
 
 
909
                if (!NAUTILUS_IS_ICON_CANVAS_ITEM (child) ||
 
910
                    usage == BOUNDS_USAGE_FOR_DISPLAY) {
 
911
                        eel_canvas_item_get_bounds (child, &tx1, &ty1, &tx2, &ty2);
 
912
                } else if (usage == BOUNDS_USAGE_FOR_LAYOUT) {
 
913
                        nautilus_icon_canvas_item_get_bounds_for_layout (NAUTILUS_ICON_CANVAS_ITEM (child),
 
914
                                                                         &tx1, &ty1, &tx2, &ty2);
 
915
                } else if (usage == BOUNDS_USAGE_FOR_ENTIRE_ITEM) {
 
916
                        nautilus_icon_canvas_item_get_bounds_for_entire_item (NAUTILUS_ICON_CANVAS_ITEM (child),
 
917
                                                                              &tx1, &ty1, &tx2, &ty2);
 
918
                } else {
 
919
                        g_assert_not_reached ();
 
920
                }
 
921
 
 
922
                if (tx1 < minx)
 
923
                        minx = tx1;
 
924
 
 
925
                if (ty1 < miny)
 
926
                        miny = ty1;
 
927
 
 
928
                if (tx2 > maxx)
 
929
                        maxx = tx2;
 
930
 
 
931
                if (ty2 > maxy)
 
932
                        maxy = ty2;
 
933
        }
 
934
 
 
935
        /* Make the bounds be relative to our parent's coordinate system */
 
936
 
 
937
        if (EEL_CANVAS_ITEM (group)->parent) {
 
938
                minx += group->xpos;
 
939
                miny += group->ypos;
 
940
                maxx += group->xpos;
 
941
                maxy += group->ypos;
 
942
        }
 
943
        
 
944
        if (x1 != NULL) {
 
945
                *x1 = minx;
 
946
        }
 
947
 
 
948
        if (y1 != NULL) {
 
949
                *y1 = miny;
 
950
        }
 
951
 
 
952
        if (x2 != NULL) {
 
953
                *x2 = maxx;
 
954
        }
 
955
 
 
956
        if (y2 != NULL) {
 
957
                *y2 = maxy;
 
958
        }
 
959
}
 
960
 
 
961
static void
 
962
get_all_icon_bounds (NautilusIconContainer *container,
 
963
                     double *x1, double *y1,
 
964
                     double *x2, double *y2,
 
965
                     NautilusIconCanvasItemBoundsUsage usage)
 
966
{
 
967
        /* FIXME bugzilla.gnome.org 42477: Do we have to do something about the rubberband
 
968
         * here? Any other non-icon items?
 
969
         */
 
970
        get_icon_bounds_for_canvas_bounds (EEL_CANVAS_GROUP (EEL_CANVAS (container)->root),
 
971
                                           x1, y1, x2, y2, usage);
 
972
}
 
973
 
 
974
/* Don't preserve visible white space the next time the scroll region
 
975
 * is recomputed when the container is not empty. */
 
976
void
 
977
nautilus_icon_container_reset_scroll_region (NautilusIconContainer *container)
 
978
{
 
979
        container->details->reset_scroll_region_trigger = TRUE;
 
980
}
 
981
 
 
982
/* Set a new scroll region without eliminating any of the currently-visible area. */
 
983
static void
 
984
canvas_set_scroll_region_include_visible_area (EelCanvas *canvas,
 
985
                                               double x1, double y1,
 
986
                                               double x2, double y2)
 
987
{
 
988
        double old_x1, old_y1, old_x2, old_y2;
 
989
        double old_scroll_x, old_scroll_y;
 
990
        double height, width;
 
991
        GtkAllocation allocation;
 
992
        
 
993
        eel_canvas_get_scroll_region (canvas, &old_x1, &old_y1, &old_x2, &old_y2);
 
994
        gtk_widget_get_allocation (GTK_WIDGET (canvas), &allocation);
 
995
 
 
996
        width = (allocation.width) / canvas->pixels_per_unit;
 
997
        height = (allocation.height) / canvas->pixels_per_unit;
 
998
 
 
999
        old_scroll_x = gtk_adjustment_get_value (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (canvas)));
 
1000
        old_scroll_y = gtk_adjustment_get_value (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (canvas)));
 
1001
 
 
1002
        x1 = MIN (x1, old_x1 + old_scroll_x);
 
1003
        y1 = MIN (y1, old_y1 + old_scroll_y);
 
1004
        x2 = MAX (x2, old_x1 + old_scroll_x + width);
 
1005
        y2 = MAX (y2, old_y1 + old_scroll_y + height);
 
1006
 
 
1007
        eel_canvas_set_scroll_region
 
1008
                (canvas, x1, y1, x2, y2);
 
1009
}
 
1010
 
 
1011
void
 
1012
nautilus_icon_container_update_scroll_region (NautilusIconContainer *container)
 
1013
{
 
1014
        double x1, y1, x2, y2;
 
1015
        double pixels_per_unit;
 
1016
        GtkAdjustment *hadj, *vadj;
 
1017
        float step_increment;
 
1018
        gboolean reset_scroll_region;
 
1019
        GtkAllocation allocation;
 
1020
 
 
1021
        pixels_per_unit = EEL_CANVAS (container)->pixels_per_unit;
 
1022
 
 
1023
        if (nautilus_icon_container_get_is_fixed_size (container)) {
 
1024
                /* Set the scroll region to the size of the container allocation */
 
1025
                gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
 
1026
                eel_canvas_set_scroll_region
 
1027
                        (EEL_CANVAS (container),
 
1028
                         (double) - container->details->left_margin / pixels_per_unit,
 
1029
                         (double) - container->details->top_margin / pixels_per_unit,
 
1030
                         ((double) (allocation.width - 1)
 
1031
                         - container->details->left_margin
 
1032
                         - container->details->right_margin)
 
1033
                         / pixels_per_unit,
 
1034
                         ((double) (allocation.height - 1)
 
1035
                         - container->details->top_margin
 
1036
                         - container->details->bottom_margin)
 
1037
                         / pixels_per_unit);
 
1038
                return;
 
1039
        }
 
1040
 
 
1041
        reset_scroll_region = container->details->reset_scroll_region_trigger
 
1042
                || nautilus_icon_container_is_empty (container)
 
1043
                || nautilus_icon_container_is_auto_layout (container);
 
1044
 
 
1045
        /* The trigger is only cleared when container is non-empty, so
 
1046
         * callers can reliably reset the scroll region when an item
 
1047
         * is added even if extraneous relayouts are called when the
 
1048
         * window is still empty.
 
1049
         */
 
1050
        if (!nautilus_icon_container_is_empty (container)) {
 
1051
                container->details->reset_scroll_region_trigger = FALSE;
 
1052
        }
 
1053
 
 
1054
        get_all_icon_bounds (container, &x1, &y1, &x2, &y2, BOUNDS_USAGE_FOR_ENTIRE_ITEM);
 
1055
 
 
1056
        /* Add border at the "end"of the layout (i.e. after the icons), to
 
1057
         * ensure we get some space when scrolled to the end.
 
1058
         * For horizontal layouts, we add a bottom border.
 
1059
         * Vertical layout is used by the compact view so the end
 
1060
         * depends on the RTL setting.
 
1061
         */
 
1062
        if (nautilus_icon_container_is_layout_vertical (container)) {
 
1063
                if (nautilus_icon_container_is_layout_rtl (container)) {
 
1064
                        x1 -= ICON_PAD_LEFT + CONTAINER_PAD_LEFT;
 
1065
                } else {
 
1066
                        x2 += ICON_PAD_RIGHT + CONTAINER_PAD_RIGHT;
 
1067
                }
 
1068
        } else {
 
1069
                y2 += ICON_PAD_BOTTOM + CONTAINER_PAD_BOTTOM;
 
1070
        }
 
1071
 
 
1072
        /* Auto-layout assumes a 0, 0 scroll origin and at least allocation->width.
 
1073
         * Then we lay out to the right or to the left, so
 
1074
         * x can be < 0 and > allocation */
 
1075
        if (nautilus_icon_container_is_auto_layout (container)) {
 
1076
                gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
 
1077
                x1 = MIN (x1, 0);
 
1078
                x2 = MAX (x2, allocation.width / pixels_per_unit);
 
1079
                y1 = 0;
 
1080
        } else {
 
1081
                /* Otherwise we add the padding that is at the start of the
 
1082
                   layout */
 
1083
                if (nautilus_icon_container_is_layout_rtl (container)) {
 
1084
                        x2 += ICON_PAD_RIGHT + CONTAINER_PAD_RIGHT;
 
1085
                } else {
 
1086
                        x1 -= ICON_PAD_LEFT + CONTAINER_PAD_LEFT;
 
1087
                }
 
1088
                y1 -= ICON_PAD_TOP + CONTAINER_PAD_TOP;
 
1089
        }
 
1090
 
 
1091
        x2 -= 1;
 
1092
        x2 = MAX(x1, x2);
 
1093
 
 
1094
        y2 -= 1;
 
1095
        y2 = MAX(y1, y2);
 
1096
 
 
1097
        if (reset_scroll_region) {
 
1098
                eel_canvas_set_scroll_region
 
1099
                        (EEL_CANVAS (container),
 
1100
                         x1, y1, x2, y2);
 
1101
        } else {
 
1102
                canvas_set_scroll_region_include_visible_area
 
1103
                        (EEL_CANVAS (container),
 
1104
                         x1, y1, x2, y2);
 
1105
        }
 
1106
 
 
1107
        hadj = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (container));
 
1108
        vadj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (container));
 
1109
 
 
1110
        /* Scroll by 1/4 icon each time you click. */
 
1111
        step_increment = nautilus_get_icon_size_for_zoom_level
 
1112
                (container->details->zoom_level) / 4;
 
1113
        if (gtk_adjustment_get_step_increment (hadj) != step_increment) {
 
1114
                gtk_adjustment_set_step_increment (hadj, step_increment);
 
1115
        }
 
1116
        if (gtk_adjustment_get_step_increment (vadj) != step_increment) {
 
1117
                gtk_adjustment_set_step_increment (vadj, step_increment);
 
1118
        }
 
1119
}
 
1120
 
 
1121
static int
 
1122
compare_icons (gconstpointer a, gconstpointer b, gpointer icon_container)
 
1123
{
 
1124
        NautilusIconContainerClass *klass;
 
1125
        const NautilusIcon *icon_a, *icon_b;
 
1126
 
 
1127
        icon_a = a;
 
1128
        icon_b = b;
 
1129
        klass  = NAUTILUS_ICON_CONTAINER_GET_CLASS (icon_container);
 
1130
 
 
1131
        return klass->compare_icons (icon_container, icon_a->data, icon_b->data);
 
1132
}
 
1133
 
 
1134
static void
 
1135
sort_icons (NautilusIconContainer *container,
 
1136
            GList                **icons)
 
1137
{
 
1138
        NautilusIconContainerClass *klass;
 
1139
 
 
1140
        klass = NAUTILUS_ICON_CONTAINER_GET_CLASS (container);
 
1141
        g_assert (klass->compare_icons != NULL);
 
1142
 
 
1143
        *icons = g_list_sort_with_data (*icons, compare_icons, container);
 
1144
}
 
1145
 
 
1146
static void
 
1147
resort (NautilusIconContainer *container)
 
1148
{
 
1149
        sort_icons (container, &container->details->icons);
 
1150
}
 
1151
 
 
1152
#if 0
 
1153
static double
 
1154
get_grid_width (NautilusIconContainer *container)
 
1155
{
 
1156
        if (container->details->label_position == NAUTILUS_ICON_LABEL_POSITION_BESIDE) {
 
1157
                return TEXT_BESIDE_ICON_GRID_WIDTH;
 
1158
        } else {
 
1159
                return STANDARD_ICON_GRID_WIDTH;
 
1160
        }
 
1161
}
 
1162
#endif
 
1163
typedef struct {
 
1164
        double width;
 
1165
        double height;
 
1166
        double x_offset;
 
1167
        double y_offset;
 
1168
} IconPositions;
 
1169
 
 
1170
static void
 
1171
lay_down_one_line (NautilusIconContainer *container,
 
1172
                   GList *line_start,
 
1173
                   GList *line_end,
 
1174
                   double y,
 
1175
                   double max_height,
 
1176
                   GArray *positions,
 
1177
                   gboolean whole_text)
 
1178
{
 
1179
        GList *p;
 
1180
        NautilusIcon *icon;
 
1181
        double x, y_offset;
 
1182
        IconPositions *position;
 
1183
        int i;
 
1184
        gboolean is_rtl;
 
1185
 
 
1186
        is_rtl = nautilus_icon_container_is_layout_rtl (container);
 
1187
 
 
1188
        /* Lay out the icons along the baseline. */
 
1189
        x = ICON_PAD_LEFT;
 
1190
        i = 0;
 
1191
        for (p = line_start; p != line_end; p = p->next) {
 
1192
                icon = p->data;
 
1193
 
 
1194
                position = &g_array_index (positions, IconPositions, i++);
 
1195
                
 
1196
                if (container->details->label_position == NAUTILUS_ICON_LABEL_POSITION_BESIDE) {
 
1197
                        y_offset = (max_height - position->height) / 2;
 
1198
                } else {
 
1199
                        y_offset = position->y_offset;
 
1200
                }
 
1201
 
 
1202
                icon_set_position
 
1203
                        (icon,
 
1204
                         is_rtl ? get_mirror_x_position (container, icon, x + position->x_offset) : x + position->x_offset,
 
1205
                         y + y_offset);
 
1206
                nautilus_icon_canvas_item_set_entire_text (icon->item, whole_text);
 
1207
 
 
1208
                icon->saved_ltr_x = is_rtl ? get_mirror_x_position (container, icon, icon->x) : icon->x;
 
1209
 
 
1210
                x += position->width;
 
1211
        }
 
1212
}
 
1213
 
 
1214
static void
 
1215
lay_down_one_column (NautilusIconContainer *container,
 
1216
                     GList *line_start,
 
1217
                     GList *line_end,
 
1218
                     double x,
 
1219
                     double y_start,
 
1220
                     double y_iter,
 
1221
                     GArray *positions)
 
1222
{
 
1223
        GList *p;
 
1224
        NautilusIcon *icon;
 
1225
        double y;
 
1226
        IconPositions *position;
 
1227
        int i;
 
1228
        gboolean is_rtl;
 
1229
 
 
1230
        is_rtl = nautilus_icon_container_is_layout_rtl (container);
 
1231
 
 
1232
        /* Lay out the icons along the baseline. */
 
1233
        y = y_start;
 
1234
        i = 0;
 
1235
        for (p = line_start; p != line_end; p = p->next) {
 
1236
                icon = p->data;
 
1237
 
 
1238
                position = &g_array_index (positions, IconPositions, i++);
 
1239
 
 
1240
                icon_set_position
 
1241
                        (icon,
 
1242
                         is_rtl ? get_mirror_x_position (container, icon, x + position->x_offset) : x + position->x_offset,
 
1243
                         y + position->y_offset);
 
1244
 
 
1245
                icon->saved_ltr_x = is_rtl ? get_mirror_x_position (container, icon, icon->x) : icon->x;
 
1246
 
 
1247
                y += y_iter;
 
1248
        }
 
1249
}
 
1250
 
 
1251
static void
 
1252
lay_down_icons_horizontal (NautilusIconContainer *container,
 
1253
                           GList *icons,
 
1254
                           double start_y)
 
1255
{
 
1256
        GList *p, *line_start;
 
1257
        NautilusIcon *icon;
 
1258
        double canvas_width, y;
 
1259
        GArray *positions;
 
1260
        IconPositions *position;
 
1261
        EelDRect bounds;
 
1262
        EelDRect icon_bounds;
 
1263
        EelDRect text_bounds;
 
1264
        double max_height_above, max_height_below;
 
1265
        double height_above, height_below;
 
1266
        double line_width;
 
1267
        double grid_width;
 
1268
        double max_text_width, max_icon_width;
 
1269
        int icon_width;
 
1270
        int i;
 
1271
        GtkAllocation allocation;
 
1272
 
 
1273
        g_assert (NAUTILUS_IS_ICON_CONTAINER (container));
 
1274
 
 
1275
        if (icons == NULL) {
 
1276
                return;
 
1277
        }
 
1278
 
 
1279
        positions = g_array_new (FALSE, FALSE, sizeof (IconPositions));
 
1280
        gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
 
1281
        
 
1282
        /* Lay out icons a line at a time. */
 
1283
        canvas_width = CANVAS_WIDTH(container, allocation);
 
1284
        max_icon_width = max_text_width = 0.0;
 
1285
 
 
1286
        if (container->details->label_position == NAUTILUS_ICON_LABEL_POSITION_BESIDE) {
 
1287
                /* Would it be worth caching these bounds for the next loop? */
 
1288
                for (p = icons; p != NULL; p = p->next) {
 
1289
                        icon = p->data;
 
1290
 
 
1291
                        icon_bounds = nautilus_icon_canvas_item_get_icon_rectangle (icon->item);
 
1292
                        max_icon_width = MAX (max_icon_width, ceil (icon_bounds.x1 - icon_bounds.x0));
 
1293
 
 
1294
                        text_bounds = nautilus_icon_canvas_item_get_text_rectangle (icon->item, TRUE);
 
1295
                        max_text_width = MAX (max_text_width, ceil (text_bounds.x1 - text_bounds.x0));
 
1296
                }
 
1297
 
 
1298
                grid_width = max_icon_width + max_text_width + ICON_PAD_LEFT + ICON_PAD_RIGHT;
 
1299
        } else {
 
1300
                grid_width = STANDARD_ICON_GRID_WIDTH;
 
1301
        }
 
1302
        
 
1303
        line_width = container->details->label_position == NAUTILUS_ICON_LABEL_POSITION_BESIDE ? ICON_PAD_LEFT : 0;
 
1304
        line_start = icons;
 
1305
        y = start_y + CONTAINER_PAD_TOP;
 
1306
        i = 0;
 
1307
        
 
1308
        max_height_above = 0;
 
1309
        max_height_below = 0;
 
1310
        for (p = icons; p != NULL; p = p->next) {
 
1311
                icon = p->data;
 
1312
 
 
1313
                /* Assume it's only one level hierarchy to avoid costly affine calculations */
 
1314
                nautilus_icon_canvas_item_get_bounds_for_layout (icon->item,
 
1315
                                                                 &bounds.x0, &bounds.y0,
 
1316
                                                                 &bounds.x1, &bounds.y1);
 
1317
 
 
1318
                icon_bounds = nautilus_icon_canvas_item_get_icon_rectangle (icon->item);
 
1319
                text_bounds = nautilus_icon_canvas_item_get_text_rectangle (icon->item, TRUE);
 
1320
 
 
1321
                icon_width = ceil ((bounds.x1 - bounds.x0)/grid_width) * grid_width;
 
1322
                
 
1323
                /* Calculate size above/below baseline */
 
1324
                height_above = icon_bounds.y1 - bounds.y0;
 
1325
                height_below = bounds.y1 - icon_bounds.y1;
 
1326
 
 
1327
                /* If this icon doesn't fit, it's time to lay out the line that's queued up. */
 
1328
                if (line_start != p && line_width + icon_width >= canvas_width ) {
 
1329
                        if (container->details->label_position == NAUTILUS_ICON_LABEL_POSITION_BESIDE) {
 
1330
                                y += ICON_PAD_TOP;
 
1331
                        } else {
 
1332
                                /* Advance to the baseline. */
 
1333
                                y += ICON_PAD_TOP + max_height_above;
 
1334
                        }
 
1335
 
 
1336
                        lay_down_one_line (container, line_start, p, y, max_height_above, positions, FALSE);
 
1337
                        
 
1338
                        if (container->details->label_position == NAUTILUS_ICON_LABEL_POSITION_BESIDE) {
 
1339
                                y += max_height_above + max_height_below + ICON_PAD_BOTTOM;
 
1340
                        } else {
 
1341
                                /* Advance to next line. */
 
1342
                                y += max_height_below + ICON_PAD_BOTTOM;
 
1343
                        }
 
1344
                        
 
1345
                        line_width = container->details->label_position == NAUTILUS_ICON_LABEL_POSITION_BESIDE ? ICON_PAD_LEFT : 0;
 
1346
                        line_start = p;
 
1347
                        i = 0;
 
1348
                        
 
1349
                        max_height_above = height_above;
 
1350
                        max_height_below = height_below;
 
1351
                } else {
 
1352
                        if (height_above > max_height_above) {
 
1353
                                max_height_above = height_above;
 
1354
                        }
 
1355
                        if (height_below > max_height_below) {
 
1356
                                max_height_below = height_below;
 
1357
                        }
 
1358
                }
 
1359
                
 
1360
                g_array_set_size (positions, i + 1);
 
1361
                position = &g_array_index (positions, IconPositions, i++);
 
1362
                position->width = icon_width;
 
1363
                position->height = icon_bounds.y1 - icon_bounds.y0;
 
1364
 
 
1365
                if (container->details->label_position == NAUTILUS_ICON_LABEL_POSITION_BESIDE) {
 
1366
                        position->x_offset = max_icon_width + ICON_PAD_LEFT + ICON_PAD_RIGHT - (icon_bounds.x1 - icon_bounds.x0);
 
1367
                        position->y_offset = 0;
 
1368
                } else {
 
1369
                        position->x_offset = (icon_width - (icon_bounds.x1 - icon_bounds.x0)) / 2;
 
1370
                        position->y_offset = icon_bounds.y0 - icon_bounds.y1;
 
1371
                }
 
1372
 
 
1373
                /* Add this icon. */
 
1374
                line_width += icon_width;
 
1375
        }
 
1376
 
 
1377
        /* Lay down that last line of icons. */
 
1378
        if (line_start != NULL) {
 
1379
                        if (container->details->label_position == NAUTILUS_ICON_LABEL_POSITION_BESIDE) {
 
1380
                                y += ICON_PAD_TOP;
 
1381
                        } else {
 
1382
                                /* Advance to the baseline. */
 
1383
                                y += ICON_PAD_TOP + max_height_above;
 
1384
                        }
 
1385
                
 
1386
                lay_down_one_line (container, line_start, NULL, y, max_height_above, positions, TRUE);
 
1387
                
 
1388
                /* Advance to next line. */
 
1389
                y += max_height_below + ICON_PAD_BOTTOM;
 
1390
        }
 
1391
 
 
1392
        g_array_free (positions, TRUE);
 
1393
}
 
1394
 
 
1395
static void
 
1396
get_max_icon_dimensions (GList *icon_start,
 
1397
                         GList *icon_end,
 
1398
                         double *max_icon_width,
 
1399
                         double *max_icon_height,
 
1400
                         double *max_text_width,
 
1401
                         double *max_text_height,
 
1402
                         double *max_bounds_height)
 
1403
{
 
1404
        NautilusIcon *icon;
 
1405
        EelDRect icon_bounds;
 
1406
        EelDRect text_bounds;
 
1407
        GList *p;
 
1408
        double y1, y2;
 
1409
 
 
1410
        *max_icon_width = *max_text_width = 0.0;
 
1411
        *max_icon_height = *max_text_height = 0.0;
 
1412
        *max_bounds_height = 0.0;
 
1413
 
 
1414
        /* Would it be worth caching these bounds for the next loop? */
 
1415
        for (p = icon_start; p != icon_end; p = p->next) {
 
1416
                icon = p->data;
 
1417
 
 
1418
                icon_bounds = nautilus_icon_canvas_item_get_icon_rectangle (icon->item);
 
1419
                *max_icon_width = MAX (*max_icon_width, ceil (icon_bounds.x1 - icon_bounds.x0));
 
1420
                *max_icon_height = MAX (*max_icon_height, ceil (icon_bounds.y1 - icon_bounds.y0));
 
1421
 
 
1422
                text_bounds = nautilus_icon_canvas_item_get_text_rectangle (icon->item, TRUE);
 
1423
                *max_text_width = MAX (*max_text_width, ceil (text_bounds.x1 - text_bounds.x0));
 
1424
                *max_text_height = MAX (*max_text_height, ceil (text_bounds.y1 - text_bounds.y0));
 
1425
 
 
1426
                nautilus_icon_canvas_item_get_bounds_for_layout (icon->item,
 
1427
                                                                 NULL, &y1,
 
1428
                                                                 NULL, &y2);
 
1429
                *max_bounds_height = MAX (*max_bounds_height, y2 - y1);
 
1430
        }
 
1431
}
 
1432
 
 
1433
/* column-wise layout. At the moment, this only works with label-beside-icon (used by "Compact View"). */
 
1434
static void
 
1435
lay_down_icons_vertical (NautilusIconContainer *container,
 
1436
                         GList *icons,
 
1437
                         double start_y)
 
1438
{
 
1439
        GList *p, *line_start;
 
1440
        NautilusIcon *icon;
 
1441
        double x, canvas_height;
 
1442
        GArray *positions;
 
1443
        IconPositions *position;
 
1444
        EelDRect icon_bounds;
 
1445
        EelDRect text_bounds;
 
1446
        GtkAllocation allocation;
 
1447
 
 
1448
        double line_height;
 
1449
 
 
1450
        double max_height;
 
1451
        double max_height_with_borders;
 
1452
        double max_width;
 
1453
        double max_width_in_column;
 
1454
 
 
1455
        double max_bounds_height;
 
1456
        double max_bounds_height_with_borders;
 
1457
 
 
1458
        double max_text_width, max_icon_width;
 
1459
        double max_text_height, max_icon_height;
 
1460
        int height;
 
1461
        int i;
 
1462
 
 
1463
        g_assert (NAUTILUS_IS_ICON_CONTAINER (container));
 
1464
        g_assert (container->details->label_position == NAUTILUS_ICON_LABEL_POSITION_BESIDE);
 
1465
 
 
1466
        if (icons == NULL) {
 
1467
                return;
 
1468
        }
 
1469
 
 
1470
        positions = g_array_new (FALSE, FALSE, sizeof (IconPositions));
 
1471
        gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
 
1472
 
 
1473
        /* Lay out icons a column at a time. */
 
1474
        canvas_height = CANVAS_HEIGHT(container, allocation);
 
1475
 
 
1476
        max_icon_width = max_text_width = 0.0;
 
1477
        max_icon_height = max_text_height = 0.0;
 
1478
        max_bounds_height = 0.0;
 
1479
 
 
1480
        get_max_icon_dimensions (icons, NULL,
 
1481
                                 &max_icon_width, &max_icon_height,
 
1482
                                 &max_text_width, &max_text_height,
 
1483
                                 &max_bounds_height);
 
1484
 
 
1485
        max_width = max_icon_width + max_text_width;
 
1486
        max_height = MAX (max_icon_height, max_text_height);
 
1487
        max_height_with_borders = ICON_PAD_TOP + max_height;
 
1488
 
 
1489
        max_bounds_height_with_borders = ICON_PAD_TOP + max_bounds_height;
 
1490
 
 
1491
        line_height = ICON_PAD_TOP;
 
1492
        line_start = icons;
 
1493
        x = 0;
 
1494
        i = 0;
 
1495
 
 
1496
        max_width_in_column = 0.0;
 
1497
 
 
1498
        for (p = icons; p != NULL; p = p->next) {
 
1499
                icon = p->data;
 
1500
 
 
1501
                /* If this icon doesn't fit, it's time to lay out the column that's queued up. */
 
1502
 
 
1503
                /* We use the bounds height here, since for wrapping we also want to consider
 
1504
                 * overlapping emblems at the bottom. We may wrap a little bit too early since
 
1505
                 * the icon with the max. bounds height may actually not be in the last row, but
 
1506
                 * it is better than visual glitches
 
1507
                 */
 
1508
                if (line_start != p && line_height + (max_bounds_height_with_borders-1) >= canvas_height ) {
 
1509
                        x += ICON_PAD_LEFT;
 
1510
 
 
1511
                        /* correctly set (per-column) width */
 
1512
                        if (!container->details->all_columns_same_width) {
 
1513
                                for (i = 0; i < (int) positions->len; i++) {
 
1514
                                        position = &g_array_index (positions, IconPositions, i);
 
1515
                                        position->width = max_width_in_column;
 
1516
                                }
 
1517
                        }
 
1518
 
 
1519
                        lay_down_one_column (container, line_start, p, x, CONTAINER_PAD_TOP, max_height_with_borders, positions);
 
1520
 
 
1521
                        /* Advance to next column. */
 
1522
                        if (container->details->all_columns_same_width) {
 
1523
                                x += max_width + ICON_PAD_RIGHT;
 
1524
                        } else {
 
1525
                                x += max_width_in_column + ICON_PAD_RIGHT;
 
1526
                        }
 
1527
 
 
1528
                        line_height = ICON_PAD_TOP;
 
1529
                        line_start = p;
 
1530
                        i = 0;
 
1531
 
 
1532
                        max_width_in_column = 0;
 
1533
                }
 
1534
 
 
1535
                icon_bounds = nautilus_icon_canvas_item_get_icon_rectangle (icon->item);
 
1536
                text_bounds = nautilus_icon_canvas_item_get_text_rectangle (icon->item, TRUE);
 
1537
 
 
1538
                max_width_in_column = MAX (max_width_in_column,
 
1539
                                           ceil (icon_bounds.x1 - icon_bounds.x0) +
 
1540
                                           ceil (text_bounds.x1 - text_bounds.x0));
 
1541
 
 
1542
                g_array_set_size (positions, i + 1);
 
1543
                position = &g_array_index (positions, IconPositions, i++);
 
1544
                if (container->details->all_columns_same_width) {
 
1545
                        position->width = max_width;
 
1546
                }
 
1547
                position->height = max_height;
 
1548
                position->y_offset = ICON_PAD_TOP;
 
1549
                position->x_offset = ICON_PAD_LEFT;
 
1550
 
 
1551
                position->x_offset += max_icon_width - ceil (icon_bounds.x1 - icon_bounds.x0);
 
1552
 
 
1553
                height = MAX (ceil (icon_bounds.y1 - icon_bounds.y0), ceil(text_bounds.y1 - text_bounds.y0));
 
1554
                position->y_offset += (max_height - height) / 2;
 
1555
 
 
1556
                /* Add this icon. */
 
1557
                line_height += max_height_with_borders;
 
1558
        }
 
1559
 
 
1560
        /* Lay down that last column of icons. */
 
1561
        if (line_start != NULL) {
 
1562
                x += ICON_PAD_LEFT;
 
1563
                lay_down_one_column (container, line_start, NULL, x, CONTAINER_PAD_TOP, max_height_with_borders, positions);
 
1564
        }
 
1565
 
 
1566
        g_array_free (positions, TRUE);
 
1567
}
 
1568
 
 
1569
static void
 
1570
snap_position (NautilusIconContainer *container,
 
1571
               NautilusIcon *icon,
 
1572
               int *x, int *y)
 
1573
{
 
1574
        int center_x;
 
1575
        int baseline_y;
 
1576
        int icon_width;
 
1577
        int icon_height;
 
1578
        int total_width;
 
1579
        int total_height;
 
1580
        EelDRect icon_position;
 
1581
        GtkAllocation allocation;
 
1582
        
 
1583
        icon_position = nautilus_icon_canvas_item_get_icon_rectangle (icon->item);
 
1584
        icon_width = icon_position.x1 - icon_position.x0;
 
1585
        icon_height = icon_position.y1 - icon_position.y0;
 
1586
 
 
1587
        gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
 
1588
        total_width = CANVAS_WIDTH (container, allocation);
 
1589
        total_height = CANVAS_HEIGHT (container, allocation);
 
1590
 
 
1591
        if (nautilus_icon_container_is_layout_rtl (container))
 
1592
            *x = get_mirror_x_position (container, icon, *x);
 
1593
 
 
1594
        if (*x + icon_width / 2 < DESKTOP_PAD_HORIZONTAL + SNAP_SIZE_X) {
 
1595
                *x = DESKTOP_PAD_HORIZONTAL + SNAP_SIZE_X - icon_width / 2;
 
1596
        }
 
1597
 
 
1598
        if (*x + icon_width / 2 > total_width - (DESKTOP_PAD_HORIZONTAL + SNAP_SIZE_X)) {
 
1599
                *x = total_width - (DESKTOP_PAD_HORIZONTAL + SNAP_SIZE_X + (icon_width / 2));
 
1600
        }
 
1601
 
 
1602
        if (*y + icon_height < DESKTOP_PAD_VERTICAL + SNAP_SIZE_Y) {
 
1603
                *y = DESKTOP_PAD_VERTICAL + SNAP_SIZE_Y - icon_height;
 
1604
        }
 
1605
 
 
1606
        if (*y + icon_height > total_height - (DESKTOP_PAD_VERTICAL + SNAP_SIZE_Y)) {
 
1607
                *y = total_height - (DESKTOP_PAD_VERTICAL + SNAP_SIZE_Y + (icon_height / 2));
 
1608
        }
 
1609
 
 
1610
        center_x = *x + icon_width / 2;
 
1611
        *x = SNAP_NEAREST_HORIZONTAL (center_x) - (icon_width / 2);
 
1612
        if (nautilus_icon_container_is_layout_rtl (container)) {
 
1613
            *x = get_mirror_x_position (container, icon, *x);
 
1614
        }
 
1615
 
 
1616
 
 
1617
        /* Find the grid position vertically and place on the proper baseline */
 
1618
        baseline_y = *y + icon_height;
 
1619
        baseline_y = SNAP_NEAREST_VERTICAL (baseline_y);
 
1620
        *y = baseline_y - icon_height;
 
1621
}
 
1622
 
 
1623
static int
 
1624
compare_icons_by_position (gconstpointer a, gconstpointer b)
 
1625
{
 
1626
        NautilusIcon *icon_a, *icon_b;
 
1627
        int x1, y1, x2, y2;
 
1628
        int center_a;
 
1629
        int center_b;
 
1630
 
 
1631
        icon_a = (NautilusIcon*)a;
 
1632
        icon_b = (NautilusIcon*)b;
 
1633
 
 
1634
        icon_get_bounding_box (icon_a, &x1, &y1, &x2, &y2,
 
1635
                               BOUNDS_USAGE_FOR_DISPLAY);
 
1636
        center_a = x1 + (x2 - x1) / 2;
 
1637
        icon_get_bounding_box (icon_b, &x1, &y1, &x2, &y2,
 
1638
                               BOUNDS_USAGE_FOR_DISPLAY);
 
1639
        center_b = x1 + (x2 - x1) / 2;
 
1640
 
 
1641
        return center_a == center_b ?
 
1642
                icon_a->y - icon_b->y :
 
1643
                center_a - center_b;
 
1644
}
 
1645
 
 
1646
static PlacementGrid *
 
1647
placement_grid_new (NautilusIconContainer *container, gboolean tight)
 
1648
{
 
1649
        PlacementGrid *grid;
 
1650
        int width, height;
 
1651
        int num_columns;
 
1652
        int num_rows;
 
1653
        int i;
 
1654
        GtkAllocation allocation;
 
1655
 
 
1656
        /* Get container dimensions */
 
1657
        gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
 
1658
        width  = CANVAS_WIDTH(container, allocation);
 
1659
        height = CANVAS_HEIGHT(container, allocation);
 
1660
 
 
1661
        num_columns = width / SNAP_SIZE_X;
 
1662
        num_rows = height / SNAP_SIZE_Y;
 
1663
        
 
1664
        if (num_columns == 0 || num_rows == 0) {
 
1665
                return NULL;
 
1666
        }
 
1667
 
 
1668
        grid = g_new0 (PlacementGrid, 1);
 
1669
        grid->tight = tight;
 
1670
        grid->num_columns = num_columns;
 
1671
        grid->num_rows = num_rows;
 
1672
 
 
1673
        grid->grid_memory = g_new0 (int, (num_rows * num_columns));
 
1674
        grid->icon_grid = g_new0 (int *, num_columns);
 
1675
        
 
1676
        for (i = 0; i < num_columns; i++) {
 
1677
                grid->icon_grid[i] = grid->grid_memory + (i * num_rows);
 
1678
        }
 
1679
        
 
1680
        return grid;
 
1681
}
 
1682
 
 
1683
static void
 
1684
placement_grid_free (PlacementGrid *grid)
 
1685
{
 
1686
        g_free (grid->icon_grid);
 
1687
        g_free (grid->grid_memory);
 
1688
        g_free (grid);
 
1689
}
 
1690
 
 
1691
static gboolean
 
1692
placement_grid_position_is_free (PlacementGrid *grid, EelIRect pos)
 
1693
{
 
1694
        int x, y;
 
1695
        
 
1696
        g_assert (pos.x0 >= 0 && pos.x0 < grid->num_columns);
 
1697
        g_assert (pos.y0 >= 0 && pos.y0 < grid->num_rows);
 
1698
        g_assert (pos.x1 >= 0 && pos.x1 < grid->num_columns);
 
1699
        g_assert (pos.y1 >= 0 && pos.y1 < grid->num_rows);
 
1700
 
 
1701
        for (x = pos.x0; x <= pos.x1; x++) {
 
1702
                for (y = pos.y0; y <= pos.y1; y++) {
 
1703
                        if (grid->icon_grid[x][y] != 0) {
 
1704
                                return FALSE;
 
1705
                        }
 
1706
                }
 
1707
        }
 
1708
 
 
1709
        return TRUE;
 
1710
}
 
1711
 
 
1712
static void
 
1713
placement_grid_mark (PlacementGrid *grid, EelIRect pos)
 
1714
{
 
1715
        int x, y;
 
1716
        
 
1717
        g_assert (pos.x0 >= 0 && pos.x0 < grid->num_columns);
 
1718
        g_assert (pos.y0 >= 0 && pos.y0 < grid->num_rows);
 
1719
        g_assert (pos.x1 >= 0 && pos.x1 < grid->num_columns);
 
1720
        g_assert (pos.y1 >= 0 && pos.y1 < grid->num_rows);
 
1721
 
 
1722
        for (x = pos.x0; x <= pos.x1; x++) {
 
1723
                for (y = pos.y0; y <= pos.y1; y++) {
 
1724
                        grid->icon_grid[x][y] = 1;
 
1725
                }
 
1726
        }
 
1727
}
 
1728
 
 
1729
static void
 
1730
canvas_position_to_grid_position (PlacementGrid *grid,
 
1731
                                  EelIRect canvas_position,
 
1732
                                  EelIRect *grid_position)
 
1733
{
 
1734
        /* The first causes minimal moving around during a snap, but
 
1735
         * can end up with partially overlapping icons.  The second one won't
 
1736
         * allow any overlapping, but can cause more movement to happen 
 
1737
         * during a snap. */
 
1738
        if (grid->tight) {
 
1739
                grid_position->x0 = ceil ((double)(canvas_position.x0 - DESKTOP_PAD_HORIZONTAL) / SNAP_SIZE_X);
 
1740
                grid_position->y0 = ceil ((double)(canvas_position.y0 - DESKTOP_PAD_VERTICAL) / SNAP_SIZE_Y);
 
1741
                grid_position->x1 = floor ((double)(canvas_position.x1 - DESKTOP_PAD_HORIZONTAL) / SNAP_SIZE_X);
 
1742
                grid_position->y1 = floor ((double)(canvas_position.y1 - DESKTOP_PAD_VERTICAL) / SNAP_SIZE_Y);
 
1743
        } else {
 
1744
                grid_position->x0 = floor ((double)(canvas_position.x0 - DESKTOP_PAD_HORIZONTAL) / SNAP_SIZE_X);
 
1745
                grid_position->y0 = floor ((double)(canvas_position.y0 - DESKTOP_PAD_VERTICAL) / SNAP_SIZE_Y);
 
1746
                grid_position->x1 = floor ((double)(canvas_position.x1 - DESKTOP_PAD_HORIZONTAL) / SNAP_SIZE_X);
 
1747
                grid_position->y1 = floor ((double)(canvas_position.y1 - DESKTOP_PAD_VERTICAL) / SNAP_SIZE_Y);
 
1748
        }
 
1749
 
 
1750
        grid_position->x0 = CLAMP (grid_position->x0, 0, grid->num_columns - 1);
 
1751
        grid_position->y0 = CLAMP (grid_position->y0, 0, grid->num_rows - 1);
 
1752
        grid_position->x1 = CLAMP (grid_position->x1, grid_position->x0, grid->num_columns - 1);
 
1753
        grid_position->y1 = CLAMP (grid_position->y1, grid_position->y0, grid->num_rows - 1);
 
1754
}
 
1755
 
 
1756
static void
 
1757
placement_grid_mark_icon (PlacementGrid *grid, NautilusIcon *icon)
 
1758
{
 
1759
        EelIRect icon_pos;
 
1760
        EelIRect grid_pos;
 
1761
        
 
1762
        icon_get_bounding_box (icon,
 
1763
                               &icon_pos.x0, &icon_pos.y0,
 
1764
                               &icon_pos.x1, &icon_pos.y1,
 
1765
                               BOUNDS_USAGE_FOR_LAYOUT);
 
1766
        canvas_position_to_grid_position (grid, 
 
1767
                                          icon_pos,
 
1768
                                          &grid_pos);
 
1769
        placement_grid_mark (grid, grid_pos);
 
1770
}
 
1771
 
 
1772
static void
 
1773
find_empty_location (NautilusIconContainer *container,
 
1774
                     PlacementGrid *grid,
 
1775
                     NautilusIcon *icon,
 
1776
                     int start_x,
 
1777
                     int start_y,
 
1778
                     int *x, 
 
1779
                     int *y)
 
1780
{
 
1781
        double icon_width, icon_height;
 
1782
        int canvas_width;
 
1783
        int canvas_height;
 
1784
        int height_for_bound_check;
 
1785
        EelIRect icon_position;
 
1786
        EelDRect pixbuf_rect;
 
1787
        gboolean collision;
 
1788
        GtkAllocation allocation;
 
1789
 
 
1790
        /* Get container dimensions */
 
1791
        gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
 
1792
        canvas_width  = CANVAS_WIDTH(container, allocation);
 
1793
        canvas_height = CANVAS_HEIGHT(container, allocation);
 
1794
 
 
1795
        icon_get_bounding_box (icon,
 
1796
                               &icon_position.x0, &icon_position.y0,
 
1797
                               &icon_position.x1, &icon_position.y1,
 
1798
                               BOUNDS_USAGE_FOR_LAYOUT);
 
1799
        icon_width = icon_position.x1 - icon_position.x0;
 
1800
        icon_height = icon_position.y1 - icon_position.y0;
 
1801
 
 
1802
        icon_get_bounding_box (icon,
 
1803
                               NULL, &icon_position.y0,
 
1804
                               NULL, &icon_position.y1,
 
1805
                               BOUNDS_USAGE_FOR_ENTIRE_ITEM);
 
1806
        height_for_bound_check = icon_position.y1 - icon_position.y0;
 
1807
 
 
1808
        pixbuf_rect = nautilus_icon_canvas_item_get_icon_rectangle (icon->item);
 
1809
        
 
1810
        /* Start the icon on a grid location */
 
1811
        snap_position (container, icon, &start_x, &start_y);
 
1812
 
 
1813
        icon_position.x0 = start_x;
 
1814
        icon_position.y0 = start_y;
 
1815
        icon_position.x1 = icon_position.x0 + icon_width;
 
1816
        icon_position.y1 = icon_position.y0 + icon_height;
 
1817
 
 
1818
        do {
 
1819
                EelIRect grid_position;
 
1820
                gboolean need_new_column;
 
1821
 
 
1822
                collision = FALSE;
 
1823
                
 
1824
                canvas_position_to_grid_position (grid,
 
1825
                                                  icon_position,
 
1826
                                                  &grid_position);
 
1827
 
 
1828
                need_new_column = icon_position.y0 + height_for_bound_check + DESKTOP_PAD_VERTICAL > canvas_height;
 
1829
 
 
1830
                if (need_new_column ||
 
1831
                    !placement_grid_position_is_free (grid, grid_position)) {
 
1832
                        icon_position.y0 += SNAP_SIZE_Y;
 
1833
                        icon_position.y1 = icon_position.y0 + icon_height;
 
1834
                        
 
1835
                        if (need_new_column) {
 
1836
                                /* Move to the next column */
 
1837
                                icon_position.y0 = DESKTOP_PAD_VERTICAL + SNAP_SIZE_Y - (pixbuf_rect.y1 - pixbuf_rect.y0);
 
1838
                                while (icon_position.y0 < DESKTOP_PAD_VERTICAL) {
 
1839
                                        icon_position.y0 += SNAP_SIZE_Y;
 
1840
                                }
 
1841
                                icon_position.y1 = icon_position.y0 + icon_height;
 
1842
                                
 
1843
                                icon_position.x0 += SNAP_SIZE_X;
 
1844
                                icon_position.x1 = icon_position.x0 + icon_width;
 
1845
                        }
 
1846
                                
 
1847
                        collision = TRUE;
 
1848
                }
 
1849
        } while (collision && (icon_position.x1 < canvas_width));
 
1850
 
 
1851
        *x = icon_position.x0;
 
1852
        *y = icon_position.y0;
 
1853
}
 
1854
 
 
1855
static void
 
1856
align_icons (NautilusIconContainer *container)
 
1857
{
 
1858
        GList *unplaced_icons;
 
1859
        GList *l;
 
1860
        PlacementGrid *grid;
 
1861
 
 
1862
        unplaced_icons = g_list_copy (container->details->icons);
 
1863
        
 
1864
        unplaced_icons = g_list_sort (unplaced_icons, 
 
1865
                                      compare_icons_by_position);
 
1866
 
 
1867
        if (nautilus_icon_container_is_layout_rtl (container)) {
 
1868
                unplaced_icons = g_list_reverse (unplaced_icons);
 
1869
        }
 
1870
 
 
1871
        grid = placement_grid_new (container, TRUE);
 
1872
 
 
1873
        if (!grid) {
 
1874
                return;
 
1875
        }
 
1876
 
 
1877
        for (l = unplaced_icons; l != NULL; l = l->next) {
 
1878
                NautilusIcon *icon;
 
1879
                int x, y;
 
1880
 
 
1881
                icon = l->data;
 
1882
                x = icon->saved_ltr_x;
 
1883
                y = icon->y;
 
1884
                find_empty_location (container, grid, 
 
1885
                                     icon, x, y, &x, &y);
 
1886
 
 
1887
                icon_set_position (icon, x, y);
 
1888
                icon->saved_ltr_x = icon->x;
 
1889
                placement_grid_mark_icon (grid, icon);
 
1890
        }
 
1891
 
 
1892
        g_list_free (unplaced_icons);
 
1893
 
 
1894
        placement_grid_free (grid);
 
1895
 
 
1896
        if (nautilus_icon_container_is_layout_rtl (container)) {
 
1897
                nautilus_icon_container_set_rtl_positions (container);
 
1898
        }
 
1899
}
 
1900
 
 
1901
static double
 
1902
get_mirror_x_position (NautilusIconContainer *container, NautilusIcon *icon, double x)
 
1903
{
 
1904
        EelDRect icon_bounds;
 
1905
        GtkAllocation allocation;
 
1906
 
 
1907
        gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
 
1908
        icon_bounds = nautilus_icon_canvas_item_get_icon_rectangle (icon->item);
 
1909
 
 
1910
        return CANVAS_WIDTH(container, allocation) - x - (icon_bounds.x1 - icon_bounds.x0);
 
1911
}
 
1912
 
 
1913
static void
 
1914
nautilus_icon_container_set_rtl_positions (NautilusIconContainer *container)
 
1915
{
 
1916
        GList *l;
 
1917
        NautilusIcon *icon;
 
1918
        double x;
 
1919
 
 
1920
        if (!container->details->icons) {
 
1921
                return;
 
1922
        }
 
1923
 
 
1924
        for (l = container->details->icons; l != NULL; l = l->next) {
 
1925
                icon = l->data;
 
1926
                x = get_mirror_x_position (container, icon, icon->saved_ltr_x);
 
1927
                icon_set_position (icon, x, icon->y);
 
1928
        }
 
1929
}
 
1930
 
 
1931
static void
 
1932
lay_down_icons_vertical_desktop (NautilusIconContainer *container, GList *icons)
 
1933
{
 
1934
        GList *p, *placed_icons, *unplaced_icons;
 
1935
        int total, new_length, placed;
 
1936
        NautilusIcon *icon;
 
1937
        int height, max_width, column_width, icon_width, icon_height;
 
1938
        int x, y, x1, x2, y1, y2;
 
1939
        EelDRect icon_rect;
 
1940
        GtkAllocation allocation;
 
1941
 
 
1942
        /* Get container dimensions */
 
1943
        gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
 
1944
        height = CANVAS_HEIGHT(container, allocation);
 
1945
 
 
1946
        /* Determine which icons have and have not been placed */
 
1947
        placed_icons = NULL;
 
1948
        unplaced_icons = NULL;
 
1949
        
 
1950
        total = g_list_length (container->details->icons);
 
1951
        new_length = g_list_length (icons);
 
1952
        placed = total - new_length;
 
1953
        if (placed > 0) {
 
1954
                PlacementGrid *grid;
 
1955
                /* Add only placed icons in list */
 
1956
                for (p = container->details->icons; p != NULL; p = p->next) {
 
1957
                        icon = p->data;
 
1958
                        if (icon_is_positioned (icon)) {
 
1959
                                icon_set_position(icon, icon->saved_ltr_x, icon->y);
 
1960
                                placed_icons = g_list_prepend (placed_icons, icon);
 
1961
                        } else {
 
1962
                                icon->x = 0;
 
1963
                                icon->y = 0;
 
1964
                                unplaced_icons = g_list_prepend (unplaced_icons, icon);
 
1965
                        }
 
1966
                }
 
1967
                placed_icons = g_list_reverse (placed_icons);
 
1968
                unplaced_icons = g_list_reverse (unplaced_icons);
 
1969
 
 
1970
                grid = placement_grid_new (container, FALSE);
 
1971
 
 
1972
                if (grid) {
 
1973
                        for (p = placed_icons; p != NULL; p = p->next) {
 
1974
                                placement_grid_mark_icon
 
1975
                                        (grid, (NautilusIcon*)p->data);
 
1976
                        }
 
1977
                        
 
1978
                        /* Place unplaced icons in the best locations */
 
1979
                        for (p = unplaced_icons; p != NULL; p = p->next) {
 
1980
                                icon = p->data;
 
1981
                                
 
1982
                                icon_rect = nautilus_icon_canvas_item_get_icon_rectangle (icon->item);
 
1983
                                
 
1984
                                /* Start the icon in the first column */
 
1985
                                x = DESKTOP_PAD_HORIZONTAL + (SNAP_SIZE_X / 2) - ((icon_rect.x1 - icon_rect.x0) / 2);
 
1986
                                y = DESKTOP_PAD_VERTICAL + SNAP_SIZE_Y - (icon_rect.y1 - icon_rect.y0);
 
1987
 
 
1988
                                find_empty_location (container,
 
1989
                                                     grid,
 
1990
                                                     icon,
 
1991
                                                     x, y,
 
1992
                                                     &x, &y);
 
1993
                                
 
1994
                                icon_set_position (icon, x, y);
 
1995
                                icon->saved_ltr_x = x;
 
1996
                                placement_grid_mark_icon (grid, icon);
 
1997
                        }
 
1998
 
 
1999
                        placement_grid_free (grid);
 
2000
                }
 
2001
                
 
2002
                g_list_free (placed_icons);
 
2003
                g_list_free (unplaced_icons);
 
2004
        } else {
 
2005
                /* There are no placed icons.  Just lay them down using our rules */            
 
2006
                x = DESKTOP_PAD_HORIZONTAL;
 
2007
 
 
2008
                while (icons != NULL) {
 
2009
                        int center_x;
 
2010
                        int baseline;
 
2011
                        int icon_height_for_bound_check;
 
2012
                        gboolean should_snap;
 
2013
                        
 
2014
                        should_snap = container->details->keep_aligned;
 
2015
                        
 
2016
                        y = DESKTOP_PAD_VERTICAL;
 
2017
 
 
2018
                        max_width = 0;
 
2019
                        
 
2020
                        /* Calculate max width for column */
 
2021
                        for (p = icons; p != NULL; p = p->next) {
 
2022
                                icon = p->data;
 
2023
 
 
2024
                                icon_get_bounding_box (icon, &x1, &y1, &x2, &y2,
 
2025
                                                       BOUNDS_USAGE_FOR_LAYOUT);
 
2026
                                icon_width = x2 - x1;
 
2027
                                icon_height = y2 - y1;
 
2028
 
 
2029
                                icon_get_bounding_box (icon, NULL, &y1, NULL, &y2,
 
2030
                                                       BOUNDS_USAGE_FOR_ENTIRE_ITEM);
 
2031
                                icon_height_for_bound_check = y2 - y1;
 
2032
 
 
2033
                                if (should_snap) {
 
2034
                                        /* Snap the baseline to a grid position */
 
2035
                                        icon_rect = nautilus_icon_canvas_item_get_icon_rectangle (icon->item);
 
2036
                                        baseline = y + (icon_rect.y1 - icon_rect.y0);
 
2037
                                        baseline = SNAP_CEIL_VERTICAL (baseline);
 
2038
                                        y = baseline - (icon_rect.y1 - icon_rect.y0);
 
2039
                                }
 
2040
                                    
 
2041
                                /* Check and see if we need to move to a new column */
 
2042
                                if (y != DESKTOP_PAD_VERTICAL && y + icon_height_for_bound_check > height) {
 
2043
                                        break;
 
2044
                                }
 
2045
 
 
2046
                                if (max_width < icon_width) {
 
2047
                                        max_width = icon_width;
 
2048
                                }
 
2049
                                
 
2050
                                y += icon_height + DESKTOP_PAD_VERTICAL;
 
2051
                        }
 
2052
 
 
2053
                        y = DESKTOP_PAD_VERTICAL;
 
2054
 
 
2055
                        center_x = x + max_width / 2;
 
2056
                        column_width = max_width;
 
2057
                        if (should_snap) {
 
2058
                                /* Find the grid column to center on */
 
2059
                                center_x = SNAP_CEIL_HORIZONTAL (center_x);
 
2060
                                column_width = (center_x - x) + (max_width / 2);
 
2061
                        }
 
2062
                        
 
2063
                        /* Lay out column */
 
2064
                        for (p = icons; p != NULL; p = p->next) {
 
2065
                                icon = p->data;
 
2066
                                icon_get_bounding_box (icon, &x1, &y1, &x2, &y2,
 
2067
                                                       BOUNDS_USAGE_FOR_LAYOUT);
 
2068
                                icon_height = y2 - y1;
 
2069
 
 
2070
                                icon_get_bounding_box (icon, NULL, &y1, NULL, &y2,
 
2071
                                                       BOUNDS_USAGE_FOR_ENTIRE_ITEM);
 
2072
                                icon_height_for_bound_check = y2 - y1;
 
2073
                                
 
2074
                                icon_rect = nautilus_icon_canvas_item_get_icon_rectangle (icon->item);
 
2075
 
 
2076
                                if (should_snap) {
 
2077
                                        baseline = y + (icon_rect.y1 - icon_rect.y0);
 
2078
                                        baseline = SNAP_CEIL_VERTICAL (baseline);
 
2079
                                        y = baseline - (icon_rect.y1 - icon_rect.y0);
 
2080
                                }
 
2081
                                
 
2082
                                /* Check and see if we need to move to a new column */
 
2083
                                if (y != DESKTOP_PAD_VERTICAL && y > height - icon_height_for_bound_check &&
 
2084
                                    /* Make sure we lay out at least one icon per column, to make progress */
 
2085
                                    p != icons) {
 
2086
                                        x += column_width + DESKTOP_PAD_HORIZONTAL;
 
2087
                                        break;
 
2088
                                }
 
2089
                                
 
2090
                                icon_set_position (icon,
 
2091
                                                   center_x - (icon_rect.x1 - icon_rect.x0) / 2,
 
2092
                                                   y);
 
2093
                                
 
2094
                                icon->saved_ltr_x = icon->x;
 
2095
                                y += icon_height + DESKTOP_PAD_VERTICAL;
 
2096
                        }
 
2097
                        icons = p;
 
2098
                }
 
2099
        }
 
2100
 
 
2101
        /* These modes are special. We freeze all of our positions
 
2102
         * after we do the layout.
 
2103
         */
 
2104
        /* FIXME bugzilla.gnome.org 42478: 
 
2105
         * This should not be tied to the direction of layout.
 
2106
         * It should be a separate switch.
 
2107
         */
 
2108
        nautilus_icon_container_freeze_icon_positions (container);
 
2109
}
 
2110
 
 
2111
 
 
2112
static void
 
2113
lay_down_icons (NautilusIconContainer *container, GList *icons, double start_y)
 
2114
{
 
2115
        switch (container->details->layout_mode)
 
2116
        {
 
2117
        case NAUTILUS_ICON_LAYOUT_L_R_T_B:
 
2118
        case NAUTILUS_ICON_LAYOUT_R_L_T_B:
 
2119
                lay_down_icons_horizontal (container, icons, start_y);
 
2120
                break;
 
2121
                
 
2122
        case NAUTILUS_ICON_LAYOUT_T_B_L_R:
 
2123
        case NAUTILUS_ICON_LAYOUT_T_B_R_L:
 
2124
                if (nautilus_icon_container_get_is_desktop (container)) {
 
2125
                        lay_down_icons_vertical_desktop (container, icons);
 
2126
                } else {
 
2127
                        lay_down_icons_vertical (container, icons, start_y);
 
2128
                }
 
2129
                break;
 
2130
                
 
2131
        default:
 
2132
                g_assert_not_reached ();
 
2133
        }
 
2134
}
 
2135
 
 
2136
static void
 
2137
redo_layout_internal (NautilusIconContainer *container)
 
2138
{
 
2139
        finish_adding_new_icons (container);
 
2140
 
 
2141
        /* Don't do any re-laying-out during stretching. Later we
 
2142
         * might add smart logic that does this and leaves room for
 
2143
         * the stretched icon, but if we do it we want it to be fast
 
2144
         * and only re-lay-out when it's really needed.
 
2145
         */
 
2146
        if (container->details->auto_layout
 
2147
            && container->details->drag_state != DRAG_STATE_STRETCH) {
 
2148
                if (container->details->needs_resort) {
 
2149
                        resort (container);
 
2150
                        container->details->needs_resort = FALSE;
 
2151
                }
 
2152
                lay_down_icons (container, container->details->icons, 0);
 
2153
        }
 
2154
 
 
2155
        if (nautilus_icon_container_is_layout_rtl (container)) {
 
2156
                nautilus_icon_container_set_rtl_positions (container);
 
2157
        }
 
2158
 
 
2159
        nautilus_icon_container_update_scroll_region (container);
 
2160
 
 
2161
        process_pending_icon_to_reveal (container);
 
2162
        process_pending_icon_to_rename (container);
 
2163
        nautilus_icon_container_update_visible_icons (container);
 
2164
}
 
2165
 
 
2166
static gboolean
 
2167
redo_layout_callback (gpointer callback_data)
 
2168
{
 
2169
        NautilusIconContainer *container;
 
2170
 
 
2171
        container = NAUTILUS_ICON_CONTAINER (callback_data);
 
2172
        redo_layout_internal (container);
 
2173
        container->details->idle_id = 0;
 
2174
 
 
2175
        return FALSE;
 
2176
}
 
2177
 
 
2178
static void
 
2179
unschedule_redo_layout (NautilusIconContainer *container)
 
2180
{
 
2181
        if (container->details->idle_id != 0) {
 
2182
                g_source_remove (container->details->idle_id);
 
2183
                container->details->idle_id = 0;
 
2184
        }
 
2185
}
 
2186
 
 
2187
static void
 
2188
schedule_redo_layout (NautilusIconContainer *container)
 
2189
{
 
2190
        if (container->details->idle_id == 0
 
2191
            && container->details->has_been_allocated) {
 
2192
                container->details->idle_id = g_idle_add
 
2193
                        (redo_layout_callback, container);
 
2194
        }
 
2195
}
 
2196
 
 
2197
static void
 
2198
redo_layout (NautilusIconContainer *container)
 
2199
{
 
2200
        unschedule_redo_layout (container);
 
2201
        redo_layout_internal (container);
 
2202
}
 
2203
 
 
2204
static void
 
2205
reload_icon_positions (NautilusIconContainer *container)
 
2206
{
 
2207
        GList *p, *no_position_icons;
 
2208
        NautilusIcon *icon;
 
2209
        gboolean have_stored_position;
 
2210
        NautilusIconPosition position;
 
2211
        EelDRect bounds;
 
2212
        double bottom;
 
2213
        EelCanvasItem *item;
 
2214
 
 
2215
        g_assert (!container->details->auto_layout);
 
2216
 
 
2217
        resort (container);
 
2218
 
 
2219
        no_position_icons = NULL;
 
2220
 
 
2221
        /* Place all the icons with positions. */
 
2222
        bottom = 0;
 
2223
        for (p = container->details->icons; p != NULL; p = p->next) {
 
2224
                icon = p->data;
 
2225
 
 
2226
                have_stored_position = FALSE;
 
2227
                g_signal_emit (container,
 
2228
                                 signals[GET_STORED_ICON_POSITION], 0,
 
2229
                                 icon->data,
 
2230
                                 &position,
 
2231
                                 &have_stored_position);
 
2232
                if (have_stored_position) {
 
2233
                        icon_set_position (icon, position.x, position.y);
 
2234
                        item = EEL_CANVAS_ITEM (icon->item);
 
2235
                        nautilus_icon_canvas_item_get_bounds_for_layout (icon->item,
 
2236
                                                                         &bounds.x0,
 
2237
                                                                         &bounds.y0,
 
2238
                                                                         &bounds.x1,
 
2239
                                                                         &bounds.y1);
 
2240
                        eel_canvas_item_i2w (item->parent,
 
2241
                                             &bounds.x0,
 
2242
                                             &bounds.y0);
 
2243
                        eel_canvas_item_i2w (item->parent,
 
2244
                                             &bounds.x1,
 
2245
                                             &bounds.y1);
 
2246
                        if (bounds.y1 > bottom) {
 
2247
                                bottom = bounds.y1;
 
2248
                        }
 
2249
                } else {
 
2250
                        no_position_icons = g_list_prepend (no_position_icons, icon);
 
2251
                }
 
2252
        }
 
2253
        no_position_icons = g_list_reverse (no_position_icons);
 
2254
 
 
2255
        /* Place all the other icons. */
 
2256
        lay_down_icons (container, no_position_icons, bottom + ICON_PAD_BOTTOM);
 
2257
        g_list_free (no_position_icons);
 
2258
}
 
2259
 
 
2260
/* Container-level icon handling functions.  */
 
2261
 
 
2262
static gboolean
 
2263
button_event_modifies_selection (GdkEventButton *event)
 
2264
{
 
2265
        return (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) != 0;
 
2266
}
 
2267
 
 
2268
/* invalidate the cached label sizes for all the icons */
 
2269
static void
 
2270
invalidate_label_sizes (NautilusIconContainer *container)
 
2271
{
 
2272
        GList *p;
 
2273
        NautilusIcon *icon;
 
2274
        
 
2275
        for (p = container->details->icons; p != NULL; p = p->next) {
 
2276
                icon = p->data;
 
2277
 
 
2278
                nautilus_icon_canvas_item_invalidate_label_size (icon->item);           
 
2279
        }
 
2280
}
 
2281
 
 
2282
/* invalidate the entire labels (i.e. their attributes) for all the icons */
 
2283
static void
 
2284
invalidate_labels (NautilusIconContainer *container)
 
2285
{
 
2286
        GList *p;
 
2287
        NautilusIcon *icon;
 
2288
        
 
2289
        for (p = container->details->icons; p != NULL; p = p->next) {
 
2290
                icon = p->data;
 
2291
 
 
2292
                nautilus_icon_canvas_item_invalidate_label (icon->item);                
 
2293
        }
 
2294
}
 
2295
 
 
2296
static gboolean
 
2297
select_range (NautilusIconContainer *container,
 
2298
              NautilusIcon *icon1,
 
2299
              NautilusIcon *icon2,
 
2300
              gboolean unselect_outside_range)
 
2301
{
 
2302
        gboolean selection_changed;
 
2303
        GList *p;
 
2304
        NautilusIcon *icon;
 
2305
        NautilusIcon *unmatched_icon;
 
2306
        gboolean select;
 
2307
 
 
2308
        selection_changed = FALSE;
 
2309
 
 
2310
        unmatched_icon = NULL;
 
2311
        select = FALSE;
 
2312
        for (p = container->details->icons; p != NULL; p = p->next) {
 
2313
                icon = p->data;
 
2314
 
 
2315
                if (unmatched_icon == NULL) {
 
2316
                        if (icon == icon1) {
 
2317
                                unmatched_icon = icon2;
 
2318
                                select = TRUE;
 
2319
                        } else if (icon == icon2) {
 
2320
                                unmatched_icon = icon1;
 
2321
                                select = TRUE;
 
2322
                        }
 
2323
                }
 
2324
                
 
2325
                if (select || unselect_outside_range) {
 
2326
                        selection_changed |= icon_set_selected
 
2327
                                (container, icon, select);
 
2328
                }
 
2329
 
 
2330
                if (unmatched_icon != NULL && icon == unmatched_icon) {
 
2331
                        select = FALSE;
 
2332
                }
 
2333
                
 
2334
        }
 
2335
        
 
2336
        if (selection_changed && icon2 != NULL) {
 
2337
                emit_atk_focus_tracker_notify (icon2);
 
2338
        }
 
2339
        return selection_changed;
 
2340
}
 
2341
 
 
2342
 
 
2343
static gboolean
 
2344
select_one_unselect_others (NautilusIconContainer *container,
 
2345
                            NautilusIcon *icon_to_select)
 
2346
{
 
2347
        gboolean selection_changed;
 
2348
        GList *p;
 
2349
        NautilusIcon *icon;
 
2350
 
 
2351
        selection_changed = FALSE;
 
2352
        
 
2353
        for (p = container->details->icons; p != NULL; p = p->next) {
 
2354
                icon = p->data;
 
2355
 
 
2356
                selection_changed |= icon_set_selected
 
2357
                        (container, icon, icon == icon_to_select);
 
2358
        }
 
2359
        
 
2360
        if (selection_changed && icon_to_select != NULL) {
 
2361
                emit_atk_focus_tracker_notify (icon_to_select);
 
2362
                reveal_icon (container, icon_to_select);
 
2363
        }
 
2364
        return selection_changed;
 
2365
}
 
2366
 
 
2367
static gboolean
 
2368
unselect_all (NautilusIconContainer *container)
 
2369
{
 
2370
        return select_one_unselect_others (container, NULL);
 
2371
}
 
2372
 
 
2373
void
 
2374
nautilus_icon_container_move_icon (NautilusIconContainer *container,
 
2375
                                   NautilusIcon *icon,
 
2376
                                   int x, int y,
 
2377
                                   double scale,
 
2378
                                   gboolean raise,
 
2379
                                   gboolean snap,
 
2380
                                   gboolean update_position)
 
2381
{
 
2382
        NautilusIconContainerDetails *details;
 
2383
        gboolean emit_signal;
 
2384
        NautilusIconPosition position;
 
2385
        
 
2386
        details = container->details;
 
2387
        
 
2388
        emit_signal = FALSE;
 
2389
        
 
2390
        if (icon == get_icon_being_renamed (container)) {
 
2391
                end_renaming_mode (container, TRUE);
 
2392
        }
 
2393
 
 
2394
        if (scale != icon->scale) {
 
2395
                icon->scale = scale;
 
2396
                nautilus_icon_container_update_icon (container, icon);
 
2397
                if (update_position) {
 
2398
                        redo_layout (container); 
 
2399
                        emit_signal = TRUE;
 
2400
                }
 
2401
        }
 
2402
 
 
2403
        if (!details->auto_layout) {
 
2404
                if (details->keep_aligned && snap) {
 
2405
                        snap_position (container, icon, &x, &y);
 
2406
                }
 
2407
 
 
2408
                if (x != icon->x || y != icon->y) {
 
2409
                        icon_set_position (icon, x, y);
 
2410
                        emit_signal = update_position;
 
2411
                }
 
2412
 
 
2413
                icon->saved_ltr_x = nautilus_icon_container_is_layout_rtl (container) ? get_mirror_x_position (container, icon, icon->x) : icon->x;
 
2414
        }
 
2415
        
 
2416
        if (emit_signal) {
 
2417
                position.x = icon->saved_ltr_x;
 
2418
                position.y = icon->y;
 
2419
                position.scale = scale;
 
2420
                g_signal_emit (container,
 
2421
                                 signals[ICON_POSITION_CHANGED], 0,
 
2422
                                 icon->data, &position);
 
2423
        }
 
2424
        
 
2425
        if (raise) {
 
2426
                icon_raise (icon);
 
2427
        }
 
2428
 
 
2429
        /* FIXME bugzilla.gnome.org 42474: 
 
2430
         * Handling of the scroll region is inconsistent here. In
 
2431
         * the scale-changing case, redo_layout is called, which updates the
 
2432
         * scroll region appropriately. In other cases, it's up to the
 
2433
         * caller to make sure the scroll region is updated. This could
 
2434
         * lead to hard-to-track-down bugs.
 
2435
         */
 
2436
}
 
2437
 
 
2438
/* Implementation of rubberband selection.  */
 
2439
static void
 
2440
rubberband_select (NautilusIconContainer *container,
 
2441
                   const EelDRect *previous_rect,
 
2442
                   const EelDRect *current_rect)
 
2443
{
 
2444
        GList *p;
 
2445
        gboolean selection_changed, is_in, canvas_rect_calculated;
 
2446
        NautilusIcon *icon;
 
2447
        EelIRect canvas_rect;
 
2448
        EelCanvas *canvas;
 
2449
                        
 
2450
        selection_changed = FALSE;
 
2451
        canvas_rect_calculated = FALSE;
 
2452
 
 
2453
        for (p = container->details->icons; p != NULL; p = p->next) {
 
2454
                icon = p->data;
 
2455
                
 
2456
                if (!canvas_rect_calculated) {
 
2457
                        /* Only do this calculation once, since all the canvas items
 
2458
                         * we are interating are in the same coordinate space
 
2459
                         */
 
2460
                        canvas = EEL_CANVAS_ITEM (icon->item)->canvas;
 
2461
                        eel_canvas_w2c (canvas,
 
2462
                                        current_rect->x0,
 
2463
                                        current_rect->y0,
 
2464
                                        &canvas_rect.x0,
 
2465
                                        &canvas_rect.y0);
 
2466
                        eel_canvas_w2c (canvas,
 
2467
                                        current_rect->x1,
 
2468
                                        current_rect->y1,
 
2469
                                        &canvas_rect.x1,
 
2470
                                        &canvas_rect.y1);
 
2471
                        canvas_rect_calculated = TRUE;
 
2472
                }
 
2473
                
 
2474
                is_in = nautilus_icon_canvas_item_hit_test_rectangle (icon->item, canvas_rect);
 
2475
 
 
2476
                selection_changed |= icon_set_selected
 
2477
                        (container, icon,
 
2478
                         is_in ^ icon->was_selected_before_rubberband);
 
2479
        }
 
2480
 
 
2481
        if (selection_changed) {
 
2482
                g_signal_emit (container,
 
2483
                                 signals[SELECTION_CHANGED], 0);
 
2484
        }
 
2485
}
 
2486
 
 
2487
static int
 
2488
rubberband_timeout_callback (gpointer data)
 
2489
{
 
2490
        NautilusIconContainer *container;
 
2491
        GtkWidget *widget;
 
2492
        NautilusIconRubberbandInfo *band_info;
 
2493
        int x, y;
 
2494
        double x1, y1, x2, y2;
 
2495
        double world_x, world_y;
 
2496
        int x_scroll, y_scroll;
 
2497
        int adj_x, adj_y;
 
2498
        gboolean adj_changed;
 
2499
        GtkAllocation allocation;
 
2500
        
 
2501
        EelDRect selection_rect;
 
2502
 
 
2503
        widget = GTK_WIDGET (data);
 
2504
        container = NAUTILUS_ICON_CONTAINER (data);
 
2505
        band_info = &container->details->rubberband_info;
 
2506
 
 
2507
        g_assert (band_info->timer_id != 0);
 
2508
 
 
2509
        adj_changed = FALSE;
 
2510
        gtk_widget_get_allocation (widget, &allocation);
 
2511
 
 
2512
        adj_x = gtk_adjustment_get_value (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (container)));
 
2513
        if (adj_x != band_info->last_adj_x) {
 
2514
                band_info->last_adj_x = adj_x;
 
2515
                adj_changed = TRUE;
 
2516
        }
 
2517
 
 
2518
        adj_y = gtk_adjustment_get_value (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (container)));
 
2519
        if (adj_y != band_info->last_adj_y) {
 
2520
                band_info->last_adj_y = adj_y;
 
2521
                adj_changed = TRUE;
 
2522
        }
 
2523
 
 
2524
        gtk_widget_get_pointer (widget, &x, &y);
 
2525
 
 
2526
        if (x < 0) {
 
2527
                x_scroll = x;
 
2528
                x = 0;
 
2529
        } else if (x >= allocation.width) {
 
2530
                x_scroll = x - allocation.width + 1;
 
2531
                x = allocation.width - 1;
 
2532
        } else {
 
2533
                x_scroll = 0;
 
2534
        }
 
2535
 
 
2536
        if (y < 0) {
 
2537
                y_scroll = y;
 
2538
                y = 0;
 
2539
        } else if (y >= allocation.height) {
 
2540
                y_scroll = y - allocation.height + 1;
 
2541
                y = allocation.height - 1;
 
2542
        } else {
 
2543
                y_scroll = 0;
 
2544
        }
 
2545
 
 
2546
        if (y_scroll == 0 && x_scroll == 0
 
2547
            && (int) band_info->prev_x == x && (int) band_info->prev_y == y && !adj_changed) {
 
2548
                return TRUE;
 
2549
        }
 
2550
 
 
2551
        nautilus_icon_container_scroll (container, x_scroll, y_scroll);
 
2552
 
 
2553
        /* Remember to convert from widget to scrolled window coords */
 
2554
        eel_canvas_window_to_world (EEL_CANVAS (container),
 
2555
                                    x + gtk_adjustment_get_value (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (container))),
 
2556
                                    y + gtk_adjustment_get_value (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (container))),
 
2557
                                    &world_x, &world_y);
 
2558
 
 
2559
        if (world_x < band_info->start_x) {
 
2560
                x1 = world_x;
 
2561
                x2 = band_info->start_x;
 
2562
        } else {
 
2563
                x1 = band_info->start_x;
 
2564
                x2 = world_x;
 
2565
        }
 
2566
 
 
2567
        if (world_y < band_info->start_y) {
 
2568
                y1 = world_y;
 
2569
                y2 = band_info->start_y;
 
2570
        } else {
 
2571
                y1 = band_info->start_y;
 
2572
                y2 = world_y;
 
2573
        }
 
2574
 
 
2575
        /* Don't let the area of the selection rectangle be empty.
 
2576
         * Aside from the fact that it would be funny when the rectangle disappears,
 
2577
         * this also works around a crash in libart that happens sometimes when a
 
2578
         * zero height rectangle is passed.
 
2579
         */
 
2580
        x2 = MAX (x1 + 1, x2);
 
2581
        y2 = MAX (y1 + 1, y2);
 
2582
 
 
2583
        eel_canvas_item_set
 
2584
                (band_info->selection_rectangle,
 
2585
                 "x1", x1, "y1", y1,
 
2586
                 "x2", x2, "y2", y2,
 
2587
                 NULL);
 
2588
 
 
2589
        selection_rect.x0 = x1;
 
2590
        selection_rect.y0 = y1;
 
2591
        selection_rect.x1 = x2;
 
2592
        selection_rect.y1 = y2;
 
2593
 
 
2594
        rubberband_select (container,
 
2595
                           &band_info->prev_rect,
 
2596
                           &selection_rect);
 
2597
        
 
2598
        band_info->prev_x = x;
 
2599
        band_info->prev_y = y;
 
2600
 
 
2601
        band_info->prev_rect = selection_rect;
 
2602
 
 
2603
        return TRUE;
 
2604
}
 
2605
 
 
2606
static void
 
2607
start_rubberbanding (NautilusIconContainer *container,
 
2608
                     GdkEventButton *event)
 
2609
{
 
2610
        AtkObject *accessible;
 
2611
        NautilusIconContainerDetails *details;
 
2612
        NautilusIconRubberbandInfo *band_info;
 
2613
        GdkRGBA bg_color, border_color;
 
2614
        GList *p;
 
2615
        NautilusIcon *icon;
 
2616
        GtkStyleContext *context;
 
2617
 
 
2618
        details = container->details;
 
2619
        band_info = &details->rubberband_info;
 
2620
 
 
2621
        g_signal_emit (container,
 
2622
                       signals[BAND_SELECT_STARTED], 0);
 
2623
 
 
2624
        for (p = details->icons; p != NULL; p = p->next) {
 
2625
                icon = p->data;
 
2626
                icon->was_selected_before_rubberband = icon->is_selected;
 
2627
        }
 
2628
 
 
2629
        eel_canvas_window_to_world
 
2630
                (EEL_CANVAS (container), event->x, event->y,
 
2631
                 &band_info->start_x, &band_info->start_y);
 
2632
 
 
2633
        context = gtk_widget_get_style_context (GTK_WIDGET (container));
 
2634
        gtk_style_context_save (context);
 
2635
        gtk_style_context_add_class (context, GTK_STYLE_CLASS_RUBBERBAND);
 
2636
 
 
2637
        gtk_style_context_get_background_color (context, GTK_STATE_FLAG_NORMAL, &bg_color);
 
2638
        gtk_style_context_get_border_color (context, GTK_STATE_FLAG_NORMAL, &border_color);
 
2639
 
 
2640
        gtk_style_context_restore (context);
 
2641
 
 
2642
        band_info->selection_rectangle = eel_canvas_item_new
 
2643
                (eel_canvas_root
 
2644
                 (EEL_CANVAS (container)),
 
2645
                 NAUTILUS_TYPE_SELECTION_CANVAS_ITEM,
 
2646
                 "x1", band_info->start_x,
 
2647
                 "y1", band_info->start_y,
 
2648
                 "x2", band_info->start_x,
 
2649
                 "y2", band_info->start_y,
 
2650
                 "fill_color_rgba", &bg_color,
 
2651
                 "outline_color_rgba", &border_color,
 
2652
                 "width_pixels", 1,
 
2653
                 NULL);
 
2654
 
 
2655
        accessible = atk_gobject_accessible_for_object
 
2656
                (G_OBJECT (band_info->selection_rectangle));
 
2657
        atk_object_set_name (accessible, "selection");
 
2658
        atk_object_set_description (accessible, _("The selection rectangle"));
 
2659
 
 
2660
        band_info->prev_x = event->x - gtk_adjustment_get_value (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (container)));
 
2661
        band_info->prev_y = event->y - gtk_adjustment_get_value (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (container)));
 
2662
 
 
2663
        band_info->active = TRUE;
 
2664
 
 
2665
        if (band_info->timer_id == 0) {
 
2666
                band_info->timer_id = g_timeout_add
 
2667
                        (RUBBERBAND_TIMEOUT_INTERVAL,
 
2668
                         rubberband_timeout_callback,
 
2669
                         container);
 
2670
        }
 
2671
 
 
2672
        eel_canvas_item_grab (band_info->selection_rectangle,
 
2673
                                (GDK_POINTER_MOTION_MASK
 
2674
                                 | GDK_BUTTON_RELEASE_MASK 
 
2675
                                 | GDK_SCROLL_MASK),
 
2676
                                NULL, event->time);
 
2677
}
 
2678
 
 
2679
static void
 
2680
stop_rubberbanding (NautilusIconContainer *container,
 
2681
                    guint32 time)
 
2682
{
 
2683
        NautilusIconRubberbandInfo *band_info;
 
2684
        GList *icons;
 
2685
 
 
2686
        band_info = &container->details->rubberband_info;
 
2687
 
 
2688
        g_assert (band_info->timer_id != 0);
 
2689
        g_source_remove (band_info->timer_id);
 
2690
        band_info->timer_id = 0;
 
2691
 
 
2692
        band_info->active = FALSE;
 
2693
 
 
2694
        /* Destroy this canvas item; the parent will unref it. */
 
2695
        eel_canvas_item_ungrab (band_info->selection_rectangle, time);
 
2696
        nautilus_selection_canvas_item_fade_out (NAUTILUS_SELECTION_CANVAS_ITEM (band_info->selection_rectangle), 150);
 
2697
        band_info->selection_rectangle = NULL;
 
2698
 
 
2699
        /* if only one item has been selected, use it as range
 
2700
         * selection base (cf. handle_icon_button_press) */
 
2701
        icons = nautilus_icon_container_get_selected_icons (container);
 
2702
        if (g_list_length (icons) == 1) {
 
2703
                container->details->range_selection_base_icon = icons->data;
 
2704
        }
 
2705
        g_list_free (icons);
 
2706
 
 
2707
        g_signal_emit (container,
 
2708
                         signals[BAND_SELECT_ENDED], 0);
 
2709
}
 
2710
 
 
2711
/* Keyboard navigation.  */
 
2712
 
 
2713
typedef gboolean (* IsBetterIconFunction) (NautilusIconContainer *container,
 
2714
                                           NautilusIcon *start_icon,
 
2715
                                           NautilusIcon *best_so_far,
 
2716
                                           NautilusIcon *candidate,
 
2717
                                           void *data);
 
2718
 
 
2719
static NautilusIcon *
 
2720
find_best_icon (NautilusIconContainer *container,
 
2721
                NautilusIcon *start_icon,
 
2722
                IsBetterIconFunction function,
 
2723
                void *data)
 
2724
{
 
2725
        GList *p;
 
2726
        NautilusIcon *best, *candidate;
 
2727
 
 
2728
        best = NULL;
 
2729
        for (p = container->details->icons; p != NULL; p = p->next) {
 
2730
                candidate = p->data;
 
2731
 
 
2732
                if (candidate != start_icon) {
 
2733
                        if ((* function) (container, start_icon, best, candidate, data)) {
 
2734
                                best = candidate;
 
2735
                        }
 
2736
                }
 
2737
        }
 
2738
        return best;
 
2739
}
 
2740
 
 
2741
static NautilusIcon *
 
2742
find_best_selected_icon (NautilusIconContainer *container,
 
2743
                         NautilusIcon *start_icon,
 
2744
                         IsBetterIconFunction function,
 
2745
                         void *data)
 
2746
{
 
2747
        GList *p;
 
2748
        NautilusIcon *best, *candidate;
 
2749
 
 
2750
        best = NULL;
 
2751
        for (p = container->details->icons; p != NULL; p = p->next) {
 
2752
                candidate = p->data;
 
2753
 
 
2754
                if (candidate != start_icon && candidate->is_selected) {
 
2755
                        if ((* function) (container, start_icon, best, candidate, data)) {
 
2756
                                best = candidate;
 
2757
                        }
 
2758
                }
 
2759
        }
 
2760
        return best;
 
2761
}
 
2762
 
 
2763
static int
 
2764
compare_icons_by_uri (NautilusIconContainer *container,
 
2765
                      NautilusIcon *icon_a,
 
2766
                      NautilusIcon *icon_b)
 
2767
{
 
2768
        char *uri_a, *uri_b;
 
2769
        int result;
 
2770
 
 
2771
        g_assert (NAUTILUS_IS_ICON_CONTAINER (container));
 
2772
        g_assert (icon_a != NULL);
 
2773
        g_assert (icon_b != NULL);
 
2774
        g_assert (icon_a != icon_b);
 
2775
 
 
2776
        uri_a = nautilus_icon_container_get_icon_uri (container, icon_a);
 
2777
        uri_b = nautilus_icon_container_get_icon_uri (container, icon_b);
 
2778
        result = strcmp (uri_a, uri_b);
 
2779
        g_assert (result != 0);
 
2780
        g_free (uri_a);
 
2781
        g_free (uri_b);
 
2782
        
 
2783
        return result;
 
2784
}
 
2785
 
 
2786
static int
 
2787
get_cmp_point_x (NautilusIconContainer *container,
 
2788
                 EelDRect icon_rect)
 
2789
{
 
2790
        if (container->details->label_position == NAUTILUS_ICON_LABEL_POSITION_BESIDE) {
 
2791
                if (gtk_widget_get_direction (GTK_WIDGET (container)) == GTK_TEXT_DIR_RTL) {
 
2792
                        return icon_rect.x0;
 
2793
                } else {
 
2794
                        return icon_rect.x1;
 
2795
                }
 
2796
        } else {
 
2797
                return (icon_rect.x0 + icon_rect.x1) / 2;
 
2798
        }
 
2799
}
 
2800
 
 
2801
static int
 
2802
get_cmp_point_y (NautilusIconContainer *container,
 
2803
                 EelDRect icon_rect)
 
2804
{
 
2805
        if (container->details->label_position == NAUTILUS_ICON_LABEL_POSITION_BESIDE) {
 
2806
                return (icon_rect.y0 + icon_rect.y1)/2;
 
2807
        } else {
 
2808
                return icon_rect.y1;
 
2809
        }
 
2810
}
 
2811
 
 
2812
 
 
2813
static int
 
2814
compare_icons_horizontal (NautilusIconContainer *container,
 
2815
                          NautilusIcon *icon_a,
 
2816
                          NautilusIcon *icon_b)
 
2817
{
 
2818
        EelDRect world_rect;
 
2819
        int ax, bx;
 
2820
 
 
2821
        world_rect = nautilus_icon_canvas_item_get_icon_rectangle (icon_a->item);
 
2822
        eel_canvas_w2c
 
2823
                (EEL_CANVAS (container),
 
2824
                 get_cmp_point_x (container, world_rect),
 
2825
                 get_cmp_point_y (container, world_rect),
 
2826
                 &ax,
 
2827
                 NULL);
 
2828
        world_rect = nautilus_icon_canvas_item_get_icon_rectangle (icon_b->item);
 
2829
        eel_canvas_w2c
 
2830
                (EEL_CANVAS (container),
 
2831
                 get_cmp_point_x (container, world_rect),
 
2832
                 get_cmp_point_y (container, world_rect),
 
2833
                 &bx,
 
2834
                 NULL);
 
2835
        
 
2836
        if (ax < bx) {
 
2837
                return -1;
 
2838
        }
 
2839
        if (ax > bx) {
 
2840
                return +1;
 
2841
        }
 
2842
        return 0;
 
2843
}
 
2844
 
 
2845
static int
 
2846
compare_icons_vertical (NautilusIconContainer *container,
 
2847
                        NautilusIcon *icon_a,
 
2848
                        NautilusIcon *icon_b)
 
2849
{
 
2850
        EelDRect world_rect;
 
2851
        int ay, by;
 
2852
 
 
2853
        world_rect = nautilus_icon_canvas_item_get_icon_rectangle (icon_a->item);
 
2854
        eel_canvas_w2c
 
2855
                (EEL_CANVAS (container),
 
2856
                 get_cmp_point_x (container, world_rect),
 
2857
                 get_cmp_point_y (container, world_rect),
 
2858
                 NULL,
 
2859
                 &ay);
 
2860
        world_rect = nautilus_icon_canvas_item_get_icon_rectangle (icon_b->item);
 
2861
        eel_canvas_w2c
 
2862
                (EEL_CANVAS (container),
 
2863
                 get_cmp_point_x (container, world_rect),
 
2864
                 get_cmp_point_y (container, world_rect),
 
2865
                 NULL,
 
2866
                 &by);
 
2867
        
 
2868
        if (ay < by) {
 
2869
                return -1;
 
2870
        }
 
2871
        if (ay > by) {
 
2872
                return +1;
 
2873
        }
 
2874
        return 0;
 
2875
}
 
2876
 
 
2877
static int
 
2878
compare_icons_horizontal_first (NautilusIconContainer *container,
 
2879
                                NautilusIcon *icon_a,
 
2880
                                NautilusIcon *icon_b)
 
2881
{
 
2882
        EelDRect world_rect;
 
2883
        int ax, ay, bx, by;
 
2884
 
 
2885
        world_rect = nautilus_icon_canvas_item_get_icon_rectangle (icon_a->item);
 
2886
        eel_canvas_w2c
 
2887
                (EEL_CANVAS (container),
 
2888
                 get_cmp_point_x (container, world_rect),
 
2889
                 get_cmp_point_y (container, world_rect),
 
2890
                 &ax,
 
2891
                 &ay);
 
2892
        world_rect = nautilus_icon_canvas_item_get_icon_rectangle (icon_b->item);
 
2893
        eel_canvas_w2c
 
2894
                (EEL_CANVAS (container),
 
2895
                 get_cmp_point_x (container, world_rect),
 
2896
                 get_cmp_point_y (container, world_rect),
 
2897
                 &bx,
 
2898
                 &by);
 
2899
        
 
2900
        if (ax < bx) {
 
2901
                return -1;
 
2902
        }
 
2903
        if (ax > bx) {
 
2904
                return +1;
 
2905
        }
 
2906
        if (ay < by) {
 
2907
                return -1;
 
2908
        }
 
2909
        if (ay > by) {
 
2910
                return +1;
 
2911
        }
 
2912
        return compare_icons_by_uri (container, icon_a, icon_b);
 
2913
}
 
2914
 
 
2915
static int
 
2916
compare_icons_vertical_first (NautilusIconContainer *container,
 
2917
                              NautilusIcon *icon_a,
 
2918
                              NautilusIcon *icon_b)
 
2919
{
 
2920
        EelDRect world_rect;
 
2921
        int ax, ay, bx, by;
 
2922
 
 
2923
        world_rect = nautilus_icon_canvas_item_get_icon_rectangle (icon_a->item);
 
2924
        eel_canvas_w2c
 
2925
                (EEL_CANVAS (container),
 
2926
                 get_cmp_point_x (container, world_rect),
 
2927
                 get_cmp_point_y (container, world_rect),
 
2928
                 &ax,
 
2929
                 &ay);
 
2930
        world_rect = nautilus_icon_canvas_item_get_icon_rectangle (icon_b->item);
 
2931
        eel_canvas_w2c
 
2932
                (EEL_CANVAS (container),
 
2933
                 get_cmp_point_x (container, world_rect),
 
2934
                 get_cmp_point_y (container, world_rect),
 
2935
                 &bx,
 
2936
                 &by);
 
2937
        
 
2938
        if (ay < by) {
 
2939
                return -1;
 
2940
        }
 
2941
        if (ay > by) {
 
2942
                return +1;
 
2943
        }
 
2944
        if (ax < bx) {
 
2945
                return -1;
 
2946
        }
 
2947
        if (ax > bx) {
 
2948
                return +1;
 
2949
        }
 
2950
        return compare_icons_by_uri (container, icon_a, icon_b);
 
2951
}
 
2952
 
 
2953
static gboolean
 
2954
leftmost_in_top_row (NautilusIconContainer *container,
 
2955
                     NautilusIcon *start_icon,
 
2956
                     NautilusIcon *best_so_far,
 
2957
                     NautilusIcon *candidate,
 
2958
                     void *data)
 
2959
{
 
2960
        if (best_so_far == NULL) {
 
2961
                return TRUE;
 
2962
        }
 
2963
        return compare_icons_vertical_first (container, best_so_far, candidate) > 0;
 
2964
}
 
2965
 
 
2966
static gboolean
 
2967
rightmost_in_top_row (NautilusIconContainer *container,
 
2968
                      NautilusIcon *start_icon,
 
2969
                      NautilusIcon *best_so_far,
 
2970
                      NautilusIcon *candidate,
 
2971
                      void *data)
 
2972
{
 
2973
        if (best_so_far == NULL) {
 
2974
                return TRUE;
 
2975
        }
 
2976
        return compare_icons_vertical (container, best_so_far, candidate) > 0;
 
2977
        return compare_icons_horizontal (container, best_so_far, candidate) < 0;
 
2978
}
 
2979
 
 
2980
static gboolean
 
2981
rightmost_in_bottom_row (NautilusIconContainer *container,
 
2982
                         NautilusIcon *start_icon,
 
2983
                         NautilusIcon *best_so_far,
 
2984
                         NautilusIcon *candidate,
 
2985
                         void *data)
 
2986
{
 
2987
        if (best_so_far == NULL) {
 
2988
                return TRUE;
 
2989
        }
 
2990
        return compare_icons_vertical_first (container, best_so_far, candidate) < 0;
 
2991
}
 
2992
 
 
2993
static int
 
2994
compare_with_start_row (NautilusIconContainer *container,
 
2995
                        NautilusIcon *icon)
 
2996
{
 
2997
        EelCanvasItem *item;
 
2998
 
 
2999
        item = EEL_CANVAS_ITEM (icon->item);
 
3000
        
 
3001
        if (container->details->arrow_key_start_y < item->y1) {
 
3002
                return -1;
 
3003
        }
 
3004
        if (container->details->arrow_key_start_y > item->y2) {
 
3005
                return +1;
 
3006
        }
 
3007
        return 0;
 
3008
}
 
3009
 
 
3010
static int
 
3011
compare_with_start_column (NautilusIconContainer *container,
 
3012
                           NautilusIcon *icon)
 
3013
{
 
3014
        EelCanvasItem *item;
 
3015
 
 
3016
        item = EEL_CANVAS_ITEM (icon->item);
 
3017
        
 
3018
        if (container->details->arrow_key_start_x < item->x1) {
 
3019
                return -1;
 
3020
        }
 
3021
        if (container->details->arrow_key_start_x > item->x2) {
 
3022
                return +1;
 
3023
        }
 
3024
        return 0;
 
3025
}
 
3026
 
 
3027
static gboolean
 
3028
same_row_right_side_leftmost (NautilusIconContainer *container,
 
3029
                              NautilusIcon *start_icon,
 
3030
                              NautilusIcon *best_so_far,
 
3031
                              NautilusIcon *candidate,
 
3032
                              void *data)
 
3033
{
 
3034
        /* Candidates not on the start row do not qualify. */
 
3035
        if (compare_with_start_row (container, candidate) != 0) {
 
3036
                return FALSE;
 
3037
        }
 
3038
 
 
3039
        /* Candidates that are farther right lose out. */
 
3040
        if (best_so_far != NULL) {
 
3041
                if (compare_icons_horizontal_first (container,
 
3042
                                                    best_so_far,
 
3043
                                                    candidate) < 0) {
 
3044
                        return FALSE;
 
3045
                }
 
3046
        }
 
3047
 
 
3048
        /* Candidate to the left of the start do not qualify. */
 
3049
        if (compare_icons_horizontal_first (container,
 
3050
                                            candidate,
 
3051
                                            start_icon) <= 0) {
 
3052
                return FALSE;
 
3053
        }
 
3054
 
 
3055
        return TRUE;
 
3056
}
 
3057
 
 
3058
static gboolean
 
3059
same_row_left_side_rightmost (NautilusIconContainer *container,
 
3060
                              NautilusIcon *start_icon,
 
3061
                              NautilusIcon *best_so_far,
 
3062
                              NautilusIcon *candidate,
 
3063
                              void *data)
 
3064
{
 
3065
        /* Candidates not on the start row do not qualify. */
 
3066
        if (compare_with_start_row (container, candidate) != 0) {
 
3067
                return FALSE;
 
3068
        }
 
3069
 
 
3070
        /* Candidates that are farther left lose out. */
 
3071
        if (best_so_far != NULL) {
 
3072
                if (compare_icons_horizontal_first (container,
 
3073
                                                    best_so_far,
 
3074
                                                    candidate) > 0) {
 
3075
                        return FALSE;
 
3076
                }
 
3077
        }
 
3078
 
 
3079
        /* Candidate to the right of the start do not qualify. */
 
3080
        if (compare_icons_horizontal_first (container,
 
3081
                                            candidate,
 
3082
                                            start_icon) >= 0) {
 
3083
                return FALSE;
 
3084
        }
 
3085
 
 
3086
        return TRUE;
 
3087
}
 
3088
 
 
3089
static gboolean
 
3090
next_row_leftmost (NautilusIconContainer *container,
 
3091
                   NautilusIcon *start_icon,
 
3092
                   NautilusIcon *best_so_far,
 
3093
                   NautilusIcon *candidate,
 
3094
                   void *data)
 
3095
{
 
3096
        /* sort out icons that are not below the current row */
 
3097
        if (compare_with_start_row (container, candidate) >= 0) {
 
3098
                return FALSE;
 
3099
        }
 
3100
 
 
3101
        if (best_so_far != NULL) {
 
3102
                if (compare_icons_vertical_first (container,
 
3103
                                                  best_so_far,
 
3104
                                                  candidate) > 0) {
 
3105
                        /* candidate is above best choice, but below the current row */
 
3106
                        return TRUE;
 
3107
                }
 
3108
 
 
3109
                if (compare_icons_horizontal_first (container,
 
3110
                                                    best_so_far,
 
3111
                                                    candidate) > 0) {
 
3112
                        return TRUE;
 
3113
                }
 
3114
        }
 
3115
 
 
3116
        return best_so_far == NULL;
 
3117
}
 
3118
 
 
3119
static gboolean
 
3120
next_row_rightmost (NautilusIconContainer *container,
 
3121
                    NautilusIcon *start_icon,
 
3122
                    NautilusIcon *best_so_far,
 
3123
                    NautilusIcon *candidate,
 
3124
                    void *data)
 
3125
{
 
3126
        /* sort out icons that are not below the current row */
 
3127
        if (compare_with_start_row (container, candidate) >= 0) {
 
3128
                return FALSE;
 
3129
        }
 
3130
 
 
3131
        if (best_so_far != NULL) {
 
3132
                if (compare_icons_vertical_first (container,
 
3133
                                                  best_so_far,
 
3134
                                                  candidate) > 0) {
 
3135
                        /* candidate is above best choice, but below the current row */
 
3136
                        return TRUE;
 
3137
                }
 
3138
 
 
3139
                if (compare_icons_horizontal_first (container,
 
3140
                                                    best_so_far,
 
3141
                                                    candidate) < 0) {
 
3142
                        return TRUE;
 
3143
                }
 
3144
        }
 
3145
 
 
3146
        return best_so_far == NULL;
 
3147
}
 
3148
 
 
3149
static gboolean
 
3150
next_column_bottommost (NautilusIconContainer *container,
 
3151
                        NautilusIcon *start_icon,
 
3152
                        NautilusIcon *best_so_far,
 
3153
                        NautilusIcon *candidate,
 
3154
                        void *data)
 
3155
{
 
3156
        /* sort out icons that are not on the right of the current column */
 
3157
        if (compare_with_start_column (container, candidate) >= 0) {
 
3158
                return FALSE;
 
3159
        }
 
3160
 
 
3161
        if (best_so_far != NULL) {
 
3162
                if (compare_icons_horizontal_first (container,
 
3163
                                                  best_so_far,
 
3164
                                                  candidate) > 0) {
 
3165
                        /* candidate is above best choice, but below the current row */
 
3166
                        return TRUE;
 
3167
                }
 
3168
 
 
3169
                if (compare_icons_vertical_first (container,
 
3170
                                                  best_so_far,
 
3171
                                                  candidate) < 0) {
 
3172
                        return TRUE;
 
3173
                }
 
3174
        }
 
3175
 
 
3176
        return best_so_far == NULL;
 
3177
}
 
3178
 
 
3179
static gboolean
 
3180
previous_row_rightmost (NautilusIconContainer *container,
 
3181
                        NautilusIcon *start_icon,
 
3182
                        NautilusIcon *best_so_far,
 
3183
                        NautilusIcon *candidate,
 
3184
                        void *data)
 
3185
{
 
3186
        /* sort out icons that are not above the current row */
 
3187
        if (compare_with_start_row (container, candidate) <= 0) {
 
3188
                return FALSE;
 
3189
        }
 
3190
 
 
3191
        if (best_so_far != NULL) {
 
3192
                if (compare_icons_vertical_first (container,
 
3193
                                                  best_so_far,
 
3194
                                                  candidate) < 0) {
 
3195
                        /* candidate is below the best choice, but above the current row */
 
3196
                        return TRUE;
 
3197
                }
 
3198
 
 
3199
                if (compare_icons_horizontal_first (container,
 
3200
                                                    best_so_far,
 
3201
                                                    candidate) < 0) {
 
3202
                        return TRUE;
 
3203
                }
 
3204
        }
 
3205
 
 
3206
        return best_so_far == NULL;
 
3207
}
 
3208
 
 
3209
static gboolean
 
3210
same_column_above_lowest (NautilusIconContainer *container,
 
3211
                          NautilusIcon *start_icon,
 
3212
                          NautilusIcon *best_so_far,
 
3213
                          NautilusIcon *candidate,
 
3214
                          void *data)
 
3215
{
 
3216
        /* Candidates not on the start column do not qualify. */
 
3217
        if (compare_with_start_column (container, candidate) != 0) {
 
3218
                return FALSE;
 
3219
        }
 
3220
 
 
3221
        /* Candidates that are higher lose out. */
 
3222
        if (best_so_far != NULL) {
 
3223
                if (compare_icons_vertical_first (container,
 
3224
                                                  best_so_far,
 
3225
                                                  candidate) > 0) {
 
3226
                        return FALSE;
 
3227
                }
 
3228
        }
 
3229
 
 
3230
        /* Candidates below the start do not qualify. */
 
3231
        if (compare_icons_vertical_first (container,
 
3232
                                          candidate,
 
3233
                                          start_icon) >= 0) {
 
3234
                return FALSE;
 
3235
        }
 
3236
 
 
3237
        return TRUE;
 
3238
}
 
3239
 
 
3240
static gboolean
 
3241
same_column_below_highest (NautilusIconContainer *container,
 
3242
                           NautilusIcon *start_icon,
 
3243
                           NautilusIcon *best_so_far,
 
3244
                           NautilusIcon *candidate,
 
3245
                           void *data)
 
3246
{
 
3247
        /* Candidates not on the start column do not qualify. */
 
3248
        if (compare_with_start_column (container, candidate) != 0) {
 
3249
                return FALSE;
 
3250
        }
 
3251
 
 
3252
        /* Candidates that are lower lose out. */
 
3253
        if (best_so_far != NULL) {
 
3254
                if (compare_icons_vertical_first (container,
 
3255
                                                  best_so_far,
 
3256
                                                  candidate) < 0) {
 
3257
                        return FALSE;
 
3258
                }
 
3259
        }
 
3260
 
 
3261
        /* Candidates above the start do not qualify. */
 
3262
        if (compare_icons_vertical_first (container,
 
3263
                                          candidate,
 
3264
                                          start_icon) <= 0) {
 
3265
                return FALSE;
 
3266
        }
 
3267
 
 
3268
        return TRUE;
 
3269
}
 
3270
 
 
3271
static gboolean
 
3272
previous_column_highest (NautilusIconContainer *container,
 
3273
                         NautilusIcon *start_icon,
 
3274
                         NautilusIcon *best_so_far,
 
3275
                         NautilusIcon *candidate,
 
3276
                         void *data)
 
3277
{
 
3278
        /* sort out icons that are not before the current column */
 
3279
        if (compare_with_start_column (container, candidate) <= 0) {
 
3280
                return FALSE;
 
3281
        }
 
3282
 
 
3283
        if (best_so_far != NULL) {
 
3284
                if (compare_icons_horizontal (container,
 
3285
                                              best_so_far,
 
3286
                                              candidate) < 0) {
 
3287
                        /* candidate is right of the best choice, but left of the current column */
 
3288
                        return TRUE;
 
3289
                }
 
3290
 
 
3291
                if (compare_icons_vertical (container,
 
3292
                                            best_so_far,
 
3293
                                            candidate) > 0) {
 
3294
                        return TRUE;
 
3295
                }
 
3296
        }
 
3297
 
 
3298
        return best_so_far == NULL;
 
3299
}
 
3300
 
 
3301
 
 
3302
static gboolean
 
3303
next_column_highest (NautilusIconContainer *container,
 
3304
                     NautilusIcon *start_icon,
 
3305
                     NautilusIcon *best_so_far,
 
3306
                     NautilusIcon *candidate,
 
3307
                     void *data)
 
3308
{
 
3309
        /* sort out icons that are not after the current column */
 
3310
        if (compare_with_start_column (container, candidate) >= 0) {
 
3311
                return FALSE;
 
3312
        }
 
3313
 
 
3314
        if (best_so_far != NULL) {
 
3315
                if (compare_icons_horizontal_first (container,
 
3316
                                                    best_so_far,
 
3317
                                                    candidate) > 0) {
 
3318
                        /* candidate is left of the best choice, but right of the current column */
 
3319
                        return TRUE;
 
3320
                }
 
3321
 
 
3322
                if (compare_icons_vertical_first (container,
 
3323
                                                  best_so_far,
 
3324
                                                  candidate) > 0) {
 
3325
                        return TRUE;
 
3326
                }
 
3327
        }
 
3328
 
 
3329
        return best_so_far == NULL;
 
3330
}
 
3331
 
 
3332
static gboolean
 
3333
previous_column_lowest (NautilusIconContainer *container,
 
3334
                        NautilusIcon *start_icon,
 
3335
                        NautilusIcon *best_so_far,
 
3336
                        NautilusIcon *candidate,
 
3337
                        void *data)
 
3338
{
 
3339
        /* sort out icons that are not before the current column */
 
3340
        if (compare_with_start_column (container, candidate) <= 0) {
 
3341
                return FALSE;
 
3342
        }
 
3343
 
 
3344
        if (best_so_far != NULL) {
 
3345
                if (compare_icons_horizontal_first (container,
 
3346
                                                    best_so_far,
 
3347
                                                    candidate) < 0) {
 
3348
                        /* candidate is right of the best choice, but left of the current column */
 
3349
                        return TRUE;
 
3350
                }
 
3351
 
 
3352
                if (compare_icons_vertical_first (container,
 
3353
                                                  best_so_far,
 
3354
                                                  candidate) < 0) {
 
3355
                        return TRUE;
 
3356
                }
 
3357
        }
 
3358
 
 
3359
        return best_so_far == NULL;
 
3360
}
 
3361
 
 
3362
static gboolean
 
3363
last_column_lowest (NautilusIconContainer *container,
 
3364
                    NautilusIcon *start_icon,
 
3365
                    NautilusIcon *best_so_far,
 
3366
                    NautilusIcon *candidate,
 
3367
                    void *data)
 
3368
{
 
3369
        if (best_so_far == NULL) {
 
3370
                return TRUE;
 
3371
        }
 
3372
        return compare_icons_horizontal_first (container, best_so_far, candidate) < 0;
 
3373
}
 
3374
 
 
3375
static gboolean
 
3376
closest_in_90_degrees (NautilusIconContainer *container,
 
3377
                       NautilusIcon *start_icon,
 
3378
                       NautilusIcon *best_so_far,
 
3379
                       NautilusIcon *candidate,
 
3380
                       void *data)
 
3381
{
 
3382
        EelDRect world_rect;
 
3383
        int x, y;
 
3384
        int dx, dy;
 
3385
        int dist;
 
3386
        int *best_dist;
 
3387
 
 
3388
 
 
3389
        world_rect = nautilus_icon_canvas_item_get_icon_rectangle (candidate->item);
 
3390
        eel_canvas_w2c
 
3391
                (EEL_CANVAS (container),
 
3392
                 get_cmp_point_x (container, world_rect),
 
3393
                 get_cmp_point_y (container, world_rect),
 
3394
                 &x,
 
3395
                 &y);
 
3396
 
 
3397
        dx = x - container->details->arrow_key_start_x;
 
3398
        dy = y - container->details->arrow_key_start_y;
 
3399
        
 
3400
        switch (container->details->arrow_key_direction) {
 
3401
        case GTK_DIR_UP:
 
3402
                if (dy > 0 ||
 
3403
                    ABS(dx) > ABS(dy)) {
 
3404
                        return FALSE;
 
3405
                }
 
3406
                break;
 
3407
        case GTK_DIR_DOWN:
 
3408
                if (dy < 0 ||
 
3409
                    ABS(dx) > ABS(dy)) {
 
3410
                        return FALSE;
 
3411
                }
 
3412
                break;
 
3413
        case GTK_DIR_LEFT:
 
3414
                if (dx > 0 ||
 
3415
                    ABS(dy) > ABS(dx)) {
 
3416
                        return FALSE;
 
3417
                }
 
3418
                break;
 
3419
        case GTK_DIR_RIGHT:
 
3420
                if (dx < 0 ||
 
3421
                    ABS(dy) > ABS(dx)) {
 
3422
                        return FALSE;
 
3423
                }
 
3424
                break;
 
3425
        default:
 
3426
                g_assert_not_reached();
 
3427
        }
 
3428
 
 
3429
        dist = dx*dx + dy*dy;
 
3430
        best_dist = data;
 
3431
        
 
3432
        if (best_so_far == NULL) {
 
3433
                *best_dist = dist;
 
3434
                return TRUE;
 
3435
        }
 
3436
 
 
3437
        if (dist < *best_dist) {
 
3438
                *best_dist = dist;
 
3439
                return TRUE;
 
3440
        }
 
3441
 
 
3442
        return FALSE;
 
3443
}
 
3444
 
 
3445
static EelDRect 
 
3446
get_rubberband (NautilusIcon *icon1,
 
3447
                NautilusIcon *icon2)
 
3448
{
 
3449
        EelDRect rect1;
 
3450
        EelDRect rect2;
 
3451
        EelDRect ret;
 
3452
 
 
3453
        eel_canvas_item_get_bounds (EEL_CANVAS_ITEM (icon1->item),
 
3454
                                    &rect1.x0, &rect1.y0, 
 
3455
                                    &rect1.x1, &rect1.y1);
 
3456
        eel_canvas_item_get_bounds (EEL_CANVAS_ITEM (icon2->item),
 
3457
                                    &rect2.x0, &rect2.y0, 
 
3458
                                    &rect2.x1, &rect2.y1);
 
3459
 
 
3460
        eel_drect_union (&ret, &rect1, &rect2);
 
3461
 
 
3462
        return ret;
 
3463
}
 
3464
 
 
3465
static void
 
3466
keyboard_move_to (NautilusIconContainer *container,
 
3467
                  NautilusIcon *icon,
 
3468
                  NautilusIcon *from,
 
3469
                  GdkEventKey *event)
 
3470
{
 
3471
        if (icon == NULL) {
 
3472
                return;
 
3473
        }
 
3474
 
 
3475
        if (event != NULL &&
 
3476
            (event->state & GDK_CONTROL_MASK) != 0 &&
 
3477
            (event->state & GDK_SHIFT_MASK) == 0) {
 
3478
                /* Move the keyboard focus. Use Control modifier
 
3479
                 * rather than Alt to avoid Sawfish conflict.
 
3480
                 */
 
3481
                set_keyboard_focus (container, icon);
 
3482
                container->details->keyboard_rubberband_start = NULL;
 
3483
        } else if (event != NULL &&
 
3484
                   ((event->state & GDK_CONTROL_MASK) != 0 ||
 
3485
                    !container->details->auto_layout) &&
 
3486
                   (event->state & GDK_SHIFT_MASK) != 0) {
 
3487
                /* Do rubberband selection */           
 
3488
                EelDRect rect;
 
3489
 
 
3490
                if (from && !container->details->keyboard_rubberband_start) {
 
3491
                        set_keyboard_rubberband_start (container, from);
 
3492
                } 
 
3493
 
 
3494
                set_keyboard_focus (container, icon);
 
3495
 
 
3496
                if (icon && container->details->keyboard_rubberband_start) {
 
3497
                        rect = get_rubberband (container->details->keyboard_rubberband_start,
 
3498
                                               icon);
 
3499
                        rubberband_select (container, NULL, &rect);
 
3500
                }
 
3501
        } else if (event != NULL &&
 
3502
                   (event->state & GDK_CONTROL_MASK) == 0 &&
 
3503
                   (event->state & GDK_SHIFT_MASK) != 0) {
 
3504
                /* Select range */
 
3505
                NautilusIcon *start_icon;
 
3506
 
 
3507
                start_icon = container->details->range_selection_base_icon;
 
3508
                if (start_icon == NULL || !start_icon->is_selected) {
 
3509
                        start_icon = icon;
 
3510
                        container->details->range_selection_base_icon = icon;
 
3511
                } 
 
3512
 
 
3513
                set_keyboard_focus (container, icon);
 
3514
 
 
3515
                if (select_range (container, start_icon, icon, TRUE)) {
 
3516
                        g_signal_emit (container,
 
3517
                                       signals[SELECTION_CHANGED], 0);
 
3518
                }
 
3519
        } else {
 
3520
                /* Select icons and get rid of the special keyboard focus. */
 
3521
                clear_keyboard_focus (container);
 
3522
                clear_keyboard_rubberband_start (container);
 
3523
                
 
3524
                container->details->range_selection_base_icon = icon;
 
3525
                if (select_one_unselect_others (container, icon)) {
 
3526
                        g_signal_emit (container,
 
3527
                                         signals[SELECTION_CHANGED], 0);
 
3528
                }
 
3529
        }
 
3530
        schedule_keyboard_icon_reveal (container, icon);
 
3531
}
 
3532
 
 
3533
static void
 
3534
keyboard_home (NautilusIconContainer *container,
 
3535
               GdkEventKey *event)
 
3536
{
 
3537
        NautilusIcon *from;
 
3538
        NautilusIcon *to;
 
3539
        
 
3540
        /* Home selects the first icon.
 
3541
         * Control-Home sets the keyboard focus to the first icon.
 
3542
         */
 
3543
 
 
3544
        from = find_best_selected_icon (container, NULL,
 
3545
                                        rightmost_in_bottom_row, 
 
3546
                                        NULL);
 
3547
        to = find_best_icon (container, NULL, leftmost_in_top_row, NULL);       
 
3548
 
 
3549
        keyboard_move_to (container, to, from, event);
 
3550
}
 
3551
 
 
3552
static void
 
3553
keyboard_end (NautilusIconContainer *container,
 
3554
              GdkEventKey *event)
 
3555
{
 
3556
        NautilusIcon *to;
 
3557
        NautilusIcon *from;
 
3558
 
 
3559
        /* End selects the last icon.
 
3560
         * Control-End sets the keyboard focus to the last icon.
 
3561
         */
 
3562
        from = find_best_selected_icon (container, NULL,
 
3563
                                        leftmost_in_top_row, 
 
3564
                                        NULL);
 
3565
        to = find_best_icon (container, NULL,
 
3566
                             nautilus_icon_container_is_layout_vertical (container) ?
 
3567
                             last_column_lowest :
 
3568
                             rightmost_in_bottom_row,
 
3569
                             NULL);
 
3570
 
 
3571
        keyboard_move_to (container, to, from, event);
 
3572
}
 
3573
 
 
3574
static void
 
3575
record_arrow_key_start (NautilusIconContainer *container,
 
3576
                        NautilusIcon *icon,
 
3577
                        GtkDirectionType direction)
 
3578
{
 
3579
        EelDRect world_rect;
 
3580
 
 
3581
        world_rect = nautilus_icon_canvas_item_get_icon_rectangle (icon->item);
 
3582
        eel_canvas_w2c
 
3583
                (EEL_CANVAS (container),
 
3584
                 get_cmp_point_x (container, world_rect),
 
3585
                 get_cmp_point_y (container, world_rect),
 
3586
                 &container->details->arrow_key_start_x,
 
3587
                 &container->details->arrow_key_start_y);
 
3588
        container->details->arrow_key_direction = direction;
 
3589
}
 
3590
 
 
3591
static void
 
3592
keyboard_arrow_key (NautilusIconContainer *container,
 
3593
                    GdkEventKey *event,
 
3594
                    GtkDirectionType direction,
 
3595
                    IsBetterIconFunction better_start,
 
3596
                    IsBetterIconFunction empty_start,
 
3597
                    IsBetterIconFunction better_destination,
 
3598
                    IsBetterIconFunction better_destination_fallback_if_no_a11y,
 
3599
                    IsBetterIconFunction better_destination_fallback_fallback,
 
3600
                    IsBetterIconFunction better_destination_manual)
 
3601
{
 
3602
        NautilusIcon *from;
 
3603
        NautilusIcon *to;
 
3604
        int data;
 
3605
 
 
3606
        /* Chose the icon to start with.
 
3607
         * If we have a keyboard focus, start with it.
 
3608
         * Otherwise, use the single selected icon.
 
3609
         * If there's multiple selection, use the icon farthest toward the end.
 
3610
         */
 
3611
        
 
3612
        from = container->details->keyboard_focus;
 
3613
 
 
3614
        if (from == NULL) {
 
3615
                if (has_multiple_selection (container)) {
 
3616
                        if (all_selected (container)) {
 
3617
                                from = find_best_selected_icon
 
3618
                                        (container, NULL,
 
3619
                                         empty_start, NULL);
 
3620
                        } else {
 
3621
                                from = find_best_selected_icon
 
3622
                                        (container, NULL,
 
3623
                                         better_start, NULL);
 
3624
                        }
 
3625
                } else {
 
3626
                        from = get_first_selected_icon (container);
 
3627
                }
 
3628
        }
 
3629
 
 
3630
        /* If there's no icon, select the icon farthest toward the end.
 
3631
         * If there is an icon, select the next icon based on the arrow direction.
 
3632
         */
 
3633
        if (from == NULL) {
 
3634
                to = from = find_best_icon
 
3635
                        (container, NULL,
 
3636
                         empty_start, NULL);
 
3637
        } else {
 
3638
                record_arrow_key_start (container, from, direction);
 
3639
                
 
3640
                to = find_best_icon
 
3641
                        (container, from,
 
3642
                         container->details->auto_layout ? better_destination : better_destination_manual,
 
3643
                         &data);
 
3644
 
 
3645
                /* only wrap around to next/previous row/column if no a11y is used.
 
3646
                 * Visually impaired people may be easily confused by this.
 
3647
                 */
 
3648
                if (to == NULL &&
 
3649
                    better_destination_fallback_if_no_a11y != NULL &&
 
3650
                    ATK_IS_NO_OP_OBJECT (gtk_widget_get_accessible (GTK_WIDGET (container)))) {
 
3651
                        to = find_best_icon
 
3652
                                (container, from,
 
3653
                                 better_destination_fallback_if_no_a11y,
 
3654
                                 &data);
 
3655
                }
 
3656
 
 
3657
                /* With a layout like
 
3658
                 * 1 2 3
 
3659
                 * 4
 
3660
                 * (horizontal layout)
 
3661
                 *
 
3662
                 * or
 
3663
                 *
 
3664
                 * 1 4
 
3665
                 * 2
 
3666
                 * 3 
 
3667
                 * (vertical layout)
 
3668
                 *
 
3669
                 * * pressing down for any of 1,2,3 (horizontal layout)
 
3670
                 * * pressing right for any of 1,2,3 (vertical layout)
 
3671
                 *
 
3672
                 * Should select 4.
 
3673
                 */
 
3674
                if (to == NULL &&
 
3675
                    container->details->auto_layout &&
 
3676
                    better_destination_fallback_fallback != NULL) {
 
3677
                        to = find_best_icon
 
3678
                                (container, from,
 
3679
                                 better_destination_fallback_fallback,
 
3680
                                 &data);
 
3681
                }
 
3682
 
 
3683
                if (to == NULL) { 
 
3684
                        to = from;
 
3685
                }
 
3686
 
 
3687
        }
 
3688
 
 
3689
        keyboard_move_to (container, to, from, event);
 
3690
}
 
3691
 
 
3692
static gboolean
 
3693
is_rectangle_selection_event (GdkEventKey *event)
 
3694
{
 
3695
        return (event->state & GDK_CONTROL_MASK) != 0 &&
 
3696
               (event->state & GDK_SHIFT_MASK) != 0;
 
3697
}
 
3698
 
 
3699
static void
 
3700
keyboard_right (NautilusIconContainer *container,
 
3701
                GdkEventKey *event)
 
3702
{
 
3703
        IsBetterIconFunction no_a11y;
 
3704
        IsBetterIconFunction next_column_fallback;
 
3705
 
 
3706
        no_a11y = NULL;
 
3707
        if (container->details->auto_layout &&
 
3708
            !nautilus_icon_container_is_layout_vertical (container) &&
 
3709
            !is_rectangle_selection_event (event)) {
 
3710
                no_a11y = next_row_leftmost;
 
3711
        }
 
3712
 
 
3713
        next_column_fallback = NULL;
 
3714
        if (nautilus_icon_container_is_layout_vertical (container) &&
 
3715
            gtk_widget_get_direction (GTK_WIDGET (container)) != GTK_TEXT_DIR_RTL) {
 
3716
                next_column_fallback = next_column_bottommost;
 
3717
        }
 
3718
 
 
3719
        /* Right selects the next icon in the same row.
 
3720
         * Control-Right sets the keyboard focus to the next icon in the same row.
 
3721
         */
 
3722
        keyboard_arrow_key (container,
 
3723
                            event,
 
3724
                            GTK_DIR_RIGHT,
 
3725
                            rightmost_in_bottom_row,
 
3726
                            nautilus_icon_container_is_layout_rtl (container) ?
 
3727
                            rightmost_in_top_row : leftmost_in_top_row,
 
3728
                            same_row_right_side_leftmost,
 
3729
                            no_a11y,
 
3730
                            next_column_fallback,
 
3731
                            closest_in_90_degrees);
 
3732
}
 
3733
 
 
3734
static void
 
3735
keyboard_left (NautilusIconContainer *container,
 
3736
               GdkEventKey *event)
 
3737
{
 
3738
        IsBetterIconFunction no_a11y;
 
3739
        IsBetterIconFunction previous_column_fallback;
 
3740
 
 
3741
        no_a11y = NULL;
 
3742
        if (container->details->auto_layout &&
 
3743
            !nautilus_icon_container_is_layout_vertical (container) &&
 
3744
            !is_rectangle_selection_event (event)) {
 
3745
                no_a11y = previous_row_rightmost;
 
3746
        }
 
3747
 
 
3748
        previous_column_fallback = NULL;
 
3749
        if (nautilus_icon_container_is_layout_vertical (container) &&
 
3750
            gtk_widget_get_direction (GTK_WIDGET (container)) == GTK_TEXT_DIR_RTL) {
 
3751
                previous_column_fallback = previous_column_lowest;
 
3752
        }
 
3753
 
 
3754
        /* Left selects the next icon in the same row.
 
3755
         * Control-Left sets the keyboard focus to the next icon in the same row.
 
3756
         */
 
3757
        keyboard_arrow_key (container,
 
3758
                            event,
 
3759
                            GTK_DIR_LEFT,
 
3760
                            rightmost_in_bottom_row,
 
3761
                            nautilus_icon_container_is_layout_rtl (container) ?
 
3762
                            rightmost_in_top_row : leftmost_in_top_row,
 
3763
                            same_row_left_side_rightmost,
 
3764
                            no_a11y,
 
3765
                            previous_column_fallback,
 
3766
                            closest_in_90_degrees);
 
3767
}
 
3768
 
 
3769
static void
 
3770
keyboard_down (NautilusIconContainer *container,
 
3771
               GdkEventKey *event)
 
3772
{
 
3773
        IsBetterIconFunction no_a11y;
 
3774
        IsBetterIconFunction next_row_fallback;
 
3775
 
 
3776
        no_a11y = NULL;
 
3777
        if (container->details->auto_layout &&
 
3778
            nautilus_icon_container_is_layout_vertical (container) &&
 
3779
            !is_rectangle_selection_event (event)) {
 
3780
                if (gtk_widget_get_direction (GTK_WIDGET (container)) == GTK_TEXT_DIR_RTL) {
 
3781
                        no_a11y = previous_column_highest;
 
3782
                } else {
 
3783
                        no_a11y = next_column_highest;
 
3784
                }
 
3785
        }
 
3786
 
 
3787
        next_row_fallback = NULL;
 
3788
        if (!nautilus_icon_container_is_layout_vertical (container)) {
 
3789
                if (gtk_widget_get_direction (GTK_WIDGET (container)) == GTK_TEXT_DIR_RTL) {
 
3790
                        next_row_fallback = next_row_leftmost;
 
3791
                } else {
 
3792
                        next_row_fallback = next_row_rightmost;
 
3793
                }
 
3794
        }
 
3795
 
 
3796
        /* Down selects the next icon in the same column.
 
3797
         * Control-Down sets the keyboard focus to the next icon in the same column.
 
3798
         */
 
3799
        keyboard_arrow_key (container,
 
3800
                            event,
 
3801
                            GTK_DIR_DOWN,
 
3802
                            rightmost_in_bottom_row,
 
3803
                            nautilus_icon_container_is_layout_rtl (container) ?
 
3804
                            rightmost_in_top_row : leftmost_in_top_row,
 
3805
                            same_column_below_highest,
 
3806
                            no_a11y,
 
3807
                            next_row_fallback,
 
3808
                            closest_in_90_degrees);
 
3809
}
 
3810
 
 
3811
static void
 
3812
keyboard_up (NautilusIconContainer *container,
 
3813
             GdkEventKey *event)
 
3814
{
 
3815
        IsBetterIconFunction no_a11y;
 
3816
 
 
3817
        no_a11y = NULL;
 
3818
        if (container->details->auto_layout &&
 
3819
            nautilus_icon_container_is_layout_vertical (container) &&
 
3820
            !is_rectangle_selection_event (event)) {
 
3821
                if (gtk_widget_get_direction (GTK_WIDGET (container)) == GTK_TEXT_DIR_RTL) {
 
3822
                        no_a11y = next_column_bottommost;
 
3823
                } else {
 
3824
                        no_a11y = previous_column_lowest;
 
3825
                }
 
3826
        }
 
3827
 
 
3828
        /* Up selects the next icon in the same column.
 
3829
         * Control-Up sets the keyboard focus to the next icon in the same column.
 
3830
         */
 
3831
        keyboard_arrow_key (container,
 
3832
                            event,
 
3833
                            GTK_DIR_UP,
 
3834
                            rightmost_in_bottom_row,
 
3835
                            nautilus_icon_container_is_layout_rtl (container) ?
 
3836
                            rightmost_in_top_row : leftmost_in_top_row,
 
3837
                            same_column_above_lowest,
 
3838
                            no_a11y,
 
3839
                            NULL,
 
3840
                            closest_in_90_degrees);
 
3841
}
 
3842
 
 
3843
static void
 
3844
keyboard_space (NautilusIconContainer *container,
 
3845
                GdkEventKey *event)
 
3846
{
 
3847
        NautilusIcon *icon;
 
3848
        
 
3849
        if (!has_selection (container) &&
 
3850
            container->details->keyboard_focus != NULL) {
 
3851
                keyboard_move_to (container,
 
3852
                                  container->details->keyboard_focus,
 
3853
                                  NULL, NULL);
 
3854
        } else if ((event->state & GDK_CONTROL_MASK) != 0 &&
 
3855
                   (event->state & GDK_SHIFT_MASK) == 0) {
 
3856
                /* Control-space toggles the selection state of the current icon. */
 
3857
                if (container->details->keyboard_focus != NULL) {
 
3858
                        icon_toggle_selected (container, container->details->keyboard_focus);
 
3859
                        g_signal_emit (container, signals[SELECTION_CHANGED], 0);
 
3860
                        if  (container->details->keyboard_focus->is_selected) {
 
3861
                                container->details->range_selection_base_icon = container->details->keyboard_focus;
 
3862
                        } 
 
3863
                } else {
 
3864
                        icon = find_best_selected_icon (container,
 
3865
                                                        NULL,
 
3866
                                                        leftmost_in_top_row,
 
3867
                                                        NULL);
 
3868
                        if (icon == NULL) {
 
3869
                                icon = find_best_icon (container,
 
3870
                                                       NULL,
 
3871
                                                       leftmost_in_top_row,
 
3872
                                                       NULL);
 
3873
                        }
 
3874
                        if (icon != NULL) {
 
3875
                                set_keyboard_focus (container, icon);
 
3876
                        }
 
3877
                }
 
3878
        } else if ((event->state & GDK_SHIFT_MASK) != 0) {
 
3879
                activate_selected_items_alternate (container, NULL);
 
3880
        } else {
 
3881
                preview_selected_items (container);
 
3882
        }
 
3883
}
 
3884
 
 
3885
/* look for the first icon that matches the longest part of a given
 
3886
 * search pattern
 
3887
 */
 
3888
typedef struct {
 
3889
        gunichar *name;
 
3890
        int last_match_length;
 
3891
} BestNameMatch;
 
3892
 
 
3893
#ifndef TAB_NAVIGATION_DISABLED
 
3894
static void
 
3895
select_previous_or_next_icon (NautilusIconContainer *container, 
 
3896
                              gboolean next, 
 
3897
                              GdkEventKey *event)
 
3898
{
 
3899
        NautilusIcon *icon;
 
3900
        const GList *item;
 
3901
 
 
3902
        item = NULL;
 
3903
        /* Chose the icon to start with.
 
3904
         * If we have a keyboard focus, start with it.
 
3905
         * Otherwise, use the single selected icon.
 
3906
         */
 
3907
        icon = container->details->keyboard_focus;
 
3908
        if (icon == NULL) {
 
3909
                icon = get_first_selected_icon (container);
 
3910
        }
 
3911
 
 
3912
        if (icon != NULL) {
 
3913
                /* must have at least @icon in the list */
 
3914
                g_assert (container->details->icons != NULL);
 
3915
                item = g_list_find (container->details->icons, icon);
 
3916
                g_assert (item != NULL);
 
3917
                
 
3918
                item = next ? item->next : item->prev;
 
3919
                if (item == NULL) {
 
3920
                        item = next ? g_list_first (container->details->icons) : g_list_last (container->details->icons);
 
3921
                }
 
3922
 
 
3923
        } else if (container->details->icons != NULL) {
 
3924
                /* no selection yet, pick the first or last item to select */
 
3925
                item = next ? g_list_first (container->details->icons) : g_list_last (container->details->icons);
 
3926
        }
 
3927
 
 
3928
        icon = (item != NULL) ? item->data : NULL;
 
3929
 
 
3930
        if (icon != NULL) {
 
3931
                keyboard_move_to (container, icon, NULL, event);
 
3932
        }
 
3933
}
 
3934
#endif
 
3935
 
 
3936
static void
 
3937
destroy (GtkWidget *object)
 
3938
{
 
3939
        NautilusIconContainer *container;
 
3940
 
 
3941
        container = NAUTILUS_ICON_CONTAINER (object);
 
3942
 
 
3943
        nautilus_icon_container_clear (container);
 
3944
 
 
3945
        if (container->details->rubberband_info.timer_id != 0) {
 
3946
                g_source_remove (container->details->rubberband_info.timer_id);
 
3947
                container->details->rubberband_info.timer_id = 0;
 
3948
        }
 
3949
 
 
3950
        if (container->details->idle_id != 0) {
 
3951
                g_source_remove (container->details->idle_id);
 
3952
                container->details->idle_id = 0;
 
3953
        }
 
3954
 
 
3955
        if (container->details->stretch_idle_id != 0) {
 
3956
                g_source_remove (container->details->stretch_idle_id);
 
3957
                container->details->stretch_idle_id = 0;
 
3958
        }
 
3959
 
 
3960
        if (container->details->align_idle_id != 0) {
 
3961
                g_source_remove (container->details->align_idle_id);
 
3962
                container->details->align_idle_id = 0;
 
3963
        }
 
3964
 
 
3965
        if (container->details->selection_changed_id != 0) {
 
3966
                g_source_remove (container->details->selection_changed_id);
 
3967
                container->details->selection_changed_id = 0;
 
3968
        }
 
3969
 
 
3970
        if (container->details->size_allocation_count_id != 0) {
 
3971
                g_source_remove (container->details->size_allocation_count_id);
 
3972
                container->details->size_allocation_count_id = 0;
 
3973
        }
 
3974
 
 
3975
        /* destroy interactive search dialog */
 
3976
        if (container->details->search_window) {
 
3977
                gtk_widget_destroy (container->details->search_window);
 
3978
                container->details->search_window = NULL;
 
3979
                container->details->search_entry = NULL;
 
3980
        }
 
3981
 
 
3982
        remove_search_entry_timeout (container);
 
3983
 
 
3984
        GTK_WIDGET_CLASS (nautilus_icon_container_parent_class)->destroy (object);
 
3985
}
 
3986
 
 
3987
static void
 
3988
finalize (GObject *object)
 
3989
{
 
3990
        NautilusIconContainerDetails *details;
 
3991
 
 
3992
        details = NAUTILUS_ICON_CONTAINER (object)->details;
 
3993
 
 
3994
        g_signal_handlers_disconnect_by_func (nautilus_icon_view_preferences,
 
3995
                                              text_ellipsis_limit_changed_container_callback,
 
3996
                                              object);
 
3997
        g_signal_handlers_disconnect_by_func (nautilus_desktop_preferences,
 
3998
                                              text_ellipsis_limit_changed_container_callback,
 
3999
                                              object);
 
4000
 
 
4001
        g_hash_table_destroy (details->icon_set);
 
4002
        details->icon_set = NULL;
 
4003
 
 
4004
        g_free (details->font);
 
4005
 
 
4006
        if (details->a11y_item_action_queue != NULL) {
 
4007
                while (!g_queue_is_empty (details->a11y_item_action_queue)) {
 
4008
                        g_free (g_queue_pop_head (details->a11y_item_action_queue));
 
4009
                }
 
4010
                g_queue_free (details->a11y_item_action_queue);
 
4011
        }
 
4012
        if (details->a11y_item_action_idle_handler != 0) {
 
4013
                g_source_remove (details->a11y_item_action_idle_handler);
 
4014
        }
 
4015
 
 
4016
        g_free (details);
 
4017
 
 
4018
        G_OBJECT_CLASS (nautilus_icon_container_parent_class)->finalize (object);
 
4019
}
 
4020
 
 
4021
/* GtkWidget methods.  */
 
4022
 
 
4023
static gboolean
 
4024
clear_size_allocation_count (gpointer data)
 
4025
{
 
4026
        NautilusIconContainer *container;
 
4027
 
 
4028
        container = NAUTILUS_ICON_CONTAINER (data);
 
4029
 
 
4030
        container->details->size_allocation_count_id = 0;
 
4031
        container->details->size_allocation_count = 0;
 
4032
 
 
4033
        return FALSE;
 
4034
}
 
4035
 
 
4036
static void
 
4037
size_allocate (GtkWidget *widget,
 
4038
               GtkAllocation *allocation)
 
4039
{
 
4040
        NautilusIconContainer *container;
 
4041
        gboolean need_layout_redone;
 
4042
        GtkAllocation wid_allocation;
 
4043
 
 
4044
        container = NAUTILUS_ICON_CONTAINER (widget);
 
4045
 
 
4046
        need_layout_redone = !container->details->has_been_allocated;
 
4047
        gtk_widget_get_allocation (widget, &wid_allocation);
 
4048
 
 
4049
        if (allocation->width != wid_allocation.width) {
 
4050
                need_layout_redone = TRUE;
 
4051
        }
 
4052
 
 
4053
        if (allocation->height != wid_allocation.height) {
 
4054
                need_layout_redone = TRUE;
 
4055
        }
 
4056
 
 
4057
        /* Under some conditions we can end up in a loop when size allocating.
 
4058
         * This happens when the icons don't fit without a scrollbar, but fits
 
4059
         * when a scrollbar is added (bug #129963 for details).
 
4060
         * We keep track of this looping by increasing a counter in size_allocate
 
4061
         * and clearing it in a high-prio idle (the only way to detect the loop is
 
4062
         * done).
 
4063
         * When we've done at more than two iterations (with/without scrollbar)
 
4064
         * we terminate this looping by not redoing the layout when the width
 
4065
         * is wider than the current one (i.e when removing the scrollbar).
 
4066
         */
 
4067
        if (container->details->size_allocation_count_id == 0) {
 
4068
                container->details->size_allocation_count_id = 
 
4069
                        g_idle_add_full  (G_PRIORITY_HIGH,
 
4070
                                          clear_size_allocation_count,
 
4071
                                          container, NULL);
 
4072
        }
 
4073
        container->details->size_allocation_count++;
 
4074
        if (container->details->size_allocation_count > 2 &&
 
4075
            allocation->width >= wid_allocation.width) {
 
4076
                need_layout_redone = FALSE;
 
4077
        }
 
4078
        
 
4079
        GTK_WIDGET_CLASS (nautilus_icon_container_parent_class)->size_allocate (widget, allocation);
 
4080
 
 
4081
        container->details->has_been_allocated = TRUE;
 
4082
 
 
4083
        if (need_layout_redone) {
 
4084
                redo_layout (container);
 
4085
        }
 
4086
}
 
4087
 
 
4088
static GtkSizeRequestMode
 
4089
get_request_mode (GtkWidget *widget)
 
4090
{
 
4091
  /* Don't trade size at all, since we get whatever we get anyway. */
 
4092
  return GTK_SIZE_REQUEST_CONSTANT_SIZE;
 
4093
}
 
4094
 
 
4095
/* We need to implement these since the GtkScrolledWindow uses them
 
4096
   to guess whether to show scrollbars or not, and if we don't report
 
4097
   anything it'll tend to get it wrong causing double calls
 
4098
   to size_allocate (at different sizes) during its size allocation. */
 
4099
static void
 
4100
get_prefered_width (GtkWidget *widget,
 
4101
                    gint      *minimum_size,
 
4102
                    gint      *natural_size)
 
4103
{
 
4104
        EelCanvasGroup *root;
 
4105
        double x1, x2;
 
4106
        int cx1, cx2;
 
4107
        int width;
 
4108
 
 
4109
        root = eel_canvas_root (EEL_CANVAS (widget));
 
4110
        eel_canvas_item_get_bounds (EEL_CANVAS_ITEM (root),
 
4111
                                    &x1, NULL, &x2, NULL);
 
4112
        eel_canvas_w2c (EEL_CANVAS (widget), x1, 0, &cx1, NULL);
 
4113
        eel_canvas_w2c (EEL_CANVAS (widget), x2, 0, &cx2, NULL);
 
4114
 
 
4115
        width = cx2 - cx1;
 
4116
        if (natural_size) {
 
4117
                *natural_size = width;
 
4118
        }
 
4119
        if (minimum_size) {
 
4120
                *minimum_size = width;
 
4121
        }
 
4122
}
 
4123
 
 
4124
static void
 
4125
get_prefered_height (GtkWidget *widget,
 
4126
                     gint      *minimum_size,
 
4127
                     gint      *natural_size)
 
4128
{
 
4129
        EelCanvasGroup *root;
 
4130
        double y1, y2;
 
4131
        int cy1, cy2;
 
4132
        int height;
 
4133
 
 
4134
        root = eel_canvas_root (EEL_CANVAS (widget));
 
4135
        eel_canvas_item_get_bounds (EEL_CANVAS_ITEM (root),
 
4136
                                    NULL, &y1, NULL, &y2);
 
4137
        eel_canvas_w2c (EEL_CANVAS (widget), 0, y1, NULL, &cy1);
 
4138
        eel_canvas_w2c (EEL_CANVAS (widget), 0, y2, NULL, &cy2);
 
4139
 
 
4140
        height = cy2 - cy1;
 
4141
        if (natural_size) {
 
4142
                *natural_size = height;
 
4143
        }
 
4144
        if (minimum_size) {
 
4145
                *minimum_size = height;
 
4146
        }
 
4147
}
 
4148
 
 
4149
static void
 
4150
setup_background (NautilusIconContainer *container)
 
4151
{
 
4152
        GdkWindow *window;
 
4153
        GdkRGBA color;
 
4154
        GtkStyleContext *style;
 
4155
 
 
4156
        if (container->details->is_desktop) {
 
4157
                return;
 
4158
        }
 
4159
 
 
4160
        style = gtk_widget_get_style_context (GTK_WIDGET (container));
 
4161
 
 
4162
        DEBUG ("Container %p: setting up background, is_active %d", container,
 
4163
               container->details->active_background);
 
4164
 
 
4165
        window = gtk_layout_get_bin_window (GTK_LAYOUT (container));
 
4166
 
 
4167
        if (!container->details->active_background) {
 
4168
                gtk_style_context_get_background_color (style, GTK_STATE_FLAG_NORMAL, &color);
 
4169
 
 
4170
                DEBUG ("Container %p, making color inactive", container);
 
4171
                eel_make_color_inactive (&color);
 
4172
 
 
4173
                gtk_widget_override_background_color (GTK_WIDGET (container), GTK_STATE_FLAG_NORMAL,
 
4174
                                                      &color);
 
4175
                gtk_style_context_set_background (style, window);
 
4176
        } else {
 
4177
                DEBUG ("Container %p, removing color override", container);
 
4178
                gtk_widget_override_background_color (GTK_WIDGET (container), GTK_STATE_FLAG_NORMAL,
 
4179
                                                      NULL);
 
4180
                gtk_style_context_set_background (style, window);
 
4181
        }
 
4182
}
 
4183
 
 
4184
static void
 
4185
realize (GtkWidget *widget)
 
4186
{
 
4187
        GtkAdjustment *vadj, *hadj;
 
4188
        NautilusIconContainer *container;
 
4189
 
 
4190
        GTK_WIDGET_CLASS (nautilus_icon_container_parent_class)->realize (widget);
 
4191
 
 
4192
        container = NAUTILUS_ICON_CONTAINER (widget);
 
4193
 
 
4194
        /* Ensure that the desktop window is native so the background
 
4195
           set on it is drawn by X. */
 
4196
        if (container->details->is_desktop) {
 
4197
                gdk_x11_window_get_xid (gtk_layout_get_bin_window (GTK_LAYOUT (widget)));
 
4198
        }
 
4199
 
 
4200
        /* Set up DnD.  */
 
4201
        nautilus_icon_dnd_init (container);
 
4202
 
 
4203
        setup_background (container);
 
4204
 
 
4205
        hadj = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (widget));
 
4206
        g_signal_connect (hadj, "value_changed",
 
4207
                          G_CALLBACK (handle_hadjustment_changed), widget);
 
4208
 
 
4209
        vadj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (widget));
 
4210
        g_signal_connect (vadj, "value_changed",
 
4211
                          G_CALLBACK (handle_vadjustment_changed), widget);
 
4212
 
 
4213
}
 
4214
 
 
4215
static void
 
4216
unrealize (GtkWidget *widget)
 
4217
{
 
4218
        NautilusIconContainer *container;
 
4219
 
 
4220
        container = NAUTILUS_ICON_CONTAINER (widget);
 
4221
 
 
4222
        nautilus_icon_dnd_fini (container);
 
4223
        remove_search_entry_timeout (container);
 
4224
 
 
4225
        GTK_WIDGET_CLASS (nautilus_icon_container_parent_class)->unrealize (widget);
 
4226
}
 
4227
 
 
4228
static void
 
4229
style_updated (GtkWidget *widget)
 
4230
{
 
4231
        NautilusIconContainer *container;
 
4232
 
 
4233
        container = NAUTILUS_ICON_CONTAINER (widget);
 
4234
        container->details->use_drop_shadows = container->details->drop_shadows_requested;
 
4235
 
 
4236
        /* Don't chain up to parent, if this is a desktop container,
 
4237
         * because that resets the background of the window.
 
4238
         */
 
4239
        if (!nautilus_icon_container_get_is_desktop (container)) {
 
4240
                GTK_WIDGET_CLASS (nautilus_icon_container_parent_class)->style_updated (widget);
 
4241
        }
 
4242
 
 
4243
        if (gtk_widget_get_realized (widget)) {
 
4244
                invalidate_labels (container);
 
4245
                nautilus_icon_container_request_update_all (container);
 
4246
        }
 
4247
}
 
4248
 
 
4249
static gboolean
 
4250
button_press_event (GtkWidget *widget,
 
4251
                    GdkEventButton *event)
 
4252
{
 
4253
        NautilusIconContainer *container;
 
4254
        gboolean selection_changed;
 
4255
        gboolean return_value;
 
4256
        gboolean clicked_on_icon;
 
4257
 
 
4258
        container = NAUTILUS_ICON_CONTAINER (widget);
 
4259
        container->details->button_down_time = event->time;
 
4260
        
 
4261
        /* Forget about the old keyboard selection now that we've started mousing. */
 
4262
        clear_keyboard_focus (container);
 
4263
        clear_keyboard_rubberband_start (container);
 
4264
 
 
4265
        if (event->type == GDK_2BUTTON_PRESS || event->type == GDK_3BUTTON_PRESS) {
 
4266
                /* We use our own double-click detection. */
 
4267
                return TRUE;
 
4268
        }
 
4269
 
 
4270
        /* Invoke the canvas event handler and see if an item picks up the event. */
 
4271
        clicked_on_icon = GTK_WIDGET_CLASS (nautilus_icon_container_parent_class)->button_press_event (widget, event);
 
4272
        
 
4273
        /* Move focus to icon container, unless we're still renaming (to avoid exiting
 
4274
         * renaming mode)
 
4275
         */
 
4276
        if (!gtk_widget_has_focus (widget) && !(is_renaming (container) || is_renaming_pending (container))) {
 
4277
                gtk_widget_grab_focus (widget);
 
4278
        }
 
4279
 
 
4280
        if (clicked_on_icon) {
 
4281
                return TRUE;
 
4282
        }
 
4283
 
 
4284
        if (event->button == DRAG_BUTTON &&
 
4285
            event->type == GDK_BUTTON_PRESS) {
 
4286
                /* Clear the last click icon for double click */
 
4287
                container->details->double_click_icon[1] = container->details->double_click_icon[0];
 
4288
                container->details->double_click_icon[0] = NULL;
 
4289
        }
 
4290
        
 
4291
        /* Button 1 does rubber banding. */
 
4292
        if (event->button == RUBBERBAND_BUTTON) {
 
4293
                if (! button_event_modifies_selection (event)) {
 
4294
                        selection_changed = unselect_all (container);
 
4295
                        if (selection_changed) {
 
4296
                                g_signal_emit (container,
 
4297
                                                 signals[SELECTION_CHANGED], 0);
 
4298
                        }
 
4299
                }
 
4300
 
 
4301
                start_rubberbanding (container, event);
 
4302
                return TRUE;
 
4303
        }
 
4304
 
 
4305
        /* Prevent multi-button weirdness such as bug 6181 */
 
4306
        if (container->details->rubberband_info.active) {
 
4307
                return TRUE;
 
4308
        }
 
4309
        
 
4310
        /* Button 2 may be passed to the window manager. */
 
4311
        if (event->button == MIDDLE_BUTTON) {
 
4312
                selection_changed = unselect_all (container);
 
4313
                if (selection_changed) {
 
4314
                        g_signal_emit (container, signals[SELECTION_CHANGED], 0);
 
4315
                }
 
4316
                g_signal_emit (widget, signals[MIDDLE_CLICK], 0, event);
 
4317
                return TRUE;
 
4318
        }
 
4319
 
 
4320
        /* Button 3 does a contextual menu. */
 
4321
        if (event->button == CONTEXTUAL_MENU_BUTTON) {
 
4322
                end_renaming_mode (container, TRUE);
 
4323
                selection_changed = unselect_all (container);
 
4324
                if (selection_changed) {
 
4325
                        g_signal_emit (container, signals[SELECTION_CHANGED], 0);
 
4326
                }
 
4327
                g_signal_emit (widget, signals[CONTEXT_CLICK_BACKGROUND], 0, event);
 
4328
                return TRUE;
 
4329
        }
 
4330
        
 
4331
        /* Otherwise, we emit a button_press message. */
 
4332
        g_signal_emit (widget,
 
4333
                       signals[BUTTON_PRESS], 0, event,
 
4334
                       &return_value);
 
4335
        return return_value;
 
4336
}
 
4337
 
 
4338
static void
 
4339
nautilus_icon_container_did_not_drag (NautilusIconContainer *container,
 
4340
                                      GdkEventButton *event)
 
4341
{
 
4342
        NautilusIconContainerDetails *details;
 
4343
        gboolean selection_changed;
 
4344
        static gint64 last_click_time = 0;
 
4345
        static gint click_count = 0;
 
4346
        gint double_click_time;
 
4347
        gint64 current_time;
 
4348
                
 
4349
        details = container->details;
 
4350
 
 
4351
        if (details->icon_selected_on_button_down &&
 
4352
            ((event->state & GDK_CONTROL_MASK) != 0 ||
 
4353
             (event->state & GDK_SHIFT_MASK) == 0)) {
 
4354
                if (button_event_modifies_selection (event)) {
 
4355
                        details->range_selection_base_icon = NULL;
 
4356
                        icon_toggle_selected (container, details->drag_icon);
 
4357
                        g_signal_emit (container,
 
4358
                                       signals[SELECTION_CHANGED], 0);
 
4359
                } else {
 
4360
                        details->range_selection_base_icon = details->drag_icon;
 
4361
                        selection_changed = select_one_unselect_others 
 
4362
                                (container, details->drag_icon);
 
4363
                        
 
4364
                        if (selection_changed) {
 
4365
                                g_signal_emit (container,
 
4366
                                               signals[SELECTION_CHANGED], 0);
 
4367
                        }
 
4368
                }
 
4369
        } 
 
4370
        
 
4371
        if (details->drag_icon != NULL &&
 
4372
            (details->single_click_mode ||
 
4373
             event->button == MIDDLE_BUTTON)) {
 
4374
                /* Determine click count */
 
4375
                g_object_get (G_OBJECT (gtk_widget_get_settings (GTK_WIDGET (container))), 
 
4376
                              "gtk-double-click-time", &double_click_time,
 
4377
                              NULL);
 
4378
                current_time = eel_get_system_time ();
 
4379
                if (current_time - last_click_time < double_click_time * 1000) {
 
4380
                        click_count++;
 
4381
                } else {
 
4382
                        click_count = 0;
 
4383
                }
 
4384
                
 
4385
                /* Stash time for next compare */
 
4386
                last_click_time = current_time;
 
4387
 
 
4388
                /* If single-click mode, activate the selected icons, unless modifying
 
4389
                 * the selection or pressing for a very long time, or double clicking.
 
4390
                 */
 
4391
 
 
4392
                
 
4393
                if (click_count == 0 &&
 
4394
                    event->time - details->button_down_time < MAX_CLICK_TIME &&
 
4395
                    ! button_event_modifies_selection (event)) {
 
4396
                        
 
4397
                        /* It's a tricky UI issue whether this should activate
 
4398
                         * just the clicked item (as if it were a link), or all
 
4399
                         * the selected items (as if you were issuing an "activate
 
4400
                         * selection" command). For now, we're trying the activate
 
4401
                         * entire selection version to see how it feels. Note that
 
4402
                         * NautilusList goes the other way because its "links" seem
 
4403
                         * much more link-like.
 
4404
                         */
 
4405
                        if (event->button == MIDDLE_BUTTON) {
 
4406
                                activate_selected_items_alternate (container, NULL);
 
4407
                        } else {
 
4408
                                activate_selected_items (container);
 
4409
                        }
 
4410
                }
 
4411
        }
 
4412
}
 
4413
 
 
4414
static gboolean
 
4415
clicked_within_double_click_interval (NautilusIconContainer *container)
 
4416
{
 
4417
        static gint64 last_click_time = 0;
 
4418
        static gint click_count = 0;
 
4419
        gint double_click_time;
 
4420
        gint64 current_time;
 
4421
 
 
4422
        /* Determine click count */
 
4423
        g_object_get (G_OBJECT (gtk_widget_get_settings (GTK_WIDGET (container))), 
 
4424
                      "gtk-double-click-time", &double_click_time,
 
4425
                      NULL);
 
4426
        current_time = eel_get_system_time ();
 
4427
        if (current_time - last_click_time < double_click_time * 1000) {
 
4428
                click_count++;
 
4429
        } else {
 
4430
                click_count = 0;
 
4431
        }
 
4432
 
 
4433
        /* Stash time for next compare */
 
4434
        last_click_time = current_time;
 
4435
 
 
4436
        /* Only allow double click */
 
4437
        if (click_count == 1) {
 
4438
                click_count = 0;
 
4439
                return TRUE;
 
4440
        } else {
 
4441
                return FALSE;
 
4442
        }
 
4443
}
 
4444
 
 
4445
static void
 
4446
clear_drag_state (NautilusIconContainer *container)
 
4447
{
 
4448
        container->details->drag_icon = NULL;
 
4449
        container->details->drag_state = DRAG_STATE_INITIAL;
 
4450
}
 
4451
 
 
4452
static gboolean
 
4453
start_stretching (NautilusIconContainer *container)
 
4454
{
 
4455
        NautilusIconContainerDetails *details;
 
4456
        NautilusIcon *icon;
 
4457
        EelDPoint world_point;
 
4458
        GtkWidget *toplevel;
 
4459
        GtkCornerType corner;
 
4460
        GdkCursor *cursor;
 
4461
 
 
4462
        details = container->details;
 
4463
        icon = details->stretch_icon;
 
4464
        
 
4465
        /* Check if we hit the stretch handles. */
 
4466
        world_point.x = details->drag_x;
 
4467
        world_point.y = details->drag_y;
 
4468
        if (!nautilus_icon_canvas_item_hit_test_stretch_handles (icon->item, world_point, &corner)) {
 
4469
                return FALSE;
 
4470
        }
 
4471
 
 
4472
        switch (corner) {
 
4473
        case GTK_CORNER_TOP_LEFT:
 
4474
                cursor = gdk_cursor_new (GDK_TOP_LEFT_CORNER);
 
4475
                break;
 
4476
        case GTK_CORNER_BOTTOM_LEFT:
 
4477
                cursor = gdk_cursor_new (GDK_BOTTOM_LEFT_CORNER);
 
4478
                break;
 
4479
        case GTK_CORNER_TOP_RIGHT:
 
4480
                cursor = gdk_cursor_new (GDK_TOP_RIGHT_CORNER);
 
4481
                break;
 
4482
        case GTK_CORNER_BOTTOM_RIGHT:
 
4483
                cursor = gdk_cursor_new (GDK_BOTTOM_RIGHT_CORNER);
 
4484
                break;
 
4485
        default: 
 
4486
                cursor = NULL;
 
4487
                break;
 
4488
        }
 
4489
        /* Set up the dragging. */
 
4490
        details->drag_state = DRAG_STATE_STRETCH;
 
4491
        eel_canvas_w2c (EEL_CANVAS (container),
 
4492
                          details->drag_x,
 
4493
                          details->drag_y,
 
4494
                          &details->stretch_start.pointer_x,
 
4495
                          &details->stretch_start.pointer_y);
 
4496
        eel_canvas_w2c (EEL_CANVAS (container),
 
4497
                          icon->x, icon->y,
 
4498
                          &details->stretch_start.icon_x,
 
4499
                          &details->stretch_start.icon_y);
 
4500
        icon_get_size (container, icon,
 
4501
                       &details->stretch_start.icon_size);
 
4502
 
 
4503
        eel_canvas_item_grab (EEL_CANVAS_ITEM (icon->item),
 
4504
                                (GDK_POINTER_MOTION_MASK
 
4505
                                 | GDK_BUTTON_RELEASE_MASK),
 
4506
                                cursor,
 
4507
                                GDK_CURRENT_TIME);
 
4508
        if (cursor)
 
4509
                g_object_unref (cursor);
 
4510
 
 
4511
        /* Ensure the window itself is focused.. */
 
4512
        toplevel = gtk_widget_get_toplevel (GTK_WIDGET (container));
 
4513
        if (toplevel != NULL && gtk_widget_get_realized (toplevel)) {
 
4514
                gdk_window_focus (gtk_widget_get_window (toplevel), GDK_CURRENT_TIME);
 
4515
        }
 
4516
 
 
4517
        return TRUE;
 
4518
}
 
4519
 
 
4520
static gboolean
 
4521
update_stretch_at_idle (NautilusIconContainer *container)
 
4522
{
 
4523
        NautilusIconContainerDetails *details;
 
4524
        NautilusIcon *icon;
 
4525
        double world_x, world_y;
 
4526
        StretchState stretch_state;
 
4527
 
 
4528
        details = container->details;
 
4529
        icon = details->stretch_icon;
 
4530
 
 
4531
        if (icon == NULL) {
 
4532
                container->details->stretch_idle_id = 0;
 
4533
                return FALSE;
 
4534
        }
 
4535
 
 
4536
        eel_canvas_w2c (EEL_CANVAS (container),
 
4537
                          details->world_x, details->world_y,
 
4538
                          &stretch_state.pointer_x, &stretch_state.pointer_y);
 
4539
 
 
4540
        compute_stretch (&details->stretch_start,
 
4541
                         &stretch_state);
 
4542
 
 
4543
        eel_canvas_c2w (EEL_CANVAS (container),
 
4544
                          stretch_state.icon_x, stretch_state.icon_y,
 
4545
                          &world_x, &world_y);
 
4546
 
 
4547
        icon_set_position (icon, world_x, world_y);
 
4548
        icon_set_size (container, icon, stretch_state.icon_size, FALSE, FALSE);
 
4549
 
 
4550
        container->details->stretch_idle_id = 0;
 
4551
 
 
4552
        return FALSE;
 
4553
}       
 
4554
 
 
4555
static void
 
4556
continue_stretching (NautilusIconContainer *container,
 
4557
                     double world_x, double world_y)
 
4558
{
 
4559
 
 
4560
        g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container));
 
4561
 
 
4562
        container->details->world_x = world_x;
 
4563
        container->details->world_y = world_y;
 
4564
 
 
4565
        if (container->details->stretch_idle_id == 0) {         
 
4566
                container->details->stretch_idle_id = g_idle_add ((GSourceFunc) update_stretch_at_idle, container);
 
4567
        }
 
4568
}
 
4569
 
 
4570
static gboolean
 
4571
keyboard_stretching (NautilusIconContainer *container,
 
4572
                     GdkEventKey           *event)
 
4573
{
 
4574
        NautilusIcon *icon;
 
4575
        guint size;
 
4576
 
 
4577
        icon = container->details->stretch_icon;
 
4578
 
 
4579
        if (icon == NULL || !icon->is_selected) {
 
4580
                return FALSE;
 
4581
        }
 
4582
 
 
4583
        icon_get_size (container, icon, &size);
 
4584
 
 
4585
        switch (event->keyval) {
 
4586
        case GDK_KEY_equal:
 
4587
        case GDK_KEY_plus:
 
4588
        case GDK_KEY_KP_Add:
 
4589
                icon_set_size (container, icon, size + 5, FALSE, FALSE);
 
4590
                break;
 
4591
        case GDK_KEY_minus:
 
4592
        case GDK_KEY_KP_Subtract:
 
4593
                icon_set_size (container, icon, size - 5, FALSE, FALSE);
 
4594
                break;
 
4595
        case GDK_KEY_0:
 
4596
        case GDK_KEY_KP_0:
 
4597
                nautilus_icon_container_move_icon (container, icon,
 
4598
                                                   icon->x, icon->y,
 
4599
                                                   1.0,
 
4600
                                                   FALSE, TRUE, TRUE);
 
4601
                break;
 
4602
        }
 
4603
        
 
4604
        return TRUE;
 
4605
}
 
4606
 
 
4607
static void
 
4608
ungrab_stretch_icon (NautilusIconContainer *container)
 
4609
{
 
4610
        eel_canvas_item_ungrab (EEL_CANVAS_ITEM (container->details->stretch_icon->item),
 
4611
                                  GDK_CURRENT_TIME);
 
4612
}
 
4613
 
 
4614
static void
 
4615
end_stretching (NautilusIconContainer *container,
 
4616
                double world_x, double world_y)
 
4617
{
 
4618
        NautilusIconPosition position;
 
4619
        NautilusIcon *icon;
 
4620
        
 
4621
        continue_stretching (container, world_x, world_y);
 
4622
        ungrab_stretch_icon (container);
 
4623
 
 
4624
        /* now that we're done stretching, update the icon's position */
 
4625
        
 
4626
        icon = container->details->drag_icon;   
 
4627
        if (nautilus_icon_container_is_layout_rtl (container)) {
 
4628
                position.x = icon->saved_ltr_x = get_mirror_x_position (container, icon, icon->x);
 
4629
        } else {
 
4630
                position.x = icon->x;
 
4631
        }
 
4632
        position.y = icon->y;
 
4633
        position.scale = icon->scale;
 
4634
        g_signal_emit (container,
 
4635
                         signals[ICON_POSITION_CHANGED], 0,
 
4636
                         icon->data, &position);
 
4637
        
 
4638
        clear_drag_state (container);
 
4639
        redo_layout (container);
 
4640
}
 
4641
 
 
4642
static gboolean
 
4643
undo_stretching (NautilusIconContainer *container)
 
4644
{
 
4645
        NautilusIcon *stretched_icon;
 
4646
 
 
4647
        stretched_icon = container->details->stretch_icon;
 
4648
 
 
4649
        if (stretched_icon == NULL) {
 
4650
                return FALSE;
 
4651
        }
 
4652
 
 
4653
        if (container->details->drag_state == DRAG_STATE_STRETCH) {
 
4654
                ungrab_stretch_icon (container);
 
4655
                clear_drag_state (container);
 
4656
        }
 
4657
        nautilus_icon_canvas_item_set_show_stretch_handles
 
4658
                (stretched_icon->item, FALSE);
 
4659
        
 
4660
        icon_set_position (stretched_icon,
 
4661
                           container->details->stretch_initial_x,
 
4662
                           container->details->stretch_initial_y);
 
4663
        icon_set_size (container,
 
4664
                       stretched_icon, 
 
4665
                       container->details->stretch_initial_size,
 
4666
                       TRUE,
 
4667
                       TRUE);
 
4668
        
 
4669
        container->details->stretch_icon = NULL;                                
 
4670
        emit_stretch_ended (container, stretched_icon);
 
4671
        redo_layout (container);
 
4672
 
 
4673
        return TRUE;
 
4674
}
 
4675
 
 
4676
static gboolean
 
4677
button_release_event (GtkWidget *widget,
 
4678
                      GdkEventButton *event)
 
4679
{
 
4680
        NautilusIconContainer *container;
 
4681
        NautilusIconContainerDetails *details;
 
4682
        double world_x, world_y;
 
4683
        
 
4684
        container = NAUTILUS_ICON_CONTAINER (widget);
 
4685
        details = container->details;
 
4686
 
 
4687
        if (event->button == RUBBERBAND_BUTTON && details->rubberband_info.active) {
 
4688
                stop_rubberbanding (container, event->time);
 
4689
                return TRUE;
 
4690
        }
 
4691
        
 
4692
        if (event->button == details->drag_button) {
 
4693
                details->drag_button = 0;
 
4694
 
 
4695
                switch (details->drag_state) {
 
4696
                case DRAG_STATE_MOVE_OR_COPY:
 
4697
                        if (!details->drag_started) {
 
4698
                                nautilus_icon_container_did_not_drag (container, event);
 
4699
                        } else {
 
4700
                                nautilus_icon_dnd_end_drag (container);
 
4701
                                DEBUG ("Ending drag from icon container");
 
4702
                        }
 
4703
                        break;
 
4704
                case DRAG_STATE_STRETCH:
 
4705
                        eel_canvas_window_to_world
 
4706
                                (EEL_CANVAS (container), event->x, event->y, &world_x, &world_y);
 
4707
                        end_stretching (container, world_x, world_y);
 
4708
                        break;
 
4709
                default:
 
4710
                        break;
 
4711
                }
 
4712
 
 
4713
                clear_drag_state (container);
 
4714
                return TRUE;
 
4715
        }
 
4716
 
 
4717
        return GTK_WIDGET_CLASS (nautilus_icon_container_parent_class)->button_release_event (widget, event);
 
4718
}
 
4719
 
 
4720
static int
 
4721
motion_notify_event (GtkWidget *widget,
 
4722
                     GdkEventMotion *event)
 
4723
{
 
4724
        NautilusIconContainer *container;
 
4725
        NautilusIconContainerDetails *details;
 
4726
        double world_x, world_y;
 
4727
        int canvas_x, canvas_y;
 
4728
        GdkDragAction actions;
 
4729
 
 
4730
        container = NAUTILUS_ICON_CONTAINER (widget);
 
4731
        details = container->details;
 
4732
 
 
4733
        if (details->drag_button != 0) {
 
4734
                switch (details->drag_state) {
 
4735
                case DRAG_STATE_MOVE_OR_COPY:
 
4736
                        if (details->drag_started) {
 
4737
                                break;
 
4738
                        }
 
4739
 
 
4740
                        eel_canvas_window_to_world
 
4741
                                (EEL_CANVAS (container), event->x, event->y, &world_x, &world_y);
 
4742
                        
 
4743
                        if (gtk_drag_check_threshold (widget, 
 
4744
                                                      details->drag_x,
 
4745
                                                      details->drag_y,
 
4746
                                                      world_x,
 
4747
                                                      world_y)) {
 
4748
                                details->drag_started = TRUE;
 
4749
                                details->drag_state = DRAG_STATE_MOVE_OR_COPY;
 
4750
 
 
4751
                                end_renaming_mode (container, TRUE);
 
4752
                        
 
4753
                                eel_canvas_w2c (EEL_CANVAS (container),
 
4754
                                                  details->drag_x,
 
4755
                                                  details->drag_y,
 
4756
                                                  &canvas_x,
 
4757
                                                  &canvas_y);
 
4758
 
 
4759
                                actions = GDK_ACTION_COPY
 
4760
                                        | GDK_ACTION_LINK
 
4761
                                        | GDK_ACTION_ASK;
 
4762
 
 
4763
                                if (container->details->drag_allow_moves) {
 
4764
                                        actions |= GDK_ACTION_MOVE;
 
4765
                                }
 
4766
 
 
4767
                                nautilus_icon_dnd_begin_drag (container,
 
4768
                                                              actions,
 
4769
                                                              details->drag_button,
 
4770
                                                              event, 
 
4771
                                                              canvas_x,
 
4772
                                                              canvas_y);
 
4773
                                DEBUG ("Beginning drag from icon container");
 
4774
                        }
 
4775
                        break;
 
4776
                case DRAG_STATE_STRETCH:
 
4777
                        eel_canvas_window_to_world
 
4778
                                (EEL_CANVAS (container), event->x, event->y, &world_x, &world_y);
 
4779
                        continue_stretching (container, world_x, world_y);
 
4780
                        break;
 
4781
                default:
 
4782
                        break;
 
4783
                }
 
4784
        }
 
4785
 
 
4786
        return GTK_WIDGET_CLASS (nautilus_icon_container_parent_class)->motion_notify_event (widget, event);
 
4787
}
 
4788
 
 
4789
static void
 
4790
nautilus_icon_container_search_position_func (NautilusIconContainer *container,
 
4791
                                              GtkWidget *search_dialog)
 
4792
{
 
4793
        gint x, y;
 
4794
        gint cont_x, cont_y;
 
4795
        gint cont_width, cont_height;
 
4796
        GdkWindow *cont_window;
 
4797
        GdkScreen *screen;
 
4798
        GtkRequisition requisition;
 
4799
        gint monitor_num;
 
4800
        GdkRectangle monitor;
 
4801
 
 
4802
 
 
4803
        cont_window = gtk_widget_get_window (GTK_WIDGET (container));
 
4804
        screen = gdk_window_get_screen (cont_window);
 
4805
 
 
4806
        monitor_num = gdk_screen_get_monitor_at_window (screen, cont_window);
 
4807
        gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
 
4808
 
 
4809
        gtk_widget_realize (search_dialog);
 
4810
 
 
4811
        gdk_window_get_origin (cont_window, &cont_x, &cont_y);
 
4812
        cont_width = gdk_window_get_width (cont_window);
 
4813
        cont_height = gdk_window_get_height (cont_window);
 
4814
 
 
4815
        gtk_widget_get_preferred_size (search_dialog, &requisition, NULL);
 
4816
 
 
4817
        if (cont_x + cont_width > gdk_screen_get_width (screen)) {
 
4818
                x = gdk_screen_get_width (screen) - requisition.width;
 
4819
        } else if (cont_x + cont_width - requisition.width < 0) {
 
4820
                x = 0;
 
4821
        } else {
 
4822
                x = cont_x + cont_width - requisition.width;
 
4823
        }
 
4824
 
 
4825
        if (cont_y + cont_height + requisition.height > gdk_screen_get_height (screen)) {
 
4826
                y = gdk_screen_get_height (screen) - requisition.height;
 
4827
        } else if (cont_y + cont_height < 0) { /* isn't really possible ... */
 
4828
                y = 0;
 
4829
        } else {
 
4830
                y = cont_y + cont_height;
 
4831
        }
 
4832
 
 
4833
        gdk_window_move (gtk_widget_get_window (search_dialog), x, y);
 
4834
}
 
4835
 
 
4836
/* Cut and paste from gtkwindow.c */
 
4837
static void
 
4838
send_focus_change (GtkWidget *widget, gboolean in)
 
4839
{
 
4840
        GdkEvent *fevent;
 
4841
        
 
4842
        fevent = gdk_event_new (GDK_FOCUS_CHANGE);
 
4843
 
 
4844
        g_object_ref (widget);
 
4845
        ((GdkEventFocus *) fevent)->in = in;
 
4846
 
 
4847
        gtk_widget_send_focus_change (widget, fevent);
 
4848
 
 
4849
        fevent->focus_change.type = GDK_FOCUS_CHANGE;
 
4850
        fevent->focus_change.window = g_object_ref (gtk_widget_get_window (widget));
 
4851
        fevent->focus_change.in = in;
 
4852
 
 
4853
        gtk_widget_event (widget, fevent);
 
4854
 
 
4855
        g_object_notify (G_OBJECT (widget), "has-focus");
 
4856
 
 
4857
        g_object_unref (widget);
 
4858
        gdk_event_free (fevent);
 
4859
}
 
4860
 
 
4861
static void
 
4862
nautilus_icon_container_search_dialog_hide (GtkWidget *search_dialog,
 
4863
                                            NautilusIconContainer *container)
 
4864
{
 
4865
        if (container->details->search_entry_changed_id) {
 
4866
                g_signal_handler_disconnect (container->details->search_entry,
 
4867
                                             container->details->search_entry_changed_id);
 
4868
                container->details->search_entry_changed_id = 0;
 
4869
        }
 
4870
 
 
4871
        remove_search_entry_timeout (container);
 
4872
 
 
4873
        /* send focus-in event */
 
4874
        send_focus_change (GTK_WIDGET (container->details->search_entry), FALSE);
 
4875
        gtk_widget_hide (search_dialog);
 
4876
        gtk_entry_set_text (GTK_ENTRY (container->details->search_entry), "");
 
4877
}
 
4878
 
 
4879
static gboolean
 
4880
nautilus_icon_container_search_entry_flush_timeout (gpointer data)
 
4881
{
 
4882
        NautilusIconContainer *container = data;
 
4883
 
 
4884
        container->details->typeselect_flush_timeout = 0;
 
4885
        nautilus_icon_container_search_dialog_hide (container->details->search_window, container);
 
4886
 
 
4887
        return FALSE;
 
4888
}
 
4889
 
 
4890
static void
 
4891
add_search_entry_timeout (NautilusIconContainer *container)
 
4892
{
 
4893
        container->details->typeselect_flush_timeout =
 
4894
                g_timeout_add_seconds (NAUTILUS_ICON_CONTAINER_SEARCH_DIALOG_TIMEOUT,
 
4895
                                       nautilus_icon_container_search_entry_flush_timeout,
 
4896
                                       container);
 
4897
}
 
4898
 
 
4899
static void
 
4900
remove_search_entry_timeout (NautilusIconContainer *container)
 
4901
{
 
4902
        if (container->details->typeselect_flush_timeout) {
 
4903
                g_source_remove (container->details->typeselect_flush_timeout);
 
4904
                container->details->typeselect_flush_timeout = 0;
 
4905
        }
 
4906
}
 
4907
 
 
4908
static void
 
4909
reset_search_entry_timeout (NautilusIconContainer *container)
 
4910
{
 
4911
        remove_search_entry_timeout (container);
 
4912
        add_search_entry_timeout (container);
 
4913
}
 
4914
 
 
4915
/* Because we're visible but offscreen, we just set a flag in the preedit
 
4916
 * callback.
 
4917
 */
 
4918
static void
 
4919
nautilus_icon_container_search_preedit_changed (GtkEntry *entry,
 
4920
                                                gchar *preedit,
 
4921
                                                NautilusIconContainer *container)
 
4922
{
 
4923
        container->details->imcontext_changed = 1;
 
4924
        reset_search_entry_timeout (container);
 
4925
}
 
4926
 
 
4927
static void
 
4928
nautilus_icon_container_search_activate (GtkEntry *entry,
 
4929
                                         NautilusIconContainer *container)
 
4930
{
 
4931
        nautilus_icon_container_search_dialog_hide (container->details->search_window,
 
4932
                                                    container);
 
4933
 
 
4934
        activate_selected_items (container);
 
4935
}
 
4936
 
 
4937
static gboolean
 
4938
nautilus_icon_container_search_delete_event (GtkWidget *widget,
 
4939
                                             GdkEventAny *event,
 
4940
                                             NautilusIconContainer *container)
 
4941
{
 
4942
        nautilus_icon_container_search_dialog_hide (widget, container);
 
4943
 
 
4944
        return TRUE;
 
4945
}
 
4946
 
 
4947
static gboolean
 
4948
nautilus_icon_container_search_button_press_event (GtkWidget *widget,
 
4949
                                                   GdkEventButton *event,
 
4950
                                                   NautilusIconContainer *container)
 
4951
{
 
4952
        nautilus_icon_container_search_dialog_hide (widget, container);
 
4953
 
 
4954
        if (event->window == gtk_layout_get_bin_window (GTK_LAYOUT (container))) {
 
4955
                button_press_event (GTK_WIDGET (container), event);
 
4956
        }
 
4957
 
 
4958
        return TRUE;
 
4959
}
 
4960
 
 
4961
static gboolean
 
4962
nautilus_icon_container_search_entry_button_press_event (GtkWidget *widget,
 
4963
                                                         GdkEventButton *event,
 
4964
                                                         NautilusIconContainer *container)
 
4965
{
 
4966
        reset_search_entry_timeout (container);
 
4967
 
 
4968
        return FALSE;
 
4969
}
 
4970
 
 
4971
static void
 
4972
nautilus_icon_container_search_populate_popup (GtkEntry *entry,
 
4973
                                               GtkMenu *menu,
 
4974
                                               NautilusIconContainer *container)
 
4975
{
 
4976
        remove_search_entry_timeout (container);
 
4977
        g_signal_connect_swapped (menu, "hide",
 
4978
                                  G_CALLBACK (add_search_entry_timeout), container);
 
4979
}
 
4980
 
 
4981
static void
 
4982
nautilus_icon_container_get_icon_text (NautilusIconContainer *container,
 
4983
                                       NautilusIconData      *data,
 
4984
                                       char                 **editable_text,
 
4985
                                       char                 **additional_text,
 
4986
                                       gboolean               include_invisible)
 
4987
{
 
4988
        NautilusIconContainerClass *klass;
 
4989
 
 
4990
        klass = NAUTILUS_ICON_CONTAINER_GET_CLASS (container);
 
4991
        g_assert (klass->get_icon_text != NULL);
 
4992
 
 
4993
        klass->get_icon_text (container, data, editable_text, additional_text, include_invisible);
 
4994
}
 
4995
 
 
4996
static gboolean
 
4997
nautilus_icon_container_search_iter (NautilusIconContainer *container,
 
4998
                                     const char *key, gint n)
 
4999
{
 
5000
        GList *p;
 
5001
        NautilusIcon *icon;
 
5002
        char *name;
 
5003
        int count;
 
5004
        char *normalized_key, *case_normalized_key;
 
5005
        char *normalized_name, *case_normalized_name;
 
5006
        
 
5007
        g_assert (key != NULL);
 
5008
        g_assert (n >= 1);
 
5009
        
 
5010
        normalized_key = g_utf8_normalize (key, -1, G_NORMALIZE_ALL);
 
5011
        if (!normalized_key) {
 
5012
                return FALSE;
 
5013
        }
 
5014
        case_normalized_key = g_utf8_casefold (normalized_key, -1);
 
5015
        g_free (normalized_key);
 
5016
        if (!case_normalized_key) {
 
5017
                return FALSE;
 
5018
        }
 
5019
        
 
5020
        icon = NULL;
 
5021
        name = NULL;
 
5022
        count = 0;
 
5023
        for (p = container->details->icons; p != NULL && count != n; p = p->next) {
 
5024
                icon = p->data;
 
5025
                nautilus_icon_container_get_icon_text (container, icon->data, &name,
 
5026
                                                       NULL, TRUE);
 
5027
                
 
5028
                /* This can happen if a key event is handled really early while
 
5029
                 * loading the icon container, before the items have all been
 
5030
                 * updated once.
 
5031
                 */
 
5032
                if (!name) {
 
5033
                        continue;
 
5034
                }
 
5035
                        
 
5036
                normalized_name = g_utf8_normalize (name, -1, G_NORMALIZE_ALL);
 
5037
                if (!normalized_name) {
 
5038
                        continue;
 
5039
                }
 
5040
                case_normalized_name = g_utf8_casefold (normalized_name, -1);
 
5041
                g_free (normalized_name);
 
5042
                if (!case_normalized_name) {
 
5043
                        continue;
 
5044
                }
 
5045
                
 
5046
                if (strncmp (case_normalized_key, case_normalized_name,
 
5047
                             strlen (case_normalized_key)) == 0) {
 
5048
                        count++;
 
5049
                }
 
5050
 
 
5051
                g_free (case_normalized_name);
 
5052
                g_free (name);
 
5053
                name = NULL;
 
5054
        }
 
5055
 
 
5056
        g_free (case_normalized_key);
 
5057
 
 
5058
        if (count == n) {
 
5059
                if (select_one_unselect_others (container, icon)) {
 
5060
                        g_signal_emit (container, signals[SELECTION_CHANGED], 0);
 
5061
                }
 
5062
                schedule_keyboard_icon_reveal (container, icon);
 
5063
                
 
5064
                return TRUE;
 
5065
        }
 
5066
        
 
5067
        return FALSE;
 
5068
}
 
5069
 
 
5070
static void
 
5071
nautilus_icon_container_search_move (GtkWidget *window,
 
5072
                                     NautilusIconContainer *container,
 
5073
                                     gboolean up)
 
5074
{
 
5075
        gboolean ret;
 
5076
        gint len;
 
5077
        const gchar *text;
 
5078
 
 
5079
        text = gtk_entry_get_text (GTK_ENTRY (container->details->search_entry));
 
5080
 
 
5081
        g_assert (text != NULL);
 
5082
 
 
5083
        if (container->details->selected_iter == 0) {
 
5084
                return;
 
5085
        }
 
5086
        
 
5087
        if (up && container->details->selected_iter == 1) {
 
5088
                return;
 
5089
        }
 
5090
 
 
5091
        len = strlen (text);
 
5092
 
 
5093
        if (len < 1) {
 
5094
                return;
 
5095
        }
 
5096
 
 
5097
        /* search */
 
5098
        unselect_all (container);
 
5099
 
 
5100
        ret = nautilus_icon_container_search_iter (container, text,
 
5101
                up?((container->details->selected_iter) - 1):((container->details->selected_iter + 1)));
 
5102
 
 
5103
        if (ret) {
 
5104
                /* found */
 
5105
                container->details->selected_iter += up?(-1):(1);
 
5106
        } else {
 
5107
                /* return to old iter */
 
5108
                nautilus_icon_container_search_iter (container, text,
 
5109
                                        container->details->selected_iter);
 
5110
        }
 
5111
}
 
5112
 
 
5113
static gboolean
 
5114
nautilus_icon_container_search_scroll_event (GtkWidget *widget,
 
5115
                                             GdkEventScroll *event,
 
5116
                                             NautilusIconContainer *container)
 
5117
{
 
5118
        gboolean retval = FALSE;
 
5119
 
 
5120
        if (event->direction == GDK_SCROLL_UP) {
 
5121
                nautilus_icon_container_search_move (widget, container, TRUE);
 
5122
                retval = TRUE;
 
5123
        } else if (event->direction == GDK_SCROLL_DOWN) {
 
5124
                nautilus_icon_container_search_move (widget, container, FALSE);
 
5125
                retval = TRUE;
 
5126
        }
 
5127
 
 
5128
        reset_search_entry_timeout (container);
 
5129
 
 
5130
        return retval;
 
5131
}
 
5132
 
 
5133
static gboolean
 
5134
nautilus_icon_container_search_key_press_event (GtkWidget *widget,
 
5135
                                                GdkEventKey *event,
 
5136
                                                NautilusIconContainer *container)
 
5137
{
 
5138
        gboolean retval = FALSE;
 
5139
 
 
5140
        g_assert (GTK_IS_WIDGET (widget));
 
5141
        g_assert (NAUTILUS_IS_ICON_CONTAINER (container));
 
5142
 
 
5143
        /* close window and cancel the search */
 
5144
        if (event->keyval == GDK_KEY_Escape || event->keyval == GDK_KEY_Tab) {
 
5145
                nautilus_icon_container_search_dialog_hide (widget, container);
 
5146
                return TRUE;
 
5147
        }
 
5148
 
 
5149
        /* close window and activate alternate */
 
5150
        if (event->keyval == GDK_KEY_Return && event->state & GDK_SHIFT_MASK) {
 
5151
                nautilus_icon_container_search_dialog_hide (widget,
 
5152
                                                            container);
 
5153
 
 
5154
                activate_selected_items_alternate (container, NULL);
 
5155
                return TRUE;
 
5156
        }
 
5157
 
 
5158
        /* select previous matching iter */
 
5159
        if (event->keyval == GDK_KEY_Up || event->keyval == GDK_KEY_KP_Up) {
 
5160
                nautilus_icon_container_search_move (widget, container, TRUE);
 
5161
                retval = TRUE;
 
5162
        }
 
5163
 
 
5164
        if (((event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == (GDK_CONTROL_MASK | GDK_SHIFT_MASK))
 
5165
            && (event->keyval == GDK_KEY_g || event->keyval == GDK_KEY_G)) {
 
5166
                nautilus_icon_container_search_move (widget, container, TRUE);
 
5167
                retval = TRUE;
 
5168
        }
 
5169
 
 
5170
        /* select next matching iter */
 
5171
        if (event->keyval == GDK_KEY_Down || event->keyval == GDK_KEY_KP_Down) {
 
5172
                nautilus_icon_container_search_move (widget, container, FALSE);
 
5173
                retval = TRUE;
 
5174
        }
 
5175
 
 
5176
        if (((event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == GDK_CONTROL_MASK)
 
5177
            && (event->keyval == GDK_KEY_g || event->keyval == GDK_KEY_G)) {
 
5178
                nautilus_icon_container_search_move (widget, container, FALSE);
 
5179
                retval = TRUE;
 
5180
        }
 
5181
 
 
5182
        reset_search_entry_timeout (container);
 
5183
 
 
5184
        return retval;
 
5185
}
 
5186
 
 
5187
static void
 
5188
nautilus_icon_container_search_init (GtkWidget   *entry,
 
5189
                                     NautilusIconContainer *container)
 
5190
{
 
5191
        gint ret;
 
5192
        gint len;
 
5193
        const gchar *text;
 
5194
 
 
5195
        g_assert (GTK_IS_ENTRY (entry));
 
5196
        g_assert (NAUTILUS_IS_ICON_CONTAINER (container));
 
5197
 
 
5198
        text = gtk_entry_get_text (GTK_ENTRY (entry));
 
5199
        len = strlen (text);
 
5200
 
 
5201
        /* search */
 
5202
        unselect_all (container);
 
5203
        reset_search_entry_timeout (container);
 
5204
 
 
5205
        if (len < 1) {
 
5206
                return;
 
5207
        }
 
5208
 
 
5209
        ret = nautilus_icon_container_search_iter (container, text, 1);
 
5210
 
 
5211
        if (ret) {
 
5212
                container->details->selected_iter = 1;
 
5213
        }
 
5214
}
 
5215
 
 
5216
static void
 
5217
nautilus_icon_container_ensure_interactive_directory (NautilusIconContainer *container)
 
5218
{
 
5219
        GtkWidget *frame, *vbox;
 
5220
 
 
5221
        if (container->details->search_window != NULL) {
 
5222
                return;
 
5223
        }
 
5224
 
 
5225
        container->details->search_window = gtk_window_new (GTK_WINDOW_POPUP);
 
5226
 
 
5227
        gtk_window_set_modal (GTK_WINDOW (container->details->search_window), TRUE);
 
5228
        gtk_window_set_type_hint (GTK_WINDOW (container->details->search_window),
 
5229
                                  GDK_WINDOW_TYPE_HINT_COMBO);
 
5230
 
 
5231
        g_signal_connect (container->details->search_window, "delete_event",
 
5232
                          G_CALLBACK (nautilus_icon_container_search_delete_event),
 
5233
                          container);
 
5234
        g_signal_connect (container->details->search_window, "key_press_event",
 
5235
                          G_CALLBACK (nautilus_icon_container_search_key_press_event),
 
5236
                          container);
 
5237
        g_signal_connect (container->details->search_window, "button_press_event",
 
5238
                          G_CALLBACK (nautilus_icon_container_search_button_press_event),
 
5239
                          container);
 
5240
        g_signal_connect (container->details->search_window, "scroll_event",
 
5241
                          G_CALLBACK (nautilus_icon_container_search_scroll_event),
 
5242
                          container);
 
5243
 
 
5244
        frame = gtk_frame_new (NULL);
 
5245
        gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
 
5246
        gtk_widget_show (frame);
 
5247
        gtk_container_add (GTK_CONTAINER (container->details->search_window), frame);
 
5248
 
 
5249
        vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
 
5250
        gtk_widget_show (vbox);
 
5251
        gtk_container_add (GTK_CONTAINER (frame), vbox);
 
5252
        gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
 
5253
 
 
5254
        /* add entry */
 
5255
        container->details->search_entry = gtk_entry_new ();
 
5256
        gtk_widget_show (container->details->search_entry);
 
5257
        g_signal_connect (container->details->search_entry, "populate-popup",
 
5258
                          G_CALLBACK (nautilus_icon_container_search_populate_popup),
 
5259
                          container);
 
5260
        g_signal_connect (container->details->search_entry, "activate",
 
5261
                          G_CALLBACK (nautilus_icon_container_search_activate),
 
5262
                          container);
 
5263
        g_signal_connect (container->details->search_entry, "preedit-changed",
 
5264
                          G_CALLBACK (nautilus_icon_container_search_preedit_changed),
 
5265
                          container);
 
5266
        g_signal_connect (container->details->search_entry, "button-press-event",
 
5267
                          G_CALLBACK (nautilus_icon_container_search_entry_button_press_event),
 
5268
                          container);
 
5269
        gtk_container_add (GTK_CONTAINER (vbox), container->details->search_entry);
 
5270
 
 
5271
        gtk_widget_realize (container->details->search_entry);
 
5272
}
 
5273
 
 
5274
/* Pops up the interactive search entry.  If keybinding is TRUE then the user
 
5275
 * started this by typing the start_interactive_search keybinding.  Otherwise, it came from 
 
5276
 */
 
5277
static gboolean
 
5278
nautilus_icon_container_start_interactive_search (NautilusIconContainer *container)
 
5279
{
 
5280
        /* We only start interactive search if we have focus.  If one of our
 
5281
         * children have focus, we don't want to start the search.
 
5282
         */
 
5283
        GtkWidgetClass *entry_parent_class;
 
5284
 
 
5285
        if (container->details->search_window != NULL &&
 
5286
            gtk_widget_get_visible (container->details->search_window)) {
 
5287
                return TRUE;
 
5288
        }
 
5289
 
 
5290
        if (!gtk_widget_has_focus (GTK_WIDGET (container))) {
 
5291
                return FALSE;
 
5292
        }
 
5293
 
 
5294
        nautilus_icon_container_ensure_interactive_directory (container);
 
5295
 
 
5296
        /* done, show it */
 
5297
        nautilus_icon_container_search_position_func (container, container->details->search_window);
 
5298
        gtk_widget_show (container->details->search_window);
 
5299
        if (container->details->search_entry_changed_id == 0) {
 
5300
                container->details->search_entry_changed_id =
 
5301
                        g_signal_connect (container->details->search_entry, "changed",
 
5302
                                G_CALLBACK (nautilus_icon_container_search_init),
 
5303
                                container);
 
5304
        }
 
5305
 
 
5306
        /* Grab focus will select all the text.  We don't want that to happen, so we
 
5307
        * call the parent instance and bypass the selection change.  This is probably
 
5308
        * really non-kosher. */
 
5309
        entry_parent_class = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (container->details->search_entry));
 
5310
        (entry_parent_class->grab_focus) (container->details->search_entry);
 
5311
 
 
5312
        /* send focus-in event */
 
5313
        send_focus_change (container->details->search_entry, TRUE);
 
5314
 
 
5315
        /* search first matching iter */
 
5316
        nautilus_icon_container_search_init (container->details->search_entry, container);
 
5317
 
 
5318
        return TRUE;
 
5319
}
 
5320
 
 
5321
static gboolean
 
5322
handle_popups (NautilusIconContainer *container,
 
5323
               GdkEventKey           *event,
 
5324
               const char            *signal)
 
5325
{
 
5326
        GdkEventButton button_event = { 0 };
 
5327
 
 
5328
        /* ensure we clear the drag state before showing the menu */
 
5329
        clear_drag_state (container);
 
5330
 
 
5331
        g_signal_emit_by_name (container, signal, &button_event);
 
5332
 
 
5333
        return TRUE;
 
5334
}
 
5335
 
 
5336
static int
 
5337
key_press_event (GtkWidget *widget,
 
5338
                 GdkEventKey *event)
 
5339
{
 
5340
        NautilusIconContainer *container;
 
5341
        gboolean handled;
 
5342
 
 
5343
        container = NAUTILUS_ICON_CONTAINER (widget);
 
5344
        handled = FALSE;
 
5345
 
 
5346
        if (is_renaming (container) || is_renaming_pending (container)) {
 
5347
                switch (event->keyval) {
 
5348
                case GDK_KEY_Return:
 
5349
                case GDK_KEY_KP_Enter:
 
5350
                        end_renaming_mode (container, TRUE);    
 
5351
                        handled = TRUE;
 
5352
                        break;                  
 
5353
                case GDK_KEY_Escape:
 
5354
                        end_renaming_mode (container, FALSE);
 
5355
                        handled = TRUE;
 
5356
                        break;
 
5357
                default:
 
5358
                        break;
 
5359
                }
 
5360
        } else {
 
5361
                switch (event->keyval) {
 
5362
                case GDK_KEY_Home:
 
5363
                case GDK_KEY_KP_Home:
 
5364
                        keyboard_home (container, event);
 
5365
                        handled = TRUE;
 
5366
                        break;
 
5367
                case GDK_KEY_End:
 
5368
                case GDK_KEY_KP_End:
 
5369
                        keyboard_end (container, event);
 
5370
                        handled = TRUE;
 
5371
                        break;
 
5372
                case GDK_KEY_Left:
 
5373
                case GDK_KEY_KP_Left:
 
5374
                        /* Don't eat Alt-Left, as that is used for history browsing */
 
5375
                        if ((event->state & GDK_MOD1_MASK) == 0) {
 
5376
                                keyboard_left (container, event);
 
5377
                                handled = TRUE;
 
5378
                        }
 
5379
                        break;
 
5380
                case GDK_KEY_Up:
 
5381
                case GDK_KEY_KP_Up:
 
5382
                        /* Don't eat Alt-Up, as that is used for alt-shift-Up */
 
5383
                        if ((event->state & GDK_MOD1_MASK) == 0) {
 
5384
                                keyboard_up (container, event);
 
5385
                                handled = TRUE;
 
5386
                        }
 
5387
                        break;
 
5388
                case GDK_KEY_Right:
 
5389
                case GDK_KEY_KP_Right:
 
5390
                        /* Don't eat Alt-Right, as that is used for history browsing */
 
5391
                        if ((event->state & GDK_MOD1_MASK) == 0) {
 
5392
                                keyboard_right (container, event);
 
5393
                                handled = TRUE;
 
5394
                        }
 
5395
                        break;
 
5396
                case GDK_KEY_Down:
 
5397
                case GDK_KEY_KP_Down:
 
5398
                        /* Don't eat Alt-Down, as that is used for Open */
 
5399
                        if ((event->state & GDK_MOD1_MASK) == 0) {
 
5400
                                keyboard_down (container, event);
 
5401
                                handled = TRUE;
 
5402
                        }
 
5403
                        break;
 
5404
                case GDK_KEY_space:
 
5405
                        keyboard_space (container, event);
 
5406
                        handled = TRUE;
 
5407
                        break;
 
5408
#ifndef TAB_NAVIGATION_DISABLED
 
5409
                case GDK_KEY_Tab:
 
5410
                case GDK_KEY_ISO_Left_Tab:
 
5411
                        select_previous_or_next_icon (container, 
 
5412
                                                      (event->state & GDK_SHIFT_MASK) == 0, event);
 
5413
                        handled = TRUE;
 
5414
                        break;
 
5415
#endif
 
5416
                case GDK_KEY_Return:
 
5417
                case GDK_KEY_KP_Enter:
 
5418
                        if ((event->state & GDK_SHIFT_MASK) != 0) {
 
5419
                                activate_selected_items_alternate (container, NULL);
 
5420
                        } else {
 
5421
                                activate_selected_items (container);
 
5422
                        }
 
5423
                        
 
5424
                        handled = TRUE;
 
5425
                        break;
 
5426
                case GDK_KEY_Escape:
 
5427
                        handled = undo_stretching (container);
 
5428
                        break;
 
5429
                case GDK_KEY_plus:
 
5430
                case GDK_KEY_minus:
 
5431
                case GDK_KEY_equal:
 
5432
                case GDK_KEY_KP_Add:
 
5433
                case GDK_KEY_KP_Subtract:
 
5434
                case GDK_KEY_0:
 
5435
                case GDK_KEY_KP_0:
 
5436
                        if (event->state & GDK_CONTROL_MASK) {
 
5437
                                handled = keyboard_stretching (container, event);
 
5438
                        }
 
5439
                        break;
 
5440
                case GDK_KEY_F10:
 
5441
                        /* handle Ctrl+F10 because we want to display the
 
5442
                         * background popup even if something is selected.
 
5443
                         * The other cases are handled by popup_menu().
 
5444
                         */
 
5445
                        if (event->state & GDK_CONTROL_MASK) {
 
5446
                                handled = handle_popups (container, event,
 
5447
                                                         "context_click_background");
 
5448
                        }
 
5449
                        break;
 
5450
                case GDK_KEY_v:
 
5451
                        /* Eat Control + v to not enable type ahead */
 
5452
                        if ((event->state & GDK_CONTROL_MASK) != 0) {
 
5453
                                handled = TRUE;
 
5454
                        }
 
5455
                        break;
 
5456
                default:
 
5457
                        break;
 
5458
                }
 
5459
        }
 
5460
 
 
5461
        if (!handled) {
 
5462
                handled = GTK_WIDGET_CLASS (nautilus_icon_container_parent_class)->key_press_event (widget, event);
 
5463
        }
 
5464
        
 
5465
        /* We pass the event to the search_entry.  If its text changes, then we
 
5466
         * start the typeahead find capabilities.
 
5467
         * Copied from NautilusIconContainer */
 
5468
        if (!handled &&
 
5469
            event->keyval != GDK_KEY_slash /* don't steal slash key event, used for "go to" */ &&
 
5470
            event->keyval != GDK_KEY_BackSpace &&
 
5471
            event->keyval != GDK_KEY_Delete) {
 
5472
                GdkEvent *new_event;
 
5473
                GdkWindow *window;
 
5474
                char *old_text;
 
5475
                const char *new_text;
 
5476
                gboolean retval;
 
5477
                GdkScreen *screen;
 
5478
                gboolean text_modified;
 
5479
                gulong popup_menu_id;
 
5480
 
 
5481
                nautilus_icon_container_ensure_interactive_directory (container);
 
5482
 
 
5483
                /* Make a copy of the current text */
 
5484
                old_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (container->details->search_entry)));
 
5485
                new_event = gdk_event_copy ((GdkEvent *) event);
 
5486
                window = ((GdkEventKey *) new_event)->window;
 
5487
                ((GdkEventKey *) new_event)->window = gtk_widget_get_window (container->details->search_entry);
 
5488
                gtk_widget_realize (container->details->search_window);
 
5489
 
 
5490
                popup_menu_id = g_signal_connect (container->details->search_entry, 
 
5491
                                                  "popup_menu", G_CALLBACK (gtk_true), NULL);
 
5492
 
 
5493
                /* Move the entry off screen */
 
5494
                screen = gtk_widget_get_screen (GTK_WIDGET (container));
 
5495
                gtk_window_move (GTK_WINDOW (container->details->search_window),
 
5496
                gdk_screen_get_width (screen) + 1,
 
5497
                gdk_screen_get_height (screen) + 1);
 
5498
                gtk_widget_show (container->details->search_window);
 
5499
 
 
5500
                /* Send the event to the window.  If the preedit_changed signal is emitted
 
5501
                 * during this event, we will set priv->imcontext_changed  */
 
5502
                container->details->imcontext_changed = FALSE;
 
5503
                retval = gtk_widget_event (container->details->search_entry, new_event);
 
5504
                gtk_widget_hide (container->details->search_window);
 
5505
 
 
5506
                g_signal_handler_disconnect (container->details->search_entry, 
 
5507
                                             popup_menu_id);
 
5508
 
 
5509
                /* We check to make sure that the entry tried to handle the text, and that
 
5510
                 * the text has changed. */
 
5511
                new_text = gtk_entry_get_text (GTK_ENTRY (container->details->search_entry));
 
5512
                text_modified = strcmp (old_text, new_text) != 0;
 
5513
                g_free (old_text);
 
5514
                if (container->details->imcontext_changed ||    /* we're in a preedit */
 
5515
                    (retval && text_modified)) {                /* ...or the text was modified */
 
5516
                        if (nautilus_icon_container_start_interactive_search (container)) {
 
5517
                                gtk_widget_grab_focus (GTK_WIDGET (container));
 
5518
                                return TRUE;
 
5519
                        } else {
 
5520
                                gtk_entry_set_text (GTK_ENTRY (container->details->search_entry), "");
 
5521
                                return FALSE;
 
5522
                        }
 
5523
                }
 
5524
 
 
5525
                ((GdkEventKey *) new_event)->window = window;
 
5526
                gdk_event_free (new_event);
 
5527
        }
 
5528
 
 
5529
        return handled;
 
5530
}
 
5531
 
 
5532
static gboolean
 
5533
popup_menu (GtkWidget *widget)
 
5534
{
 
5535
        NautilusIconContainer *container;
 
5536
 
 
5537
        container = NAUTILUS_ICON_CONTAINER (widget);
 
5538
 
 
5539
        if (has_selection (container)) {
 
5540
                handle_popups (container, NULL,
 
5541
                               "context_click_selection");
 
5542
        } else {
 
5543
                handle_popups (container, NULL,
 
5544
                               "context_click_background");
 
5545
        }
 
5546
 
 
5547
        return TRUE;
 
5548
}
 
5549
 
 
5550
static void
 
5551
draw_canvas_background (EelCanvas *canvas,
 
5552
                        cairo_t   *cr)
 
5553
{
 
5554
        /* Don't chain up to the parent to avoid clearing and redrawing */
 
5555
}
 
5556
 
 
5557
 
 
5558
static AtkObject *
 
5559
get_accessible (GtkWidget *widget)
 
5560
{
 
5561
        AtkObject *accessible;
 
5562
        
 
5563
        if ((accessible = eel_accessibility_get_atk_object (widget))) {
 
5564
                return accessible;
 
5565
        }
 
5566
        
 
5567
        accessible = g_object_new 
 
5568
                (nautilus_icon_container_accessible_get_type (), NULL);
 
5569
        
 
5570
        return eel_accessibility_set_atk_object_return (widget, accessible);
 
5571
}
 
5572
 
 
5573
static void
 
5574
grab_notify_cb  (GtkWidget        *widget,
 
5575
                 gboolean          was_grabbed)
 
5576
{
 
5577
        NautilusIconContainer *container;
 
5578
 
 
5579
        container = NAUTILUS_ICON_CONTAINER (widget);
 
5580
        
 
5581
        if (container->details->rubberband_info.active &&
 
5582
            !was_grabbed) {
 
5583
                /* we got a (un)grab-notify during rubberband.
 
5584
                 * This happens when a new modal dialog shows
 
5585
                 * up (e.g. authentication or an error). Stop
 
5586
                 * the rubberbanding so that we can handle the
 
5587
                 * dialog. */
 
5588
                stop_rubberbanding (container,
 
5589
                                    GDK_CURRENT_TIME);
 
5590
        }
 
5591
}
 
5592
 
 
5593
static void
 
5594
text_ellipsis_limit_changed_container_callback (gpointer callback_data)
 
5595
{
 
5596
        NautilusIconContainer *container;
 
5597
 
 
5598
        container = NAUTILUS_ICON_CONTAINER (callback_data);
 
5599
        invalidate_label_sizes (container);
 
5600
        schedule_redo_layout (container);
 
5601
}
 
5602
 
 
5603
static GObject*
 
5604
nautilus_icon_container_constructor (GType                  type,
 
5605
                                     guint                  n_construct_params,
 
5606
                                     GObjectConstructParam *construct_params)
 
5607
{
 
5608
        NautilusIconContainer *container;
 
5609
        GObject *object;
 
5610
 
 
5611
        object = G_OBJECT_CLASS (nautilus_icon_container_parent_class)->constructor
 
5612
                (type,
 
5613
                 n_construct_params,
 
5614
                 construct_params);
 
5615
 
 
5616
        container = NAUTILUS_ICON_CONTAINER (object);
 
5617
        if (nautilus_icon_container_get_is_desktop (container)) {
 
5618
                g_signal_connect_swapped (nautilus_desktop_preferences,
 
5619
                                          "changed::" NAUTILUS_PREFERENCES_DESKTOP_TEXT_ELLIPSIS_LIMIT,
 
5620
                                          G_CALLBACK (text_ellipsis_limit_changed_container_callback),
 
5621
                                          container);
 
5622
        } else {
 
5623
                g_signal_connect_swapped (nautilus_icon_view_preferences,
 
5624
                                          "changed::" NAUTILUS_PREFERENCES_ICON_VIEW_TEXT_ELLIPSIS_LIMIT,
 
5625
                                          G_CALLBACK (text_ellipsis_limit_changed_container_callback),
 
5626
                                          container);
 
5627
        }
 
5628
 
 
5629
        return object;
 
5630
}
 
5631
 
 
5632
/* Initialization.  */
 
5633
 
 
5634
static void
 
5635
nautilus_icon_container_class_init (NautilusIconContainerClass *class)
 
5636
{
 
5637
        GtkWidgetClass *widget_class;
 
5638
        EelCanvasClass *canvas_class;
 
5639
 
 
5640
        G_OBJECT_CLASS (class)->constructor = nautilus_icon_container_constructor;
 
5641
        G_OBJECT_CLASS (class)->finalize = finalize;
 
5642
 
 
5643
        /* Signals.  */
 
5644
 
 
5645
        signals[SELECTION_CHANGED]
 
5646
                = g_signal_new ("selection_changed",
 
5647
                                G_TYPE_FROM_CLASS (class),
 
5648
                                G_SIGNAL_RUN_LAST,
 
5649
                                G_STRUCT_OFFSET (NautilusIconContainerClass,
 
5650
                                                 selection_changed),
 
5651
                                NULL, NULL,
 
5652
                                g_cclosure_marshal_VOID__VOID,
 
5653
                                G_TYPE_NONE, 0);
 
5654
        signals[BUTTON_PRESS]
 
5655
                = g_signal_new ("button_press",
 
5656
                                G_TYPE_FROM_CLASS (class),
 
5657
                                G_SIGNAL_RUN_LAST,
 
5658
                                G_STRUCT_OFFSET (NautilusIconContainerClass,
 
5659
                                                 button_press),
 
5660
                                NULL, NULL,
 
5661
                                g_cclosure_marshal_generic,
 
5662
                                G_TYPE_BOOLEAN, 1,
 
5663
                                GDK_TYPE_EVENT);
 
5664
        signals[ACTIVATE]
 
5665
                = g_signal_new ("activate",
 
5666
                                G_TYPE_FROM_CLASS (class),
 
5667
                                G_SIGNAL_RUN_LAST,
 
5668
                                G_STRUCT_OFFSET (NautilusIconContainerClass,
 
5669
                                                 activate),
 
5670
                                NULL, NULL,
 
5671
                                g_cclosure_marshal_VOID__POINTER,
 
5672
                                G_TYPE_NONE, 1,
 
5673
                                G_TYPE_POINTER);
 
5674
        signals[ACTIVATE_ALTERNATE]
 
5675
                = g_signal_new ("activate_alternate",
 
5676
                                G_TYPE_FROM_CLASS (class),
 
5677
                                G_SIGNAL_RUN_LAST,
 
5678
                                G_STRUCT_OFFSET (NautilusIconContainerClass,
 
5679
                                                 activate_alternate),
 
5680
                                NULL, NULL,
 
5681
                                g_cclosure_marshal_VOID__POINTER,
 
5682
                                G_TYPE_NONE, 1,
 
5683
                                G_TYPE_POINTER);
 
5684
        signals[ACTIVATE_PREVIEWER]
 
5685
                = g_signal_new ("activate_previewer",
 
5686
                                G_TYPE_FROM_CLASS (class),
 
5687
                                G_SIGNAL_RUN_LAST,
 
5688
                                G_STRUCT_OFFSET (NautilusIconContainerClass,
 
5689
                                                 activate_previewer),
 
5690
                                NULL, NULL,
 
5691
                                g_cclosure_marshal_generic,
 
5692
                                G_TYPE_NONE, 2,
 
5693
                                G_TYPE_POINTER, G_TYPE_POINTER);
 
5694
        signals[CONTEXT_CLICK_SELECTION]
 
5695
                = g_signal_new ("context_click_selection",
 
5696
                                G_TYPE_FROM_CLASS (class),
 
5697
                                G_SIGNAL_RUN_LAST,
 
5698
                                G_STRUCT_OFFSET (NautilusIconContainerClass,
 
5699
                                                 context_click_selection),
 
5700
                                NULL, NULL,
 
5701
                                g_cclosure_marshal_VOID__POINTER,
 
5702
                                G_TYPE_NONE, 1,
 
5703
                                G_TYPE_POINTER);
 
5704
        signals[CONTEXT_CLICK_BACKGROUND]
 
5705
                = g_signal_new ("context_click_background",
 
5706
                                G_TYPE_FROM_CLASS (class),
 
5707
                                G_SIGNAL_RUN_LAST,
 
5708
                                G_STRUCT_OFFSET (NautilusIconContainerClass,
 
5709
                                                 context_click_background),
 
5710
                                NULL, NULL,
 
5711
                                g_cclosure_marshal_VOID__POINTER,
 
5712
                                G_TYPE_NONE, 1,
 
5713
                                G_TYPE_POINTER);
 
5714
        signals[MIDDLE_CLICK]
 
5715
                = g_signal_new ("middle_click",
 
5716
                                G_TYPE_FROM_CLASS (class),
 
5717
                                G_SIGNAL_RUN_LAST,
 
5718
                                G_STRUCT_OFFSET (NautilusIconContainerClass,
 
5719
                                                 middle_click),
 
5720
                                NULL, NULL,
 
5721
                                g_cclosure_marshal_VOID__POINTER,
 
5722
                                G_TYPE_NONE, 1,
 
5723
                                G_TYPE_POINTER);
 
5724
        signals[ICON_POSITION_CHANGED]
 
5725
                = g_signal_new ("icon_position_changed",
 
5726
                                G_TYPE_FROM_CLASS (class),
 
5727
                                G_SIGNAL_RUN_LAST,
 
5728
                                G_STRUCT_OFFSET (NautilusIconContainerClass,
 
5729
                                                 icon_position_changed),
 
5730
                                NULL, NULL,
 
5731
                                g_cclosure_marshal_generic,
 
5732
                                G_TYPE_NONE, 2,
 
5733
                                G_TYPE_POINTER,
 
5734
                                G_TYPE_POINTER);
 
5735
        signals[ICON_STRETCH_STARTED]
 
5736
                = g_signal_new ("icon_stretch_started",
 
5737
                                G_TYPE_FROM_CLASS (class),
 
5738
                                G_SIGNAL_RUN_LAST,
 
5739
                                G_STRUCT_OFFSET (NautilusIconContainerClass,
 
5740
                                                 icon_stretch_started),
 
5741
                                NULL, NULL,
 
5742
                                g_cclosure_marshal_VOID__POINTER,
 
5743
                                G_TYPE_NONE, 1,
 
5744
                                G_TYPE_POINTER);
 
5745
        signals[ICON_STRETCH_ENDED]
 
5746
                = g_signal_new ("icon_stretch_ended",
 
5747
                                G_TYPE_FROM_CLASS (class),
 
5748
                                G_SIGNAL_RUN_LAST,
 
5749
                                G_STRUCT_OFFSET (NautilusIconContainerClass,
 
5750
                                                     icon_stretch_ended),
 
5751
                                NULL, NULL,
 
5752
                                g_cclosure_marshal_VOID__POINTER,
 
5753
                                G_TYPE_NONE, 1,
 
5754
                                G_TYPE_POINTER);
 
5755
        signals[ICON_RENAME_STARTED]
 
5756
                = g_signal_new ("icon_rename_started",
 
5757
                                G_TYPE_FROM_CLASS (class),
 
5758
                                G_SIGNAL_RUN_LAST,
 
5759
                                G_STRUCT_OFFSET (NautilusIconContainerClass,
 
5760
                                                 icon_rename_started),
 
5761
                                NULL, NULL,
 
5762
                                g_cclosure_marshal_VOID__POINTER,
 
5763
                                G_TYPE_NONE, 1,
 
5764
                                G_TYPE_POINTER);
 
5765
        signals[ICON_RENAME_ENDED]
 
5766
                = g_signal_new ("icon_rename_ended",
 
5767
                                G_TYPE_FROM_CLASS (class),
 
5768
                                G_SIGNAL_RUN_LAST,
 
5769
                                G_STRUCT_OFFSET (NautilusIconContainerClass,
 
5770
                                                 icon_rename_ended),
 
5771
                                NULL, NULL,
 
5772
                                g_cclosure_marshal_generic,
 
5773
                                G_TYPE_NONE, 2,
 
5774
                                G_TYPE_POINTER,
 
5775
                                G_TYPE_STRING);
 
5776
        signals[GET_ICON_URI]
 
5777
                = g_signal_new ("get_icon_uri",
 
5778
                                G_TYPE_FROM_CLASS (class),
 
5779
                                G_SIGNAL_RUN_LAST,
 
5780
                                G_STRUCT_OFFSET (NautilusIconContainerClass,
 
5781
                                                 get_icon_uri),
 
5782
                                NULL, NULL,
 
5783
                                g_cclosure_marshal_generic,
 
5784
                                G_TYPE_STRING, 1,
 
5785
                                G_TYPE_POINTER);
 
5786
        signals[GET_ICON_DROP_TARGET_URI]
 
5787
                = g_signal_new ("get_icon_drop_target_uri",
 
5788
                                G_TYPE_FROM_CLASS (class),
 
5789
                                G_SIGNAL_RUN_LAST,
 
5790
                                G_STRUCT_OFFSET (NautilusIconContainerClass,
 
5791
                                                 get_icon_drop_target_uri),
 
5792
                                NULL, NULL,
 
5793
                                g_cclosure_marshal_generic,
 
5794
                                G_TYPE_STRING, 1,
 
5795
                                G_TYPE_POINTER);
 
5796
        signals[MOVE_COPY_ITEMS] 
 
5797
                = g_signal_new ("move_copy_items",
 
5798
                                G_TYPE_FROM_CLASS (class),
 
5799
                                G_SIGNAL_RUN_LAST,
 
5800
                                G_STRUCT_OFFSET (NautilusIconContainerClass, 
 
5801
                                                 move_copy_items),
 
5802
                                NULL, NULL,
 
5803
                                g_cclosure_marshal_generic,
 
5804
                                G_TYPE_NONE, 6,
 
5805
                                G_TYPE_POINTER,
 
5806
                                G_TYPE_POINTER,
 
5807
                                G_TYPE_POINTER,
 
5808
                                GDK_TYPE_DRAG_ACTION,
 
5809
                                G_TYPE_INT,
 
5810
                                G_TYPE_INT);
 
5811
        signals[HANDLE_NETSCAPE_URL]
 
5812
                = g_signal_new ("handle_netscape_url",
 
5813
                                G_TYPE_FROM_CLASS (class),
 
5814
                                G_SIGNAL_RUN_LAST,
 
5815
                                G_STRUCT_OFFSET (NautilusIconContainerClass, 
 
5816
                                                 handle_netscape_url),
 
5817
                                NULL, NULL,
 
5818
                                g_cclosure_marshal_generic,
 
5819
                                G_TYPE_NONE, 5,
 
5820
                                G_TYPE_STRING,
 
5821
                                G_TYPE_STRING,
 
5822
                                GDK_TYPE_DRAG_ACTION,
 
5823
                                G_TYPE_INT,
 
5824
                                G_TYPE_INT);
 
5825
        signals[HANDLE_URI_LIST] 
 
5826
                = g_signal_new ("handle_uri_list",
 
5827
                                G_TYPE_FROM_CLASS (class),
 
5828
                                G_SIGNAL_RUN_LAST,
 
5829
                                G_STRUCT_OFFSET (NautilusIconContainerClass, 
 
5830
                                                     handle_uri_list),
 
5831
                                NULL, NULL,
 
5832
                                g_cclosure_marshal_generic,
 
5833
                                G_TYPE_NONE, 5,
 
5834
                                G_TYPE_STRING,
 
5835
                                G_TYPE_STRING,
 
5836
                                GDK_TYPE_DRAG_ACTION,
 
5837
                                G_TYPE_INT,
 
5838
                                G_TYPE_INT);
 
5839
        signals[HANDLE_TEXT]
 
5840
                = g_signal_new ("handle_text",
 
5841
                                G_TYPE_FROM_CLASS (class),
 
5842
                                G_SIGNAL_RUN_LAST,
 
5843
                                G_STRUCT_OFFSET (NautilusIconContainerClass, 
 
5844
                                                 handle_text),
 
5845
                                NULL, NULL,
 
5846
                                g_cclosure_marshal_generic,
 
5847
                                G_TYPE_NONE, 5,
 
5848
                                G_TYPE_STRING,
 
5849
                                G_TYPE_STRING,
 
5850
                                GDK_TYPE_DRAG_ACTION,
 
5851
                                G_TYPE_INT,
 
5852
                                G_TYPE_INT);
 
5853
        signals[HANDLE_RAW]
 
5854
                = g_signal_new ("handle_raw",
 
5855
                                G_TYPE_FROM_CLASS (class),
 
5856
                                G_SIGNAL_RUN_LAST,
 
5857
                                G_STRUCT_OFFSET (NautilusIconContainerClass,
 
5858
                                                 handle_raw),
 
5859
                                NULL, NULL,
 
5860
                                g_cclosure_marshal_generic,
 
5861
                                G_TYPE_NONE, 7,
 
5862
                                G_TYPE_POINTER,
 
5863
                                G_TYPE_INT,
 
5864
                                G_TYPE_STRING,
 
5865
                                G_TYPE_STRING,
 
5866
                                GDK_TYPE_DRAG_ACTION,
 
5867
                                G_TYPE_INT,
 
5868
                                G_TYPE_INT);
 
5869
        signals[GET_CONTAINER_URI] 
 
5870
                = g_signal_new ("get_container_uri",
 
5871
                                G_TYPE_FROM_CLASS (class),
 
5872
                                G_SIGNAL_RUN_LAST,
 
5873
                                G_STRUCT_OFFSET (NautilusIconContainerClass, 
 
5874
                                                 get_container_uri),
 
5875
                                NULL, NULL,
 
5876
                                g_cclosure_marshal_generic,
 
5877
                                G_TYPE_STRING, 0);
 
5878
        signals[CAN_ACCEPT_ITEM] 
 
5879
                = g_signal_new ("can_accept_item",
 
5880
                                G_TYPE_FROM_CLASS (class),
 
5881
                                G_SIGNAL_RUN_LAST,
 
5882
                                G_STRUCT_OFFSET (NautilusIconContainerClass, 
 
5883
                                                 can_accept_item),
 
5884
                                NULL, NULL,
 
5885
                                g_cclosure_marshal_generic,
 
5886
                                G_TYPE_INT, 2,
 
5887
                                G_TYPE_POINTER,
 
5888
                                G_TYPE_STRING);
 
5889
        signals[GET_STORED_ICON_POSITION]
 
5890
                = g_signal_new ("get_stored_icon_position",
 
5891
                                G_TYPE_FROM_CLASS (class),
 
5892
                                G_SIGNAL_RUN_LAST,
 
5893
                                G_STRUCT_OFFSET (NautilusIconContainerClass,
 
5894
                                                 get_stored_icon_position),
 
5895
                                NULL, NULL,
 
5896
                                g_cclosure_marshal_generic,
 
5897
                                G_TYPE_BOOLEAN, 2,
 
5898
                                G_TYPE_POINTER,
 
5899
                                G_TYPE_POINTER);
 
5900
        signals[GET_STORED_LAYOUT_TIMESTAMP]
 
5901
                = g_signal_new ("get_stored_layout_timestamp",
 
5902
                                G_TYPE_FROM_CLASS (class),
 
5903
                                G_SIGNAL_RUN_LAST,
 
5904
                                G_STRUCT_OFFSET (NautilusIconContainerClass,
 
5905
                                                 get_stored_layout_timestamp),
 
5906
                                NULL, NULL,
 
5907
                                g_cclosure_marshal_generic,
 
5908
                                G_TYPE_BOOLEAN, 2,
 
5909
                                G_TYPE_POINTER,
 
5910
                                G_TYPE_POINTER);
 
5911
        signals[STORE_LAYOUT_TIMESTAMP]
 
5912
                = g_signal_new ("store_layout_timestamp",
 
5913
                                G_TYPE_FROM_CLASS (class),
 
5914
                                G_SIGNAL_RUN_LAST,
 
5915
                                G_STRUCT_OFFSET (NautilusIconContainerClass,
 
5916
                                                 store_layout_timestamp),
 
5917
                                NULL, NULL,
 
5918
                                g_cclosure_marshal_generic,
 
5919
                                G_TYPE_BOOLEAN, 2,
 
5920
                                G_TYPE_POINTER,
 
5921
                                G_TYPE_POINTER);
 
5922
        signals[LAYOUT_CHANGED]
 
5923
                = g_signal_new ("layout_changed",
 
5924
                                G_TYPE_FROM_CLASS (class),
 
5925
                                G_SIGNAL_RUN_LAST,
 
5926
                                G_STRUCT_OFFSET (NautilusIconContainerClass,
 
5927
                                                 layout_changed),
 
5928
                                NULL, NULL,
 
5929
                                g_cclosure_marshal_VOID__VOID,
 
5930
                                G_TYPE_NONE, 0);
 
5931
        signals[BAND_SELECT_STARTED]
 
5932
                = g_signal_new ("band_select_started",
 
5933
                                G_TYPE_FROM_CLASS (class),
 
5934
                                G_SIGNAL_RUN_LAST,
 
5935
                                G_STRUCT_OFFSET (NautilusIconContainerClass,
 
5936
                                                 band_select_started),
 
5937
                                NULL, NULL,
 
5938
                                g_cclosure_marshal_VOID__VOID,
 
5939
                                G_TYPE_NONE, 0);
 
5940
        signals[BAND_SELECT_ENDED]
 
5941
                = g_signal_new ("band_select_ended",
 
5942
                                G_TYPE_FROM_CLASS (class),
 
5943
                                G_SIGNAL_RUN_LAST,
 
5944
                                G_STRUCT_OFFSET (NautilusIconContainerClass,
 
5945
                                                     band_select_ended),
 
5946
                                NULL, NULL,
 
5947
                                g_cclosure_marshal_VOID__VOID,
 
5948
                                G_TYPE_NONE, 0);
 
5949
        signals[ICON_ADDED]
 
5950
                = g_signal_new ("icon_added",
 
5951
                                G_TYPE_FROM_CLASS (class),
 
5952
                                G_SIGNAL_RUN_LAST,
 
5953
                                G_STRUCT_OFFSET (NautilusIconContainerClass,
 
5954
                                                 icon_added),
 
5955
                                NULL, NULL,
 
5956
                                g_cclosure_marshal_VOID__POINTER,
 
5957
                                G_TYPE_NONE, 1, G_TYPE_POINTER);
 
5958
        signals[ICON_REMOVED]
 
5959
                = g_signal_new ("icon_removed",
 
5960
                                G_TYPE_FROM_CLASS (class),
 
5961
                                G_SIGNAL_RUN_LAST,
 
5962
                                G_STRUCT_OFFSET (NautilusIconContainerClass,
 
5963
                                                 icon_removed),
 
5964
                                NULL, NULL,
 
5965
                                g_cclosure_marshal_VOID__POINTER,
 
5966
                                G_TYPE_NONE, 1, G_TYPE_POINTER);
 
5967
 
 
5968
        signals[CLEARED]
 
5969
                = g_signal_new ("cleared",
 
5970
                                G_TYPE_FROM_CLASS (class),
 
5971
                                G_SIGNAL_RUN_LAST,
 
5972
                                G_STRUCT_OFFSET (NautilusIconContainerClass,
 
5973
                                                 cleared),
 
5974
                                NULL, NULL,
 
5975
                                g_cclosure_marshal_VOID__VOID,
 
5976
                                G_TYPE_NONE, 0);
 
5977
 
 
5978
        /* GtkWidget class.  */
 
5979
 
 
5980
        widget_class = GTK_WIDGET_CLASS (class);
 
5981
        widget_class->destroy = destroy;
 
5982
        widget_class->size_allocate = size_allocate;
 
5983
        widget_class->get_request_mode = get_request_mode;
 
5984
        widget_class->get_preferred_width = get_prefered_width;
 
5985
        widget_class->get_preferred_height = get_prefered_height;
 
5986
        widget_class->realize = realize;
 
5987
        widget_class->unrealize = unrealize;
 
5988
        widget_class->button_press_event = button_press_event;
 
5989
        widget_class->button_release_event = button_release_event;
 
5990
        widget_class->motion_notify_event = motion_notify_event;
 
5991
        widget_class->key_press_event = key_press_event;
 
5992
        widget_class->popup_menu = popup_menu;
 
5993
        widget_class->get_accessible = get_accessible;
 
5994
        widget_class->style_updated = style_updated;
 
5995
        widget_class->grab_notify = grab_notify_cb;
 
5996
 
 
5997
        canvas_class = EEL_CANVAS_CLASS (class);
 
5998
        canvas_class->draw_background = draw_canvas_background;
 
5999
 
 
6000
        gtk_widget_class_install_style_property (widget_class,
 
6001
                                                 g_param_spec_boolean ("activate_prelight_icon_label",
 
6002
                                                                     "Activate Prelight Icon Label",
 
6003
                                                                     "Whether icon labels should make use of its prelight color in prelight state",
 
6004
                                                                     FALSE,
 
6005
                                                                     G_PARAM_READABLE));
 
6006
}
 
6007
 
 
6008
static void
 
6009
update_selected (NautilusIconContainer *container)
 
6010
{
 
6011
        GList *node;
 
6012
        NautilusIcon *icon;
 
6013
        
 
6014
        for (node = container->details->icons; node != NULL; node = node->next) {
 
6015
                icon = node->data;
 
6016
                if (icon->is_selected) {
 
6017
                        eel_canvas_item_request_update (EEL_CANVAS_ITEM (icon->item));
 
6018
                }
 
6019
        }
 
6020
}
 
6021
 
 
6022
static gboolean
 
6023
handle_focus_in_event (GtkWidget *widget, GdkEventFocus *event, gpointer user_data)
 
6024
{
 
6025
        update_selected (NAUTILUS_ICON_CONTAINER (widget));
 
6026
 
 
6027
        return FALSE;
 
6028
}
 
6029
 
 
6030
static gboolean
 
6031
handle_focus_out_event (GtkWidget *widget, GdkEventFocus *event, gpointer user_data)
 
6032
{
 
6033
        /* End renaming and commit change. */
 
6034
        end_renaming_mode (NAUTILUS_ICON_CONTAINER (widget), TRUE);
 
6035
        update_selected (NAUTILUS_ICON_CONTAINER (widget));
 
6036
 
 
6037
        return FALSE;
 
6038
}
 
6039
 
 
6040
 
 
6041
static int text_ellipsis_limits[NAUTILUS_ZOOM_LEVEL_N_ENTRIES];
 
6042
static int desktop_text_ellipsis_limit;
 
6043
 
 
6044
static gboolean
 
6045
get_text_ellipsis_limit_for_zoom (char **strs,
 
6046
                                  const char *zoom_level,
 
6047
                                  int *limit)
 
6048
{
 
6049
        char **p;
 
6050
        char *str;
 
6051
        gboolean success;
 
6052
 
 
6053
        success = FALSE;
 
6054
 
 
6055
        /* default */
 
6056
        *limit = 3;
 
6057
 
 
6058
        if (zoom_level != NULL) {
 
6059
                str = g_strdup_printf ("%s:%%d", zoom_level);
 
6060
        } else {
 
6061
                str = g_strdup ("%d");
 
6062
        }
 
6063
 
 
6064
        if (strs != NULL) {
 
6065
                for (p = strs; *p != NULL; p++) {
 
6066
                        if (sscanf (*p, str, limit)) {
 
6067
                                success = TRUE;
 
6068
                        }
 
6069
                }
 
6070
        }
 
6071
 
 
6072
        g_free (str);
 
6073
 
 
6074
        return success;
 
6075
}
 
6076
 
 
6077
static const char * zoom_level_names[] = {
 
6078
        "smallest",
 
6079
        "smaller",
 
6080
        "small",
 
6081
        "standard",
 
6082
        "large",
 
6083
        "larger",
 
6084
        "largest"
 
6085
};
 
6086
 
 
6087
static void
 
6088
text_ellipsis_limit_changed_callback (gpointer callback_data)
 
6089
{
 
6090
        char **pref;
 
6091
        unsigned int i;
 
6092
        int one_limit;
 
6093
 
 
6094
        pref = g_settings_get_strv (nautilus_icon_view_preferences,
 
6095
                                    NAUTILUS_PREFERENCES_ICON_VIEW_TEXT_ELLIPSIS_LIMIT);
 
6096
 
 
6097
        /* set default */
 
6098
        get_text_ellipsis_limit_for_zoom (pref, NULL, &one_limit);
 
6099
        for (i = 0; i < NAUTILUS_ZOOM_LEVEL_N_ENTRIES; i++) {
 
6100
                text_ellipsis_limits[i] = one_limit;
 
6101
        }
 
6102
 
 
6103
        /* override for each zoom level */
 
6104
        for (i = 0; i < G_N_ELEMENTS(zoom_level_names); i++) {
 
6105
                if (get_text_ellipsis_limit_for_zoom (pref,
 
6106
                                                      zoom_level_names[i],
 
6107
                                                      &one_limit)) {
 
6108
                        text_ellipsis_limits[i] = one_limit;
 
6109
                }
 
6110
        }
 
6111
 
 
6112
        g_strfreev (pref);
 
6113
}
 
6114
 
 
6115
static void
 
6116
desktop_text_ellipsis_limit_changed_callback (gpointer callback_data)
 
6117
{
 
6118
        int pref;
 
6119
 
 
6120
        pref = g_settings_get_int (nautilus_desktop_preferences, NAUTILUS_PREFERENCES_DESKTOP_TEXT_ELLIPSIS_LIMIT);
 
6121
        desktop_text_ellipsis_limit = pref;
 
6122
}
 
6123
 
 
6124
static void
 
6125
nautilus_icon_container_init (NautilusIconContainer *container)
 
6126
{
 
6127
        NautilusIconContainerDetails *details;
 
6128
        static gboolean setup_prefs = FALSE;
 
6129
 
 
6130
        details = g_new0 (NautilusIconContainerDetails, 1);
 
6131
 
 
6132
        details->icon_set = g_hash_table_new (g_direct_hash, g_direct_equal);
 
6133
        details->layout_timestamp = UNDEFINED_TIME;
 
6134
        details->active_background = TRUE;
 
6135
        details->zoom_level = NAUTILUS_ZOOM_LEVEL_STANDARD;
 
6136
 
 
6137
        details->font_size_table[NAUTILUS_ZOOM_LEVEL_SMALLEST] = -2 * PANGO_SCALE;
 
6138
        details->font_size_table[NAUTILUS_ZOOM_LEVEL_SMALLER] = -2 * PANGO_SCALE;
 
6139
        details->font_size_table[NAUTILUS_ZOOM_LEVEL_SMALL] = -0 * PANGO_SCALE;
 
6140
        details->font_size_table[NAUTILUS_ZOOM_LEVEL_STANDARD] = 0 * PANGO_SCALE;
 
6141
        details->font_size_table[NAUTILUS_ZOOM_LEVEL_LARGE] = 0 * PANGO_SCALE;
 
6142
        details->font_size_table[NAUTILUS_ZOOM_LEVEL_LARGER] = 0 * PANGO_SCALE;
 
6143
        details->font_size_table[NAUTILUS_ZOOM_LEVEL_LARGEST] = 0 * PANGO_SCALE;
 
6144
 
 
6145
        container->details = details;
 
6146
 
 
6147
        g_signal_connect (container, "focus-in-event",
 
6148
                          G_CALLBACK (handle_focus_in_event), NULL);
 
6149
        g_signal_connect (container, "focus-out-event",
 
6150
                          G_CALLBACK (handle_focus_out_event), NULL);
 
6151
 
 
6152
        if (!setup_prefs) {
 
6153
                g_signal_connect_swapped (nautilus_icon_view_preferences,
 
6154
                                          "changed::" NAUTILUS_PREFERENCES_ICON_VIEW_TEXT_ELLIPSIS_LIMIT,
 
6155
                                          G_CALLBACK (text_ellipsis_limit_changed_callback),
 
6156
                                          NULL);
 
6157
                text_ellipsis_limit_changed_callback (NULL);
 
6158
 
 
6159
                g_signal_connect_swapped (nautilus_icon_view_preferences,
 
6160
                                          "changed::" NAUTILUS_PREFERENCES_DESKTOP_TEXT_ELLIPSIS_LIMIT,
 
6161
                                          G_CALLBACK (desktop_text_ellipsis_limit_changed_callback),
 
6162
                                          NULL);
 
6163
                desktop_text_ellipsis_limit_changed_callback (NULL);
 
6164
 
 
6165
                setup_prefs = TRUE;
 
6166
        }
 
6167
}
 
6168
 
 
6169
typedef struct {
 
6170
        NautilusIconContainer *container;
 
6171
        GdkEventButton        *event;
 
6172
} ContextMenuParameters;
 
6173
 
 
6174
static gboolean
 
6175
handle_icon_double_click (NautilusIconContainer *container,
 
6176
                          NautilusIcon *icon,
 
6177
                          GdkEventButton *event)
 
6178
{
 
6179
        NautilusIconContainerDetails *details;
 
6180
 
 
6181
        if (event->button != DRAG_BUTTON) {
 
6182
                return FALSE;
 
6183
        }
 
6184
 
 
6185
        details = container->details;
 
6186
 
 
6187
        if (!details->single_click_mode &&
 
6188
            clicked_within_double_click_interval (container) &&
 
6189
            details->double_click_icon[0] == details->double_click_icon[1] &&
 
6190
            details->double_click_button[0] == details->double_click_button[1]) {
 
6191
                if (!button_event_modifies_selection (event)) {
 
6192
                        activate_selected_items (container);
 
6193
                        return TRUE;
 
6194
                } else if ((event->state & GDK_CONTROL_MASK) == 0 &&
 
6195
                           (event->state & GDK_SHIFT_MASK) != 0) {
 
6196
                        activate_selected_items_alternate (container, icon);
 
6197
                        return TRUE;
 
6198
                }
 
6199
        }
 
6200
 
 
6201
        return FALSE;
 
6202
}
 
6203
 
 
6204
/* NautilusIcon event handling.  */
 
6205
 
 
6206
/* Conceptually, pressing button 1 together with CTRL or SHIFT toggles
 
6207
 * selection of a single icon without affecting the other icons;
 
6208
 * without CTRL or SHIFT, it selects a single icon and un-selects all
 
6209
 * the other icons.  But in this latter case, the de-selection should
 
6210
 * only happen when the button is released if the icon is already
 
6211
 * selected, because the user might select multiple icons and drag all
 
6212
 * of them by doing a simple click-drag.
 
6213
*/
 
6214
 
 
6215
static gboolean
 
6216
handle_icon_button_press (NautilusIconContainer *container,
 
6217
                          NautilusIcon *icon,
 
6218
                          GdkEventButton *event)
 
6219
{
 
6220
        NautilusIconContainerDetails *details;
 
6221
 
 
6222
        details = container->details;
 
6223
 
 
6224
        if (event->type == GDK_2BUTTON_PRESS || event->type == GDK_3BUTTON_PRESS) {
 
6225
                return TRUE;
 
6226
        }
 
6227
        
 
6228
        if (event->button != DRAG_BUTTON
 
6229
            && event->button != CONTEXTUAL_MENU_BUTTON
 
6230
            && event->button != DRAG_MENU_BUTTON) {
 
6231
                return TRUE;
 
6232
        }
 
6233
 
 
6234
        if ((event->button == DRAG_BUTTON) &&
 
6235
            event->type == GDK_BUTTON_PRESS) {
 
6236
                /* The next double click has to be on this icon */
 
6237
                details->double_click_icon[1] = details->double_click_icon[0];
 
6238
                details->double_click_icon[0] = icon;
 
6239
 
 
6240
                details->double_click_button[1] = details->double_click_button[0];
 
6241
                details->double_click_button[0] = event->button;
 
6242
        }
 
6243
 
 
6244
        if (handle_icon_double_click (container, icon, event)) {
 
6245
                /* Double clicking does not trigger a D&D action. */
 
6246
                details->drag_button = 0;
 
6247
                details->drag_icon = NULL;
 
6248
                return TRUE;
 
6249
        }
 
6250
 
 
6251
        if (event->button == DRAG_BUTTON
 
6252
            || event->button == DRAG_MENU_BUTTON) {
 
6253
                        details->drag_button = event->button;
 
6254
                details->drag_icon = icon;
 
6255
                details->drag_x = event->x;
 
6256
                details->drag_y = event->y;
 
6257
                details->drag_state = DRAG_STATE_MOVE_OR_COPY;
 
6258
                details->drag_started = FALSE;
 
6259
 
 
6260
                /* Check to see if this is a click on the stretch handles.
 
6261
                 * If so, it won't modify the selection.
 
6262
                 */
 
6263
                if (icon == container->details->stretch_icon) {
 
6264
                        if (start_stretching (container)) {
 
6265
                                return TRUE;
 
6266
                        }
 
6267
                }
 
6268
        }
 
6269
 
 
6270
        /* Modify the selection as appropriate. Selection is modified
 
6271
         * the same way for contextual menu as it would be without. 
 
6272
         */
 
6273
        details->icon_selected_on_button_down = icon->is_selected;
 
6274
        
 
6275
        if ((event->button == DRAG_BUTTON || event->button == MIDDLE_BUTTON) &&
 
6276
            (event->state & GDK_SHIFT_MASK) != 0) {
 
6277
                NautilusIcon *start_icon;
 
6278
 
 
6279
                start_icon = details->range_selection_base_icon;
 
6280
                if (start_icon == NULL || !start_icon->is_selected) {
 
6281
                        start_icon = icon;
 
6282
                        details->range_selection_base_icon = icon;
 
6283
                } 
 
6284
                if (select_range (container, start_icon, icon,
 
6285
                                  (event->state & GDK_CONTROL_MASK) == 0)) {
 
6286
                        g_signal_emit (container,
 
6287
                                       signals[SELECTION_CHANGED], 0);
 
6288
                }
 
6289
        } else if (!details->icon_selected_on_button_down) {
 
6290
                details->range_selection_base_icon = icon;
 
6291
                if (button_event_modifies_selection (event)) {
 
6292
                        icon_toggle_selected (container, icon);
 
6293
                        g_signal_emit (container,
 
6294
                                       signals[SELECTION_CHANGED], 0);
 
6295
                } else {
 
6296
                        select_one_unselect_others (container, icon);
 
6297
                        g_signal_emit (container,
 
6298
                                       signals[SELECTION_CHANGED], 0);
 
6299
                }
 
6300
        }
 
6301
 
 
6302
        if (event->button == CONTEXTUAL_MENU_BUTTON) {
 
6303
                clear_drag_state (container);
 
6304
 
 
6305
                g_signal_emit (container,
 
6306
                               signals[CONTEXT_CLICK_SELECTION], 0,
 
6307
                               event);
 
6308
        }
 
6309
 
 
6310
 
 
6311
        return TRUE;
 
6312
}
 
6313
 
 
6314
static int
 
6315
item_event_callback (EelCanvasItem *item,
 
6316
                     GdkEvent *event,
 
6317
                     gpointer data)
 
6318
{
 
6319
        NautilusIconContainer *container;
 
6320
        NautilusIcon *icon;
 
6321
 
 
6322
        container = NAUTILUS_ICON_CONTAINER (data);
 
6323
 
 
6324
        icon = NAUTILUS_ICON_CANVAS_ITEM (item)->user_data;
 
6325
        g_assert (icon != NULL);
 
6326
 
 
6327
        switch (event->type) {
 
6328
        case GDK_BUTTON_PRESS:
 
6329
                if (handle_icon_button_press (container, icon, &event->button)) {
 
6330
                        /* Stop the event from being passed along further. Returning
 
6331
                         * TRUE ain't enough. 
 
6332
                         */
 
6333
                        return TRUE;
 
6334
                }
 
6335
                return FALSE;
 
6336
        default:
 
6337
                return FALSE;
 
6338
        }
 
6339
}
 
6340
 
 
6341
GtkWidget *
 
6342
nautilus_icon_container_new (void)
 
6343
{
 
6344
        return gtk_widget_new (NAUTILUS_TYPE_ICON_CONTAINER, NULL);
 
6345
}
 
6346
 
 
6347
/* Clear all of the icons in the container. */
 
6348
void
 
6349
nautilus_icon_container_clear (NautilusIconContainer *container)
 
6350
{
 
6351
        NautilusIconContainerDetails *details;
 
6352
        NautilusIcon *icon;
 
6353
        GList *p;
 
6354
 
 
6355
        g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container));
 
6356
 
 
6357
        details = container->details;
 
6358
        details->layout_timestamp = UNDEFINED_TIME;
 
6359
        details->store_layout_timestamps_when_finishing_new_icons = FALSE;
 
6360
 
 
6361
        if (details->icons == NULL) {
 
6362
                return;
 
6363
        }
 
6364
 
 
6365
        end_renaming_mode (container, TRUE);
 
6366
        
 
6367
        clear_keyboard_focus (container);
 
6368
        clear_keyboard_rubberband_start (container);
 
6369
        unschedule_keyboard_icon_reveal (container);
 
6370
        set_pending_icon_to_reveal (container, NULL);
 
6371
        details->stretch_icon = NULL;
 
6372
        details->drop_target = NULL;
 
6373
 
 
6374
        for (p = details->icons; p != NULL; p = p->next) {
 
6375
                icon = p->data;
 
6376
                if (icon->is_monitored) {
 
6377
                        nautilus_icon_container_stop_monitor_top_left (container,
 
6378
                                                                       icon->data,
 
6379
                                                                       icon);
 
6380
                }
 
6381
                icon_free (p->data);
 
6382
        }
 
6383
        g_list_free (details->icons);
 
6384
        details->icons = NULL;
 
6385
        g_list_free (details->new_icons);
 
6386
        details->new_icons = NULL;
 
6387
        
 
6388
        g_hash_table_destroy (details->icon_set);
 
6389
        details->icon_set = g_hash_table_new (g_direct_hash, g_direct_equal);
 
6390
 
 
6391
        nautilus_icon_container_update_scroll_region (container);
 
6392
}
 
6393
 
 
6394
gboolean
 
6395
nautilus_icon_container_is_empty (NautilusIconContainer *container)
 
6396
{
 
6397
        return container->details->icons == NULL;
 
6398
}
 
6399
 
 
6400
NautilusIconData *
 
6401
nautilus_icon_container_get_first_visible_icon (NautilusIconContainer *container)
 
6402
{
 
6403
        GList *l;
 
6404
        NautilusIcon *icon, *best_icon;
 
6405
        double x, y;
 
6406
        double x1, y1, x2, y2;
 
6407
        double *pos, best_pos;
 
6408
        double hadj_v, vadj_v, h_page_size;
 
6409
        gboolean better_icon;
 
6410
        gboolean compare_lt;
 
6411
 
 
6412
        hadj_v = gtk_adjustment_get_value (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (container)));
 
6413
        vadj_v = gtk_adjustment_get_value (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (container)));
 
6414
        h_page_size = gtk_adjustment_get_page_size (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (container)));
 
6415
 
 
6416
        if (nautilus_icon_container_is_layout_rtl (container)) {
 
6417
                x = hadj_v + h_page_size - ICON_PAD_LEFT - 1;
 
6418
                y = vadj_v;
 
6419
        } else {
 
6420
                x = hadj_v;
 
6421
                y = vadj_v;
 
6422
        }
 
6423
 
 
6424
        eel_canvas_c2w (EEL_CANVAS (container),
 
6425
                        x, y,
 
6426
                        &x, &y);
 
6427
 
 
6428
        l = container->details->icons;
 
6429
        best_icon = NULL;
 
6430
        best_pos = 0;
 
6431
        while (l != NULL) {
 
6432
                icon = l->data;
 
6433
 
 
6434
                if (icon_is_positioned (icon)) {
 
6435
                        eel_canvas_item_get_bounds (EEL_CANVAS_ITEM (icon->item),
 
6436
                                                    &x1, &y1, &x2, &y2);
 
6437
 
 
6438
                        compare_lt = FALSE;
 
6439
                        if (nautilus_icon_container_is_layout_vertical (container)) {
 
6440
                                pos = &x1;
 
6441
                                if (nautilus_icon_container_is_layout_rtl (container)) {
 
6442
                                        compare_lt = TRUE;
 
6443
                                        better_icon = x1 < x + ICON_PAD_LEFT;
 
6444
                                } else {
 
6445
                                        better_icon = x2 > x + ICON_PAD_LEFT;
 
6446
                                }
 
6447
                        } else {
 
6448
                                pos = &y1;
 
6449
                                better_icon = y2 > y + ICON_PAD_TOP;
 
6450
                        }
 
6451
                        if (better_icon) {
 
6452
                                if (best_icon == NULL) {
 
6453
                                        better_icon = TRUE;
 
6454
                                } else if (compare_lt) {
 
6455
                                        better_icon = best_pos < *pos;
 
6456
                                } else {
 
6457
                                        better_icon = best_pos > *pos;
 
6458
                                }
 
6459
 
 
6460
                                if (better_icon) {
 
6461
                                        best_icon = icon;
 
6462
                                        best_pos = *pos;
 
6463
                                }
 
6464
                        }
 
6465
                }
 
6466
 
 
6467
                l = l->next;
 
6468
        }
 
6469
 
 
6470
        return best_icon ? best_icon->data : NULL;
 
6471
}
 
6472
 
 
6473
/* puts the icon at the top of the screen */
 
6474
void
 
6475
nautilus_icon_container_scroll_to_icon (NautilusIconContainer  *container,
 
6476
                                        NautilusIconData       *data)
 
6477
{
 
6478
        GList *l;
 
6479
        NautilusIcon *icon;
 
6480
        GtkAdjustment *hadj, *vadj;
 
6481
        EelIRect bounds;
 
6482
        GtkAllocation allocation;
 
6483
 
 
6484
        hadj = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (container));
 
6485
        vadj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (container));
 
6486
        gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
 
6487
 
 
6488
        /* We need to force a relayout now if there are updates queued
 
6489
         * since we need the final positions */
 
6490
        nautilus_icon_container_layout_now (container);
 
6491
        
 
6492
        l = container->details->icons;
 
6493
        while (l != NULL) {
 
6494
                icon = l->data;
 
6495
                
 
6496
                if (icon->data == data &&
 
6497
                    icon_is_positioned (icon)) {
 
6498
 
 
6499
                        if (nautilus_icon_container_is_auto_layout (container)) {
 
6500
                                /* ensure that we reveal the entire row/column */
 
6501
                                icon_get_row_and_column_bounds (container, icon, &bounds, TRUE);
 
6502
                        } else {
 
6503
                                item_get_canvas_bounds (EEL_CANVAS_ITEM (icon->item), &bounds, TRUE);
 
6504
                        }
 
6505
 
 
6506
                        if (nautilus_icon_container_is_layout_vertical (container)) {
 
6507
                                if (nautilus_icon_container_is_layout_rtl (container)) {
 
6508
                                        gtk_adjustment_set_value (hadj, bounds.x1 - allocation.width);
 
6509
                                } else {
 
6510
                                        gtk_adjustment_set_value (hadj, bounds.x0);
 
6511
                                }
 
6512
                        } else {
 
6513
                                gtk_adjustment_set_value (vadj, bounds.y0);
 
6514
                        }
 
6515
                }
 
6516
                
 
6517
                l = l->next;
 
6518
        }
 
6519
}
 
6520
 
 
6521
/* Call a function for all the icons. */
 
6522
typedef struct {
 
6523
        NautilusIconCallback callback;
 
6524
        gpointer callback_data;
 
6525
} CallbackAndData;
 
6526
 
 
6527
static void
 
6528
call_icon_callback (gpointer data, gpointer callback_data)
 
6529
{
 
6530
        NautilusIcon *icon;
 
6531
        CallbackAndData *callback_and_data;
 
6532
 
 
6533
        icon = data;
 
6534
        callback_and_data = callback_data;
 
6535
        (* callback_and_data->callback) (icon->data, callback_and_data->callback_data);
 
6536
}
 
6537
 
 
6538
void
 
6539
nautilus_icon_container_for_each (NautilusIconContainer *container,
 
6540
                                  NautilusIconCallback callback,
 
6541
                                  gpointer callback_data)
 
6542
{
 
6543
        CallbackAndData callback_and_data;
 
6544
 
 
6545
        g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container));
 
6546
 
 
6547
        callback_and_data.callback = callback;
 
6548
        callback_and_data.callback_data = callback_data;
 
6549
 
 
6550
        g_list_foreach (container->details->icons,
 
6551
                        call_icon_callback, &callback_and_data);
 
6552
}
 
6553
 
 
6554
static int
 
6555
selection_changed_at_idle_callback (gpointer data)
 
6556
{
 
6557
        NautilusIconContainer *container;
 
6558
 
 
6559
        container = NAUTILUS_ICON_CONTAINER (data);
 
6560
        
 
6561
        g_signal_emit (container,
 
6562
                       signals[SELECTION_CHANGED], 0);
 
6563
 
 
6564
        container->details->selection_changed_id = 0;
 
6565
        return FALSE;
 
6566
}
 
6567
 
 
6568
/* utility routine to remove a single icon from the container */
 
6569
 
 
6570
static void
 
6571
icon_destroy (NautilusIconContainer *container,
 
6572
              NautilusIcon *icon)
 
6573
{
 
6574
        NautilusIconContainerDetails *details;
 
6575
        gboolean was_selected;
 
6576
        NautilusIcon *icon_to_focus;
 
6577
        GList *item;
 
6578
 
 
6579
        details = container->details;
 
6580
 
 
6581
        item = g_list_find (details->icons, icon);
 
6582
        item = item->next ? item->next : item->prev;
 
6583
        icon_to_focus = (item != NULL) ? item->data : NULL;
 
6584
 
 
6585
        details->icons = g_list_remove (details->icons, icon);
 
6586
        details->new_icons = g_list_remove (details->new_icons, icon);
 
6587
        g_hash_table_remove (details->icon_set, icon->data);
 
6588
 
 
6589
        was_selected = icon->is_selected;
 
6590
 
 
6591
        if (details->keyboard_focus == icon ||
 
6592
            details->keyboard_focus == NULL) {
 
6593
                if (icon_to_focus != NULL) {
 
6594
                        set_keyboard_focus (container, icon_to_focus);
 
6595
                } else {
 
6596
                        clear_keyboard_focus (container);
 
6597
                }
 
6598
        }
 
6599
        
 
6600
        if (details->keyboard_rubberband_start == icon) {
 
6601
                clear_keyboard_rubberband_start (container);
 
6602
        }
 
6603
 
 
6604
        if (details->keyboard_icon_to_reveal == icon) {
 
6605
                unschedule_keyboard_icon_reveal (container);
 
6606
        }
 
6607
        if (details->drag_icon == icon) {
 
6608
                clear_drag_state (container);
 
6609
        }
 
6610
        if (details->drop_target == icon) {
 
6611
                details->drop_target = NULL;
 
6612
        }
 
6613
        if (details->range_selection_base_icon == icon) {
 
6614
                details->range_selection_base_icon = NULL;
 
6615
        }
 
6616
        if (details->pending_icon_to_reveal == icon) {
 
6617
                set_pending_icon_to_reveal (container, NULL);
 
6618
        }
 
6619
        if (details->stretch_icon == icon) {
 
6620
                details->stretch_icon = NULL;
 
6621
        }
 
6622
 
 
6623
        if (icon->is_monitored) {
 
6624
                nautilus_icon_container_stop_monitor_top_left (container,
 
6625
                                                               icon->data,
 
6626
                                                               icon);
 
6627
        }
 
6628
        icon_free (icon);
 
6629
 
 
6630
        if (was_selected) {
 
6631
                /* Coalesce multiple removals causing multiple selection_changed events */
 
6632
                details->selection_changed_id = g_idle_add (selection_changed_at_idle_callback, container);
 
6633
        }
 
6634
}
 
6635
 
 
6636
/* activate any selected items in the container */
 
6637
static void
 
6638
activate_selected_items (NautilusIconContainer *container)
 
6639
{
 
6640
        GList *selection;
 
6641
 
 
6642
        g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container));
 
6643
 
 
6644
        selection = nautilus_icon_container_get_selection (container);
 
6645
        if (selection != NULL) {
 
6646
                g_signal_emit (container,
 
6647
                                 signals[ACTIVATE], 0,
 
6648
                                 selection);
 
6649
        }
 
6650
        g_list_free (selection);
 
6651
}
 
6652
 
 
6653
static void
 
6654
preview_selected_items (NautilusIconContainer *container)
 
6655
{
 
6656
        GList *selection;
 
6657
        GArray *locations;
 
6658
        gint idx;
 
6659
 
 
6660
        g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container));
 
6661
 
 
6662
        selection = nautilus_icon_container_get_selection (container);
 
6663
        locations = nautilus_icon_container_get_selected_icon_locations (container);
 
6664
 
 
6665
        for (idx = 0; idx < locations->len; idx++) {
 
6666
                GdkPoint *point = &(g_array_index (locations, GdkPoint, idx));
 
6667
                gint scroll_x, scroll_y;
 
6668
 
 
6669
                eel_canvas_get_scroll_offsets (EEL_CANVAS (container),
 
6670
                                               &scroll_x, &scroll_y);
 
6671
 
 
6672
                point->x -= scroll_x;
 
6673
                point->y -= scroll_y;
 
6674
        }
 
6675
 
 
6676
        if (selection != NULL) {
 
6677
                g_signal_emit (container,
 
6678
                               signals[ACTIVATE_PREVIEWER], 0,
 
6679
                               selection, locations);
 
6680
        }
 
6681
        g_list_free (selection);        
 
6682
}
 
6683
 
 
6684
static void
 
6685
activate_selected_items_alternate (NautilusIconContainer *container,
 
6686
                                   NautilusIcon *icon)
 
6687
{
 
6688
        GList *selection;
 
6689
 
 
6690
        g_assert (NAUTILUS_IS_ICON_CONTAINER (container));
 
6691
 
 
6692
        if (icon != NULL) {
 
6693
                selection = g_list_prepend (NULL, icon->data);
 
6694
        } else {
 
6695
                selection = nautilus_icon_container_get_selection (container);
 
6696
        }
 
6697
        if (selection != NULL) {
 
6698
                g_signal_emit (container,
 
6699
                                 signals[ACTIVATE_ALTERNATE], 0,
 
6700
                                 selection);
 
6701
        }
 
6702
        g_list_free (selection);
 
6703
}
 
6704
 
 
6705
static NautilusIcon *
 
6706
get_icon_being_renamed (NautilusIconContainer *container)
 
6707
{
 
6708
        NautilusIcon *rename_icon;
 
6709
 
 
6710
        if (!is_renaming (container)) {
 
6711
                return NULL;
 
6712
        }
 
6713
 
 
6714
        g_assert (!has_multiple_selection (container));
 
6715
 
 
6716
        rename_icon = get_first_selected_icon (container);
 
6717
        g_assert (rename_icon != NULL);
 
6718
 
 
6719
        return rename_icon;
 
6720
}                        
 
6721
 
 
6722
static NautilusIconInfo *
 
6723
nautilus_icon_container_get_icon_images (NautilusIconContainer *container,
 
6724
                                         NautilusIconData      *data,
 
6725
                                         int                    size,
 
6726
                                         char                 **embedded_text,
 
6727
                                         gboolean               for_drag_accept,
 
6728
                                         gboolean               need_large_embeddded_text,
 
6729
                                         gboolean              *embedded_text_needs_loading,
 
6730
                                         gboolean              *has_open_window)
 
6731
{
 
6732
        NautilusIconContainerClass *klass;
 
6733
 
 
6734
        klass = NAUTILUS_ICON_CONTAINER_GET_CLASS (container);
 
6735
        g_assert (klass->get_icon_images != NULL);
 
6736
 
 
6737
        return klass->get_icon_images (container, data, size, embedded_text, for_drag_accept, need_large_embeddded_text, embedded_text_needs_loading, has_open_window);
 
6738
}
 
6739
 
 
6740
static void
 
6741
nautilus_icon_container_freeze_updates (NautilusIconContainer *container)
 
6742
{
 
6743
        NautilusIconContainerClass *klass;
 
6744
 
 
6745
        klass = NAUTILUS_ICON_CONTAINER_GET_CLASS (container);
 
6746
        g_assert (klass->freeze_updates != NULL);
 
6747
 
 
6748
        klass->freeze_updates (container);
 
6749
}
 
6750
 
 
6751
static void
 
6752
nautilus_icon_container_unfreeze_updates (NautilusIconContainer *container)
 
6753
{
 
6754
        NautilusIconContainerClass *klass;
 
6755
 
 
6756
        klass = NAUTILUS_ICON_CONTAINER_GET_CLASS (container);
 
6757
        g_assert (klass->unfreeze_updates != NULL);
 
6758
 
 
6759
        klass->unfreeze_updates (container);
 
6760
}
 
6761
 
 
6762
static void
 
6763
nautilus_icon_container_start_monitor_top_left (NautilusIconContainer *container,
 
6764
                                                NautilusIconData *data,
 
6765
                                                gconstpointer client,
 
6766
                                                gboolean large_text)
 
6767
{
 
6768
        NautilusIconContainerClass *klass;
 
6769
 
 
6770
        klass = NAUTILUS_ICON_CONTAINER_GET_CLASS (container);
 
6771
        g_assert (klass->start_monitor_top_left != NULL);
 
6772
 
 
6773
        klass->start_monitor_top_left (container, data, client, large_text);
 
6774
}
 
6775
 
 
6776
static void
 
6777
nautilus_icon_container_stop_monitor_top_left (NautilusIconContainer *container,
 
6778
                                               NautilusIconData *data,
 
6779
                                               gconstpointer client)
 
6780
{
 
6781
        NautilusIconContainerClass *klass;
 
6782
 
 
6783
        klass = NAUTILUS_ICON_CONTAINER_GET_CLASS (container);
 
6784
        g_return_if_fail (klass->stop_monitor_top_left != NULL);
 
6785
 
 
6786
        klass->stop_monitor_top_left (container, data, client);
 
6787
}
 
6788
 
 
6789
 
 
6790
static void
 
6791
nautilus_icon_container_prioritize_thumbnailing (NautilusIconContainer *container,
 
6792
                                                 NautilusIcon *icon)
 
6793
{
 
6794
        NautilusIconContainerClass *klass;
 
6795
 
 
6796
        klass = NAUTILUS_ICON_CONTAINER_GET_CLASS (container);
 
6797
        g_assert (klass->prioritize_thumbnailing != NULL);
 
6798
 
 
6799
        klass->prioritize_thumbnailing (container, icon->data);
 
6800
}
 
6801
 
 
6802
static void
 
6803
nautilus_icon_container_update_visible_icons (NautilusIconContainer *container)
 
6804
{
 
6805
        GtkAdjustment *vadj, *hadj;
 
6806
        double min_y, max_y;
 
6807
        double min_x, max_x;
 
6808
        double x0, y0, x1, y1;
 
6809
        GList *node;
 
6810
        NautilusIcon *icon;
 
6811
        gboolean visible;
 
6812
        GtkAllocation allocation;
 
6813
 
 
6814
        hadj = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (container));
 
6815
        vadj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (container));
 
6816
        gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
 
6817
 
 
6818
        min_x = gtk_adjustment_get_value (hadj);
 
6819
        max_x = min_x + allocation.width;
 
6820
        
 
6821
        min_y = gtk_adjustment_get_value (vadj);
 
6822
        max_y = min_y + allocation.height;
 
6823
 
 
6824
        eel_canvas_c2w (EEL_CANVAS (container),
 
6825
                        min_x, min_y, &min_x, &min_y);
 
6826
        eel_canvas_c2w (EEL_CANVAS (container),
 
6827
                        max_x, max_y, &max_x, &max_y);
 
6828
        
 
6829
        /* Do the iteration in reverse to get the render-order from top to
 
6830
         * bottom for the prioritized thumbnails.
 
6831
         */
 
6832
        for (node = g_list_last (container->details->icons); node != NULL; node = node->prev) {
 
6833
                icon = node->data;
 
6834
 
 
6835
                if (icon_is_positioned (icon)) {
 
6836
                        eel_canvas_item_get_bounds (EEL_CANVAS_ITEM (icon->item),
 
6837
                                                    &x0,
 
6838
                                                    &y0,
 
6839
                                                    &x1,
 
6840
                                                    &y1);
 
6841
                        eel_canvas_item_i2w (EEL_CANVAS_ITEM (icon->item)->parent,
 
6842
                                             &x0,
 
6843
                                             &y0);
 
6844
                        eel_canvas_item_i2w (EEL_CANVAS_ITEM (icon->item)->parent,
 
6845
                                             &x1,
 
6846
                                             &y1);
 
6847
 
 
6848
                        if (nautilus_icon_container_is_layout_vertical (container)) {
 
6849
                                visible = x1 >= min_x && x0 <= max_x;
 
6850
                        } else {
 
6851
                                visible = y1 >= min_y && y0 <= max_y;
 
6852
                        }
 
6853
 
 
6854
                        if (visible) {
 
6855
                                nautilus_icon_canvas_item_set_is_visible (icon->item, TRUE);
 
6856
                                nautilus_icon_container_prioritize_thumbnailing (container,
 
6857
                                                                                 icon);
 
6858
                        } else {
 
6859
                                nautilus_icon_canvas_item_set_is_visible (icon->item, FALSE);
 
6860
                        }
 
6861
                }
 
6862
        }
 
6863
}
 
6864
 
 
6865
static void
 
6866
handle_vadjustment_changed (GtkAdjustment *adjustment,
 
6867
                            NautilusIconContainer *container)
 
6868
{
 
6869
        if (!nautilus_icon_container_is_layout_vertical (container)) {
 
6870
                nautilus_icon_container_update_visible_icons (container);
 
6871
        }
 
6872
}
 
6873
 
 
6874
static void
 
6875
handle_hadjustment_changed (GtkAdjustment *adjustment,
 
6876
                            NautilusIconContainer *container)
 
6877
{
 
6878
        if (nautilus_icon_container_is_layout_vertical (container)) {
 
6879
                nautilus_icon_container_update_visible_icons (container);
 
6880
        }
 
6881
}
 
6882
 
 
6883
 
 
6884
void 
 
6885
nautilus_icon_container_update_icon (NautilusIconContainer *container,
 
6886
                                     NautilusIcon *icon)
 
6887
{
 
6888
        NautilusIconContainerDetails *details;
 
6889
        guint icon_size;
 
6890
        guint min_image_size, max_image_size;
 
6891
        NautilusIconInfo *icon_info;
 
6892
        GdkPoint *attach_points;
 
6893
        int n_attach_points;
 
6894
        gboolean has_embedded_text_rect;
 
6895
        GdkPixbuf *pixbuf;
 
6896
        char *editable_text, *additional_text;
 
6897
        char *embedded_text;
 
6898
        GdkRectangle embedded_text_rect;
 
6899
        gboolean large_embedded_text;
 
6900
        gboolean embedded_text_needs_loading;
 
6901
        gboolean has_open_window;
 
6902
        
 
6903
        if (icon == NULL) {
 
6904
                return;
 
6905
        }
 
6906
 
 
6907
        details = container->details;
 
6908
 
 
6909
        /* compute the maximum size based on the scale factor */
 
6910
        min_image_size = MINIMUM_IMAGE_SIZE * EEL_CANVAS (container)->pixels_per_unit;
 
6911
        max_image_size = MAX (MAXIMUM_IMAGE_SIZE * EEL_CANVAS (container)->pixels_per_unit, NAUTILUS_ICON_MAXIMUM_SIZE);
 
6912
 
 
6913
        /* Get the appropriate images for the file. */
 
6914
        if (container->details->forced_icon_size > 0) {
 
6915
                icon_size = container->details->forced_icon_size;
 
6916
        } else {
 
6917
                icon_get_size (container, icon, &icon_size);
 
6918
        }
 
6919
 
 
6920
 
 
6921
        icon_size = MAX (icon_size, min_image_size);
 
6922
        icon_size = MIN (icon_size, max_image_size);
 
6923
 
 
6924
        DEBUG ("Icon size, getting for size %d", icon_size);
 
6925
 
 
6926
        /* Get the icons. */
 
6927
        embedded_text = NULL;
 
6928
        large_embedded_text = icon_size > ICON_SIZE_FOR_LARGE_EMBEDDED_TEXT;
 
6929
        icon_info = nautilus_icon_container_get_icon_images (container, icon->data, icon_size,
 
6930
                                                             &embedded_text,
 
6931
                                                             icon == details->drop_target,                                                           
 
6932
                                                             large_embedded_text, &embedded_text_needs_loading,
 
6933
                                                             &has_open_window);
 
6934
 
 
6935
        if (container->details->forced_icon_size > 0) {
 
6936
                pixbuf = nautilus_icon_info_get_pixbuf_at_size (icon_info, icon_size);
 
6937
        } else {
 
6938
                pixbuf = nautilus_icon_info_get_pixbuf (icon_info);
 
6939
        }
 
6940
 
 
6941
        nautilus_icon_info_get_attach_points (icon_info, &attach_points, &n_attach_points);
 
6942
        has_embedded_text_rect = nautilus_icon_info_get_embedded_rect (icon_info,
 
6943
                                                                       &embedded_text_rect);
 
6944
 
 
6945
        g_object_unref (icon_info);
 
6946
 
 
6947
        if (has_embedded_text_rect && embedded_text_needs_loading) {
 
6948
                icon->is_monitored = TRUE;
 
6949
                nautilus_icon_container_start_monitor_top_left (container, icon->data, icon, large_embedded_text);
 
6950
        }
 
6951
        
 
6952
        nautilus_icon_container_get_icon_text (container,
 
6953
                                               icon->data,
 
6954
                                               &editable_text,
 
6955
                                               &additional_text,
 
6956
                                               FALSE);
 
6957
 
 
6958
        /* If name of icon being renamed was changed from elsewhere, end renaming mode. 
 
6959
         * Alternatively, we could replace the characters in the editable text widget
 
6960
         * with the new name, but that could cause timing problems if the user just
 
6961
         * happened to be typing at that moment.
 
6962
         */
 
6963
        if (icon == get_icon_being_renamed (container) &&
 
6964
            g_strcmp0 (editable_text,
 
6965
                       nautilus_icon_canvas_item_get_editable_text (icon->item)) != 0) {
 
6966
                end_renaming_mode (container, FALSE);
 
6967
        }
 
6968
 
 
6969
        eel_canvas_item_set (EEL_CANVAS_ITEM (icon->item),
 
6970
                             "editable_text", editable_text,
 
6971
                             "additional_text", additional_text,
 
6972
                             "highlighted_for_drop", icon == details->drop_target,
 
6973
                             NULL);
 
6974
 
 
6975
        nautilus_icon_canvas_item_set_image (icon->item, pixbuf);
 
6976
        nautilus_icon_canvas_item_set_attach_points (icon->item, attach_points, n_attach_points);
 
6977
        nautilus_icon_canvas_item_set_embedded_text_rect (icon->item, &embedded_text_rect);
 
6978
        nautilus_icon_canvas_item_set_embedded_text (icon->item, embedded_text);
 
6979
 
 
6980
        /* Let the pixbufs go. */
 
6981
        g_object_unref (pixbuf);
 
6982
 
 
6983
        g_free (editable_text);
 
6984
        g_free (additional_text);
 
6985
}
 
6986
 
 
6987
static gboolean
 
6988
assign_icon_position (NautilusIconContainer *container,
 
6989
                      NautilusIcon *icon)
 
6990
{
 
6991
        gboolean have_stored_position;
 
6992
        NautilusIconPosition position;
 
6993
 
 
6994
        /* Get the stored position. */
 
6995
        have_stored_position = FALSE;
 
6996
        position.scale = 1.0;
 
6997
        g_signal_emit (container,
 
6998
                         signals[GET_STORED_ICON_POSITION], 0,
 
6999
                         icon->data,
 
7000
                         &position,
 
7001
                         &have_stored_position);
 
7002
        icon->scale = position.scale;
 
7003
        if (!container->details->auto_layout) {
 
7004
                if (have_stored_position) {
 
7005
                        icon_set_position (icon, position.x, position.y);
 
7006
                        icon->saved_ltr_x = icon->x;
 
7007
                } else {
 
7008
                        return FALSE;
 
7009
                }
 
7010
        }
 
7011
        return TRUE;
 
7012
}
 
7013
 
 
7014
static void
 
7015
finish_adding_icon (NautilusIconContainer *container,
 
7016
                    NautilusIcon *icon)
 
7017
{
 
7018
        nautilus_icon_container_update_icon (container, icon);
 
7019
        eel_canvas_item_show (EEL_CANVAS_ITEM (icon->item));
 
7020
 
 
7021
        g_signal_connect_object (icon->item, "event",
 
7022
                                 G_CALLBACK (item_event_callback), container, 0);
 
7023
 
 
7024
        g_signal_emit (container, signals[ICON_ADDED], 0, icon->data);
 
7025
}
 
7026
 
 
7027
static void
 
7028
finish_adding_new_icons (NautilusIconContainer *container)
 
7029
{
 
7030
        GList *p, *new_icons, *no_position_icons, *semi_position_icons;
 
7031
        NautilusIcon *icon;
 
7032
        double bottom;
 
7033
 
 
7034
        new_icons = container->details->new_icons;
 
7035
        container->details->new_icons = NULL;
 
7036
 
 
7037
        /* Position most icons (not unpositioned manual-layout icons). */
 
7038
        new_icons = g_list_reverse (new_icons);
 
7039
        no_position_icons = semi_position_icons = NULL;
 
7040
        for (p = new_icons; p != NULL; p = p->next) {
 
7041
                icon = p->data;
 
7042
                if (icon->has_lazy_position) {
 
7043
                        assign_icon_position (container, icon);
 
7044
                        semi_position_icons = g_list_prepend (semi_position_icons, icon);
 
7045
                } else if (!assign_icon_position (container, icon)) {
 
7046
                        no_position_icons = g_list_prepend (no_position_icons, icon);
 
7047
                }
 
7048
 
 
7049
                finish_adding_icon (container, icon);
 
7050
        }
 
7051
        g_list_free (new_icons);
 
7052
 
 
7053
        if (semi_position_icons != NULL) {
 
7054
                PlacementGrid *grid;
 
7055
                time_t now;
 
7056
                gboolean dummy;
 
7057
 
 
7058
                g_assert (!container->details->auto_layout);
 
7059
 
 
7060
                semi_position_icons = g_list_reverse (semi_position_icons);
 
7061
 
 
7062
                /* This is currently only used on the desktop.
 
7063
                 * Thus, we pass FALSE for tight, like lay_down_icons_tblr */
 
7064
                grid = placement_grid_new (container, FALSE);
 
7065
 
 
7066
                for (p = container->details->icons; p != NULL; p = p->next) {
 
7067
                        icon = p->data;
 
7068
 
 
7069
                        if (icon_is_positioned (icon) && !icon->has_lazy_position) {
 
7070
                                placement_grid_mark_icon (grid, icon);
 
7071
                        }
 
7072
                }
 
7073
 
 
7074
                now = time (NULL);
 
7075
 
 
7076
                for (p = semi_position_icons; p != NULL; p = p->next) {
 
7077
                        NautilusIcon *icon;
 
7078
                        NautilusIconPosition position;
 
7079
                        int x, y;
 
7080
 
 
7081
                        icon = p->data;
 
7082
                        x = icon->x;
 
7083
                        y = icon->y;
 
7084
 
 
7085
                        find_empty_location (container, grid, 
 
7086
                                             icon, x, y, &x, &y);
 
7087
 
 
7088
                        icon_set_position (icon, x, y);
 
7089
 
 
7090
                        position.x = icon->x;
 
7091
                        position.y = icon->y;
 
7092
                        position.scale = icon->scale;
 
7093
                        placement_grid_mark_icon (grid, icon);
 
7094
                        g_signal_emit (container, signals[ICON_POSITION_CHANGED], 0,
 
7095
                                       icon->data, &position);
 
7096
                        g_signal_emit (container, signals[STORE_LAYOUT_TIMESTAMP], 0,
 
7097
                                       icon->data, &now, &dummy);
 
7098
 
 
7099
                        /* ensure that next time we run this code, the formerly semi-positioned
 
7100
                         * icons are treated as being positioned. */
 
7101
                        icon->has_lazy_position = FALSE;
 
7102
                }
 
7103
 
 
7104
                placement_grid_free (grid);
 
7105
 
 
7106
                g_list_free (semi_position_icons);
 
7107
        }
 
7108
 
 
7109
        /* Position the unpositioned manual layout icons. */
 
7110
        if (no_position_icons != NULL) {
 
7111
                g_assert (!container->details->auto_layout);
 
7112
                
 
7113
                sort_icons (container, &no_position_icons);
 
7114
                if (nautilus_icon_container_get_is_desktop (container)) {
 
7115
                        lay_down_icons (container, no_position_icons, CONTAINER_PAD_TOP);
 
7116
                } else {
 
7117
                        get_all_icon_bounds (container, NULL, NULL, NULL, &bottom, BOUNDS_USAGE_FOR_LAYOUT);
 
7118
                        lay_down_icons (container, no_position_icons, bottom + ICON_PAD_BOTTOM);
 
7119
                }
 
7120
                g_list_free (no_position_icons);
 
7121
        }
 
7122
 
 
7123
        if (container->details->store_layout_timestamps_when_finishing_new_icons) {
 
7124
                store_layout_timestamps_now (container);
 
7125
                container->details->store_layout_timestamps_when_finishing_new_icons = FALSE;
 
7126
        }
 
7127
}
 
7128
 
 
7129
static gboolean
 
7130
is_old_or_unknown_icon_data (NautilusIconContainer *container,
 
7131
                             NautilusIconData *data)
 
7132
{
 
7133
        time_t timestamp;
 
7134
        gboolean success;
 
7135
 
 
7136
        if (container->details->layout_timestamp == UNDEFINED_TIME) {
 
7137
                /* don't know */
 
7138
                return FALSE;
 
7139
        }
 
7140
 
 
7141
        g_signal_emit (container,
 
7142
                       signals[GET_STORED_LAYOUT_TIMESTAMP], 0,
 
7143
                       data, &timestamp, &success);
 
7144
        return (!success || timestamp < container->details->layout_timestamp);
 
7145
}
 
7146
 
 
7147
/**
 
7148
 * nautilus_icon_container_add:
 
7149
 * @container: A NautilusIconContainer
 
7150
 * @data: Icon data.
 
7151
 * 
 
7152
 * Add icon to represent @data to container.
 
7153
 * Returns FALSE if there was already such an icon.
 
7154
 **/
 
7155
gboolean
 
7156
nautilus_icon_container_add (NautilusIconContainer *container,
 
7157
                             NautilusIconData *data)
 
7158
{
 
7159
        NautilusIconContainerDetails *details;
 
7160
        NautilusIcon *icon;
 
7161
        EelCanvasItem *band, *item;
 
7162
        
 
7163
        g_return_val_if_fail (NAUTILUS_IS_ICON_CONTAINER (container), FALSE);
 
7164
        g_return_val_if_fail (data != NULL, FALSE);
 
7165
 
 
7166
        details = container->details;
 
7167
 
 
7168
        if (g_hash_table_lookup (details->icon_set, data) != NULL) {
 
7169
                return FALSE;
 
7170
        }
 
7171
 
 
7172
        /* Create the new icon, including the canvas item. */
 
7173
        icon = g_new0 (NautilusIcon, 1);
 
7174
        icon->data = data;
 
7175
        icon->x = ICON_UNPOSITIONED_VALUE;
 
7176
        icon->y = ICON_UNPOSITIONED_VALUE;
 
7177
 
 
7178
        /* Whether the saved icon position should only be used
 
7179
         * if the previous icon position is free. If the position
 
7180
         * is occupied, another position near the last one will
 
7181
         */
 
7182
        icon->has_lazy_position = is_old_or_unknown_icon_data (container, data);
 
7183
        icon->scale = 1.0;
 
7184
        icon->item = NAUTILUS_ICON_CANVAS_ITEM
 
7185
                (eel_canvas_item_new (EEL_CANVAS_GROUP (EEL_CANVAS (container)->root),
 
7186
                                      nautilus_icon_canvas_item_get_type (),
 
7187
                                      "visible", FALSE,
 
7188
                                      NULL));
 
7189
        icon->item->user_data = icon;
 
7190
 
 
7191
        /* Make sure the icon is under the selection_rectangle */
 
7192
        item = EEL_CANVAS_ITEM (icon->item);
 
7193
        band = NAUTILUS_ICON_CONTAINER (item->canvas)->details->rubberband_info.selection_rectangle;
 
7194
        if (band) {
 
7195
                eel_canvas_item_send_behind (item, band);
 
7196
        }
 
7197
        
 
7198
        /* Put it on both lists. */
 
7199
        details->icons = g_list_prepend (details->icons, icon);
 
7200
        details->new_icons = g_list_prepend (details->new_icons, icon);
 
7201
 
 
7202
        g_hash_table_insert (details->icon_set, data, icon);
 
7203
 
 
7204
        details->needs_resort = TRUE;
 
7205
 
 
7206
        /* Run an idle function to add the icons. */
 
7207
        schedule_redo_layout (container);
 
7208
        
 
7209
        return TRUE;
 
7210
}
 
7211
 
 
7212
void
 
7213
nautilus_icon_container_layout_now (NautilusIconContainer *container)
 
7214
{
 
7215
        if (container->details->idle_id != 0) {
 
7216
                unschedule_redo_layout (container);
 
7217
                redo_layout_internal (container);
 
7218
        }
 
7219
 
 
7220
        /* Also need to make sure we're properly resized, for instance
 
7221
         * newly added files may trigger a change in the size allocation and
 
7222
         * thus toggle scrollbars on */
 
7223
        gtk_container_check_resize (GTK_CONTAINER (gtk_widget_get_parent (GTK_WIDGET (container))));
 
7224
}
 
7225
 
 
7226
/**
 
7227
 * nautilus_icon_container_remove:
 
7228
 * @container: A NautilusIconContainer.
 
7229
 * @data: Icon data.
 
7230
 * 
 
7231
 * Remove the icon with this data.
 
7232
 **/
 
7233
gboolean
 
7234
nautilus_icon_container_remove (NautilusIconContainer *container,
 
7235
                                NautilusIconData *data)
 
7236
{
 
7237
        NautilusIcon *icon;
 
7238
 
 
7239
        g_return_val_if_fail (NAUTILUS_IS_ICON_CONTAINER (container), FALSE);
 
7240
        g_return_val_if_fail (data != NULL, FALSE);
 
7241
 
 
7242
        end_renaming_mode (container, FALSE);
 
7243
                
 
7244
        icon = g_hash_table_lookup (container->details->icon_set, data);
 
7245
 
 
7246
        if (icon == NULL) {
 
7247
                return FALSE;
 
7248
        }
 
7249
 
 
7250
        icon_destroy (container, icon);
 
7251
        schedule_redo_layout (container);
 
7252
 
 
7253
        g_signal_emit (container, signals[ICON_REMOVED], 0, icon);
 
7254
 
 
7255
        return TRUE;
 
7256
}
 
7257
 
 
7258
/**
 
7259
 * nautilus_icon_container_request_update:
 
7260
 * @container: A NautilusIconContainer.
 
7261
 * @data: Icon data.
 
7262
 * 
 
7263
 * Update the icon with this data.
 
7264
 **/
 
7265
void
 
7266
nautilus_icon_container_request_update (NautilusIconContainer *container,
 
7267
                                        NautilusIconData *data)
 
7268
{
 
7269
        NautilusIcon *icon;
 
7270
 
 
7271
        g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container));
 
7272
        g_return_if_fail (data != NULL);
 
7273
 
 
7274
        icon = g_hash_table_lookup (container->details->icon_set, data);
 
7275
 
 
7276
        if (icon != NULL) {
 
7277
                nautilus_icon_container_update_icon (container, icon);
 
7278
                container->details->needs_resort = TRUE;
 
7279
                schedule_redo_layout (container);
 
7280
        }
 
7281
}
 
7282
 
 
7283
/* zooming */
 
7284
 
 
7285
NautilusZoomLevel
 
7286
nautilus_icon_container_get_zoom_level (NautilusIconContainer *container)
 
7287
{
 
7288
        return container->details->zoom_level;
 
7289
}
 
7290
 
 
7291
void
 
7292
nautilus_icon_container_set_zoom_level (NautilusIconContainer *container, int new_level)
 
7293
{
 
7294
        NautilusIconContainerDetails *details;
 
7295
        int pinned_level;
 
7296
        double pixels_per_unit;
 
7297
        
 
7298
        details = container->details;
 
7299
 
 
7300
        end_renaming_mode (container, TRUE);
 
7301
                
 
7302
        pinned_level = new_level;
 
7303
        if (pinned_level < NAUTILUS_ZOOM_LEVEL_SMALLEST) {
 
7304
                pinned_level = NAUTILUS_ZOOM_LEVEL_SMALLEST;
 
7305
        } else if (pinned_level > NAUTILUS_ZOOM_LEVEL_LARGEST) {
 
7306
                pinned_level = NAUTILUS_ZOOM_LEVEL_LARGEST;
 
7307
        }
 
7308
 
 
7309
        if (pinned_level == details->zoom_level) {
 
7310
                return;
 
7311
        }
 
7312
        
 
7313
        details->zoom_level = pinned_level;
 
7314
        
 
7315
        pixels_per_unit = (double) nautilus_get_icon_size_for_zoom_level (pinned_level)
 
7316
                / NAUTILUS_ICON_SIZE_STANDARD;
 
7317
        eel_canvas_set_pixels_per_unit (EEL_CANVAS (container), pixels_per_unit);
 
7318
 
 
7319
        invalidate_labels (container);
 
7320
        nautilus_icon_container_request_update_all (container);
 
7321
}
 
7322
 
 
7323
/**
 
7324
 * nautilus_icon_container_request_update_all:
 
7325
 * For each icon, synchronizes the displayed information (image, text) with the
 
7326
 * information from the model.
 
7327
 * 
 
7328
 * @container: An icon container.
 
7329
 **/
 
7330
void
 
7331
nautilus_icon_container_request_update_all (NautilusIconContainer *container)
 
7332
{
 
7333
        GList *node;
 
7334
        NautilusIcon *icon;
 
7335
 
 
7336
        g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container));
 
7337
 
 
7338
        for (node = container->details->icons; node != NULL; node = node->next) {
 
7339
                icon = node->data;
 
7340
                nautilus_icon_container_update_icon (container, icon);
 
7341
        }
 
7342
 
 
7343
        container->details->needs_resort = TRUE;
 
7344
        redo_layout (container);
 
7345
}
 
7346
 
 
7347
/**
 
7348
 * nautilus_icon_container_reveal:
 
7349
 * Change scroll position as necessary to reveal the specified item.
 
7350
 */
 
7351
void
 
7352
nautilus_icon_container_reveal (NautilusIconContainer *container, NautilusIconData *data)
 
7353
{
 
7354
        NautilusIcon *icon;
 
7355
 
 
7356
        g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container));
 
7357
        g_return_if_fail (data != NULL);
 
7358
 
 
7359
        icon = g_hash_table_lookup (container->details->icon_set, data);
 
7360
 
 
7361
        if (icon != NULL) {
 
7362
                reveal_icon (container, icon);
 
7363
        }
 
7364
}
 
7365
 
 
7366
/**
 
7367
 * nautilus_icon_container_get_selection:
 
7368
 * @container: An icon container.
 
7369
 * 
 
7370
 * Get a list of the icons currently selected in @container.
 
7371
 * 
 
7372
 * Return value: A GList of the programmer-specified data associated to each
 
7373
 * selected icon, or NULL if no icon is selected.  The caller is expected to
 
7374
 * free the list when it is not needed anymore.
 
7375
 **/
 
7376
GList *
 
7377
nautilus_icon_container_get_selection (NautilusIconContainer *container)
 
7378
{
 
7379
        GList *list, *p;
 
7380
 
 
7381
        g_return_val_if_fail (NAUTILUS_IS_ICON_CONTAINER (container), NULL);
 
7382
 
 
7383
        list = NULL;
 
7384
        for (p = container->details->icons; p != NULL; p = p->next) {
 
7385
                NautilusIcon *icon;
 
7386
 
 
7387
                icon = p->data;
 
7388
                if (icon->is_selected) {
 
7389
                        list = g_list_prepend (list, icon->data);
 
7390
                }
 
7391
        }
 
7392
 
 
7393
        return g_list_reverse (list);
 
7394
}
 
7395
 
 
7396
static GList *
 
7397
nautilus_icon_container_get_selected_icons (NautilusIconContainer *container)
 
7398
{
 
7399
        GList *list, *p;
 
7400
 
 
7401
        g_return_val_if_fail (NAUTILUS_IS_ICON_CONTAINER (container), NULL);
 
7402
 
 
7403
        list = NULL;
 
7404
        for (p = container->details->icons; p != NULL; p = p->next) {
 
7405
                NautilusIcon *icon;
 
7406
 
 
7407
                icon = p->data;
 
7408
                if (icon->is_selected) {
 
7409
                        list = g_list_prepend (list, icon);
 
7410
                }
 
7411
        }
 
7412
 
 
7413
        return g_list_reverse (list);
 
7414
}
 
7415
 
 
7416
/**
 
7417
 * nautilus_icon_container_invert_selection:
 
7418
 * @container: An icon container.
 
7419
 * 
 
7420
 * Inverts the selection in @container.
 
7421
 * 
 
7422
 **/
 
7423
void
 
7424
nautilus_icon_container_invert_selection (NautilusIconContainer *container)
 
7425
{
 
7426
        GList *p;
 
7427
 
 
7428
        g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container));
 
7429
 
 
7430
        for (p = container->details->icons; p != NULL; p = p->next) {
 
7431
                NautilusIcon *icon;
 
7432
 
 
7433
                icon = p->data;
 
7434
                icon_toggle_selected (container, icon);
 
7435
        }
 
7436
 
 
7437
        g_signal_emit (container, signals[SELECTION_CHANGED], 0);
 
7438
}
 
7439
 
 
7440
 
 
7441
/* Returns an array of GdkPoints of locations of the icons. */
 
7442
static GArray *
 
7443
nautilus_icon_container_get_icon_locations (NautilusIconContainer *container,
 
7444
                                            GList *icons)
 
7445
{
 
7446
        GArray *result;
 
7447
        GList *node;
 
7448
        int index;
 
7449
                
 
7450
        result = g_array_new (FALSE, TRUE, sizeof (GdkPoint));
 
7451
        result = g_array_set_size (result, g_list_length (icons));
 
7452
                
 
7453
        for (index = 0, node = icons; node != NULL; index++, node = node->next) {
 
7454
                g_array_index (result, GdkPoint, index).x =
 
7455
                        ((NautilusIcon *)node->data)->x;
 
7456
                g_array_index (result, GdkPoint, index).y =
 
7457
                        ((NautilusIcon *)node->data)->y;
 
7458
        }
 
7459
 
 
7460
        return result;
 
7461
}
 
7462
 
 
7463
/**
 
7464
 * nautilus_icon_container_get_selected_icon_locations:
 
7465
 * @container: An icon container widget.
 
7466
 * 
 
7467
 * Returns an array of GdkPoints of locations of the selected icons.
 
7468
 **/
 
7469
GArray *
 
7470
nautilus_icon_container_get_selected_icon_locations (NautilusIconContainer *container)
 
7471
{
 
7472
        GArray *result;
 
7473
        GList *icons;
 
7474
 
 
7475
        g_return_val_if_fail (NAUTILUS_IS_ICON_CONTAINER (container), NULL);
 
7476
 
 
7477
        icons = nautilus_icon_container_get_selected_icons (container);
 
7478
        result = nautilus_icon_container_get_icon_locations (container, icons);
 
7479
        g_list_free (icons);
 
7480
        
 
7481
        return result;
 
7482
}
 
7483
 
 
7484
/**
 
7485
 * nautilus_icon_container_select_all:
 
7486
 * @container: An icon container widget.
 
7487
 * 
 
7488
 * Select all the icons in @container at once.
 
7489
 **/
 
7490
void
 
7491
nautilus_icon_container_select_all (NautilusIconContainer *container)
 
7492
{
 
7493
        gboolean selection_changed;
 
7494
        GList *p;
 
7495
        NautilusIcon *icon;
 
7496
 
 
7497
        g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container));
 
7498
 
 
7499
        selection_changed = FALSE;
 
7500
 
 
7501
        for (p = container->details->icons; p != NULL; p = p->next) {
 
7502
                icon = p->data;
 
7503
                
 
7504
                selection_changed |= icon_set_selected (container, icon, TRUE);
 
7505
        }
 
7506
 
 
7507
        if (selection_changed) {
 
7508
                g_signal_emit (container,
 
7509
                                 signals[SELECTION_CHANGED], 0);
 
7510
        }
 
7511
}
 
7512
 
 
7513
/**
 
7514
 * nautilus_icon_container_set_selection:
 
7515
 * @container: An icon container widget.
 
7516
 * @selection: A list of NautilusIconData *.
 
7517
 * 
 
7518
 * Set the selection to exactly the icons in @container which have
 
7519
 * programmer data matching one of the items in @selection.
 
7520
 **/
 
7521
void
 
7522
nautilus_icon_container_set_selection (NautilusIconContainer *container,
 
7523
                                       GList *selection)
 
7524
{
 
7525
        gboolean selection_changed;
 
7526
        GHashTable *hash;
 
7527
        GList *p;
 
7528
        NautilusIcon *icon;
 
7529
 
 
7530
        g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container));
 
7531
 
 
7532
        selection_changed = FALSE;
 
7533
 
 
7534
        hash = g_hash_table_new (NULL, NULL);
 
7535
        for (p = selection; p != NULL; p = p->next) {
 
7536
                g_hash_table_insert (hash, p->data, p->data);
 
7537
        }
 
7538
        for (p = container->details->icons; p != NULL; p = p->next) {
 
7539
                icon = p->data;
 
7540
                
 
7541
                selection_changed |= icon_set_selected
 
7542
                        (container, icon,
 
7543
                         g_hash_table_lookup (hash, icon->data) != NULL);
 
7544
        }
 
7545
        g_hash_table_destroy (hash);
 
7546
 
 
7547
        if (selection_changed) {
 
7548
                g_signal_emit (container,
 
7549
                                 signals[SELECTION_CHANGED], 0);
 
7550
        }
 
7551
}
 
7552
 
 
7553
/**
 
7554
 * nautilus_icon_container_select_list_unselect_others.
 
7555
 * @container: An icon container widget.
 
7556
 * @selection: A list of NautilusIcon *.
 
7557
 * 
 
7558
 * Set the selection to exactly the icons in @selection.
 
7559
 **/
 
7560
void
 
7561
nautilus_icon_container_select_list_unselect_others (NautilusIconContainer *container,
 
7562
                                                     GList *selection)
 
7563
{
 
7564
        gboolean selection_changed;
 
7565
        GHashTable *hash;
 
7566
        GList *p;
 
7567
        NautilusIcon *icon;
 
7568
 
 
7569
        g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container));
 
7570
 
 
7571
        selection_changed = FALSE;
 
7572
 
 
7573
        hash = g_hash_table_new (NULL, NULL);
 
7574
        for (p = selection; p != NULL; p = p->next) {
 
7575
                g_hash_table_insert (hash, p->data, p->data);
 
7576
        }
 
7577
        for (p = container->details->icons; p != NULL; p = p->next) {
 
7578
                icon = p->data;
 
7579
                
 
7580
                selection_changed |= icon_set_selected
 
7581
                        (container, icon,
 
7582
                         g_hash_table_lookup (hash, icon) != NULL);
 
7583
        }
 
7584
        g_hash_table_destroy (hash);
 
7585
 
 
7586
        if (selection_changed) {
 
7587
                g_signal_emit (container,
 
7588
                                 signals[SELECTION_CHANGED], 0);
 
7589
        }
 
7590
}
 
7591
 
 
7592
/**
 
7593
 * nautilus_icon_container_unselect_all:
 
7594
 * @container: An icon container widget.
 
7595
 * 
 
7596
 * Deselect all the icons in @container.
 
7597
 **/
 
7598
void
 
7599
nautilus_icon_container_unselect_all (NautilusIconContainer *container)
 
7600
{
 
7601
        if (unselect_all (container)) {
 
7602
                g_signal_emit (container,
 
7603
                                 signals[SELECTION_CHANGED], 0);
 
7604
        }
 
7605
}
 
7606
 
 
7607
/**
 
7608
 * nautilus_icon_container_get_icon_by_uri:
 
7609
 * @container: An icon container widget.
 
7610
 * @uri: The uri of an icon to find.
 
7611
 * 
 
7612
 * Locate an icon, given the URI. The URI must match exactly.
 
7613
 * Later we may have to have some way of figuring out if the
 
7614
 * URI specifies the same object that does not require an exact match.
 
7615
 **/
 
7616
NautilusIcon *
 
7617
nautilus_icon_container_get_icon_by_uri (NautilusIconContainer *container,
 
7618
                                         const char *uri)
 
7619
{
 
7620
        NautilusIconContainerDetails *details;
 
7621
        GList *p;
 
7622
 
 
7623
        /* Eventually, we must avoid searching the entire icon list,
 
7624
           but it's OK for now.
 
7625
           A hash table mapping uri to icon is one possibility.
 
7626
        */
 
7627
 
 
7628
        details = container->details;
 
7629
 
 
7630
        for (p = details->icons; p != NULL; p = p->next) {
 
7631
                NautilusIcon *icon;
 
7632
                char *icon_uri;
 
7633
                gboolean is_match;
 
7634
 
 
7635
                icon = p->data;
 
7636
 
 
7637
                icon_uri = nautilus_icon_container_get_icon_uri
 
7638
                        (container, icon);
 
7639
                is_match = strcmp (uri, icon_uri) == 0;
 
7640
                g_free (icon_uri);
 
7641
 
 
7642
                if (is_match) {
 
7643
                        return icon;
 
7644
                }
 
7645
        }
 
7646
 
 
7647
        return NULL;
 
7648
}
 
7649
 
 
7650
static NautilusIcon *
 
7651
get_nth_selected_icon (NautilusIconContainer *container, int index)
 
7652
{
 
7653
        GList *p;
 
7654
        NautilusIcon *icon;
 
7655
        int selection_count;
 
7656
 
 
7657
        g_assert (index > 0);
 
7658
 
 
7659
        /* Find the nth selected icon. */
 
7660
        selection_count = 0;
 
7661
        for (p = container->details->icons; p != NULL; p = p->next) {
 
7662
                icon = p->data;
 
7663
                if (icon->is_selected) {
 
7664
                        if (++selection_count == index) {
 
7665
                                return icon;
 
7666
                        }
 
7667
                }
 
7668
        }
 
7669
        return NULL;
 
7670
}
 
7671
 
 
7672
static NautilusIcon *
 
7673
get_first_selected_icon (NautilusIconContainer *container)
 
7674
{
 
7675
        return get_nth_selected_icon (container, 1);
 
7676
}
 
7677
 
 
7678
static gboolean
 
7679
has_multiple_selection (NautilusIconContainer *container)
 
7680
{
 
7681
        return get_nth_selected_icon (container, 2) != NULL;
 
7682
}
 
7683
 
 
7684
static gboolean
 
7685
all_selected (NautilusIconContainer *container)
 
7686
{
 
7687
        GList *p;
 
7688
        NautilusIcon *icon;
 
7689
 
 
7690
        for (p = container->details->icons; p != NULL; p = p->next) {
 
7691
                icon = p->data;
 
7692
                if (!icon->is_selected) {
 
7693
                        return FALSE;
 
7694
                }
 
7695
        }
 
7696
        return TRUE;
 
7697
}
 
7698
 
 
7699
static gboolean
 
7700
has_selection (NautilusIconContainer *container)
 
7701
{
 
7702
        return get_nth_selected_icon (container, 1) != NULL;
 
7703
}
 
7704
 
 
7705
/**
 
7706
 * nautilus_icon_container_show_stretch_handles:
 
7707
 * @container: An icon container widget.
 
7708
 * 
 
7709
 * Makes stretch handles visible on the first selected icon.
 
7710
 **/
 
7711
void
 
7712
nautilus_icon_container_show_stretch_handles (NautilusIconContainer *container)
 
7713
{
 
7714
        NautilusIconContainerDetails *details;
 
7715
        NautilusIcon *icon;
 
7716
        guint initial_size;
 
7717
        
 
7718
        icon = get_first_selected_icon (container);
 
7719
        if (icon == NULL) {
 
7720
                return;
 
7721
        }
 
7722
 
 
7723
        /* Check if it already has stretch handles. */
 
7724
        details = container->details;
 
7725
        if (details->stretch_icon == icon) {
 
7726
                return;
 
7727
        }
 
7728
 
 
7729
        /* Get rid of the existing stretch handles and put them on the new icon. */
 
7730
        if (details->stretch_icon != NULL) {
 
7731
                nautilus_icon_canvas_item_set_show_stretch_handles
 
7732
                        (details->stretch_icon->item, FALSE);
 
7733
                ungrab_stretch_icon (container);
 
7734
                emit_stretch_ended (container, details->stretch_icon);
 
7735
        }
 
7736
        nautilus_icon_canvas_item_set_show_stretch_handles (icon->item, TRUE);
 
7737
        details->stretch_icon = icon;
 
7738
        
 
7739
        icon_get_size (container, icon, &initial_size);
 
7740
 
 
7741
        /* only need to keep size in one dimension, since they are constrained to be the same */
 
7742
        container->details->stretch_initial_x = icon->x;
 
7743
        container->details->stretch_initial_y = icon->y;
 
7744
        container->details->stretch_initial_size = initial_size;
 
7745
 
 
7746
        emit_stretch_started (container, icon);
 
7747
}
 
7748
 
 
7749
/**
 
7750
 * nautilus_icon_container_has_stretch_handles
 
7751
 * @container: An icon container widget.
 
7752
 * 
 
7753
 * Returns true if the first selected item has stretch handles.
 
7754
 **/
 
7755
gboolean
 
7756
nautilus_icon_container_has_stretch_handles (NautilusIconContainer *container)
 
7757
{
 
7758
        NautilusIcon *icon;
 
7759
 
 
7760
        icon = get_first_selected_icon (container);
 
7761
        if (icon == NULL) {
 
7762
                return FALSE;
 
7763
        }
 
7764
 
 
7765
        return icon == container->details->stretch_icon;
 
7766
}
 
7767
 
 
7768
/**
 
7769
 * nautilus_icon_container_is_stretched
 
7770
 * @container: An icon container widget.
 
7771
 * 
 
7772
 * Returns true if the any selected item is stretched to a size other than 1.0.
 
7773
 **/
 
7774
gboolean
 
7775
nautilus_icon_container_is_stretched (NautilusIconContainer *container)
 
7776
{
 
7777
        GList *p;
 
7778
        NautilusIcon *icon;
 
7779
 
 
7780
        for (p = container->details->icons; p != NULL; p = p->next) {
 
7781
                icon = p->data;
 
7782
                if (icon->is_selected && icon->scale != 1.0) {
 
7783
                        return TRUE;
 
7784
                }
 
7785
        }
 
7786
        return FALSE;
 
7787
}
 
7788
 
 
7789
/**
 
7790
 * nautilus_icon_container_unstretch
 
7791
 * @container: An icon container widget.
 
7792
 * 
 
7793
 * Gets rid of any icon stretching.
 
7794
 **/
 
7795
void
 
7796
nautilus_icon_container_unstretch (NautilusIconContainer *container)
 
7797
{
 
7798
        GList *p;
 
7799
        NautilusIcon *icon;
 
7800
 
 
7801
        for (p = container->details->icons; p != NULL; p = p->next) {
 
7802
                icon = p->data;
 
7803
                if (icon->is_selected) {
 
7804
                        nautilus_icon_container_move_icon (container, icon,
 
7805
                                                           icon->x, icon->y,
 
7806
                                                           1.0,
 
7807
                                                           FALSE, TRUE, TRUE);
 
7808
                }
 
7809
        }
 
7810
}
 
7811
 
 
7812
static void
 
7813
compute_stretch (StretchState *start,
 
7814
                 StretchState *current)
 
7815
{
 
7816
        gboolean right, bottom;
 
7817
        int x_stretch, y_stretch;
 
7818
 
 
7819
        /* FIXME bugzilla.gnome.org 45390: This doesn't correspond to
 
7820
         * the way the handles are drawn.
 
7821
         */
 
7822
        /* Figure out which handle we are dragging. */
 
7823
        right = start->pointer_x > start->icon_x + (int) start->icon_size / 2;
 
7824
        bottom = start->pointer_y > start->icon_y + (int) start->icon_size / 2;
 
7825
 
 
7826
        /* Figure out how big we should stretch. */
 
7827
        x_stretch = start->pointer_x - current->pointer_x;
 
7828
        y_stretch = start->pointer_y - current->pointer_y;
 
7829
        if (right) {
 
7830
                x_stretch = - x_stretch;
 
7831
        }
 
7832
        if (bottom) {
 
7833
                y_stretch = - y_stretch;
 
7834
        }
 
7835
        current->icon_size = MAX ((int) start->icon_size + MIN (x_stretch, y_stretch),
 
7836
                                  (int) NAUTILUS_ICON_SIZE_SMALLEST);
 
7837
 
 
7838
        /* Figure out where the corner of the icon should be. */
 
7839
        current->icon_x = start->icon_x;
 
7840
        if (!right) {
 
7841
                current->icon_x += start->icon_size - current->icon_size;
 
7842
        }
 
7843
        current->icon_y = start->icon_y;
 
7844
        if (!bottom) {
 
7845
                current->icon_y += start->icon_size - current->icon_size;
 
7846
        }
 
7847
}
 
7848
 
 
7849
char *
 
7850
nautilus_icon_container_get_icon_uri (NautilusIconContainer *container,
 
7851
                                      NautilusIcon *icon)
 
7852
{
 
7853
        char *uri;
 
7854
 
 
7855
        uri = NULL;
 
7856
        g_signal_emit (container,
 
7857
                         signals[GET_ICON_URI], 0,
 
7858
                         icon->data,
 
7859
                         &uri);
 
7860
        return uri;
 
7861
}
 
7862
 
 
7863
char *
 
7864
nautilus_icon_container_get_icon_drop_target_uri (NautilusIconContainer *container,
 
7865
                                                  NautilusIcon *icon)
 
7866
{
 
7867
        char *uri;
 
7868
 
 
7869
        uri = NULL;
 
7870
        g_signal_emit (container,
 
7871
                         signals[GET_ICON_DROP_TARGET_URI], 0,
 
7872
                         icon->data,
 
7873
                         &uri);
 
7874
        return uri;
 
7875
}
 
7876
 
 
7877
/* Call to reset the scroll region only if the container is not empty,
 
7878
 * to avoid having the flag linger until the next file is added.
 
7879
 */
 
7880
static void
 
7881
reset_scroll_region_if_not_empty (NautilusIconContainer *container)
 
7882
{
 
7883
        if (!nautilus_icon_container_is_empty (container)) {
 
7884
                nautilus_icon_container_reset_scroll_region (container);
 
7885
        }
 
7886
}
 
7887
 
 
7888
/* Switch from automatic layout to manual or vice versa.
 
7889
 * If we switch to manual layout, we restore the icon positions from the
 
7890
 * last manual layout.
 
7891
 */
 
7892
void
 
7893
nautilus_icon_container_set_auto_layout (NautilusIconContainer *container,
 
7894
                                         gboolean auto_layout)
 
7895
{
 
7896
        g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container));
 
7897
        g_return_if_fail (auto_layout == FALSE || auto_layout == TRUE);
 
7898
 
 
7899
        if (container->details->auto_layout == auto_layout) {
 
7900
                return;
 
7901
        }
 
7902
 
 
7903
        reset_scroll_region_if_not_empty (container);
 
7904
        container->details->auto_layout = auto_layout;
 
7905
 
 
7906
        if (!auto_layout) {
 
7907
                reload_icon_positions (container);
 
7908
                nautilus_icon_container_freeze_icon_positions (container);
 
7909
        }
 
7910
 
 
7911
        container->details->needs_resort = TRUE;
 
7912
        redo_layout (container);
 
7913
 
 
7914
        g_signal_emit (container, signals[LAYOUT_CHANGED], 0);
 
7915
}
 
7916
 
 
7917
gboolean
 
7918
nautilus_icon_container_is_keep_aligned (NautilusIconContainer *container)
 
7919
{
 
7920
        return container->details->keep_aligned;
 
7921
}
 
7922
 
 
7923
static gboolean
 
7924
align_icons_callback (gpointer callback_data)
 
7925
{
 
7926
        NautilusIconContainer *container;
 
7927
 
 
7928
        container = NAUTILUS_ICON_CONTAINER (callback_data);
 
7929
        align_icons (container);
 
7930
        container->details->align_idle_id = 0;
 
7931
 
 
7932
        return FALSE;
 
7933
}
 
7934
 
 
7935
static void
 
7936
unschedule_align_icons (NautilusIconContainer *container)
 
7937
{
 
7938
        if (container->details->align_idle_id != 0) {
 
7939
                g_source_remove (container->details->align_idle_id);
 
7940
                container->details->align_idle_id = 0;
 
7941
        }
 
7942
}
 
7943
 
 
7944
static void
 
7945
schedule_align_icons (NautilusIconContainer *container)
 
7946
{
 
7947
        if (container->details->align_idle_id == 0
 
7948
            && container->details->has_been_allocated) {
 
7949
                container->details->align_idle_id = g_idle_add
 
7950
                        (align_icons_callback, container);
 
7951
        }
 
7952
}
 
7953
 
 
7954
void
 
7955
nautilus_icon_container_set_keep_aligned (NautilusIconContainer *container,
 
7956
                                          gboolean keep_aligned)
 
7957
{
 
7958
        if (container->details->keep_aligned != keep_aligned) {
 
7959
                container->details->keep_aligned = keep_aligned;
 
7960
                
 
7961
                if (keep_aligned && !container->details->auto_layout) {
 
7962
                        schedule_align_icons (container);
 
7963
                } else {
 
7964
                        unschedule_align_icons (container);
 
7965
                }
 
7966
        }
 
7967
}
 
7968
 
 
7969
void
 
7970
nautilus_icon_container_set_layout_mode (NautilusIconContainer *container,
 
7971
                                         NautilusIconLayoutMode mode)
 
7972
{
 
7973
        g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container));
 
7974
 
 
7975
        container->details->layout_mode = mode;
 
7976
        invalidate_labels (container);
 
7977
 
 
7978
        container->details->needs_resort = TRUE;
 
7979
        redo_layout (container);
 
7980
 
 
7981
        g_signal_emit (container, signals[LAYOUT_CHANGED], 0);
 
7982
}
 
7983
 
 
7984
void
 
7985
nautilus_icon_container_set_label_position (NautilusIconContainer *container,
 
7986
                                            NautilusIconLabelPosition position)
 
7987
{
 
7988
        g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container));
 
7989
 
 
7990
        if (container->details->label_position != position) {
 
7991
                container->details->label_position = position;
 
7992
 
 
7993
                invalidate_labels (container);
 
7994
                nautilus_icon_container_request_update_all (container);
 
7995
 
 
7996
                schedule_redo_layout (container);
 
7997
        }
 
7998
}
 
7999
 
 
8000
/* Switch from automatic to manual layout, freezing all the icons in their
 
8001
 * current positions instead of restoring icon positions from the last manual
 
8002
 * layout as set_auto_layout does.
 
8003
 */
 
8004
void
 
8005
nautilus_icon_container_freeze_icon_positions (NautilusIconContainer *container)
 
8006
{
 
8007
        gboolean changed;
 
8008
        GList *p;
 
8009
        NautilusIcon *icon;
 
8010
        NautilusIconPosition position;
 
8011
 
 
8012
        changed = container->details->auto_layout;
 
8013
        container->details->auto_layout = FALSE;
 
8014
        
 
8015
        for (p = container->details->icons; p != NULL; p = p->next) {
 
8016
                icon = p->data;
 
8017
 
 
8018
                position.x = icon->saved_ltr_x;
 
8019
                position.y = icon->y;
 
8020
                position.scale = icon->scale;
 
8021
                g_signal_emit (container, signals[ICON_POSITION_CHANGED], 0,
 
8022
                                 icon->data, &position);
 
8023
        }
 
8024
 
 
8025
        if (changed) {
 
8026
                g_signal_emit (container, signals[LAYOUT_CHANGED], 0);
 
8027
        }
 
8028
}
 
8029
 
 
8030
/* Re-sort, switching to automatic layout if it was in manual layout. */
 
8031
void
 
8032
nautilus_icon_container_sort (NautilusIconContainer *container)
 
8033
{
 
8034
        gboolean changed;
 
8035
 
 
8036
        changed = !container->details->auto_layout;
 
8037
        container->details->auto_layout = TRUE;
 
8038
 
 
8039
        reset_scroll_region_if_not_empty (container);
 
8040
        container->details->needs_resort = TRUE;
 
8041
        redo_layout (container);
 
8042
 
 
8043
        if (changed) {
 
8044
                g_signal_emit (container, signals[LAYOUT_CHANGED], 0);
 
8045
        }
 
8046
}
 
8047
 
 
8048
gboolean
 
8049
nautilus_icon_container_is_auto_layout (NautilusIconContainer *container)
 
8050
{
 
8051
        g_return_val_if_fail (NAUTILUS_IS_ICON_CONTAINER (container), FALSE);
 
8052
 
 
8053
        return container->details->auto_layout;
 
8054
}
 
8055
 
 
8056
static void
 
8057
pending_icon_to_rename_destroy_callback (NautilusIconCanvasItem *item, NautilusIconContainer *container)
 
8058
{
 
8059
        g_assert (container->details->pending_icon_to_rename != NULL);
 
8060
        g_assert (container->details->pending_icon_to_rename->item == item);
 
8061
        container->details->pending_icon_to_rename = NULL;
 
8062
}
 
8063
 
 
8064
static NautilusIcon*
 
8065
get_pending_icon_to_rename (NautilusIconContainer *container)
 
8066
{
 
8067
        return container->details->pending_icon_to_rename;
 
8068
}
 
8069
 
 
8070
static void
 
8071
set_pending_icon_to_rename (NautilusIconContainer *container, NautilusIcon *icon)
 
8072
{
 
8073
        NautilusIcon *old_icon;
 
8074
        
 
8075
        old_icon = container->details->pending_icon_to_rename;
 
8076
        
 
8077
        if (icon == old_icon) {
 
8078
                return;
 
8079
        }
 
8080
        
 
8081
        if (old_icon != NULL) {
 
8082
                g_signal_handlers_disconnect_by_func
 
8083
                        (old_icon->item,
 
8084
                         G_CALLBACK (pending_icon_to_rename_destroy_callback),
 
8085
                         container);
 
8086
        }
 
8087
        
 
8088
        if (icon != NULL) {
 
8089
                g_signal_connect (icon->item, "destroy",
 
8090
                                  G_CALLBACK (pending_icon_to_rename_destroy_callback), container);
 
8091
        }
 
8092
        
 
8093
        container->details->pending_icon_to_rename = icon;
 
8094
}
 
8095
 
 
8096
static void
 
8097
process_pending_icon_to_rename (NautilusIconContainer *container)
 
8098
{
 
8099
        NautilusIcon *pending_icon_to_rename;
 
8100
        
 
8101
        pending_icon_to_rename = get_pending_icon_to_rename (container);
 
8102
        
 
8103
        if (pending_icon_to_rename != NULL) {
 
8104
                if (pending_icon_to_rename->is_selected && !has_multiple_selection (container)) {
 
8105
                        nautilus_icon_container_start_renaming_selected_item (container, FALSE);
 
8106
                } else {
 
8107
                        set_pending_icon_to_rename (container, NULL);
 
8108
                }
 
8109
        }
 
8110
}
 
8111
 
 
8112
static gboolean
 
8113
is_renaming_pending (NautilusIconContainer *container)
 
8114
{
 
8115
        return get_pending_icon_to_rename (container) != NULL;
 
8116
}
 
8117
 
 
8118
static gboolean
 
8119
is_renaming (NautilusIconContainer *container)
 
8120
{
 
8121
        return container->details->renaming;
 
8122
}
 
8123
 
 
8124
/**
 
8125
 * nautilus_icon_container_start_renaming_selected_item
 
8126
 * @container: An icon container widget.
 
8127
 * @select_all: Whether the whole file should initially be selected, or
 
8128
 *              only its basename (i.e. everything except its extension).
 
8129
 * 
 
8130
 * Displays the edit name widget on the first selected icon
 
8131
 **/
 
8132
void
 
8133
nautilus_icon_container_start_renaming_selected_item (NautilusIconContainer *container,
 
8134
                                                      gboolean select_all)
 
8135
{
 
8136
        NautilusIconContainerDetails *details;
 
8137
        NautilusIcon *icon;
 
8138
        EelDRect icon_rect;
 
8139
        EelDRect text_rect;
 
8140
        PangoContext *context;
 
8141
        PangoFontDescription *desc;
 
8142
        const char *editable_text;
 
8143
        int x, y, width;
 
8144
        int start_offset, end_offset;
 
8145
 
 
8146
        /* Check if it already in renaming mode, if so - select all */
 
8147
        details = container->details;
 
8148
        if (details->renaming) {
 
8149
                eel_editable_label_select_region (EEL_EDITABLE_LABEL (details->rename_widget),
 
8150
                                                  0,
 
8151
                                                  -1);
 
8152
                return;
 
8153
        }
 
8154
 
 
8155
        /* Find selected icon */
 
8156
        icon = get_first_selected_icon (container);
 
8157
        if (icon == NULL) {
 
8158
                return;
 
8159
        }
 
8160
 
 
8161
        g_assert (!has_multiple_selection (container));
 
8162
 
 
8163
 
 
8164
        if (!icon_is_positioned (icon)) {
 
8165
                set_pending_icon_to_rename (container, icon);
 
8166
                return;
 
8167
        }
 
8168
        
 
8169
        set_pending_icon_to_rename (container, NULL);
 
8170
 
 
8171
        /* Make a copy of the original editable text for a later compare */
 
8172
        editable_text = nautilus_icon_canvas_item_get_editable_text (icon->item);
 
8173
 
 
8174
        /* This could conceivably be NULL if a rename was triggered really early. */
 
8175
        if (editable_text == NULL) {
 
8176
                return;
 
8177
        }
 
8178
 
 
8179
        details->original_text = g_strdup (editable_text);
 
8180
        
 
8181
        /* Freeze updates so files added while renaming don't cause rename to loose focus, bug #318373 */
 
8182
        nautilus_icon_container_freeze_updates (container);
 
8183
 
 
8184
        /* Create text renaming widget, if it hasn't been created already.
 
8185
         * We deal with the broken icon text item widget by keeping it around
 
8186
         * so its contents can still be cut and pasted as part of the clipboard
 
8187
         */
 
8188
        if (details->rename_widget == NULL) {
 
8189
                details->rename_widget = eel_editable_label_new ("Test text");
 
8190
                eel_editable_label_set_line_wrap (EEL_EDITABLE_LABEL (details->rename_widget), TRUE);
 
8191
                eel_editable_label_set_line_wrap_mode (EEL_EDITABLE_LABEL (details->rename_widget), PANGO_WRAP_WORD_CHAR);
 
8192
                eel_editable_label_set_draw_outline (EEL_EDITABLE_LABEL (details->rename_widget), TRUE);
 
8193
 
 
8194
                if (details->label_position != NAUTILUS_ICON_LABEL_POSITION_BESIDE) {
 
8195
                        eel_editable_label_set_justify (EEL_EDITABLE_LABEL (details->rename_widget), GTK_JUSTIFY_CENTER);
 
8196
                }
 
8197
 
 
8198
                gtk_misc_set_padding (GTK_MISC (details->rename_widget), 1, 1);
 
8199
                gtk_layout_put (GTK_LAYOUT (container),
 
8200
                                details->rename_widget, 0, 0);
 
8201
        } 
 
8202
 
 
8203
        /* Set the right font */
 
8204
        if (details->font) {
 
8205
                desc = pango_font_description_from_string (details->font);
 
8206
        } else {
 
8207
                context = gtk_widget_get_pango_context (GTK_WIDGET (container));
 
8208
                desc = pango_font_description_copy (pango_context_get_font_description (context));
 
8209
                pango_font_description_set_size (desc,
 
8210
                                                 pango_font_description_get_size (desc) +
 
8211
                                                 container->details->font_size_table [container->details->zoom_level]);
 
8212
        }
 
8213
        eel_editable_label_set_font_description (EEL_EDITABLE_LABEL (details->rename_widget),
 
8214
                                                 desc);
 
8215
        pango_font_description_free (desc);
 
8216
        
 
8217
        icon_rect = nautilus_icon_canvas_item_get_icon_rectangle (icon->item);
 
8218
        text_rect = nautilus_icon_canvas_item_get_text_rectangle (icon->item, TRUE);
 
8219
 
 
8220
        if (nautilus_icon_container_is_layout_vertical (container) &&
 
8221
            container->details->label_position == NAUTILUS_ICON_LABEL_POSITION_BESIDE) {
 
8222
                /* for one-line editables, the width changes dynamically */
 
8223
                width = -1;
 
8224
        } else {
 
8225
                width = nautilus_icon_canvas_item_get_max_text_width (icon->item);
 
8226
        }
 
8227
 
 
8228
        if (details->label_position == NAUTILUS_ICON_LABEL_POSITION_BESIDE) {
 
8229
                eel_canvas_w2c (EEL_CANVAS_ITEM (icon->item)->canvas,
 
8230
                                text_rect.x0,
 
8231
                                text_rect.y0,
 
8232
                                &x, &y);
 
8233
        } else {
 
8234
                eel_canvas_w2c (EEL_CANVAS_ITEM (icon->item)->canvas,
 
8235
                                (icon_rect.x0 + icon_rect.x1) / 2,
 
8236
                                icon_rect.y1,
 
8237
                                &x, &y);
 
8238
                x = x - width / 2 - 1;
 
8239
        }
 
8240
 
 
8241
        gtk_layout_move (GTK_LAYOUT (container),
 
8242
                         details->rename_widget,
 
8243
                         x, y);
 
8244
        
 
8245
        gtk_widget_set_size_request (details->rename_widget,
 
8246
                                     width, -1);
 
8247
        eel_editable_label_set_text (EEL_EDITABLE_LABEL (details->rename_widget),
 
8248
                                     editable_text);
 
8249
        if (select_all) {
 
8250
                start_offset = 0;
 
8251
                end_offset = -1;
 
8252
        } else {
 
8253
                eel_filename_get_rename_region (editable_text, &start_offset, &end_offset);
 
8254
        }
 
8255
 
 
8256
        gtk_widget_show (details->rename_widget);
 
8257
        gtk_widget_grab_focus (details->rename_widget);
 
8258
 
 
8259
        eel_editable_label_select_region (EEL_EDITABLE_LABEL (details->rename_widget),
 
8260
                                          start_offset,
 
8261
                                          end_offset);
 
8262
        
 
8263
        g_signal_emit (container,
 
8264
                       signals[ICON_RENAME_STARTED], 0,
 
8265
                       GTK_EDITABLE (details->rename_widget));
 
8266
        
 
8267
        nautilus_icon_container_update_icon (container, icon);
 
8268
        
 
8269
        /* We are in renaming mode */
 
8270
        details->renaming = TRUE;
 
8271
        nautilus_icon_canvas_item_set_renaming (icon->item, TRUE);
 
8272
}
 
8273
 
 
8274
static void
 
8275
end_renaming_mode (NautilusIconContainer *container, gboolean commit)
 
8276
{
 
8277
        NautilusIcon *icon;
 
8278
        const char *changed_text = NULL;
 
8279
 
 
8280
        set_pending_icon_to_rename (container, NULL);
 
8281
 
 
8282
        icon = get_icon_being_renamed (container);
 
8283
        if (icon == NULL) {
 
8284
                return;
 
8285
        }
 
8286
 
 
8287
        /* We are not in renaming mode */
 
8288
        container->details->renaming = FALSE;
 
8289
        nautilus_icon_canvas_item_set_renaming (icon->item, FALSE);
 
8290
        
 
8291
        nautilus_icon_container_unfreeze_updates (container);
 
8292
 
 
8293
        if (commit) {
 
8294
                set_pending_icon_to_reveal (container, icon);
 
8295
        }
 
8296
 
 
8297
        gtk_widget_grab_focus (GTK_WIDGET (container));
 
8298
        
 
8299
        if (commit) {
 
8300
                /* Verify that text has been modified before signalling change. */                      
 
8301
                changed_text = eel_editable_label_get_text (EEL_EDITABLE_LABEL (container->details->rename_widget));
 
8302
                if (strcmp (container->details->original_text, changed_text) == 0) {
 
8303
                        changed_text = NULL;
 
8304
                }
 
8305
        }
 
8306
 
 
8307
        g_signal_emit (container,
 
8308
                       signals[ICON_RENAME_ENDED], 0,
 
8309
                       icon->data,
 
8310
                       changed_text);
 
8311
 
 
8312
        gtk_widget_hide (container->details->rename_widget);
 
8313
        g_free (container->details->original_text);
 
8314
}
 
8315
 
 
8316
gboolean
 
8317
nautilus_icon_container_has_stored_icon_positions (NautilusIconContainer *container)
 
8318
{
 
8319
        GList *p;
 
8320
        NautilusIcon *icon;
 
8321
        gboolean have_stored_position;
 
8322
        NautilusIconPosition position;
 
8323
 
 
8324
        for (p = container->details->icons; p != NULL; p = p->next) {
 
8325
                icon = p->data;
 
8326
 
 
8327
                have_stored_position = FALSE;
 
8328
                g_signal_emit (container,
 
8329
                                 signals[GET_STORED_ICON_POSITION], 0,
 
8330
                                 icon->data,
 
8331
                                 &position,
 
8332
                                 &have_stored_position);
 
8333
                if (have_stored_position) {
 
8334
                        return TRUE;
 
8335
                }
 
8336
        }
 
8337
        return FALSE;
 
8338
}
 
8339
 
 
8340
void
 
8341
nautilus_icon_container_set_single_click_mode (NautilusIconContainer *container,
 
8342
                                               gboolean single_click_mode)
 
8343
{
 
8344
        g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container));
 
8345
 
 
8346
        container->details->single_click_mode = single_click_mode;
 
8347
}
 
8348
 
 
8349
/* Return if the icon container is a fixed size */
 
8350
gboolean
 
8351
nautilus_icon_container_get_is_fixed_size (NautilusIconContainer *container)
 
8352
{
 
8353
        g_return_val_if_fail (NAUTILUS_IS_ICON_CONTAINER (container), FALSE);
 
8354
 
 
8355
        return container->details->is_fixed_size;
 
8356
}
 
8357
 
 
8358
/* Set the icon container to be a fixed size */
 
8359
void
 
8360
nautilus_icon_container_set_is_fixed_size (NautilusIconContainer *container,
 
8361
                                           gboolean is_fixed_size)
 
8362
{
 
8363
        g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container));
 
8364
 
 
8365
        container->details->is_fixed_size = is_fixed_size;
 
8366
}
 
8367
 
 
8368
gboolean
 
8369
nautilus_icon_container_get_is_desktop (NautilusIconContainer *container)
 
8370
{
 
8371
        g_return_val_if_fail (NAUTILUS_IS_ICON_CONTAINER (container), FALSE);
 
8372
 
 
8373
        return container->details->is_desktop;
 
8374
}
 
8375
 
 
8376
void
 
8377
nautilus_icon_container_set_is_desktop (NautilusIconContainer *container,
 
8378
                                           gboolean is_desktop)
 
8379
{
 
8380
        g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container));
 
8381
 
 
8382
        container->details->is_desktop = is_desktop;
 
8383
 
 
8384
        if (is_desktop) {
 
8385
                GtkStyleContext *context;
 
8386
 
 
8387
                context = gtk_widget_get_style_context (GTK_WIDGET (container));
 
8388
                gtk_style_context_add_class (context, "nautilus-desktop");
 
8389
        }
 
8390
}
 
8391
 
 
8392
void
 
8393
nautilus_icon_container_set_margins (NautilusIconContainer *container,
 
8394
                                     int left_margin,
 
8395
                                     int right_margin,
 
8396
                                     int top_margin,
 
8397
                                     int bottom_margin)
 
8398
{
 
8399
        g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container));
 
8400
 
 
8401
        container->details->left_margin = left_margin;
 
8402
        container->details->right_margin = right_margin;
 
8403
        container->details->top_margin = top_margin;
 
8404
        container->details->bottom_margin = bottom_margin;
 
8405
 
 
8406
        /* redo layout of icons as the margins have changed */
 
8407
        schedule_redo_layout (container);
 
8408
}
 
8409
 
 
8410
void
 
8411
nautilus_icon_container_set_use_drop_shadows (NautilusIconContainer  *container,
 
8412
                                              gboolean                use_drop_shadows)
 
8413
{
 
8414
        if (container->details->drop_shadows_requested == use_drop_shadows) {
 
8415
                return;
 
8416
        }
 
8417
 
 
8418
        container->details->drop_shadows_requested = use_drop_shadows;
 
8419
        container->details->use_drop_shadows = use_drop_shadows;
 
8420
        gtk_widget_queue_draw (GTK_WIDGET (container));
 
8421
}
 
8422
 
 
8423
/* handle theme changes */
 
8424
 
 
8425
void
 
8426
nautilus_icon_container_set_font (NautilusIconContainer *container,
 
8427
                                  const char *font)
 
8428
{
 
8429
        g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container));
 
8430
 
 
8431
        if (g_strcmp0 (container->details->font, font) == 0) {
 
8432
                return;
 
8433
        }
 
8434
 
 
8435
        g_free (container->details->font);
 
8436
        container->details->font = g_strdup (font);
 
8437
 
 
8438
        invalidate_labels (container);
 
8439
        nautilus_icon_container_request_update_all (container);
 
8440
        gtk_widget_queue_draw (GTK_WIDGET (container));
 
8441
}
 
8442
 
 
8443
void
 
8444
nautilus_icon_container_set_font_size_table (NautilusIconContainer *container,
 
8445
                                             const int font_size_table[NAUTILUS_ZOOM_LEVEL_LARGEST + 1])
 
8446
{
 
8447
        int old_font_size;
 
8448
        int i;
 
8449
        
 
8450
        g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container));
 
8451
        g_return_if_fail (font_size_table != NULL);
 
8452
        
 
8453
        old_font_size = container->details->font_size_table[container->details->zoom_level];
 
8454
 
 
8455
        for (i = 0; i <= NAUTILUS_ZOOM_LEVEL_LARGEST; i++) {
 
8456
                if (container->details->font_size_table[i] != font_size_table[i]) {
 
8457
                        container->details->font_size_table[i] = font_size_table[i];
 
8458
                }
 
8459
        }
 
8460
 
 
8461
        if (old_font_size != container->details->font_size_table[container->details->zoom_level]) {
 
8462
                invalidate_labels (container);
 
8463
                nautilus_icon_container_request_update_all (container);
 
8464
        }
 
8465
}
 
8466
 
 
8467
/**
 
8468
 * nautilus_icon_container_get_icon_description
 
8469
 * @container: An icon container widget.
 
8470
 * @data: Icon data 
 
8471
 * 
 
8472
 * Gets the description for the icon. This function may return NULL.
 
8473
 **/
 
8474
char*
 
8475
nautilus_icon_container_get_icon_description (NautilusIconContainer *container,
 
8476
                                              NautilusIconData      *data)
 
8477
{
 
8478
        NautilusIconContainerClass *klass;
 
8479
 
 
8480
        klass = NAUTILUS_ICON_CONTAINER_GET_CLASS (container);
 
8481
 
 
8482
        if (klass->get_icon_description) {
 
8483
                return klass->get_icon_description (container, data);
 
8484
        } else {
 
8485
                return NULL;
 
8486
        }
 
8487
}
 
8488
 
 
8489
gboolean
 
8490
nautilus_icon_container_get_allow_moves (NautilusIconContainer *container)
 
8491
{
 
8492
        g_return_val_if_fail (NAUTILUS_IS_ICON_CONTAINER (container), FALSE);
 
8493
 
 
8494
        return container->details->drag_allow_moves;
 
8495
}
 
8496
 
 
8497
void
 
8498
nautilus_icon_container_set_allow_moves (NautilusIconContainer *container,
 
8499
                                         gboolean               allow_moves)
 
8500
{
 
8501
        g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container));
 
8502
 
 
8503
        container->details->drag_allow_moves = allow_moves;
 
8504
}
 
8505
 
 
8506
void
 
8507
nautilus_icon_container_set_forced_icon_size (NautilusIconContainer *container,
 
8508
                                              int                    forced_icon_size)
 
8509
{
 
8510
        g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container));
 
8511
 
 
8512
        if (forced_icon_size != container->details->forced_icon_size) {
 
8513
                container->details->forced_icon_size = forced_icon_size;
 
8514
 
 
8515
                invalidate_label_sizes (container);
 
8516
                nautilus_icon_container_request_update_all (container);
 
8517
        }
 
8518
}
 
8519
 
 
8520
void
 
8521
nautilus_icon_container_set_all_columns_same_width (NautilusIconContainer *container,
 
8522
                                                    gboolean               all_columns_same_width)
 
8523
{
 
8524
        g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container));
 
8525
 
 
8526
        if (all_columns_same_width != container->details->all_columns_same_width) {
 
8527
                container->details->all_columns_same_width = all_columns_same_width;
 
8528
 
 
8529
                invalidate_labels (container);
 
8530
                nautilus_icon_container_request_update_all (container);
 
8531
        }
 
8532
}
 
8533
 
 
8534
/**
 
8535
 * nautilus_icon_container_set_highlighted_for_clipboard
 
8536
 * @container: An icon container widget.
 
8537
 * @data: Icon Data associated with all icons that should be highlighted.
 
8538
 *        Others will be unhighlighted.
 
8539
 **/
 
8540
void
 
8541
nautilus_icon_container_set_highlighted_for_clipboard (NautilusIconContainer *container,
 
8542
                                                       GList                 *clipboard_icon_data)
 
8543
{
 
8544
        GList *l;
 
8545
        NautilusIcon *icon;
 
8546
        gboolean highlighted_for_clipboard;
 
8547
 
 
8548
        g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container));
 
8549
 
 
8550
        for (l = container->details->icons; l != NULL; l = l->next) {
 
8551
                icon = l->data;
 
8552
                highlighted_for_clipboard = (g_list_find (clipboard_icon_data, icon->data) != NULL);
 
8553
 
 
8554
                eel_canvas_item_set (EEL_CANVAS_ITEM (icon->item),
 
8555
                                     "highlighted-for-clipboard", highlighted_for_clipboard,
 
8556
                                     NULL);
 
8557
        }
 
8558
 
 
8559
}
 
8560
 
 
8561
void
 
8562
nautilus_icon_container_set_active (NautilusIconContainer *container,
 
8563
                                    gboolean active)
 
8564
{
 
8565
        container->details->active_background = active;
 
8566
 
 
8567
        if (gtk_widget_get_realized (GTK_WIDGET (container))) {
 
8568
                setup_background (container);
 
8569
        }
 
8570
}
 
8571
 
 
8572
/* NautilusIconContainerAccessible */
 
8573
 
 
8574
static NautilusIconContainerAccessiblePrivate *
 
8575
accessible_get_priv (AtkObject *accessible)
 
8576
{
 
8577
        NautilusIconContainerAccessiblePrivate *priv;
 
8578
        
 
8579
        priv = g_object_get_qdata (G_OBJECT (accessible), 
 
8580
                                   accessible_private_data_quark);
 
8581
 
 
8582
        return priv;
 
8583
}
 
8584
 
 
8585
/* AtkAction interface */
 
8586
 
 
8587
static gboolean
 
8588
nautilus_icon_container_accessible_do_action (AtkAction *accessible, int i)
 
8589
{
 
8590
        GtkWidget *widget;
 
8591
        NautilusIconContainer *container;
 
8592
        GList *selection;
 
8593
 
 
8594
        g_return_val_if_fail (i < LAST_ACTION, FALSE);
 
8595
 
 
8596
        widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
 
8597
        if (!widget) {
 
8598
                return FALSE;
 
8599
        }
 
8600
        
 
8601
        container = NAUTILUS_ICON_CONTAINER (widget);
 
8602
        switch (i) {
 
8603
        case ACTION_ACTIVATE :
 
8604
                selection = nautilus_icon_container_get_selection (container);
 
8605
 
 
8606
                if (selection) {
 
8607
                        g_signal_emit_by_name (container, "activate", selection);
 
8608
                        g_list_free (selection);
 
8609
                }
 
8610
                break;
 
8611
        case ACTION_MENU :
 
8612
                handle_popups (container, NULL,"context_click_background");
 
8613
                break;
 
8614
        default :
 
8615
                g_warning ("Invalid action passed to NautilusIconContainerAccessible::do_action");
 
8616
                return FALSE;
 
8617
        }
 
8618
        return TRUE;
 
8619
}
 
8620
 
 
8621
static int
 
8622
nautilus_icon_container_accessible_get_n_actions (AtkAction *accessible)
 
8623
{
 
8624
        return LAST_ACTION;
 
8625
}
 
8626
 
 
8627
static const char *
 
8628
nautilus_icon_container_accessible_action_get_description (AtkAction *accessible, 
 
8629
                                                           int i)
 
8630
{
 
8631
        NautilusIconContainerAccessiblePrivate *priv;
 
8632
        
 
8633
        g_assert (i < LAST_ACTION);
 
8634
 
 
8635
        priv = accessible_get_priv (ATK_OBJECT (accessible));
 
8636
        
 
8637
        if (priv->action_descriptions[i]) {
 
8638
                return priv->action_descriptions[i];
 
8639
        } else {
 
8640
                return nautilus_icon_container_accessible_action_descriptions[i];
 
8641
        }
 
8642
}
 
8643
 
 
8644
static const char *
 
8645
nautilus_icon_container_accessible_action_get_name (AtkAction *accessible, int i)
 
8646
{
 
8647
        g_assert (i < LAST_ACTION);
 
8648
 
 
8649
        return nautilus_icon_container_accessible_action_names[i];
 
8650
}
 
8651
 
 
8652
static const char *
 
8653
nautilus_icon_container_accessible_action_get_keybinding (AtkAction *accessible, 
 
8654
                                                          int i)
 
8655
{
 
8656
        g_assert (i < LAST_ACTION);
 
8657
 
 
8658
        return NULL;
 
8659
}
 
8660
 
 
8661
static gboolean
 
8662
nautilus_icon_container_accessible_action_set_description (AtkAction *accessible, 
 
8663
                                                           int i, 
 
8664
                                                           const char *description)
 
8665
{
 
8666
        NautilusIconContainerAccessiblePrivate *priv;
 
8667
 
 
8668
        g_assert (i < LAST_ACTION);
 
8669
 
 
8670
        priv = accessible_get_priv (ATK_OBJECT (accessible));
 
8671
 
 
8672
        if (priv->action_descriptions[i]) {
 
8673
                g_free (priv->action_descriptions[i]);
 
8674
        }
 
8675
        priv->action_descriptions[i] = g_strdup (description);
 
8676
 
 
8677
        return FALSE;
 
8678
}
 
8679
 
 
8680
static void
 
8681
nautilus_icon_container_accessible_action_interface_init (AtkActionIface *iface)
 
8682
{
 
8683
        iface->do_action = nautilus_icon_container_accessible_do_action;
 
8684
        iface->get_n_actions = nautilus_icon_container_accessible_get_n_actions;
 
8685
        iface->get_description = nautilus_icon_container_accessible_action_get_description;
 
8686
        iface->get_name = nautilus_icon_container_accessible_action_get_name;
 
8687
        iface->get_keybinding = nautilus_icon_container_accessible_action_get_keybinding;
 
8688
        iface->set_description = nautilus_icon_container_accessible_action_set_description;
 
8689
}
 
8690
 
 
8691
/* AtkSelection interface */
 
8692
 
 
8693
static void
 
8694
nautilus_icon_container_accessible_update_selection (AtkObject *accessible)
 
8695
{
 
8696
        NautilusIconContainer *container;
 
8697
        NautilusIconContainerAccessiblePrivate *priv;
 
8698
        GList *l;
 
8699
        NautilusIcon *icon;
 
8700
 
 
8701
        container = NAUTILUS_ICON_CONTAINER (gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible)));
 
8702
 
 
8703
        priv = accessible_get_priv (accessible);
 
8704
 
 
8705
        if (priv->selection) {
 
8706
                g_list_free (priv->selection);
 
8707
                priv->selection = NULL;
 
8708
        }
 
8709
        
 
8710
        for (l = container->details->icons; l != NULL; l = l->next) {
 
8711
                icon = l->data;
 
8712
                if (icon->is_selected) {
 
8713
                        priv->selection = g_list_prepend (priv->selection, 
 
8714
                                                          icon);
 
8715
                }
 
8716
        }
 
8717
 
 
8718
        priv->selection = g_list_reverse (priv->selection);
 
8719
}
 
8720
 
 
8721
static void
 
8722
nautilus_icon_container_accessible_selection_changed_cb (NautilusIconContainer *container,
 
8723
                                                         gpointer data)
 
8724
{
 
8725
        g_signal_emit_by_name (data, "selection_changed");
 
8726
}
 
8727
 
 
8728
static void
 
8729
nautilus_icon_container_accessible_icon_added_cb (NautilusIconContainer *container,
 
8730
                                                  NautilusIconData *icon_data,
 
8731
                                                  gpointer data)
 
8732
{
 
8733
        NautilusIcon *icon;
 
8734
        AtkObject *atk_parent;
 
8735
        AtkObject *atk_child;
 
8736
        int index;
 
8737
 
 
8738
        icon = g_hash_table_lookup (container->details->icon_set, icon_data);
 
8739
        if (icon) {
 
8740
                atk_parent = ATK_OBJECT (data);
 
8741
                atk_child = atk_gobject_accessible_for_object 
 
8742
                        (G_OBJECT (icon->item));
 
8743
                index = g_list_index (container->details->icons, icon);
 
8744
                
 
8745
                g_signal_emit_by_name (atk_parent, "children_changed::add",
 
8746
                                       index, atk_child, NULL);
 
8747
        }
 
8748
}
 
8749
 
 
8750
static void
 
8751
nautilus_icon_container_accessible_icon_removed_cb (NautilusIconContainer *container,
 
8752
                                                    NautilusIconData *icon_data,
 
8753
                                                    gpointer data)
 
8754
{
 
8755
        NautilusIcon *icon;
 
8756
        AtkObject *atk_parent;
 
8757
        AtkObject *atk_child;
 
8758
        int index;
 
8759
        
 
8760
        icon = g_hash_table_lookup (container->details->icon_set, icon_data);
 
8761
        if (icon) {
 
8762
                atk_parent = ATK_OBJECT (data);
 
8763
                atk_child = atk_gobject_accessible_for_object 
 
8764
                        (G_OBJECT (icon->item));
 
8765
                index = g_list_index (container->details->icons, icon);
 
8766
                
 
8767
                g_signal_emit_by_name (atk_parent, "children_changed::remove",
 
8768
                                       index, atk_child, NULL);
 
8769
        }
 
8770
}
 
8771
 
 
8772
static void
 
8773
nautilus_icon_container_accessible_cleared_cb (NautilusIconContainer *container, 
 
8774
                                               gpointer data)
 
8775
{
 
8776
        g_signal_emit_by_name (data, "children_changed", 0, NULL, NULL);
 
8777
}
 
8778
 
 
8779
 
 
8780
static gboolean 
 
8781
nautilus_icon_container_accessible_add_selection (AtkSelection *accessible, 
 
8782
                                                  int i)
 
8783
{
 
8784
        GtkWidget *widget;
 
8785
        NautilusIconContainer *container;
 
8786
        GList *l;
 
8787
        GList *selection;
 
8788
        NautilusIcon *icon;
 
8789
 
 
8790
        widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
 
8791
        if (!widget) {
 
8792
                return FALSE;
 
8793
        }
 
8794
 
 
8795
        container = NAUTILUS_ICON_CONTAINER (widget);
 
8796
        
 
8797
        l = g_list_nth (container->details->icons, i);
 
8798
        if (l) {
 
8799
                icon = l->data;
 
8800
                
 
8801
                selection = nautilus_icon_container_get_selection (container);
 
8802
                selection = g_list_prepend (selection, 
 
8803
                                            icon->data);
 
8804
                nautilus_icon_container_set_selection (container, selection);
 
8805
                
 
8806
                g_list_free (selection);
 
8807
                return TRUE;
 
8808
        }
 
8809
 
 
8810
        return FALSE;
 
8811
}
 
8812
 
 
8813
static gboolean
 
8814
nautilus_icon_container_accessible_clear_selection (AtkSelection *accessible)
 
8815
{
 
8816
        GtkWidget *widget;
 
8817
        NautilusIconContainer *container;
 
8818
 
 
8819
        widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
 
8820
        if (!widget) {
 
8821
                return FALSE;
 
8822
        }
 
8823
 
 
8824
        container = NAUTILUS_ICON_CONTAINER (widget);
 
8825
 
 
8826
        nautilus_icon_container_unselect_all (container);
 
8827
 
 
8828
        return TRUE;
 
8829
}
 
8830
 
 
8831
static AtkObject *
 
8832
nautilus_icon_container_accessible_ref_selection (AtkSelection *accessible, 
 
8833
                                                  int i)
 
8834
{
 
8835
        AtkObject *atk_object;
 
8836
        NautilusIconContainerAccessiblePrivate *priv;
 
8837
        GList *item;
 
8838
        NautilusIcon *icon;
 
8839
 
 
8840
        nautilus_icon_container_accessible_update_selection (ATK_OBJECT (accessible));
 
8841
        priv = accessible_get_priv (ATK_OBJECT (accessible));
 
8842
 
 
8843
        item = (g_list_nth (priv->selection, i));
 
8844
 
 
8845
        if (item) {
 
8846
                icon = item->data;
 
8847
                atk_object = atk_gobject_accessible_for_object (G_OBJECT (icon->item));
 
8848
                if (atk_object) {
 
8849
                        g_object_ref (atk_object);
 
8850
                }
 
8851
 
 
8852
                return atk_object;
 
8853
        } else {
 
8854
                return NULL;
 
8855
        }
 
8856
}
 
8857
 
 
8858
static int
 
8859
nautilus_icon_container_accessible_get_selection_count (AtkSelection *accessible)
 
8860
{
 
8861
        int count;
 
8862
        NautilusIconContainerAccessiblePrivate *priv;
 
8863
 
 
8864
        nautilus_icon_container_accessible_update_selection (ATK_OBJECT (accessible));
 
8865
        priv = accessible_get_priv (ATK_OBJECT (accessible));
 
8866
 
 
8867
        count = g_list_length (priv->selection);
 
8868
        
 
8869
        return count;
 
8870
}
 
8871
 
 
8872
static gboolean
 
8873
nautilus_icon_container_accessible_is_child_selected (AtkSelection *accessible,
 
8874
                                                      int i)
 
8875
{
 
8876
        NautilusIconContainer *container;
 
8877
        GList *l;
 
8878
        NautilusIcon *icon;
 
8879
        GtkWidget *widget;
 
8880
 
 
8881
        widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
 
8882
        if (!widget) {
 
8883
                return FALSE;
 
8884
        }
 
8885
 
 
8886
        container = NAUTILUS_ICON_CONTAINER (widget);
 
8887
 
 
8888
        l = g_list_nth (container->details->icons, i);
 
8889
        if (l) {
 
8890
                icon = l->data;
 
8891
                return icon->is_selected;
 
8892
        }
 
8893
        return FALSE;
 
8894
}
 
8895
 
 
8896
static gboolean
 
8897
nautilus_icon_container_accessible_remove_selection (AtkSelection *accessible,
 
8898
                                                     int i)
 
8899
{
 
8900
        NautilusIconContainer *container;
 
8901
        NautilusIconContainerAccessiblePrivate *priv;
 
8902
        GList *l;
 
8903
        GList *selection;
 
8904
        NautilusIcon *icon;
 
8905
        GtkWidget *widget;
 
8906
 
 
8907
        widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
 
8908
        if (!widget) {
 
8909
                return FALSE;
 
8910
        }
 
8911
 
 
8912
        nautilus_icon_container_accessible_update_selection (ATK_OBJECT (accessible));
 
8913
        priv = accessible_get_priv (ATK_OBJECT (accessible));
 
8914
 
 
8915
        container = NAUTILUS_ICON_CONTAINER (widget);
 
8916
        
 
8917
        l = g_list_nth (priv->selection, i);
 
8918
        if (l) {
 
8919
                icon = l->data;
 
8920
                
 
8921
                selection = nautilus_icon_container_get_selection (container);
 
8922
                selection = g_list_remove (selection, icon->data);
 
8923
                nautilus_icon_container_set_selection (container, selection);
 
8924
                
 
8925
                g_list_free (selection);
 
8926
                return TRUE;
 
8927
        }
 
8928
 
 
8929
        return FALSE;   
 
8930
}
 
8931
 
 
8932
static gboolean
 
8933
nautilus_icon_container_accessible_select_all_selection (AtkSelection *accessible)
 
8934
{
 
8935
        NautilusIconContainer *container;
 
8936
        GtkWidget *widget;
 
8937
 
 
8938
        widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
 
8939
        if (!widget) {
 
8940
                return FALSE;
 
8941
        }
 
8942
 
 
8943
        container = NAUTILUS_ICON_CONTAINER (widget);
 
8944
 
 
8945
        nautilus_icon_container_select_all (container);
 
8946
 
 
8947
        return TRUE;
 
8948
}
 
8949
 
 
8950
void
 
8951
nautilus_icon_container_widget_to_file_operation_position (NautilusIconContainer *container,
 
8952
                                                           GdkPoint              *position)
 
8953
{
 
8954
        double x, y;
 
8955
 
 
8956
        g_return_if_fail (position != NULL);
 
8957
 
 
8958
        x = position->x;
 
8959
        y = position->y;
 
8960
 
 
8961
        eel_canvas_window_to_world (EEL_CANVAS (container), x, y, &x, &y);
 
8962
 
 
8963
        position->x = (int) x;
 
8964
        position->y = (int) y;
 
8965
 
 
8966
        /* ensure that we end up in the middle of the icon */
 
8967
        position->x -= nautilus_get_icon_size_for_zoom_level (container->details->zoom_level) / 2;
 
8968
        position->y -= nautilus_get_icon_size_for_zoom_level (container->details->zoom_level) / 2;
 
8969
}
 
8970
 
 
8971
static void 
 
8972
nautilus_icon_container_accessible_selection_interface_init (AtkSelectionIface *iface)
 
8973
{
 
8974
        iface->add_selection = nautilus_icon_container_accessible_add_selection;
 
8975
        iface->clear_selection = nautilus_icon_container_accessible_clear_selection;
 
8976
        iface->ref_selection = nautilus_icon_container_accessible_ref_selection;
 
8977
        iface->get_selection_count = nautilus_icon_container_accessible_get_selection_count;
 
8978
        iface->is_child_selected = nautilus_icon_container_accessible_is_child_selected;
 
8979
        iface->remove_selection = nautilus_icon_container_accessible_remove_selection;
 
8980
        iface->select_all_selection = nautilus_icon_container_accessible_select_all_selection;
 
8981
}
 
8982
 
 
8983
 
 
8984
static gint 
 
8985
nautilus_icon_container_accessible_get_n_children (AtkObject *accessible)
 
8986
{
 
8987
        NautilusIconContainer *container;
 
8988
        GtkWidget *widget;
 
8989
        gint i;
 
8990
        
 
8991
        widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
 
8992
        if (!widget) {
 
8993
                return FALSE;
 
8994
        }
 
8995
 
 
8996
        container = NAUTILUS_ICON_CONTAINER (widget);
 
8997
 
 
8998
        i = g_hash_table_size (container->details->icon_set);
 
8999
        if (container->details->rename_widget) {
 
9000
                i++;
 
9001
        }
 
9002
        return i;
 
9003
}
 
9004
 
 
9005
static AtkObject* 
 
9006
nautilus_icon_container_accessible_ref_child (AtkObject *accessible, int i)
 
9007
{
 
9008
        AtkObject *atk_object;
 
9009
        NautilusIconContainer *container;
 
9010
        GList *item;
 
9011
        NautilusIcon *icon;
 
9012
        GtkWidget *widget;
 
9013
        
 
9014
        widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
 
9015
        if (!widget) {
 
9016
                return NULL;
 
9017
        }
 
9018
 
 
9019
        container = NAUTILUS_ICON_CONTAINER (widget);
 
9020
        
 
9021
        item = (g_list_nth (container->details->icons, i));
 
9022
        
 
9023
        if (item) {
 
9024
                icon = item->data;
 
9025
                
 
9026
                atk_object = atk_gobject_accessible_for_object (G_OBJECT (icon->item));
 
9027
                g_object_ref (atk_object);
 
9028
                
 
9029
                return atk_object;
 
9030
        } else {
 
9031
                if (i == g_list_length (container->details->icons)) {
 
9032
                        if (container->details->rename_widget) {
 
9033
                                atk_object = gtk_widget_get_accessible (container->details->rename_widget);
 
9034
                                g_object_ref (atk_object);
 
9035
 
 
9036
                                return atk_object;
 
9037
                        }
 
9038
                }
 
9039
                return NULL;
 
9040
        }
 
9041
}
 
9042
 
 
9043
static void
 
9044
nautilus_icon_container_accessible_initialize (AtkObject *accessible, 
 
9045
                                               gpointer data)
 
9046
{
 
9047
        NautilusIconContainer *container;
 
9048
        NautilusIconContainerAccessiblePrivate *priv;
 
9049
 
 
9050
        if (ATK_OBJECT_CLASS (accessible_parent_class)->initialize) {
 
9051
                ATK_OBJECT_CLASS (accessible_parent_class)->initialize (accessible, data);
 
9052
        }
 
9053
 
 
9054
        priv = g_new0 (NautilusIconContainerAccessiblePrivate, 1);
 
9055
        g_object_set_qdata (G_OBJECT (accessible), 
 
9056
                            accessible_private_data_quark, 
 
9057
                            priv);
 
9058
 
 
9059
        if (GTK_IS_ACCESSIBLE (accessible)) {
 
9060
                nautilus_icon_container_accessible_update_selection 
 
9061
                        (ATK_OBJECT (accessible));
 
9062
                
 
9063
                container = NAUTILUS_ICON_CONTAINER (gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible)));
 
9064
                g_signal_connect (G_OBJECT (container), "selection_changed",
 
9065
                                  G_CALLBACK (nautilus_icon_container_accessible_selection_changed_cb), 
 
9066
                                  accessible);
 
9067
                g_signal_connect (G_OBJECT (container), "icon_added",
 
9068
                                  G_CALLBACK (nautilus_icon_container_accessible_icon_added_cb), 
 
9069
                                  accessible);
 
9070
                g_signal_connect (G_OBJECT (container), "icon_removed",
 
9071
                                  G_CALLBACK (nautilus_icon_container_accessible_icon_removed_cb), 
 
9072
                                  accessible);
 
9073
                g_signal_connect (G_OBJECT (container), "cleared",
 
9074
                                  G_CALLBACK (nautilus_icon_container_accessible_cleared_cb), 
 
9075
                                  accessible);
 
9076
        }
 
9077
}
 
9078
 
 
9079
static void
 
9080
nautilus_icon_container_accessible_finalize (GObject *object)
 
9081
{
 
9082
        NautilusIconContainerAccessiblePrivate *priv;
 
9083
        int i;
 
9084
 
 
9085
        priv = accessible_get_priv (ATK_OBJECT (object));
 
9086
        if (priv->selection) {
 
9087
                g_list_free (priv->selection);
 
9088
        }
 
9089
 
 
9090
        for (i = 0; i < LAST_ACTION; i++) {
 
9091
                if (priv->action_descriptions[i]) {
 
9092
                        g_free (priv->action_descriptions[i]);
 
9093
                }
 
9094
        }
 
9095
        
 
9096
        g_free (priv);
 
9097
 
 
9098
        G_OBJECT_CLASS (accessible_parent_class)->finalize (object);
 
9099
}
 
9100
 
 
9101
static void
 
9102
nautilus_icon_container_accessible_class_init (AtkObjectClass *klass)
 
9103
{
 
9104
        GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
9105
 
 
9106
        accessible_parent_class = g_type_class_peek_parent (klass);
 
9107
 
 
9108
        gobject_class->finalize = nautilus_icon_container_accessible_finalize;
 
9109
 
 
9110
        klass->get_n_children = nautilus_icon_container_accessible_get_n_children;
 
9111
        klass->ref_child = nautilus_icon_container_accessible_ref_child;
 
9112
        klass->initialize = nautilus_icon_container_accessible_initialize;
 
9113
 
 
9114
        accessible_private_data_quark = g_quark_from_static_string ("icon-container-accessible-private-data");
 
9115
}
 
9116
 
 
9117
static GType
 
9118
nautilus_icon_container_accessible_get_type (void)
 
9119
{
 
9120
        static GType type = 0;
 
9121
 
 
9122
        if (!type) {
 
9123
                static GInterfaceInfo atk_action_info = {
 
9124
                        (GInterfaceInitFunc) nautilus_icon_container_accessible_action_interface_init,
 
9125
                        (GInterfaceFinalizeFunc) NULL,
 
9126
                        NULL
 
9127
                };              
 
9128
                
 
9129
                static GInterfaceInfo atk_selection_info = {
 
9130
                        (GInterfaceInitFunc) nautilus_icon_container_accessible_selection_interface_init,
 
9131
                        (GInterfaceFinalizeFunc) NULL,
 
9132
                        NULL
 
9133
                };              
 
9134
 
 
9135
                type = eel_accessibility_create_derived_type 
 
9136
                        ("NautilusIconContainerAccessible",
 
9137
                         EEL_TYPE_CANVAS,
 
9138
                         nautilus_icon_container_accessible_class_init);
 
9139
                
 
9140
                g_type_add_interface_static (type, ATK_TYPE_ACTION,
 
9141
                                             &atk_action_info);
 
9142
                g_type_add_interface_static (type, ATK_TYPE_SELECTION,
 
9143
                                             &atk_selection_info);
 
9144
        }
 
9145
 
 
9146
        return type;
 
9147
}
 
9148
 
 
9149
#if ! defined (NAUTILUS_OMIT_SELF_CHECK)
 
9150
 
 
9151
static char *
 
9152
check_compute_stretch (int icon_x, int icon_y, int icon_size,
 
9153
                       int start_pointer_x, int start_pointer_y,
 
9154
                       int end_pointer_x, int end_pointer_y)
 
9155
{
 
9156
        StretchState start, current;
 
9157
 
 
9158
        start.icon_x = icon_x;
 
9159
        start.icon_y = icon_y;
 
9160
        start.icon_size = icon_size;
 
9161
        start.pointer_x = start_pointer_x;
 
9162
        start.pointer_y = start_pointer_y;
 
9163
        current.pointer_x = end_pointer_x;
 
9164
        current.pointer_y = end_pointer_y;
 
9165
 
 
9166
        compute_stretch (&start, &current);
 
9167
 
 
9168
        return g_strdup_printf ("%d,%d:%d",
 
9169
                                current.icon_x,
 
9170
                                current.icon_y,
 
9171
                                current.icon_size);
 
9172
}
 
9173
 
 
9174
void
 
9175
nautilus_self_check_icon_container (void)
 
9176
{
 
9177
        EEL_CHECK_STRING_RESULT (check_compute_stretch (0, 0, 16, 0, 0, 0, 0), "0,0:16");
 
9178
        EEL_CHECK_STRING_RESULT (check_compute_stretch (0, 0, 16, 16, 16, 17, 17), "0,0:17");
 
9179
        EEL_CHECK_STRING_RESULT (check_compute_stretch (0, 0, 16, 16, 16, 17, 16), "0,0:16");
 
9180
        EEL_CHECK_STRING_RESULT (check_compute_stretch (100, 100, 64, 105, 105, 40, 40), "35,35:129");
 
9181
}
 
9182
 
 
9183
gboolean
 
9184
nautilus_icon_container_is_layout_rtl (NautilusIconContainer *container)
 
9185
{
 
9186
        g_return_val_if_fail (NAUTILUS_IS_ICON_CONTAINER (container), 0);
 
9187
 
 
9188
        return container->details->layout_mode == NAUTILUS_ICON_LAYOUT_T_B_R_L ||
 
9189
                container->details->layout_mode == NAUTILUS_ICON_LAYOUT_R_L_T_B;
 
9190
}
 
9191
 
 
9192
gboolean
 
9193
nautilus_icon_container_is_layout_vertical (NautilusIconContainer *container)
 
9194
{
 
9195
        g_return_val_if_fail (NAUTILUS_IS_ICON_CONTAINER (container), FALSE);
 
9196
 
 
9197
        return (container->details->layout_mode == NAUTILUS_ICON_LAYOUT_T_B_L_R ||
 
9198
                container->details->layout_mode == NAUTILUS_ICON_LAYOUT_T_B_R_L);
 
9199
}
 
9200
 
 
9201
int
 
9202
nautilus_icon_container_get_max_layout_lines_for_pango (NautilusIconContainer  *container)
 
9203
{
 
9204
        int limit;
 
9205
 
 
9206
        if (nautilus_icon_container_get_is_desktop (container)) {
 
9207
                limit = desktop_text_ellipsis_limit;
 
9208
        } else {
 
9209
                limit = text_ellipsis_limits[container->details->zoom_level];
 
9210
        }
 
9211
 
 
9212
        if (limit <= 0) {
 
9213
                return G_MININT;
 
9214
        }
 
9215
 
 
9216
        return -limit;
 
9217
}
 
9218
 
 
9219
int
 
9220
nautilus_icon_container_get_max_layout_lines (NautilusIconContainer  *container)
 
9221
{
 
9222
        int limit;
 
9223
 
 
9224
        if (nautilus_icon_container_get_is_desktop (container)) {
 
9225
                limit = desktop_text_ellipsis_limit;
 
9226
        } else {
 
9227
                limit = text_ellipsis_limits[container->details->zoom_level];
 
9228
        }
 
9229
 
 
9230
        if (limit <= 0) {
 
9231
                return G_MAXINT;
 
9232
        }
 
9233
 
 
9234
        return limit;
 
9235
}
 
9236
 
 
9237
void
 
9238
nautilus_icon_container_begin_loading (NautilusIconContainer *container)
 
9239
{
 
9240
        gboolean dummy;
 
9241
 
 
9242
        if (nautilus_icon_container_get_store_layout_timestamps (container)) {
 
9243
                container->details->layout_timestamp = UNDEFINED_TIME;
 
9244
                g_signal_emit (container,
 
9245
                               signals[GET_STORED_LAYOUT_TIMESTAMP], 0,
 
9246
                               NULL, &container->details->layout_timestamp, &dummy);
 
9247
        }
 
9248
}
 
9249
 
 
9250
static void
 
9251
store_layout_timestamps_now (NautilusIconContainer *container)
 
9252
{
 
9253
        NautilusIcon *icon;
 
9254
        GList *p;
 
9255
        gboolean dummy;
 
9256
 
 
9257
        container->details->layout_timestamp = time (NULL);
 
9258
        g_signal_emit (container,
 
9259
                       signals[STORE_LAYOUT_TIMESTAMP], 0,
 
9260
                       NULL, &container->details->layout_timestamp, &dummy);
 
9261
 
 
9262
        for (p = container->details->icons; p != NULL; p = p->next) {
 
9263
                icon = p->data;
 
9264
 
 
9265
                g_signal_emit (container,
 
9266
                               signals[STORE_LAYOUT_TIMESTAMP], 0,
 
9267
                               icon->data, &container->details->layout_timestamp, &dummy);
 
9268
        }
 
9269
}
 
9270
 
 
9271
 
 
9272
void
 
9273
nautilus_icon_container_end_loading (NautilusIconContainer *container,
 
9274
                                     gboolean               all_icons_added)
 
9275
{
 
9276
        if (all_icons_added &&
 
9277
            nautilus_icon_container_get_store_layout_timestamps (container)) {
 
9278
                if (container->details->new_icons == NULL) {
 
9279
                        store_layout_timestamps_now (container);
 
9280
                } else {
 
9281
                        container->details->store_layout_timestamps_when_finishing_new_icons = TRUE;
 
9282
                }
 
9283
        }
 
9284
}
 
9285
 
 
9286
gboolean
 
9287
nautilus_icon_container_get_store_layout_timestamps (NautilusIconContainer *container)
 
9288
{
 
9289
        return container->details->store_layout_timestamps;
 
9290
}
 
9291
 
 
9292
 
 
9293
void
 
9294
nautilus_icon_container_set_store_layout_timestamps (NautilusIconContainer *container,
 
9295
                                                     gboolean               store_layout_timestamps)
 
9296
{
 
9297
        container->details->store_layout_timestamps = store_layout_timestamps;
 
9298
}
 
9299
 
 
9300
 
 
9301
#endif /* ! NAUTILUS_OMIT_SELF_CHECK */