1 /*
2 * Photos - access, organize and share your photos on GNOME
3 * Copyright © 2014 Pranav Kant
4 * Copyright © 2014 – 2019 Red Hat, Inc.
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program 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
14 * GNU 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
20
21 #include "config.h"
22
23 #include <glib/gi18n.h>
24
25 #include "photos-base-item.h"
26 #include "photos-delete-notification.h"
27 #include "photos-item-manager.h"
28 #include "photos-notification-manager.h"
29 #include "photos-search-context.h"
30
31
32 struct _PhotosDeleteNotification
33 {
34 GtkGrid parent_instance;
35 GList *items;
36 GtkWidget *ntfctn_mngr;
37 PhotosBaseManager *item_mngr;
38 guint timeout_id;
39 };
40
41 enum
42 {
43 PROP_0,
44 PROP_ITEMS
45 };
46
47
48 G_DEFINE_TYPE (PhotosDeleteNotification, photos_delete_notification, GTK_TYPE_GRID);
49
50
51 enum
52 {
53 DELETE_TIMEOUT = 10 /* s */
54 };
55
56
57 static void
photos_delete_notification_remove_timeout(PhotosDeleteNotification * self)58 photos_delete_notification_remove_timeout (PhotosDeleteNotification *self)
59 {
60 if (self->timeout_id != 0)
61 {
62 g_source_remove (self->timeout_id);
63 self->timeout_id = 0;
64 }
65 }
66
67
68 static void
photos_delete_notification_destroy(PhotosDeleteNotification * self)69 photos_delete_notification_destroy (PhotosDeleteNotification *self)
70 {
71 photos_delete_notification_remove_timeout (self);
72 gtk_widget_destroy (GTK_WIDGET (self));
73 }
74
75
76 static void
photos_delete_notification_item_trash(GObject * source_object,GAsyncResult * res,gpointer user_data)77 photos_delete_notification_item_trash (GObject *source_object, GAsyncResult *res, gpointer user_data)
78 {
79 PhotosBaseItem *item = PHOTOS_BASE_ITEM (source_object);
80
81 {
82 g_autoptr (GError) error = NULL;
83
84 if (!photos_base_item_trash_finish (item, res, &error))
85 {
86 const gchar *uri;
87
88 uri = photos_base_item_get_uri (item);
89 g_warning ("Unable to delete item at %s: %s", uri, error->message);
90 }
91 }
92 }
93
94
95 static void
photos_delete_notification_delete_items(PhotosDeleteNotification * self)96 photos_delete_notification_delete_items (PhotosDeleteNotification *self)
97 {
98 GList *l;
99
100 for (l = self->items; l != NULL; l = l->next)
101 {
102 PhotosBaseItem *item = PHOTOS_BASE_ITEM (l->data);
103 photos_base_item_trash_async (item, NULL, photos_delete_notification_item_trash, NULL);
104 }
105 }
106
107
108 static gboolean
photos_delete_notification_timeout(gpointer user_data)109 photos_delete_notification_timeout (gpointer user_data)
110 {
111 PhotosDeleteNotification *self = PHOTOS_DELETE_NOTIFICATION (user_data);
112
113 self->timeout_id = 0;
114 gtk_widget_destroy (GTK_WIDGET (self));
115 return G_SOURCE_REMOVE;
116 }
117
118
119 static void
photos_delete_notification_undo_clicked(PhotosDeleteNotification * self)120 photos_delete_notification_undo_clicked (PhotosDeleteNotification *self)
121 {
122 GList *l;
123
124 for (l = self->items; l != NULL; l = l->next)
125 {
126 PhotosBaseItem *item = PHOTOS_BASE_ITEM (l->data);
127 photos_item_manager_unhide_item (PHOTOS_ITEM_MANAGER (self->item_mngr), item);
128 }
129
130 g_list_free_full (self->items, g_object_unref);
131 self->items = NULL;
132
133 photos_delete_notification_destroy (self);
134 }
135
136
137 static void
photos_delete_notification_constructed(GObject * object)138 photos_delete_notification_constructed (GObject *object)
139 {
140 PhotosDeleteNotification *self = PHOTOS_DELETE_NOTIFICATION (object);
141 GtkWidget *close;
142 GtkWidget *image;
143 GtkWidget *label;
144 GtkWidget *undo;
145 g_autofree gchar *msg = NULL;
146 guint length;
147
148 G_OBJECT_CLASS (photos_delete_notification_parent_class)->constructed (object);
149
150 length = g_list_length (self->items);
151 if (length == 1)
152 {
153 const gchar *name;
154
155 name = photos_base_item_get_name_with_fallback (PHOTOS_BASE_ITEM (self->items->data));
156 msg = g_strdup_printf (_("“%s” deleted"), name);
157 }
158 else
159 msg = g_strdup_printf (ngettext ("%d item deleted", "%d items deleted", length), length);
160
161 label = gtk_label_new (msg);
162 gtk_widget_set_halign (label, GTK_ALIGN_START);
163 gtk_widget_set_hexpand (label, TRUE);
164 gtk_container_add (GTK_CONTAINER (self), label);
165
166 undo = gtk_button_new_with_label (_("Undo"));
167 gtk_widget_set_valign (undo, GTK_ALIGN_CENTER);
168 gtk_container_add (GTK_CONTAINER (self), undo);
169 g_signal_connect_swapped (undo, "clicked", G_CALLBACK (photos_delete_notification_undo_clicked), self);
170
171 image = gtk_image_new_from_icon_name ("window-close-symbolic", GTK_ICON_SIZE_INVALID);
172 gtk_widget_set_margin_bottom (image, 2);
173 gtk_widget_set_margin_top (image, 2);
174 gtk_image_set_pixel_size (GTK_IMAGE (image), 16);
175
176 close = gtk_button_new ();
177 gtk_widget_set_valign (close, GTK_ALIGN_CENTER);
178 gtk_widget_set_focus_on_click (close, FALSE);
179 gtk_button_set_relief (GTK_BUTTON (close), GTK_RELIEF_NONE);
180 gtk_button_set_image (GTK_BUTTON (close), image);
181 gtk_container_add (GTK_CONTAINER (self), close);
182 g_signal_connect_swapped (close, "clicked", G_CALLBACK (gtk_widget_destroy), self);
183
184 photos_notification_manager_add_notification (PHOTOS_NOTIFICATION_MANAGER (self->ntfctn_mngr),
185 GTK_WIDGET (self));
186
187 self->timeout_id = g_timeout_add_seconds (DELETE_TIMEOUT, photos_delete_notification_timeout, self);
188 }
189
190
191 static void
photos_delete_notification_dispose(GObject * object)192 photos_delete_notification_dispose (GObject *object)
193 {
194 PhotosDeleteNotification *self = PHOTOS_DELETE_NOTIFICATION (object);
195
196 photos_delete_notification_delete_items (self);
197 photos_delete_notification_remove_timeout (self);
198
199 g_list_free_full (self->items, g_object_unref);
200 self->items = NULL;
201
202 g_clear_object (&self->ntfctn_mngr);
203 g_clear_object (&self->item_mngr);
204
205 G_OBJECT_CLASS (photos_delete_notification_parent_class)->dispose (object);
206 }
207
208
209 static void
photos_delete_notification_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)210 photos_delete_notification_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
211 {
212 PhotosDeleteNotification *self = PHOTOS_DELETE_NOTIFICATION (object);
213
214 switch (prop_id)
215 {
216 case PROP_ITEMS:
217 {
218 GList *items;
219
220 items = (GList *) g_value_get_pointer (value);
221 self->items = g_list_copy_deep (items, (GCopyFunc) g_object_ref, NULL);
222 break;
223 }
224
225 default:
226 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
227 break;
228 }
229 }
230
231
232 static void
photos_delete_notification_init(PhotosDeleteNotification * self)233 photos_delete_notification_init (PhotosDeleteNotification *self)
234 {
235 GApplication *app;
236 PhotosSearchContextState *state;
237
238 app = g_application_get_default ();
239 state = photos_search_context_get_state (PHOTOS_SEARCH_CONTEXT (app));
240
241 self->ntfctn_mngr = photos_notification_manager_dup_singleton ();
242 self->item_mngr = g_object_ref (state->item_mngr);
243 }
244
245
246 static void
photos_delete_notification_class_init(PhotosDeleteNotificationClass * class)247 photos_delete_notification_class_init (PhotosDeleteNotificationClass *class)
248 {
249 GObjectClass *object_class = G_OBJECT_CLASS (class);
250
251 object_class->constructed = photos_delete_notification_constructed;
252 object_class->dispose = photos_delete_notification_dispose;
253 object_class->set_property = photos_delete_notification_set_property;
254
255 g_object_class_install_property (object_class,
256 PROP_ITEMS,
257 g_param_spec_pointer ("items",
258 "List of PhotosBaseItems",
259 "List of items that are being deleted",
260 G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
261 }
262
263
264 void
photos_delete_notification_new(GList * items)265 photos_delete_notification_new (GList *items)
266 {
267 g_return_if_fail (items != NULL);
268
269 g_object_new (PHOTOS_TYPE_DELETE_NOTIFICATION,
270 "column-spacing", 12,
271 "items", items,
272 "orientation", GTK_ORIENTATION_HORIZONTAL,
273 NULL);
274 }
275