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