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