1 /*
2 * Nautilus
3 *
4 * Copyright (C) 2011, Red Hat, Inc.
5 *
6 * Nautilus is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
10 *
11 * Nautilus is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
18 *
19 * Author: Cosimo Cecchi <cosimoc@redhat.com>
20 *
21 */
22
23 #include "nautilus-toolbar.h"
24
25 #include <glib/gi18n.h>
26 #include <math.h>
27
28 #include "animation/ide-box-theatric.h"
29 #include "animation/egg-animation.h"
30
31 #include "nautilus-application.h"
32 #include "nautilus-bookmark.h"
33 #include "nautilus-file-operations.h"
34 #include "nautilus-file-undo-manager.h"
35 #include "nautilus-global-preferences.h"
36 #include "nautilus-location-entry.h"
37 #include "nautilus-pathbar.h"
38 #include "nautilus-progress-info-manager.h"
39 #include "nautilus-progress-info-widget.h"
40 #include "nautilus-toolbar-menu-sections.h"
41 #include "nautilus-ui-utilities.h"
42 #include "nautilus-window.h"
43 #include "nautilus-container-max-width.h"
44
45 #define OPERATION_MINIMUM_TIME 2 /*s */
46 #define NEEDS_ATTENTION_ANIMATION_TIMEOUT 2000 /*ms */
47 #define REMOVE_FINISHED_OPERATIONS_TIEMOUT 3 /*s */
48
49 #define ANIMATION_X_GROW 30
50 #define ANIMATION_Y_GROW 30
51
52 /* Just design, context at https://gitlab.gnome.org/GNOME/nautilus/issues/548#note_274131 */
53 #define SWITCHER_MAX_WIDTH 840
54
55 typedef enum
56 {
57 NAUTILUS_NAVIGATION_DIRECTION_NONE,
58 NAUTILUS_NAVIGATION_DIRECTION_BACK,
59 NAUTILUS_NAVIGATION_DIRECTION_FORWARD
60 } NautilusNavigationDirection;
61
62 struct _NautilusToolbar
63 {
64 GtkHeaderBar parent_instance;
65
66 NautilusWindow *window;
67
68 GtkWidget *path_bar_container;
69 GtkWidget *location_entry_container;
70 GtkWidget *search_container;
71 GtkWidget *toolbar_switcher;
72 GtkWidget *toolbar_switcher_container;
73 NautilusContainerMaxWidth *toolbar_switcher_container_max_width;
74 GtkWidget *path_bar;
75 GtkWidget *location_entry;
76
77 gboolean show_location_entry;
78 gboolean location_entry_should_auto_hide;
79
80 guint start_operations_timeout_id;
81 guint remove_finished_operations_timeout_id;
82 guint operations_button_attention_timeout_id;
83
84 GtkWidget *operations_button;
85 GtkWidget *view_button;
86 GtkWidget *view_menu_zoom_section;
87 GtkWidget *view_menu_undo_redo_section;
88 GtkWidget *view_menu_extended_section;
89 GtkWidget *undo_button;
90 GtkWidget *redo_button;
91 GtkWidget *view_toggle_button;
92 GtkWidget *view_toggle_icon;
93
94 GtkWidget *app_menu;
95
96 GtkWidget *operations_popover;
97 GtkWidget *operations_container;
98 GtkWidget *operations_revealer;
99 GtkWidget *operations_icon;
100
101 GtkWidget *forward_button;
102 GtkGesture *forward_button_longpress_gesture;
103 GtkGesture *forward_button_multi_press_gesture;
104
105 GtkWidget *back_button;
106 GtkGesture *back_button_longpress_gesture;
107 GtkGesture *back_button_multi_press_gesture;
108
109 GtkWidget *search_button;
110
111 GtkWidget *location_entry_close_button;
112
113 NautilusProgressInfoManager *progress_manager;
114
115 /* active slot & bindings */
116 NautilusWindowSlot *window_slot;
117 GBinding *icon_binding;
118 GBinding *search_binding;
119 GBinding *tooltip_binding;
120 };
121
122 enum
123 {
124 PROP_WINDOW = 1,
125 PROP_SHOW_LOCATION_ENTRY,
126 PROP_WINDOW_SLOT,
127 PROP_SEARCHING,
128 NUM_PROPERTIES
129 };
130
131 static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
132
133 G_DEFINE_TYPE (NautilusToolbar, nautilus_toolbar, HDY_TYPE_HEADER_BAR);
134
135 static void nautilus_toolbar_set_window_slot_real (NautilusToolbar *self,
136 NautilusWindowSlot *slot);
137 static void update_operations (NautilusToolbar *self);
138
139 static void
toolbar_update_appearance(NautilusToolbar * self)140 toolbar_update_appearance (NautilusToolbar *self)
141 {
142 gboolean show_location_entry;
143
144 show_location_entry = self->show_location_entry ||
145 g_settings_get_boolean (nautilus_preferences,
146 NAUTILUS_PREFERENCES_ALWAYS_USE_LOCATION_ENTRY);
147
148 if (self->window_slot != NULL &&
149 nautilus_window_slot_get_searching (self->window_slot))
150 {
151 gtk_stack_set_visible_child_name (GTK_STACK (self->toolbar_switcher), "search");
152 }
153 else if (show_location_entry)
154 {
155 gtk_stack_set_visible_child_name (GTK_STACK (self->toolbar_switcher), "location");
156 }
157 else
158 {
159 gtk_stack_set_visible_child_name (GTK_STACK (self->toolbar_switcher), "pathbar");
160 }
161 }
162
163 static void
activate_back_or_forward_menu_item(GtkMenuItem * menu_item,NautilusWindowSlot * window_slot,gboolean back)164 activate_back_or_forward_menu_item (GtkMenuItem *menu_item,
165 NautilusWindowSlot *window_slot,
166 gboolean back)
167 {
168 int index;
169
170 g_assert (GTK_IS_MENU_ITEM (menu_item));
171
172 index = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (menu_item), "user_data"));
173
174 nautilus_window_slot_back_or_forward (window_slot, back, index);
175 }
176
177 static void
activate_back_menu_item_callback(GtkMenuItem * menu_item,NautilusToolbar * self)178 activate_back_menu_item_callback (GtkMenuItem *menu_item,
179 NautilusToolbar *self)
180 {
181 activate_back_or_forward_menu_item (menu_item, self->window_slot, TRUE);
182 }
183
184 static void
activate_forward_menu_item_callback(GtkMenuItem * menu_item,NautilusToolbar * self)185 activate_forward_menu_item_callback (GtkMenuItem *menu_item,
186 NautilusToolbar *self)
187 {
188 activate_back_or_forward_menu_item (menu_item, self->window_slot, FALSE);
189 }
190
191 static void
fill_menu(NautilusToolbar * self,GtkWidget * menu,gboolean back)192 fill_menu (NautilusToolbar *self,
193 GtkWidget *menu,
194 gboolean back)
195 {
196 GtkWidget *menu_item;
197 int index;
198 GList *list;
199
200 list = back ? nautilus_window_slot_get_back_history (self->window_slot) :
201 nautilus_window_slot_get_forward_history (self->window_slot);
202
203 index = 0;
204 while (list != NULL)
205 {
206 menu_item = nautilus_bookmark_menu_item_new (NAUTILUS_BOOKMARK (list->data));
207 g_object_set_data (G_OBJECT (menu_item), "user_data", GINT_TO_POINTER (index));
208 gtk_widget_show (GTK_WIDGET (menu_item));
209 g_signal_connect_object (menu_item, "activate",
210 back
211 ? G_CALLBACK (activate_back_menu_item_callback)
212 : G_CALLBACK (activate_forward_menu_item_callback),
213 self, 0);
214
215 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
216 list = g_list_next (list);
217 ++index;
218 }
219 }
220
221 static void
show_menu(NautilusToolbar * self,GtkWidget * widget,const GdkEvent * event)222 show_menu (NautilusToolbar *self,
223 GtkWidget *widget,
224 const GdkEvent *event)
225 {
226 GtkWidget *menu;
227 NautilusNavigationDirection direction;
228
229 menu = gtk_menu_new ();
230
231 direction = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (widget),
232 "nav-direction"));
233
234 switch (direction)
235 {
236 case NAUTILUS_NAVIGATION_DIRECTION_FORWARD:
237 {
238 fill_menu (self, menu, FALSE);
239 }
240 break;
241
242 case NAUTILUS_NAVIGATION_DIRECTION_BACK:
243 {
244 fill_menu (self, menu, TRUE);
245 }
246 break;
247
248 default:
249 {
250 g_assert_not_reached ();
251 }
252 break;
253 }
254
255 gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (self->window), NULL);
256 gtk_menu_popup_at_widget (GTK_MENU (menu), widget,
257 GDK_GRAVITY_SOUTH_WEST,
258 GDK_GRAVITY_NORTH_WEST,
259 event);
260 }
261
262 static void
navigation_button_press_cb(GtkGestureMultiPress * gesture,gint n_press,gdouble x,gdouble y,gpointer user_data)263 navigation_button_press_cb (GtkGestureMultiPress *gesture,
264 gint n_press,
265 gdouble x,
266 gdouble y,
267 gpointer user_data)
268 {
269 NautilusToolbar *self;
270 GtkWidget *widget;
271 GdkEventSequence *sequence;
272 const GdkEvent *event;
273
274 self = NAUTILUS_TOOLBAR (user_data);
275 widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
276 sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
277 event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
278
279 show_menu (self, widget, event);
280 }
281
282 static void
back_button_longpress_cb(GtkGestureLongPress * gesture,double x,double y,gpointer user_data)283 back_button_longpress_cb (GtkGestureLongPress *gesture,
284 double x,
285 double y,
286 gpointer user_data)
287 {
288 NautilusToolbar *self = user_data;
289 GdkEventSequence *sequence;
290 const GdkEvent *event;
291
292 sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
293 event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
294
295 show_menu (self, self->back_button, event);
296 }
297
298 static void
forward_button_longpress_cb(GtkGestureLongPress * gesture,double x,double y,gpointer user_data)299 forward_button_longpress_cb (GtkGestureLongPress *gesture,
300 double x,
301 double y,
302 gpointer user_data)
303 {
304 NautilusToolbar *self = user_data;
305 GdkEventSequence *sequence;
306 const GdkEvent *event;
307
308 sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
309 event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
310
311 show_menu (self, self->forward_button, event);
312 }
313
314 static gboolean
should_show_progress_info(NautilusProgressInfo * info)315 should_show_progress_info (NautilusProgressInfo *info)
316 {
317 return nautilus_progress_info_get_total_elapsed_time (info) +
318 nautilus_progress_info_get_remaining_time (info) > OPERATION_MINIMUM_TIME;
319 }
320
321 static GList *
get_filtered_progress_infos(NautilusToolbar * self)322 get_filtered_progress_infos (NautilusToolbar *self)
323 {
324 GList *l;
325 GList *filtered_progress_infos;
326 GList *progress_infos;
327
328 progress_infos = nautilus_progress_info_manager_get_all_infos (self->progress_manager);
329 filtered_progress_infos = NULL;
330
331 for (l = progress_infos; l != NULL; l = l->next)
332 {
333 if (should_show_progress_info (l->data))
334 {
335 filtered_progress_infos = g_list_append (filtered_progress_infos, l->data);
336 }
337 }
338
339 return filtered_progress_infos;
340 }
341
342 static gboolean
should_hide_operations_button(NautilusToolbar * self)343 should_hide_operations_button (NautilusToolbar *self)
344 {
345 GList *progress_infos;
346 GList *l;
347
348 progress_infos = get_filtered_progress_infos (self);
349
350 for (l = progress_infos; l != NULL; l = l->next)
351 {
352 if (nautilus_progress_info_get_total_elapsed_time (l->data) +
353 nautilus_progress_info_get_remaining_time (l->data) > OPERATION_MINIMUM_TIME &&
354 !nautilus_progress_info_get_is_cancelled (l->data) &&
355 !nautilus_progress_info_get_is_finished (l->data))
356 {
357 return FALSE;
358 }
359 }
360
361 g_list_free (progress_infos);
362
363 return TRUE;
364 }
365
366 static gboolean
on_remove_finished_operations_timeout(NautilusToolbar * self)367 on_remove_finished_operations_timeout (NautilusToolbar *self)
368 {
369 nautilus_progress_info_manager_remove_finished_or_cancelled_infos (self->progress_manager);
370 if (should_hide_operations_button (self))
371 {
372 gtk_revealer_set_reveal_child (GTK_REVEALER (self->operations_revealer),
373 FALSE);
374 }
375 else
376 {
377 update_operations (self);
378 }
379
380 self->remove_finished_operations_timeout_id = 0;
381
382 return G_SOURCE_REMOVE;
383 }
384
385 static void
unschedule_remove_finished_operations(NautilusToolbar * self)386 unschedule_remove_finished_operations (NautilusToolbar *self)
387 {
388 if (self->remove_finished_operations_timeout_id != 0)
389 {
390 g_source_remove (self->remove_finished_operations_timeout_id);
391 self->remove_finished_operations_timeout_id = 0;
392 }
393 }
394
395 static void
schedule_remove_finished_operations(NautilusToolbar * self)396 schedule_remove_finished_operations (NautilusToolbar *self)
397 {
398 if (self->remove_finished_operations_timeout_id == 0)
399 {
400 self->remove_finished_operations_timeout_id =
401 g_timeout_add_seconds (REMOVE_FINISHED_OPERATIONS_TIEMOUT,
402 (GSourceFunc) on_remove_finished_operations_timeout,
403 self);
404 }
405 }
406
407 static void
remove_operations_button_attention_style(NautilusToolbar * self)408 remove_operations_button_attention_style (NautilusToolbar *self)
409 {
410 GtkStyleContext *style_context;
411
412 style_context = gtk_widget_get_style_context (self->operations_button);
413 gtk_style_context_remove_class (style_context,
414 "nautilus-operations-button-needs-attention");
415 }
416
417 static gboolean
on_remove_operations_button_attention_style_timeout(NautilusToolbar * self)418 on_remove_operations_button_attention_style_timeout (NautilusToolbar *self)
419 {
420 remove_operations_button_attention_style (self);
421 self->operations_button_attention_timeout_id = 0;
422
423 return G_SOURCE_REMOVE;
424 }
425
426 static void
unschedule_operations_button_attention_style(NautilusToolbar * self)427 unschedule_operations_button_attention_style (NautilusToolbar *self)
428 {
429 if (self->operations_button_attention_timeout_id != 0)
430 {
431 g_source_remove (self->operations_button_attention_timeout_id);
432 self->operations_button_attention_timeout_id = 0;
433 }
434 }
435
436 static void
add_operations_button_attention_style(NautilusToolbar * self)437 add_operations_button_attention_style (NautilusToolbar *self)
438 {
439 GtkStyleContext *style_context;
440
441 style_context = gtk_widget_get_style_context (self->operations_button);
442
443 unschedule_operations_button_attention_style (self);
444 remove_operations_button_attention_style (self);
445
446 gtk_style_context_add_class (style_context,
447 "nautilus-operations-button-needs-attention");
448 self->operations_button_attention_timeout_id = g_timeout_add (NEEDS_ATTENTION_ANIMATION_TIMEOUT,
449 (GSourceFunc) on_remove_operations_button_attention_style_timeout,
450 self);
451 }
452
453 static void
on_progress_info_cancelled(NautilusToolbar * self)454 on_progress_info_cancelled (NautilusToolbar *self)
455 {
456 /* Update the pie chart progress */
457 gtk_widget_queue_draw (self->operations_icon);
458
459 if (!nautilus_progress_manager_has_viewers (self->progress_manager))
460 {
461 schedule_remove_finished_operations (self);
462 }
463 }
464
465 static void
on_progress_info_progress_changed(NautilusToolbar * self)466 on_progress_info_progress_changed (NautilusToolbar *self)
467 {
468 /* Update the pie chart progress */
469 gtk_widget_queue_draw (self->operations_icon);
470 }
471
472 static void
on_progress_info_finished(NautilusToolbar * self,NautilusProgressInfo * info)473 on_progress_info_finished (NautilusToolbar *self,
474 NautilusProgressInfo *info)
475 {
476 gchar *main_label;
477 GFile *folder_to_open;
478
479 /* Update the pie chart progress */
480 gtk_widget_queue_draw (self->operations_icon);
481
482 if (!nautilus_progress_manager_has_viewers (self->progress_manager))
483 {
484 schedule_remove_finished_operations (self);
485 }
486
487 folder_to_open = nautilus_progress_info_get_destination (info);
488 /* If destination is null, don't show a notification. This happens when the
489 * operation is a trash operation, which we already show a diferent kind of
490 * notification */
491 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->operations_button)) &&
492 folder_to_open != NULL)
493 {
494 add_operations_button_attention_style (self);
495 main_label = nautilus_progress_info_get_status (info);
496 nautilus_window_show_operation_notification (self->window,
497 main_label,
498 folder_to_open);
499 g_free (main_label);
500 }
501
502 g_clear_object (&folder_to_open);
503 }
504
505 static void
disconnect_progress_infos(NautilusToolbar * self)506 disconnect_progress_infos (NautilusToolbar *self)
507 {
508 GList *progress_infos;
509 GList *l;
510
511 progress_infos = nautilus_progress_info_manager_get_all_infos (self->progress_manager);
512 for (l = progress_infos; l != NULL; l = l->next)
513 {
514 g_signal_handlers_disconnect_by_data (l->data, self);
515 }
516 }
517
518 static void
update_operations(NautilusToolbar * self)519 update_operations (NautilusToolbar *self)
520 {
521 GList *progress_infos;
522 GList *l;
523 GtkWidget *progress;
524 gboolean should_show_progress_button = FALSE;
525
526 gtk_container_foreach (GTK_CONTAINER (self->operations_container),
527 (GtkCallback) gtk_widget_destroy,
528 NULL);
529
530 disconnect_progress_infos (self);
531
532 progress_infos = get_filtered_progress_infos (self);
533 for (l = progress_infos; l != NULL; l = l->next)
534 {
535 should_show_progress_button = should_show_progress_button ||
536 should_show_progress_info (l->data);
537
538 g_signal_connect_swapped (l->data, "finished",
539 G_CALLBACK (on_progress_info_finished), self);
540 g_signal_connect_swapped (l->data, "cancelled",
541 G_CALLBACK (on_progress_info_cancelled), self);
542 g_signal_connect_swapped (l->data, "progress-changed",
543 G_CALLBACK (on_progress_info_progress_changed), self);
544 progress = nautilus_progress_info_widget_new (l->data);
545 gtk_box_pack_start (GTK_BOX (self->operations_container),
546 progress,
547 FALSE, FALSE, 0);
548 }
549
550 g_list_free (progress_infos);
551
552 if (should_show_progress_button &&
553 !gtk_revealer_get_reveal_child (GTK_REVEALER (self->operations_revealer)))
554 {
555 add_operations_button_attention_style (self);
556 gtk_revealer_set_reveal_child (GTK_REVEALER (self->operations_revealer),
557 TRUE);
558 gtk_widget_queue_draw (self->operations_icon);
559
560 /* Show the popover at start to increase visibility.
561 * Check whether the toolbar is visible or not before showing the
562 * popover. This can happens if the window has the disables-chrome
563 * property set. */
564 if (gtk_widget_is_visible (GTK_WIDGET (self)))
565 {
566 GtkAllocation rect;
567 IdeBoxTheatric *theatric;
568
569 gtk_widget_get_allocation (GTK_WIDGET (self->operations_button), &rect);
570 theatric = g_object_new (IDE_TYPE_BOX_THEATRIC,
571 "alpha", 0.9,
572 "background", "#fdfdfd",
573 "target", self->operations_button,
574 "height", rect.height,
575 "width", rect.width,
576 "x", rect.x,
577 "y", rect.y,
578 NULL);
579
580 egg_object_animate_full (theatric,
581 EGG_ANIMATION_EASE_IN_CUBIC,
582 250,
583 gtk_widget_get_frame_clock (GTK_WIDGET (self->operations_button)),
584 g_object_unref,
585 theatric,
586 "x", rect.x - ANIMATION_X_GROW,
587 "width", rect.width + (ANIMATION_X_GROW * 2),
588 "y", rect.y - ANIMATION_Y_GROW,
589 "height", rect.height + (ANIMATION_Y_GROW * 2),
590 "alpha", 0.0,
591 NULL);
592 }
593 }
594
595 /* Since we removed the info widgets, we need to restore the focus */
596 if (gtk_widget_get_visible (self->operations_popover))
597 {
598 gtk_widget_grab_focus (self->operations_popover);
599 }
600 }
601
602 static gboolean
on_progress_info_started_timeout(NautilusToolbar * self)603 on_progress_info_started_timeout (NautilusToolbar *self)
604 {
605 GList *progress_infos;
606 GList *filtered_progress_infos;
607
608 update_operations (self);
609
610 /* In case we didn't show the operations button because the operation total
611 * time stimation is not good enough, update again to make sure we don't miss
612 * a long time operation because of that */
613
614 progress_infos = nautilus_progress_info_manager_get_all_infos (self->progress_manager);
615 filtered_progress_infos = get_filtered_progress_infos (self);
616 if (!nautilus_progress_manager_are_all_infos_finished_or_cancelled (self->progress_manager) &&
617 g_list_length (progress_infos) != g_list_length (filtered_progress_infos))
618 {
619 g_list_free (filtered_progress_infos);
620 return G_SOURCE_CONTINUE;
621 }
622 else
623 {
624 g_list_free (filtered_progress_infos);
625 self->start_operations_timeout_id = 0;
626 return G_SOURCE_REMOVE;
627 }
628 }
629
630 static void
schedule_operations_start(NautilusToolbar * self)631 schedule_operations_start (NautilusToolbar *self)
632 {
633 if (self->start_operations_timeout_id == 0)
634 {
635 /* Timeout is a little more than what we require for a stimated operation
636 * total time, to make sure the stimated total time is correct */
637 self->start_operations_timeout_id =
638 g_timeout_add (SECONDS_NEEDED_FOR_APROXIMATE_TRANSFER_RATE * 1000 + 500,
639 (GSourceFunc) on_progress_info_started_timeout,
640 self);
641 }
642 }
643
644 static void
unschedule_operations_start(NautilusToolbar * self)645 unschedule_operations_start (NautilusToolbar *self)
646 {
647 if (self->start_operations_timeout_id != 0)
648 {
649 g_source_remove (self->start_operations_timeout_id);
650 self->start_operations_timeout_id = 0;
651 }
652 }
653
654 static void
on_progress_info_started(NautilusProgressInfo * info,NautilusToolbar * self)655 on_progress_info_started (NautilusProgressInfo *info,
656 NautilusToolbar *self)
657 {
658 g_signal_handlers_disconnect_by_data (info, self);
659 schedule_operations_start (self);
660 }
661
662 static void
on_new_progress_info(NautilusProgressInfoManager * manager,NautilusProgressInfo * info,NautilusToolbar * self)663 on_new_progress_info (NautilusProgressInfoManager *manager,
664 NautilusProgressInfo *info,
665 NautilusToolbar *self)
666 {
667 g_signal_connect (info, "started",
668 G_CALLBACK (on_progress_info_started), self);
669 }
670
671 static void
on_operations_icon_draw(GtkWidget * widget,cairo_t * cr,NautilusToolbar * self)672 on_operations_icon_draw (GtkWidget *widget,
673 cairo_t *cr,
674 NautilusToolbar *self)
675 {
676 gfloat elapsed_progress = 0;
677 gint remaining_progress = 0;
678 gint total_progress;
679 gdouble ratio;
680 GList *progress_infos;
681 GList *l;
682 guint width;
683 guint height;
684 gboolean all_cancelled;
685 GdkRGBA background;
686 GdkRGBA foreground;
687 GtkStyleContext *style_context;
688
689 style_context = gtk_widget_get_style_context (widget);
690 gtk_style_context_get_color (style_context, gtk_widget_get_state_flags (widget), &foreground);
691 background = foreground;
692 background.alpha *= 0.3;
693
694 all_cancelled = TRUE;
695 progress_infos = get_filtered_progress_infos (self);
696 for (l = progress_infos; l != NULL; l = l->next)
697 {
698 if (!nautilus_progress_info_get_is_cancelled (l->data))
699 {
700 all_cancelled = FALSE;
701 remaining_progress += nautilus_progress_info_get_remaining_time (l->data);
702 elapsed_progress += nautilus_progress_info_get_elapsed_time (l->data);
703 }
704 }
705
706 g_list_free (progress_infos);
707
708 total_progress = remaining_progress + elapsed_progress;
709
710 if (all_cancelled)
711 {
712 ratio = 1.0;
713 }
714 else
715 {
716 if (total_progress > 0)
717 {
718 ratio = MAX (0.05, elapsed_progress / total_progress);
719 }
720 else
721 {
722 ratio = 0.05;
723 }
724 }
725
726
727 width = gtk_widget_get_allocated_width (widget);
728 height = gtk_widget_get_allocated_height (widget);
729
730 gdk_cairo_set_source_rgba (cr, &background);
731 cairo_arc (cr,
732 width / 2.0, height / 2.0,
733 MIN (width, height) / 2.0,
734 0, 2 * G_PI);
735 cairo_fill (cr);
736 cairo_move_to (cr, width / 2.0, height / 2.0);
737 gdk_cairo_set_source_rgba (cr, &foreground);
738 cairo_arc (cr,
739 width / 2.0, height / 2.0,
740 MIN (width, height) / 2.0,
741 -G_PI / 2.0, ratio * 2 * G_PI - G_PI / 2.0);
742
743 cairo_fill (cr);
744 }
745
746 static void
on_operations_button_toggled(NautilusToolbar * self,GtkToggleButton * button)747 on_operations_button_toggled (NautilusToolbar *self,
748 GtkToggleButton *button)
749 {
750 if (gtk_toggle_button_get_active (button))
751 {
752 unschedule_remove_finished_operations (self);
753 nautilus_progress_manager_add_viewer (self->progress_manager,
754 G_OBJECT (self));
755 }
756 else
757 {
758 nautilus_progress_manager_remove_viewer (self->progress_manager,
759 G_OBJECT (self));
760 }
761 }
762
763 static void
on_progress_has_viewers_changed(NautilusProgressInfoManager * manager,NautilusToolbar * self)764 on_progress_has_viewers_changed (NautilusProgressInfoManager *manager,
765 NautilusToolbar *self)
766 {
767 if (nautilus_progress_manager_has_viewers (manager))
768 {
769 unschedule_remove_finished_operations (self);
770 return;
771 }
772
773 if (nautilus_progress_manager_are_all_infos_finished_or_cancelled (manager))
774 {
775 unschedule_remove_finished_operations (self);
776 schedule_remove_finished_operations (self);
777 }
778 }
779
780 static void
update_menu_item(GtkWidget * menu_item,NautilusToolbar * self,const char * action_name,gboolean enabled,char * label)781 update_menu_item (GtkWidget *menu_item,
782 NautilusToolbar *self,
783 const char *action_name,
784 gboolean enabled,
785 char *label)
786 {
787 GAction *action;
788 GValue val = G_VALUE_INIT;
789
790 /* Activate/deactivate */
791 action = g_action_map_lookup_action (G_ACTION_MAP (self->window), action_name);
792 g_simple_action_set_enabled (G_SIMPLE_ACTION (action), enabled);
793
794 /* Set the text of the menu item. Can't use gtk_button_set_label here as
795 * we need to set the text property, not the label. There's no equivalent
796 * gtk_model_button_set_text function (refer to #766083 for discussion
797 * on adding a set_text function)
798 */
799 g_value_init (&val, G_TYPE_STRING);
800 g_value_set_string (&val, label);
801 g_object_set_property (G_OBJECT (menu_item), "text", &val);
802 g_value_unset (&val);
803 }
804
805 static void
undo_manager_changed(NautilusToolbar * self)806 undo_manager_changed (NautilusToolbar *self)
807 {
808 NautilusFileUndoInfo *info;
809 NautilusFileUndoManagerState undo_state;
810 gboolean undo_active;
811 gboolean redo_active;
812 g_autofree gchar *undo_label = NULL;
813 g_autofree gchar *redo_label = NULL;
814 g_autofree gchar *undo_description = NULL;
815 g_autofree gchar *redo_description = NULL;
816 gboolean is_undo;
817
818 /* Look up the last action from the undo manager, and get the text that
819 * describes it, e.g. "Undo Create Folder"/"Redo Create Folder"
820 */
821 info = nautilus_file_undo_manager_get_action ();
822 undo_state = nautilus_file_undo_manager_get_state ();
823 undo_active = redo_active = FALSE;
824 if (info != NULL && undo_state > NAUTILUS_FILE_UNDO_MANAGER_STATE_NONE)
825 {
826 is_undo = undo_state == NAUTILUS_FILE_UNDO_MANAGER_STATE_UNDO;
827
828 /* The last action can either be undone/redone. Activate the corresponding
829 * menu item and deactivate the other
830 */
831 undo_active = is_undo;
832 redo_active = !is_undo;
833 nautilus_file_undo_info_get_strings (info, &undo_label, &undo_description,
834 &redo_label, &redo_description);
835 }
836
837 /* Set the label of the undo and redo menu items, and activate them appropriately
838 */
839 if (!undo_active || undo_label == NULL)
840 {
841 g_free (undo_label);
842 undo_label = g_strdup (_("_Undo"));
843 }
844 update_menu_item (self->undo_button, self, "undo", undo_active, undo_label);
845
846 if (!redo_active || redo_label == NULL)
847 {
848 g_free (redo_label);
849 redo_label = g_strdup (_("_Redo"));
850 }
851 update_menu_item (self->redo_button, self, "redo", redo_active, redo_label);
852 }
853
854 static void
on_location_entry_close(GtkWidget * close_button,NautilusToolbar * self)855 on_location_entry_close (GtkWidget *close_button,
856 NautilusToolbar *self)
857 {
858 nautilus_toolbar_set_show_location_entry (self, FALSE);
859 }
860
861 static gboolean
on_location_entry_populate_popup(GtkEntry * entry,GtkWidget * widget,gpointer user_data)862 on_location_entry_populate_popup (GtkEntry *entry,
863 GtkWidget *widget,
864 gpointer user_data)
865 {
866 NautilusToolbar *toolbar;
867
868 toolbar = user_data;
869
870 toolbar->location_entry_should_auto_hide = FALSE;
871
872 return GDK_EVENT_PROPAGATE;
873 }
874
875 static void
on_location_entry_focus_changed(GObject * object,GParamSpec * pspec,gpointer user_data)876 on_location_entry_focus_changed (GObject *object,
877 GParamSpec *pspec,
878 gpointer user_data)
879 {
880 NautilusToolbar *toolbar;
881
882 toolbar = NAUTILUS_TOOLBAR (user_data);
883
884 if (gtk_widget_has_focus (GTK_WIDGET (object)))
885 {
886 toolbar->location_entry_should_auto_hide = TRUE;
887 }
888 else if (toolbar->location_entry_should_auto_hide)
889 {
890 nautilus_toolbar_set_show_location_entry (toolbar, FALSE);
891 }
892 }
893
894 static void
nautilus_toolbar_constructed(GObject * object)895 nautilus_toolbar_constructed (GObject *object)
896 {
897 g_autoptr (GtkBuilder) builder = NULL;
898 NautilusToolbar *self = NAUTILUS_TOOLBAR (object);
899
900 builder = gtk_builder_new_from_resource ("/org/gnome/nautilus/ui/nautilus-toolbar-switcher.ui");
901 self->toolbar_switcher = GTK_WIDGET (gtk_builder_get_object (builder, "toolbar_switcher"));
902 self->search_container = GTK_WIDGET (gtk_builder_get_object (builder, "search_container"));
903 self->path_bar_container = GTK_WIDGET (gtk_builder_get_object (builder, "path_bar_container"));
904 self->location_entry_container = GTK_WIDGET (gtk_builder_get_object (builder, "location_entry_container"));
905
906 self->toolbar_switcher_container_max_width = nautilus_container_max_width_new ();
907 nautilus_container_max_width_set_max_width (self->toolbar_switcher_container_max_width,
908 SWITCHER_MAX_WIDTH);
909 gtk_container_add (GTK_CONTAINER (self->toolbar_switcher_container_max_width),
910 self->toolbar_switcher);
911 gtk_container_add (GTK_CONTAINER (self->toolbar_switcher_container),
912 GTK_WIDGET (self->toolbar_switcher_container_max_width));
913
914 self->path_bar = g_object_new (NAUTILUS_TYPE_PATH_BAR, NULL);
915 gtk_container_add (GTK_CONTAINER (self->path_bar_container),
916 self->path_bar);
917
918 self->location_entry = nautilus_location_entry_new ();
919 gtk_container_add (GTK_CONTAINER (self->location_entry_container),
920 self->location_entry);
921 self->location_entry_close_button = gtk_button_new_from_icon_name ("window-close-symbolic",
922 GTK_ICON_SIZE_BUTTON);
923 gtk_container_add (GTK_CONTAINER (self->location_entry_container),
924 self->location_entry_close_button);
925 g_signal_connect (self->location_entry_close_button, "clicked",
926 G_CALLBACK (on_location_entry_close), self);
927
928 self->progress_manager = nautilus_progress_info_manager_dup_singleton ();
929 g_signal_connect (self->progress_manager, "new-progress-info",
930 G_CALLBACK (on_new_progress_info), self);
931 g_signal_connect (self->progress_manager, "has-viewers-changed",
932 G_CALLBACK (on_progress_has_viewers_changed), self);
933
934 update_operations (self);
935
936 self->back_button_longpress_gesture = gtk_gesture_long_press_new (self->back_button);
937 g_signal_connect (self->back_button_longpress_gesture, "pressed",
938 G_CALLBACK (back_button_longpress_cb), self);
939
940 self->forward_button_longpress_gesture = gtk_gesture_long_press_new (self->forward_button);
941 g_signal_connect (self->forward_button_longpress_gesture, "pressed",
942 G_CALLBACK (forward_button_longpress_cb), self);
943
944 g_object_set_data (G_OBJECT (self->back_button), "nav-direction",
945 GUINT_TO_POINTER (NAUTILUS_NAVIGATION_DIRECTION_BACK));
946 g_object_set_data (G_OBJECT (self->forward_button), "nav-direction",
947 GUINT_TO_POINTER (NAUTILUS_NAVIGATION_DIRECTION_FORWARD));
948
949
950 self->back_button_multi_press_gesture = gtk_gesture_multi_press_new (self->back_button);
951 gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (self->back_button_multi_press_gesture),
952 GDK_BUTTON_SECONDARY);
953 g_signal_connect (self->back_button_multi_press_gesture, "pressed",
954 G_CALLBACK (navigation_button_press_cb), self);
955
956 self->forward_button_multi_press_gesture = gtk_gesture_multi_press_new (self->forward_button);
957 gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (self->forward_button_multi_press_gesture),
958 GDK_BUTTON_SECONDARY);
959 g_signal_connect (self->forward_button_multi_press_gesture, "pressed",
960 G_CALLBACK (navigation_button_press_cb), self);
961
962 g_signal_connect (self->operations_popover, "show",
963 (GCallback) gtk_widget_grab_focus, NULL);
964 g_signal_connect_swapped (self->operations_popover, "closed",
965 (GCallback) gtk_widget_grab_focus, self);
966 g_signal_connect (self->location_entry, "populate-popup",
967 G_CALLBACK (on_location_entry_populate_popup), self);
968 g_signal_connect (self->location_entry, "notify::has-focus",
969 G_CALLBACK (on_location_entry_focus_changed), self);
970
971 gtk_widget_show_all (GTK_WIDGET (self));
972 toolbar_update_appearance (self);
973 }
974
975 static void
nautilus_toolbar_init(NautilusToolbar * self)976 nautilus_toolbar_init (NautilusToolbar *self)
977 {
978 gtk_widget_init_template (GTK_WIDGET (self));
979 }
980
981 void
nautilus_toolbar_on_window_constructed(NautilusToolbar * self)982 nautilus_toolbar_on_window_constructed (NautilusToolbar *self)
983 {
984 /* undo_manager_changed manipulates the window actions, so set it up
985 * after the window and it's actions have been constructed
986 */
987 g_signal_connect_object (nautilus_file_undo_manager_get (),
988 "undo-changed",
989 G_CALLBACK (undo_manager_changed),
990 self,
991 G_CONNECT_SWAPPED);
992
993 undo_manager_changed (self);
994 }
995
996 static void
nautilus_toolbar_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)997 nautilus_toolbar_get_property (GObject *object,
998 guint property_id,
999 GValue *value,
1000 GParamSpec *pspec)
1001 {
1002 NautilusToolbar *self = NAUTILUS_TOOLBAR (object);
1003
1004 switch (property_id)
1005 {
1006 case PROP_SHOW_LOCATION_ENTRY:
1007 {
1008 g_value_set_boolean (value, self->show_location_entry);
1009 }
1010 break;
1011
1012 case PROP_SEARCHING:
1013 {
1014 g_value_set_boolean (value, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->search_button)));
1015 }
1016 break;
1017
1018 default:
1019 {
1020 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1021 }
1022 break;
1023 }
1024 }
1025
1026 static void
on_window_slot_destroyed(gpointer data,GObject * where_the_object_was)1027 on_window_slot_destroyed (gpointer data,
1028 GObject *where_the_object_was)
1029 {
1030 NautilusToolbar *self;
1031
1032 self = NAUTILUS_TOOLBAR (data);
1033
1034 /* The window slot was finalized, and the binding has already been removed.
1035 * Null it here, so that dispose() does not trip over itself when removing it.
1036 */
1037 self->icon_binding = NULL;
1038 self->search_binding = NULL;
1039
1040 nautilus_toolbar_set_window_slot_real (self, NULL);
1041 }
1042
1043 static void
on_window_focus_changed(GObject * object,GParamSpec * pspec,gpointer user_data)1044 on_window_focus_changed (GObject *object,
1045 GParamSpec *pspec,
1046 gpointer user_data)
1047 {
1048 GtkWidget *widget;
1049 NautilusToolbar *toolbar;
1050
1051 widget = GTK_WIDGET (object);
1052 toolbar = NAUTILUS_TOOLBAR (user_data);
1053
1054 if (g_settings_get_boolean (nautilus_preferences,
1055 NAUTILUS_PREFERENCES_ALWAYS_USE_LOCATION_ENTRY))
1056 {
1057 return;
1058 }
1059
1060 /* The working assumption being made here is, if the location entry is visible,
1061 * the user must have switched windows while having keyboard focus on the entry
1062 * (because otherwise it would be invisible),
1063 * so we focus the entry explicitly to reset the “should auto-hide” flag.
1064 */
1065 if (gtk_widget_has_focus (widget) && toolbar->show_location_entry)
1066 {
1067 gtk_widget_grab_focus (toolbar->location_entry);
1068 }
1069 /* The location entry in general is hidden when it loses focus,
1070 * but hiding it when switching windows could be undesirable, as the user
1071 * might want to copy a path from somewhere. This here prevents that from happening.
1072 */
1073 else
1074 {
1075 toolbar->location_entry_should_auto_hide = FALSE;
1076 }
1077 }
1078
1079 static void
nautilus_toolbar_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)1080 nautilus_toolbar_set_property (GObject *object,
1081 guint property_id,
1082 const GValue *value,
1083 GParamSpec *pspec)
1084 {
1085 NautilusToolbar *self = NAUTILUS_TOOLBAR (object);
1086
1087 switch (property_id)
1088 {
1089 case PROP_WINDOW:
1090 {
1091 if (self->window != NULL)
1092 {
1093 g_signal_handlers_disconnect_by_func (self->window,
1094 on_window_focus_changed, self);
1095 }
1096 self->window = g_value_get_object (value);
1097 if (self->window != NULL)
1098 {
1099 g_signal_connect (self->window, "notify::has-focus",
1100 G_CALLBACK (on_window_focus_changed), self);
1101 }
1102 }
1103 break;
1104
1105 case PROP_SHOW_LOCATION_ENTRY:
1106 {
1107 nautilus_toolbar_set_show_location_entry (self, g_value_get_boolean (value));
1108 }
1109 break;
1110
1111 case PROP_WINDOW_SLOT:
1112 {
1113 nautilus_toolbar_set_window_slot (self, g_value_get_object (value));
1114 }
1115 break;
1116
1117 case PROP_SEARCHING:
1118 {
1119 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->search_button),
1120 g_value_get_boolean (value));
1121 }
1122 break;
1123
1124 default:
1125 {
1126 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1127 }
1128 break;
1129 }
1130 }
1131
1132 static void
nautilus_toolbar_dispose(GObject * object)1133 nautilus_toolbar_dispose (GObject *object)
1134 {
1135 NautilusToolbar *self;
1136
1137 self = NAUTILUS_TOOLBAR (object);
1138
1139 g_clear_object (&self->forward_button_multi_press_gesture);
1140 g_clear_object (&self->back_button_multi_press_gesture);
1141 g_clear_pointer (&self->icon_binding, g_binding_unbind);
1142 g_clear_pointer (&self->search_binding, g_binding_unbind);
1143
1144 G_OBJECT_CLASS (nautilus_toolbar_parent_class)->dispose (object);
1145 }
1146
1147 static void
nautilus_toolbar_finalize(GObject * obj)1148 nautilus_toolbar_finalize (GObject *obj)
1149 {
1150 NautilusToolbar *self = NAUTILUS_TOOLBAR (obj);
1151
1152 g_signal_handlers_disconnect_by_func (nautilus_preferences,
1153 toolbar_update_appearance, self);
1154
1155 if (self->window_slot != NULL)
1156 {
1157 g_signal_handlers_disconnect_by_data (self->window_slot, self);
1158 g_object_weak_unref (G_OBJECT (self->window_slot),
1159 on_window_slot_destroyed, self);
1160 self->window_slot = NULL;
1161 }
1162 disconnect_progress_infos (self);
1163 unschedule_remove_finished_operations (self);
1164 unschedule_operations_start (self);
1165 unschedule_operations_button_attention_style (self);
1166
1167 g_signal_handlers_disconnect_by_data (self->progress_manager, self);
1168 g_clear_object (&self->progress_manager);
1169
1170 g_signal_handlers_disconnect_by_func (self->window,
1171 on_window_focus_changed, self);
1172
1173 g_clear_object (&self->back_button_longpress_gesture);
1174 g_clear_object (&self->forward_button_longpress_gesture);
1175
1176 G_OBJECT_CLASS (nautilus_toolbar_parent_class)->finalize (obj);
1177 }
1178
1179 static void
nautilus_toolbar_class_init(NautilusToolbarClass * klass)1180 nautilus_toolbar_class_init (NautilusToolbarClass *klass)
1181 {
1182 GObjectClass *oclass;
1183 GtkWidgetClass *widget_class;
1184
1185 widget_class = GTK_WIDGET_CLASS (klass);
1186 oclass = G_OBJECT_CLASS (klass);
1187 oclass->get_property = nautilus_toolbar_get_property;
1188 oclass->set_property = nautilus_toolbar_set_property;
1189 oclass->dispose = nautilus_toolbar_dispose;
1190 oclass->finalize = nautilus_toolbar_finalize;
1191 oclass->constructed = nautilus_toolbar_constructed;
1192
1193 properties[PROP_WINDOW] =
1194 g_param_spec_object ("window",
1195 "The NautilusWindow",
1196 "The NautilusWindow this toolbar is part of",
1197 NAUTILUS_TYPE_WINDOW,
1198 G_PARAM_WRITABLE |
1199 G_PARAM_STATIC_STRINGS);
1200 properties[PROP_SHOW_LOCATION_ENTRY] =
1201 g_param_spec_boolean ("show-location-entry",
1202 "Whether to show the location entry",
1203 "Whether to show the location entry instead of the pathbar",
1204 FALSE,
1205 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1206
1207 properties [PROP_WINDOW_SLOT] =
1208 g_param_spec_object ("window-slot",
1209 "Window slot currently active",
1210 "Window slot currently acive",
1211 NAUTILUS_TYPE_WINDOW_SLOT,
1212 (G_PARAM_READWRITE |
1213 G_PARAM_STATIC_STRINGS));
1214
1215 properties [PROP_SEARCHING] =
1216 g_param_spec_boolean ("searching",
1217 "Current view is searching",
1218 "Whether the current view is searching or not",
1219 FALSE,
1220 G_PARAM_READWRITE);
1221
1222 g_object_class_install_properties (oclass, NUM_PROPERTIES, properties);
1223
1224 gtk_widget_class_set_template_from_resource (widget_class,
1225 "/org/gnome/nautilus/ui/nautilus-toolbar.ui");
1226
1227 gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, operations_button);
1228 gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, operations_icon);
1229 gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, operations_popover);
1230 gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, operations_container);
1231 gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, operations_revealer);
1232 gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, view_button);
1233 gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, view_toggle_button);
1234 gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, view_toggle_icon);
1235 gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, app_menu);
1236 gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, back_button);
1237 gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, forward_button);
1238 gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, toolbar_switcher_container);
1239
1240 gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, view_menu_zoom_section);
1241 gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, view_menu_undo_redo_section);
1242 gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, view_menu_extended_section);
1243 gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, undo_button);
1244 gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, redo_button);
1245
1246 gtk_widget_class_bind_template_child (widget_class, NautilusToolbar, search_button);
1247
1248 gtk_widget_class_bind_template_callback (widget_class, on_operations_icon_draw);
1249 gtk_widget_class_bind_template_callback (widget_class, on_operations_button_toggled);
1250 }
1251
1252 GtkWidget *
nautilus_toolbar_new()1253 nautilus_toolbar_new ()
1254 {
1255 return g_object_new (NAUTILUS_TYPE_TOOLBAR,
1256 NULL);
1257 }
1258
1259 GtkWidget *
nautilus_toolbar_get_path_bar(NautilusToolbar * self)1260 nautilus_toolbar_get_path_bar (NautilusToolbar *self)
1261 {
1262 return self->path_bar;
1263 }
1264
1265 GtkWidget *
nautilus_toolbar_get_location_entry(NautilusToolbar * self)1266 nautilus_toolbar_get_location_entry (NautilusToolbar *self)
1267 {
1268 return self->location_entry;
1269 }
1270
1271 void
nautilus_toolbar_set_show_location_entry(NautilusToolbar * self,gboolean show_location_entry)1272 nautilus_toolbar_set_show_location_entry (NautilusToolbar *self,
1273 gboolean show_location_entry)
1274 {
1275 if (show_location_entry != self->show_location_entry)
1276 {
1277 self->show_location_entry = show_location_entry;
1278 toolbar_update_appearance (self);
1279
1280 g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SHOW_LOCATION_ENTRY]);
1281 }
1282 }
1283
1284 static void
container_remove_all_children(GtkContainer * container)1285 container_remove_all_children (GtkContainer *container)
1286 {
1287 GList *children;
1288 GList *child;
1289
1290 children = gtk_container_get_children (container);
1291 for (child = children; child != NULL; child = g_list_next (child))
1292 {
1293 gtk_container_remove (container, GTK_WIDGET (child->data));
1294 }
1295 g_list_free (children);
1296 }
1297
1298 static void
slot_on_extensions_background_menu_changed(NautilusToolbar * self,GParamSpec * param,NautilusWindowSlot * slot)1299 slot_on_extensions_background_menu_changed (NautilusToolbar *self,
1300 GParamSpec *param,
1301 NautilusWindowSlot *slot)
1302 {
1303 g_autoptr (GMenuModel) menu = NULL;
1304
1305 menu = nautilus_window_slot_get_extensions_background_menu (slot);
1306 nautilus_path_bar_set_extensions_background_menu (NAUTILUS_PATH_BAR (self->path_bar),
1307 menu);
1308 }
1309
1310 static void
slot_on_templates_menu_changed(NautilusToolbar * self,GParamSpec * param,NautilusWindowSlot * slot)1311 slot_on_templates_menu_changed (NautilusToolbar *self,
1312 GParamSpec *param,
1313 NautilusWindowSlot *slot)
1314 {
1315 g_autoptr (GMenuModel) menu = NULL;
1316
1317 menu = nautilus_window_slot_get_templates_menu (slot);
1318 nautilus_path_bar_set_templates_menu (NAUTILUS_PATH_BAR (self->path_bar),
1319 menu);
1320 }
1321
1322 static void
on_slot_toolbar_menu_sections_changed(NautilusToolbar * self,GParamSpec * param,NautilusWindowSlot * slot)1323 on_slot_toolbar_menu_sections_changed (NautilusToolbar *self,
1324 GParamSpec *param,
1325 NautilusWindowSlot *slot)
1326 {
1327 NautilusToolbarMenuSections *new_sections;
1328
1329 container_remove_all_children (GTK_CONTAINER (self->view_menu_zoom_section));
1330 container_remove_all_children (GTK_CONTAINER (self->view_menu_extended_section));
1331
1332 new_sections = nautilus_window_slot_get_toolbar_menu_sections (slot);
1333 if (new_sections == NULL)
1334 {
1335 return;
1336 }
1337
1338 gtk_widget_set_visible (self->view_menu_undo_redo_section,
1339 new_sections->supports_undo_redo);
1340
1341 if (new_sections->zoom_section != NULL)
1342 {
1343 gtk_box_pack_start (GTK_BOX (self->view_menu_zoom_section),
1344 new_sections->zoom_section, FALSE, FALSE, 0);
1345 }
1346
1347 if (new_sections->extended_section != NULL)
1348 {
1349 gtk_box_pack_start (GTK_BOX (self->view_menu_extended_section),
1350 new_sections->extended_section, FALSE, FALSE, 0);
1351 }
1352
1353 gtk_widget_set_sensitive (self->view_button, (new_sections->extended_section != NULL ||
1354 new_sections->zoom_section != NULL ||
1355 new_sections->supports_undo_redo));
1356 }
1357
1358
1359 static void
disconnect_toolbar_menu_sections_change_handler(NautilusToolbar * self)1360 disconnect_toolbar_menu_sections_change_handler (NautilusToolbar *self)
1361 {
1362 if (self->window_slot == NULL)
1363 {
1364 return;
1365 }
1366
1367 g_signal_handlers_disconnect_by_func (self->window_slot,
1368 G_CALLBACK (on_slot_toolbar_menu_sections_changed),
1369 self);
1370 }
1371
1372 static gboolean
nautilus_toolbar_view_toggle_icon_transform_to(GBinding * binding,const GValue * from_value,GValue * to_value,gpointer user_data)1373 nautilus_toolbar_view_toggle_icon_transform_to (GBinding *binding,
1374 const GValue *from_value,
1375 GValue *to_value,
1376 gpointer user_data)
1377 {
1378 GIcon *icon;
1379
1380 icon = g_value_get_object (from_value);
1381
1382 /* As per design decision, we let the previous used icon if no
1383 * view menu is available */
1384 if (icon)
1385 {
1386 g_value_set_object (to_value, icon);
1387 }
1388
1389 return TRUE;
1390 }
1391
1392 static gboolean
nautilus_toolbar_view_toggle_tooltip_transform_to(GBinding * binding,const GValue * from_value,GValue * to_value,gpointer user_data)1393 nautilus_toolbar_view_toggle_tooltip_transform_to (GBinding *binding,
1394 const GValue *from_value,
1395 GValue *to_value,
1396 gpointer user_data)
1397 {
1398 const gchar *tooltip;
1399
1400 tooltip = g_value_get_string (from_value);
1401
1402 /* As per design decision, we let the previous used tooltip if no
1403 * view menu is available */
1404 if (tooltip)
1405 {
1406 g_value_set_string (to_value, tooltip);
1407 }
1408
1409 return TRUE;
1410 }
1411
1412 /* Called from on_window_slot_destroyed(), since bindings and signal handlers
1413 * are automatically removed once the slot goes away.
1414 */
1415 static void
nautilus_toolbar_set_window_slot_real(NautilusToolbar * self,NautilusWindowSlot * slot)1416 nautilus_toolbar_set_window_slot_real (NautilusToolbar *self,
1417 NautilusWindowSlot *slot)
1418 {
1419 g_autoptr (GList) children = NULL;
1420
1421 self->window_slot = slot;
1422
1423 if (self->window_slot != NULL)
1424 {
1425 g_object_weak_ref (G_OBJECT (self->window_slot),
1426 on_window_slot_destroyed,
1427 self);
1428
1429 self->icon_binding = g_object_bind_property_full (self->window_slot, "icon",
1430 self->view_toggle_icon, "gicon",
1431 G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE,
1432 (GBindingTransformFunc) nautilus_toolbar_view_toggle_icon_transform_to,
1433 NULL,
1434 self,
1435 NULL);
1436
1437 self->tooltip_binding = g_object_bind_property_full (self->window_slot, "tooltip",
1438 self->view_toggle_button, "tooltip-text",
1439 G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE,
1440 (GBindingTransformFunc) nautilus_toolbar_view_toggle_tooltip_transform_to,
1441 NULL,
1442 self,
1443 NULL);
1444
1445 self->search_binding = g_object_bind_property (self->window_slot, "searching",
1446 self, "searching",
1447 G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
1448
1449 on_slot_toolbar_menu_sections_changed (self, NULL, self->window_slot);
1450 g_signal_connect_swapped (self->window_slot, "notify::toolbar-menu-sections",
1451 G_CALLBACK (on_slot_toolbar_menu_sections_changed), self);
1452 g_signal_connect_swapped (self->window_slot, "notify::extensions-background-menu",
1453 G_CALLBACK (slot_on_extensions_background_menu_changed), self);
1454 g_signal_connect_swapped (self->window_slot, "notify::templates-menu",
1455 G_CALLBACK (slot_on_templates_menu_changed), self);
1456 g_signal_connect_swapped (self->window_slot, "notify::searching",
1457 G_CALLBACK (toolbar_update_appearance), self);
1458 }
1459
1460 children = gtk_container_get_children (GTK_CONTAINER (self->search_container));
1461 if (children != NULL)
1462 {
1463 gtk_container_remove (GTK_CONTAINER (self->search_container),
1464 children->data);
1465 }
1466
1467 if (self->window_slot != NULL)
1468 {
1469 gtk_container_add (GTK_CONTAINER (self->search_container),
1470 GTK_WIDGET (nautilus_window_slot_get_query_editor (self->window_slot)));
1471 }
1472
1473 toolbar_update_appearance (self);
1474
1475 g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_WINDOW_SLOT]);
1476 g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SEARCHING]);
1477 }
1478
1479 void
nautilus_toolbar_set_window_slot(NautilusToolbar * self,NautilusWindowSlot * window_slot)1480 nautilus_toolbar_set_window_slot (NautilusToolbar *self,
1481 NautilusWindowSlot *window_slot)
1482 {
1483 g_return_if_fail (NAUTILUS_IS_TOOLBAR (self));
1484 g_return_if_fail (window_slot == NULL || NAUTILUS_IS_WINDOW_SLOT (window_slot));
1485
1486 if (self->window_slot == window_slot)
1487 {
1488 return;
1489 }
1490
1491 g_clear_pointer (&self->icon_binding, g_binding_unbind);
1492 g_clear_pointer (&self->search_binding, g_binding_unbind);
1493
1494 disconnect_toolbar_menu_sections_change_handler (self);
1495 if (self->window_slot != NULL)
1496 {
1497 g_signal_handlers_disconnect_by_data (self->window_slot, self);
1498 g_object_weak_unref (G_OBJECT (self->window_slot),
1499 on_window_slot_destroyed, self);
1500 }
1501
1502 nautilus_toolbar_set_window_slot_real (self, window_slot);
1503 }
1504
1505 gboolean
nautilus_toolbar_is_menu_visible(NautilusToolbar * self)1506 nautilus_toolbar_is_menu_visible (NautilusToolbar *self)
1507 {
1508 g_return_val_if_fail (NAUTILUS_IS_TOOLBAR (self), FALSE);
1509
1510 return gtk_widget_is_visible (self->app_menu);
1511 }
1512
1513 gboolean
nautilus_toolbar_is_operations_button_active(NautilusToolbar * self)1514 nautilus_toolbar_is_operations_button_active (NautilusToolbar *self)
1515 {
1516 return gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->operations_button));
1517 }
1518