1 /* Copyright (C) 2005-2006 Fabio Marzocca <thesaltydog@gmail.com>
2  * Copyright (C) 2012-2021 MATE Developers
3  *
4  * This file is part of MATE Utils.
5  *
6  * MATE Utils 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 2 of the License, or
9  * (at your option) any later version.
10  *
11  * MATE Utils 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 MATE Utils.  If not, see <https://www.gnu.org/licenses/>.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 
24 #include <string.h>
25 #include <sys/stat.h>
26 #include <gtk/gtk.h>
27 #include <glib/gi18n.h>
28 #include <glib/gstdio.h>
29 #include <glibtop/mountlist.h>
30 #include <glibtop/fsusage.h>
31 #include "baobab.h"
32 #include "baobab-utils.h"
33 #include "baobab-prefs.h"
34 
35 #define BAOBAB_PREFERENCES_UI_RESOURCE "/org/mate/disk-usage-analyzer/baobab-dialog-scan-props.ui"
36 #define GET_WIDGET(x) (GTK_WIDGET (gtk_builder_get_object (builder, (x))))
37 
38 enum
39 {
40 	COL_CHECK,
41 	COL_DEVICE,
42 	COL_MOUNT_D,
43 	COL_MOUNT,
44 	COL_TYPE,
45 	COL_FS_SIZE,
46 	COL_FS_AVAIL,
47 	TOT_COLUMNS
48 };
49 
50 static gboolean
add_excluded_item(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,GPtrArray * uris)51 add_excluded_item (GtkTreeModel  *model,
52 		   GtkTreePath   *path,
53 		   GtkTreeIter   *iter,
54 		   GPtrArray     *uris)
55 {
56 	gchar *mount;
57 	gboolean check;
58 
59 	gtk_tree_model_get (model,
60 			    iter,
61 			    COL_MOUNT, &mount,
62 			    COL_CHECK, &check,
63 			    -1);
64 
65 	if (!check) {
66 		g_ptr_array_add (uris, mount);
67 	}
68 	else {
69 		g_free (mount);
70 	}
71 
72 	return FALSE;
73 }
74 
75 static gchar **
get_excluded_locations(GtkTreeModel * model)76 get_excluded_locations (GtkTreeModel *model)
77 {
78 	GPtrArray *uris;
79 
80 	uris = g_ptr_array_new ();
81 
82 	gtk_tree_model_foreach (model,
83 				(GtkTreeModelForeachFunc) add_excluded_item,
84 				uris);
85 
86 	g_ptr_array_add (uris, NULL);
87 
88 	return (gchar **) g_ptr_array_free (uris, FALSE);
89 }
90 
91 static void
save_excluded_uris(GtkTreeModel * model)92 save_excluded_uris (GtkTreeModel *model)
93 {
94 	gchar **uris;
95 
96 	uris = get_excluded_locations (model);
97 
98 	g_settings_set_strv (baobab.prefs_settings,
99 			     BAOBAB_SETTINGS_EXCLUDED_URIS,
100 			     (const gchar * const *) uris);
101 
102 	g_strfreev (uris);
103 }
104 
105 static void
filechooser_response_cb(GtkDialog * dialog,gint response_id,GtkTreeModel * model)106 filechooser_response_cb (GtkDialog *dialog,
107                          gint response_id,
108                          GtkTreeModel *model)
109 {
110 	switch (response_id) {
111 		case GTK_RESPONSE_HELP:
112 			baobab_help_display (GTK_WINDOW (baobab.window),
113 			                     "mate-disk-usage-analyzer", "baobab-preferences");
114 			break;
115 		case GTK_RESPONSE_DELETE_EVENT:
116 		case GTK_RESPONSE_CLOSE:
117 			save_excluded_uris (model);
118 		default:
119 			gtk_widget_destroy (GTK_WIDGET (dialog));
120 			break;
121 	}
122 }
123 
124 static void
check_toggled(GtkCellRendererToggle * cell,gchar * path_str,GtkTreeModel * model)125 check_toggled (GtkCellRendererToggle *cell,
126 	       gchar *path_str,
127 	       GtkTreeModel *model)
128 {
129 	GtkTreeIter iter;
130 	GtkTreePath *path = gtk_tree_path_new_from_string (path_str);
131 	gboolean toggle;
132 	gchar *mountpoint;
133 
134 	/* get toggled iter */
135 	gtk_tree_model_get_iter (model, &iter, path);
136 	gtk_tree_model_get (model,
137 			    &iter,
138 			    COL_CHECK, &toggle,
139 			    COL_MOUNT_D, &mountpoint,
140 			    -1);
141 
142 	/* check if root dir */
143 	if (strcmp ("/", mountpoint) == 0)
144 		goto clean_up;
145 
146 	/* set new value */
147 	gtk_list_store_set (GTK_LIST_STORE (model),
148 			    &iter,
149 			    COL_CHECK, !toggle,
150 			    -1);
151 
152  clean_up:
153 	g_free (mountpoint);
154 	gtk_tree_path_free (path);
155 }
156 
157 static void
create_tree_props(GtkBuilder * builder,GtkTreeModel * model)158 create_tree_props (GtkBuilder *builder, GtkTreeModel *model)
159 {
160 	GtkCellRenderer *cell;
161 	GtkTreeViewColumn *col;
162 	GtkWidget *tvw;
163 
164 	tvw = GET_WIDGET ("tree_view_props");
165 
166 	/* checkbox column */
167 	cell = gtk_cell_renderer_toggle_new ();
168 	g_signal_connect (cell, "toggled",
169 			  G_CALLBACK (check_toggled), model);
170 
171 	col = gtk_tree_view_column_new_with_attributes (_("Scan"), cell,
172 							"active", COL_CHECK,
173 							NULL);
174 	gtk_tree_view_append_column (GTK_TREE_VIEW (tvw), col);
175 
176 	/* First text column */
177 	cell = gtk_cell_renderer_text_new ();
178 	col = gtk_tree_view_column_new_with_attributes (_("Device"), cell,
179 							"markup", COL_DEVICE,
180 							"text", COL_DEVICE,
181 							NULL);
182 	gtk_tree_view_append_column (GTK_TREE_VIEW (tvw), col);
183 
184 	/* second text column */
185 	cell = gtk_cell_renderer_text_new ();
186 	col = gtk_tree_view_column_new_with_attributes (_("Mount Point"),
187 							cell, "markup",
188 							COL_MOUNT_D, "text",
189 							COL_MOUNT_D, NULL);
190 	gtk_tree_view_append_column (GTK_TREE_VIEW (tvw), col);
191 
192 	/* third text column */
193 	cell = gtk_cell_renderer_text_new ();
194 	col = gtk_tree_view_column_new_with_attributes (_("Filesystem Type"),
195 							cell, "markup",
196 							COL_TYPE, "text",
197 							COL_TYPE, NULL);
198 	gtk_tree_view_append_column (GTK_TREE_VIEW (tvw), col);
199 
200 	/* fourth text column */
201 	cell = gtk_cell_renderer_text_new ();
202 	col = gtk_tree_view_column_new_with_attributes (_("Total Size"),
203 							cell, "markup",
204 							COL_FS_SIZE, "text",
205 							COL_FS_SIZE, NULL);
206 	g_object_set (G_OBJECT (cell), "xalign", (gfloat) 1.0, NULL);
207 	gtk_tree_view_append_column (GTK_TREE_VIEW (tvw), col);
208 
209 	/* fifth text column */
210 	cell = gtk_cell_renderer_text_new ();
211 	col = gtk_tree_view_column_new_with_attributes (_("Available"),
212 							cell, "markup",
213 							COL_FS_AVAIL, "text",
214 							COL_FS_AVAIL, NULL);
215 	g_object_set (G_OBJECT (cell), "xalign", (gfloat) 1.0, NULL);
216 	gtk_tree_view_append_column (GTK_TREE_VIEW (tvw), col);
217 
218 	gtk_tree_view_set_model (GTK_TREE_VIEW (tvw), model);
219 	g_object_unref (model);
220 }
221 
222 static void
fill_props_model(GtkListStore * store)223 fill_props_model (GtkListStore *store)
224 {
225 	GtkTreeIter iter;
226 	guint lo;
227 	glibtop_mountlist mountlist;
228 	glibtop_mountentry *mountentry, *mountentry_tofree;
229 	guint64 fstotal, fsavail;
230 
231 	mountentry_tofree = glibtop_get_mountlist (&mountlist, 0);
232 
233 	for (lo = 0, mountentry = mountentry_tofree;
234 	     lo < mountlist.number;
235 	     lo++, mountentry++) {
236 		glibtop_fsusage fsusage;
237 		gchar * total, *avail;
238 		GFile *file;
239 		gchar *uri;
240 		gboolean excluded;
241 
242 		struct stat buf;
243 		if (g_stat (mountentry->devname,&buf) == -1)
244 			continue;
245 
246 		glibtop_get_fsusage (&fsusage, mountentry->mountdir);
247 		fstotal = fsusage.blocks * fsusage.block_size;
248 		fsavail = fsusage.bfree * fsusage.block_size;
249 
250 			total = g_format_size(fstotal);
251 			avail = g_format_size(fsavail);
252 
253 		file = g_file_new_for_path (mountentry->mountdir);
254 		uri = g_file_get_uri (file);
255 		excluded = baobab_is_excluded_location (file);
256 
257 		gtk_list_store_append (store, &iter);
258 		gtk_list_store_set (store, &iter,
259 				    COL_CHECK, !excluded,
260 				    COL_DEVICE, mountentry->devname,
261 				    COL_MOUNT_D, mountentry->mountdir,
262 				    COL_MOUNT, uri,
263 				    COL_TYPE, mountentry->type,
264 				    COL_FS_SIZE, total,
265 				    COL_FS_AVAIL, avail,
266 				    -1);
267 		g_free(total);
268 		g_free(avail);
269 		g_free(uri);
270 		g_object_unref(file);
271 	}
272 
273 	g_free (mountentry_tofree);
274 }
275 
276 void
baobab_prefs_dialog(void)277 baobab_prefs_dialog (void)
278 {
279 	GtkBuilder *builder;
280 	GtkWidget *dlg;
281 	GtkWidget *check_enablehome;
282 	GtkListStore *model;
283 	GError *error = NULL;
284 
285 	builder = gtk_builder_new ();
286 
287 	if (gtk_builder_add_from_resource (builder, BAOBAB_PREFERENCES_UI_RESOURCE, &error) == 0) {
288 		g_critical ("Can't load user interface resource for the scan properties dialog: %s",
289 			    error->message);
290 		g_object_unref (builder);
291 		g_error_free (error);
292 
293 		return;
294 	}
295 
296 	dlg = GET_WIDGET ("dialog_scan_props");
297 
298 	gtk_window_set_transient_for (GTK_WINDOW (dlg),
299 				      GTK_WINDOW (baobab.window));
300 
301 	model = gtk_list_store_new (TOT_COLUMNS,
302 				    G_TYPE_BOOLEAN,	/* checkbox */
303 				    G_TYPE_STRING,	/* device */
304 				    G_TYPE_STRING,	/*mount point display */
305 				    G_TYPE_STRING,	/* mount point uri */
306 				    G_TYPE_STRING,	/* fs type */
307 				    G_TYPE_STRING,	/* fs size */
308 				    G_TYPE_STRING	/* fs avail */
309 				    );
310 
311 	create_tree_props (builder, GTK_TREE_MODEL (model));
312 	fill_props_model (model);
313 
314 	check_enablehome = GET_WIDGET ("check_enable_home");
315 	g_settings_bind (baobab.prefs_settings,
316 			 BAOBAB_SETTINGS_MONITOR_HOME,
317 			 check_enablehome, "active",
318 			 G_SETTINGS_BIND_DEFAULT);
319 
320 	g_signal_connect (dlg, "response",
321 			  G_CALLBACK (filechooser_response_cb), model);
322 
323 	gtk_widget_show_all (dlg);
324 }
325 
326