1 /* EasyTAG - tag editor for audio files
2  * Copyright (C) 2014  David King <amigadave@amigadave.com>
3  * Copyright (C) 2000-2003  Jerome Couderc <easytag@gmail.com>
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the Free
7  * Software Foundation; either version 2 of the License, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License along with
16  * this program; if not, write to the Free Software Foundation, Inc., 51
17  * Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18  */
19 
20 /* Some parts of this browser are taken from:
21  * XMMS - Cross-platform multimedia player
22  * Copyright (C) 1998-1999  Peter Alm, Mikael Alm, Olle Hallnas,
23  * Thomas Nilsson and 4Front Technologies
24  */
25 
26 #include "config.h"
27 
28 #include "browser.h"
29 
30 #include <gdk/gdkkeysyms.h>
31 #include <glib/gi18n.h>
32 #include <glib/gstdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <errno.h>
36 
37 #include "application_window.h"
38 #include "charset.h"
39 #include "dlm.h"
40 #include "easytag.h"
41 #include "et_core.h"
42 #include "file_list.h"
43 #include "scan_dialog.h"
44 #include "log.h"
45 #include "misc.h"
46 #include "setting.h"
47 
48 #include "win32/win32dep.h"
49 
50 typedef struct
51 {
52     GtkWidget *files_label;
53     GtkWidget *open_button;
54 
55     GtkWidget *entry_combo;
56     GtkListStore *entry_model;
57 
58     GtkWidget *directory_album_artist_notebook;
59 
60     GtkListStore *file_model;
61     GtkWidget *file_view;
62     GtkWidget *file_menu;
63     guint file_selected_handler;
64     EtSortMode file_sort_mode;
65 
66     GtkWidget *album_view;
67     GtkWidget *album_menu;
68     GtkListStore *album_model;
69     guint album_selected_handler;
70 
71     GtkWidget *artist_view;
72     GtkWidget *artist_menu;
73     GtkListStore *artist_model;
74     guint artist_selected_handler;
75 
76     GtkWidget *directory_view; /* Tree of directories. */
77     GtkWidget *directory_view_menu;
78     GtkTreeStore *directory_model;
79 
80     GtkListStore *run_program_model;
81 
82     GtkWidget *open_directory_with_dialog;
83     GtkWidget *open_directory_with_combobox;
84 
85     GtkWidget *open_files_with_dialog;
86     GtkWidget *open_files_with_combobox;
87 
88     /* The Rename Directory window. */
89     GtkWidget *rename_directory_dialog;
90     GtkWidget *rename_directory_entry;
91     GtkWidget *rename_directory_mask_toggle;
92     GtkWidget *rename_directory_mask_entry;
93     GtkWidget *rename_directory_preview_label;
94 
95     GFile *current_path;
96 } EtBrowserPrivate;
97 
98 G_DEFINE_TYPE_WITH_PRIVATE (EtBrowser, et_browser, GTK_TYPE_BIN)
99 
100 /*
101  * EtPathState:
102  * @ET_PATH_STATE_OPEN: the path is open or has been read
103  * @ET_PATH_STATE_CLOSED: the path is closed or could not be read
104  *
105  * Whether to generate an icon with an indicaton that the directory is open
106  * (being viewed) or closed (not yet viewed or read).
107  */
108 typedef enum
109 {
110     ET_PATH_STATE_OPEN,
111     ET_PATH_STATE_CLOSED
112 } EtPathState;
113 
114 enum
115 {
116     LIST_FILE_NAME,
117     /* Tag fields. */
118     LIST_FILE_TITLE,
119     LIST_FILE_ARTIST,
120     LIST_FILE_ALBUM_ARTIST,
121     LIST_FILE_ALBUM,
122     LIST_FILE_YEAR,
123     LIST_FILE_DISCNO,
124     LIST_FILE_TRACK,
125     LIST_FILE_GENRE,
126     LIST_FILE_COMMENT,
127     LIST_FILE_COMPOSER,
128     LIST_FILE_ORIG_ARTIST,
129     LIST_FILE_COPYRIGHT,
130     LIST_FILE_URL,
131     LIST_FILE_ENCODED_BY,
132     /* End of columns with associated UI columns. */
133     LIST_FILE_POINTER,
134     LIST_FILE_KEY,
135     LIST_FILE_OTHERDIR, /* To change color for alternate directories. */
136     LIST_FONT_WEIGHT,
137     LIST_ROW_BACKGROUND,
138     LIST_ROW_FOREGROUND,
139     LIST_COLUMN_COUNT
140 };
141 
142 enum
143 {
144     ALBUM_GICON,
145     ALBUM_NAME,
146     ALBUM_NUM_FILES,
147     ALBUM_ETFILE_LIST_POINTER,
148     ALBUM_FONT_WEIGHT,
149     ALBUM_ROW_FOREGROUND,
150     ALBUM_ALL_ALBUMS_ROW,
151     ALBUM_ALL_ALBUMS_SEPARATOR_ROW,
152     ALBUM_COLUMN_COUNT
153 };
154 
155 enum
156 {
157     ARTIST_PIXBUF,
158     ARTIST_NAME,
159     ARTIST_NUM_ALBUMS,
160     ARTIST_NUM_FILES,
161     ARTIST_ALBUM_LIST_POINTER,
162     ARTIST_FONT_WEIGHT,
163     ARTIST_ROW_FOREGROUND,
164     ARTIST_COLUMN_COUNT
165 };
166 
167 enum
168 {
169     TREE_COLUMN_DIR_NAME,
170     TREE_COLUMN_FULL_PATH,
171     TREE_COLUMN_SCANNED,
172     TREE_COLUMN_HAS_SUBDIR,
173     TREE_COLUMN_ICON,
174     TREE_COLUMN_COUNT
175 };
176 
177 /**************
178  * Prototypes *
179  **************/
180 
181 static void Browser_Tree_Handle_Rename (EtBrowser *self,
182                                         GtkTreeIter *parentnode,
183                                         const gchar *old_path,
184                                         const gchar *new_path);
185 
186 static void Browser_List_Set_Row_Appearance (EtBrowser *self, GtkTreeIter *iter);
187 static gint Browser_List_Sort_Func (GtkTreeModel *model, GtkTreeIter *a,
188                                     GtkTreeIter *b, gpointer data);
189 static void Browser_List_Select_File_By_Iter (EtBrowser *self,
190                                               GtkTreeIter *iter,
191                                               gboolean select_it);
192 
193 static void Browser_Artist_List_Row_Selected (EtBrowser *self,
194                                               GtkTreeSelection *selection);
195 static void Browser_Artist_List_Set_Row_Appearance (EtBrowser *self, GtkTreeIter *row);
196 
197 static void Browser_Album_List_Load_Files (EtBrowser *self, GList *albumlist,
198                                            ET_File *etfile_to_select);
199 static void Browser_Album_List_Row_Selected (EtBrowser *self,
200                                              GtkTreeSelection *selection);
201 static void Browser_Album_List_Set_Row_Appearance (EtBrowser *self, GtkTreeIter *row);
202 
203 static gboolean check_for_subdir (const gchar *path);
204 
205 static GtkTreePath *Find_Child_Node (EtBrowser *self, GtkTreeIter *parent, gchar *searchtext);
206 
207 static GIcon *get_gicon_for_path (const gchar *path, EtPathState path_state);
208 
209 /* For window to rename a directory */
210 static void Destroy_Rename_Directory_Window (EtBrowser *self);
211 static void Rename_Directory_With_Mask_Toggled (EtBrowser *self);
212 
213 /* For window to run a program with the directory */
214 static void Destroy_Run_Program_Tree_Window (EtBrowser *self);
215 static void Run_Program_With_Directory (EtBrowser *self);
216 
217 /* For window to run a program with the file */
218 static void Destroy_Run_Program_List_Window (EtBrowser *self);
219 
220 static void empty_entry_disable_widget (GtkWidget *widget, GtkEntry *entry);
221 
222 static void et_rename_directory_on_response (GtkDialog *dialog,
223                                              gint response_id,
224                                              gpointer user_data);
225 static void et_run_program_tree_on_response (GtkDialog *dialog,
226                                              gint response_id,
227                                              gpointer user_data);
228 static void et_run_program_list_on_response (GtkDialog *dialog,
229                                              gint response_id,
230                                              gpointer user_data);
231 
232 static void et_browser_on_column_clicked (GtkTreeViewColumn *column,
233                                           gpointer data);
234 
235 
236 /*************
237  * Functions *
238  *************/
239 /*
240  * Load home directory
241  */
242 void
et_browser_go_home(EtBrowser * self)243 et_browser_go_home (EtBrowser *self)
244 {
245     GFile *file;
246 
247     file = g_file_new_for_path (g_get_home_dir ());
248     et_browser_select_dir (self, file);
249     g_object_unref (file);
250 }
251 
252 /*
253  * Load desktop directory
254  */
255 void
et_browser_go_desktop(EtBrowser * self)256 et_browser_go_desktop (EtBrowser *self)
257 {
258     GFile *file;
259 
260     file = g_file_new_for_path (g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP));
261     et_browser_select_dir (self, file);
262     g_object_unref (file);
263 }
264 
265 /*
266  * Load documents directory
267  */
268 void
et_browser_go_documents(EtBrowser * self)269 et_browser_go_documents (EtBrowser *self)
270 {
271     GFile *file;
272 
273     file = g_file_new_for_path (g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
274     et_browser_select_dir (self, file);
275     g_object_unref (file);
276 }
277 
278 /*
279  * Load downloads directory
280  */
281 void
et_browser_go_downloads(EtBrowser * self)282 et_browser_go_downloads (EtBrowser *self)
283 {
284     GFile *file;
285 
286     file = g_file_new_for_path (g_get_user_special_dir (G_USER_DIRECTORY_DOWNLOAD));
287     et_browser_select_dir (self, file);
288     g_object_unref (file);
289 }
290 
291 /*
292  * Load music directory
293  */
294 void
et_browser_go_music(EtBrowser * self)295 et_browser_go_music (EtBrowser *self)
296 {
297     GFile *file;
298 
299     file = g_file_new_for_path (g_get_user_special_dir (G_USER_DIRECTORY_MUSIC));
300     et_browser_select_dir (self, file);
301     g_object_unref (file);
302 }
303 
304 
305 /*
306  * Load default directory
307  */
308 void
et_browser_load_default_dir(EtBrowser * self)309 et_browser_load_default_dir (EtBrowser *self)
310 {
311     GFile **files;
312     GVariant *default_path;
313     const gchar *path;
314 
315     default_path = g_settings_get_value (MainSettings, "default-path");
316     path = g_variant_get_bytestring (default_path);
317 
318     files = g_new (GFile *, 1);
319     files[0] = g_file_new_for_path (path);
320     g_application_open (g_application_get_default (), files, 1, "");
321 
322     g_object_unref (files[0]);
323     g_variant_unref (default_path);
324     g_free (files);
325 }
326 
327 void
et_browser_run_player_for_album_list(EtBrowser * self)328 et_browser_run_player_for_album_list (EtBrowser *self)
329 {
330     EtBrowserPrivate *priv;
331     GtkTreeIter iter;
332     GtkTreeSelection *selection;
333     GList *l;
334     GList *file_list = NULL;
335     GError *error = NULL;
336 
337     priv = et_browser_get_instance_private (self);
338 
339     g_return_if_fail (priv->album_view != NULL);
340 
341     selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->album_view));
342 
343     if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
344         return;
345 
346     gtk_tree_model_get (GTK_TREE_MODEL (priv->album_model), &iter,
347                         ALBUM_ETFILE_LIST_POINTER, &l, -1);
348 
349     for (; l != NULL; l = g_list_next (l))
350     {
351         ET_File *etfile = (ET_File *)l->data;
352         const gchar *path = ((File_Name *)etfile->FileNameCur->data)->value;
353         file_list = g_list_prepend (file_list, g_file_new_for_path (path));
354     }
355 
356     file_list = g_list_reverse (file_list);
357 
358     if (!et_run_audio_player (file_list, &error))
359     {
360         Log_Print (LOG_ERROR, _("Failed to launch program ‘%s’"),
361                    error->message);
362         g_error_free (error);
363     }
364 
365     g_list_free_full (file_list, g_object_unref);
366 }
367 
368 void
et_browser_run_player_for_artist_list(EtBrowser * self)369 et_browser_run_player_for_artist_list (EtBrowser *self)
370 {
371     EtBrowserPrivate *priv;
372     GtkTreeIter iter;
373     GtkTreeSelection *selection;
374     GList *l, *m;
375     GList *file_list = NULL;
376     GError *error = NULL;
377 
378     priv = et_browser_get_instance_private (self);
379 
380     g_return_if_fail (priv->artist_view != NULL);
381 
382     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->artist_view));
383     if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
384         return;
385 
386     gtk_tree_model_get (GTK_TREE_MODEL (priv->artist_model), &iter,
387                         ARTIST_ALBUM_LIST_POINTER, &l, -1);
388 
389     for (; l != NULL; l = g_list_next (l))
390     {
391         for (m = l->data; m != NULL; m = g_list_next (m))
392         {
393             ET_File *etfile = (ET_File *)m->data;
394             const gchar *path = ((File_Name *)etfile->FileNameCur->data)->value;
395             file_list = g_list_prepend (file_list, g_file_new_for_path (path));
396         }
397     }
398 
399     file_list = g_list_reverse (file_list);
400 
401     if (!et_run_audio_player (file_list, &error))
402     {
403         Log_Print (LOG_ERROR, _("Failed to launch program ‘%s’"),
404                    error->message);
405         g_error_free (error);
406     }
407 
408     g_list_free_full (file_list, g_object_unref);
409 }
410 
411 void
et_browser_run_player_for_selection(EtBrowser * self)412 et_browser_run_player_for_selection (EtBrowser *self)
413 {
414     EtBrowserPrivate *priv;
415     GList *selfilelist = NULL;
416     GList *l;
417     GList *file_list = NULL;
418     GtkTreeSelection *selection;
419     GError *error = NULL;
420 
421     priv = et_browser_get_instance_private (self);
422 
423     selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->file_view));
424     selfilelist = gtk_tree_selection_get_selected_rows(selection, NULL);
425 
426     for (l = selfilelist; l != NULL; l = g_list_next (l))
427     {
428         ET_File *etfile = et_browser_get_et_file_from_path (self, l->data);
429         const gchar *path = ((File_Name *)etfile->FileNameCur->data)->value;
430         file_list = g_list_prepend (file_list, g_file_new_for_path (path));
431     }
432 
433     file_list = g_list_reverse (file_list);
434 
435     if (!et_run_audio_player (file_list, &error))
436     {
437         Log_Print (LOG_ERROR, _("Failed to launch program ‘%s’"),
438                    error->message);
439         g_error_free (error);
440     }
441 
442     g_list_free_full (file_list, g_object_unref);
443     g_list_free_full (selfilelist, (GDestroyNotify)gtk_tree_path_free);
444 }
445 
446 /*
447  * Get the path from the selected node (row) in the browser
448  * Warning: return NULL if no row selected int the tree.
449  * Remember to free the value returned from this function!
450  */
451 static gchar *
Browser_Tree_Get_Path_Of_Selected_Node(EtBrowser * self)452 Browser_Tree_Get_Path_Of_Selected_Node (EtBrowser *self)
453 {
454     EtBrowserPrivate *priv;
455     GtkTreeSelection *selection;
456     GtkTreeIter selectedIter;
457     gchar *path;
458 
459     priv = et_browser_get_instance_private (self);
460 
461     g_return_val_if_fail (priv->directory_view != NULL, NULL);
462 
463     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->directory_view));
464     if (selection
465     && gtk_tree_selection_get_selected(selection, NULL, &selectedIter))
466     {
467         gtk_tree_model_get(GTK_TREE_MODEL(priv->directory_model), &selectedIter,
468                            TREE_COLUMN_FULL_PATH, &path, -1);
469         return path;
470     }else
471     {
472         return NULL;
473     }
474 }
475 
476 
477 /*
478  * Set the current path to be shown in the browser.
479  */
480 static void
et_browser_set_current_path(EtBrowser * self,GFile * file)481 et_browser_set_current_path (EtBrowser *self,
482                              GFile *file)
483 {
484     EtBrowserPrivate *priv;
485 
486     g_return_if_fail (file != NULL);
487 
488     priv = et_browser_get_instance_private (self);
489 
490     /* Ref the new file first, in case the current file is passed in. */
491     g_object_ref (file);
492 
493     if (priv->current_path)
494     {
495         g_object_unref (priv->current_path);
496     }
497 
498     priv->current_path = file;
499 }
500 
501 
502 /*
503  * Return the current path
504  */
505 GFile *
et_browser_get_current_path(EtBrowser * self)506 et_browser_get_current_path (EtBrowser *self)
507 {
508     EtBrowserPrivate *priv;
509 
510     g_return_val_if_fail (ET_BROWSER (self), NULL);
511 
512     priv = et_browser_get_instance_private (self);
513 
514     return priv->current_path;
515 }
516 
517 GtkTreeSelection *
et_browser_get_selection(EtBrowser * self)518 et_browser_get_selection (EtBrowser *self)
519 {
520     EtBrowserPrivate *priv;
521 
522     g_return_val_if_fail (ET_BROWSER (self), NULL);
523 
524     priv = et_browser_get_instance_private (self);
525 
526     return gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->file_view));
527 }
528 
529 /*
530  * Reload the current directory.
531  */
532 void
et_browser_reload_directory(EtBrowser * self)533 et_browser_reload_directory (EtBrowser *self)
534 {
535     EtBrowserPrivate *priv;
536 
537     priv = et_browser_get_instance_private (self);
538 
539     if (priv->directory_view && priv->current_path != NULL)
540     {
541         /* Unselect files, to automatically reload the file of the directory. */
542         GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->directory_view));
543 
544         if (selection)
545         {
546             gtk_tree_selection_unselect_all(selection);
547         }
548 
549         et_browser_select_dir (self, priv->current_path);
550     }
551 }
552 
553 /*
554  * Set the current path (selected node) in browser as default path (within config variable).
555  */
556 void
et_browser_set_current_path_default(EtBrowser * self)557 et_browser_set_current_path_default (EtBrowser *self)
558 {
559     EtBrowserPrivate *priv;
560     gchar *path;
561 
562     g_return_if_fail (ET_BROWSER (self));
563 
564     priv = et_browser_get_instance_private (self);
565 
566     path = g_file_get_path (priv->current_path);
567     g_settings_set_value (MainSettings, "default-path",
568                           g_variant_new_bytestring (path));
569     g_free (path);
570 
571     et_application_window_status_bar_message (ET_APPLICATION_WINDOW (MainWindow),
572                                               _("New default directory selected for browser"),
573                                               TRUE);
574 }
575 
576 /*
577  * When you press the key 'enter' in the BrowserEntry to validate the text (browse the directory)
578  */
579 static void
Browser_Entry_Activated(EtBrowser * self,GtkEntry * entry)580 Browser_Entry_Activated (EtBrowser *self,
581                          GtkEntry *entry)
582 {
583     EtBrowserPrivate *priv;
584     const gchar *parse_name;
585     GFile *file;
586 
587     priv = et_browser_get_instance_private (self);
588 
589     parse_name = gtk_entry_get_text (entry);
590     Add_String_To_Combo_List (GTK_LIST_STORE (priv->entry_model), parse_name);
591 
592     file = g_file_parse_name (parse_name);
593 
594     et_browser_select_dir (self, file);
595 
596     g_object_unref (file);
597 }
598 
599 /*
600  * Set a text into the BrowserEntry (and don't activate it)
601  */
602 void
et_browser_entry_set_text(EtBrowser * self,const gchar * text)603 et_browser_entry_set_text (EtBrowser *self, const gchar *text)
604 {
605     EtBrowserPrivate *priv;
606 
607     priv = et_browser_get_instance_private (self);
608 
609     if (!text || !priv->entry_combo)
610         return;
611 
612     gtk_entry_set_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (priv->entry_combo))),
613                         text);
614 }
615 
616 /*
617  * Button to go to parent directory
618  */
619 void
et_browser_go_parent(EtBrowser * self)620 et_browser_go_parent (EtBrowser *self)
621 {
622     GFile *parent;
623 
624     parent = g_file_get_parent (et_browser_get_current_path (self));
625 
626     if (parent)
627     {
628         et_browser_select_dir (self, parent);
629         g_object_unref (parent);
630     }
631     else
632     {
633         g_debug ("%s", "No parent found for current browser path");
634     }
635 }
636 
637 /*
638  * Set a text into the priv->files_label
639  */
640 void
et_browser_label_set_text(EtBrowser * self,const gchar * text)641 et_browser_label_set_text (EtBrowser *self, const gchar *text)
642 {
643     EtBrowserPrivate *priv;
644 
645     g_return_if_fail (ET_BROWSER (self));
646     g_return_if_fail (text != NULL);
647 
648     priv = et_browser_get_instance_private (self);
649 
650     gtk_label_set_text (GTK_LABEL (priv->files_label), text);
651 }
652 
653 /*
654  * Key Press events into browser tree
655  */
656 static gboolean
Browser_Tree_Key_Press(GtkWidget * tree,GdkEvent * event,gpointer data)657 Browser_Tree_Key_Press (GtkWidget *tree, GdkEvent *event, gpointer data)
658 {
659     GdkEventKey *kevent;
660     GtkTreeIter SelectedNode;
661     GtkTreeModel *treeModel;
662     GtkTreeSelection *treeSelection;
663     GtkTreePath *treePath;
664 
665     g_return_val_if_fail (tree != NULL, FALSE);
666 
667     treeSelection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
668 
669     if (event && event->type==GDK_KEY_PRESS)
670     {
671         if (!gtk_tree_selection_get_selected(treeSelection, &treeModel, &SelectedNode))
672             return FALSE;
673 
674         kevent = (GdkEventKey *)event;
675         treePath = gtk_tree_model_get_path(GTK_TREE_MODEL(treeModel), &SelectedNode);
676 
677         switch(kevent->keyval)
678         {
679             case GDK_KEY_KP_Enter:    /* Enter key in Num Pad */
680             case GDK_KEY_Return:      /* 'Normal' Enter key */
681             case GDK_KEY_t:           /* Expand/Collapse node */
682             case GDK_KEY_T:           /* Expand/Collapse node */
683                 if(gtk_tree_view_row_expanded(GTK_TREE_VIEW(tree), treePath))
684                     gtk_tree_view_collapse_row(GTK_TREE_VIEW(tree), treePath);
685                 else
686                     gtk_tree_view_expand_row(GTK_TREE_VIEW(tree), treePath, FALSE);
687 
688                 gtk_tree_path_free(treePath);
689                 return TRUE;
690                 break;
691 
692             case GDK_KEY_e:           /* Expand node */
693             case GDK_KEY_E:           /* Expand node */
694                 gtk_tree_view_expand_row(GTK_TREE_VIEW(tree), treePath, FALSE);
695                 gtk_tree_path_free(treePath);
696                 return TRUE;
697                 break;
698 
699             case GDK_KEY_c:           /* Collapse node */
700             case GDK_KEY_C:           /* Collapse node */
701                 gtk_tree_view_collapse_row(GTK_TREE_VIEW(tree), treePath);
702                 gtk_tree_path_free(treePath);
703                 return TRUE;
704                 break;
705             /* Ignore all other keypresses. */
706             default:
707                 break;
708         }
709         gtk_tree_path_free(treePath);
710     }
711     return FALSE;
712 }
713 
714 /*
715  * Key press into browser list
716  *   - Delete = delete file
717  * Also tries to capture text input and relate it to files
718  */
719 static gboolean
Browser_List_Key_Press(GtkWidget * list,GdkEvent * event,gpointer data)720 Browser_List_Key_Press (GtkWidget *list, GdkEvent *event, gpointer data)
721 {
722     GdkEventKey *kevent;
723     GtkTreeSelection *fileSelection;
724 
725     g_return_val_if_fail (list != NULL, FALSE);
726 
727     fileSelection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
728 
729     kevent = (GdkEventKey *)event;
730     if (event && event->type==GDK_KEY_PRESS)
731     {
732         if (gtk_tree_selection_count_selected_rows(fileSelection))
733         {
734             switch(kevent->keyval)
735             {
736                 case GDK_KEY_Delete:
737                     g_action_group_activate_action (G_ACTION_GROUP (MainWindow),
738                                                     "delete", NULL);
739                     return TRUE;
740                 /* Ignore all other keypresses. */
741                 default:
742                     break;
743             }
744         }
745     }
746 
747     return FALSE;
748 }
749 
750 /*
751  * Collapse (close) tree recursively up to the root node.
752  */
753 void
et_browser_collapse(EtBrowser * self)754 et_browser_collapse (EtBrowser *self)
755 {
756     EtBrowserPrivate *priv;
757 #ifndef G_OS_WIN32
758     GtkTreePath *rootPath;
759 #endif /* !G_OS_WIN32 */
760 
761     priv = et_browser_get_instance_private (self);
762 
763     g_return_if_fail (priv->directory_view != NULL);
764 
765     gtk_tree_view_collapse_all (GTK_TREE_VIEW (priv->directory_view));
766 
767 #ifndef G_OS_WIN32
768     /* But keep the main directory opened */
769     rootPath = gtk_tree_path_new_first();
770     gtk_tree_view_expand_to_path (GTK_TREE_VIEW (priv->directory_view),
771                                   rootPath);
772     gtk_tree_path_free(rootPath);
773 #endif /* !G_OS_WIN32 */
774 }
775 
776 
777 /*
778  * Set a row (or node) visible in the TreeView (by scrolling the tree)
779  */
780 static void
Browser_Tree_Set_Node_Visible(GtkWidget * directoryView,GtkTreePath * path)781 Browser_Tree_Set_Node_Visible (GtkWidget *directoryView, GtkTreePath *path)
782 {
783     g_return_if_fail (directoryView != NULL || path != NULL);
784 
785     gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(directoryView), path, NULL, TRUE, 0.5, 0.0);
786 }
787 
788 
789 /*
790  * Set a row visible in the file list (by scrolling the list)
791  */
792 static void
et_browser_set_row_visible(EtBrowser * self,GtkTreeIter * rowIter)793 et_browser_set_row_visible (EtBrowser *self, GtkTreeIter *rowIter)
794 {
795     EtBrowserPrivate *priv;
796 
797     /*
798      * TODO: Make this only scroll to the row if it is not visible
799      * (like in easytag GTK1)
800      * See function gtk_tree_view_get_visible_rect() ??
801      */
802     GtkTreePath *rowPath;
803 
804     priv = et_browser_get_instance_private (self);
805 
806     g_return_if_fail (rowIter != NULL);
807 
808     rowPath = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->file_model),
809                                        rowIter);
810     gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (priv->file_view), rowPath,
811                                   NULL, FALSE, 0, 0);
812     gtk_tree_path_free (rowPath);
813 }
814 
815 /*
816  * Triggers when a new node in the browser tree is selected
817  * Do file-save confirmation, and then prompt the new dir to be loaded
818  */
819 static gboolean
Browser_Tree_Node_Selected(EtBrowser * self,GtkTreeSelection * selection)820 Browser_Tree_Node_Selected (EtBrowser *self, GtkTreeSelection *selection)
821 {
822     EtBrowserPrivate *priv;
823     gchar *pathName;
824     GFile *file;
825     gchar *parse_name;
826     static gboolean first_read = TRUE;
827     GtkTreeIter selectedIter;
828     GtkTreePath *selectedPath;
829 
830     priv = et_browser_get_instance_private (self);
831 
832     if (!gtk_tree_selection_get_selected(selection, NULL, &selectedIter))
833         return TRUE;
834     selectedPath = gtk_tree_model_get_path(GTK_TREE_MODEL(priv->directory_model), &selectedIter);
835 
836     /* Open the node */
837     if (g_settings_get_boolean (MainSettings, "browse-expand-children"))
838     {
839         gtk_tree_view_expand_row (GTK_TREE_VIEW (priv->directory_view),
840                                   selectedPath, FALSE);
841     }
842     gtk_tree_path_free(selectedPath);
843 
844     /* Don't start a new reading, if another one is running... */
845     if (ReadingDirectory == TRUE)
846         return TRUE;
847 
848     /* Browser_Tree_Set_Node_Visible (priv->directory_view, selectedPath); */
849     gtk_tree_model_get(GTK_TREE_MODEL(priv->directory_model), &selectedIter,
850                        TREE_COLUMN_FULL_PATH, &pathName, -1);
851     if (!pathName)
852         return FALSE;
853 
854     et_application_window_update_et_file_from_ui (ET_APPLICATION_WINDOW (MainWindow));
855 
856     /* FIXME: Not clean to put this here. */
857     et_application_window_update_actions (ET_APPLICATION_WINDOW (MainWindow));
858 
859     /* Check if all files have been saved before changing the directory */
860     if (g_settings_get_boolean (MainSettings, "confirm-when-unsaved-files")
861         && et_file_list_check_all_saved (ETCore->ETFileList) != TRUE)
862     {
863         GtkWidget *msgdialog;
864         gint response;
865 
866         msgdialog = gtk_message_dialog_new(GTK_WINDOW(MainWindow),
867                                            GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
868                                            GTK_MESSAGE_QUESTION,
869                                            GTK_BUTTONS_NONE,
870                                            "%s",
871                                            _("Some files have been modified but not saved"));
872         gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (msgdialog),
873                                                   "%s",
874                                                   _("Do you want to save them before changing directory?"));
875         gtk_dialog_add_buttons (GTK_DIALOG (msgdialog), _("_Discard"),
876                                 GTK_RESPONSE_NO, _("_Cancel"),
877                                 GTK_RESPONSE_CANCEL, _("_Save"),
878                                 GTK_RESPONSE_YES, NULL);
879         gtk_dialog_set_default_response (GTK_DIALOG (msgdialog),
880                                          GTK_RESPONSE_YES);
881         gtk_window_set_title(GTK_WINDOW(msgdialog),_("Confirm Directory Change"));
882 
883         response = gtk_dialog_run(GTK_DIALOG(msgdialog));
884         gtk_widget_destroy(msgdialog);
885         switch (response)
886         {
887             case GTK_RESPONSE_YES:
888                 if (Save_All_Files_With_Answer(FALSE)==-1)
889                 {
890                     g_free (pathName);
891                     return TRUE;
892                 }
893                 break;
894             case GTK_RESPONSE_NO:
895                 break;
896             case GTK_RESPONSE_CANCEL:
897             case GTK_RESPONSE_DELETE_EVENT:
898                 g_free (pathName);
899                 return TRUE;
900                 break;
901             default:
902                 g_assert_not_reached ();
903                 break;
904         }
905     }
906 
907     /* Memorize the current path */
908     file = g_file_new_for_path (pathName);
909     et_browser_set_current_path (self, file);
910 
911     /* Display the selected path into the BrowserEntry */
912     parse_name = g_file_get_parse_name (file);
913     gtk_entry_set_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (priv->entry_combo))),
914                         parse_name);
915     g_free (parse_name);
916 
917     /* Start to read the directory */
918     /* Skip loading the file list the first time that it is shown, if the user
919      * has requested the read to be skipped. */
920     if (!first_read
921         || g_settings_get_boolean (MainSettings, "load-on-startup"))
922     {
923         gboolean dir_loaded;
924         GtkTreeIter parentIter;
925 
926         dir_loaded = Read_Directory(pathName);
927 
928         // If the directory can't be loaded, the directory musn't exist.
929         // So we load the parent node and refresh the children
930         if (dir_loaded == FALSE)
931         {
932             if (gtk_tree_selection_get_selected(selection, NULL, &selectedIter))
933             {
934                 /* If the path could not be read, then it is possible that it
935                  * has a subdirectory with readable permissions. In that case
936                  * do not refresh the children. */
937                 if (gtk_tree_model_iter_parent(GTK_TREE_MODEL(priv->directory_model),&parentIter,&selectedIter) )
938                 {
939                     selectedPath = gtk_tree_model_get_path(GTK_TREE_MODEL(priv->directory_model), &parentIter);
940                     gtk_tree_selection_select_iter (selection, &parentIter);
941                     if (gtk_tree_model_iter_has_child (GTK_TREE_MODEL (priv->directory_model),
942                                                        &selectedIter) == FALSE
943                                                        && !g_file_query_exists (file, NULL))
944                     {
945                         gtk_tree_view_collapse_row (GTK_TREE_VIEW (priv->directory_view),
946                                                     selectedPath);
947                         if (g_settings_get_boolean (MainSettings,
948                                                     "browse-expand-children"))
949                         {
950                             gtk_tree_view_expand_row (GTK_TREE_VIEW (priv->directory_view),
951                                                       selectedPath, FALSE);
952                         }
953                         gtk_tree_path_free (selectedPath);
954                     }
955                 }
956             }
957         }
958 
959     }else
960     {
961         /* As we don't use the function 'Read_Directory' we must add this function here */
962         et_application_window_update_actions (ET_APPLICATION_WINDOW (MainWindow));
963     }
964 
965     first_read = FALSE;
966 
967     g_object_unref (file);
968     g_free(pathName);
969     return FALSE;
970 }
971 
972 
973 #ifdef G_OS_WIN32
974 static gboolean
et_browser_win32_get_drive_root(EtBrowser * self,gchar * drive,GtkTreeIter * rootNode,GtkTreePath ** rootPath)975 et_browser_win32_get_drive_root (EtBrowser *self,
976                                  gchar *drive,
977                                  GtkTreeIter *rootNode,
978                                  GtkTreePath **rootPath)
979 {
980     EtBrowserPrivate *priv;
981     gint root_index;
982     gboolean found = FALSE;
983     GtkTreeIter parentNode;
984     gchar *nodeName;
985 
986     priv = et_browser_get_instance_private (self);
987 
988     gtk_tree_model_get_iter_first(GTK_TREE_MODEL(priv->directory_model), &parentNode);
989 
990     // Find root of path, ie: the drive letter
991     root_index = 0;
992 
993     do
994     {
995         gtk_tree_model_get(GTK_TREE_MODEL(priv->directory_model), &parentNode,
996                            TREE_COLUMN_FULL_PATH, &nodeName, -1);
997         if (strncasecmp(drive,nodeName, strlen(drive)) == 0)
998         {
999             g_free(nodeName);
1000             found = TRUE;
1001             break;
1002         }
1003         root_index++;
1004     } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(priv->directory_model), &parentNode));
1005 
1006     if (!found) return FALSE;
1007 
1008     *rootNode = parentNode;
1009     *rootPath = gtk_tree_path_new_from_indices(root_index, -1);
1010 
1011     return TRUE;
1012 }
1013 #endif /* G_OS_WIN32 */
1014 
1015 
1016 /*
1017  * et_browser_select_dir:
1018  *
1019  * Select the directory corresponding to the 'path' in the tree browser, but it
1020  * doesn't read it! Check if path is correct before selecting it.
1021  */
1022 void
et_browser_select_dir(EtBrowser * self,GFile * file)1023 et_browser_select_dir (EtBrowser *self,
1024                        GFile *file)
1025 {
1026     EtBrowserPrivate *priv;
1027     gchar *current_path;
1028     GtkTreePath *rootPath = NULL;
1029     GtkTreeIter parentNode, currentNode;
1030     gint index = 1; // Skip the first token as it is NULL due to leading /
1031     gchar **parts;
1032     gchar *nodeName;
1033     gchar *temp;
1034 
1035     priv = et_browser_get_instance_private (self);
1036 
1037     g_return_if_fail (priv->directory_view != NULL);
1038 
1039     /* Don't check here if the path is valid. It will be done later when
1040      * selecting a node in the tree */
1041 
1042     et_browser_set_current_path (self, file);
1043     current_path = g_file_get_path (file);
1044 
1045     parts = g_strsplit(current_path, G_DIR_SEPARATOR_S, 0);
1046     g_free (current_path);
1047 
1048     // Expand root node (fill parentNode and rootPath)
1049 #ifdef G_OS_WIN32
1050     if (!et_browser_win32_get_drive_root (self, parts[0], &parentNode,
1051                                           &rootPath))
1052     {
1053         return;
1054     }
1055 #else /* !G_OS_WIN32 */
1056     if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->directory_model),
1057                                         &parentNode))
1058     {
1059         g_message ("%s", "priv->directory_model is empty");
1060         return;
1061     }
1062 
1063     rootPath = gtk_tree_path_new_first();
1064 #endif /* !G_OS_WIN32 */
1065     if (rootPath)
1066     {
1067         gtk_tree_view_expand_to_path (GTK_TREE_VIEW (priv->directory_view),
1068                                       rootPath);
1069         gtk_tree_path_free(rootPath);
1070     }
1071 
1072     while (parts[index]) // it is NULL-terminated
1073     {
1074         if (parts[index] == '\0')
1075         {
1076             index++;
1077             continue;
1078         }
1079 
1080         if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(priv->directory_model), &currentNode, &parentNode))
1081         {
1082             gchar *path, *parent_path;
1083             GFile *directory;
1084 
1085             gtk_tree_model_get (GTK_TREE_MODEL (priv->directory_model),
1086                                 &parentNode, TREE_COLUMN_FULL_PATH,
1087                                 &parent_path, -1);
1088             path = g_build_filename (parent_path, parts[index], NULL);
1089             g_free (parent_path);
1090 
1091             directory = g_file_new_for_path (path);
1092 
1093             /* As dir name was not found in any node, check whether it exists
1094              * or not. */
1095             if (g_file_query_file_type (directory, G_FILE_QUERY_INFO_NONE, NULL)
1096                 == G_FILE_TYPE_DIRECTORY)
1097             {
1098                 /* It exists and is readable permission of parent directory is executable */
1099                 GIcon *icon;
1100                 GtkTreeIter iter;
1101 
1102                 /* Create a new node for this directory name. */
1103                 icon = get_gicon_for_path (path, ET_PATH_STATE_CLOSED);
1104 
1105                 gtk_tree_store_insert_with_values (GTK_TREE_STORE (priv->directory_model),
1106                                                    &iter, &parentNode, 0,
1107                                                    TREE_COLUMN_DIR_NAME, parts[index],
1108                                                    TREE_COLUMN_FULL_PATH, path,
1109                                                    TREE_COLUMN_HAS_SUBDIR, check_for_subdir (current_path),
1110                                                    TREE_COLUMN_SCANNED, TRUE,
1111                                                    TREE_COLUMN_ICON, icon, -1);
1112 
1113                 currentNode = iter;
1114                 g_object_unref (icon);
1115             }
1116             else
1117             {
1118                 g_object_unref (directory);
1119                 g_free (path);
1120                 break;
1121             }
1122 
1123             g_object_unref (directory);
1124             g_free (path);
1125         }
1126 
1127         do
1128         {
1129             gtk_tree_model_get(GTK_TREE_MODEL(priv->directory_model), &currentNode,
1130                                TREE_COLUMN_FULL_PATH, &temp, -1);
1131             nodeName = g_path_get_basename(temp);
1132             g_free(temp);
1133 #ifdef G_OS_WIN32
1134             if (strcasecmp(parts[index],nodeName) == 0)
1135 #else /* !G_OS_WIN32 */
1136             if (strcmp(parts[index],nodeName) == 0)
1137 #endif /* !G_OS_WIN32 */
1138             {
1139                 g_free(nodeName);
1140                 break;
1141             }
1142             g_free(nodeName);
1143 
1144             if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->directory_model),
1145                                            &currentNode))
1146             {
1147                 /* Path was not found in tree, such as when a hidden path was
1148                  * passed in, but hidden paths are set to not be displayed. */
1149                 g_strfreev (parts);
1150                 return;
1151             }
1152         } while (1);
1153 
1154         parentNode = currentNode;
1155         rootPath = gtk_tree_model_get_path(GTK_TREE_MODEL(priv->directory_model), &parentNode);
1156         if (rootPath)
1157         {
1158             gtk_tree_view_expand_to_path (GTK_TREE_VIEW (priv->directory_view),
1159                                           rootPath);
1160             gtk_tree_path_free(rootPath);
1161         }
1162         index++;
1163     }
1164 
1165     rootPath = gtk_tree_model_get_path(GTK_TREE_MODEL(priv->directory_model), &parentNode);
1166     if (rootPath)
1167     {
1168         gtk_tree_view_expand_to_path (GTK_TREE_VIEW (priv->directory_view),
1169                                       rootPath);
1170         Browser_Tree_Set_Node_Visible (priv->directory_view, rootPath);
1171         /* Select the node to load the corresponding directory. */
1172         gtk_tree_selection_select_path (gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->directory_view)),
1173                                         rootPath);
1174         gtk_tree_path_free(rootPath);
1175     }
1176 
1177     g_strfreev(parts);
1178     return;
1179 }
1180 
1181 /*
1182  * Callback to select-row event
1183  * Displays the file info of the lowest selected file in the right-hand pane
1184  */
1185 static void
Browser_List_Row_Selected(EtBrowser * self,GtkTreeSelection * selection)1186 Browser_List_Row_Selected (EtBrowser *self, GtkTreeSelection *selection)
1187 {
1188     EtBrowserPrivate *priv;
1189     gint n_selected;
1190     GtkTreePath *cursor_path;
1191     GtkTreeIter cursor_iter;
1192     ET_File *cursor_et_file;
1193 
1194 
1195     priv = et_browser_get_instance_private (self);
1196 
1197     n_selected = gtk_tree_selection_count_selected_rows (selection);
1198 
1199     /*
1200      * After a file is deleted, this function is called :
1201      * So we must handle the situation if no rows are selected
1202      */
1203     if (n_selected == 0)
1204     {
1205         /* TODO: Clear the tag area and file area. */
1206         return;
1207     }
1208 
1209     gtk_tree_view_get_cursor (GTK_TREE_VIEW (priv->file_view),
1210                               &cursor_path, NULL);
1211 
1212     if (!cursor_path)
1213     {
1214         return;
1215     }
1216 
1217     if (gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->file_model),
1218                                  &cursor_iter, cursor_path))
1219     {
1220         if (gtk_tree_selection_iter_is_selected (selection, &cursor_iter))
1221         {
1222             gtk_tree_model_get (GTK_TREE_MODEL (priv->file_model),
1223                                 &cursor_iter, LIST_FILE_POINTER,
1224                                 &cursor_et_file, -1);
1225             et_application_window_select_file_by_et_file (ET_APPLICATION_WINDOW (MainWindow),
1226                                                           cursor_et_file);
1227         }
1228         /* TODO: Clear the tag/file area if the cursor row was unselected, such
1229          * as by inverting the selection or Ctrl-clicking. */
1230     }
1231     else
1232     {
1233         g_warning ("%s", "Error getting iter from cursor path");
1234     }
1235 
1236     gtk_tree_path_free (cursor_path);
1237 }
1238 
1239 /*
1240  * Empty model, disabling Browser_List_Row_Selected () during clear because it
1241  * is called and causes crashes otherwise.
1242  */
1243 static void
et_browser_clear_file_model(EtBrowser * self)1244 et_browser_clear_file_model (EtBrowser *self)
1245 {
1246     EtBrowserPrivate *priv;
1247     GtkTreeSelection *selection;
1248 
1249     priv = et_browser_get_instance_private (self);
1250 
1251     selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->file_view));
1252 
1253     g_signal_handler_block (selection, priv->file_selected_handler);
1254 
1255     gtk_list_store_clear (priv->file_model);
1256     gtk_tree_view_columns_autosize (GTK_TREE_VIEW (priv->file_view));
1257 
1258     g_signal_handler_unblock (selection, priv->file_selected_handler);
1259 }
1260 
1261 /*
1262  * Loads the specified etfilelist into the browser list
1263  * Also supports optionally selecting a specific etfile
1264  * but be careful, this does not call Browser_List_Row_Selected !
1265  */
1266 void
et_browser_load_file_list(EtBrowser * self,GList * etfilelist,const ET_File * etfile_to_select)1267 et_browser_load_file_list (EtBrowser *self,
1268                            GList *etfilelist,
1269                            const ET_File *etfile_to_select)
1270 {
1271     EtBrowserPrivate *priv;
1272     GList *l;
1273     gboolean activate_bg_color = 0;
1274     GtkTreeIter rowIter;
1275 
1276     g_return_if_fail (ET_BROWSER (self));
1277 
1278     priv = et_browser_get_instance_private (self);
1279 
1280     et_browser_clear_file_model (self);
1281 
1282     for (l = g_list_first (etfilelist); l != NULL; l = g_list_next (l))
1283     {
1284         guint fileKey = ((ET_File *)l->data)->ETFileKey;
1285         const gchar *current_filename_utf8 = ((File_Name *)((ET_File *)l->data)->FileNameCur->data)->value_utf8;
1286         gchar *basename_utf8 = g_path_get_basename (current_filename_utf8);
1287         File_Tag *FileTag = ((File_Tag *)((ET_File *)l->data)->FileTag->data);
1288         gchar *track;
1289         gchar *disc;
1290 
1291         // Change background color when changing directory (the first row must not be changed)
1292         if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(priv->file_model), NULL) > 0)
1293         {
1294             gchar *dir1_utf8;
1295             gchar *dir2_utf8;
1296             const gchar *previous_filename_utf8 = ((File_Name *)((ET_File *)l->prev->data)->FileNameCur->data)->value_utf8;
1297 
1298             dir1_utf8 = g_path_get_dirname(previous_filename_utf8);
1299             dir2_utf8 = g_path_get_dirname(current_filename_utf8);
1300 
1301             if (g_utf8_collate(dir1_utf8, dir2_utf8) != 0)
1302                 activate_bg_color = !activate_bg_color;
1303 
1304             g_free(dir1_utf8);
1305             g_free(dir2_utf8);
1306         }
1307 
1308         /* File list displays the current filename (name on disc) and tag
1309          * fields. */
1310         track = g_strconcat(FileTag->track ? FileTag->track : "",FileTag->track_total ? "/" : NULL,FileTag->track_total,NULL);
1311         disc  = g_strconcat (FileTag->disc_number ? FileTag->disc_number : "",
1312                              FileTag->disc_total ? "/"
1313                                                  : NULL, FileTag->disc_total,
1314                              NULL);
1315 
1316         gtk_list_store_insert_with_values (priv->file_model, &rowIter, G_MAXINT,
1317                                            LIST_FILE_NAME, basename_utf8,
1318                                            LIST_FILE_POINTER, l->data,
1319                                            LIST_FILE_KEY, fileKey,
1320                                            LIST_FILE_OTHERDIR,
1321                                            activate_bg_color,
1322                                            LIST_FILE_TITLE, FileTag->title,
1323                                            LIST_FILE_ARTIST, FileTag->artist,
1324                                            LIST_FILE_ALBUM_ARTIST,
1325                                            FileTag->album_artist,
1326                                            LIST_FILE_ALBUM, FileTag->album,
1327                                            LIST_FILE_YEAR, FileTag->year,
1328                                            LIST_FILE_DISCNO, disc,
1329                                            LIST_FILE_TRACK, track,
1330                                            LIST_FILE_GENRE, FileTag->genre,
1331                                            LIST_FILE_COMMENT, FileTag->comment,
1332                                            LIST_FILE_COMPOSER,
1333                                            FileTag->composer,
1334                                            LIST_FILE_ORIG_ARTIST,
1335                                            FileTag->orig_artist,
1336                                            LIST_FILE_COPYRIGHT,
1337                                            FileTag->copyright,
1338                                            LIST_FILE_URL, FileTag->url,
1339                                            LIST_FILE_ENCODED_BY,
1340                                            FileTag->encoded_by, -1);
1341         g_free(basename_utf8);
1342         g_free(track);
1343         g_free (disc);
1344 
1345         if (etfile_to_select == l->data)
1346         {
1347             Browser_List_Select_File_By_Iter (self, &rowIter, TRUE);
1348             //ET_Display_File_Data_To_UI (l->data);
1349         }
1350 
1351         /* Set appearance of the row. */
1352         Browser_List_Set_Row_Appearance (self, &rowIter);
1353     }
1354 }
1355 
1356 
1357 /*
1358  * Update state of files in the list after changes (without clearing the list model!)
1359  *  - Refresh 'filename' is file saved,
1360  *  - Change color is something changed on the file
1361  */
1362 void
et_browser_refresh_list(EtBrowser * self)1363 et_browser_refresh_list (EtBrowser *self)
1364 {
1365     EtBrowserPrivate *priv;
1366     //GtkTreeIter iter;
1367     GtkTreePath *currentPath = NULL;
1368     GtkTreeIter iter;
1369     gint row;
1370     gchar *current_basename_utf8;
1371     gchar *track;
1372     gchar *disc;
1373     gboolean valid;
1374     GVariant *variant;
1375 
1376     g_return_if_fail (ET_BROWSER (self));
1377 
1378     priv = et_browser_get_instance_private (self);
1379 
1380     if (!ETCore->ETFileDisplayedList || !priv->file_view
1381     ||  gtk_tree_model_iter_n_children(GTK_TREE_MODEL(priv->file_model), NULL) == 0)
1382     {
1383         return;
1384     }
1385 
1386     // Browse the full list for changes
1387     //gtk_tree_model_get_iter_first(GTK_TREE_MODEL(priv->file_model), &iter);
1388     //    g_print("above worked %d rows\n", gtk_tree_model_iter_n_children(GTK_TREE_MODEL(priv->file_model), NULL));
1389 
1390     currentPath = gtk_tree_path_new_first();
1391 
1392     valid = gtk_tree_model_get_iter(GTK_TREE_MODEL(priv->file_model), &iter, currentPath);
1393     while (valid)
1394     {
1395         const ET_File *ETFile;
1396         const File_Tag *FileTag;
1397         const File_Name *FileName;
1398         // Refresh filename and other fields
1399         gtk_tree_model_get(GTK_TREE_MODEL(priv->file_model), &iter,
1400                            LIST_FILE_POINTER, &ETFile, -1);
1401 
1402         FileName = (File_Name *)ETFile->FileNameCur->data;
1403         FileTag  = (File_Tag *)ETFile->FileTag->data;
1404 
1405         current_basename_utf8 = g_path_get_basename(FileName->value_utf8);
1406         track = g_strconcat(FileTag->track ? FileTag->track : "",FileTag->track_total ? "/" : NULL,FileTag->track_total,NULL);
1407         disc  = g_strconcat (FileTag->disc_number ? FileTag->disc_number : "",
1408                              FileTag->disc_total ? "/" : NULL,
1409                              FileTag->disc_total, NULL);
1410 
1411         gtk_list_store_set(priv->file_model, &iter,
1412                            LIST_FILE_NAME,          current_basename_utf8,
1413                            LIST_FILE_TITLE,         FileTag->title,
1414                            LIST_FILE_ARTIST,        FileTag->artist,
1415                            LIST_FILE_ALBUM_ARTIST,  FileTag->album_artist,
1416 						   LIST_FILE_ALBUM,         FileTag->album,
1417                            LIST_FILE_YEAR,          FileTag->year,
1418                            LIST_FILE_DISCNO, disc,
1419                            LIST_FILE_TRACK,         track,
1420                            LIST_FILE_GENRE,         FileTag->genre,
1421                            LIST_FILE_COMMENT,       FileTag->comment,
1422                            LIST_FILE_COMPOSER,      FileTag->composer,
1423                            LIST_FILE_ORIG_ARTIST,   FileTag->orig_artist,
1424                            LIST_FILE_COPYRIGHT,     FileTag->copyright,
1425                            LIST_FILE_URL,           FileTag->url,
1426                            LIST_FILE_ENCODED_BY,    FileTag->encoded_by,
1427                            -1);
1428         g_free(current_basename_utf8);
1429         g_free(track);
1430         g_free (disc);
1431 
1432         Browser_List_Set_Row_Appearance (self, &iter);
1433 
1434         valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(priv->file_model), &iter);
1435     }
1436     gtk_tree_path_free(currentPath);
1437 
1438     variant = g_action_group_get_action_state (G_ACTION_GROUP (MainWindow),
1439                                                "file-artist-view");
1440 
1441     // When displaying Artist + Album lists => refresh also rows color
1442     if (strcmp (g_variant_get_string (variant, NULL), "artist") == 0)
1443     {
1444 
1445         for (row = 0; row < gtk_tree_model_iter_n_children (GTK_TREE_MODEL (priv->artist_model), NULL); row++)
1446         {
1447             if (row == 0)
1448                 currentPath = gtk_tree_path_new_first();
1449             else
1450                 gtk_tree_path_next(currentPath);
1451 
1452             gtk_tree_model_get_iter(GTK_TREE_MODEL(priv->artist_model), &iter, currentPath);
1453             Browser_Artist_List_Set_Row_Appearance (self, &iter);
1454         }
1455         gtk_tree_path_free(currentPath);
1456 
1457 
1458         for (row=0; row < gtk_tree_model_iter_n_children(GTK_TREE_MODEL(priv->album_model), NULL); row++)
1459         {
1460             if (row == 0)
1461                 currentPath = gtk_tree_path_new_first();
1462             else
1463                 gtk_tree_path_next(currentPath);
1464 
1465             gtk_tree_model_get_iter(GTK_TREE_MODEL(priv->album_model), &iter, currentPath);
1466             Browser_Album_List_Set_Row_Appearance (self, &iter);
1467         }
1468         gtk_tree_path_free(currentPath);
1469     }
1470 
1471     g_variant_unref (variant);
1472 }
1473 
1474 
1475 /*
1476  * Update state of one file in the list after changes (without clearing the clist!)
1477  *  - Refresh filename is file saved,
1478  *  - Change color is something change on the file
1479  */
1480 void
et_browser_refresh_file_in_list(EtBrowser * self,const ET_File * ETFile)1481 et_browser_refresh_file_in_list (EtBrowser *self,
1482                                  const ET_File *ETFile)
1483 {
1484     EtBrowserPrivate *priv;
1485     GList *selectedRow = NULL;
1486     GVariant *variant;
1487     GtkTreeSelection *selection;
1488     GtkTreeIter selectedIter;
1489     const ET_File *etfile;
1490     const File_Tag *FileTag;
1491     const File_Name *FileName;
1492     gboolean row_found = FALSE;
1493     gchar *current_basename_utf8;
1494     gchar *track;
1495     gchar *disc;
1496     gboolean valid;
1497     gchar *artist, *album;
1498 
1499     g_return_if_fail (ET_BROWSER (self));
1500 
1501     priv = et_browser_get_instance_private (self);
1502 
1503     if (!ETCore->ETFileDisplayedList || !priv->file_view || !ETFile ||
1504         gtk_tree_model_iter_n_children(GTK_TREE_MODEL(priv->file_model), NULL) == 0)
1505     {
1506         return;
1507     }
1508 
1509     // Search the row of the modified file to update it (when found: row_found=TRUE)
1510     // 1/3. Get position of ETFile in ETFileList
1511     if (row_found == FALSE)
1512     {
1513         valid = gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(priv->file_model), &selectedIter, NULL, ETFile->IndexKey-1);
1514         if (valid)
1515         {
1516             gtk_tree_model_get(GTK_TREE_MODEL(priv->file_model), &selectedIter,
1517                            LIST_FILE_POINTER, &etfile, -1);
1518             if (ETFile->ETFileKey == etfile->ETFileKey)
1519             {
1520                 row_found = TRUE;
1521             }
1522         }
1523     }
1524 
1525     // 2/3. Try with the selected file in list (works only if we select the same file)
1526     if (row_found == FALSE)
1527     {
1528         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->file_view));
1529         selectedRow = gtk_tree_selection_get_selected_rows(selection, NULL);
1530         if (selectedRow && selectedRow->data != NULL)
1531         {
1532             valid = gtk_tree_model_get_iter(GTK_TREE_MODEL(priv->file_model), &selectedIter,
1533                                     (GtkTreePath*) selectedRow->data);
1534             if (valid)
1535             {
1536                 gtk_tree_model_get(GTK_TREE_MODEL(priv->file_model), &selectedIter,
1537                                    LIST_FILE_POINTER, &etfile, -1);
1538                 if (ETFile->ETFileKey == etfile->ETFileKey)
1539                 {
1540                     row_found = TRUE;
1541                 }
1542             }
1543         }
1544 
1545         g_list_free_full (selectedRow, (GDestroyNotify)gtk_tree_path_free);
1546     }
1547 
1548     // 3/3. Fails, now we browse the full list to find it
1549     if (row_found == FALSE)
1550     {
1551         valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(priv->file_model), &selectedIter);
1552         while (valid)
1553         {
1554             gtk_tree_model_get(GTK_TREE_MODEL(priv->file_model), &selectedIter,
1555                                LIST_FILE_POINTER, &etfile, -1);
1556             if (ETFile->ETFileKey == etfile->ETFileKey)
1557             {
1558                 row_found = TRUE;
1559                 break;
1560             } else
1561             {
1562                 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(priv->file_model), &selectedIter);
1563             }
1564         }
1565     }
1566 
1567     // Error somewhere...
1568     if (row_found == FALSE)
1569         return;
1570 
1571     // Displayed the filename and refresh other fields
1572     FileName = (File_Name *)etfile->FileNameCur->data;
1573     FileTag  = (File_Tag *)etfile->FileTag->data;
1574 
1575     current_basename_utf8 = g_path_get_basename(FileName->value_utf8);
1576     track = g_strconcat(FileTag->track ? FileTag->track : "",FileTag->track_total ? "/" : NULL,FileTag->track_total,NULL);
1577     disc  = g_strconcat (FileTag->disc_number ? FileTag->disc_number : "",
1578                          FileTag->disc_total ? "/" : NULL, FileTag->disc_total,
1579                          NULL);
1580 
1581     gtk_list_store_set(priv->file_model, &selectedIter,
1582                        LIST_FILE_NAME,          current_basename_utf8,
1583                        LIST_FILE_TITLE,         FileTag->title,
1584                        LIST_FILE_ARTIST,        FileTag->artist,
1585                        LIST_FILE_ALBUM_ARTIST,  FileTag->album_artist,
1586 					   LIST_FILE_ALBUM,         FileTag->album,
1587                        LIST_FILE_YEAR,          FileTag->year,
1588                        LIST_FILE_DISCNO, disc,
1589                        LIST_FILE_TRACK,         track,
1590                        LIST_FILE_GENRE,         FileTag->genre,
1591                        LIST_FILE_COMMENT,       FileTag->comment,
1592                        LIST_FILE_COMPOSER,      FileTag->composer,
1593                        LIST_FILE_ORIG_ARTIST,   FileTag->orig_artist,
1594                        LIST_FILE_COPYRIGHT,     FileTag->copyright,
1595                        LIST_FILE_URL,           FileTag->url,
1596                        LIST_FILE_ENCODED_BY,    FileTag->encoded_by,
1597                        -1);
1598     g_free(current_basename_utf8);
1599     g_free(track);
1600     g_free (disc);
1601 
1602     /* Change appearance (line to red) if filename changed. */
1603     Browser_List_Set_Row_Appearance (self, &selectedIter);
1604 
1605     variant = g_action_group_get_action_state (G_ACTION_GROUP (MainWindow),
1606                                                "file-artist-view");
1607 
1608     /* When displaying Artist + Album lists => refresh also rows color. */
1609     if (strcmp (g_variant_get_string (variant, NULL), "artist") == 0)
1610     {
1611         gchar *current_artist = ((File_Tag *)ETFile->FileTag->data)->artist;
1612         gchar *current_album  = ((File_Tag *)ETFile->FileTag->data)->album;
1613 
1614         valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->artist_model),
1615                                                &selectedIter);
1616 
1617         while (valid)
1618         {
1619                 gtk_tree_model_get (GTK_TREE_MODEL (priv->artist_model),
1620                                     &selectedIter, ARTIST_NAME, &artist, -1);
1621 
1622                 if ((!current_artist && !artist)
1623                     || (current_artist && artist
1624                         && g_utf8_collate (current_artist, artist) == 0))
1625                 {
1626                     /* Set color of the row. */
1627                     Browser_Artist_List_Set_Row_Appearance (self,
1628                                                             &selectedIter);
1629                     g_free (artist);
1630                     break;
1631                 }
1632 
1633                 g_free (artist);
1634 
1635                 valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->artist_model),
1636                                                   &selectedIter);
1637         }
1638 
1639         //
1640         // FIX ME : see also if we must add a new line / or change list of the ETFile
1641         //
1642         valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->album_model),
1643                                                &selectedIter);
1644 
1645         while (valid)
1646         {
1647             gtk_tree_model_get (GTK_TREE_MODEL (priv->album_model),
1648                                 &selectedIter, ALBUM_NAME, &album, -1);
1649 
1650             if ((!current_album && !album)
1651                 || (current_album && album
1652                     && g_utf8_collate (current_album, album) == 0))
1653             {
1654                 /* Set color of the row. */
1655                 Browser_Album_List_Set_Row_Appearance (self, &selectedIter);
1656                 g_free (album);
1657                 break;
1658             }
1659 
1660             g_free (album);
1661 
1662             valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->album_model),
1663                                               &selectedIter);
1664         }
1665 
1666         //
1667         // FIX ME : see also if we must add a new line / or change list of the ETFile
1668         //
1669     }
1670 
1671     g_variant_unref (variant);
1672 }
1673 
1674 
1675 /*
1676  * Set the appearance of the row
1677  *  - change background according LIST_FILE_OTHERDIR
1678  *  - change foreground according file status (saved or not)
1679  */
1680 static void
Browser_List_Set_Row_Appearance(EtBrowser * self,GtkTreeIter * iter)1681 Browser_List_Set_Row_Appearance (EtBrowser *self, GtkTreeIter *iter)
1682 {
1683     EtBrowserPrivate *priv;
1684     ET_File *rowETFile = NULL;
1685     gboolean otherdir = FALSE;
1686     const GdkRGBA LIGHT_BLUE = { 0.866, 0.933, 1.0, 1.0 };
1687     const GdkRGBA *background;
1688     //gchar *temp = NULL;
1689 
1690     priv = et_browser_get_instance_private (self);
1691 
1692     if (iter == NULL)
1693         return;
1694 
1695     // Get the ETFile reference
1696     gtk_tree_model_get(GTK_TREE_MODEL(priv->file_model), iter,
1697                        LIST_FILE_POINTER,   &rowETFile,
1698                        LIST_FILE_OTHERDIR,  &otherdir,
1699                        //LIST_FILE_NAME,      &temp,
1700                        -1);
1701 
1702     // Must change background color?
1703     if (otherdir)
1704         background = &LIGHT_BLUE;
1705     else
1706         background = NULL;
1707 
1708     // Set text to bold/red if 'filename' or 'tag' changed
1709     if (!et_file_check_saved (rowETFile))
1710     {
1711         if (g_settings_get_boolean (MainSettings, "file-changed-bold"))
1712         {
1713             gtk_list_store_set(priv->file_model, iter,
1714                                LIST_FONT_WEIGHT,    PANGO_WEIGHT_BOLD,
1715                                LIST_ROW_BACKGROUND, background,
1716                                LIST_ROW_FOREGROUND, NULL, -1);
1717         } else
1718         {
1719             gtk_list_store_set(priv->file_model, iter,
1720                                LIST_FONT_WEIGHT,    PANGO_WEIGHT_NORMAL,
1721                                LIST_ROW_BACKGROUND, background,
1722                                LIST_ROW_FOREGROUND, &RED, -1);
1723         }
1724     } else
1725     {
1726         gtk_list_store_set(priv->file_model, iter,
1727                            LIST_FONT_WEIGHT,    PANGO_WEIGHT_NORMAL,
1728                            LIST_ROW_BACKGROUND, background,
1729                            LIST_ROW_FOREGROUND, NULL ,-1);
1730     }
1731 
1732     // Update text fields
1733     // Don't do it here
1734     /*if (rowETFile)
1735     {
1736         File_Tag *FileTag = ((File_Tag *)((ET_File *)rowETFile)->FileTag->data);
1737 
1738         gtk_list_store_set(priv->file_model, iter,
1739                            LIST_FILE_TITLE,         FileTag->title,
1740                            LIST_FILE_ARTIST,        FileTag->artist,
1741                            LIST_FILE_ALBUM_ARTIST,  FileTag->album_artist,
1742 						   LIST_FILE_ALBUM,         FileTag->album,
1743                            LIST_FILE_YEAR,          FileTag->year,
1744                            LIST_FILE_TRACK,         FileTag->track,
1745                            LIST_FILE_GENRE,         FileTag->genre,
1746                            LIST_FILE_COMMENT,       FileTag->comment,
1747                            LIST_FILE_COMPOSER,      FileTag->composer,
1748                            LIST_FILE_ORIG_ARTIST,   FileTag->orig_artist,
1749                            LIST_FILE_COPYRIGHT,     FileTag->copyright,
1750                            LIST_FILE_URL,           FileTag->url,
1751                            LIST_FILE_ENCODED_BY,    FileTag->encoded_by,
1752                            -1);
1753     }*/
1754 
1755     // Frees allocated item from gtk_tree_model_get...
1756     //g_free(temp);
1757 }
1758 
1759 
1760 /*
1761  * Remove a file from the list, by ETFile
1762  */
1763 void
et_browser_remove_file(EtBrowser * self,const ET_File * searchETFile)1764 et_browser_remove_file (EtBrowser *self,
1765                         const ET_File *searchETFile)
1766 {
1767     EtBrowserPrivate *priv;
1768     gint row;
1769     GtkTreePath *currentPath = NULL;
1770     GtkTreeIter currentIter;
1771     ET_File *currentETFile;
1772     gboolean valid;
1773 
1774     if (searchETFile == NULL)
1775         return;
1776 
1777     priv = et_browser_get_instance_private (self);
1778 
1779     // Go through the file list until it is found
1780     for (row=0; row < gtk_tree_model_iter_n_children(GTK_TREE_MODEL(priv->file_model), NULL); row++)
1781     {
1782         if (row == 0)
1783             currentPath = gtk_tree_path_new_first();
1784         else
1785             gtk_tree_path_next(currentPath);
1786 
1787         valid = gtk_tree_model_get_iter(GTK_TREE_MODEL(priv->file_model), &currentIter, currentPath);
1788         if (valid)
1789         {
1790             gtk_tree_model_get(GTK_TREE_MODEL(priv->file_model), &currentIter,
1791                                LIST_FILE_POINTER, &currentETFile, -1);
1792 
1793             if (currentETFile == searchETFile)
1794             {
1795                 gtk_list_store_remove(priv->file_model, &currentIter);
1796                 break;
1797             }
1798         }
1799     }
1800 
1801     gtk_tree_path_free (currentPath);
1802 }
1803 
1804 /*
1805  * Get ETFile pointer of a file from a Tree Iter
1806  */
1807 ET_File *
et_browser_get_et_file_from_path(EtBrowser * self,GtkTreePath * path)1808 et_browser_get_et_file_from_path (EtBrowser *self, GtkTreePath *path)
1809 {
1810     EtBrowserPrivate *priv;
1811     GtkTreeIter iter;
1812 
1813     g_return_val_if_fail (ET_BROWSER (self), NULL);
1814 
1815     priv = et_browser_get_instance_private (self);
1816 
1817     if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->file_model), &iter,
1818                                   path))
1819     {
1820         return NULL;
1821     }
1822 
1823     return et_browser_get_et_file_from_iter (self, &iter);
1824 }
1825 
1826 /*
1827  * Get ETFile pointer of a file from a Tree Iter
1828  */
1829 ET_File *
et_browser_get_et_file_from_iter(EtBrowser * self,GtkTreeIter * iter)1830 et_browser_get_et_file_from_iter (EtBrowser *self, GtkTreeIter *iter)
1831 {
1832     EtBrowserPrivate *priv;
1833     ET_File *etfile;
1834 
1835     g_return_val_if_fail (ET_BROWSER (self), NULL);
1836 
1837     priv = et_browser_get_instance_private (self);
1838 
1839     gtk_tree_model_get (GTK_TREE_MODEL (priv->file_model), iter,
1840                         LIST_FILE_POINTER, &etfile, -1);
1841     return etfile;
1842 }
1843 
1844 
1845 /*
1846  * Select the specified file in the list, by its ETFile
1847  */
1848 void
et_browser_select_file_by_et_file(EtBrowser * self,const ET_File * file,gboolean select_it)1849 et_browser_select_file_by_et_file (EtBrowser *self,
1850                                    const ET_File *file,
1851                                    gboolean select_it)
1852 {
1853     GtkTreePath *currentPath = NULL;
1854 
1855     currentPath = et_browser_select_file_by_et_file2 (self, file, select_it,
1856                                                       NULL);
1857 
1858     if (currentPath)
1859     {
1860         gtk_tree_path_free (currentPath);
1861     }
1862 }
1863 /*
1864  * Select the specified file in the list, by its ETFile
1865  *  - startPath : if set : starting path to try increase speed
1866  *  - returns allocated "currentPath" to free
1867  */
1868 GtkTreePath *
et_browser_select_file_by_et_file2(EtBrowser * self,const ET_File * searchETFile,gboolean select_it,GtkTreePath * startPath)1869 et_browser_select_file_by_et_file2 (EtBrowser *self,
1870                                     const ET_File *searchETFile,
1871                                     gboolean select_it,
1872                                     GtkTreePath *startPath)
1873 {
1874     EtBrowserPrivate *priv;
1875     gint row;
1876     GtkTreePath *currentPath = NULL;
1877     GtkTreeIter currentIter;
1878     ET_File *currentETFile;
1879     gboolean valid;
1880 
1881     g_return_val_if_fail (searchETFile != NULL, NULL);
1882 
1883     priv = et_browser_get_instance_private (self);
1884 
1885     // If the path is used, we try the next item (to increase speed), as it is correct in many cases...
1886     if (startPath)
1887     {
1888         // Try the next path
1889         gtk_tree_path_next(startPath);
1890         valid = gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->file_model),
1891                                          &currentIter, startPath);
1892         if (valid)
1893         {
1894             gtk_tree_model_get(GTK_TREE_MODEL(priv->file_model), &currentIter,
1895                                LIST_FILE_POINTER, &currentETFile, -1);
1896             // It is the good file?
1897             if (currentETFile == searchETFile)
1898             {
1899                 Browser_List_Select_File_By_Iter (self, &currentIter,
1900                                                   select_it);
1901                 return startPath;
1902             }
1903         }
1904     }
1905 
1906     // Else, we try the whole list...
1907     // Go through the file list until it is found
1908     currentPath = gtk_tree_path_new_first();
1909     for (row=0; row < gtk_tree_model_iter_n_children(GTK_TREE_MODEL(priv->file_model), NULL); row++)
1910     {
1911         valid = gtk_tree_model_get_iter(GTK_TREE_MODEL(priv->file_model), &currentIter, currentPath);
1912         if (valid)
1913         {
1914             gtk_tree_model_get(GTK_TREE_MODEL(priv->file_model), &currentIter,
1915                                LIST_FILE_POINTER, &currentETFile, -1);
1916 
1917             if (currentETFile == searchETFile)
1918             {
1919                 Browser_List_Select_File_By_Iter (self, &currentIter,
1920                                                   select_it);
1921                 return currentPath;
1922                 //break;
1923             }
1924         }
1925         gtk_tree_path_next(currentPath);
1926     }
1927     gtk_tree_path_free(currentPath);
1928 
1929     return NULL;
1930 }
1931 
1932 
1933 /*
1934  * Select the specified file in the list, by an iter
1935  */
1936 static void
Browser_List_Select_File_By_Iter(EtBrowser * self,GtkTreeIter * rowIter,gboolean select_it)1937 Browser_List_Select_File_By_Iter (EtBrowser *self,
1938                                   GtkTreeIter *rowIter,
1939                                   gboolean select_it)
1940 {
1941     EtBrowserPrivate *priv;
1942 
1943     priv = et_browser_get_instance_private (self);
1944 
1945     if (select_it)
1946     {
1947         GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->file_view));
1948 
1949         if (selection)
1950         {
1951             g_signal_handler_block (selection, priv->file_selected_handler);
1952             gtk_tree_selection_select_iter(selection, rowIter);
1953             g_signal_handler_unblock (selection, priv->file_selected_handler);
1954         }
1955     }
1956     et_browser_set_row_visible (self, rowIter);
1957 }
1958 
1959 /*
1960  * Select the specified file in the list, by a string representation of an iter
1961  * e.g. output of gtk_tree_model_get_string_from_iter()
1962  */
1963 void
et_browser_select_file_by_iter_string(EtBrowser * self,const gchar * stringIter,gboolean select_it)1964 et_browser_select_file_by_iter_string (EtBrowser *self,
1965                                        const gchar* stringIter,
1966                                        gboolean select_it)
1967 {
1968     EtBrowserPrivate *priv;
1969     GtkTreeIter iter;
1970 
1971     priv = et_browser_get_instance_private (self);
1972 
1973     g_return_if_fail (priv->file_model != NULL || priv->file_view != NULL);
1974 
1975     if (gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(priv->file_model), &iter, stringIter))
1976     {
1977         if (select_it)
1978         {
1979             GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->file_view));
1980 
1981             // FIX ME : Why signal was blocked if selected? Don't remember...
1982             if (selection)
1983             {
1984                 //g_signal_handlers_block_by_func(G_OBJECT(selection),G_CALLBACK(Browser_List_Row_Selected),NULL);
1985                 gtk_tree_selection_select_iter(selection, &iter);
1986                 //g_signal_handlers_unblock_by_func(G_OBJECT(selection),G_CALLBACK(Browser_List_Row_Selected),NULL);
1987             }
1988         }
1989         et_browser_set_row_visible (self, &iter);
1990     }
1991 }
1992 
1993 /*
1994  * Select the specified file in the list, by fuzzy string matching based on
1995  * the Damerau-Levenshtein Metric (patch from Santtu Lakkala - 23/08/2004)
1996  */
1997 ET_File *
et_browser_select_file_by_dlm(EtBrowser * self,const gchar * string,gboolean select_it)1998 et_browser_select_file_by_dlm (EtBrowser *self,
1999                                const gchar* string,
2000                                gboolean select_it)
2001 {
2002     EtBrowserPrivate *priv;
2003     GtkTreeIter iter;
2004     GtkTreeIter iter2;
2005     GtkTreeSelection *selection;
2006     ET_File *current_etfile = NULL, *retval = NULL;
2007     gchar *current_filename = NULL, *current_title = NULL;
2008     int max = 0, this;
2009 
2010     priv = et_browser_get_instance_private (self);
2011 
2012     g_return_val_if_fail (priv->file_model != NULL || priv->file_view != NULL, NULL);
2013 
2014     if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->file_model),
2015                                        &iter))
2016     {
2017         do
2018         {
2019             gtk_tree_model_get(GTK_TREE_MODEL(priv->file_model), &iter,
2020                                LIST_FILE_NAME,    &current_filename,
2021                                LIST_FILE_POINTER, &current_etfile, -1);
2022             current_title = ((File_Tag *)current_etfile->FileTag->data)->title;
2023 
2024             if ((this = dlm((current_title ? current_title : current_filename), string)) > max) // See "dlm.c"
2025             {
2026                 max = this;
2027                 iter2 = iter;
2028                 retval = current_etfile;
2029             }
2030 
2031             g_free (current_filename);
2032         } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(priv->file_model), &iter));
2033 
2034         if (select_it)
2035         {
2036             selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->file_view));
2037             if (selection)
2038             {
2039                 g_signal_handler_block (selection,
2040                                         priv->file_selected_handler);
2041                 gtk_tree_selection_select_iter(selection, &iter2);
2042                 g_signal_handler_unblock (selection,
2043                                           priv->file_selected_handler);
2044             }
2045         }
2046         et_browser_set_row_visible (self, &iter2);
2047     }
2048     return retval;
2049 }
2050 
2051 /*
2052  * Clear all entries on the file list
2053  */
2054 void
et_browser_clear(EtBrowser * self)2055 et_browser_clear (EtBrowser *self)
2056 {
2057     g_return_if_fail (ET_BROWSER (self));
2058 
2059     et_browser_clear_file_model (self);
2060     et_browser_clear_artist_model (self);
2061     et_browser_clear_album_model (self);
2062 }
2063 
2064 /*
2065  * Refresh the list sorting (call me after sort-mode has changed)
2066  */
2067 void
et_browser_refresh_sort(EtBrowser * self)2068 et_browser_refresh_sort (EtBrowser *self)
2069 {
2070     EtBrowserPrivate *priv;
2071 
2072     g_return_if_fail (ET_BROWSER (self));
2073 
2074     priv = et_browser_get_instance_private (self);
2075 
2076     gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (priv->file_model), 0,
2077                                      Browser_List_Sort_Func, NULL, NULL);
2078 }
2079 
2080 /*
2081  * Intelligently sort the file list based on the current sorting method
2082  * see also 'ET_Sort_File_List'
2083  */
2084 static gint
Browser_List_Sort_Func(GtkTreeModel * model,GtkTreeIter * a,GtkTreeIter * b,gpointer data)2085 Browser_List_Sort_Func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b,
2086                         gpointer data)
2087 {
2088     ET_File *ETFile1;
2089     ET_File *ETFile2;
2090     gint result = 0;
2091 
2092     gtk_tree_model_get(model, a, LIST_FILE_POINTER, &ETFile1, -1);
2093     gtk_tree_model_get(model, b, LIST_FILE_POINTER, &ETFile2, -1);
2094 
2095     switch (g_settings_get_enum (MainSettings, "sort-mode"))
2096     {
2097         case ET_SORT_MODE_ASCENDING_FILENAME:
2098             result = ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1, ETFile2);
2099             break;
2100         case ET_SORT_MODE_DESCENDING_FILENAME:
2101             result = ET_Comp_Func_Sort_File_By_Descending_Filename(ETFile1, ETFile2);
2102             break;
2103         case ET_SORT_MODE_ASCENDING_TITLE:
2104             result = ET_Comp_Func_Sort_File_By_Ascending_Title(ETFile1, ETFile2);
2105             break;
2106         case ET_SORT_MODE_DESCENDING_TITLE:
2107             result = ET_Comp_Func_Sort_File_By_Descending_Title(ETFile1, ETFile2);
2108             break;
2109         case ET_SORT_MODE_ASCENDING_ARTIST:
2110             result = ET_Comp_Func_Sort_File_By_Ascending_Artist(ETFile1, ETFile2);
2111             break;
2112         case ET_SORT_MODE_DESCENDING_ARTIST:
2113             result = ET_Comp_Func_Sort_File_By_Descending_Artist(ETFile1, ETFile2);
2114             break;
2115         case ET_SORT_MODE_ASCENDING_ALBUM_ARTIST:
2116             result = ET_Comp_Func_Sort_File_By_Ascending_Album_Artist(ETFile1, ETFile2);
2117             break;
2118         case ET_SORT_MODE_DESCENDING_ALBUM_ARTIST:
2119             result = ET_Comp_Func_Sort_File_By_Descending_Album_Artist(ETFile1, ETFile2);
2120             break;
2121         case ET_SORT_MODE_ASCENDING_ALBUM:
2122             result = ET_Comp_Func_Sort_File_By_Ascending_Album(ETFile1, ETFile2);
2123             break;
2124         case ET_SORT_MODE_DESCENDING_ALBUM:
2125             result = ET_Comp_Func_Sort_File_By_Descending_Album(ETFile1, ETFile2);
2126             break;
2127         case ET_SORT_MODE_ASCENDING_YEAR:
2128             result = ET_Comp_Func_Sort_File_By_Ascending_Year(ETFile1, ETFile2);
2129             break;
2130         case ET_SORT_MODE_DESCENDING_YEAR:
2131             result = ET_Comp_Func_Sort_File_By_Descending_Year(ETFile1, ETFile2);
2132             break;
2133         case ET_SORT_MODE_ASCENDING_DISC_NUMBER:
2134             result = et_comp_func_sort_file_by_ascending_disc_number (ETFile1,
2135                                                                       ETFile2);
2136             break;
2137         case ET_SORT_MODE_DESCENDING_DISC_NUMBER:
2138             result = et_comp_func_sort_file_by_descending_disc_number (ETFile1,
2139                                                                        ETFile2);
2140             break;
2141         case ET_SORT_MODE_ASCENDING_TRACK_NUMBER:
2142             result = ET_Comp_Func_Sort_File_By_Ascending_Track_Number (ETFile1, ETFile2);
2143             break;
2144         case ET_SORT_MODE_DESCENDING_TRACK_NUMBER:
2145             result = ET_Comp_Func_Sort_File_By_Descending_Track_Number (ETFile1, ETFile2);
2146             break;
2147         case ET_SORT_MODE_ASCENDING_GENRE:
2148             result = ET_Comp_Func_Sort_File_By_Ascending_Genre(ETFile1, ETFile2);
2149             break;
2150         case ET_SORT_MODE_DESCENDING_GENRE:
2151             result = ET_Comp_Func_Sort_File_By_Descending_Genre(ETFile1, ETFile2);
2152             break;
2153         case ET_SORT_MODE_ASCENDING_COMMENT:
2154             result = ET_Comp_Func_Sort_File_By_Ascending_Comment(ETFile1, ETFile2);
2155             break;
2156         case ET_SORT_MODE_DESCENDING_COMMENT:
2157             result = ET_Comp_Func_Sort_File_By_Descending_Comment(ETFile1, ETFile2);
2158             break;
2159         case ET_SORT_MODE_ASCENDING_COMPOSER:
2160             result = ET_Comp_Func_Sort_File_By_Ascending_Composer(ETFile1, ETFile2);
2161             break;
2162         case ET_SORT_MODE_DESCENDING_COMPOSER:
2163             result = ET_Comp_Func_Sort_File_By_Descending_Composer(ETFile1, ETFile2);
2164             break;
2165         case ET_SORT_MODE_ASCENDING_ORIG_ARTIST:
2166             result = ET_Comp_Func_Sort_File_By_Ascending_Orig_Artist(ETFile1, ETFile2);
2167             break;
2168         case ET_SORT_MODE_DESCENDING_ORIG_ARTIST:
2169             result = ET_Comp_Func_Sort_File_By_Descending_Orig_Artist(ETFile1, ETFile2);
2170             break;
2171         case ET_SORT_MODE_ASCENDING_COPYRIGHT:
2172             result = ET_Comp_Func_Sort_File_By_Ascending_Copyright(ETFile1, ETFile2);
2173             break;
2174         case ET_SORT_MODE_DESCENDING_COPYRIGHT:
2175             result = ET_Comp_Func_Sort_File_By_Descending_Copyright(ETFile1, ETFile2);
2176             break;
2177         case ET_SORT_MODE_ASCENDING_URL:
2178             result = ET_Comp_Func_Sort_File_By_Ascending_Url(ETFile1, ETFile2);
2179             break;
2180         case ET_SORT_MODE_DESCENDING_URL:
2181             result = ET_Comp_Func_Sort_File_By_Descending_Url(ETFile1, ETFile2);
2182             break;
2183         case ET_SORT_MODE_ASCENDING_ENCODED_BY:
2184             result = ET_Comp_Func_Sort_File_By_Ascending_Encoded_By(ETFile1, ETFile2);
2185             break;
2186         case ET_SORT_MODE_DESCENDING_ENCODED_BY:
2187             result = ET_Comp_Func_Sort_File_By_Descending_Encoded_By(ETFile1, ETFile2);
2188             break;
2189         case ET_SORT_MODE_ASCENDING_CREATION_DATE:
2190             result = ET_Comp_Func_Sort_File_By_Ascending_Creation_Date (ETFile1,
2191                                                                         ETFile2);
2192             break;
2193         case ET_SORT_MODE_DESCENDING_CREATION_DATE:
2194             result = ET_Comp_Func_Sort_File_By_Descending_Creation_Date (ETFile1,
2195                                                                          ETFile2);
2196             break;
2197         case ET_SORT_MODE_ASCENDING_FILE_TYPE:
2198             result = ET_Comp_Func_Sort_File_By_Ascending_File_Type(ETFile1, ETFile2);
2199             break;
2200         case ET_SORT_MODE_DESCENDING_FILE_TYPE:
2201             result = ET_Comp_Func_Sort_File_By_Descending_File_Type(ETFile1, ETFile2);
2202             break;
2203         case ET_SORT_MODE_ASCENDING_FILE_SIZE:
2204             result = ET_Comp_Func_Sort_File_By_Ascending_File_Size(ETFile1, ETFile2);
2205             break;
2206         case ET_SORT_MODE_DESCENDING_FILE_SIZE:
2207             result = ET_Comp_Func_Sort_File_By_Descending_File_Size(ETFile1, ETFile2);
2208             break;
2209         case ET_SORT_MODE_ASCENDING_FILE_DURATION:
2210             result = ET_Comp_Func_Sort_File_By_Ascending_File_Duration(ETFile1, ETFile2);
2211             break;
2212         case ET_SORT_MODE_DESCENDING_FILE_DURATION:
2213             result = ET_Comp_Func_Sort_File_By_Descending_File_Duration(ETFile1, ETFile2);
2214             break;
2215         case ET_SORT_MODE_ASCENDING_FILE_BITRATE:
2216             result = ET_Comp_Func_Sort_File_By_Ascending_File_Bitrate(ETFile1, ETFile2);
2217             break;
2218         case ET_SORT_MODE_DESCENDING_FILE_BITRATE:
2219             result = ET_Comp_Func_Sort_File_By_Descending_File_Bitrate(ETFile1, ETFile2);
2220             break;
2221         case ET_SORT_MODE_ASCENDING_FILE_SAMPLERATE:
2222             result = ET_Comp_Func_Sort_File_By_Ascending_File_Samplerate(ETFile1, ETFile2);
2223             break;
2224         case ET_SORT_MODE_DESCENDING_FILE_SAMPLERATE:
2225             result = ET_Comp_Func_Sort_File_By_Descending_File_Samplerate(ETFile1, ETFile2);
2226             break;
2227         default:
2228             g_assert_not_reached ();
2229             break;
2230     }
2231 
2232     return result;
2233 }
2234 
2235 /*
2236  * Select all files on the file list
2237  */
2238 void
et_browser_select_all(EtBrowser * self)2239 et_browser_select_all (EtBrowser *self)
2240 {
2241     EtBrowserPrivate *priv;
2242     GtkTreeSelection *selection;
2243 
2244     g_return_if_fail (ET_BROWSER (self));
2245 
2246     priv = et_browser_get_instance_private (self);
2247     selection = et_browser_get_selection (self);
2248 
2249     if (selection)
2250     {
2251         /* Must block the select signal to avoid the selecting, one by one, of
2252          * all files in the main files list. */
2253         g_signal_handler_block (selection, priv->file_selected_handler);
2254         gtk_tree_selection_select_all(selection);
2255         g_signal_handler_unblock (selection, priv->file_selected_handler);
2256     }
2257 }
2258 
2259 /*
2260  * Unselect all files on the file list
2261  */
2262 void
et_browser_unselect_all(EtBrowser * self)2263 et_browser_unselect_all (EtBrowser *self)
2264 {
2265     GtkTreeSelection *selection;
2266 
2267     selection = et_browser_get_selection (self);
2268 
2269     if (selection)
2270     {
2271         gtk_tree_selection_unselect_all (selection);
2272     }
2273 }
2274 
2275 /*
2276  * Invert the selection of the file list
2277  */
2278 void
et_browser_invert_selection(EtBrowser * self)2279 et_browser_invert_selection (EtBrowser *self)
2280 {
2281     EtBrowserPrivate *priv;
2282     GtkTreeIter iter;
2283     GtkTreeSelection *selection;
2284     gboolean valid;
2285 
2286     priv = et_browser_get_instance_private (self);
2287 
2288     g_return_if_fail (priv->file_model != NULL || priv->file_view != NULL);
2289 
2290     selection = et_browser_get_selection (self);
2291     if (selection)
2292     {
2293         /* Must block the select signal to avoid selecting all files (one by
2294          * one) in the main files list. */
2295         g_signal_handler_block (selection, priv->file_selected_handler);
2296         valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(priv->file_model), &iter);
2297         while (valid)
2298         {
2299             if (gtk_tree_selection_iter_is_selected(selection, &iter))
2300             {
2301                 gtk_tree_selection_unselect_iter(selection, &iter);
2302             } else
2303             {
2304                 gtk_tree_selection_select_iter(selection, &iter);
2305             }
2306             valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(priv->file_model), &iter);
2307         }
2308         g_signal_handler_unblock (selection, priv->file_selected_handler);
2309     }
2310 }
2311 
2312 void
et_browser_clear_artist_model(EtBrowser * self)2313 et_browser_clear_artist_model (EtBrowser *self)
2314 {
2315     EtBrowserPrivate *priv;
2316     GtkTreeSelection *selection;
2317 
2318     /* Empty Model, Disable Browser_Artist_List_Row_Selected() during clear
2319      * because it may be called and may crash.
2320     */
2321 
2322     priv = et_browser_get_instance_private (self);
2323 
2324     selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->artist_view));
2325 
2326     g_signal_handler_block (selection, priv->artist_selected_handler);
2327 
2328     gtk_list_store_clear (priv->artist_model);
2329 
2330     g_signal_handler_unblock (selection, priv->artist_selected_handler);
2331 }
2332 
2333 static void
Browser_Artist_List_Load_Files(EtBrowser * self,ET_File * etfile_to_select)2334 Browser_Artist_List_Load_Files (EtBrowser *self, ET_File *etfile_to_select)
2335 {
2336     EtBrowserPrivate *priv;
2337     GList *AlbumList;
2338     GList *etfilelist;
2339     ET_File *etfile;
2340     GList *l;
2341     GList *m;
2342     GtkTreeIter iter;
2343     GtkTreeSelection *selection;
2344     gchar *artistname, *artist_to_select = NULL;
2345 
2346     priv = et_browser_get_instance_private (self);
2347 
2348     g_return_if_fail (priv->artist_view != NULL);
2349 
2350     if (etfile_to_select)
2351         artist_to_select = ((File_Tag *)etfile_to_select->FileTag->data)->artist;
2352 
2353     et_browser_clear_artist_model (self);
2354     selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->artist_view));
2355 
2356     for (l = ETCore->ETArtistAlbumFileList; l != NULL; l = g_list_next (l))
2357     {
2358         gint   nbr_files = 0;
2359         GdkPixbuf* pixbuf;
2360 
2361         // Insert a line for each artist
2362         AlbumList = (GList *)l->data;
2363         etfilelist = (GList *)AlbumList->data;
2364         etfile     = (ET_File *)etfilelist->data;
2365         artistname = ((File_Tag *)etfile->FileTag->data)->artist;
2366 
2367         // Third column text : number of files
2368         for (m = g_list_first (AlbumList); m != NULL; m = g_list_next (m))
2369         {
2370             nbr_files += g_list_length (g_list_first ((GList *)m->data));
2371         }
2372 
2373         /* Add the new row. */
2374         pixbuf = gdk_pixbuf_new_from_resource ("/org/gnome/EasyTAG/images/artist.png",
2375                                                NULL);
2376         gtk_list_store_insert_with_values (priv->artist_model, &iter, G_MAXINT,
2377                                            ARTIST_PIXBUF, pixbuf,
2378                                            ARTIST_NAME, artistname,
2379                                            ARTIST_NUM_ALBUMS,
2380                                            g_list_length (g_list_first (AlbumList)),
2381                                            ARTIST_NUM_FILES, nbr_files,
2382                                            ARTIST_ALBUM_LIST_POINTER,
2383                                            AlbumList, -1);
2384 
2385         g_object_unref (pixbuf);
2386 
2387         // Todo: Use something better than string comparison
2388         if ( (!artistname && !artist_to_select)
2389         ||   (artistname  &&  artist_to_select && strcmp(artistname,artist_to_select) == 0) )
2390         {
2391             GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(priv->artist_model), &iter);
2392 
2393             g_signal_handler_block (selection, priv->artist_selected_handler);
2394             gtk_tree_selection_select_iter(selection, &iter);
2395             g_signal_handler_unblock (selection, priv->artist_selected_handler);
2396 
2397             gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(priv->artist_view), path, NULL, FALSE, 0, 0);
2398             gtk_tree_path_free(path);
2399 
2400             Browser_Album_List_Load_Files (self, AlbumList, etfile_to_select);
2401 
2402             // Now that we've found the artist, no need to continue searching
2403             artist_to_select = NULL;
2404         }
2405 
2406         // Set color of the row
2407         Browser_Artist_List_Set_Row_Appearance (self, &iter);
2408     }
2409 
2410     // Select the first line if we weren't asked to select anything
2411     if (!etfile_to_select && gtk_tree_model_get_iter_first(GTK_TREE_MODEL(priv->artist_model), &iter))
2412     {
2413         gtk_tree_model_get(GTK_TREE_MODEL(priv->artist_model), &iter,
2414                            ARTIST_ALBUM_LIST_POINTER, &AlbumList,
2415                            -1);
2416         et_application_window_update_et_file_from_ui (ET_APPLICATION_WINDOW (MainWindow));
2417         Browser_Album_List_Load_Files (self, AlbumList,NULL);
2418     }
2419 }
2420 
2421 
2422 /*
2423  * Callback to select-row event
2424  */
2425 static void
Browser_Artist_List_Row_Selected(EtBrowser * self,GtkTreeSelection * selection)2426 Browser_Artist_List_Row_Selected (EtBrowser *self, GtkTreeSelection* selection)
2427 {
2428     EtBrowserPrivate *priv;
2429     GList *AlbumList;
2430     GtkTreeIter iter;
2431 
2432     priv = et_browser_get_instance_private (self);
2433 
2434     // Display the relevant albums
2435     if(!gtk_tree_selection_get_selected(selection, NULL, &iter))
2436         return; // We might be called with no row selected
2437 
2438     et_application_window_update_et_file_from_ui (ET_APPLICATION_WINDOW (MainWindow));
2439 
2440     gtk_tree_model_get (GTK_TREE_MODEL (priv->artist_model), &iter,
2441                         ARTIST_ALBUM_LIST_POINTER, &AlbumList, -1);
2442     Browser_Album_List_Load_Files (self, AlbumList, NULL);
2443 }
2444 
2445 /*
2446  * Set the color of the row of priv->artist_view
2447  */
2448 static void
Browser_Artist_List_Set_Row_Appearance(EtBrowser * self,GtkTreeIter * iter)2449 Browser_Artist_List_Set_Row_Appearance (EtBrowser *self, GtkTreeIter *iter)
2450 {
2451     EtBrowserPrivate *priv;
2452     GList *l;
2453     GList *m;
2454     gboolean not_all_saved = FALSE;
2455 
2456     priv = et_browser_get_instance_private (self);
2457 
2458     // Change the style (red/bold) of the row if one of the files was changed
2459     for (gtk_tree_model_get (GTK_TREE_MODEL (priv->artist_model), iter,
2460                              ARTIST_ALBUM_LIST_POINTER, &l, -1);
2461          l != NULL; l = g_list_next (l))
2462     {
2463         for (m = (GList *)l->data; m != NULL; m = g_list_next (m))
2464         {
2465             if (!et_file_check_saved ((ET_File *)m->data))
2466             {
2467                 if (g_settings_get_boolean (MainSettings, "file-changed-bold"))
2468                 {
2469                     // Set the font-style to "bold"
2470                     gtk_list_store_set(priv->artist_model, iter,
2471                                        ARTIST_FONT_WEIGHT, PANGO_WEIGHT_BOLD, -1);
2472                 } else
2473                 {
2474                     // Set the background-color to "red"
2475                     gtk_list_store_set(priv->artist_model, iter,
2476                                        ARTIST_FONT_WEIGHT,    PANGO_WEIGHT_NORMAL,
2477                                        ARTIST_ROW_FOREGROUND, &RED, -1);
2478                 }
2479                 not_all_saved = TRUE;
2480                 break;
2481             }
2482         }
2483     }
2484 
2485     // Reset style if all files saved
2486     if (not_all_saved == FALSE)
2487     {
2488         gtk_list_store_set(priv->artist_model, iter,
2489                            ARTIST_FONT_WEIGHT,    PANGO_WEIGHT_NORMAL,
2490                            ARTIST_ROW_FOREGROUND, NULL, -1);
2491     }
2492 }
2493 
2494 void
et_browser_clear_album_model(EtBrowser * self)2495 et_browser_clear_album_model (EtBrowser *self)
2496 {
2497     EtBrowserPrivate *priv;
2498     GtkTreeSelection *selection;
2499     gboolean valid;
2500     GtkTreeIter iter;
2501 
2502     g_return_if_fail (ET_BROWSER (self));
2503 
2504     priv = et_browser_get_instance_private (self);
2505 
2506     /* Free the attached list in the "all albums" row. */
2507     valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->album_model),
2508                                            &iter);
2509 
2510     while (valid)
2511     {
2512         GList *l;
2513         gboolean all_albums_row = FALSE;
2514 
2515         gtk_tree_model_get (GTK_TREE_MODEL (priv->album_model), &iter,
2516                             ALBUM_ETFILE_LIST_POINTER, &l,
2517                             ALBUM_ALL_ALBUMS_ROW, &all_albums_row, -1);
2518 
2519         if (all_albums_row && l)
2520         {
2521             g_list_free (l);
2522             break;
2523         }
2524 
2525         valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->album_model),
2526                                           &iter);
2527     }
2528 
2529     /* Empty model, disable Browser_Album_List_Row_Selected () during clear
2530      * because it is called and crashed. */
2531 
2532     selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->album_view));
2533 
2534     g_signal_handler_block (selection, priv->album_selected_handler);
2535 
2536     gtk_list_store_clear (priv->album_model);
2537 
2538     g_signal_handler_unblock (selection, priv->album_selected_handler);
2539 }
2540 
2541 /*
2542  * Load the list of Albums for each Artist
2543  */
2544 static void
Browser_Album_List_Load_Files(EtBrowser * self,GList * albumlist,ET_File * etfile_to_select)2545 Browser_Album_List_Load_Files (EtBrowser *self,
2546                                GList *albumlist,
2547                                ET_File *etfile_to_select)
2548 {
2549     EtBrowserPrivate *priv;
2550     GList *l;
2551     GList *etfilelist = NULL;
2552     ET_File *etfile;
2553     GtkTreeIter iter;
2554     GtkTreeSelection *selection;
2555     gchar *albumname, *album_to_select = NULL;
2556 
2557     priv = et_browser_get_instance_private (self);
2558 
2559     g_return_if_fail (priv->album_view != NULL);
2560 
2561     if (etfile_to_select)
2562         album_to_select = ((File_Tag *)etfile_to_select->FileTag->data)->album;
2563 
2564     et_browser_clear_album_model (self);
2565     selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->album_view));
2566 
2567     // Create a first row to select all albums of the artist
2568     for (l = albumlist; l != NULL; l = g_list_next (l))
2569     {
2570         GList *etfilelist_tmp;
2571 
2572         etfilelist_tmp = (GList *)l->data;
2573         // We must make a copy to not "alter" the initial list by appending another list
2574         etfilelist_tmp = g_list_copy(etfilelist_tmp);
2575         etfilelist = g_list_concat(etfilelist, etfilelist_tmp);
2576     }
2577 
2578     gtk_list_store_insert_with_values (priv->album_model, &iter, G_MAXINT,
2579                                        ALBUM_NAME, _("All albums"),
2580                                        ALBUM_NUM_FILES,
2581                                        g_list_length (g_list_first (etfilelist)),
2582                                        ALBUM_ETFILE_LIST_POINTER, etfilelist,
2583                                        ALBUM_ALL_ALBUMS_ROW, TRUE,
2584                                        -1);
2585 
2586     gtk_list_store_insert_with_values (priv->album_model, &iter, G_MAXINT,
2587                                        ALBUM_ALL_ALBUMS_ROW, FALSE,
2588                                        ALBUM_ALL_ALBUMS_SEPARATOR_ROW, TRUE,
2589                                        -1);
2590 
2591     // Create a line for each album of the artist
2592     for (l = albumlist; l != NULL; l = g_list_next (l))
2593     {
2594         GIcon *icon;
2595 
2596         // Insert a line for each album
2597         etfilelist = (GList *)l->data;
2598         etfile     = (ET_File *)etfilelist->data;
2599         albumname  = ((File_Tag *)etfile->FileTag->data)->album;
2600 
2601         /* TODO: Make the icon use the symbolic variant. */
2602         icon = g_themed_icon_new_with_default_fallbacks ("media-optical-cd-audio");
2603 
2604         /* Add the new row. */
2605         gtk_list_store_insert_with_values (priv->album_model, &iter, G_MAXINT,
2606                                            ALBUM_GICON, icon,
2607                                            ALBUM_NAME, albumname,
2608                                            ALBUM_NUM_FILES,
2609                                            g_list_length (g_list_first (etfilelist)),
2610                                            ALBUM_ETFILE_LIST_POINTER,
2611                                            etfilelist, -1);
2612 
2613         g_object_unref (icon);
2614 
2615         if ( (!albumname && !album_to_select)
2616         ||   (albumname &&  album_to_select && strcmp(albumname,album_to_select) == 0) )
2617         {
2618             GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(priv->album_model), &iter);
2619 
2620             g_signal_handler_block (selection, priv->album_selected_handler);
2621             gtk_tree_selection_select_iter(selection, &iter);
2622             g_signal_handler_unblock (selection, priv->album_selected_handler);
2623 
2624             gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(priv->album_view), path, NULL, FALSE, 0, 0);
2625             gtk_tree_path_free(path);
2626 
2627             et_displayed_file_list_set (etfilelist);
2628             et_browser_load_file_list (self, etfilelist, etfile_to_select);
2629 
2630             // Now that we've found the album, no need to continue searching
2631             album_to_select = NULL;
2632         }
2633 
2634         // Set color of the row
2635         Browser_Album_List_Set_Row_Appearance (self, &iter);
2636     }
2637 
2638     // Select the first line if we werent asked to select anything
2639     if (!etfile_to_select && gtk_tree_model_get_iter_first(GTK_TREE_MODEL(priv->album_model), &iter))
2640     {
2641         gtk_tree_model_get(GTK_TREE_MODEL(priv->album_model), &iter,
2642                            ALBUM_ETFILE_LIST_POINTER, &etfilelist,
2643                            -1);
2644         et_application_window_update_et_file_from_ui (ET_APPLICATION_WINDOW (MainWindow));
2645 
2646         /* Set the attached list as "Displayed List". */
2647         et_displayed_file_list_set (etfilelist);
2648         et_browser_load_file_list (self, etfilelist, NULL);
2649 
2650         /* Displays the first item. */
2651         et_application_window_select_file_by_et_file (ET_APPLICATION_WINDOW (MainWindow),
2652                                                       (ET_File *)etfilelist->data);
2653     }
2654 }
2655 
2656 /*
2657  * Callback to select-row event
2658  */
2659 static void
Browser_Album_List_Row_Selected(EtBrowser * self,GtkTreeSelection * selection)2660 Browser_Album_List_Row_Selected (EtBrowser *self, GtkTreeSelection *selection)
2661 {
2662     EtBrowserPrivate *priv;
2663     GList *etfilelist;
2664     GtkTreeIter iter;
2665 
2666     priv = et_browser_get_instance_private (self);
2667 
2668     // We might be called with no rows selected
2669     if (!gtk_tree_selection_get_selected(selection, NULL, &iter))
2670         return;
2671 
2672     gtk_tree_model_get (GTK_TREE_MODEL (priv->album_model), &iter,
2673                        ALBUM_ETFILE_LIST_POINTER, &etfilelist, -1);
2674 
2675     et_application_window_update_et_file_from_ui (ET_APPLICATION_WINDOW (MainWindow));
2676 
2677     /* Set the attached list as "Displayed List". */
2678     et_displayed_file_list_set (etfilelist);
2679 
2680     et_browser_load_file_list (self, etfilelist, NULL);
2681 
2682     /* Displays the first item. */
2683     et_application_window_select_file_by_et_file (ET_APPLICATION_WINDOW (MainWindow),
2684                                                   (ET_File *)etfilelist->data);
2685 }
2686 
2687 /*
2688  * Set the color of the row of priv->album_view
2689  */
2690 static void
Browser_Album_List_Set_Row_Appearance(EtBrowser * self,GtkTreeIter * iter)2691 Browser_Album_List_Set_Row_Appearance (EtBrowser *self, GtkTreeIter *iter)
2692 {
2693     EtBrowserPrivate *priv;
2694     GList *l;
2695     gboolean not_all_saved = FALSE;
2696 
2697     priv = et_browser_get_instance_private (self);
2698 
2699     // Change the style (red/bold) of the row if one of the files was changed
2700     for (gtk_tree_model_get (GTK_TREE_MODEL (priv->album_model), iter,
2701                              ALBUM_ETFILE_LIST_POINTER, &l, -1);
2702          l != NULL; l = g_list_next (l))
2703     {
2704         if (!et_file_check_saved ((ET_File *)l->data))
2705         {
2706             if (g_settings_get_boolean (MainSettings, "file-changed-bold"))
2707             {
2708                 // Set the font-style to "bold"
2709                 gtk_list_store_set(priv->album_model, iter,
2710                                    ALBUM_FONT_WEIGHT, PANGO_WEIGHT_BOLD, -1);
2711             } else
2712             {
2713                 // Set the background-color to "red"
2714                 gtk_list_store_set(priv->album_model, iter,
2715                                    ALBUM_FONT_WEIGHT,    PANGO_WEIGHT_NORMAL,
2716                                    ALBUM_ROW_FOREGROUND, &RED, -1);
2717             }
2718             not_all_saved = TRUE;
2719             break;
2720         }
2721     }
2722 
2723     // Reset style if all files saved
2724     if (not_all_saved == FALSE)
2725     {
2726         gtk_list_store_set(priv->album_model, iter,
2727                            ALBUM_FONT_WEIGHT,    PANGO_WEIGHT_NORMAL,
2728                            ALBUM_ROW_FOREGROUND, NULL, -1);
2729     }
2730 }
2731 
2732 void
et_browser_set_display_mode(EtBrowser * self,EtBrowserMode mode)2733 et_browser_set_display_mode (EtBrowser *self,
2734                              EtBrowserMode mode)
2735 {
2736     EtBrowserPrivate *priv;
2737     ET_File *etfile = ETCore->ETFileDisplayed; // ETFile to display again after changing browser view
2738 
2739     g_return_if_fail (ET_BROWSER (self));
2740 
2741     priv = et_browser_get_instance_private (self);
2742 
2743     et_application_window_update_et_file_from_ui (ET_APPLICATION_WINDOW (MainWindow));
2744 
2745     switch (mode)
2746     {
2747         case ET_BROWSER_MODE_FILE:
2748             /* Set the whole list as "Displayed list". */
2749             et_displayed_file_list_set (ETCore->ETFileList);
2750 
2751             /* Display Tree Browser. */
2752             gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->directory_album_artist_notebook),
2753                                            0);
2754             et_browser_load_file_list (self, ETCore->ETFileDisplayedList,
2755                                        etfile);
2756 
2757             /* Displays the first file if nothing specified. */
2758             if (!etfile)
2759             {
2760                 GList *etfilelist = ET_Displayed_File_List_First ();
2761                 if (etfilelist)
2762                 {
2763                     etfile = (ET_File *)etfilelist->data;
2764                 }
2765 
2766                 et_application_window_select_file_by_et_file (ET_APPLICATION_WINDOW (MainWindow),
2767                                                               etfile);
2768             }
2769             break;
2770         case ET_BROWSER_MODE_ARTIST:
2771             /* Display Artist + Album lists. */
2772             gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->directory_album_artist_notebook),
2773                                            1);
2774             if (ETCore->ETArtistAlbumFileList)
2775             {
2776                 et_artist_album_file_list_free (ETCore->ETArtistAlbumFileList);
2777             }
2778 
2779             ETCore->ETArtistAlbumFileList = et_artist_album_list_new_from_file_list (ETCore->ETFileList);
2780             Browser_Artist_List_Load_Files (self, etfile);
2781             break;
2782         default:
2783             g_assert_not_reached ();
2784     }
2785     //ET_Display_File_Data_To_UI(etfile); // Causes a crash
2786 }
2787 
2788 /*
2789  * Disable (FALSE) / Enable (TRUE) all user widgets in the browser area (Tree + List + Entry)
2790  */
2791 void
et_browser_set_sensitive(EtBrowser * self,gboolean sensitive)2792 et_browser_set_sensitive (EtBrowser *self, gboolean sensitive)
2793 {
2794     EtBrowserPrivate *priv;
2795 
2796     priv = et_browser_get_instance_private (self);
2797 
2798     gtk_widget_set_sensitive (GTK_WIDGET (priv->entry_combo), sensitive);
2799     gtk_widget_set_sensitive (GTK_WIDGET (priv->directory_view), sensitive);
2800     gtk_widget_set_sensitive (GTK_WIDGET (priv->file_view), sensitive);
2801     gtk_widget_set_sensitive (GTK_WIDGET (priv->artist_view), sensitive);
2802     gtk_widget_set_sensitive (GTK_WIDGET (priv->album_view), sensitive);
2803     gtk_widget_set_sensitive (GTK_WIDGET (priv->open_button), sensitive);
2804     gtk_widget_set_sensitive (GTK_WIDGET (priv->files_label), sensitive);
2805 }
2806 
2807 static void
do_popup_menu(EtBrowser * self,GdkEventButton * event,GtkTreeView * view,GtkWidget * menu)2808 do_popup_menu (EtBrowser *self,
2809                GdkEventButton *event,
2810                GtkTreeView *view,
2811                GtkWidget *menu)
2812 {
2813     gint button;
2814     gint event_time;
2815 
2816     if (event)
2817     {
2818         button = event->button;
2819         event_time = event->time;
2820     }
2821     else
2822     {
2823         button = 0;
2824         event_time = gtk_get_current_event_time ();
2825     }
2826 
2827     /* TODO: Add popup positioning function. */
2828     gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button,
2829                     event_time);
2830 }
2831 
2832 static void
select_row_for_button_press_event(GtkTreeView * treeview,GdkEventButton * event)2833 select_row_for_button_press_event (GtkTreeView *treeview,
2834                                    GdkEventButton *event)
2835 {
2836     if (event->window == gtk_tree_view_get_bin_window (treeview))
2837     {
2838         GtkTreePath *tree_path;
2839 
2840         if (gtk_tree_view_get_path_at_pos (treeview, event->x,
2841                                            event->y, &tree_path, NULL,
2842                                            NULL,NULL))
2843         {
2844             gtk_tree_selection_select_path (gtk_tree_view_get_selection (treeview),
2845                                             tree_path);
2846             gtk_tree_path_free (tree_path);
2847         }
2848     }
2849 }
2850 
2851 static gboolean
on_album_tree_popup_menu(GtkWidget * treeview,EtBrowser * self)2852 on_album_tree_popup_menu (GtkWidget *treeview,
2853                           EtBrowser *self)
2854 {
2855     EtBrowserPrivate *priv;
2856 
2857     priv = et_browser_get_instance_private (self);
2858 
2859     do_popup_menu (self, NULL, GTK_TREE_VIEW (treeview), priv->album_menu);
2860 
2861     return GDK_EVENT_STOP;
2862 }
2863 
2864 static gboolean
on_artist_tree_popup_menu(GtkWidget * treeview,EtBrowser * self)2865 on_artist_tree_popup_menu (GtkWidget *treeview,
2866                            EtBrowser *self)
2867 {
2868     EtBrowserPrivate *priv;
2869 
2870     priv = et_browser_get_instance_private (self);
2871 
2872     do_popup_menu (self, NULL, GTK_TREE_VIEW (treeview), priv->artist_menu);
2873 
2874     return GDK_EVENT_STOP;
2875 }
2876 
2877 static gboolean
on_directory_tree_popup_menu(GtkWidget * treeview,EtBrowser * self)2878 on_directory_tree_popup_menu (GtkWidget *treeview,
2879                               EtBrowser *self)
2880 {
2881     EtBrowserPrivate *priv;
2882 
2883     priv = et_browser_get_instance_private (self);
2884 
2885     do_popup_menu (self, NULL, GTK_TREE_VIEW (treeview), priv->directory_view_menu);
2886 
2887     return GDK_EVENT_STOP;
2888 }
2889 
2890 static gboolean
on_file_tree_popup_menu(GtkWidget * treeview,EtBrowser * self)2891 on_file_tree_popup_menu (GtkWidget *treeview,
2892                          EtBrowser *self)
2893 {
2894     EtBrowserPrivate *priv;
2895 
2896     priv = et_browser_get_instance_private (self);
2897 
2898     do_popup_menu (self, NULL, GTK_TREE_VIEW (treeview), priv->file_menu);
2899 
2900     return GDK_EVENT_STOP;
2901 }
2902 
2903 static gboolean
on_album_tree_button_press_event(GtkWidget * widget,GdkEventButton * event,EtBrowser * self)2904 on_album_tree_button_press_event (GtkWidget *widget,
2905                                   GdkEventButton *event,
2906                                   EtBrowser *self)
2907 {
2908     if (gdk_event_triggers_context_menu ((GdkEvent *)event))
2909     {
2910         EtBrowserPrivate *priv;
2911 
2912         priv = et_browser_get_instance_private (self);
2913 
2914         if (GTK_IS_TREE_VIEW (widget))
2915         {
2916             select_row_for_button_press_event (GTK_TREE_VIEW (widget), event);
2917         }
2918 
2919         do_popup_menu (self, event, GTK_TREE_VIEW (priv->album_view),
2920                        priv->album_menu);
2921 
2922         return GDK_EVENT_STOP;
2923     }
2924 
2925     return GDK_EVENT_PROPAGATE;
2926 }
2927 
2928 static gboolean
on_artist_tree_button_press_event(GtkWidget * widget,GdkEventButton * event,EtBrowser * self)2929 on_artist_tree_button_press_event (GtkWidget *widget,
2930                                    GdkEventButton *event,
2931                                    EtBrowser *self)
2932 {
2933     if (gdk_event_triggers_context_menu ((GdkEvent *)event))
2934     {
2935         EtBrowserPrivate *priv;
2936 
2937         priv = et_browser_get_instance_private (self);
2938 
2939         if (GTK_IS_TREE_VIEW (widget))
2940         {
2941             select_row_for_button_press_event (GTK_TREE_VIEW (widget), event);
2942         }
2943 
2944         do_popup_menu (self, event, GTK_TREE_VIEW (priv->artist_view),
2945                        priv->artist_menu);
2946 
2947         return GDK_EVENT_STOP;
2948     }
2949 
2950     return GDK_EVENT_PROPAGATE;
2951 }
2952 
2953 static gboolean
on_directory_tree_button_press_event(GtkWidget * widget,GdkEventButton * event,EtBrowser * self)2954 on_directory_tree_button_press_event (GtkWidget *widget,
2955                                       GdkEventButton *event,
2956                                       EtBrowser *self)
2957 {
2958     if (gdk_event_triggers_context_menu ((GdkEvent *)event))
2959     {
2960         EtBrowserPrivate *priv;
2961 
2962         priv = et_browser_get_instance_private (self);
2963 
2964         if (GTK_IS_TREE_VIEW (widget))
2965         {
2966             select_row_for_button_press_event (GTK_TREE_VIEW (widget), event);
2967         }
2968 
2969         do_popup_menu (self, event, GTK_TREE_VIEW (priv->directory_view),
2970                        priv->directory_view_menu);
2971 
2972         return GDK_EVENT_STOP;
2973     }
2974 
2975     return GDK_EVENT_PROPAGATE;
2976 }
2977 
2978 /*
2979  * Browser_Popup_Menu_Handler : displays the corresponding menu
2980  */
2981 static gboolean
on_file_tree_button_press_event(GtkWidget * widget,GdkEventButton * event,EtBrowser * self)2982 on_file_tree_button_press_event (GtkWidget *widget,
2983                                  GdkEventButton *event,
2984                                  EtBrowser *self)
2985 {
2986     if (gdk_event_triggers_context_menu ((GdkEvent *)event))
2987     {
2988         EtBrowserPrivate *priv;
2989 
2990         priv = et_browser_get_instance_private (self);
2991 
2992         if (GTK_IS_TREE_VIEW (widget))
2993         {
2994             select_row_for_button_press_event (GTK_TREE_VIEW (widget), event);
2995         }
2996 
2997         do_popup_menu (self, event, GTK_TREE_VIEW (priv->file_view),
2998                        priv->file_menu);
2999 
3000         return GDK_EVENT_STOP;
3001     }
3002     else if (event->type == GDK_2BUTTON_PRESS
3003         && event->button == GDK_BUTTON_PRIMARY)
3004     {
3005         /* Double left mouse click. Select files of the same directory (useful
3006          * when browsing sub-directories). */
3007         GdkWindow *bin_window;
3008         GList *l;
3009         gchar *path_ref = NULL;
3010         gchar *patch_check = NULL;
3011         GtkTreePath *currentPath = NULL;
3012 
3013         if (!ETCore->ETFileDisplayed)
3014         {
3015             return GDK_EVENT_PROPAGATE;
3016         }
3017 
3018         bin_window = gtk_tree_view_get_bin_window (GTK_TREE_VIEW (widget));
3019 
3020         if (bin_window != event->window)
3021         {
3022             /* If the double-click is not on a tree view row, for example when
3023              * resizing a header column, ignore it. */
3024             return GDK_EVENT_PROPAGATE;
3025         }
3026 
3027         /* File taken as reference. */
3028         path_ref = g_path_get_dirname (((File_Name *)ETCore->ETFileDisplayed->FileNameCur->data)->value);
3029 
3030         /* Search and select files of the same directory. */
3031         for (l = g_list_first (ETCore->ETFileDisplayedList); l != NULL;
3032              l = g_list_next (l))
3033         {
3034             /* Path of the file to check if it is in the same directory. */
3035             patch_check = g_path_get_dirname (((File_Name *)((ET_File *)l->data)->FileNameCur->data)->value);
3036 
3037             if (path_ref && patch_check && strcmp (path_ref, patch_check) == 0)
3038             {
3039                 /* Use of 'currentPath' to try to increase speed. Indeed, in
3040                  * many cases, the next file to select, is the next in the
3041                  * list. */
3042                 currentPath = et_browser_select_file_by_et_file2 (self,
3043                                                                   (ET_File *)l->data,
3044                                                                   TRUE,
3045                                                                   currentPath);
3046             }
3047 
3048             g_free (patch_check);
3049         }
3050 
3051         g_free (path_ref);
3052 
3053         if (currentPath)
3054         {
3055             gtk_tree_path_free (currentPath);
3056         }
3057 
3058         return GDK_EVENT_STOP;
3059     }
3060     else if (event->type == GDK_3BUTTON_PRESS
3061              && event->button == GDK_BUTTON_PRIMARY)
3062     {
3063         /* Triple left mouse click, select all files of the list. */
3064         g_action_group_activate_action (G_ACTION_GROUP (MainWindow),
3065                                         "select-all", NULL);
3066         return GDK_EVENT_STOP;
3067     }
3068 
3069     return GDK_EVENT_PROPAGATE;
3070 }
3071 
3072 /*
3073  * Destroy the whole tree up to the root node
3074  */
3075 static void
Browser_Tree_Initialize(EtBrowser * self)3076 Browser_Tree_Initialize (EtBrowser *self)
3077 {
3078     EtBrowserPrivate *priv;
3079 #ifdef G_OS_WIN32
3080     GVolumeMonitor *monitor;
3081     GList *mounts;
3082     GList *l;
3083 #endif
3084     GtkTreeIter parent_iter;
3085     GtkTreeIter dummy_iter;
3086     GIcon *drive_icon;
3087 
3088     priv = et_browser_get_instance_private (self);
3089 
3090     g_return_if_fail (priv->directory_model != NULL);
3091 
3092     gtk_tree_store_clear (priv->directory_model);
3093 
3094 #ifdef G_OS_WIN32
3095     /* TODO: Connect to the monitor changed signals. */
3096     monitor = g_volume_monitor_get ();
3097     mounts = g_volume_monitor_get_mounts (monitor);
3098 
3099     for (l = mounts; l != NULL; l = g_list_next (l))
3100     {
3101         GMount *mount;
3102         gchar *name;
3103         GFile *root;
3104         gchar *path;
3105 
3106         mount = l->data;
3107         drive_icon = g_mount_get_icon (mount);
3108         name = g_mount_get_name (mount);
3109         root = g_mount_get_root (mount);
3110         path = g_file_get_path (root);
3111 
3112         gtk_tree_store_insert_with_values (priv->directory_model,
3113                                            &parent_iter, NULL, G_MAXINT,
3114                                            TREE_COLUMN_DIR_NAME,
3115                                            name,
3116                                            TREE_COLUMN_FULL_PATH,
3117                                            path,
3118                                            TREE_COLUMN_HAS_SUBDIR, TRUE,
3119                                            TREE_COLUMN_SCANNED, FALSE,
3120                                            TREE_COLUMN_ICON, drive_icon,
3121                                            -1);
3122         /* Insert dummy node. */
3123         gtk_tree_store_append (priv->directory_model, &dummy_iter,
3124                                &parent_iter);
3125 
3126         g_free (path);
3127         g_free (name);
3128         g_object_unref (root);
3129         g_object_unref (drive_icon);
3130     }
3131 
3132     g_list_free_full (mounts, g_object_unref);
3133     g_object_unref (monitor);
3134 #else /* !G_OS_WIN32 */
3135     drive_icon = get_gicon_for_path (G_DIR_SEPARATOR_S, ET_PATH_STATE_CLOSED);
3136     gtk_tree_store_insert_with_values (priv->directory_model, &parent_iter, NULL,
3137                                        G_MAXINT, TREE_COLUMN_DIR_NAME,
3138                                        G_DIR_SEPARATOR_S,
3139                                        TREE_COLUMN_FULL_PATH,
3140                                        G_DIR_SEPARATOR_S,
3141                                        TREE_COLUMN_HAS_SUBDIR, TRUE,
3142                                        TREE_COLUMN_SCANNED, FALSE,
3143                                        TREE_COLUMN_ICON, drive_icon, -1);
3144     /* Insert dummy node. */
3145     gtk_tree_store_append (priv->directory_model, &dummy_iter, &parent_iter);
3146 
3147     g_object_unref (drive_icon);
3148 #endif /* !G_OS_WIN32 */
3149 }
3150 
3151 /*
3152  * et_browser_reload: Refresh the tree browser by destroying it and rebuilding it.
3153  * Opens tree nodes corresponding to the current path.
3154  */
3155 void
et_browser_reload(EtBrowser * self)3156 et_browser_reload (EtBrowser *self)
3157 {
3158     EtBrowserPrivate *priv;
3159     gchar *current_path = NULL;
3160     GtkTreeSelection *selection;
3161 
3162     priv = et_browser_get_instance_private (self);
3163 
3164     /* Memorize the current path to load it again at the end */
3165     current_path = Browser_Tree_Get_Path_Of_Selected_Node (self);
3166 
3167     /* Select again the memorized path without loading files */
3168     selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->directory_view));
3169 
3170     if (selection)
3171     {
3172         GFile *file;
3173 
3174         g_signal_handlers_block_by_func (selection,
3175                                          G_CALLBACK (Browser_Tree_Node_Selected),
3176                                          self);
3177         Browser_Tree_Initialize (self);
3178         file = g_file_new_for_path (current_path);
3179         et_browser_select_dir (self, file);
3180         g_object_unref (file);
3181         g_signal_handlers_unblock_by_func (selection,
3182                                            G_CALLBACK (Browser_Tree_Node_Selected),
3183                                            self);
3184     }
3185     g_free(current_path);
3186 
3187     et_application_window_update_actions (ET_APPLICATION_WINDOW (MainWindow));
3188 }
3189 
3190 /*
3191  * Renames a directory
3192  * last_path:
3193  * new_path:
3194  * Parameters are non-utf8!
3195  */
3196 static void
Browser_Tree_Rename_Directory(EtBrowser * self,const gchar * last_path,const gchar * new_path)3197 Browser_Tree_Rename_Directory (EtBrowser *self,
3198                                const gchar *last_path,
3199                                const gchar *new_path)
3200 {
3201     EtBrowserPrivate *priv;
3202     gchar **textsplit;
3203     gint i;
3204     GtkTreeIter  iter;
3205     GtkTreePath *childpath;
3206     GtkTreePath *parentpath;
3207     gchar *new_basename_utf8;
3208     gchar *path;
3209     GFile *file;
3210 
3211     if (!last_path || !new_path)
3212         return;
3213 
3214     priv = et_browser_get_instance_private (self);
3215 
3216     /*
3217      * Find the existing tree entry
3218      */
3219     textsplit = g_strsplit(last_path, G_DIR_SEPARATOR_S, 0);
3220 
3221 #ifdef G_OS_WIN32
3222     if (!et_browser_win32_get_drive_root (self, textsplit[0], &iter,
3223                                           &parentpath))
3224     {
3225         return;
3226     }
3227 #else /* !G_OS_WIN32 */
3228     parentpath = gtk_tree_path_new_first();
3229 #endif /* !G_OS_WIN32 */
3230 
3231     for (i = 1; textsplit[i] != NULL; i++)
3232     {
3233         gboolean valid = gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->directory_model),
3234                                                   &iter, parentpath);
3235         if (valid)
3236         {
3237             childpath = Find_Child_Node (self, &iter, textsplit[i]);
3238         }
3239         else
3240         {
3241             childpath = NULL;
3242         }
3243 
3244         if (childpath == NULL)
3245         {
3246             // ERROR! Could not find it!
3247             gchar *text_utf8 = g_filename_display_name (textsplit[i]);
3248             g_critical ("Error: Searching for %s, could not find node %s in tree.",
3249                         last_path, text_utf8);
3250             g_strfreev(textsplit);
3251             g_free(text_utf8);
3252             return;
3253         }
3254         gtk_tree_path_free(parentpath);
3255         parentpath = childpath;
3256     }
3257 
3258     gtk_tree_model_get_iter(GTK_TREE_MODEL(priv->directory_model), &iter, parentpath);
3259     gtk_tree_path_free(parentpath);
3260 
3261     /* Rename the on-screen node */
3262     new_basename_utf8 = g_filename_display_basename (new_path);
3263     gtk_tree_store_set(priv->directory_model, &iter,
3264                        TREE_COLUMN_DIR_NAME,  new_basename_utf8,
3265                        TREE_COLUMN_FULL_PATH, new_path,
3266                        -1);
3267 
3268     /* Update fullpath of child nodes */
3269     Browser_Tree_Handle_Rename (self, &iter, last_path, new_path);
3270 
3271     /* Update the variable of the current path */
3272     path = Browser_Tree_Get_Path_Of_Selected_Node (self);
3273     file = g_file_new_for_path (path);
3274     et_browser_set_current_path (self, file);
3275     g_object_unref (file);
3276     g_free(path);
3277 
3278     g_strfreev(textsplit);
3279     g_free(new_basename_utf8);
3280 }
3281 
3282 /*
3283  * Browser_Tree_Handle_Rename:
3284  * @self: an #EtBrowser
3285  * @parentnode: the parent node in a tree model containing directory paths
3286  * @old_path: (type filename): the old path, in GLib filename encoding
3287  * @new_path: (type filename): the new path, in GLib filename encoding
3288  *
3289  * Recursive function to update paths of all child nodes.
3290  */
3291 static void
Browser_Tree_Handle_Rename(EtBrowser * self,GtkTreeIter * parentnode,const gchar * old_path,const gchar * new_path)3292 Browser_Tree_Handle_Rename (EtBrowser *self,
3293                             GtkTreeIter *parentnode,
3294                             const gchar *old_path,
3295                             const gchar *new_path)
3296 {
3297     EtBrowserPrivate *priv;
3298     GtkTreeIter iter;
3299 
3300     priv = et_browser_get_instance_private (self);
3301 
3302     /* If there are no children then nothing needs to be done! */
3303     if (!gtk_tree_model_iter_children (GTK_TREE_MODEL (priv->directory_model),
3304                                        &iter, parentnode))
3305     {
3306         return;
3307     }
3308 
3309     do
3310     {
3311         gchar *path;
3312         gsize path_shift;
3313         gchar *path_new;
3314 
3315         gtk_tree_model_get(GTK_TREE_MODEL(priv->directory_model), &iter,
3316                            TREE_COLUMN_FULL_PATH, &path, -1);
3317         if(path == NULL)
3318             continue;
3319 
3320         /* Graft the new path onto the old path. */
3321         path_shift = strlen (old_path);
3322         path_new = g_strconcat (new_path, path + path_shift, NULL);
3323 
3324         gtk_tree_store_set(priv->directory_model, &iter,
3325                            TREE_COLUMN_FULL_PATH, path_new, -1);
3326 
3327         g_free(path_new);
3328         g_free(path);
3329 
3330         /* Recurse if necessary. */
3331         if (gtk_tree_model_iter_has_child (GTK_TREE_MODEL (priv->directory_model),
3332                                            &iter))
3333         {
3334             Browser_Tree_Handle_Rename (self, &iter, old_path, new_path);
3335         }
3336 
3337     } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(priv->directory_model), &iter));
3338 
3339 }
3340 
3341 /*
3342  * Find the child node of "parentnode" that has text of "childtext
3343  * Returns NULL on failure
3344  */
3345 static GtkTreePath *
Find_Child_Node(EtBrowser * self,GtkTreeIter * parentnode,gchar * childtext)3346 Find_Child_Node (EtBrowser *self, GtkTreeIter *parentnode, gchar *childtext)
3347 {
3348     EtBrowserPrivate *priv;
3349     gint row;
3350     GtkTreeIter iter;
3351     gchar *text;
3352     gchar *temp;
3353 
3354     priv = et_browser_get_instance_private (self);
3355 
3356     for (row=0; row < gtk_tree_model_iter_n_children(GTK_TREE_MODEL(priv->directory_model), parentnode); row++)
3357     {
3358         if (row == 0)
3359         {
3360             if (gtk_tree_model_iter_children(GTK_TREE_MODEL(priv->directory_model), &iter, parentnode) == FALSE) return NULL;
3361         } else
3362         {
3363             if (gtk_tree_model_iter_next(GTK_TREE_MODEL(priv->directory_model), &iter) == FALSE)
3364                 return NULL;
3365         }
3366         gtk_tree_model_get(GTK_TREE_MODEL(priv->directory_model), &iter,
3367                            TREE_COLUMN_FULL_PATH, &temp, -1);
3368         text = g_path_get_basename(temp);
3369         g_free(temp);
3370         if(strcmp(childtext,text) == 0)
3371         {
3372             g_free(text);
3373             return gtk_tree_model_get_path(GTK_TREE_MODEL(priv->directory_model), &iter);
3374         }
3375         g_free(text);
3376 
3377     }
3378 
3379     return NULL;
3380 }
3381 
3382 /*
3383  * check_for_subdir:
3384  * @path: (type filename): the path to test
3385  *
3386  * Check if @path has any subdirectories.
3387  *
3388  * Returns: %TRUE if subdirectories exist, %FALSE otherwise
3389  */
3390 static gboolean
check_for_subdir(const gchar * path)3391 check_for_subdir (const gchar *path)
3392 {
3393     GFile *dir;
3394     GFileEnumerator *enumerator;
3395 
3396     dir = g_file_new_for_path (path);
3397     enumerator = g_file_enumerate_children (dir,
3398                                             G_FILE_ATTRIBUTE_STANDARD_TYPE ","
3399                                             G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN,
3400                                             G_FILE_QUERY_INFO_NONE,
3401                                             NULL, NULL);
3402     g_object_unref (dir);
3403 
3404     if (enumerator)
3405     {
3406         GFileInfo *childinfo;
3407 
3408         while ((childinfo = g_file_enumerator_next_file (enumerator,
3409                                                          NULL, NULL)))
3410         {
3411             if ((g_file_info_get_file_type (childinfo) ==
3412                  G_FILE_TYPE_DIRECTORY) &&
3413                 (g_settings_get_boolean (MainSettings, "browse-show-hidden")
3414                  || !g_file_info_get_is_hidden (childinfo)))
3415             {
3416                 g_object_unref (childinfo);
3417                 g_file_enumerator_close (enumerator, NULL, NULL);
3418                 g_object_unref (enumerator);
3419                 return TRUE;
3420             }
3421 
3422             g_object_unref (childinfo);
3423         }
3424 
3425         g_file_enumerator_close (enumerator, NULL, NULL);
3426         g_object_unref (enumerator);
3427     }
3428 
3429     return FALSE;
3430 }
3431 
3432 /*
3433  * get_gicon_for_path:
3434  * @path: (type filename): path to create icon for
3435  * @path_state: whether the icon should be shown open or closed
3436  *
3437  * Check the permissions for the supplied @path (authorized?, readonly?,
3438  * unreadable?) and return an appropriate icon.
3439  *
3440  * Returns: an icon corresponding to the @path
3441  */
3442 static GIcon *
get_gicon_for_path(const gchar * path,EtPathState path_state)3443 get_gicon_for_path (const gchar *path, EtPathState path_state)
3444 {
3445     GIcon *folder_icon;
3446     GIcon *emblem_icon;
3447     GIcon *emblemed_icon;
3448     GEmblem *emblem;
3449     GFile *file;
3450     GFileInfo *info;
3451     GError *error = NULL;
3452 
3453     switch (path_state)
3454     {
3455         case ET_PATH_STATE_OPEN:
3456             folder_icon = g_themed_icon_new ("folder-open");
3457             break;
3458         case ET_PATH_STATE_CLOSED:
3459             folder_icon = g_themed_icon_new ("folder");
3460             break;
3461         default:
3462             g_assert_not_reached ();
3463     }
3464 
3465     file = g_file_new_for_path (path);
3466     info = g_file_query_info (file, G_FILE_ATTRIBUTE_ACCESS_CAN_READ ","
3467                               G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE,
3468                               G_FILE_QUERY_INFO_NONE, NULL, &error);
3469 
3470     if (info == NULL)
3471     {
3472         g_warning ("Error while querying path information: %s",
3473                    error->message);
3474         g_clear_error (&error);
3475         info = g_file_info_new ();
3476         g_file_info_set_attribute_boolean (info,
3477                                            G_FILE_ATTRIBUTE_ACCESS_CAN_READ,
3478                                            FALSE);
3479     }
3480 
3481     if (!g_file_info_get_attribute_boolean (info,
3482                                             G_FILE_ATTRIBUTE_ACCESS_CAN_READ))
3483     {
3484         emblem_icon = g_themed_icon_new ("emblem-unreadable");
3485         emblem = g_emblem_new_with_origin (emblem_icon,
3486                                            G_EMBLEM_ORIGIN_LIVEMETADATA);
3487         emblemed_icon = g_emblemed_icon_new (folder_icon, emblem);
3488         g_object_unref (folder_icon);
3489         g_object_unref (emblem_icon);
3490         g_object_unref (emblem);
3491 
3492         folder_icon = emblemed_icon;
3493     }
3494     else if (!g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE))
3495     {
3496         emblem_icon = g_themed_icon_new ("emblem-readonly");
3497         emblem = g_emblem_new_with_origin (emblem_icon,
3498                                            G_EMBLEM_ORIGIN_LIVEMETADATA);
3499         emblemed_icon = g_emblemed_icon_new (folder_icon, emblem);
3500         g_object_unref (folder_icon);
3501         g_object_unref (emblem_icon);
3502         g_object_unref (emblem);
3503 
3504         folder_icon = emblemed_icon;
3505     }
3506 
3507     g_object_unref (file);
3508     g_object_unref (info);
3509 
3510     return folder_icon;
3511 }
3512 
3513 /*
3514  * Open up a node on the browser tree
3515  * Scanning and showing all subdirectories
3516  */
3517 static void
expand_cb(EtBrowser * self,GtkTreeIter * iter,GtkTreePath * gtreePath,GtkTreeView * tree)3518 expand_cb (EtBrowser *self, GtkTreeIter *iter, GtkTreePath *gtreePath, GtkTreeView *tree)
3519 {
3520     EtBrowserPrivate *priv;
3521     GFile *dir;
3522     GFileEnumerator *enumerator;
3523     gchar *fullpath_file;
3524     gchar *parentPath;
3525     gboolean treeScanned;
3526     gboolean has_subdir = FALSE;
3527     GtkTreeIter currentIter;
3528     GtkTreeIter subNodeIter;
3529     GIcon *icon;
3530 
3531     priv = et_browser_get_instance_private (self);
3532 
3533     g_return_if_fail (priv->directory_model != NULL);
3534 
3535     gtk_tree_model_get(GTK_TREE_MODEL(priv->directory_model), iter,
3536                        TREE_COLUMN_FULL_PATH, &parentPath,
3537                        TREE_COLUMN_SCANNED,   &treeScanned, -1);
3538 
3539     if (treeScanned)
3540         return;
3541 
3542     dir = g_file_new_for_path (parentPath);
3543     enumerator = g_file_enumerate_children (dir,
3544                                             G_FILE_ATTRIBUTE_STANDARD_TYPE ","
3545                                             G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME ","
3546                                             G_FILE_ATTRIBUTE_STANDARD_NAME ","
3547                                             G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN,
3548                                             G_FILE_QUERY_INFO_NONE,
3549                                             NULL, NULL);
3550 
3551     if (enumerator)
3552     {
3553         GFileInfo *childinfo;
3554 
3555         while ((childinfo = g_file_enumerator_next_file (enumerator,
3556                                                          NULL, NULL))
3557                != NULL)
3558         {
3559             GFile *child;
3560             gboolean isdir;
3561 
3562             child = g_file_enumerator_get_child (enumerator, childinfo);
3563             fullpath_file = g_file_get_path (child);
3564             isdir = g_file_info_get_file_type (childinfo) == G_FILE_TYPE_DIRECTORY;
3565 
3566             if (isdir &&
3567                 (g_settings_get_boolean (MainSettings, "browse-show-hidden")
3568                  || !g_file_info_get_is_hidden (childinfo)))
3569             {
3570                 const gchar *dirname_utf8;
3571                 dirname_utf8 = g_file_info_get_display_name (childinfo);
3572 
3573                 has_subdir = check_for_subdir (fullpath_file);
3574 
3575                 /* Select pixmap according permissions for the directory. */
3576                 icon = get_gicon_for_path (fullpath_file,
3577                                            ET_PATH_STATE_CLOSED);
3578 
3579                 gtk_tree_store_insert_with_values (priv->directory_model,
3580                                                    &currentIter, iter,
3581                                                    G_MAXINT,
3582                                                    TREE_COLUMN_DIR_NAME,
3583                                                    dirname_utf8,
3584                                                    TREE_COLUMN_FULL_PATH,
3585                                                    fullpath_file,
3586                                                    TREE_COLUMN_HAS_SUBDIR,
3587                                                    !has_subdir,
3588                                                    TREE_COLUMN_SCANNED, FALSE,
3589                                                    TREE_COLUMN_ICON, icon, -1);
3590 
3591                 if (has_subdir)
3592                 {
3593                     /* Insert a dummy node. */
3594                     gtk_tree_store_append(priv->directory_model, &subNodeIter, &currentIter);
3595                 }
3596 
3597                 g_object_unref (icon);
3598             }
3599 
3600             g_free (fullpath_file);
3601             g_object_unref (childinfo);
3602             g_object_unref (child);
3603         }
3604 
3605         g_file_enumerator_close (enumerator, NULL, NULL);
3606         g_object_unref (enumerator);
3607 
3608         /* Remove dummy node. */
3609         gtk_tree_model_iter_children (GTK_TREE_MODEL (priv->directory_model),
3610                                       &subNodeIter, iter);
3611         gtk_tree_store_remove (priv->directory_model, &subNodeIter);
3612     }
3613 
3614     g_object_unref (dir);
3615     icon = get_gicon_for_path (parentPath, ET_PATH_STATE_OPEN);
3616 
3617 #ifdef G_OS_WIN32
3618     // set open folder pixmap except on drive (depth == 0)
3619     if (gtk_tree_path_get_depth(gtreePath) > 1)
3620     {
3621         // update the icon of the node to opened folder :-)
3622         gtk_tree_store_set(priv->directory_model, iter,
3623                            TREE_COLUMN_SCANNED, TRUE,
3624                            TREE_COLUMN_ICON, icon,
3625                            -1);
3626     }
3627 #else /* !G_OS_WIN32 */
3628     // update the icon of the node to opened folder :-)
3629     gtk_tree_store_set(priv->directory_model, iter,
3630                        TREE_COLUMN_SCANNED, TRUE,
3631                        TREE_COLUMN_ICON, icon,
3632                        -1);
3633 #endif /* !G_OS_WIN32 */
3634 
3635     gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(priv->directory_model),
3636                                          TREE_COLUMN_DIR_NAME, GTK_SORT_ASCENDING);
3637 
3638     g_object_unref (icon);
3639     g_free(parentPath);
3640 }
3641 
3642 static void
collapse_cb(EtBrowser * self,GtkTreeIter * iter,GtkTreePath * treePath,GtkTreeView * tree)3643 collapse_cb (EtBrowser *self, GtkTreeIter *iter, GtkTreePath *treePath, GtkTreeView *tree)
3644 {
3645     EtBrowserPrivate *priv;
3646     GtkTreeIter subNodeIter;
3647     gchar *path;
3648     GIcon *icon;
3649     GFile *file;
3650     GFileInfo *fileinfo;
3651     GError *error = NULL;
3652 
3653     priv = et_browser_get_instance_private (self);
3654 
3655     g_return_if_fail (priv->directory_model != NULL);
3656 
3657     gtk_tree_model_get (GTK_TREE_MODEL (priv->directory_model), iter,
3658                         TREE_COLUMN_FULL_PATH, &path, -1);
3659 
3660     /* If the directory is not readable, do not delete its children. */
3661     file = g_file_new_for_path (path);
3662     g_free (path);
3663     fileinfo = g_file_query_info (file, G_FILE_ATTRIBUTE_ACCESS_CAN_READ,
3664                                   G_FILE_QUERY_INFO_NONE, NULL, &error);
3665     g_object_unref (file);
3666 
3667     if (fileinfo)
3668     {
3669         if (!g_file_info_get_attribute_boolean (fileinfo,
3670                                                 G_FILE_ATTRIBUTE_ACCESS_CAN_READ))
3671         {
3672             g_object_unref (fileinfo);
3673             return;
3674         }
3675 
3676         g_object_unref (fileinfo);
3677     }
3678 
3679     gtk_tree_model_iter_children(GTK_TREE_MODEL(priv->directory_model),
3680                                  &subNodeIter, iter);
3681     while (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(priv->directory_model), iter))
3682     {
3683         gtk_tree_model_iter_children(GTK_TREE_MODEL(priv->directory_model), &subNodeIter, iter);
3684         gtk_tree_store_remove(priv->directory_model, &subNodeIter);
3685     }
3686 
3687     gtk_tree_model_get (GTK_TREE_MODEL (priv->directory_model), iter,
3688                         TREE_COLUMN_FULL_PATH, &path, -1);
3689     icon = get_gicon_for_path (path, ET_PATH_STATE_OPEN);
3690     g_free (path);
3691 #ifdef G_OS_WIN32
3692     // set closed folder pixmap except on drive (depth == 0)
3693     if(gtk_tree_path_get_depth(treePath) > 1)
3694     {
3695         // update the icon of the node to closed folder :-)
3696         gtk_tree_store_set(priv->directory_model, iter,
3697                            TREE_COLUMN_SCANNED, FALSE,
3698                            TREE_COLUMN_ICON, icon, -1);
3699     }
3700 #else /* !G_OS_WIN32 */
3701     // update the icon of the node to closed folder :-)
3702     gtk_tree_store_set(priv->directory_model, iter,
3703                        TREE_COLUMN_SCANNED, FALSE,
3704                        TREE_COLUMN_ICON, icon, -1);
3705 #endif /* !G_OS_WIN32 */
3706 
3707     /* Insert dummy node only if directory exists. */
3708     if (error)
3709     {
3710         /* Remove the parent (missing) directory from the tree. */
3711         if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
3712         {
3713             gtk_tree_store_remove (priv->directory_model, iter);
3714         }
3715 
3716         g_error_free (error);
3717     }
3718     else
3719     {
3720         gtk_tree_store_append (priv->directory_model, &subNodeIter, iter);
3721     }
3722 
3723     g_object_unref (icon);
3724 }
3725 
3726 static void
on_sort_mode_changed(EtBrowser * self,gchar * key,GSettings * settings)3727 on_sort_mode_changed (EtBrowser *self, gchar *key, GSettings *settings)
3728 {
3729     EtBrowserPrivate *priv;
3730     EtSortMode sort_mode;
3731     GtkTreeViewColumn *column;
3732 
3733     priv = et_browser_get_instance_private (self);
3734 
3735     sort_mode = g_settings_get_enum (settings, key);
3736     column = et_browser_get_column_for_column_id (self, sort_mode / 2);
3737 
3738     /* If the column to sort is different than the old sorted column. */
3739     if (sort_mode / 2 != priv->file_sort_mode / 2)
3740     {
3741         GtkTreeViewColumn *old_column;
3742 
3743         old_column = et_browser_get_column_for_column_id (self,
3744                                                           priv->file_sort_mode / 2);
3745 
3746         /* Reset the sort order of the old sort column. */
3747         if (gtk_tree_view_column_get_sort_order (old_column)
3748             == GTK_SORT_DESCENDING)
3749         {
3750             gtk_tree_view_column_set_sort_order (old_column,
3751                                                  GTK_SORT_ASCENDING);
3752         }
3753 
3754         gtk_tree_view_column_set_sort_indicator (old_column, FALSE);
3755     }
3756 
3757     /* New sort mode is for a column with a visible counterpart. */
3758     if (sort_mode / 2 < ET_SORT_MODE_ASCENDING_CREATION_DATE)
3759     {
3760         gtk_tree_view_column_set_sort_indicator (column, TRUE);
3761     }
3762 
3763     /* Even is GTK_SORT_ASCENDING, odd is GTK_SORT_DESCENDING. */
3764     gtk_tree_view_column_set_sort_order (column, sort_mode % 2);
3765 
3766     /* Store the new sort mode. */
3767     priv->file_sort_mode = sort_mode;
3768 
3769     et_browser_refresh_sort (self);
3770 }
3771 
3772 /*
3773  * Open the file selection window and saves the selected file path into entry
3774  */
3775 static void
open_file_selection_dialog(GtkWidget * entry,const gchar * title,GtkFileChooserAction action)3776 open_file_selection_dialog (GtkWidget *entry,
3777                             const gchar *title,
3778                             GtkFileChooserAction action)
3779 {
3780     const gchar *tmp;
3781     gchar *filename, *filename_utf8;
3782     GtkWidget *dialog;
3783     GtkWindow *parent_window = NULL;
3784     gint response;
3785 
3786     parent_window = (GtkWindow*) gtk_widget_get_toplevel (entry);
3787     if (!gtk_widget_is_toplevel (GTK_WIDGET (parent_window)))
3788     {
3789         g_warning ("%s", "Could not get parent window");
3790         return;
3791     }
3792 
3793     dialog = gtk_file_chooser_dialog_new (title, parent_window, action,
3794                                           _("_Cancel"), GTK_RESPONSE_CANCEL,
3795                                           _("_Open"), GTK_RESPONSE_ACCEPT,
3796                                           NULL);
3797 
3798     /* Set initial directory. */
3799     tmp = gtk_entry_get_text (GTK_ENTRY (entry));
3800 
3801     if (tmp && *tmp)
3802     {
3803         if (!gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (dialog), tmp))
3804         {
3805             gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog),
3806                                                  tmp);
3807         }
3808     }
3809 
3810     response = gtk_dialog_run (GTK_DIALOG (dialog));
3811 
3812     if (response == GTK_RESPONSE_ACCEPT)
3813     {
3814         filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
3815         filename_utf8 = g_filename_display_name (filename);
3816         gtk_entry_set_text (GTK_ENTRY (entry), filename_utf8);
3817         g_free (filename);
3818         g_free (filename_utf8);
3819         /* Useful for the button on the main window. */
3820         gtk_widget_grab_focus (GTK_WIDGET (entry));
3821         g_signal_emit_by_name (entry, "activate");
3822     }
3823 
3824     gtk_widget_destroy (dialog);
3825 }
3826 
3827 /* File selection window */
3828 static void
File_Selection_Window_For_File(GtkWidget * entry)3829 File_Selection_Window_For_File (GtkWidget *entry)
3830 {
3831     open_file_selection_dialog (entry, _("Select File"),
3832                                 GTK_FILE_CHOOSER_ACTION_OPEN);
3833 }
3834 
3835 static void
File_Selection_Window_For_Directory(GtkWidget * entry)3836 File_Selection_Window_For_Directory (GtkWidget *entry)
3837 {
3838     open_file_selection_dialog (entry, _("Select Directory"),
3839                                 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
3840 }
3841 
3842 static gboolean
album_list_separator_func(GtkTreeModel * model,GtkTreeIter * iter,gpointer user_data)3843 album_list_separator_func (GtkTreeModel *model,
3844                            GtkTreeIter *iter,
3845                            gpointer user_data)
3846 {
3847     gboolean separator_row;
3848 
3849     gtk_tree_model_get (model, iter, ALBUM_ALL_ALBUMS_SEPARATOR_ROW,
3850                         &separator_row, -1);
3851 
3852     return separator_row;
3853 }
3854 
3855 /*
3856  * Create item of the browser (Entry + Tree + List).
3857  */
3858 static void
create_browser(EtBrowser * self)3859 create_browser (EtBrowser *self)
3860 {
3861     EtBrowserPrivate *priv;
3862     gsize i;
3863     GtkBuilder *builder;
3864     GMenuModel *menu_model;
3865     GFile *file;
3866 
3867     priv = et_browser_get_instance_private (self);
3868 
3869     /* History list */
3870     Load_Path_Entry_List (priv->entry_model, MISC_COMBO_TEXT);
3871 
3872     g_signal_connect_swapped (gtk_bin_get_child (GTK_BIN (priv->entry_combo)),
3873                               "activate", G_CALLBACK (Browser_Entry_Activated),
3874                               self);
3875     /* The button to select a directory to browse. */
3876     g_signal_connect_swapped (priv->open_button, "clicked",
3877                               G_CALLBACK (File_Selection_Window_For_Directory),
3878                               gtk_bin_get_child (GTK_BIN (priv->entry_combo)));
3879 
3880     /* The tree view */
3881     Browser_Tree_Initialize (self);
3882 
3883     /* Create popup menu on browser tree view. */
3884     builder = gtk_builder_new_from_resource ("/org/gnome/EasyTAG/menus.ui");
3885 
3886     menu_model = G_MENU_MODEL (gtk_builder_get_object (builder,
3887                                                        "directory-menu"));
3888     priv->directory_view_menu = gtk_menu_new_from_model (menu_model);
3889     gtk_menu_attach_to_widget (GTK_MENU (priv->directory_view_menu), priv->directory_view, NULL);
3890 
3891     /* The ScrollWindows with the Artist and Album Lists. */
3892     priv->artist_selected_handler = g_signal_connect_swapped (gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->artist_view)),
3893                                                               "changed",
3894                                                               G_CALLBACK (Browser_Artist_List_Row_Selected),
3895                                                               self);
3896 
3897     /* Create popup menu on browser artist list. */
3898     menu_model = G_MENU_MODEL (gtk_builder_get_object (builder,
3899                                                        "directory-artist-menu"));
3900     priv->artist_menu = gtk_menu_new_from_model (menu_model);
3901     gtk_menu_attach_to_widget (GTK_MENU (priv->artist_menu), priv->artist_view,
3902                                NULL);
3903 
3904     gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (priv->album_view),
3905                                           album_list_separator_func, NULL,
3906                                           NULL);
3907 
3908     priv->album_selected_handler = g_signal_connect_swapped (gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->album_view)),
3909                                                              "changed",
3910                                                              G_CALLBACK (Browser_Album_List_Row_Selected),
3911                                                              self);
3912 
3913     /* Create Popup Menu on browser album list. */
3914     menu_model = G_MENU_MODEL (gtk_builder_get_object (builder,
3915                                                        "directory-album-menu"));
3916     priv->album_menu = gtk_menu_new_from_model (menu_model);
3917     gtk_menu_attach_to_widget (GTK_MENU (priv->album_menu), priv->album_view,
3918                                NULL);
3919 
3920     /* The file list */
3921     /* Add columns to tree view. See ET_FILE_LIST_COLUMN. */
3922     for (i = 0; i <= LIST_FILE_ENCODED_BY; i++)
3923     {
3924         const guint ascending_sort = 2 * i;
3925         GtkTreeViewColumn *column = gtk_tree_view_get_column (GTK_TREE_VIEW (priv->file_view),
3926                                                               i);
3927 
3928         g_object_set_data (G_OBJECT (column), "browser", self);
3929         g_signal_connect (column, "clicked",
3930                           G_CALLBACK (et_browser_on_column_clicked),
3931                           GINT_TO_POINTER (ascending_sort));
3932     }
3933 
3934     g_signal_connect_swapped (MainSettings, "changed::sort-mode",
3935                               G_CALLBACK (on_sort_mode_changed), self);
3936     // To sort list
3937     gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (priv->file_model), 0,
3938                                       Browser_List_Sort_Func, NULL, NULL);
3939     et_browser_refresh_sort (self);
3940     gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (priv->file_model),
3941                                           0, GTK_SORT_ASCENDING);
3942 
3943     priv->file_selected_handler = g_signal_connect_swapped (gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->file_view)),
3944                                                             "changed",
3945                                                             G_CALLBACK (Browser_List_Row_Selected),
3946                                                             self);
3947 
3948     /* Create popup menu on file list. */
3949     menu_model = G_MENU_MODEL (gtk_builder_get_object (builder, "file-menu"));
3950     priv->file_menu = gtk_menu_new_from_model (menu_model);
3951     gtk_menu_attach_to_widget (GTK_MENU (priv->file_menu), priv->file_view,
3952                                NULL);
3953 
3954     g_object_unref (builder);
3955 
3956     /* The list store for run program combos. */
3957     priv->run_program_model = gtk_list_store_new(MISC_COMBO_COUNT, G_TYPE_STRING);
3958 
3959     /* TODO: Give the browser area a sensible default size. */
3960 
3961     /* Set home variable as current path */
3962     file = g_file_new_for_path (g_get_home_dir ());
3963     et_browser_set_current_path (self, file);
3964     g_object_unref (file);
3965 }
3966 
3967 /*
3968  * et_browser_on_column_clicked:
3969  * @column: the tree view column to sort
3970  * @data: the (required) #ET_Sorting_Type, converted to a pointer with
3971  * #GINT_TO_POINTER
3972  *
3973  * Set the sort mode and display appropriate sort indicator when
3974  * column is clicked.
3975  */
3976 static void
et_browser_on_column_clicked(GtkTreeViewColumn * column,gpointer data)3977 et_browser_on_column_clicked (GtkTreeViewColumn *column, gpointer data)
3978 {
3979     /* Flip to a descing sort mode if the row is already sorted ascending. */
3980     if (gtk_tree_view_column_get_sort_order (column) == GTK_SORT_ASCENDING)
3981     {
3982         g_settings_set_enum (MainSettings, "sort-mode",
3983                              GPOINTER_TO_INT (data) + 1);
3984     }
3985     else
3986     {
3987         g_settings_set_enum (MainSettings, "sort-mode",
3988                              GPOINTER_TO_INT (data));
3989     }
3990 }
3991 
3992 /*******************************
3993  * Scanner To Rename Directory *
3994  *******************************/
3995 static void
rename_directory_generate_preview(EtBrowser * self)3996 rename_directory_generate_preview (EtBrowser *self)
3997 {
3998     EtBrowserPrivate *priv;
3999     gchar *preview_text = NULL;
4000     gchar *mask = NULL;
4001 
4002     priv = et_browser_get_instance_private (self);
4003 
4004     if (!ETCore->ETFileDisplayed
4005     ||  !priv->rename_directory_dialog || !priv->rename_directory_mask_entry || !priv->rename_directory_preview_label)
4006         return;
4007 
4008     mask = g_settings_get_string (MainSettings,
4009                                   "rename-directory-default-mask");
4010 
4011     if (!mask)
4012         return;
4013 
4014     preview_text = et_scan_generate_new_filename_from_mask (ETCore->ETFileDisplayed,
4015                                                             mask, FALSE);
4016 
4017     if (GTK_IS_LABEL(priv->rename_directory_preview_label))
4018     {
4019         if (preview_text)
4020         {
4021             //gtk_label_set_text(GTK_LABEL(priv->rename_file_preview_label),preview_text);
4022             gchar *tmp_string = g_markup_printf_escaped("%s",preview_text); // To avoid problem with strings containing characters like '&'
4023             gchar *str = g_strdup_printf("<i>%s</i>",tmp_string);
4024             gtk_label_set_markup(GTK_LABEL(priv->rename_directory_preview_label),str);
4025             g_free(tmp_string);
4026             g_free(str);
4027         } else
4028         {
4029             gtk_label_set_text(GTK_LABEL(priv->rename_directory_preview_label),"");
4030         }
4031 
4032         // Force the window to be redrawed else the preview label may be not placed correctly
4033         gtk_widget_queue_resize(priv->rename_directory_dialog);
4034     }
4035 
4036     g_free(mask);
4037     g_free(preview_text);
4038 }
4039 
4040 /*
4041  * The window to Rename a directory into the browser.
4042  */
4043 void
et_browser_show_rename_directory_dialog(EtBrowser * self)4044 et_browser_show_rename_directory_dialog (EtBrowser *self)
4045 {
4046     EtBrowserPrivate *priv;
4047     GtkBuilder *builder;
4048     GtkWidget *label;
4049     GtkWidget *button;
4050     GFile *parent;
4051     gchar *parent_path;
4052     gchar *basename;
4053     gchar *display_basename;
4054     gchar *string;
4055 
4056     priv = et_browser_get_instance_private (self);
4057 
4058     if (priv->rename_directory_dialog != NULL)
4059     {
4060         gtk_window_present(GTK_WINDOW(priv->rename_directory_dialog));
4061         return;
4062     }
4063 
4064     /* We get the full path but we musn't display the parent directories */
4065     parent = g_file_get_parent (priv->current_path);
4066 
4067     if (!parent)
4068     {
4069         return;
4070     }
4071 
4072     parent_path = g_file_get_path (parent);
4073     g_object_unref (parent);
4074 
4075     if (!parent_path)
4076     {
4077         return;
4078     }
4079 
4080     basename = g_file_get_basename (priv->current_path);
4081 
4082     if (!basename)
4083     {
4084         return;
4085     }
4086 
4087     display_basename = g_filename_display_name (basename);
4088 
4089     builder = gtk_builder_new_from_resource ("/org/gnome/EasyTAG/browser_dialogs.ui");
4090 
4091     priv->rename_directory_dialog = GTK_WIDGET (gtk_builder_get_object (builder,
4092                                                                         "rename_directory_dialog"));
4093 
4094     gtk_window_set_transient_for (GTK_WINDOW (priv->rename_directory_dialog),
4095                                   GTK_WINDOW (MainWindow));
4096     gtk_dialog_set_default_response (GTK_DIALOG (priv->rename_directory_dialog),
4097                                      GTK_RESPONSE_APPLY);
4098 
4099     /* We attach useful data to the combobox */
4100     g_object_set_data_full (G_OBJECT (priv->rename_directory_dialog),
4101                             "Parent_Directory", parent_path, g_free);
4102     g_object_set_data_full (G_OBJECT (priv->rename_directory_dialog),
4103                             "Current_Directory", basename, g_free);
4104     g_signal_connect (priv->rename_directory_dialog, "response",
4105                       G_CALLBACK (et_rename_directory_on_response), self);
4106 
4107     string = g_strdup_printf (_("Rename the directory ‘%s’ to:"),
4108                               display_basename);
4109     label = GTK_WIDGET (gtk_builder_get_object (builder, "rename_label"));
4110     gtk_label_set_label (GTK_LABEL (label), string);
4111     g_free (string);
4112     gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
4113 
4114     /* The entry to rename the directory. */
4115     priv->rename_directory_entry = GTK_WIDGET (gtk_builder_get_object (builder,
4116                                                                        "rename_entry"));
4117     /* Set the directory into the combobox */
4118     gtk_entry_set_text (GTK_ENTRY (priv->rename_directory_entry),
4119                         display_basename);
4120 
4121     /* Rename directory : check box + entry + Status icon */
4122     priv->rename_directory_mask_toggle = GTK_WIDGET (gtk_builder_get_object (builder,
4123                                                                              "rename_mask_check"));
4124     g_settings_bind (MainSettings, "rename-directory-with-mask",
4125                      priv->rename_directory_mask_toggle, "active",
4126                      G_SETTINGS_BIND_DEFAULT);
4127     g_signal_connect_swapped (priv->rename_directory_mask_toggle, "toggled",
4128                               G_CALLBACK (Rename_Directory_With_Mask_Toggled),
4129                               self);
4130 
4131     /* The entry to enter the mask to apply. */
4132     priv->rename_directory_mask_entry = GTK_WIDGET (gtk_builder_get_object (builder,
4133                                                                             "rename_mask_entry"));
4134     gtk_widget_set_size_request(priv->rename_directory_mask_entry, 80, -1);
4135 
4136     /* Signal to generate preview (preview of the new directory). */
4137     g_signal_connect_swapped (priv->rename_directory_mask_entry,
4138                               "changed",
4139                               G_CALLBACK (rename_directory_generate_preview),
4140                               self);
4141 
4142     g_settings_bind (MainSettings, "rename-directory-default-mask",
4143                      priv->rename_directory_mask_entry, "text",
4144                      G_SETTINGS_BIND_DEFAULT);
4145 
4146     /* Mask status icon. Signal connection to check if mask is correct to the
4147      * mask entry. */
4148     g_signal_connect (priv->rename_directory_mask_entry, "changed",
4149                       G_CALLBACK (entry_check_rename_file_mask), NULL);
4150 
4151     /* Preview label. */
4152     priv->rename_directory_preview_label = GTK_WIDGET (gtk_builder_get_object (builder,
4153                                                                                "rename_preview_label"));
4154     /* Button to save: to rename directory */
4155     button = gtk_dialog_get_widget_for_response (GTK_DIALOG (priv->rename_directory_dialog),
4156                                                  GTK_RESPONSE_APPLY);
4157     g_signal_connect_swapped (priv->rename_directory_entry,
4158                               "changed",
4159                               G_CALLBACK (empty_entry_disable_widget),
4160                               G_OBJECT (button));
4161 
4162     g_object_unref (builder);
4163 
4164     gtk_widget_show_all (priv->rename_directory_dialog);
4165 
4166     /* To initialize the 'Use mask' check button state. */
4167     g_signal_emit_by_name (G_OBJECT (priv->rename_directory_mask_toggle),
4168                            "toggled");
4169 
4170     /* To initialize PreviewLabel + MaskStatusIconBox. */
4171     g_signal_emit_by_name (priv->rename_directory_mask_entry, "changed");
4172 
4173     g_free (display_basename);
4174 }
4175 
4176 static void
Destroy_Rename_Directory_Window(EtBrowser * self)4177 Destroy_Rename_Directory_Window (EtBrowser *self)
4178 {
4179     EtBrowserPrivate *priv;
4180 
4181     priv = et_browser_get_instance_private (self);
4182 
4183     if (priv->rename_directory_dialog)
4184     {
4185         gtk_widget_destroy(priv->rename_directory_dialog);
4186         priv->rename_directory_preview_label = NULL;
4187         priv->rename_directory_dialog = NULL;
4188     }
4189 }
4190 
4191 static void
Rename_Directory(EtBrowser * self)4192 Rename_Directory (EtBrowser *self)
4193 {
4194     EtBrowserPrivate *priv;
4195     gchar *directory_parent;
4196     gchar *directory_last_name;
4197     gchar *directory_new_name;
4198     gchar *directory_new_name_file;
4199     gchar *last_path;
4200     gchar *last_path_utf8;
4201     gchar *new_path;
4202     gchar *new_path_utf8;
4203     gchar *tmp_path;
4204     gchar *tmp_path_utf8;
4205     gint   fd_tmp;
4206 
4207     priv = et_browser_get_instance_private (self);
4208 
4209     g_return_if_fail (priv->rename_directory_dialog != NULL);
4210 
4211     directory_parent    = g_object_get_data(G_OBJECT(priv->rename_directory_dialog),"Parent_Directory");
4212     directory_last_name = g_object_get_data(G_OBJECT(priv->rename_directory_dialog),"Current_Directory");
4213 
4214     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->rename_directory_mask_toggle)))
4215     {
4216         /* Renamed from mask. */
4217         gchar *mask;
4218 
4219         mask = g_settings_get_string (MainSettings,
4220                                       "rename-directory-default-mask");
4221         directory_new_name = et_scan_generate_new_directory_name_from_mask (ETCore->ETFileDisplayed,
4222                                                                             mask,
4223                                                                             FALSE);
4224         g_free (mask);
4225 
4226     }
4227     else
4228     {
4229         /* Renamed 'manually'. */
4230         directory_new_name  = g_strdup (gtk_entry_get_text (GTK_ENTRY (priv->rename_directory_entry)));
4231     }
4232 
4233     /* Check if a name for the directory have been supplied */
4234     if (et_str_empty (directory_new_name))
4235     {
4236         GtkWidget *msgdialog;
4237 
4238         msgdialog = gtk_message_dialog_new(GTK_WINDOW(MainWindow),
4239                                            GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
4240                                            GTK_MESSAGE_ERROR,
4241                                            GTK_BUTTONS_CLOSE,
4242                                            "%s",
4243                                            _("You must type a directory name"));
4244         gtk_window_set_title(GTK_WINDOW(msgdialog),_("Directory Name Error"));
4245 
4246         gtk_dialog_run(GTK_DIALOG(msgdialog));
4247         gtk_widget_destroy(msgdialog);
4248         g_free(directory_new_name);
4249         return;
4250     }
4251 
4252     /* Check that we can write the new directory name */
4253     directory_new_name_file = filename_from_display(directory_new_name);
4254     if (!directory_new_name_file)
4255     {
4256         GtkWidget *msgdialog;
4257 
4258         msgdialog = gtk_message_dialog_new(GTK_WINDOW(MainWindow),
4259                                            GTK_DIALOG_MODAL  | GTK_DIALOG_DESTROY_WITH_PARENT,
4260                                            GTK_MESSAGE_ERROR,
4261                                            GTK_BUTTONS_CLOSE,
4262                                            _("Could not convert ‘%s’ into filename encoding"),
4263                                            directory_new_name);
4264         gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (msgdialog),
4265                                                   _("Please use another name."));
4266         gtk_window_set_title(GTK_WINDOW(msgdialog),_("Directory Name Error"));
4267 
4268         gtk_dialog_run(GTK_DIALOG(msgdialog));
4269         gtk_widget_destroy(msgdialog);
4270         g_free(directory_new_name_file);
4271     }
4272 
4273     g_free (directory_new_name);
4274 
4275     /* If the directory name haven't been changed, we do nothing! */
4276     if (directory_last_name && directory_new_name_file
4277     && strcmp(directory_last_name,directory_new_name_file)==0)
4278     {
4279         Destroy_Rename_Directory_Window (self);
4280         g_free(directory_new_name_file);
4281         return;
4282     }
4283 
4284     /* Build the current and new absolute paths */
4285     last_path = g_build_filename (directory_parent, directory_last_name, NULL);
4286     last_path_utf8 = g_filename_display_name (last_path);
4287     new_path = g_build_filename (directory_parent, directory_new_name_file,
4288                                  NULL);
4289     new_path_utf8 = g_filename_display_name (new_path);
4290 
4291     /* TODO: Replace with g_file_move(). */
4292     /* Check if the new directory name doesn't already exists, and detect if
4293      * it's only a case change (needed for vfat) */
4294     if (g_file_test (new_path, G_FILE_TEST_IS_DIR))
4295     {
4296         GtkWidget *msgdialog;
4297         //gint response;
4298 
4299         if (strcasecmp(last_path,new_path) != 0)
4300         {
4301     // TODO
4302     //        // The same directory already exists. So we ask if we want to move the files
4303     //        msg = g_strdup_printf(_("The directory already exists!\n(%s)\nDo you want "
4304     //            "to move the files?"),new_path_utf8);
4305     //        msgbox = msg_box_new(_("Confirm"),
4306     //                             GTK_WINDOW(MainWindow),
4307     //                             NULL,
4308     //                             GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
4309     //                             msg,
4310     //                             GTK_STOCK_DIALOG_QUESTION,
4311     //                             GTK_STOCK_NO,  GTK_RESPONSE_NO,
4312 	//                             GTK_STOCK_YES, GTK_RESPONSE_YES,
4313     //                             NULL);
4314     //        g_free(msg);
4315     //        response = gtk_dialog_run(GTK_DIALOG(msgbox));
4316     //        gtk_widget_destroy(msgbox);
4317     //
4318     //        switch (response)
4319     //        {
4320     //            case GTK_STOCK_YES:
4321     //                // Here we must rename all files with the new location, and remove the directory
4322     //
4323     //                Rename_File ()
4324     //
4325     //                break;
4326     //            case BUTTON_NO:
4327     //                break;
4328     //        }
4329 
4330             msgdialog = gtk_message_dialog_new(GTK_WINDOW(MainWindow),
4331                                                GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
4332                                                GTK_MESSAGE_ERROR,
4333                                                GTK_BUTTONS_CLOSE,
4334                                                "%s",
4335                                                "Cannot rename file");
4336             gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (msgdialog),
4337                                                       _("The directory name ‘%s’ already exists."),new_path_utf8);
4338             gtk_window_set_title(GTK_WINDOW(msgdialog),_("Rename File Error"));
4339 
4340             gtk_dialog_run(GTK_DIALOG(msgdialog));
4341             gtk_widget_destroy(msgdialog);
4342 
4343             g_free(directory_new_name_file);
4344             g_free(last_path);
4345             g_free(last_path_utf8);
4346             g_free(new_path);
4347             g_free(new_path_utf8);
4348 
4349             return;
4350         }
4351     }
4352 
4353     /* Temporary path (useful when changing only string case) */
4354     tmp_path = g_strdup_printf("%s.XXXXXX",last_path);
4355     tmp_path_utf8 = g_filename_display_name (tmp_path);
4356 
4357     if ((fd_tmp = g_mkstemp (tmp_path)) >= 0)
4358     {
4359         /* TODO: handle error. */
4360         g_close (fd_tmp, NULL);
4361         g_unlink (tmp_path);
4362     }
4363 
4364     /* Rename the directory from 'last name' to 'tmp name' */
4365     if (g_rename (last_path, tmp_path) != 0)
4366     {
4367         GtkWidget *msgdialog;
4368 
4369         msgdialog = gtk_message_dialog_new(GTK_WINDOW(MainWindow),
4370                                            GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
4371                                            GTK_MESSAGE_ERROR,
4372                                            GTK_BUTTONS_CLOSE,
4373                                            "Cannot rename directory '%s' to '%s'",
4374                                            last_path_utf8,
4375                                            tmp_path_utf8);
4376         gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(msgdialog),"%s",g_strerror(errno));
4377         gtk_window_set_title(GTK_WINDOW(msgdialog),_("Rename Directory Error"));
4378 
4379         gtk_dialog_run(GTK_DIALOG(msgdialog));
4380         gtk_widget_destroy(msgdialog);
4381 
4382         g_free(directory_new_name_file);
4383         g_free(last_path);
4384         g_free(last_path_utf8);
4385         g_free(new_path);
4386         g_free(new_path_utf8);
4387         g_free(tmp_path);
4388         g_free(tmp_path_utf8);
4389 
4390         return;
4391     }
4392 
4393     /* Rename the directory from 'tmp name' to 'new name' (final name) */
4394     if (g_rename (tmp_path, new_path) != 0)
4395     {
4396         GtkWidget *msgdialog;
4397 
4398         msgdialog = gtk_message_dialog_new(GTK_WINDOW(MainWindow),
4399                                            GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
4400                                            GTK_MESSAGE_ERROR,
4401                                            GTK_BUTTONS_CLOSE,
4402                                            "Cannot rename directory '%s' to '%s",
4403                                            tmp_path_utf8,
4404                                            new_path_utf8);
4405         gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(msgdialog),"%s",g_strerror(errno));
4406         gtk_window_set_title(GTK_WINDOW(msgdialog),_("Rename Directory Error"));
4407 
4408         gtk_dialog_run(GTK_DIALOG(msgdialog));
4409         gtk_widget_destroy(msgdialog);
4410 
4411         g_free(directory_new_name_file);
4412         g_free(last_path);
4413         g_free(last_path_utf8);
4414         g_free(new_path);
4415         g_free(new_path_utf8);
4416         g_free(tmp_path);
4417         g_free(tmp_path_utf8);
4418 
4419         return;
4420     }
4421 
4422     et_file_list_update_directory_name (ETCore->ETFileList, last_path,
4423                                         new_path);
4424     Browser_Tree_Rename_Directory (self, last_path, new_path);
4425 
4426     // To update file path in the browser entry
4427     if (ETCore->ETFileDisplayedList)
4428     {
4429         et_application_window_display_et_file (ET_APPLICATION_WINDOW (MainWindow),
4430                                                ETCore->ETFileDisplayed);
4431     }else
4432     {
4433         gchar *tmp = g_file_get_parse_name (et_browser_get_current_path (self));
4434         et_browser_entry_set_text (self, tmp);
4435         g_free (tmp);
4436     }
4437 
4438     Destroy_Rename_Directory_Window (self);
4439     g_free(last_path);
4440     g_free(last_path_utf8);
4441     g_free(new_path);
4442     g_free(new_path_utf8);
4443     g_free(tmp_path);
4444     g_free(tmp_path_utf8);
4445     g_free(directory_new_name_file);
4446     et_application_window_status_bar_message (ET_APPLICATION_WINDOW (MainWindow),
4447                                               _("Directory renamed"), TRUE);
4448 }
4449 
4450 static void
Rename_Directory_With_Mask_Toggled(EtBrowser * self)4451 Rename_Directory_With_Mask_Toggled (EtBrowser *self)
4452 {
4453     EtBrowserPrivate *priv;
4454 
4455     priv = et_browser_get_instance_private (self);
4456 
4457     gtk_widget_set_sensitive (priv->rename_directory_entry,
4458                               !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->rename_directory_mask_toggle)));
4459     gtk_widget_set_sensitive (priv->rename_directory_mask_entry,
4460                               gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->rename_directory_mask_toggle)));
4461     gtk_widget_set_sensitive (priv->rename_directory_preview_label,
4462                               gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->rename_directory_mask_toggle)));
4463 }
4464 
4465 
4466 /*
4467  * Window where is typed the name of the program to run, which
4468  * receives the current directory as parameter.
4469  */
4470 void
et_browser_show_open_directory_with_dialog(EtBrowser * self)4471 et_browser_show_open_directory_with_dialog (EtBrowser *self)
4472 {
4473     EtBrowserPrivate *priv;
4474     GtkBuilder *builder;
4475     GtkWidget *button;
4476     gchar *current_directory = NULL;
4477 
4478     g_return_if_fail (ET_BROWSER (self));
4479 
4480     priv = et_browser_get_instance_private (self);
4481 
4482     if (priv->open_directory_with_dialog != NULL)
4483     {
4484         gtk_window_present(GTK_WINDOW(priv->open_directory_with_dialog));
4485         return;
4486     }
4487 
4488     /* Current directory. */
4489     if (!priv->current_path)
4490     {
4491         return;
4492     }
4493 
4494     current_directory = g_file_get_path (priv->current_path);
4495 
4496     builder = gtk_builder_new_from_resource ("/org/gnome/EasyTAG/browser_dialogs.ui");
4497 
4498     priv->open_directory_with_dialog = GTK_WIDGET (gtk_builder_get_object (builder,
4499                                                                            "open_directory_dialog"));
4500 
4501     gtk_window_set_transient_for (GTK_WINDOW (priv->open_directory_with_dialog),
4502                                   GTK_WINDOW (MainWindow));
4503     gtk_dialog_set_default_response (GTK_DIALOG (priv->open_directory_with_dialog),
4504                                      GTK_RESPONSE_OK);
4505     g_signal_connect (priv->open_directory_with_dialog, "response",
4506                       G_CALLBACK (et_run_program_tree_on_response), self);
4507 
4508     /* The combobox to enter the program to run */
4509     priv->open_directory_with_combobox = GTK_WIDGET (gtk_builder_get_object (builder,
4510                                                                              "open_directory_combo"));
4511     gtk_combo_box_set_model (GTK_COMBO_BOX (priv->open_directory_with_combobox),
4512                              GTK_TREE_MODEL (priv->run_program_model));
4513 
4514     /* History list */
4515     gtk_list_store_clear (priv->run_program_model);
4516     Load_Run_Program_With_Directory_List (priv->run_program_model,
4517                                           MISC_COMBO_TEXT);
4518     g_signal_connect_swapped (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (priv->open_directory_with_combobox))),
4519                               "activate",
4520                               G_CALLBACK (Run_Program_With_Directory),
4521                               self);
4522 
4523     /* The button to Browse */
4524     button = GTK_WIDGET (gtk_builder_get_object (builder,
4525                                                  "open_directory_button"));
4526     g_signal_connect_swapped (button, "clicked",
4527                               G_CALLBACK (File_Selection_Window_For_File),
4528                               G_OBJECT (gtk_bin_get_child (GTK_BIN (priv->open_directory_with_combobox))));
4529 
4530     /* We attach useful data to the combobox (into Run_Program_With_Directory) */
4531     g_object_set_data_full (G_OBJECT (priv->open_directory_with_combobox),
4532                             "Current_Directory", current_directory, g_free);
4533 
4534     /* Button to execute */
4535     button = gtk_dialog_get_widget_for_response (GTK_DIALOG (priv->open_directory_with_dialog),
4536                                                  GTK_RESPONSE_OK);
4537     g_signal_connect_swapped (button, "clicked",
4538                               G_CALLBACK (Run_Program_With_Directory),
4539                               self);
4540     g_signal_connect_swapped (gtk_bin_get_child (GTK_BIN (priv->open_directory_with_combobox)),
4541                               "changed",
4542                               G_CALLBACK (empty_entry_disable_widget),
4543                               G_OBJECT (button));
4544     g_signal_emit_by_name (G_OBJECT (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (priv->open_directory_with_combobox)))),
4545                            "changed", NULL);
4546 
4547     gtk_widget_show_all (priv->open_directory_with_dialog);
4548 }
4549 
4550 static void
Destroy_Run_Program_Tree_Window(EtBrowser * self)4551 Destroy_Run_Program_Tree_Window (EtBrowser *self)
4552 {
4553     EtBrowserPrivate *priv;
4554 
4555     priv = et_browser_get_instance_private (self);
4556 
4557     if (priv->open_directory_with_dialog)
4558     {
4559         gtk_widget_hide (priv->open_directory_with_dialog);
4560     }
4561 }
4562 
4563 void
Run_Program_With_Directory(EtBrowser * self)4564 Run_Program_With_Directory (EtBrowser *self)
4565 {
4566     EtBrowserPrivate *priv;
4567     gchar *program_name;
4568     gchar *current_directory;
4569     GList *args_list = NULL;
4570     gboolean program_ran;
4571     GError *error = NULL;
4572 
4573     priv = et_browser_get_instance_private (self);
4574 
4575     program_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (priv->open_directory_with_combobox)))));
4576     current_directory = g_object_get_data (G_OBJECT (priv->open_directory_with_combobox),
4577                                            "Current_Directory");
4578 
4579     // List of parameters (here only one! : the current directory)
4580     args_list = g_list_append(args_list,current_directory);
4581 
4582     program_ran = et_run_program (program_name, args_list, &error);
4583     g_list_free(args_list);
4584 
4585     if (program_ran)
4586     {
4587         gchar *msg;
4588 
4589         // Append newest choice to the drop down list
4590         Add_String_To_Combo_List(priv->run_program_model, program_name);
4591 
4592         // Save list attached to the combobox
4593         Save_Run_Program_With_Directory_List(priv->run_program_model, MISC_COMBO_TEXT);
4594 
4595         Destroy_Run_Program_Tree_Window (self);
4596 
4597         msg = g_strdup_printf (_("Executed command ‘%s’"), program_name);
4598         et_application_window_status_bar_message (ET_APPLICATION_WINDOW (MainWindow),
4599                                                   msg, TRUE);
4600 
4601         g_free (msg);
4602     }
4603     else
4604     {
4605         Log_Print (LOG_ERROR, _("Failed to launch program ‘%s’"),
4606                    error->message);
4607         g_clear_error (&error);
4608     }
4609 
4610     g_free(program_name);
4611 }
4612 
4613 static void
Run_Program_With_Selected_Files(EtBrowser * self)4614 Run_Program_With_Selected_Files (EtBrowser *self)
4615 {
4616     EtBrowserPrivate *priv;
4617     gchar   *program_name;
4618     GList   *selected_paths;
4619     GList *l;
4620     GList   *args_list = NULL;
4621     GtkTreeIter iter;
4622     gboolean program_ran;
4623     GError *error = NULL;
4624 
4625     priv = et_browser_get_instance_private (self);
4626 
4627     if (!GTK_IS_COMBO_BOX (priv->open_files_with_combobox) || !ETCore->ETFileDisplayedList)
4628         return;
4629 
4630     // Programe name to run
4631     program_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (priv->open_files_with_combobox)))));
4632 
4633     // List of files to pass as parameters
4634     selected_paths = gtk_tree_selection_get_selected_rows(gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->file_view)), NULL);
4635 
4636     for (l = selected_paths; l != NULL; l = g_list_next (l))
4637     {
4638         if (gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->file_model), &iter,
4639                                      (GtkTreePath *)l->data))
4640         {
4641             const ET_File *ETFile;
4642 
4643             gtk_tree_model_get (GTK_TREE_MODEL (priv->file_model), &iter,
4644                                 LIST_FILE_POINTER, &ETFile, -1);
4645 
4646             args_list = g_list_prepend (args_list,
4647                                         ((File_Name *)ETFile->FileNameCur->data)->value);
4648             //args_list = g_list_append(args_list,((File_Name *)ETFile->FileNameCur->data)->value_utf8);
4649         }
4650     }
4651 
4652     args_list = g_list_reverse (args_list);
4653     program_ran = et_run_program (program_name, args_list, &error);
4654 
4655     g_list_free_full (selected_paths, (GDestroyNotify)gtk_tree_path_free);
4656     g_list_free(args_list);
4657 
4658     if (program_ran)
4659     {
4660         gchar *msg;
4661 
4662         // Append newest choice to the drop down list
4663         //gtk_list_store_prepend(GTK_LIST_STORE(priv->run_program_model), &iter);
4664         //gtk_list_store_set(priv->run_program_model, &iter, MISC_COMBO_TEXT, program_name, -1);
4665         Add_String_To_Combo_List(GTK_LIST_STORE(priv->run_program_model), program_name);
4666 
4667         // Save list attached to the combobox
4668         Save_Run_Program_With_File_List(priv->run_program_model, MISC_COMBO_TEXT);
4669 
4670         Destroy_Run_Program_List_Window (self);
4671 
4672         msg = g_strdup_printf (_("Executed command ‘%s’"), program_name);
4673         et_application_window_status_bar_message (ET_APPLICATION_WINDOW (MainWindow),
4674                                                   msg, TRUE);
4675 
4676         g_free (msg);
4677     }
4678     else
4679     {
4680         Log_Print (LOG_ERROR, _("Failed to launch program ‘%s’"),
4681                    error->message);
4682         g_clear_error (&error);
4683     }
4684 
4685     g_free(program_name);
4686 }
4687 
4688 /*
4689  * Window where is typed the name of the program to run, which
4690  * receives the current file as parameter.
4691  */
4692 void
et_browser_show_open_files_with_dialog(EtBrowser * self)4693 et_browser_show_open_files_with_dialog (EtBrowser *self)
4694 {
4695     EtBrowserPrivate *priv;
4696     GtkBuilder *builder;
4697     GtkWidget *button;
4698 
4699     g_return_if_fail (ET_BROWSER (self));
4700 
4701     priv = et_browser_get_instance_private (self);
4702 
4703     if (priv->open_files_with_dialog != NULL)
4704     {
4705         gtk_window_present(GTK_WINDOW(priv->open_files_with_dialog));
4706         return;
4707     }
4708 
4709     builder = gtk_builder_new_from_resource ("/org/gnome/EasyTAG/browser_dialogs.ui");
4710 
4711     priv->open_files_with_dialog = GTK_WIDGET (gtk_builder_get_object (builder,
4712                                                                        "open_files_dialog"));
4713     gtk_dialog_set_default_response (GTK_DIALOG (priv->open_files_with_dialog),
4714                                      GTK_RESPONSE_OK);
4715     gtk_window_set_transient_for (GTK_WINDOW (priv->open_files_with_dialog),
4716                                   GTK_WINDOW (MainWindow));
4717     g_signal_connect ((priv->open_files_with_dialog), "response",
4718                       G_CALLBACK (et_run_program_list_on_response), self);
4719 
4720     /* The combobox to enter the program to run */
4721     priv->open_files_with_combobox = GTK_WIDGET (gtk_builder_get_object (builder,
4722                                                                          "open_files_combo"));
4723     gtk_combo_box_set_model (GTK_COMBO_BOX (priv->open_files_with_combobox),
4724                              GTK_TREE_MODEL (priv->run_program_model));
4725     gtk_widget_set_size_request (GTK_WIDGET (priv->open_files_with_combobox),
4726                                  250, -1);
4727 
4728     /* History list */
4729     gtk_list_store_clear (priv->run_program_model);
4730     Load_Run_Program_With_File_List (priv->run_program_model, MISC_COMBO_TEXT);
4731     g_signal_connect_swapped (gtk_bin_get_child (GTK_BIN (priv->open_files_with_combobox)),
4732                               "activate",
4733                               G_CALLBACK (Run_Program_With_Selected_Files),
4734 			                  self);
4735 
4736     /* The button to Browse */
4737     button = GTK_WIDGET (gtk_builder_get_object (builder,
4738                                                  "open_files_button"));
4739     g_signal_connect_swapped (button, "clicked",
4740                               G_CALLBACK (File_Selection_Window_For_File),
4741                               G_OBJECT (gtk_bin_get_child (GTK_BIN (priv->open_files_with_combobox))));
4742 
4743     g_object_unref (builder);
4744 
4745     /* Button to execute */
4746     button = gtk_dialog_get_widget_for_response (GTK_DIALOG (priv->open_files_with_dialog),
4747                                                  GTK_RESPONSE_OK);
4748     g_signal_connect_swapped (button, "clicked",
4749                               G_CALLBACK (Run_Program_With_Selected_Files),
4750 			                  self);
4751     g_signal_connect_swapped (gtk_bin_get_child (GTK_BIN (priv->open_files_with_combobox)),
4752                               "changed",
4753                               G_CALLBACK (empty_entry_disable_widget),
4754                               G_OBJECT (button));
4755     g_signal_emit_by_name (gtk_bin_get_child (GTK_BIN (priv->open_files_with_combobox)),
4756                            "changed", NULL);
4757 
4758     gtk_widget_show_all (priv->open_files_with_dialog);
4759 }
4760 
4761 static void
Destroy_Run_Program_List_Window(EtBrowser * self)4762 Destroy_Run_Program_List_Window (EtBrowser *self)
4763 {
4764     EtBrowserPrivate *priv;
4765 
4766     priv = et_browser_get_instance_private (self);
4767 
4768     if (priv->open_files_with_dialog)
4769     {
4770         gtk_widget_hide (priv->open_files_with_dialog);
4771     }
4772 }
4773 
4774 /*
4775  * empty_entry_disable_widget:
4776  * @widget: a widget to set sensitive if @entry contains text
4777  * @entry: the entry for which to test the text
4778  *
4779  * Make @widget insensitive if @entry contains no text, or sensitive otherwise.
4780  */
4781 static void
empty_entry_disable_widget(GtkWidget * widget,GtkEntry * entry)4782 empty_entry_disable_widget (GtkWidget *widget, GtkEntry *entry)
4783 {
4784     const gchar *text;
4785 
4786     g_return_if_fail (widget != NULL && entry != NULL);
4787 
4788     text = gtk_entry_get_text (GTK_ENTRY (entry));
4789 
4790     gtk_widget_set_sensitive (widget, text && *text);
4791 }
4792 
4793 /*
4794  * et_rename_directory_on_response:
4795  * @dialog: the dialog which emitted the response signal
4796  * @response_id: the response ID
4797  * @user_data: user data set when the signal was connected
4798  *
4799  * Signal handler for the rename directory dialog.
4800  */
4801 static void
et_rename_directory_on_response(GtkDialog * dialog,gint response_id,gpointer user_data)4802 et_rename_directory_on_response (GtkDialog *dialog, gint response_id,
4803                                  gpointer user_data)
4804 {
4805     EtBrowser *self;
4806 
4807     self = ET_BROWSER (user_data);
4808 
4809     switch (response_id)
4810     {
4811         case GTK_RESPONSE_APPLY:
4812             Rename_Directory (self);
4813             break;
4814         case GTK_RESPONSE_CANCEL:
4815         case GTK_RESPONSE_DELETE_EVENT:
4816             Destroy_Rename_Directory_Window (self);
4817             break;
4818         default:
4819             g_assert_not_reached ();
4820     }
4821 }
4822 
4823 /*
4824  * et_run_program_tree_on_response:
4825  * @dialog: the dialog which emitted the response signal
4826  * @response_id: the response ID
4827  * @user_data: user data set when the signal was connected
4828  *
4829  * Signal handler for the run program on directory tree dialog.
4830  */
4831 static void
et_run_program_tree_on_response(GtkDialog * dialog,gint response_id,gpointer user_data)4832 et_run_program_tree_on_response (GtkDialog *dialog, gint response_id,
4833                                  gpointer user_data)
4834 {
4835     EtBrowser *self;
4836 
4837     self = ET_BROWSER (user_data);
4838 
4839     switch (response_id)
4840     {
4841         case GTK_RESPONSE_OK:
4842             /* FIXME: Ignored for now. */
4843             break;
4844         case GTK_RESPONSE_CANCEL:
4845         case GTK_RESPONSE_DELETE_EVENT:
4846             Destroy_Run_Program_Tree_Window (self);
4847             break;
4848         default:
4849             g_assert_not_reached ();
4850     }
4851 }
4852 /*
4853  * et_run_program_list_on_response:
4854  * @dialog: the dialog which emitted the response signal
4855  * @response_id: the response ID
4856  * @user_data: user data set when the signal was connected
4857  *
4858  * Signal handler for the run program on selected file dialog.
4859  */
4860 static void
et_run_program_list_on_response(GtkDialog * dialog,gint response_id,gpointer user_data)4861 et_run_program_list_on_response (GtkDialog *dialog, gint response_id,
4862                                  gpointer user_data)
4863 {
4864     EtBrowser *self;
4865 
4866     self = ET_BROWSER (user_data);
4867 
4868     switch (response_id)
4869     {
4870         case GTK_RESPONSE_OK:
4871             /* FIXME: Ignored for now. */
4872             break;
4873         case GTK_RESPONSE_CANCEL:
4874         case GTK_RESPONSE_DELETE_EVENT:
4875             Destroy_Run_Program_List_Window (self);
4876             break;
4877         default:
4878             g_assert_not_reached ();
4879     }
4880 }
4881 
4882 /*
4883  * get_sort_order_for_column_id:
4884  * @column_id: the column ID for which to set the sort order
4885  *
4886  * Gets the sort order for the given column ID from the browser list treeview
4887  * column.
4888  *
4889  * Returns: the sort order for @column_id
4890  */
4891 GtkSortType
et_browser_get_sort_order_for_column_id(EtBrowser * self,gint column_id)4892 et_browser_get_sort_order_for_column_id (EtBrowser *self, gint column_id)
4893 {
4894     GtkTreeViewColumn *column;
4895 
4896     column = et_browser_get_column_for_column_id (self, column_id);
4897     return gtk_tree_view_column_get_sort_order (column);
4898 }
4899 
4900 /*
4901  * get_column_for_column_id:
4902  * @column_id: the column ID of the #GtkTreeViewColumn to fetch
4903  *
4904  * Gets the browser list treeview column for the given column ID.
4905  *
4906  * Returns: (transfer none): the tree view column corresponding to @column_id
4907  */
4908 GtkTreeViewColumn *
et_browser_get_column_for_column_id(EtBrowser * self,gint column_id)4909 et_browser_get_column_for_column_id (EtBrowser *self, gint column_id)
4910 {
4911     EtBrowserPrivate *priv;
4912 
4913     priv = et_browser_get_instance_private (self);
4914 
4915     return gtk_tree_view_get_column (GTK_TREE_VIEW (priv->file_view),
4916                                      column_id);
4917 }
4918 
4919 static void
et_browser_destroy(GtkWidget * widget)4920 et_browser_destroy (GtkWidget *widget)
4921 {
4922     EtBrowserPrivate *priv;
4923 
4924     priv = et_browser_get_instance_private (ET_BROWSER (widget));
4925 
4926     /* Save combobox history list before exit. */
4927     if (priv->entry_model)
4928     {
4929         Save_Path_Entry_List (priv->entry_model, MISC_COMBO_TEXT);
4930         priv->entry_model = NULL;
4931         /* The model is disposed when the combo box is disposed. */
4932     }
4933 
4934     GTK_WIDGET_CLASS (et_browser_parent_class)->destroy (widget);
4935 }
4936 
4937 static void
et_browser_finalize(GObject * object)4938 et_browser_finalize (GObject *object)
4939 {
4940     EtBrowserPrivate *priv;
4941 
4942     priv = et_browser_get_instance_private (ET_BROWSER (object));
4943 
4944     g_clear_object (&priv->current_path);
4945     g_clear_object (&priv->run_program_model);
4946 
4947     G_OBJECT_CLASS (et_browser_parent_class)->finalize (object);
4948 }
4949 
4950 static void
et_browser_init(EtBrowser * self)4951 et_browser_init (EtBrowser *self)
4952 {
4953     gtk_widget_init_template (GTK_WIDGET (self));
4954     create_browser (self);
4955 }
4956 
4957 static
et_browser_class_init(EtBrowserClass * klass)4958 void et_browser_class_init (EtBrowserClass *klass)
4959 {
4960     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
4961 
4962     G_OBJECT_CLASS (klass)->finalize = et_browser_finalize;
4963     widget_class->destroy = et_browser_destroy;
4964 
4965     gtk_widget_class_set_template_from_resource (widget_class,
4966                                                  "/org/gnome/EasyTAG/browser.ui");
4967     gtk_widget_class_bind_template_child_private (widget_class, EtBrowser,
4968                                                   files_label);
4969     gtk_widget_class_bind_template_child_private (widget_class, EtBrowser,
4970                                                   open_button);
4971     gtk_widget_class_bind_template_child_private (widget_class, EtBrowser,
4972                                                   entry_model);
4973     gtk_widget_class_bind_template_child_private (widget_class, EtBrowser,
4974                                                   entry_combo);
4975     gtk_widget_class_bind_template_child_private (widget_class, EtBrowser,
4976                                                   directory_album_artist_notebook);
4977     gtk_widget_class_bind_template_child_private (widget_class, EtBrowser,
4978                                                   file_model);
4979     gtk_widget_class_bind_template_child_private (widget_class, EtBrowser,
4980                                                   file_view);
4981     gtk_widget_class_bind_template_child_private (widget_class, EtBrowser,
4982                                                   album_model);
4983     gtk_widget_class_bind_template_child_private (widget_class, EtBrowser,
4984                                                   album_view);
4985     gtk_widget_class_bind_template_child_private (widget_class, EtBrowser,
4986                                                   artist_model);
4987     gtk_widget_class_bind_template_child_private (widget_class, EtBrowser,
4988                                                   artist_view);
4989     gtk_widget_class_bind_template_child_private (widget_class, EtBrowser,
4990                                                   directory_model);
4991     gtk_widget_class_bind_template_child_private (widget_class, EtBrowser,
4992                                                   directory_view);
4993     gtk_widget_class_bind_template_callback (widget_class, collapse_cb);
4994     gtk_widget_class_bind_template_callback (widget_class, expand_cb);
4995     gtk_widget_class_bind_template_callback (widget_class,
4996                                              on_album_tree_button_press_event);
4997     gtk_widget_class_bind_template_callback (widget_class,
4998                                              on_artist_tree_button_press_event);
4999     gtk_widget_class_bind_template_callback (widget_class,
5000                                              on_directory_tree_button_press_event);
5001     gtk_widget_class_bind_template_callback (widget_class,
5002                                              on_file_tree_button_press_event);
5003     gtk_widget_class_bind_template_callback (widget_class,
5004                                              on_album_tree_popup_menu);
5005     gtk_widget_class_bind_template_callback (widget_class,
5006                                              on_artist_tree_popup_menu);
5007     gtk_widget_class_bind_template_callback (widget_class,
5008                                              on_directory_tree_popup_menu);
5009     gtk_widget_class_bind_template_callback (widget_class,
5010                                              on_file_tree_popup_menu);
5011     gtk_widget_class_bind_template_callback (widget_class,
5012                                              Browser_Entry_Activated);
5013     gtk_widget_class_bind_template_callback (widget_class,
5014                                              Browser_List_Key_Press);
5015     gtk_widget_class_bind_template_callback (widget_class,
5016                                              Browser_Tree_Key_Press);
5017     gtk_widget_class_bind_template_callback (widget_class,
5018                                              Browser_Tree_Node_Selected);
5019 }
5020 
5021 /*
5022  * et_browser_new:
5023  *
5024  * Create a new EtBrowser instance.
5025  *
5026  * Returns: a new #EtBrowser
5027  */
5028 EtBrowser *
et_browser_new(void)5029 et_browser_new (void)
5030 {
5031     return g_object_new (ET_TYPE_BROWSER, NULL);
5032 }
5033