1 #include "nautilus-view-icon-controller.h"
2 #include "nautilus-view-icon-ui.h"
3 #include "nautilus-view-item-model.h"
4 #include "nautilus-view-icon-item-ui.h"
5 #include "nautilus-view-model.h"
6 #include "nautilus-files-view.h"
7 #include "nautilus-file.h"
8 #include "nautilus-metadata.h"
9 #include "nautilus-window-slot.h"
10 #include "nautilus-directory.h"
11 #include "nautilus-global-preferences.h"
12 
13 struct _NautilusViewIconController
14 {
15     NautilusFilesView parent_instance;
16 
17     NautilusViewIconUi *view_ui;
18     NautilusViewModel *model;
19 
20     GIcon *view_icon;
21     GActionGroup *action_group;
22     gint zoom_level;
23 
24     GtkGesture *multi_press_gesture;
25 };
26 
27 G_DEFINE_TYPE (NautilusViewIconController, nautilus_view_icon_controller, NAUTILUS_TYPE_FILES_VIEW)
28 
29 typedef struct
30 {
31     const NautilusFileSortType sort_type;
32     const gchar *metadata_name;
33     const gchar *action_target_name;
34     gboolean reversed;
35 } SortConstants;
36 
37 static const SortConstants sorts_constants[] =
38 {
39     {
40         NAUTILUS_FILE_SORT_BY_DISPLAY_NAME,
41         "name",
42         "name",
43         FALSE,
44     },
45     {
46         NAUTILUS_FILE_SORT_BY_DISPLAY_NAME,
47         "name",
48         "name-desc",
49         TRUE,
50     },
51     {
52         NAUTILUS_FILE_SORT_BY_SIZE,
53         "size",
54         "size",
55         TRUE,
56     },
57     {
58         NAUTILUS_FILE_SORT_BY_TYPE,
59         "type",
60         "type",
61         FALSE,
62     },
63     {
64         NAUTILUS_FILE_SORT_BY_MTIME,
65         "modification date",
66         "modification-date",
67         FALSE,
68     },
69     {
70         NAUTILUS_FILE_SORT_BY_MTIME,
71         "modification date",
72         "modification-date-desc",
73         TRUE,
74     },
75     {
76         NAUTILUS_FILE_SORT_BY_ATIME,
77         "access date",
78         "access-date",
79         FALSE,
80     },
81     {
82         NAUTILUS_FILE_SORT_BY_ATIME,
83         "access date",
84         "access-date-desc",
85         TRUE,
86     },
87     {
88         NAUTILUS_FILE_SORT_BY_BTIME,
89         "creation date",
90         "creation-date",
91         FALSE,
92     },
93     {
94         NAUTILUS_FILE_SORT_BY_BTIME,
95         "creation date",
96         "creation-date-desc",
97         TRUE,
98     },
99     {
100         NAUTILUS_FILE_SORT_BY_TRASHED_TIME,
101         "trashed",
102         "trash-time",
103         TRUE,
104     },
105     {
106         NAUTILUS_FILE_SORT_BY_SEARCH_RELEVANCE,
107         NULL,
108         "search-relevance",
109         TRUE,
110     }
111 };
112 
113 static guint get_icon_size_for_zoom_level (NautilusCanvasZoomLevel zoom_level);
114 
115 static const SortConstants *
get_sorts_constants_from_action_target_name(const gchar * action_target_name)116 get_sorts_constants_from_action_target_name (const gchar *action_target_name)
117 {
118     int i;
119 
120     for (i = 0; i < G_N_ELEMENTS (sorts_constants); i++)
121     {
122         if (g_strcmp0 (sorts_constants[i].action_target_name, action_target_name) == 0)
123         {
124             return &sorts_constants[i];
125         }
126     }
127 
128     return &sorts_constants[0];
129 }
130 
131 static const SortConstants *
get_sorts_constants_from_sort_type(NautilusFileSortType sort_type,gboolean reversed)132 get_sorts_constants_from_sort_type (NautilusFileSortType sort_type,
133                                     gboolean             reversed)
134 {
135     guint i;
136 
137     for (i = 0; i < G_N_ELEMENTS (sorts_constants); i++)
138     {
139         if (sort_type == sorts_constants[i].sort_type
140             && reversed == sorts_constants[i].reversed)
141         {
142             return &sorts_constants[i];
143         }
144     }
145 
146     return &sorts_constants[0];
147 }
148 
149 static const SortConstants *
get_sorts_constants_from_metadata_text(const char * metadata_name,gboolean reversed)150 get_sorts_constants_from_metadata_text (const char *metadata_name,
151                                         gboolean    reversed)
152 {
153     guint i;
154 
155     for (i = 0; i < G_N_ELEMENTS (sorts_constants); i++)
156     {
157         if (g_strcmp0 (sorts_constants[i].metadata_name, metadata_name) == 0
158             && reversed == sorts_constants[i].reversed)
159         {
160             return &sorts_constants[i];
161         }
162     }
163 
164     return &sorts_constants[0];
165 }
166 
167 static const SortConstants *
get_default_sort_order(NautilusFile * file)168 get_default_sort_order (NautilusFile *file)
169 {
170     NautilusFileSortType sort_type;
171     gboolean reversed;
172 
173     sort_type = nautilus_file_get_default_sort_type (file, &reversed);
174 
175     return get_sorts_constants_from_sort_type (sort_type, reversed);
176 }
177 
178 static const SortConstants *
get_directory_sort_by(NautilusFile * file)179 get_directory_sort_by (NautilusFile *file)
180 {
181     const SortConstants *default_sort;
182     g_autofree char *sort_by = NULL;
183     gboolean reversed;
184 
185     default_sort = get_default_sort_order (file);
186     g_return_val_if_fail (default_sort != NULL, NULL);
187 
188     sort_by = nautilus_file_get_metadata (file,
189                                           NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_BY,
190                                           default_sort->metadata_name);
191 
192     reversed = nautilus_file_get_boolean_metadata (file,
193                                                    NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_REVERSED,
194                                                    default_sort->reversed);
195 
196     return get_sorts_constants_from_metadata_text (sort_by, reversed);
197 }
198 
199 static void
set_directory_sort_metadata(NautilusFile * file,const SortConstants * sort)200 set_directory_sort_metadata (NautilusFile        *file,
201                              const SortConstants *sort)
202 {
203     const SortConstants *default_sort;
204 
205     default_sort = get_default_sort_order (file);
206 
207     nautilus_file_set_metadata (file,
208                                 NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_BY,
209                                 default_sort->metadata_name,
210                                 sort->metadata_name);
211     nautilus_file_set_boolean_metadata (file,
212                                         NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_REVERSED,
213                                         default_sort->reversed,
214                                         sort->reversed);
215 }
216 
217 static void
update_sort_order_from_metadata_and_preferences(NautilusViewIconController * self)218 update_sort_order_from_metadata_and_preferences (NautilusViewIconController *self)
219 {
220     const SortConstants *default_directory_sort;
221     GActionGroup *view_action_group;
222 
223     default_directory_sort = get_directory_sort_by (nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (self)));
224     view_action_group = nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (self));
225     g_action_group_change_action_state (view_action_group,
226                                         "sort",
227                                         g_variant_new_string (get_sorts_constants_from_sort_type (default_directory_sort->sort_type, default_directory_sort->reversed)->action_target_name));
228 }
229 
230 static void
real_begin_loading(NautilusFilesView * files_view)231 real_begin_loading (NautilusFilesView *files_view)
232 {
233     NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
234 
235     /* TODO: This calls sort once, and update_context_menus calls update_actions which calls
236      * the action again
237      */
238     update_sort_order_from_metadata_and_preferences (self);
239 
240     /*TODO move this to the files view class begin_loading and hook up? */
241 
242     /* We could have changed to the trash directory or to searching, and then
243      * we need to update the menus */
244     nautilus_files_view_update_context_menus (files_view);
245     nautilus_files_view_update_toolbar_menus (files_view);
246 }
247 
248 static void
real_clear(NautilusFilesView * files_view)249 real_clear (NautilusFilesView *files_view)
250 {
251     NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
252 
253     nautilus_view_model_remove_all_items (self->model);
254 }
255 
256 
257 /* FIXME: ideally this should go into the model so there is not need to
258  * recreate the model with the new data */
259 static void
real_file_changed(NautilusFilesView * files_view,NautilusFile * file,NautilusDirectory * directory)260 real_file_changed (NautilusFilesView *files_view,
261                    NautilusFile      *file,
262                    NautilusDirectory *directory)
263 {
264     NautilusViewIconController *self;
265     NautilusViewItemModel *item_model;
266     NautilusViewItemModel *new_item_model;
267 
268     self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
269     item_model = nautilus_view_model_get_item_from_file (self->model, file);
270     nautilus_view_model_remove_item (self->model, item_model);
271     new_item_model = nautilus_view_item_model_new (file,
272                                                    get_icon_size_for_zoom_level (self->zoom_level));
273     nautilus_view_model_add_item (self->model, new_item_model);
274 }
275 
276 static GList *
real_get_selection(NautilusFilesView * files_view)277 real_get_selection (NautilusFilesView *files_view)
278 {
279     NautilusViewIconController *self;
280     GList *selected_files = NULL;
281     GList *l;
282     g_autoptr (GList) selected_items = NULL;
283 
284     self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
285     selected_items = gtk_flow_box_get_selected_children (GTK_FLOW_BOX (self->view_ui));
286     for (l = selected_items; l != NULL; l = l->next)
287     {
288         NautilusViewItemModel *item_model;
289 
290         item_model = nautilus_view_icon_item_ui_get_model (NAUTILUS_VIEW_ICON_ITEM_UI (l->data));
291         selected_files = g_list_prepend (selected_files,
292                                          g_object_ref (nautilus_view_item_model_get_file (item_model)));
293     }
294 
295     return selected_files;
296 }
297 
298 static gboolean
real_is_empty(NautilusFilesView * files_view)299 real_is_empty (NautilusFilesView *files_view)
300 {
301     NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
302 
303     return g_list_model_get_n_items (G_LIST_MODEL (nautilus_view_model_get_g_model (self->model))) == 0;
304 }
305 
306 static void
real_end_file_changes(NautilusFilesView * files_view)307 real_end_file_changes (NautilusFilesView *files_view)
308 {
309 }
310 
311 static void
real_remove_file(NautilusFilesView * files_view,NautilusFile * file,NautilusDirectory * directory)312 real_remove_file (NautilusFilesView *files_view,
313                   NautilusFile      *file,
314                   NautilusDirectory *directory)
315 {
316     NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
317     NautilusFile *current_file;
318     NautilusViewItemModel *current_item_model;
319     guint i = 0;
320 
321     while ((current_item_model = NAUTILUS_VIEW_ITEM_MODEL (g_list_model_get_item (G_LIST_MODEL (nautilus_view_model_get_g_model (self->model)), i))))
322     {
323         current_file = nautilus_view_item_model_get_file (current_item_model);
324         if (current_file == file)
325         {
326             g_list_store_remove (nautilus_view_model_get_g_model (self->model), i);
327             break;
328         }
329         i++;
330     }
331 }
332 
333 static GQueue *
convert_glist_to_queue(GList * list)334 convert_glist_to_queue (GList *list)
335 {
336     GList *l;
337     GQueue *queue;
338 
339     queue = g_queue_new ();
340     for (l = list; l != NULL; l = l->next)
341     {
342         g_queue_push_tail (queue, l->data);
343     }
344 
345     return queue;
346 }
347 
348 static GQueue *
convert_files_to_item_models(NautilusViewIconController * self,GQueue * files)349 convert_files_to_item_models (NautilusViewIconController *self,
350                               GQueue                     *files)
351 {
352     GList *l;
353     GQueue *models;
354 
355     models = g_queue_new ();
356     for (l = g_queue_peek_head_link (files); l != NULL; l = l->next)
357     {
358         NautilusViewItemModel *item_model;
359 
360         item_model = nautilus_view_item_model_new (NAUTILUS_FILE (l->data),
361                                                    get_icon_size_for_zoom_level (self->zoom_level));
362         g_queue_push_tail (models, item_model);
363     }
364 
365     return models;
366 }
367 
368 static void
real_set_selection(NautilusFilesView * files_view,GList * selection)369 real_set_selection (NautilusFilesView *files_view,
370                     GList             *selection)
371 {
372     NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
373     g_autoptr (GQueue) selection_files = NULL;
374     g_autoptr (GQueue) selection_item_models = NULL;
375 
376     selection_files = convert_glist_to_queue (selection);
377     selection_item_models = nautilus_view_model_get_items_from_files (self->model, selection_files);
378     nautilus_view_icon_ui_set_selection (self->view_ui, selection_item_models);
379     nautilus_files_view_notify_selection_changed (files_view);
380 }
381 
382 static void
real_select_all(NautilusFilesView * files_view)383 real_select_all (NautilusFilesView *files_view)
384 {
385     NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
386     gtk_flow_box_select_all (GTK_FLOW_BOX (self->view_ui));
387 }
388 
389 static GtkWidget *
get_first_selected_item_ui(NautilusViewIconController * self)390 get_first_selected_item_ui (NautilusViewIconController *self)
391 {
392     g_autolist (NautilusFile) selection = NULL;
393     NautilusFile *file;
394     NautilusViewItemModel *item_model;
395 
396     selection = nautilus_view_get_selection (NAUTILUS_VIEW (self));
397     if (selection == NULL)
398     {
399         return NULL;
400     }
401 
402     file = NAUTILUS_FILE (selection->data);
403     item_model = nautilus_view_model_get_item_from_file (self->model, file);
404 
405     return nautilus_view_item_model_get_item_ui (item_model);
406 }
407 
408 static void
reveal_item_ui(NautilusViewIconController * self,GtkWidget * item_ui)409 reveal_item_ui (NautilusViewIconController *self,
410                 GtkWidget                  *item_ui)
411 {
412     GtkAllocation allocation;
413     GtkWidget *content_widget;
414     GtkAdjustment *vadjustment;
415     int view_height;
416 
417     gtk_widget_get_allocation (item_ui, &allocation);
418     content_widget = nautilus_files_view_get_content_widget (NAUTILUS_FILES_VIEW (self));
419     view_height = gtk_widget_get_allocated_height (content_widget);
420     vadjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (content_widget));
421 
422     /* Scroll only as necessary. TODO: Would be nice to have this as part of
423      * GtkFlowBox. GtkTreeView has something similar. */
424     if (allocation.y < gtk_adjustment_get_value (vadjustment))
425     {
426         gtk_adjustment_set_value (vadjustment, allocation.y);
427     }
428     else if (allocation.y + allocation.height >
429              gtk_adjustment_get_value (vadjustment) + view_height)
430     {
431         gtk_adjustment_set_value (vadjustment,
432                                   allocation.y + allocation.height - view_height);
433     }
434 }
435 
436 static void
real_reveal_selection(NautilusFilesView * files_view)437 real_reveal_selection (NautilusFilesView *files_view)
438 {
439     NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
440     GtkWidget *item_ui;
441 
442     item_ui = get_first_selected_item_ui (self);
443 
444     if (item_ui != NULL)
445     {
446         reveal_item_ui (self, item_ui);
447     }
448 }
449 
450 static gboolean
showing_recent_directory(NautilusFilesView * view)451 showing_recent_directory (NautilusFilesView *view)
452 {
453     NautilusFile *file;
454 
455     file = nautilus_files_view_get_directory_as_file (view);
456     if (file != NULL)
457     {
458         return nautilus_file_is_in_recent (file);
459     }
460     return FALSE;
461 }
462 
463 static gboolean
showing_search_directory(NautilusFilesView * view)464 showing_search_directory (NautilusFilesView *view)
465 {
466     NautilusFile *file;
467 
468     file = nautilus_files_view_get_directory_as_file (view);
469     if (file != NULL)
470     {
471         return nautilus_file_is_in_search (file);
472     }
473     return FALSE;
474 }
475 
476 static void
real_update_actions_state(NautilusFilesView * files_view)477 real_update_actions_state (NautilusFilesView *files_view)
478 {
479     GAction *action;
480     GActionGroup *view_action_group;
481 
482     NAUTILUS_FILES_VIEW_CLASS (nautilus_view_icon_controller_parent_class)->update_actions_state (files_view);
483 
484     view_action_group = nautilus_files_view_get_action_group (files_view);
485     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group), "sort");
486     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
487                                  !showing_recent_directory (files_view) &&
488                                  !showing_search_directory (files_view));
489 }
490 
491 static void
real_bump_zoom_level(NautilusFilesView * files_view,int zoom_increment)492 real_bump_zoom_level (NautilusFilesView *files_view,
493                       int                zoom_increment)
494 {
495     NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
496     NautilusCanvasZoomLevel new_level;
497 
498     new_level = self->zoom_level + zoom_increment;
499 
500     if (new_level >= NAUTILUS_CANVAS_ZOOM_LEVEL_SMALL &&
501         new_level <= NAUTILUS_CANVAS_ZOOM_LEVEL_LARGEST)
502     {
503         g_action_group_change_action_state (self->action_group,
504                                             "zoom-to-level",
505                                             g_variant_new_int32 (new_level));
506     }
507 }
508 
509 static guint
get_icon_size_for_zoom_level(NautilusCanvasZoomLevel zoom_level)510 get_icon_size_for_zoom_level (NautilusCanvasZoomLevel zoom_level)
511 {
512     switch (zoom_level)
513     {
514         case NAUTILUS_CANVAS_ZOOM_LEVEL_SMALL:
515         {
516             return NAUTILUS_CANVAS_ICON_SIZE_SMALL;
517         }
518         break;
519 
520         case NAUTILUS_CANVAS_ZOOM_LEVEL_STANDARD:
521         {
522             return NAUTILUS_CANVAS_ICON_SIZE_STANDARD;
523         }
524         break;
525 
526         case NAUTILUS_CANVAS_ZOOM_LEVEL_LARGE:
527         {
528             return NAUTILUS_CANVAS_ICON_SIZE_LARGE;
529         }
530         break;
531 
532         case NAUTILUS_CANVAS_ZOOM_LEVEL_LARGER:
533         {
534             return NAUTILUS_CANVAS_ICON_SIZE_LARGER;
535         }
536         break;
537 
538         case NAUTILUS_CANVAS_ZOOM_LEVEL_LARGEST:
539         {
540             return NAUTILUS_CANVAS_ICON_SIZE_LARGEST;
541         }
542         break;
543     }
544     g_return_val_if_reached (NAUTILUS_CANVAS_ICON_SIZE_STANDARD);
545 }
546 
547 static gint
get_default_zoom_level(void)548 get_default_zoom_level (void)
549 {
550     NautilusCanvasZoomLevel default_zoom_level;
551 
552     default_zoom_level = g_settings_get_enum (nautilus_icon_view_preferences,
553                                               NAUTILUS_PREFERENCES_ICON_VIEW_DEFAULT_ZOOM_LEVEL);
554 
555     return default_zoom_level;
556 }
557 
558 static void
set_icon_size(NautilusViewIconController * self,gint icon_size)559 set_icon_size (NautilusViewIconController *self,
560                gint                        icon_size)
561 {
562     NautilusViewItemModel *current_item_model;
563     guint i = 0;
564 
565     while ((current_item_model = NAUTILUS_VIEW_ITEM_MODEL (g_list_model_get_item (G_LIST_MODEL (nautilus_view_model_get_g_model (self->model)), i))))
566     {
567         nautilus_view_item_model_set_icon_size (current_item_model,
568                                                 get_icon_size_for_zoom_level (self->zoom_level));
569         i++;
570     }
571 }
572 
573 static void
set_zoom_level(NautilusViewIconController * self,guint new_level)574 set_zoom_level (NautilusViewIconController *self,
575                 guint                       new_level)
576 {
577     self->zoom_level = new_level;
578 
579     set_icon_size (self, get_icon_size_for_zoom_level (new_level));
580 
581     nautilus_files_view_update_toolbar_menus (NAUTILUS_FILES_VIEW (self));
582 }
583 
584 static void
real_restore_standard_zoom_level(NautilusFilesView * files_view)585 real_restore_standard_zoom_level (NautilusFilesView *files_view)
586 {
587     NautilusViewIconController *self;
588 
589     self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
590     g_action_group_change_action_state (self->action_group,
591                                         "zoom-to-level",
592                                         g_variant_new_int32 (NAUTILUS_CANVAS_ZOOM_LEVEL_LARGE));
593 }
594 
595 static gfloat
real_get_zoom_level_percentage(NautilusFilesView * files_view)596 real_get_zoom_level_percentage (NautilusFilesView *files_view)
597 {
598     NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
599 
600     return (gfloat) get_icon_size_for_zoom_level (self->zoom_level) /
601            NAUTILUS_CANVAS_ICON_SIZE_LARGE;
602 }
603 
604 static gboolean
real_is_zoom_level_default(NautilusFilesView * files_view)605 real_is_zoom_level_default (NautilusFilesView *files_view)
606 {
607     NautilusViewIconController *self;
608     guint icon_size;
609 
610     self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
611     icon_size = get_icon_size_for_zoom_level (self->zoom_level);
612 
613     return icon_size == NAUTILUS_CANVAS_ICON_SIZE_LARGE;
614 }
615 
616 static gboolean
real_can_zoom_in(NautilusFilesView * files_view)617 real_can_zoom_in (NautilusFilesView *files_view)
618 {
619     return TRUE;
620 }
621 
622 static gboolean
real_can_zoom_out(NautilusFilesView * files_view)623 real_can_zoom_out (NautilusFilesView *files_view)
624 {
625     return TRUE;
626 }
627 
628 static GdkRectangle *
get_rectangle_for_item_ui(NautilusViewIconController * self,GtkWidget * item_ui)629 get_rectangle_for_item_ui (NautilusViewIconController *self,
630                            GtkWidget                  *item_ui)
631 {
632     GdkRectangle *rectangle;
633     GtkWidget *content_widget;
634     GtkAdjustment *vadjustment;
635     GtkAdjustment *hadjustment;
636 
637     rectangle = g_new0 (GdkRectangle, 1);
638     gtk_widget_get_allocation (item_ui, rectangle);
639 
640     content_widget = nautilus_files_view_get_content_widget (NAUTILUS_FILES_VIEW (self));
641     vadjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (content_widget));
642     hadjustment = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (content_widget));
643 
644     rectangle->x -= gtk_adjustment_get_value (hadjustment);
645     rectangle->y -= gtk_adjustment_get_value (vadjustment);
646 
647     return rectangle;
648 }
649 
650 static GdkRectangle *
real_compute_rename_popover_pointing_to(NautilusFilesView * files_view)651 real_compute_rename_popover_pointing_to (NautilusFilesView *files_view)
652 {
653     NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
654     GtkWidget *item_ui;
655 
656     /* We only allow one item to be renamed with a popover */
657     item_ui = get_first_selected_item_ui (self);
658     g_return_val_if_fail (item_ui != NULL, NULL);
659 
660     return get_rectangle_for_item_ui (self, item_ui);
661 }
662 
663 static GdkRectangle *
real_reveal_for_selection_context_menu(NautilusFilesView * files_view)664 real_reveal_for_selection_context_menu (NautilusFilesView *files_view)
665 {
666     g_autolist (NautilusFile) selection = NULL;
667     NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
668     GtkWidget *item_ui;
669 
670     selection = nautilus_view_get_selection (NAUTILUS_VIEW (files_view));
671     g_return_val_if_fail (selection != NULL, NULL);
672 
673     /* Get the focused item_ui, if selected.
674      * Otherwise, get the selected item_ui which is sorted the lowest.*/
675     item_ui = gtk_container_get_focus_child (GTK_CONTAINER (self->view_ui));
676     if (item_ui == NULL || !gtk_flow_box_child_is_selected (GTK_FLOW_BOX_CHILD (item_ui)))
677     {
678         g_autoptr (GList) list = gtk_flow_box_get_selected_children (GTK_FLOW_BOX (self->view_ui));
679 
680         list = g_list_last (list);
681         item_ui = GTK_WIDGET (list->data);
682     }
683 
684     reveal_item_ui (self, item_ui);
685 
686     return get_rectangle_for_item_ui (self, item_ui);
687 }
688 
689 static void
real_click_policy_changed(NautilusFilesView * files_view)690 real_click_policy_changed (NautilusFilesView *files_view)
691 {
692 }
693 
694 static void
on_button_press_event(GtkGestureMultiPress * gesture,gint n_press,gdouble x,gdouble y,gpointer user_data)695 on_button_press_event (GtkGestureMultiPress *gesture,
696                        gint                  n_press,
697                        gdouble               x,
698                        gdouble               y,
699                        gpointer              user_data)
700 {
701     NautilusViewIconController *self;
702     guint button;
703     GdkEventSequence *sequence;
704     const GdkEvent *event;
705     g_autolist (NautilusFile) selection = NULL;
706     GtkWidget *child_at_pos;
707 
708     self = NAUTILUS_VIEW_ICON_CONTROLLER (user_data);
709     button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
710     sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
711     event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
712 
713     /* Need to update the selection so the popup has the right actions enabled */
714     selection = nautilus_view_get_selection (NAUTILUS_VIEW (self));
715     child_at_pos = GTK_WIDGET (gtk_flow_box_get_child_at_pos (GTK_FLOW_BOX (self->view_ui),
716                                                               x, y));
717     if (child_at_pos != NULL)
718     {
719         NautilusFile *selected_file;
720         NautilusViewItemModel *item_model;
721 
722         item_model = nautilus_view_icon_item_ui_get_model (NAUTILUS_VIEW_ICON_ITEM_UI (child_at_pos));
723         selected_file = nautilus_view_item_model_get_file (item_model);
724         if (g_list_find (selection, selected_file) == NULL)
725         {
726             g_list_foreach (selection, (GFunc) g_object_unref, NULL);
727             selection = g_list_append (NULL, g_object_ref (selected_file));
728         }
729         else
730         {
731             selection = g_list_prepend (selection, g_object_ref (selected_file));
732         }
733 
734         nautilus_view_set_selection (NAUTILUS_VIEW (self), selection);
735 
736         if (button == GDK_BUTTON_SECONDARY)
737         {
738             nautilus_files_view_pop_up_selection_context_menu (NAUTILUS_FILES_VIEW (self),
739                                                                event);
740         }
741     }
742     else
743     {
744         nautilus_view_set_selection (NAUTILUS_VIEW (self), NULL);
745         if (button == GDK_BUTTON_SECONDARY)
746         {
747             nautilus_files_view_pop_up_background_context_menu (NAUTILUS_FILES_VIEW (self),
748                                                                 event);
749         }
750     }
751 }
752 
753 static void
on_longpress_gesture_pressed_callback(GtkGestureLongPress * gesture,gdouble x,gdouble y,gpointer user_data)754 on_longpress_gesture_pressed_callback (GtkGestureLongPress *gesture,
755                                        gdouble              x,
756                                        gdouble              y,
757                                        gpointer             user_data)
758 {
759     NautilusViewIconController *self;
760     g_autoptr (GList) selection = NULL;
761     GtkWidget *child_at_pos;
762     GdkEventButton *event_button;
763     GdkEventSequence *event_sequence;
764     GdkEvent *event;
765 
766     event_sequence = gtk_gesture_get_last_updated_sequence (GTK_GESTURE (gesture));
767     event = (GdkEvent *) gtk_gesture_get_last_event (GTK_GESTURE (gesture), event_sequence);
768 
769     self = NAUTILUS_VIEW_ICON_CONTROLLER (user_data);
770     event_button = (GdkEventButton *) event;
771 
772     /* Need to update the selection so the popup has the right actions enabled */
773     selection = nautilus_view_get_selection (NAUTILUS_VIEW (self));
774     child_at_pos = GTK_WIDGET (gtk_flow_box_get_child_at_pos (GTK_FLOW_BOX (self->view_ui),
775                                                               event_button->x, event_button->y));
776     if (child_at_pos != NULL)
777     {
778         NautilusFile *selected_file;
779         NautilusViewItemModel *item_model;
780 
781         item_model = nautilus_view_icon_item_ui_get_model (NAUTILUS_VIEW_ICON_ITEM_UI (child_at_pos));
782         selected_file = nautilus_view_item_model_get_file (item_model);
783         if (g_list_find (selection, selected_file) == NULL)
784         {
785             g_list_foreach (selection, (GFunc) g_object_unref, NULL);
786             selection = g_list_append (NULL, g_object_ref (selected_file));
787         }
788         else
789         {
790             selection = g_list_prepend (selection, g_object_ref (selected_file));
791         }
792 
793         nautilus_view_set_selection (NAUTILUS_VIEW (self), selection);
794 
795         nautilus_files_view_pop_up_selection_context_menu (NAUTILUS_FILES_VIEW (self),
796                                                            event);
797     }
798     else
799     {
800         nautilus_view_set_selection (NAUTILUS_VIEW (self), NULL);
801         nautilus_files_view_pop_up_background_context_menu (NAUTILUS_FILES_VIEW (self),
802                                                             event);
803     }
804 
805     g_list_foreach (selection, (GFunc) g_object_unref, NULL);
806 }
807 
808 static int
real_compare_files(NautilusFilesView * files_view,NautilusFile * file1,NautilusFile * file2)809 real_compare_files (NautilusFilesView *files_view,
810                     NautilusFile      *file1,
811                     NautilusFile      *file2)
812 {
813     if (file1 < file2)
814     {
815         return -1;
816     }
817 
818     if (file1 > file2)
819     {
820         return +1;
821     }
822 
823     return 0;
824 }
825 
826 static void
real_end_loading(NautilusFilesView * files_view,gboolean all_files_seen)827 real_end_loading (NautilusFilesView *files_view,
828                   gboolean           all_files_seen)
829 {
830 }
831 
832 static char *
real_get_first_visible_file(NautilusFilesView * files_view)833 real_get_first_visible_file (NautilusFilesView *files_view)
834 {
835     return NULL;
836 }
837 
838 static void
real_scroll_to_file(NautilusFilesView * files_view,const char * uri)839 real_scroll_to_file (NautilusFilesView *files_view,
840                      const char        *uri)
841 {
842 }
843 
844 static void
real_sort_directories_first_changed(NautilusFilesView * files_view)845 real_sort_directories_first_changed (NautilusFilesView *files_view)
846 {
847     NautilusViewModelSortData sort_data;
848     NautilusViewModelSortData *current_sort_data;
849     NautilusViewIconController *self;
850 
851     self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
852     current_sort_data = nautilus_view_model_get_sort_type (self->model);
853     sort_data.sort_type = current_sort_data->sort_type;
854     sort_data.reversed = current_sort_data->reversed;
855     sort_data.directories_first = nautilus_files_view_should_sort_directories_first (NAUTILUS_FILES_VIEW (self));
856 
857     nautilus_view_model_set_sort_type (self->model, &sort_data);
858 }
859 
860 static void
action_sort_order_changed(GSimpleAction * action,GVariant * value,gpointer user_data)861 action_sort_order_changed (GSimpleAction *action,
862                            GVariant      *value,
863                            gpointer       user_data)
864 {
865     const gchar *target_name;
866     const SortConstants *sort_constants;
867     NautilusViewModelSortData sort_data;
868     NautilusViewIconController *self;
869 
870     /* Don't resort if the action is in the same state as before */
871     if (g_strcmp0 (g_variant_get_string (value, NULL), g_variant_get_string (g_action_get_state (G_ACTION (action)), NULL)) == 0)
872     {
873         return;
874     }
875 
876     self = NAUTILUS_VIEW_ICON_CONTROLLER (user_data);
877     target_name = g_variant_get_string (value, NULL);
878     sort_constants = get_sorts_constants_from_action_target_name (target_name);
879     sort_data.sort_type = sort_constants->sort_type;
880     sort_data.reversed = sort_constants->reversed;
881     sort_data.directories_first = nautilus_files_view_should_sort_directories_first (NAUTILUS_FILES_VIEW (self));
882 
883     nautilus_view_model_set_sort_type (self->model, &sort_data);
884     set_directory_sort_metadata (nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (self)),
885                                  sort_constants);
886 
887     g_simple_action_set_state (action, value);
888 }
889 
890 static void
real_add_files(NautilusFilesView * files_view,GList * files)891 real_add_files (NautilusFilesView *files_view,
892                 GList             *files)
893 {
894     NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
895     g_autoptr (GQueue) files_queue = NULL;
896     g_autoptr (GQueue) item_models = NULL;
897 
898     files_queue = convert_glist_to_queue (files);
899     item_models = convert_files_to_item_models (self, files_queue);
900     nautilus_view_model_add_items (self->model, item_models);
901 }
902 
903 
904 static guint
real_get_view_id(NautilusFilesView * files_view)905 real_get_view_id (NautilusFilesView *files_view)
906 {
907     return NAUTILUS_VIEW_GRID_ID;
908 }
909 
910 static GIcon *
real_get_icon(NautilusFilesView * files_view)911 real_get_icon (NautilusFilesView *files_view)
912 {
913     NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
914 
915     return self->view_icon;
916 }
917 
918 static void
real_select_first(NautilusFilesView * files_view)919 real_select_first (NautilusFilesView *files_view)
920 {
921 }
922 
923 static void
real_preview_selection_event(NautilusFilesView * files_view,GtkDirectionType direction)924 real_preview_selection_event (NautilusFilesView *files_view,
925                               GtkDirectionType   direction)
926 {
927     NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view);
928     GtkMovementStep step;
929     gint count;
930     gboolean handled;
931 
932     step = (direction == GTK_DIR_UP || direction == GTK_DIR_DOWN) ?
933            GTK_MOVEMENT_DISPLAY_LINES : GTK_MOVEMENT_VISUAL_POSITIONS;
934     count = (direction == GTK_DIR_RIGHT || direction == GTK_DIR_DOWN) ?
935             1 : -1;
936 
937     g_signal_emit_by_name (self->view_ui, "move-cursor", step, count, &handled);
938 }
939 
940 static void
action_zoom_to_level(GSimpleAction * action,GVariant * state,gpointer user_data)941 action_zoom_to_level (GSimpleAction *action,
942                       GVariant      *state,
943                       gpointer       user_data)
944 {
945     NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (user_data);
946     int zoom_level;
947 
948     zoom_level = g_variant_get_int32 (state);
949     set_zoom_level (self, zoom_level);
950     g_simple_action_set_state (G_SIMPLE_ACTION (action), state);
951 
952     if (g_settings_get_enum (nautilus_icon_view_preferences,
953                              NAUTILUS_PREFERENCES_ICON_VIEW_DEFAULT_ZOOM_LEVEL) != zoom_level)
954     {
955         g_settings_set_enum (nautilus_icon_view_preferences,
956                              NAUTILUS_PREFERENCES_ICON_VIEW_DEFAULT_ZOOM_LEVEL,
957                              zoom_level);
958     }
959 }
960 
961 static void
dispose(GObject * object)962 dispose (GObject *object)
963 {
964     NautilusViewIconController *self;
965 
966     self = NAUTILUS_VIEW_ICON_CONTROLLER (object);
967 
968     g_clear_object (&self->multi_press_gesture);
969 
970     G_OBJECT_CLASS (nautilus_view_icon_controller_parent_class)->dispose (object);
971 }
972 
973 static void
finalize(GObject * object)974 finalize (GObject *object)
975 {
976     G_OBJECT_CLASS (nautilus_view_icon_controller_parent_class)->finalize (object);
977 }
978 
979 
980 const GActionEntry view_icon_actions[] =
981 {
982     { "sort", NULL, "s", "'invalid'", action_sort_order_changed },
983     { "zoom-to-level", NULL, NULL, "100", action_zoom_to_level }
984 };
985 
986 static void
constructed(GObject * object)987 constructed (GObject *object)
988 {
989     NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (object);
990     GtkWidget *content_widget;
991     GtkAdjustment *hadjustment;
992     GtkAdjustment *vadjustment;
993     GActionGroup *view_action_group;
994     GtkGesture *longpress_gesture;
995 
996     content_widget = nautilus_files_view_get_content_widget (NAUTILUS_FILES_VIEW (self));
997     hadjustment = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (content_widget));
998     vadjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (content_widget));
999 
1000     self->model = nautilus_view_model_new ();
1001     self->view_ui = nautilus_view_icon_ui_new (self);
1002     gtk_flow_box_set_hadjustment (GTK_FLOW_BOX (self->view_ui), hadjustment);
1003     gtk_flow_box_set_vadjustment (GTK_FLOW_BOX (self->view_ui), vadjustment);
1004     gtk_widget_show (GTK_WIDGET (self->view_ui));
1005     self->view_icon = g_themed_icon_new ("view-grid-symbolic");
1006 
1007     /* Compensating for the lack of event boxen to allow clicks outside the flow box. */
1008     self->multi_press_gesture = gtk_gesture_multi_press_new (GTK_WIDGET (content_widget));
1009     gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (self->multi_press_gesture),
1010                                                 GTK_PHASE_CAPTURE);
1011     gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (self->multi_press_gesture),
1012                                    0);
1013     g_signal_connect (self->multi_press_gesture, "pressed",
1014                       G_CALLBACK (on_button_press_event), self);
1015 
1016     longpress_gesture = gtk_gesture_long_press_new (GTK_WIDGET (self->view_ui));
1017     gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (longpress_gesture),
1018                                                 GTK_PHASE_CAPTURE);
1019     gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (longpress_gesture),
1020                                        TRUE);
1021     g_signal_connect (longpress_gesture, "pressed",
1022                       (GCallback) on_longpress_gesture_pressed_callback,
1023                       self);
1024 
1025     gtk_container_add (GTK_CONTAINER (content_widget), GTK_WIDGET (self->view_ui));
1026 
1027     self->action_group = nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (self));
1028     g_action_map_add_action_entries (G_ACTION_MAP (self->action_group),
1029                                      view_icon_actions,
1030                                      G_N_ELEMENTS (view_icon_actions),
1031                                      self);
1032 
1033     gtk_widget_show_all (GTK_WIDGET (self));
1034 
1035     view_action_group = nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (self));
1036     g_action_map_add_action_entries (G_ACTION_MAP (view_action_group),
1037                                      view_icon_actions,
1038                                      G_N_ELEMENTS (view_icon_actions),
1039                                      self);
1040     self->zoom_level = get_default_zoom_level ();
1041     /* Keep the action synced with the actual value, so the toolbar can poll it */
1042     g_action_group_change_action_state (nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (self)),
1043                                         "zoom-to-level", g_variant_new_int32 (self->zoom_level));
1044 }
1045 
1046 static void
nautilus_view_icon_controller_class_init(NautilusViewIconControllerClass * klass)1047 nautilus_view_icon_controller_class_init (NautilusViewIconControllerClass *klass)
1048 {
1049     GObjectClass *object_class = G_OBJECT_CLASS (klass);
1050     NautilusFilesViewClass *files_view_class = NAUTILUS_FILES_VIEW_CLASS (klass);
1051 
1052     object_class->dispose = dispose;
1053     object_class->finalize = finalize;
1054     object_class->constructed = constructed;
1055 
1056     files_view_class->add_files = real_add_files;
1057     files_view_class->begin_loading = real_begin_loading;
1058     files_view_class->bump_zoom_level = real_bump_zoom_level;
1059     files_view_class->can_zoom_in = real_can_zoom_in;
1060     files_view_class->can_zoom_out = real_can_zoom_out;
1061     files_view_class->click_policy_changed = real_click_policy_changed;
1062     files_view_class->clear = real_clear;
1063     files_view_class->file_changed = real_file_changed;
1064     files_view_class->get_selection = real_get_selection;
1065     /* TODO: remove this get_selection_for_file_transfer, this doesn't even
1066      * take into account we could us the view for recursive search :/
1067      * CanvasView has the same issue. */
1068     files_view_class->get_selection_for_file_transfer = real_get_selection;
1069     files_view_class->is_empty = real_is_empty;
1070     files_view_class->remove_file = real_remove_file;
1071     files_view_class->update_actions_state = real_update_actions_state;
1072     files_view_class->reveal_selection = real_reveal_selection;
1073     files_view_class->select_all = real_select_all;
1074     files_view_class->set_selection = real_set_selection;
1075     files_view_class->compare_files = real_compare_files;
1076     files_view_class->sort_directories_first_changed = real_sort_directories_first_changed;
1077     files_view_class->end_file_changes = real_end_file_changes;
1078     files_view_class->end_loading = real_end_loading;
1079     files_view_class->get_view_id = real_get_view_id;
1080     files_view_class->get_first_visible_file = real_get_first_visible_file;
1081     files_view_class->scroll_to_file = real_scroll_to_file;
1082     files_view_class->get_icon = real_get_icon;
1083     files_view_class->select_first = real_select_first;
1084     files_view_class->restore_standard_zoom_level = real_restore_standard_zoom_level;
1085     files_view_class->get_zoom_level_percentage = real_get_zoom_level_percentage;
1086     files_view_class->is_zoom_level_default = real_is_zoom_level_default;
1087     files_view_class->compute_rename_popover_pointing_to = real_compute_rename_popover_pointing_to;
1088     files_view_class->reveal_for_selection_context_menu = real_reveal_for_selection_context_menu;
1089     files_view_class->preview_selection_event = real_preview_selection_event;
1090 }
1091 
1092 static void
nautilus_view_icon_controller_init(NautilusViewIconController * self)1093 nautilus_view_icon_controller_init (NautilusViewIconController *self)
1094 {
1095 }
1096 
1097 NautilusViewIconController *
nautilus_view_icon_controller_new(NautilusWindowSlot * slot)1098 nautilus_view_icon_controller_new (NautilusWindowSlot *slot)
1099 {
1100     return g_object_new (NAUTILUS_TYPE_VIEW_ICON_CONTROLLER,
1101                          "window-slot", slot,
1102                          NULL);
1103 }
1104 
1105 NautilusViewModel *
nautilus_view_icon_controller_get_model(NautilusViewIconController * self)1106 nautilus_view_icon_controller_get_model (NautilusViewIconController *self)
1107 {
1108     g_return_val_if_fail (NAUTILUS_IS_VIEW_ICON_CONTROLLER (self), NULL);
1109 
1110     return self->model;
1111 }
1112