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 <gtk/gtk.h>
25 #include <glib.h>
26 #include <glib/gprintf.h>
27 #include <glib/gi18n.h>
28 #include <string.h>
29
30 #include "baobab.h"
31 #include "baobab-treeview.h"
32 #include "baobab-cell-renderer-progress.h"
33 #include "baobab-utils.h"
34 #include "callbacks.h"
35
36 #define GET_WIDGET(x) (GTK_WIDGET (gtk_builder_get_object (baobab.main_ui, (x))))
37
38 static GtkTreeStore *
create_model(void)39 create_model (void)
40 {
41 GtkTreeStore *mdl = gtk_tree_store_new (NUM_TREE_COLUMNS,
42 G_TYPE_STRING, /* COL_DIR_NAME */
43 G_TYPE_STRING, /* COL_H_PARSENAME */
44 G_TYPE_DOUBLE, /* COL_H_PERC */
45 G_TYPE_STRING, /* COL_DIR_SIZE */
46 G_TYPE_UINT64, /* COL_H_SIZE */
47 G_TYPE_UINT64, /* COL_H_ALLOCSIZE */
48 G_TYPE_STRING, /* COL_ELEMENTS */
49 G_TYPE_INT, /* COL_H_ELEMENTS */
50 G_TYPE_STRING, /* COL_HARDLINK */
51 G_TYPE_UINT64 /* COL_H_HARDLINK */
52 );
53
54 return mdl;
55 }
56
57 static void
on_tv_row_expanded(GtkTreeView * treeview,GtkTreeIter * arg1,GtkTreePath * arg2,gpointer data)58 on_tv_row_expanded (GtkTreeView *treeview,
59 GtkTreeIter *arg1,
60 GtkTreePath *arg2,
61 gpointer data)
62 {
63 gtk_tree_view_columns_autosize (treeview);
64 }
65
66 static void
on_tv_cur_changed(GtkTreeView * treeview,gpointer data)67 on_tv_cur_changed (GtkTreeView *treeview, gpointer data)
68 {
69 GtkTreeIter iter;
70 gchar *parsename = NULL;
71
72 gtk_tree_selection_get_selected (gtk_tree_view_get_selection (treeview), NULL, &iter);
73
74 if (gtk_tree_store_iter_is_valid (baobab.model, &iter)) {
75 gtk_tree_model_get (GTK_TREE_MODEL (baobab.model), &iter,
76 COL_H_PARSENAME, &parsename, -1);
77 }
78 }
79
80 static void
contents_changed(void)81 contents_changed (void)
82 {
83 if (messageyesno (_("Rescan your home folder?"),
84 _("The content of your home folder has changed. Select rescan to update the disk usage details."),
85 GTK_MESSAGE_QUESTION, _("_Rescan"), baobab.window) == GTK_RESPONSE_OK) {
86 baobab_rescan_current_dir ();
87 }
88 else {
89 /* Just update the total */
90 baobab_update_filesystem ();
91 }
92 }
93
94 static gboolean
on_tv_button_press(GtkWidget * widget,GdkEventButton * event,gpointer data)95 on_tv_button_press (GtkWidget *widget,
96 GdkEventButton *event,
97 gpointer data)
98 {
99 GtkTreePath *path;
100 GtkTreeIter iter;
101 GFile *file;
102
103 gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
104 (gint) event->x, (gint) event->y,
105 &path, NULL, NULL, NULL);
106 if (!path)
107 return TRUE;
108
109 /* get the selected path */
110 g_free (baobab.selected_path);
111 gtk_tree_model_get_iter (GTK_TREE_MODEL (baobab.model), &iter,
112 path);
113 gtk_tree_model_get (GTK_TREE_MODEL (baobab.model), &iter,
114 COL_H_PARSENAME, &baobab.selected_path, -1);
115
116 file = g_file_parse_name (baobab.selected_path);
117
118 if (baobab.CONTENTS_CHANGED_DELAYED) {
119 GFile *home_file;
120
121 home_file = g_file_new_for_path (g_get_home_dir ());
122 if (g_file_has_prefix (file, home_file)) {
123 baobab.CONTENTS_CHANGED_DELAYED = FALSE;
124 if (baobab.STOP_SCANNING) {
125 contents_changed ();
126 }
127 }
128 g_object_unref (home_file);
129 }
130
131 /* right-click */
132 if (event->button == 3) {
133
134 if (g_file_query_exists (file, NULL)) {
135 popupmenu_list (path, event, can_trash_file (file));
136 }
137 }
138
139 gtk_tree_path_free (path);
140 g_object_unref (file);
141
142 return FALSE;
143 }
144
145 static gboolean
baobab_treeview_equal_func(GtkTreeModel * model,gint column,const gchar * key,GtkTreeIter * iter,gpointer data)146 baobab_treeview_equal_func (GtkTreeModel *model,
147 gint column,
148 const gchar *key,
149 GtkTreeIter *iter,
150 gpointer data)
151 {
152 gboolean results = TRUE;
153 gchar *name;
154
155 gtk_tree_model_get (model, iter, 1, &name, -1);
156
157 if (name != NULL) {
158 gchar * casefold_key;
159 gchar * casefold_name;
160
161 casefold_key = g_utf8_casefold (key, -1);
162 casefold_name = g_utf8_casefold (name, -1);
163
164 if ((casefold_key != NULL) &&
165 (casefold_name != NULL) &&
166 (strstr (casefold_name, casefold_key) != NULL)) {
167 results = FALSE;
168 }
169 g_free (casefold_key);
170 g_free (casefold_name);
171 g_free (name);
172 }
173 return results;
174 }
175
176 static void
perc_cell_data_func(GtkTreeViewColumn * col,GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,gpointer user_data)177 perc_cell_data_func (GtkTreeViewColumn *col,
178 GtkCellRenderer *renderer,
179 GtkTreeModel *model,
180 GtkTreeIter *iter,
181 gpointer user_data)
182 {
183 gdouble perc;
184 gchar textperc[10];
185
186 gtk_tree_model_get (model, iter, COL_H_PERC, &perc, -1);
187
188 if (perc < 0)
189 strcpy (textperc, "-.- %");
190 else if (perc == 100.0)
191 strcpy (textperc, "100 %");
192 else
193 g_sprintf (textperc, " %.1f %%", perc);
194
195 g_object_set (renderer, "text", textperc, NULL);
196 }
197
198 GtkWidget *
create_directory_treeview(void)199 create_directory_treeview (void)
200 {
201 GtkCellRenderer *cell;
202 GtkTreeViewColumn *col;
203
204 GtkWidget *tvw = GET_WIDGET ("treeview1");
205
206 g_signal_connect (tvw, "row-expanded",
207 G_CALLBACK (on_tv_row_expanded), NULL);
208 g_signal_connect (tvw, "cursor-changed",
209 G_CALLBACK (on_tv_cur_changed), NULL);
210 g_signal_connect (tvw, "button-press-event",
211 G_CALLBACK (on_tv_button_press), NULL);
212
213 /* dir name column */
214 g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (tvw)), "changed",
215 G_CALLBACK (on_tv_selection_changed), NULL);
216 cell = gtk_cell_renderer_text_new ();
217 col = gtk_tree_view_column_new_with_attributes (NULL, cell, "markup",
218 COL_DIR_NAME, "text",
219 COL_DIR_NAME, NULL);
220 gtk_tree_view_column_set_sort_column_id (col, COL_DIR_NAME);
221 gtk_tree_view_column_set_reorderable (col, TRUE);
222 gtk_tree_view_column_set_title (col, _("Folder"));
223 gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
224 gtk_tree_view_column_set_resizable (col, TRUE);
225 gtk_tree_view_append_column (GTK_TREE_VIEW (tvw), col);
226
227 /* percentage bar & text column */
228 col = gtk_tree_view_column_new ();
229
230 cell = baobab_cell_renderer_progress_new ();
231 gtk_tree_view_column_pack_start (col, cell, FALSE);
232 gtk_tree_view_column_set_attributes (col, cell, "perc",
233 COL_H_PERC, NULL);
234
235 cell = gtk_cell_renderer_text_new ();
236 gtk_tree_view_column_pack_start (col, cell, TRUE);
237 gtk_tree_view_column_set_cell_data_func (col, cell,
238 perc_cell_data_func,
239 NULL, NULL);
240
241 g_object_set (G_OBJECT (cell), "xalign", (gfloat) 1.0, NULL);
242 gtk_tree_view_column_set_sort_column_id (col, COL_H_PERC);
243 gtk_tree_view_column_set_reorderable (col, TRUE);
244 gtk_tree_view_column_set_title (col, _("Usage"));
245 gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
246 gtk_tree_view_column_set_resizable (col, FALSE);
247 gtk_tree_view_append_column (GTK_TREE_VIEW (tvw), col);
248
249 /* directory size column */
250 cell = gtk_cell_renderer_text_new ();
251 col = gtk_tree_view_column_new_with_attributes (NULL, cell, "markup",
252 COL_DIR_SIZE, "text",
253 COL_DIR_SIZE, NULL);
254 g_object_set (G_OBJECT (cell), "xalign", (gfloat) 1.0, NULL);
255 gtk_tree_view_column_set_sort_column_id (col,
256 baobab.show_allocated ? COL_H_ALLOCSIZE : COL_H_SIZE);
257 gtk_tree_view_column_set_reorderable (col, TRUE);
258 gtk_tree_view_column_set_title (col, _("Size"));
259 gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
260 gtk_tree_view_column_set_resizable (col, TRUE);
261 gtk_tree_view_append_column (GTK_TREE_VIEW (tvw), col);
262
263 /* store this column, we need it when toggling 'allocated' */
264 g_object_set_data (G_OBJECT (tvw), "baobab_size_col", col);
265
266 /* objects column */
267 cell = gtk_cell_renderer_text_new ();
268 col = gtk_tree_view_column_new_with_attributes (NULL, cell, "markup",
269 COL_ELEMENTS, "text",
270 COL_ELEMENTS, NULL);
271 g_object_set (G_OBJECT (cell), "xalign", (gfloat) 1.0, NULL);
272 gtk_tree_view_column_set_sort_column_id (col, COL_H_ELEMENTS);
273 gtk_tree_view_column_set_reorderable (col, TRUE);
274 gtk_tree_view_column_set_title (col, _("Contents"));
275 gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
276 gtk_tree_view_column_set_resizable (col, TRUE);
277 gtk_tree_view_append_column (GTK_TREE_VIEW (tvw), col);
278
279 /* hardlink column */
280 cell = gtk_cell_renderer_text_new ();
281 col = gtk_tree_view_column_new_with_attributes (NULL, cell, "markup",
282 COL_HARDLINK, "text",
283 COL_HARDLINK, NULL);
284 gtk_tree_view_append_column (GTK_TREE_VIEW (tvw), col);
285
286 gtk_tree_view_collapse_all (GTK_TREE_VIEW (tvw));
287 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tvw), FALSE);
288 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (GET_WIDGET ("scrolledwindow1")),
289 GTK_POLICY_AUTOMATIC,
290 GTK_POLICY_AUTOMATIC);
291
292 gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (tvw),
293 baobab_treeview_equal_func,
294 NULL, NULL);
295
296 baobab.model = create_model ();
297
298 /* By default, sort by size */
299 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (baobab.model),
300 baobab.show_allocated ? COL_H_ALLOCSIZE : COL_H_SIZE,
301 GTK_SORT_DESCENDING);
302
303 gtk_tree_view_set_model (GTK_TREE_VIEW (tvw),
304 GTK_TREE_MODEL (baobab.model));
305 g_object_unref (baobab.model);
306
307 return tvw;
308 }
309
310 void
baobab_treeview_show_allocated_size(GtkWidget * tv,gboolean show_allocated)311 baobab_treeview_show_allocated_size (GtkWidget *tv,
312 gboolean show_allocated)
313 {
314 gint sort_id;
315 gint new_sort_id;
316 GtkSortType order;
317 GtkTreeViewColumn *size_col;
318
319 g_return_if_fail (GTK_IS_TREE_VIEW (tv));
320
321 gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (baobab.model),
322 &sort_id, &order);
323
324 /* set the sort id for the size column */
325 size_col = g_object_get_data (G_OBJECT (tv), "baobab_size_col");
326 new_sort_id = show_allocated ? COL_H_ALLOCSIZE : COL_H_SIZE;
327 gtk_tree_view_column_set_sort_column_id (size_col, new_sort_id);
328
329 /* if we are currently sorted on size or allocated size,
330 * then trigger a resort (with the same order) */
331 if (sort_id == COL_H_SIZE || sort_id == COL_H_ALLOCSIZE) {
332 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (baobab.model),
333 new_sort_id, order);
334 }
335 }
336