1 /* nautilus-ui-utilities.c - helper functions for GtkUIManager stuff
2  *
3  *  Copyright (C) 2004 Red Hat, Inc.
4  *
5  *  The Gnome Library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Library General Public License as
7  *  published by the Free Software Foundation; either version 2 of the
8  *  License, or (at your option) any later version.
9  *
10  *  The Gnome Library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Library General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Library General Public
16  *  License along with the Gnome Library; see the file COPYING.LIB.  If not,
17  *  see <http://www.gnu.org/licenses/>.
18  *
19  *  Authors: Alexander Larsson <alexl@redhat.com>
20  */
21 
22 #include <config.h>
23 
24 #include "nautilus-ui-utilities.h"
25 #include "nautilus-icon-info.h"
26 #include "nautilus-application.h"
27 #include <eel/eel-graphic-effects.h>
28 
29 #include <gio/gio.h>
30 #include <gtk/gtk.h>
31 #include <libgd/gd.h>
32 #include <string.h>
33 #include <glib/gi18n.h>
34 
35 /**
36  * nautilus_gmenu_set_from_model:
37  * @target_menu: the #GMenu to be filled
38  * @source_model: (nullable): a #GMenuModel to copy items from
39  *
40  * This will replace the content of @target_menu with a copy of all items from
41  * @source_model.
42  *
43  * If the @source_model is empty (i.e., its item count is 0), or if it is %NULL,
44  * then the @target_menu is left empty.
45  */
46 void
nautilus_gmenu_set_from_model(GMenu * target_menu,GMenuModel * source_model)47 nautilus_gmenu_set_from_model (GMenu      *target_menu,
48                                GMenuModel *source_model)
49 {
50     g_return_if_fail (G_IS_MENU (target_menu));
51     g_return_if_fail (source_model == NULL || G_IS_MENU_MODEL (source_model));
52 
53     /* First, empty the menu... */
54     g_menu_remove_all (target_menu);
55 
56     /* ...then, repopulate it (maybe). */
57     if (source_model != NULL)
58     {
59         gint n_items;
60 
61         n_items = g_menu_model_get_n_items (source_model);
62         for (gint i = 0; i < n_items; i++)
63         {
64             g_autoptr (GMenuItem) item = NULL;
65             item = g_menu_item_new_from_model (source_model, i);
66             g_menu_append_item (target_menu, item);
67         }
68     }
69 }
70 
71 #define NAUTILUS_THUMBNAIL_FRAME_LEFT 3
72 #define NAUTILUS_THUMBNAIL_FRAME_TOP 3
73 #define NAUTILUS_THUMBNAIL_FRAME_RIGHT 3
74 #define NAUTILUS_THUMBNAIL_FRAME_BOTTOM 3
75 
76 void
nautilus_ui_frame_image(GdkPixbuf ** pixbuf)77 nautilus_ui_frame_image (GdkPixbuf **pixbuf)
78 {
79     GtkBorder border;
80     GdkPixbuf *pixbuf_with_frame;
81 
82     border.left = NAUTILUS_THUMBNAIL_FRAME_LEFT;
83     border.top = NAUTILUS_THUMBNAIL_FRAME_TOP;
84     border.right = NAUTILUS_THUMBNAIL_FRAME_RIGHT;
85     border.bottom = NAUTILUS_THUMBNAIL_FRAME_BOTTOM;
86 
87     pixbuf_with_frame = gd_embed_image_in_frame (*pixbuf,
88                                                  "resource:///org/gnome/nautilus/icons/thumbnail_frame.png",
89                                                  &border, &border);
90     g_object_unref (*pixbuf);
91 
92     *pixbuf = pixbuf_with_frame;
93 }
94 
95 static GdkPixbuf *filmholes_left = NULL;
96 static GdkPixbuf *filmholes_right = NULL;
97 
98 static gboolean
ensure_filmholes(void)99 ensure_filmholes (void)
100 {
101     if (filmholes_left == NULL)
102     {
103         filmholes_left = gdk_pixbuf_new_from_resource ("/org/gnome/nautilus/icons/filmholes.png", NULL);
104     }
105     if (filmholes_right == NULL &&
106         filmholes_left != NULL)
107     {
108         filmholes_right = gdk_pixbuf_flip (filmholes_left, TRUE);
109     }
110 
111     return (filmholes_left && filmholes_right);
112 }
113 
114 void
nautilus_ui_frame_video(GdkPixbuf ** pixbuf)115 nautilus_ui_frame_video (GdkPixbuf **pixbuf)
116 {
117     int width, height;
118     int holes_width, holes_height;
119     int i;
120 
121     if (!ensure_filmholes ())
122     {
123         return;
124     }
125 
126     width = gdk_pixbuf_get_width (*pixbuf);
127     height = gdk_pixbuf_get_height (*pixbuf);
128     holes_width = gdk_pixbuf_get_width (filmholes_left);
129     holes_height = gdk_pixbuf_get_height (filmholes_left);
130 
131     for (i = 0; i < height; i += holes_height)
132     {
133         gdk_pixbuf_composite (filmholes_left, *pixbuf, 0, i,
134                               MIN (width, holes_width),
135                               MIN (height - i, holes_height),
136                               0, i, 1, 1, GDK_INTERP_NEAREST, 255);
137     }
138 
139     for (i = 0; i < height; i += holes_height)
140     {
141         gdk_pixbuf_composite (filmholes_right, *pixbuf,
142                               width - holes_width, i,
143                               MIN (width, holes_width),
144                               MIN (height - i, holes_height),
145                               width - holes_width, i,
146                               1, 1, GDK_INTERP_NEAREST, 255);
147     }
148 }
149 
150 gboolean
nautilus_file_date_in_between(guint64 unix_file_time,GDateTime * initial_date,GDateTime * end_date)151 nautilus_file_date_in_between (guint64    unix_file_time,
152                                GDateTime *initial_date,
153                                GDateTime *end_date)
154 {
155     GDateTime *date;
156     gboolean in_between;
157 
158     /* Silently ignore errors */
159     if (unix_file_time == 0)
160     {
161         return FALSE;
162     }
163 
164     date = g_date_time_new_from_unix_local (unix_file_time);
165 
166     /* For the end date, we want to make end_date inclusive,
167      * for that the difference between the start of the day and the in_between
168      * has to be more than -1 day
169      */
170     in_between = g_date_time_difference (date, initial_date) > 0 &&
171                  g_date_time_difference (end_date, date) / G_TIME_SPAN_DAY > -1;
172 
173     g_date_time_unref (date);
174 
175     return in_between;
176 }
177 
178 static const gchar *
get_text_for_days_ago(gint days,gboolean prefix_with_since)179 get_text_for_days_ago (gint     days,
180                        gboolean prefix_with_since)
181 {
182     if (days < 7)
183     {
184         /* days */
185         return prefix_with_since ?
186                ngettext ("Since %d day ago", "Since %d days ago", days) :
187                ngettext ("%d day ago", "%d days ago", days);
188     }
189     if (days < 30)
190     {
191         /* weeks */
192         return prefix_with_since ?
193                ngettext ("Since last week", "Since %d weeks ago", days / 7) :
194                ngettext ("Last week", "%d weeks ago", days / 7);
195     }
196     if (days < 365)
197     {
198         /* months */
199         return prefix_with_since ?
200                ngettext ("Since last month", "Since %d months ago", days / 30) :
201                ngettext ("Last month", "%d months ago", days / 30);
202     }
203 
204     /* years */
205     return prefix_with_since ?
206            ngettext ("Since last year", "Since %d years ago", days / 365) :
207            ngettext ("Last year", "%d years ago", days / 365);
208 }
209 
210 gchar *
get_text_for_date_range(GPtrArray * date_range,gboolean prefix_with_since)211 get_text_for_date_range (GPtrArray *date_range,
212                          gboolean   prefix_with_since)
213 {
214     gint days;
215     gint normalized;
216     GDateTime *initial_date;
217     GDateTime *end_date;
218     gchar *formatted_date;
219     gchar *label;
220 
221     if (!date_range)
222     {
223         return NULL;
224     }
225 
226     initial_date = g_ptr_array_index (date_range, 0);
227     end_date = g_ptr_array_index (date_range, 1);
228     days = g_date_time_difference (end_date, initial_date) / G_TIME_SPAN_DAY;
229     formatted_date = g_date_time_format (initial_date, "%x");
230 
231     if (days < 1)
232     {
233         label = g_strdup (formatted_date);
234     }
235     else
236     {
237         if (days < 7)
238         {
239             /* days */
240             normalized = days;
241         }
242         else if (days < 30)
243         {
244             /* weeks */
245             normalized = days / 7;
246         }
247         else if (days < 365)
248         {
249             /* months */
250             normalized = days / 30;
251         }
252         else
253         {
254             /* years */
255             normalized = days / 365;
256         }
257 
258 #pragma GCC diagnostic push
259 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
260         label = g_strdup_printf (get_text_for_days_ago (days,
261                                                         prefix_with_since),
262                                  normalized);
263 #pragma GCC diagnostic pop
264     }
265 
266     g_free (formatted_date);
267 
268     return label;
269 }
270 
271 GtkDialog *
show_dialog(const gchar * primary_text,const gchar * secondary_text,GtkWindow * parent,GtkMessageType type)272 show_dialog (const gchar    *primary_text,
273              const gchar    *secondary_text,
274              GtkWindow      *parent,
275              GtkMessageType  type)
276 {
277     GtkWidget *dialog;
278 
279     g_return_val_if_fail (parent != NULL, NULL);
280 
281     dialog = gtk_message_dialog_new (parent,
282                                      GTK_DIALOG_MODAL,
283                                      type,
284                                      GTK_BUTTONS_OK,
285                                      "%s", primary_text);
286 
287     gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
288                                               "%s", secondary_text);
289 
290     gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
291 
292     gtk_widget_show (dialog);
293 
294     g_signal_connect (GTK_DIALOG (dialog), "response",
295                       G_CALLBACK (gtk_widget_destroy), NULL);
296 
297     return GTK_DIALOG (dialog);
298 }
299 
300 static void
notify_unmount_done(GMountOperation * op,const gchar * message)301 notify_unmount_done (GMountOperation *op,
302                      const gchar     *message)
303 {
304     NautilusApplication *application;
305     gchar *notification_id;
306 
307     application = nautilus_application_get_default ();
308     notification_id = g_strdup_printf ("nautilus-mount-operation-%p", op);
309     nautilus_application_withdraw_notification (application, notification_id);
310 
311     if (message != NULL)
312     {
313         GNotification *unplug;
314         GIcon *icon;
315         gchar **strings;
316 
317         strings = g_strsplit (message, "\n", 0);
318         icon = g_themed_icon_new ("media-removable-symbolic");
319         unplug = g_notification_new (strings[0]);
320         g_notification_set_body (unplug, strings[1]);
321         g_notification_set_icon (unplug, icon);
322 
323         nautilus_application_send_notification (application, notification_id, unplug);
324         g_object_unref (unplug);
325         g_object_unref (icon);
326         g_strfreev (strings);
327     }
328 
329     g_free (notification_id);
330 }
331 
332 static void
notify_unmount_show(GMountOperation * op,const gchar * message)333 notify_unmount_show (GMountOperation *op,
334                      const gchar     *message)
335 {
336     NautilusApplication *application;
337     GNotification *unmount;
338     gchar *notification_id;
339     GIcon *icon;
340     gchar **strings;
341 
342     application = nautilus_application_get_default ();
343     strings = g_strsplit (message, "\n", 0);
344     icon = g_themed_icon_new ("media-removable");
345 
346     unmount = g_notification_new (strings[0]);
347     g_notification_set_body (unmount, strings[1]);
348     g_notification_set_icon (unmount, icon);
349     g_notification_set_priority (unmount, G_NOTIFICATION_PRIORITY_URGENT);
350 
351     notification_id = g_strdup_printf ("nautilus-mount-operation-%p", op);
352     nautilus_application_send_notification (application, notification_id, unmount);
353     g_object_unref (unmount);
354     g_object_unref (icon);
355     g_strfreev (strings);
356     g_free (notification_id);
357 }
358 
359 void
show_unmount_progress_cb(GMountOperation * op,const gchar * message,gint64 time_left,gint64 bytes_left,gpointer user_data)360 show_unmount_progress_cb (GMountOperation *op,
361                           const gchar     *message,
362                           gint64           time_left,
363                           gint64           bytes_left,
364                           gpointer         user_data)
365 {
366     if (bytes_left == 0)
367     {
368         notify_unmount_done (op, message);
369     }
370     else
371     {
372         notify_unmount_show (op, message);
373     }
374 }
375 
376 void
show_unmount_progress_aborted_cb(GMountOperation * op,gpointer user_data)377 show_unmount_progress_aborted_cb (GMountOperation *op,
378                                   gpointer         user_data)
379 {
380     notify_unmount_done (op, NULL);
381 }
382