1 /*
2 * Copyright (C) 2019-2021 Alexandros Theodotou <alex at zrythm dot org>
3 *
4 * This file is part of Zrythm
5 *
6 * Zrythm is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero 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 * Zrythm 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 Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with Zrythm. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20 #include <stdlib.h>
21 #include <string.h>
22
23 #include "audio/supported_file.h"
24 #include "gui/backend/file_manager.h"
25 #include "settings/settings.h"
26 #include "utils/arrays.h"
27 #include "utils/io.h"
28 #include "utils/objects.h"
29 #include "utils/string.h"
30 #include "utils/strv_builder.h"
31 #include "zrythm.h"
32
33 #include <gtk/gtk.h>
34 #include <glib/gi18n.h>
35
36 /**
37 * Creates the file manager.
38 */
39 FileManager *
file_manager_new(void)40 file_manager_new (void)
41 {
42 FileManager * self = object_new (FileManager);
43
44 /*self->num_collections = 0;*/
45
46 self->files =
47 g_ptr_array_new_full (
48 400, (GDestroyNotify) supported_file_free);
49 self->locations =
50 g_ptr_array_new_with_free_func (
51 (GDestroyNotify) file_browser_location_free);
52
53 /* add standard locations */
54 FileBrowserLocation * fl =
55 file_browser_location_new ();
56 /* TRANSLATORS: Home directory */
57 fl->label = g_strdup (_("Home"));
58 fl->path = g_strdup (g_get_home_dir ());
59 fl->special_location = FILE_MANAGER_HOME;
60 g_ptr_array_add (self->locations, fl);
61
62 file_manager_set_selection (
63 self, fl, false, false);
64
65 fl =
66 file_browser_location_new ();
67 /* TRANSLATORS: Desktop directory */
68 fl->label = g_strdup (_("Desktop"));
69 fl->path =
70 g_strdup (
71 g_get_user_special_dir (
72 G_USER_DIRECTORY_DESKTOP));
73 fl->special_location = FILE_MANAGER_DESKTOP;
74 g_ptr_array_add (self->locations, fl);
75
76 if (!ZRYTHM_TESTING)
77 {
78 /* add bookmarks */
79 char ** bookmarks =
80 g_settings_get_strv (
81 S_UI_FILE_BROWSER,
82 "file-browser-bookmarks");
83 for (size_t i = 0; bookmarks[i] != NULL; i++)
84 {
85 char * bookmark = bookmarks[i];
86 fl =
87 file_browser_location_new ();
88 fl->label = g_path_get_basename (bookmark);
89 fl->path = g_strdup (bookmark);
90 fl->special_location =
91 FILE_MANAGER_NONE;
92 g_ptr_array_add (self->locations, fl);
93 }
94 g_strfreev (bookmarks);
95
96 /* set remembered location */
97 FileBrowserLocation * loc =
98 file_browser_location_new ();
99 loc->path =
100 g_settings_get_string (
101 S_UI_FILE_BROWSER, "last-location");
102 if (strlen (loc->path) > 0 &&
103 g_file_test (
104 loc->path, G_FILE_TEST_IS_DIR))
105 {
106 file_manager_set_selection (
107 self, loc, true, false);
108 }
109 file_browser_location_free (loc);
110 }
111
112 return self;
113 }
114
115 static int
alphaBetize(const void * _a,const void * _b)116 alphaBetize (const void * _a,
117 const void * _b)
118 {
119 SupportedFile * a = *(SupportedFile * const *) _a;
120 SupportedFile * b = *(SupportedFile * const *) _b;
121 int r = strcasecmp(a->label, b->label);
122 if (r) return r;
123 /* if equal ignoring case, use opposite of strcmp()
124 * result to get lower before upper */
125 return -strcmp(a->label, b->label); /* aka: return strcmp(b, a); */
126 }
127
128 static void
load_files_from_location(FileManager * self,FileBrowserLocation * location)129 load_files_from_location (
130 FileManager * self,
131 FileBrowserLocation * location)
132 {
133 const gchar * file;
134 SupportedFile * fd;
135
136 g_ptr_array_remove_range (
137 self->files, 0, self->files->len);
138
139 GDir * dir =
140 g_dir_open (location->path, 0, NULL);
141 if (!dir)
142 {
143 g_warning ("Could not open dir %s",
144 location->path);
145 return;
146 }
147
148 /* create special parent dir entry */
149 fd = object_new (SupportedFile);
150 /*g_message ("pre path %s",*/
151 /*location->path);*/
152 fd->abs_path =
153 io_path_get_parent_dir (location->path);
154 /*g_message ("after path %s",*/
155 /*fd->abs_path);*/
156 fd->type = FILE_TYPE_PARENT_DIR;
157 fd->hidden = 0;
158 fd->label = g_strdup ("..");
159 if (strlen (location->path) > 1)
160 {
161 g_ptr_array_add (self->files, fd);
162 }
163 else
164 {
165 supported_file_free (fd);
166 fd = NULL;
167 }
168
169 while ((file = g_dir_read_name (dir)))
170 {
171 fd = object_new (SupportedFile);
172 /*fd->dnd_type = UI_DND_TYPE_FILE_DESCRIPTOR;*/
173
174 /* set absolute path & label */
175 char * absolute_path =
176 g_strdup_printf (
177 "%s%s%s",
178 strlen (location->path) == 1 ?
179 "" : location->path,
180 G_DIR_SEPARATOR_S, file);
181 fd->abs_path = absolute_path;
182 fd->label = g_strdup (file);
183
184 GError * err = NULL;
185 GFile * gfile =
186 g_file_new_for_path (absolute_path);
187 GFileInfo * info =
188 g_file_query_info (
189 gfile, G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN,
190 G_FILE_QUERY_INFO_NONE, NULL, &err);
191 if (err)
192 {
193 g_warning (
194 "failed to query file info for %s",
195 absolute_path);
196 }
197 else
198 {
199 fd->hidden =
200 g_file_info_get_is_hidden (info);
201 g_object_unref (info);
202 }
203 g_object_unref (gfile);
204
205 /* set type */
206 if (g_file_test (
207 absolute_path, G_FILE_TEST_IS_DIR))
208 {
209 fd->type = FILE_TYPE_DIR;
210 }
211 else
212 {
213 fd->type = supported_file_get_type (file);
214 }
215
216 /* force hidden if starts with . */
217 if (file[0] == '.')
218 fd->hidden = true;
219
220 g_ptr_array_add (self->files, fd);
221 /*g_message ("File found: %s (%d - %d)",*/
222 /*fd->abs_path,*/
223 /*fd->type,*/
224 /*fd->hidden);*/
225 }
226 g_dir_close (dir);
227
228 g_ptr_array_sort (
229 self->files, (GCompareFunc) alphaBetize);
230 g_message ("Total files: %d", self->files->len);
231 }
232
233 /**
234 * Loads the files under the current selection.
235 */
236 void
file_manager_load_files(FileManager * self)237 file_manager_load_files (FileManager * self)
238 {
239 if (self->selection)
240 {
241 load_files_from_location (
242 self,
243 (FileBrowserLocation *) self->selection);
244 }
245 else
246 {
247 g_ptr_array_remove_range (
248 self->files, 0, self->files->len);
249 }
250 }
251
252 /**
253 * @param save_to_settings Whether to save this
254 * location to GSettings.
255 */
256 void
file_manager_set_selection(FileManager * self,FileBrowserLocation * sel,bool load_files,bool save_to_settings)257 file_manager_set_selection (
258 FileManager * self,
259 FileBrowserLocation * sel,
260 bool load_files,
261 bool save_to_settings)
262 {
263 g_debug ("setting selection to %s", sel->path);
264
265 if (self->selection)
266 file_browser_location_free (self->selection);
267
268 self->selection =
269 file_browser_location_clone (sel);
270 if (load_files)
271 {
272 file_manager_load_files (self);
273 }
274 if (save_to_settings)
275 {
276 g_settings_set_string (
277 S_UI_FILE_BROWSER, "last-location",
278 self->selection->path);
279 }
280 }
281
282 static bool
file_browser_location_equal_func(const FileBrowserLocation * a,const FileBrowserLocation * b)283 file_browser_location_equal_func (
284 const FileBrowserLocation * a,
285 const FileBrowserLocation * b)
286 {
287 bool ret =
288 string_is_equal (a->path, b->path);
289 return ret;
290 }
291
292 static void
save_locations(FileManager * self)293 save_locations (
294 FileManager * self)
295 {
296 StrvBuilder * strv_builder = strv_builder_new ();
297 for (guint i = 0;
298 i < FILE_MANAGER->locations->len; i++)
299 {
300 FileBrowserLocation * loc =
301 g_ptr_array_index (
302 FILE_MANAGER->locations, i);
303 if (loc->special_location > FILE_MANAGER_NONE)
304 continue;
305
306 strv_builder_add (
307 strv_builder, loc->path);
308 }
309
310 char ** strings = strv_builder_end (strv_builder);
311 g_settings_set_strv (
312 S_UI_FILE_BROWSER, "file-browser-bookmarks",
313 (const char * const *) strings);
314 g_strfreev (strings);
315 }
316
317 /**
318 * Adds a location and saves the settings.
319 */
320 void
file_manager_add_location_and_save(FileManager * self,const char * abs_path)321 file_manager_add_location_and_save (
322 FileManager * self,
323 const char * abs_path)
324 {
325 FileBrowserLocation * loc =
326 file_browser_location_new ();
327 loc->path = g_strdup (abs_path);
328 loc->label = g_path_get_basename (loc->path);
329
330 g_ptr_array_add (self->locations, loc);
331
332 save_locations (self);
333 }
334
335 /**
336 * Removes the given location (bookmark) from the
337 * saved locations.
338 *
339 * @param skip_if_standard Skip removal if the
340 * given location is a standard location.
341 */
342 void
file_manager_remove_location_and_save(FileManager * self,const char * location,bool skip_if_standard)343 file_manager_remove_location_and_save (
344 FileManager * self,
345 const char * location,
346 bool skip_if_standard)
347 {
348 FileBrowserLocation * loc =
349 file_browser_location_new ();
350 loc->path = g_strdup (location);
351
352 unsigned int idx;
353 bool ret =
354 g_ptr_array_find_with_equal_func (
355 self->locations, loc,
356 (GEqualFunc)
357 file_browser_location_equal_func, &idx);
358 if (ret)
359 {
360 FileBrowserLocation * existing_loc =
361 g_ptr_array_index (
362 self->locations, idx);
363 if (!skip_if_standard ||
364 existing_loc->special_location ==
365 FILE_MANAGER_NONE)
366 {
367 g_ptr_array_remove_index (
368 self->locations, idx);
369 }
370 }
371 else
372 {
373 g_warning ("%s not found", location);
374 }
375
376 file_browser_location_free (loc);
377
378 save_locations (self);
379 }
380
381 /**
382 * Frees the file manager.
383 */
384 void
file_manager_free(FileManager * self)385 file_manager_free (
386 FileManager * self)
387 {
388 g_ptr_array_free (self->files, true);
389 g_ptr_array_free (self->locations, true);
390
391 object_zero_and_free (self);
392 }
393
394
395 FileBrowserLocation *
file_browser_location_new(void)396 file_browser_location_new (void)
397 {
398 return object_new (FileBrowserLocation);
399 }
400
401 FileBrowserLocation *
file_browser_location_clone(FileBrowserLocation * loc)402 file_browser_location_clone (
403 FileBrowserLocation * loc)
404 {
405 FileBrowserLocation * self =
406 file_browser_location_new ();
407 self->path = g_strdup (loc->path);
408 self->label = g_strdup (loc->label);
409
410 return self;
411 }
412
413 void
file_browser_location_free(FileBrowserLocation * loc)414 file_browser_location_free (
415 FileBrowserLocation * loc)
416 {
417 g_free_and_null (loc->label);
418 g_free_and_null (loc->path);
419 object_zero_and_free (loc);
420 }
421