1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 
3 /*
4    nemo-trash-monitor.c: Nemo trash state watcher.
5 
6    Copyright (C) 2000, 2001 Eazel, Inc.
7 
8    This program is free software; you can redistribute it and/or
9    modify it under the terms of the GNU General Public License as
10    published by the Free Software Foundation; either version 2 of the
11    License, or (at your option) any later version.
12 
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    General Public License for more details.
17 
18    You should have received a copy of the GNU General Public
19    License along with this program; if not, write to the
20    Free Software Foundation, Inc., 51 Franklin Street - Suite 500,
21    Boston, MA 02110-1335, USA.
22 
23    Author: Pavel Cisler <pavel@eazel.com>
24 */
25 
26 #include <config.h>
27 #include "nemo-trash-monitor.h"
28 
29 #include "nemo-directory-notify.h"
30 #include "nemo-directory.h"
31 #include "nemo-file-attributes.h"
32 #include "nemo-icon-names.h"
33 #include <eel/eel-debug.h>
34 #include <gio/gio.h>
35 #include <string.h>
36 
37 struct NemoTrashMonitorDetails {
38 	gboolean empty;
39 	GIcon *icon;
40     const gchar *symbolic_icon_name;
41 	GFileMonitor *file_monitor;
42 };
43 
44 enum {
45 	TRASH_STATE_CHANGED,
46 	LAST_SIGNAL
47 };
48 
49 static guint signals[LAST_SIGNAL] = { 0 };
50 static NemoTrashMonitor *nemo_trash_monitor = NULL;
51 
G_DEFINE_TYPE(NemoTrashMonitor,nemo_trash_monitor,G_TYPE_OBJECT)52 G_DEFINE_TYPE(NemoTrashMonitor, nemo_trash_monitor, G_TYPE_OBJECT)
53 
54 static void
55 nemo_trash_monitor_finalize (GObject *object)
56 {
57 	NemoTrashMonitor *trash_monitor;
58 
59 	trash_monitor = NEMO_TRASH_MONITOR (object);
60 
61 	if (trash_monitor->details->icon) {
62 		g_object_unref (trash_monitor->details->icon);
63 	}
64 
65 	if (trash_monitor->details->file_monitor) {
66 		g_object_unref (trash_monitor->details->file_monitor);
67 	}
68 
69 	G_OBJECT_CLASS (nemo_trash_monitor_parent_class)->finalize (object);
70 }
71 
72 static void
nemo_trash_monitor_class_init(NemoTrashMonitorClass * klass)73 nemo_trash_monitor_class_init (NemoTrashMonitorClass *klass)
74 {
75 	GObjectClass *object_class;
76 
77 	object_class = G_OBJECT_CLASS (klass);
78 
79 	object_class->finalize = nemo_trash_monitor_finalize;
80 
81 	signals[TRASH_STATE_CHANGED] = g_signal_new
82 		("trash_state_changed",
83 		 G_TYPE_FROM_CLASS (object_class),
84 		 G_SIGNAL_RUN_LAST,
85 		 G_STRUCT_OFFSET (NemoTrashMonitorClass, trash_state_changed),
86 		 NULL, NULL,
87 		 g_cclosure_marshal_VOID__BOOLEAN,
88 		 G_TYPE_NONE, 1,
89 		 G_TYPE_BOOLEAN);
90 
91 	g_type_class_add_private (object_class, sizeof(NemoTrashMonitorDetails));
92 }
93 
94 static void
update_icon(NemoTrashMonitor * trash_monitor)95 update_icon (NemoTrashMonitor *trash_monitor)
96 {
97 	g_clear_object (&trash_monitor->details->icon);
98 
99 	if (trash_monitor->details->empty) {
100 		trash_monitor->details->icon = g_themed_icon_new (NEMO_ICON_TRASH);
101         trash_monitor->details->symbolic_icon_name = NEMO_ICON_SYMBOLIC_TRASH;
102 	} else {
103 		trash_monitor->details->icon = g_themed_icon_new (NEMO_ICON_TRASH_FULL);
104         trash_monitor->details->symbolic_icon_name = NEMO_ICON_SYMBOLIC_TRASH_FULL;
105 	}
106 }
107 
108 static void
update_empty_info(NemoTrashMonitor * trash_monitor,gboolean is_empty)109 update_empty_info (NemoTrashMonitor *trash_monitor,
110 		   gboolean is_empty)
111 {
112 	if (trash_monitor->details->empty == is_empty) {
113 		return;
114 	}
115 
116 	trash_monitor->details->empty = is_empty;
117 	update_icon (trash_monitor);
118 
119 	/* trash got empty or full, notify everyone who cares */
120 	g_signal_emit (trash_monitor,
121 		       signals[TRASH_STATE_CHANGED], 0,
122 		       trash_monitor->details->empty);
123 }
124 
125 /* Use G_FILE_ATTRIBUTE_TRASH_ITEM_COUNT since we only want to know whether the
126  * trash is empty or not, not access its children. This is available for the
127  * trash backend since it uses a cache. In this way we prevent flooding the
128  * trash backend with enumeration requests when trashing > 1000 files
129  */
130 static void
trash_query_info_cb(GObject * source,GAsyncResult * res,gpointer user_data)131 trash_query_info_cb (GObject *source,
132                      GAsyncResult *res,
133                      gpointer user_data)
134 {
135         NemoTrashMonitor *trash_monitor = user_data;
136         GFileInfo *info;
137         guint32 item_count;
138         gboolean is_empty = TRUE;
139 
140         info = g_file_query_info_finish (G_FILE (source), res, NULL);
141 
142         if (info != NULL) {
143                 item_count = g_file_info_get_attribute_uint32 (info,
144                                                                G_FILE_ATTRIBUTE_TRASH_ITEM_COUNT);
145                 is_empty = item_count == 0;
146 
147                 g_object_unref (info);
148 
149         }
150 
151         update_empty_info (trash_monitor, is_empty);
152 
153         g_object_unref (trash_monitor);
154 }
155 
156 static void
schedule_update_info(NemoTrashMonitor * trash_monitor)157 schedule_update_info (NemoTrashMonitor *trash_monitor)
158 {
159 	GFile *location;
160 
161 	location = g_file_new_for_uri ("trash:///");
162         g_file_query_info_async (location,
163                                  G_FILE_ATTRIBUTE_TRASH_ITEM_COUNT,
164                                  G_FILE_QUERY_INFO_NONE,
165                                  G_PRIORITY_DEFAULT, NULL,
166                                  trash_query_info_cb, g_object_ref (trash_monitor));
167 
168 	g_object_unref (location);
169 }
170 
171 static void
file_changed(GFileMonitor * monitor,GFile * child,GFile * other_file,GFileMonitorEvent event_type,gpointer user_data)172 file_changed (GFileMonitor* monitor,
173 	      GFile *child,
174 	      GFile *other_file,
175 	      GFileMonitorEvent event_type,
176 	      gpointer user_data)
177 {
178 	NemoTrashMonitor *trash_monitor;
179 
180 	trash_monitor = NEMO_TRASH_MONITOR (user_data);
181 
182 	schedule_update_info (trash_monitor);
183 }
184 
185 static void
nemo_trash_monitor_init(NemoTrashMonitor * trash_monitor)186 nemo_trash_monitor_init (NemoTrashMonitor *trash_monitor)
187 {
188 	GFile *location;
189 
190 	trash_monitor->details = G_TYPE_INSTANCE_GET_PRIVATE (trash_monitor,
191 							      NEMO_TYPE_TRASH_MONITOR,
192 							      NemoTrashMonitorDetails);
193 
194 	trash_monitor->details->empty = TRUE;
195 	update_icon (trash_monitor);
196 
197 	location = g_file_new_for_uri ("trash:///");
198 
199 	trash_monitor->details->file_monitor = g_file_monitor_file (location, 0, NULL, NULL);
200 
201 	g_signal_connect (trash_monitor->details->file_monitor, "changed",
202 			  (GCallback)file_changed, trash_monitor);
203 
204 	g_object_unref (location);
205 
206 	schedule_update_info (trash_monitor);
207 }
208 
209 static void
unref_trash_monitor(void)210 unref_trash_monitor (void)
211 {
212 	g_object_unref (nemo_trash_monitor);
213 }
214 
215 NemoTrashMonitor *
nemo_trash_monitor_get(void)216 nemo_trash_monitor_get (void)
217 {
218 	if (nemo_trash_monitor == NULL) {
219 		/* not running yet, start it up */
220 
221 		nemo_trash_monitor = NEMO_TRASH_MONITOR
222 			(g_object_new (NEMO_TYPE_TRASH_MONITOR, NULL));
223 		eel_debug_call_at_shutdown (unref_trash_monitor);
224 	}
225 
226 	return nemo_trash_monitor;
227 }
228 
229 gboolean
nemo_trash_monitor_is_empty(void)230 nemo_trash_monitor_is_empty (void)
231 {
232 	NemoTrashMonitor *monitor;
233 
234 	monitor = nemo_trash_monitor_get ();
235 	return monitor->details->empty;
236 }
237 
238 GIcon *
nemo_trash_monitor_get_icon(void)239 nemo_trash_monitor_get_icon (void)
240 {
241 	NemoTrashMonitor *monitor;
242 
243 	monitor = nemo_trash_monitor_get ();
244 	if (monitor->details->icon) {
245 		return g_object_ref (monitor->details->icon);
246 	}
247 	return NULL;
248 }
249 
250 gchar *
nemo_trash_monitor_get_symbolic_icon_name(void)251 nemo_trash_monitor_get_symbolic_icon_name (void)
252 {
253     NemoTrashMonitor *monitor;
254 
255     monitor = nemo_trash_monitor_get ();
256     if (monitor->details->symbolic_icon_name) {
257         return g_strdup (monitor->details->symbolic_icon_name);
258     }
259     return NULL;
260 }
261 
262 void
nemo_trash_monitor_add_new_trash_directories(void)263 nemo_trash_monitor_add_new_trash_directories (void)
264 {
265 	/* We trashed something... */
266 }
267