aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/browser.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/browser.c')
-rwxr-xr-xsrc/browser.c4084
1 files changed, 4084 insertions, 0 deletions
diff --git a/src/browser.c b/src/browser.c
new file mode 100755
index 0000000..e807e7a
--- /dev/null
+++ b/src/browser.c
@@ -0,0 +1,4084 @@
+/* browser.c - 2000/04/28 */
+/*
+ * EasyTAG - Tag editor for MP3 and Ogg Vorbis files
+ * Copyright (C) 2000-2003 Jerome Couderc <easytag@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+/* Some parts of this browser are taken from:
+ * XMMS - Cross-platform multimedia player
+ * Copyright (C) 1998-1999 Peter Alm, Mikael Alm, Olle Hallnas,
+ * Thomas Nilsson and 4Front Technologies
+ */
+
+#include <config.h>
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include <gdk/gdk.h>
+#include <glib/gi18n-lib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "easytag.h"
+#include "browser.h"
+#include "et_core.h"
+#include "scan.h"
+#include "msgbox.h"
+#include "bar.h"
+#include "log.h"
+#include "misc.h"
+#include "setting.h"
+#include "charset.h"
+#include "dlm.h"
+
+#include <assert.h>
+
+#ifdef WIN32
+# include <windows.h>
+# include "win32/win32dep.h"
+#endif
+
+/* Pixmaps */
+#include "../pixmaps/opened_folder.xpm"
+#include "../pixmaps/closed_folder.xpm"
+#include "../pixmaps/closed_folder_locked.xpm"
+#ifdef WIN32
+#include "../pixmaps/ram_disk.xpm"
+#endif
+
+
+
+/****************
+ * Declarations *
+ ****************/
+
+// Pixmaps
+static GdkPixbuf *opened_folder_pixmap = NULL, *closed_folder_pixmap, *closed_folder_locked_pixmap;
+#ifdef WIN32
+static GdkPixbuf *harddrive_pixmap, *removable_pixmap, *cdrom_pixmap, *network_pixmap, *ramdisk_pixmap;
+#endif
+
+GtkWidget *BrowserTree; // Tree of directories
+GtkTreeStore *directoryTreeModel;
+GtkWidget *BrowserList; // List of files
+GtkListStore *fileListModel;
+GtkWidget *BrowserLabel;
+GtkWidget *BrowserButton;
+GtkWidget *BrowserNoteBook;
+GtkWidget *BrowserArtistList;
+GtkListStore *artistListModel;
+GtkWidget *BrowserAlbumList;
+GtkListStore *albumListModel;
+gchar *BrowserCurrentPath = NULL; // Path selected in the browser area (BrowserEntry or BrowserTree)
+
+GtkListStore *RunProgramModel;
+
+GtkWidget *RunProgramTreeWindow = NULL;
+GtkWidget *RunProgramListWindow = NULL;
+
+// The Rename Directory window
+GtkWidget *RenameDirectoryWindow = NULL;
+GtkWidget *RenameDirectoryCombo;
+GtkWidget *RenameDirectoryWithMask;
+GtkWidget *RenameDirectoryMaskCombo;
+GtkListStore *RenameDirectoryMaskModel = NULL;
+GtkWidget *RenameDirectoryMaskStatusIconBox;
+GtkWidget *RenameDirectoryPreviewLabel = NULL;
+
+guint blrs_idle_handler_id = 0;
+guint blru_idle_handler_id = 0;
+guint bl_row_selected;
+
+ET_File *LastBrowserListETFileSelected; // The last ETFile selected in the BrowserList
+
+
+gchar *Rename_Directory_Masks [] =
+{
+ "%a - %b",
+ "%a_-_%b",
+ "%a - %b (%y) - %g",
+ "%a_-_%b_(%y)_-_%g",
+ "VA - %b (%y)",
+ "VA_-_%b_(%y)",
+ NULL
+};
+
+
+/**************
+ * Prototypes *
+ **************/
+
+gboolean Browser_Tree_Key_Press (GtkWidget *tree, GdkEvent *event, gpointer data);
+void Browser_Tree_Set_Node_Visible (GtkWidget *directoryView, GtkTreePath * path);
+void Browser_List_Set_Row_Visible (GtkTreeModel *treeModel, GtkTreeIter *rowIter);
+void Browser_Tree_Disable (void);
+void Browser_Tree_Enable (void);
+void Browser_Tree_Initialize (void);
+gboolean Browser_Tree_Node_Selected (GtkTreeSelection *selection, gpointer user_data);
+void Browser_Tree_Rename_Directory (gchar *last_path, gchar *new_path);
+void Browser_Tree_Handle_Rename (GtkTreeIter *parentnode, gchar *old_path, gchar *new_path);
+
+static gint Browser_List_Key_Press (GtkWidget *list, GdkEvent *event, gpointer data);
+gboolean Browser_List_Button_Press (GtkTreeView *treeView, GdkEventButton *event);
+void Browser_List_Disable (void);
+void Browser_List_Enable (void);
+void Browser_List_Row_Selected (GtkTreeSelection * selection, gpointer data);
+gint Browser_List_Sort_Func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data);
+void Browser_List_Select_All_Files (void);
+void Browser_List_Unselect_All_Files (void);
+void Browser_List_Invert_File_Selection (void);
+
+void Browser_Entry_Activated (void);
+void Browser_Entry_Disable (void);
+void Browser_Entry_Enable (void);
+
+void Browser_Button_Clicked (void);
+
+void Browser_Artist_List_Load_Files (ET_File *etfile_to_select);
+void Browser_Artist_List_Row_Selected (GtkTreeSelection *selection, gpointer data);
+void Browser_Artist_List_Set_Row_Appearance(GtkTreeIter *row);
+
+void Browser_Album_List_Load_Files (GList *albumlist, ET_File *etfile_to_select);
+void Browser_Album_List_Row_Selected (GtkTreeSelection *selection, gpointer data);
+void Browser_Album_List_Set_Row_Appearance(GtkTreeIter *row);
+
+gchar *Browser_Get_Current_Path (void);
+void Browser_Update_Current_Path (gchar *path);
+void Browser_Load_Home_Directory (void);
+void Browser_Load_Default_Directory (void);
+void Browser_Reload_Directory (void);
+
+gint Browser_Win32_Get_Drive_Root (gchar *drive, GtkTreeIter *rootNode, GtkTreePath **rootPath);
+
+static gboolean check_for_subdir (gchar *path);
+
+GtkTreePath *Find_Child_Node(GtkTreeIter *parent, gchar *searchtext);
+
+gboolean Check_For_Access_Permission (gchar *path);
+
+static void expand_cb (GtkWidget *tree, GtkTreeIter *iter, GtkTreePath *path, gpointer data);
+static void collapse_cb (GtkWidget *tree, GtkTreeIter *iter, GtkTreePath *treePath, gpointer data);
+
+/* Pop up menus */
+gboolean Browser_Popup_Menu_Handler (GtkMenu *menu, GdkEventButton *event);
+
+/* For window to rename a directory */
+void Browser_Open_Rename_Directory_Window (void);
+void Destroy_Rename_Directory_Window (void);
+void Rename_Directory (void);
+gboolean Rename_Directory_Window_Key_Press (GtkWidget *window, GdkEvent *event);
+void Rename_Directory_With_Mask_Toggled (void);
+
+/* For window to run a program with the directory */
+void Browser_Open_Run_Program_Tree_Window (void);
+void Destroy_Run_Program_Tree_Window (void);
+gboolean Run_Program_Tree_Window_Key_Press (GtkWidget *window, GdkEvent *event);
+void Run_Program_With_Directory (GtkObject *combobox);
+
+/* For window to run a program with the file */
+void Browser_Open_Run_Program_List_Window (void);
+void Destroy_Run_Program_List_Window (void);
+gboolean Run_Program_List_Window_Key_Press (GtkWidget *window, GdkEvent *event);
+void Run_Program_With_Selected_Files (GtkObject *combobox);
+
+gboolean Run_Program (gchar *program_name, GList *args_list);
+
+
+
+/*************
+ * Functions *
+ *************/
+/*
+ * Load home directory
+ */
+void Browser_Load_Home_Directory (void)
+{
+ Browser_Tree_Select_Dir(HOME_VARIABLE);
+}
+
+
+/*
+ * Load default directory
+ */
+void Browser_Load_Default_Directory (void)
+{
+ gchar *temp;
+ temp = g_strdup(DEFAULT_PATH_TO_MP3);
+
+ if (temp && g_utf8_strlen(temp, -1)>0)
+ {
+ Browser_Tree_Select_Dir(temp);
+ } else
+ {
+ g_free(temp);
+ temp = g_strdup(HOME_VARIABLE);
+ Browser_Tree_Select_Dir(HOME_VARIABLE);
+ }
+ g_free(temp);
+}
+
+
+/*
+ * Get the path from the selected node (row) in the browser
+ * Warning: return NULL if no row selected int the tree.
+ * Remember to free the value returned from this function!
+ */
+gchar *Browser_Tree_Get_Path_Of_Selected_Node (void)
+{
+ GtkTreeSelection *selection;
+ GtkTreeIter selectedIter;
+ gchar *path;
+
+ if (!BrowserTree) return NULL;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserTree));
+ if (selection
+ && gtk_tree_selection_get_selected(selection, NULL, &selectedIter))
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(directoryTreeModel), &selectedIter,
+ TREE_COLUMN_FULL_PATH, &path, -1);
+ return path;
+ }else
+ {
+ return NULL;
+ }
+}
+
+
+/*
+ * Set the 'path' within the variable BrowserCurrentPath.
+ */
+void Browser_Update_Current_Path (gchar *path)
+{
+ /* Be sure that we aren't passing 'BrowserCurrentPath' as parameter of the function :
+ * to avoid some memory problems */
+ if (path == NULL || path == BrowserCurrentPath) return;
+
+ if (BrowserCurrentPath != NULL)
+ g_free(BrowserCurrentPath);
+ BrowserCurrentPath = g_strdup(path);
+
+#ifdef WIN32
+ /* On win32 : "c:\path\to\dir" succeed with stat() for example, while "c:\path\to\dir\" fails */
+ ET_Win32_Path_Remove_Trailing_Backslash(BrowserCurrentPath);
+#endif
+
+ if (strcmp(G_DIR_SEPARATOR_S,BrowserCurrentPath) == 0)
+ gtk_widget_set_sensitive(BrowserButton,FALSE);
+ else
+ gtk_widget_set_sensitive(BrowserButton,TRUE);
+}
+
+
+/*
+ * Return the current path
+ */
+gchar *Browser_Get_Current_Path (void)
+{
+ return BrowserCurrentPath;
+}
+
+/*
+ * Reload the current directory.
+ */
+void Browser_Reload_Directory (void)
+{
+ if (BrowserTree && BrowserCurrentPath != NULL)
+ {
+ // Unselect files, to automatically reload the file of the directory
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserTree));
+ if (selection)
+ {
+ gtk_tree_selection_unselect_all(selection);
+ }
+ Browser_Tree_Select_Dir(BrowserCurrentPath);
+ }
+}
+
+/*
+ * Set the current path (selected node) in browser as default path (within config variable).
+ */
+void Set_Current_Path_As_Default (void)
+{
+ if (DEFAULT_PATH_TO_MP3 != NULL)
+ g_free(DEFAULT_PATH_TO_MP3);
+ DEFAULT_PATH_TO_MP3 = g_strdup(BrowserCurrentPath);
+ Statusbar_Message(_("New default path for files selected"),TRUE);
+}
+
+/*
+ * When you press the key 'enter' in the BrowserEntry to validate the text (browse the directory)
+ */
+void Browser_Entry_Activated (void)
+{
+ const gchar *path_utf8;
+ gchar *path;
+
+ path_utf8 = gtk_entry_get_text(GTK_ENTRY(GTK_BIN(BrowserEntryCombo)->child));
+ Add_String_To_Combo_List(GTK_LIST_STORE(BrowserEntryModel), (gchar *)path_utf8);
+
+ path = filename_from_display(path_utf8);
+
+ Browser_Tree_Select_Dir(path);
+ g_free(path);
+}
+
+/*
+ * Set a text into the BrowserEntry (and don't activate it)
+ */
+void Browser_Entry_Set_Text (gchar *text)
+{
+ if (!text || !BrowserEntryCombo)
+ return;
+
+ gtk_entry_set_text(GTK_ENTRY(GTK_BIN(BrowserEntryCombo)->child),text);
+}
+
+/*
+ * Button to go to parent directory
+ */
+void Browser_Button_Clicked (void)
+{
+ gchar *parent_dir, *path;
+
+ parent_dir = Browser_Get_Current_Path();
+ if (strlen(parent_dir)>1)
+ {
+ if ( parent_dir[strlen(parent_dir)-1]==G_DIR_SEPARATOR )
+ parent_dir[strlen(parent_dir)-1] = '\0';
+ path = g_path_get_dirname(parent_dir);
+
+ Browser_Tree_Select_Dir(path);
+ g_free(path);
+ }
+}
+
+/*
+ * Set a text into the BrowserLabel
+ */
+void Browser_Label_Set_Text (gchar *text)
+{
+ if (BrowserLabel && text)
+ gtk_label_set_text(GTK_LABEL(BrowserLabel),text?text:"");
+}
+
+/*
+ * Key Press events into browser tree
+ */
+gboolean Browser_Tree_Key_Press (GtkWidget *tree, GdkEvent *event, gpointer data)
+{
+ GdkEventKey *kevent;
+ GtkTreeIter SelectedNode;
+ GtkTreeModel *treeModel;
+ GtkTreeSelection *treeSelection;
+ GtkTreePath *treePath;
+
+ if (!tree) return FALSE;
+
+ treeSelection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
+
+ if (event && event->type==GDK_KEY_PRESS)
+ {
+ if (!gtk_tree_selection_get_selected(treeSelection, &treeModel, &SelectedNode))
+ return FALSE;
+
+ kevent = (GdkEventKey *)event;
+ treePath = gtk_tree_model_get_path(GTK_TREE_MODEL(treeModel), &SelectedNode);
+
+ switch(kevent->keyval)
+ {
+ case GDK_KP_Enter: /* Enter key in Num Pad */
+ case GDK_Return: /* 'Normal' Enter key */
+ case GDK_t: /* Expand/Collapse node */
+ case GDK_T: /* Expand/Collapse node */
+ if(gtk_tree_view_row_expanded(GTK_TREE_VIEW(tree), treePath))
+ gtk_tree_view_collapse_row(GTK_TREE_VIEW(tree), treePath);
+ else
+ gtk_tree_view_expand_row(GTK_TREE_VIEW(tree), treePath, FALSE);
+
+ gtk_tree_path_free(treePath);
+ return TRUE;
+ break;
+
+ case GDK_e: /* Expand node */
+ case GDK_E: /* Expand node */
+ gtk_tree_view_expand_row(GTK_TREE_VIEW(tree), treePath, FALSE);
+ gtk_tree_path_free(treePath);
+ return TRUE;
+ break;
+
+ case GDK_c: /* Collapse node */
+ case GDK_C: /* Collapse node */
+ gtk_tree_view_collapse_row(GTK_TREE_VIEW(tree), treePath);
+ gtk_tree_path_free(treePath);
+ return TRUE;
+ break;
+ }
+ gtk_tree_path_free(treePath);
+ }
+ return FALSE;
+}
+
+/*
+ * Key Press events into browser list
+ */
+gboolean Browser_List_Stop_Timer (guint *flag)
+{
+ *flag = FALSE;
+ return FALSE;
+}
+
+/*
+ * Key press into browser list
+ * - Delete = delete file
+ * Also tries to capture text input and relate it to files
+ */
+gboolean Browser_List_Key_Press (GtkWidget *list, GdkEvent *event, gpointer data)
+{
+ gchar *string, *current_filename = NULL, *current_filename_copy = NULL, *temp;
+ static gchar *key_string = NULL;
+ gint key_string_length;
+ static guint BrowserListTimerId = 0;
+ static gboolean timer_is_running = FALSE;
+ gint row;
+ gboolean valid;
+ GdkEventKey *kevent;
+
+ GtkTreePath *currentPath = NULL;
+ GtkTreeIter currentIter;
+ ET_File *currentETFile;
+
+ GtkTreeModel *fileListModel;
+ GtkTreeSelection *fileSelection;
+
+
+ if (!list) return FALSE;
+
+ fileListModel = gtk_tree_view_get_model(GTK_TREE_VIEW(list));
+ fileSelection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
+
+ kevent = (GdkEventKey *)event;
+ if (event && event->type==GDK_KEY_PRESS)
+ {
+ if (gtk_tree_selection_count_selected_rows(fileSelection))
+ {
+ switch(kevent->keyval)
+ {
+ case GDK_Delete:
+ Action_Delete_Selected_Files();
+ return TRUE;
+ }
+ }
+ }
+
+ /*
+ * Tries to select file corresponding to the character sequence entered
+ */
+ if ( strlen(kevent->string)>0 ) // Alphanumeric key?
+ {
+ // If the timer is running: concatenate the character of the pressed key, else starts a new string
+ string = key_string;
+ if (timer_is_running)
+ key_string = g_strconcat(key_string,kevent->string,NULL);
+ else
+ key_string = g_strdup(kevent->string);
+ g_free(string);
+
+ // Remove the current timer
+ if (BrowserListTimerId)
+ {
+ g_source_remove(BrowserListTimerId);
+ BrowserListTimerId = 0;
+ }
+ // Start a new timer of 500ms
+ BrowserListTimerId = g_timeout_add(500,(GtkFunction)Browser_List_Stop_Timer,&timer_is_running);
+ timer_is_running = TRUE;
+
+ // Browse the list keeping the current classification
+ for (row=0; row < gtk_tree_model_iter_n_children(fileListModel, NULL); row++)
+ {
+ if (row == 0)
+ currentPath = gtk_tree_path_new_first();
+ else
+ gtk_tree_path_next(currentPath);
+
+ valid = gtk_tree_model_get_iter(GTK_TREE_MODEL(fileListModel), &currentIter, currentPath);
+ if (valid)
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(fileListModel), &currentIter,
+ LIST_FILE_POINTER, &currentETFile,
+ LIST_FILE_NAME, &current_filename,
+ -1);
+
+ /* UTF-8 comparison */
+ if (g_utf8_validate(key_string, -1, NULL) == FALSE)
+ {
+ temp = convert_to_utf8(key_string);
+ g_free(key_string);
+ key_string = temp;
+ }
+
+ key_string_length = g_utf8_strlen(key_string, -1);
+ current_filename_copy = g_malloc(strlen(current_filename) + 1);
+ g_utf8_strncpy(current_filename_copy, current_filename, key_string_length);
+
+ temp = g_utf8_casefold(current_filename_copy, -1);
+ g_free(current_filename_copy);
+ current_filename_copy = temp;
+
+ temp = g_utf8_casefold(key_string, -1);
+ g_free(key_string);
+ key_string = temp;
+
+ if (g_utf8_collate(current_filename_copy,key_string)==0 )
+ {
+ if (!gtk_tree_selection_iter_is_selected(fileSelection,&currentIter))
+ gtk_tree_selection_select_iter(fileSelection,&currentIter);
+
+ g_free(current_filename);
+ break;
+ }
+ g_free(current_filename);
+ }
+ }
+ g_free(current_filename_copy);
+ gtk_tree_path_free(currentPath);
+ }
+ return FALSE;
+}
+
+/*
+ * Action for double/triple click
+ */
+gboolean Browser_List_Button_Press (GtkTreeView *treeView, GdkEventButton *event)
+{
+ if (!event)
+ return FALSE;
+
+ if (event->type==GDK_2BUTTON_PRESS && event->button==1)
+ {
+ /* Double left mouse click */
+ // Select files of the same directory (usefull when browsing sub-directories)
+ GList *etfilelist = NULL;
+ gchar *path_ref = NULL;
+ gchar *patch_check = NULL;
+ GtkTreePath *currentPath = NULL;
+
+ if (!ETCore->ETFileDisplayed)
+ return FALSE;
+
+ // File taken as reference...
+ path_ref = g_path_get_dirname( ((File_Name *)ETCore->ETFileDisplayed->FileNameCur->data)->value );
+
+ // Search and select files of the same directory
+ etfilelist = g_list_first(ETCore->ETFileDisplayedList);
+ while (etfilelist)
+ {
+ // Path of the file to check if it is in the same directory
+ patch_check = g_path_get_dirname( ((File_Name *)((ET_File *)etfilelist->data)->FileNameCur->data)->value );
+
+ if ( path_ref && patch_check && strcmp(path_ref,patch_check)==0 )
+ {
+ // Use of 'currentPath' to try to increase speed. Indeed, in many
+ // cases, the next file to select, is the next in the list
+ currentPath = Browser_List_Select_File_By_Etfile2((ET_File *)etfilelist->data,TRUE,currentPath);
+ }
+ etfilelist = g_list_next(etfilelist);
+ g_free(patch_check);
+ }
+ g_free(path_ref);
+ if (currentPath)
+ gtk_tree_path_free(currentPath);
+ }else if (event->type==GDK_3BUTTON_PRESS && event->button==1)
+ {
+ /* Triple left mouse click */
+ // Select all files of the list
+ Action_Select_All_Files();
+ }
+ return FALSE;
+}
+
+/*
+ * Collapse (close) tree recursively up to the root node.
+ */
+void Browser_Tree_Collapse (void)
+{
+ GtkTreePath *rootPath;
+
+ if (!BrowserTree) return;
+
+ gtk_tree_view_collapse_all(GTK_TREE_VIEW(BrowserTree));
+
+#ifndef WIN32
+ /* But keep the main directory opened */
+ rootPath = gtk_tree_path_new_first();
+ gtk_tree_view_expand_to_path(GTK_TREE_VIEW(BrowserTree), rootPath);
+ gtk_tree_path_free(rootPath);
+#endif
+}
+
+
+/*
+ * Set a row (or node) visible in the TreeView (by scrolling the tree)
+ */
+void Browser_Tree_Set_Node_Visible (GtkWidget *directoryView, GtkTreePath * path)
+{
+ if (!directoryView) return;
+
+ gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(directoryView), path, NULL, TRUE, 0.5, 0.0);
+}
+
+
+/*
+ * Set a row visible in the file list (by scrolling the list)
+ */
+void Browser_List_Set_Row_Visible (GtkTreeModel *treeModel, GtkTreeIter *rowIter)
+{
+ /*
+ * TODO: Make this only scroll to the row if it is not visible
+ * (like in easytag GTK1)
+ * See function gtk_tree_view_get_visible_rect() ??
+ */
+ GtkTreePath *rowPath;
+
+ if (!treeModel) return;
+
+ rowPath = gtk_tree_model_get_path(treeModel, rowIter);
+ gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(BrowserList), rowPath, NULL, FALSE, 0, 0);
+ gtk_tree_path_free(rowPath);
+}
+
+/*
+ * Triggers when a new node in the browser tree is selected
+ * Do file-save confirmation, and then prompt the new dir to be loaded
+ */
+gboolean Browser_Tree_Node_Selected (GtkTreeSelection *selection, gpointer user_data)
+{
+ gchar *pathName, *pathName_utf8;
+ static int counter = 0;
+ GtkTreeIter selectedIter;
+ GtkTreePath *selectedPath;
+
+ if (!gtk_tree_selection_get_selected(selection, NULL, &selectedIter))
+ return TRUE;
+ selectedPath = gtk_tree_model_get_path(GTK_TREE_MODEL(directoryTreeModel), &selectedIter);
+
+ /* Open the node */
+ if (OPEN_SELECTED_BROWSER_NODE)
+ {
+ gtk_tree_view_expand_row(GTK_TREE_VIEW(BrowserTree), selectedPath, FALSE);
+ }
+
+ /* Don't start a new reading, if another one is running... */
+ if (ReadingDirectory == TRUE)
+ return TRUE;
+
+ //Browser_Tree_Set_Node_Visible(BrowserTree, selectedPath);
+ gtk_tree_path_free(selectedPath);
+ gtk_tree_model_get(GTK_TREE_MODEL(directoryTreeModel), &selectedIter,
+ TREE_COLUMN_FULL_PATH, &pathName, -1);
+ if (!pathName)
+ return FALSE;
+
+ /* Save the current displayed data */
+ ET_Save_File_Data_From_UI(ETCore->ETFileDisplayed);
+ Update_Command_Buttons_Sensivity(); // Not clean to put this here...
+
+ /* Check if all files have been saved before changing the directory */
+ if (ET_Check_If_All_Files_Are_Saved() != TRUE)
+ {
+ GtkWidget *msgbox = NULL;
+ gint button;
+
+ msgbox = msg_box_new(_("Confirm..."),_("Some files have been modified but not "
+ "saved...\nDo you want to save them before changing the directory?"),
+ GTK_STOCK_DIALOG_QUESTION,BUTTON_CANCEL,BUTTON_NO,BUTTON_YES,0);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ button = msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+ switch (button)
+ {
+ case BUTTON_YES:
+ if (Save_All_Files_With_Answer(FALSE)==-1)
+ return TRUE;
+ break;
+ case BUTTON_NO:
+ break;
+ case BUTTON_CANCEL:
+ case -1:
+ return TRUE;
+ }
+ }
+
+ /* Memorize the current path */
+ Browser_Update_Current_Path(pathName);
+
+ /* Display the selected path into the BrowserEntry */
+ pathName_utf8 = filename_to_display(pathName);
+ gtk_entry_set_text(GTK_ENTRY(GTK_BIN(BrowserEntryCombo)->child), pathName_utf8);
+
+ /* Start to read the directory */
+ /* The first time 'counter' is equal to zero and if we don't want to load
+ * directory on startup, we skip the 'reading', but we must read it */
+ if (LOAD_ON_STARTUP || counter)
+ Read_Directory(pathName);
+ else
+ /* As we don't use the function 'Read_Directory' we must add this function here */
+ Update_Command_Buttons_Sensivity();
+ counter++;
+
+ g_free(pathName);
+ g_free(pathName_utf8);
+ return FALSE;
+}
+
+
+gint Browser_Win32_Get_Drive_Root (gchar *drive, GtkTreeIter *rootNode, GtkTreePath **rootPath)
+{
+ gint root_index;
+ gboolean found = FALSE;
+ GtkTreeIter parentNode;
+ gchar *nodeName;
+
+ gtk_tree_model_get_iter_first(GTK_TREE_MODEL(directoryTreeModel), &parentNode);
+
+ // Find root of path, ie: the drive letter
+ root_index = 0;
+
+ do
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(directoryTreeModel), &parentNode,
+ TREE_COLUMN_FULL_PATH, &nodeName, -1);
+ if (strncasecmp(drive,nodeName, strlen(drive)) == 0)
+ {
+ g_free(nodeName);
+ found = TRUE;
+ break;
+ }
+ root_index++;
+ } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(directoryTreeModel), &parentNode));
+
+ if (!found) return FALSE;
+
+ *rootNode = parentNode;
+ *rootPath = gtk_tree_path_new_from_indices(root_index, -1);
+
+ return TRUE;
+}
+
+
+/*
+ * Browser_Tree_Select_Dir: Select the directory corresponding to the 'path' in
+ * the tree browser, but it doesn't read it!
+ * Check if path is correct before selecting it. And returns 1 on success, else 0.
+ */
+gint Browser_Tree_Select_Dir (gchar *current_path)
+{
+ GtkTreePath *rootPath = NULL;
+ GtkTreeIter parentNode, currentNode;
+ struct stat stbuf;
+ gint index = 1; // Skip the first token as it is NULL due to leading /
+ gchar **parts;
+ gchar *nodeName;
+ gchar *temp;
+
+ if (!BrowserTree) return FALSE;
+
+ /* Load current_path */
+ if(!current_path || !*current_path)
+ {
+ return TRUE;
+ }
+
+#ifdef WIN32
+ /* On win32 : stat("c:\path\to\dir") succeed, while stat("c:\path\to\dir\") fails */
+ ET_Win32_Path_Remove_Trailing_Backslash(current_path);
+#endif
+
+ /* If path is invalid: inform the user, but load the first directories
+ * of the full path while parent directories are valid */
+ if (stat(current_path,&stbuf)==-1)
+ {
+ GtkWidget *msgbox;
+ gchar *msg;
+ gchar *current_path_utf8;
+
+ current_path_utf8 = filename_to_display(current_path);
+ msg = g_strdup_printf(_("The entered path is invalid!:\n%s\n(%s)"),
+ current_path_utf8,g_strerror(errno));
+ msgbox = msg_box_new(_("Error..."),msg,GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0);
+ g_free(msg);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+ g_free(current_path_utf8);
+ return FALSE;
+ }
+
+ Browser_Update_Current_Path(current_path);
+
+ parts = g_strsplit((const gchar*)current_path, G_DIR_SEPARATOR_S, 0);
+
+ // Expand root node (fill parentNode and rootPath)
+#ifdef WIN32
+ if (!Browser_Win32_Get_Drive_Root(parts[0], &parentNode, &rootPath))
+ return FALSE;
+#else
+ gtk_tree_model_get_iter_first(GTK_TREE_MODEL(directoryTreeModel), &parentNode);
+ rootPath = gtk_tree_path_new_first();
+#endif
+ if (rootPath)
+ {
+ gtk_tree_view_expand_to_path(GTK_TREE_VIEW(BrowserTree), rootPath);
+ gtk_tree_path_free(rootPath);
+ }
+
+ while (parts[index]) // it is NULL-terminated
+ {
+ if (strlen(parts[index]) == 0)
+ {
+ index++;
+ continue;
+ }
+
+ if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(directoryTreeModel), &currentNode, &parentNode))
+ {
+ break;
+ }
+ do
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(directoryTreeModel), &currentNode,
+ TREE_COLUMN_FULL_PATH, &temp, -1);
+ nodeName = g_path_get_basename(temp);
+ g_free(temp);
+#ifdef WIN32
+ if (strcasecmp(parts[index],nodeName) == 0)
+#else
+ if (strcmp(parts[index],nodeName) == 0)
+#endif
+ {
+ g_free(nodeName);
+ break;
+ }
+ g_free(nodeName);
+ } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(directoryTreeModel), &currentNode));
+
+ parentNode = currentNode;
+ rootPath = gtk_tree_model_get_path(GTK_TREE_MODEL(directoryTreeModel), &parentNode);
+ if (rootPath)
+ {
+ gtk_tree_view_expand_to_path(GTK_TREE_VIEW(BrowserTree), rootPath);
+ gtk_tree_path_free(rootPath);
+ }
+ index++;
+ }
+
+ rootPath = gtk_tree_model_get_path(GTK_TREE_MODEL(directoryTreeModel), &parentNode);
+ if (rootPath)
+ {
+ gtk_tree_view_expand_to_path(GTK_TREE_VIEW(BrowserTree), rootPath);
+ gtk_tree_selection_select_path(gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserTree)), rootPath);
+ Browser_Tree_Set_Node_Visible(BrowserTree, rootPath);
+ gtk_tree_path_free(rootPath);
+ }
+
+ g_strfreev(parts);
+ return TRUE;
+}
+
+/*
+ * Callback to select-row event
+ * Displays the file info of the lowest selected file in the right-hand pane
+ */
+void Browser_List_Row_Selected (GtkTreeSelection *selection, gpointer data)
+{
+ GList *selectedRows;
+ GtkTreePath *lastSelected;
+ GtkTreeIter lastFile;
+ ET_File *fileETFile;
+
+ selectedRows = gtk_tree_selection_get_selected_rows(selection, NULL);
+
+ /*
+ * After a file is deleted, this function is called :
+ * So we must handle the situation if no rows are selected
+ */
+ if (g_list_length(selectedRows) == 0)
+ {
+ g_list_foreach(selectedRows, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free(selectedRows);
+ return;
+ }
+
+ if (!LastBrowserListETFileSelected)
+ {
+ // Returns the last line selected (in ascending line order) to display the item
+ lastSelected = (GtkTreePath *)g_list_last(selectedRows)->data;
+ if (gtk_tree_model_get_iter(GTK_TREE_MODEL(fileListModel), &lastFile, lastSelected))
+ gtk_tree_model_get(GTK_TREE_MODEL(fileListModel), &lastFile, LIST_FILE_POINTER, &fileETFile, -1);
+
+ Action_Select_Nth_File_By_Etfile(fileETFile);
+ }else
+ {
+ // The real last selected line
+ Action_Select_Nth_File_By_Etfile(LastBrowserListETFileSelected);
+ }
+
+ g_list_foreach(selectedRows, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free(selectedRows);
+}
+
+/*
+ * Loads the specified etfilelist into the browser list
+ * Also supports optionally selecting a specific etfile
+ * but be careful, this does not call Browser_List_Row_Selected !
+ */
+void Browser_List_Load_File_List (GList *etfilelist, ET_File *etfile_to_select)
+{
+ gboolean activate_bg_color = 0;
+ GtkTreeIter row;
+
+ if (!BrowserList) return;
+
+ gtk_list_store_clear(fileListModel);
+ etfilelist = g_list_first(etfilelist);
+ while (etfilelist)
+ {
+ guint fileKey = ((ET_File *)etfilelist->data)->ETFileKey;
+ gchar *current_filename_utf8 = ((File_Name *)((ET_File *)etfilelist->data)->FileNameCur->data)->value_utf8;
+ gchar *basename_utf8 = g_path_get_basename(current_filename_utf8);
+
+ // Change background color when changing directory (the first row must not be changed)
+ if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(fileListModel), NULL) > 0)
+ {
+ gchar *dir1_utf8;
+ gchar *dir2_utf8;
+ gchar *previous_filename_utf8 = ((File_Name *)((ET_File *)etfilelist->prev->data)->FileNameCur->data)->value_utf8;
+
+ dir1_utf8 = g_path_get_dirname(previous_filename_utf8);
+ dir2_utf8 = g_path_get_dirname(current_filename_utf8);
+
+ if (g_utf8_collate(dir1_utf8, dir2_utf8) != 0)
+ activate_bg_color = !activate_bg_color;
+
+ g_free(dir1_utf8);
+ g_free(dir2_utf8);
+ }
+
+ // File list displays the current filename (name on HD)
+ gtk_list_store_append(fileListModel, &row);
+ gtk_list_store_set(fileListModel, &row,
+ LIST_FILE_NAME, basename_utf8,
+ LIST_FILE_POINTER, etfilelist->data,
+ LIST_FILE_KEY, fileKey,
+ LIST_FILE_OTHERDIR, activate_bg_color,
+ -1);
+ g_free(basename_utf8);
+
+ if (etfile_to_select == etfilelist->data)
+ {
+ Browser_List_Select_File_By_Iter(&row, TRUE);
+ //ET_Display_File_Data_To_UI(etfilelist->data);
+ }
+
+ // Set appearance of the row
+ Browser_List_Set_Row_Appearance(&row);
+
+ etfilelist = g_list_next(etfilelist);
+ }
+}
+
+
+/*
+ * Update state of files in the list after changes (without clearing the list model!)
+ * - Refresh filename is file saved,
+ * - Change color is something change on the file
+ */
+void Browser_List_Refresh_Whole_List (void)
+{
+ ET_File *ETFile;
+ //GtkTreeIter iter;
+ GtkTreePath *currentPath = NULL;
+ GtkTreeIter iter;
+ gint row;
+ gchar *current_basename_utf8;
+ gboolean valid;
+ GtkWidget *TBViewMode;
+
+ if (!ETCore->ETFileDisplayedList || !BrowserList ||
+ gtk_tree_model_iter_n_children(GTK_TREE_MODEL(fileListModel), NULL) == 0)
+ {
+ return;
+ }
+
+ TBViewMode = gtk_ui_manager_get_widget(UIManager, "/ToolBar/ViewModeToggle");
+
+ // Browse the full list for changes
+ //gtk_tree_model_get_iter_first(GTK_TREE_MODEL(fileListModel), &iter);
+ // g_print("above worked %d rows\n", gtk_tree_model_iter_n_children(GTK_TREE_MODEL(fileListModel), NULL));
+
+ currentPath = gtk_tree_path_new_first();
+
+ valid = gtk_tree_model_get_iter(GTK_TREE_MODEL(fileListModel), &iter, currentPath);
+ while (valid)
+ {
+ // Refresh filename
+ gtk_tree_model_get(GTK_TREE_MODEL(fileListModel), &iter,
+ LIST_FILE_POINTER, &ETFile, -1);
+ current_basename_utf8 = g_path_get_basename( ((File_Name *)ETFile->FileNameCur->data)->value_utf8 );
+ gtk_list_store_set(fileListModel, &iter, LIST_FILE_NAME, current_basename_utf8, -1);
+ g_free(current_basename_utf8);
+
+ Browser_List_Set_Row_Appearance(&iter);
+
+ valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(fileListModel), &iter);
+ }
+ gtk_tree_path_free(currentPath);
+
+ // When displaying Artist + Album lists => refresh also rows color
+ if ( gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(TBViewMode)) )
+ {
+
+ for (row=0; row < gtk_tree_model_iter_n_children(GTK_TREE_MODEL(artistListModel), NULL); row++)
+ {
+ if (row == 0)
+ currentPath = gtk_tree_path_new_first();
+ else
+ gtk_tree_path_next(currentPath);
+
+ gtk_tree_model_get_iter(GTK_TREE_MODEL(artistListModel), &iter, currentPath);
+ Browser_Artist_List_Set_Row_Appearance(&iter);
+ }
+ gtk_tree_path_free(currentPath);
+
+
+ for (row=0; row < gtk_tree_model_iter_n_children(GTK_TREE_MODEL(albumListModel), NULL); row++)
+ {
+ if (row == 0)
+ currentPath = gtk_tree_path_new_first();
+ else
+ gtk_tree_path_next(currentPath);
+
+ gtk_tree_model_get_iter(GTK_TREE_MODEL(albumListModel), &iter, currentPath);
+ Browser_Album_List_Set_Row_Appearance(&iter);
+ }
+ gtk_tree_path_free(currentPath);
+ }
+}
+
+
+/*
+ * Update state of one file in the list after changes (without clearing the clist!)
+ * - Refresh filename is file saved,
+ * - Change color is something change on the file
+ */
+void Browser_List_Refresh_File_In_List (ET_File *ETFile)
+{
+ GList *selectedRow = NULL;
+ GtkWidget *TBViewMode;
+ GtkTreeSelection *selection;
+ GtkTreeIter selectedIter;
+ GtkTreePath *currentPath = NULL;
+ ET_File *file;
+ gboolean row_found = FALSE;
+ gchar *current_basename_utf8;
+ gboolean valid;
+ gint row;
+ gchar *artist, *album;
+
+ if (!ETCore->ETFileDisplayedList || !BrowserList || !ETFile ||
+ gtk_tree_model_iter_n_children(GTK_TREE_MODEL(fileListModel), NULL) == 0)
+ {
+ return;
+ }
+
+ TBViewMode = gtk_ui_manager_get_widget(UIManager, "/ToolBar/ViewModeToggle");
+
+ // Search the row of the modified file to update it (when found: row_found=TRUE)
+ // 1/3. Get position of ETFile in ETFileList
+ if (row_found == FALSE)
+ {
+ valid = gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(fileListModel), &selectedIter, NULL, ETFile->IndexKey-1);
+ if (valid)
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(fileListModel), &selectedIter,
+ LIST_FILE_POINTER, &file, -1);
+ if (ETFile->ETFileKey == file->ETFileKey)
+ {
+ row_found = TRUE;
+ }
+ }
+ }
+
+ // 2/3. Try with the selected file in list (works only if we select the same file)
+ if (row_found == FALSE)
+ {
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList));
+ selectedRow = gtk_tree_selection_get_selected_rows(selection, NULL);
+ if (selectedRow && selectedRow->data != NULL)
+ {
+ valid = gtk_tree_model_get_iter(GTK_TREE_MODEL(fileListModel), &selectedIter,
+ (GtkTreePath*) selectedRow->data);
+ if (valid)
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(fileListModel), &selectedIter,
+ LIST_FILE_POINTER, &file, -1);
+ if (ETFile->ETFileKey == file->ETFileKey)
+ {
+ row_found = TRUE;
+ }
+ }
+ }
+ g_list_foreach(selectedRow, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free(selectedRow);
+ }
+
+ // 3/3. Fails, now we browse the full list to find it
+ if (row_found == FALSE)
+ {
+ valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(fileListModel), &selectedIter);
+ while (valid && !row_found)
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(fileListModel), &selectedIter,
+ LIST_FILE_POINTER, &file, -1);
+ if (ETFile->ETFileKey == file->ETFileKey)
+ {
+ row_found = TRUE;
+ } else
+ {
+ valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(fileListModel), &selectedIter);
+ }
+ }
+ }
+
+ // Error somewhere...
+ if (row_found == FALSE)
+ return;
+
+ // Displayed the filename
+ current_basename_utf8 = g_path_get_basename( ((File_Name *)file->FileNameCur->data)->value_utf8 );
+ gtk_list_store_set(fileListModel, &selectedIter, LIST_FILE_NAME, current_basename_utf8, -1);
+ g_free(current_basename_utf8);
+
+ // Change appearance (line to red) if filename changed
+ Browser_List_Set_Row_Appearance(&selectedIter);
+
+ // When displaying Artist + Album lists => refresh also rows color
+ if ( gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(TBViewMode)) )
+ {
+ gchar *current_artist = ((File_Tag *)ETFile->FileTag->data)->artist;
+ gchar *current_album = ((File_Tag *)ETFile->FileTag->data)->album;
+
+ for (row=0; row < gtk_tree_model_iter_n_children(GTK_TREE_MODEL(artistListModel), NULL); row++)
+ {
+ if (row == 0)
+ currentPath = gtk_tree_path_new_first();
+ else
+ gtk_tree_path_next(currentPath);
+
+ valid = gtk_tree_model_get_iter(GTK_TREE_MODEL(artistListModel), &selectedIter, currentPath);
+ if (valid)
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(artistListModel), &selectedIter, ARTIST_NAME, &artist, -1);
+
+ if ( (!current_artist && !artist)
+ || (current_artist && artist && g_utf8_collate(current_artist,artist)==0) )
+ {
+ // Set color of the row
+ Browser_Artist_List_Set_Row_Appearance(&selectedIter);
+ g_free(artist);
+ break;
+ }
+ g_free(artist);
+ }
+ }
+ gtk_tree_path_free(currentPath); currentPath = NULL;
+
+ //
+ // FIX ME : see also if we must add a new line / or change list of the ETFile
+ //
+ for (row=0; row < gtk_tree_model_iter_n_children(GTK_TREE_MODEL(albumListModel), NULL); row++)
+ {
+ if (row == 0)
+ currentPath = gtk_tree_path_new_first();
+ else
+ gtk_tree_path_next(currentPath);
+
+ valid = gtk_tree_model_get_iter(GTK_TREE_MODEL(albumListModel), &selectedIter, currentPath);
+ if (valid)
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(albumListModel), &selectedIter, ALBUM_NAME, &album, -1);
+
+ if ( (!current_album && !album)
+ || (current_album && album && g_utf8_collate(current_album,album)==0) )
+ {
+ // Set color of the row
+ Browser_Album_List_Set_Row_Appearance(&selectedIter);
+ g_free(album);
+ break;
+ }
+ g_free(album);
+ }
+ }
+ gtk_tree_path_free(currentPath); currentPath = NULL;
+
+ //
+ // FIX ME : see also if we must add a new line / or change list of the ETFile
+ //
+ }
+}
+
+
+/*
+ * Set the appearance of the row
+ */
+void Browser_List_Set_Row_Appearance (GtkTreeIter *iter)
+{
+ ET_File *rowETFile;
+ gboolean otherdir = FALSE;
+ GdkColor *backgroundcolor;
+ gchar *temp;
+
+ if (iter == NULL)
+ return;
+
+ // Get the ETFile reference
+ gtk_tree_model_get(GTK_TREE_MODEL(fileListModel), iter,
+ LIST_FILE_POINTER, &rowETFile,
+ LIST_FILE_OTHERDIR, &otherdir,
+ LIST_FILE_NAME, &temp, -1);
+
+ if (otherdir)
+ backgroundcolor = &LIGHT_BLUE;
+ else
+ backgroundcolor = NULL;
+
+ // Set text to bold/red if filename or tag changed
+ if ( ET_Check_If_File_Is_Saved(rowETFile) == FALSE )
+ {
+ if (CHANGED_FILES_DISPLAYED_TO_BOLD)
+ {
+ gtk_list_store_set(fileListModel, iter, LIST_FONT_WEIGHT, PANGO_WEIGHT_BOLD, LIST_ROW_BACKGROUND, backgroundcolor, LIST_ROW_FOREGROUND, NULL, -1);
+ } else
+ {
+ gtk_list_store_set(fileListModel, iter, LIST_FONT_WEIGHT, PANGO_WEIGHT_NORMAL, LIST_ROW_BACKGROUND, backgroundcolor, LIST_ROW_FOREGROUND, &RED, -1);
+ }
+ } else
+ {
+ gtk_list_store_set(fileListModel, iter, LIST_FONT_WEIGHT, PANGO_WEIGHT_NORMAL, LIST_ROW_BACKGROUND, backgroundcolor, LIST_ROW_FOREGROUND, NULL ,-1);
+ }
+ // Frees allocated item from gtk_tree_model_get...
+ g_free(temp);
+}
+
+
+/*
+ * Remove a file from the list, by ETFile
+ */
+void Browser_List_Remove_File (ET_File *searchETFile)
+{
+ gint row;
+ GtkTreePath *currentPath = NULL;
+ GtkTreeIter currentIter;
+ ET_File *currentETFile;
+ gboolean valid;
+
+ if (searchETFile == NULL)
+ return;
+
+ // Go through the file list until it is found
+ for (row=0; row < gtk_tree_model_iter_n_children(GTK_TREE_MODEL(fileListModel), NULL); row++)
+ {
+ if (row == 0)
+ currentPath = gtk_tree_path_new_first();
+ else
+ gtk_tree_path_next(currentPath);
+
+ valid = gtk_tree_model_get_iter(GTK_TREE_MODEL(fileListModel), &currentIter, currentPath);
+ if (valid)
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(fileListModel), &currentIter, LIST_FILE_POINTER, &currentETFile, -1);
+
+ if (currentETFile == searchETFile)
+ {
+ // Reinit this value to avoid a crash after deleting files...
+ if (LastBrowserListETFileSelected == searchETFile)
+ LastBrowserListETFileSelected = NULL;
+
+ gtk_list_store_remove(fileListModel, &currentIter);
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * Get ETFile pointer of a file from a Tree Iter
+ */
+ET_File *Browser_List_Get_ETFile_From_Path (GtkTreePath *path)
+{
+ GtkTreeIter iter;
+
+ if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(fileListModel), &iter, path))
+ return NULL;
+
+ return Browser_List_Get_ETFile_From_Iter(&iter);
+}
+
+/*
+ * Get ETFile pointer of a file from a Tree Iter
+ */
+ET_File *Browser_List_Get_ETFile_From_Iter (GtkTreeIter *iter)
+{
+ ET_File *etfile;
+
+ gtk_tree_model_get(GTK_TREE_MODEL(fileListModel), iter, LIST_FILE_POINTER, &etfile, -1);
+ return etfile;
+}
+
+
+/*
+ * Select the specified file in the list, by its ETFile
+ */
+void Browser_List_Select_File_By_Etfile (ET_File *searchETFile, gboolean select_it)
+{
+ GtkTreePath *currentPath = NULL;
+
+ currentPath = Browser_List_Select_File_By_Etfile2(searchETFile, select_it, NULL);
+ if (currentPath)
+ gtk_tree_path_free(currentPath);
+}
+/*
+ * Select the specified file in the list, by its ETFile
+ * - startPath : if set : starting path to try increase speed
+ * - returns allocated "currentPath" to free
+ */
+GtkTreePath *Browser_List_Select_File_By_Etfile2 (ET_File *searchETFile, gboolean select_it, GtkTreePath *startPath)
+{
+ gint row;
+ GtkTreePath *currentPath = NULL;
+ GtkTreeIter currentIter;
+ ET_File *currentETFile;
+ gboolean valid;
+
+ if (searchETFile == NULL)
+ return NULL;
+
+ // If the path is used, we try the next item (to increase speed), as it is correct in many cases...
+ if (startPath)
+ {
+ // Try the next path
+ gtk_tree_path_next(startPath);
+ valid = gtk_tree_model_get_iter(GTK_TREE_MODEL(fileListModel), &currentIter, startPath);
+ if (valid)
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(fileListModel), &currentIter, LIST_FILE_POINTER, &currentETFile, -1);
+ // It is the good file?
+ if (currentETFile == searchETFile)
+ {
+ Browser_List_Select_File_By_Iter(&currentIter, select_it);
+ return startPath;
+ }
+ }
+ }
+
+ // Else, we try the whole list...
+ // Go through the file list until it is found
+ currentPath = gtk_tree_path_new_first();
+ for (row=0; row < gtk_tree_model_iter_n_children(GTK_TREE_MODEL(fileListModel), NULL); row++)
+ {
+ valid = gtk_tree_model_get_iter(GTK_TREE_MODEL(fileListModel), &currentIter, currentPath);
+ if (valid)
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(fileListModel), &currentIter, LIST_FILE_POINTER, &currentETFile, -1);
+
+ if (currentETFile == searchETFile)
+ {
+ Browser_List_Select_File_By_Iter(&currentIter, select_it);
+ return currentPath;
+ //break;
+ }
+ }
+ gtk_tree_path_next(currentPath);
+ }
+ gtk_tree_path_free(currentPath);
+
+ return NULL;
+}
+
+
+/*
+ * Select the specified file in the list, by an iter
+ */
+void Browser_List_Select_File_By_Iter (GtkTreeIter *rowIter, gboolean select_it)
+{
+ if (!BrowserList) return;
+
+ if (select_it)
+ {
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList));
+ if (selection)
+ {
+ g_signal_handlers_block_by_func(G_OBJECT(selection),G_CALLBACK(Browser_List_Row_Selected),NULL);
+ gtk_tree_selection_select_iter(selection, rowIter);
+ g_signal_handlers_unblock_by_func(G_OBJECT(selection),G_CALLBACK(Browser_List_Row_Selected),NULL);
+ }
+ }
+ Browser_List_Set_Row_Visible(GTK_TREE_MODEL(fileListModel), rowIter);
+}
+
+/*
+ * Select the specified file in the list, by a string representation of an iter
+ * e.g. output of gtk_tree_model_get_string_from_iter()
+ */
+void Browser_List_Select_File_By_Iter_String (const gchar* stringIter, gboolean select_it)
+{
+ GtkTreeIter iter;
+
+ if (!fileListModel || !BrowserList) return;
+
+ if (gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(fileListModel), &iter, stringIter))
+ {
+ if (select_it)
+ {
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList));
+
+ // FIX ME : Why signal was blocked if selected? Don't remember...
+ if (selection)
+ {
+ //g_signal_handlers_block_by_func(G_OBJECT(selection),G_CALLBACK(Browser_List_Row_Selected),NULL);
+ gtk_tree_selection_select_iter(selection, &iter);
+ //g_signal_handlers_unblock_by_func(G_OBJECT(selection),G_CALLBACK(Browser_List_Row_Selected),NULL);
+ }
+ }
+ Browser_List_Set_Row_Visible(GTK_TREE_MODEL(fileListModel), &iter);
+ }
+}
+
+/*
+ * Select the specified file in the list, by fuzzy string matching based on
+ * the Damerau-Levenshtein Metric (patch from Santtu Lakkala - 23/08/2004)
+ */
+ET_File *Browser_List_Select_File_By_DLM (const gchar* string, gboolean select_it)
+{
+ GtkTreeIter iter;
+ GtkTreeIter iter2;
+ GtkTreeSelection *selection;
+ ET_File *current_etfile = NULL, *retval = NULL;
+ gchar *current_filename = NULL, *current_title = NULL;
+ int max = 0, this;
+
+ if (!fileListModel || !BrowserList) return NULL;
+
+ if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(fileListModel), &iter))
+ {
+ do
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(fileListModel), &iter,
+ LIST_FILE_NAME, &current_filename,
+ LIST_FILE_POINTER, &current_etfile, -1);
+ current_title = ((File_Tag *)current_etfile->FileTag->data)->title;
+
+ if ((this = dlm((current_title ? current_title : current_filename), string)) > max) // See "dlm.c"
+ {
+ max = this;
+ iter2 = iter;
+ retval = current_etfile;
+ }
+ } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(fileListModel), &iter));
+
+ if (select_it)
+ {
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList));
+ if (selection)
+ {
+ g_signal_handlers_block_by_func(G_OBJECT(selection),G_CALLBACK(Browser_List_Row_Selected),NULL);
+ gtk_tree_selection_select_iter(selection, &iter2);
+ g_signal_handlers_unblock_by_func(G_OBJECT(selection),G_CALLBACK(Browser_List_Row_Selected),NULL);
+ }
+ }
+ Browser_List_Set_Row_Visible(GTK_TREE_MODEL(fileListModel), &iter2);
+ }
+ return retval;
+}
+
+
+/*
+ * Unselect the specified file in the list, by its ETFile
+ */
+void Browser_List_Unselect_File_By_Etfile(ET_File *searchETFile)
+{
+ gint row;
+ GtkTreePath *currentPath = NULL;
+ GtkTreeIter currentIter;
+ ET_File *currentETFile;
+ gboolean valid;
+
+ if (searchETFile == NULL)
+ return;
+
+ // Go through the file list until it is found
+ for (row=0; row < gtk_tree_model_iter_n_children(GTK_TREE_MODEL(fileListModel), NULL); row++)
+ {
+ if (row == 0)
+ currentPath = gtk_tree_path_new_first();
+ else
+ gtk_tree_path_next(currentPath);
+
+ valid = gtk_tree_model_get_iter(GTK_TREE_MODEL(fileListModel), &currentIter, currentPath);
+ if (valid)
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(fileListModel), &currentIter, LIST_FILE_POINTER, &currentETFile, -1);
+
+ if (currentETFile == searchETFile)
+ {
+ Browser_List_Unselect_File_By_Iter(&currentIter);
+ break;
+ }
+ }
+ }
+}
+
+
+/*
+ * Unselect the specified file, by its iter.
+ */
+void Browser_List_Unselect_File_By_Iter (GtkTreeIter *rowIter)
+{
+ GtkTreeSelection *selection;
+
+ if (!BrowserList) return;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList));
+ if (selection)
+ {
+ g_signal_handlers_block_by_func(G_OBJECT(selection),G_CALLBACK(Browser_List_Row_Selected),NULL);
+ gtk_tree_selection_unselect_iter(selection, rowIter);
+ g_signal_handlers_unblock_by_func(G_OBJECT(selection),G_CALLBACK(Browser_List_Row_Selected),NULL);
+ }
+}
+
+
+/*
+ * Unselect the specified file in the list, by a string representation of an iter
+ * e.g. output of gtk_tree_model_get_string_from_iter()
+ */
+void Browser_List_Unselect_File_By_Iter_String(const gchar* stringIter)
+{
+ GtkTreeIter iter;
+
+ if (gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(fileListModel), &iter, stringIter))
+ Browser_List_Unselect_File_By_Iter(&iter);
+}
+
+/*
+ * Clear all entries on the file list
+ */
+void Browser_List_Clear()
+{
+ gtk_list_store_clear(fileListModel);
+ gtk_list_store_clear(artistListModel);
+ gtk_list_store_clear(albumListModel);
+
+}
+
+/*
+ * Refresh the list sorting (call me after SORTING_FILE_MODE has changed)
+ */
+void Browser_List_Refresh_Sort (void)
+{
+ gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(fileListModel), 0, Browser_List_Sort_Func, NULL, NULL);
+}
+
+/*
+ * Intelligently sort the file list based on the current sorting method
+ */
+gint Browser_List_Sort_Func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data)
+{
+ ET_File *ETFile1;
+ ET_File *ETFile2;
+ //gchar *text1;
+ //gchar *text2;
+ gint result = 0;
+
+ gtk_tree_model_get(model, a, LIST_FILE_POINTER, &ETFile1, -1);
+ gtk_tree_model_get(model, b, LIST_FILE_POINTER, &ETFile2, -1);
+ //gtk_tree_model_get(model, a, LIST_FILE_POINTER, &ETFile1, LIST_FILE_NAME, &text1, -1);
+ //gtk_tree_model_get(model, b, LIST_FILE_POINTER, &ETFile2, LIST_FILE_NAME, &text2, -1);
+
+ switch (SORTING_FILE_MODE)
+ {
+ case SORTING_UNKNOWN:
+ case SORTING_BY_ASCENDING_FILENAME:
+ result = ET_Comp_Func_Sort_File_By_Ascending_Filename(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_FILENAME:
+ result = ET_Comp_Func_Sort_File_By_Descending_Filename(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_ASCENDING_TRACK_NUMBER:
+ result = ET_Comp_Func_Sort_File_By_Ascending_Track_Number(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_TRACK_NUMBER:
+ result = ET_Comp_Func_Sort_File_By_Descending_Track_Number(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_ASCENDING_CREATION_DATE:
+ result = ET_Comp_Func_Sort_File_By_Ascending_Creation_Date(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_CREATION_DATE:
+ result = ET_Comp_Func_Sort_File_By_Descending_Creation_Date(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_ASCENDING_TITLE:
+ result = ET_Comp_Func_Sort_File_By_Ascending_Title(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_TITLE:
+ result = ET_Comp_Func_Sort_File_By_Descending_Title(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_ASCENDING_ARTIST:
+ result = ET_Comp_Func_Sort_File_By_Ascending_Artist(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_ARTIST:
+ result = ET_Comp_Func_Sort_File_By_Descending_Artist(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_ASCENDING_ALBUM:
+ result = ET_Comp_Func_Sort_File_By_Ascending_Album(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_ALBUM:
+ result = ET_Comp_Func_Sort_File_By_Descending_Album(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_ASCENDING_YEAR:
+ result = ET_Comp_Func_Sort_File_By_Ascending_Year(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_YEAR:
+ result = ET_Comp_Func_Sort_File_By_Descending_Year(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_ASCENDING_GENRE:
+ result = ET_Comp_Func_Sort_File_By_Ascending_Genre(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_GENRE:
+ result = ET_Comp_Func_Sort_File_By_Descending_Genre(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_ASCENDING_COMMENT:
+ result = ET_Comp_Func_Sort_File_By_Ascending_Comment(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_COMMENT:
+ result = ET_Comp_Func_Sort_File_By_Descending_Comment(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_ASCENDING_COMPOSER:
+ result = ET_Comp_Func_Sort_File_By_Ascending_Composer(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_COMPOSER:
+ result = ET_Comp_Func_Sort_File_By_Descending_Composer(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_ASCENDING_ORIG_ARTIST:
+ result = ET_Comp_Func_Sort_File_By_Ascending_Orig_Artist(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_ORIG_ARTIST:
+ result = ET_Comp_Func_Sort_File_By_Descending_Orig_Artist(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_ASCENDING_COPYRIGHT:
+ result = ET_Comp_Func_Sort_File_By_Ascending_Copyright(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_COPYRIGHT:
+ result = ET_Comp_Func_Sort_File_By_Descending_Copyright(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_ASCENDING_URL:
+ result = ET_Comp_Func_Sort_File_By_Ascending_Url(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_URL:
+ result = ET_Comp_Func_Sort_File_By_Descending_Url(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_ASCENDING_ENCODED_BY:
+ result = ET_Comp_Func_Sort_File_By_Ascending_Encoded_By(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_ENCODED_BY:
+ result = ET_Comp_Func_Sort_File_By_Descending_Encoded_By(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_ASCENDING_FILE_TYPE:
+ result = ET_Comp_Func_Sort_File_By_Ascending_File_Type(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_FILE_TYPE:
+ result = ET_Comp_Func_Sort_File_By_Descending_File_Type(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_ASCENDING_FILE_SIZE:
+ result = ET_Comp_Func_Sort_File_By_Ascending_File_Size(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_FILE_SIZE:
+ result = ET_Comp_Func_Sort_File_By_Descending_File_Size(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_ASCENDING_FILE_DURATION:
+ result = ET_Comp_Func_Sort_File_By_Ascending_File_Duration(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_FILE_DURATION:
+ result = ET_Comp_Func_Sort_File_By_Descending_File_Duration(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_ASCENDING_FILE_BITRATE:
+ result = ET_Comp_Func_Sort_File_By_Ascending_File_Bitrate(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_FILE_BITRATE:
+ result = ET_Comp_Func_Sort_File_By_Descending_File_Bitrate(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_ASCENDING_FILE_SAMPLERATE:
+ result = ET_Comp_Func_Sort_File_By_Ascending_File_Samplerate(ETFile1, ETFile2);
+ break;
+ case SORTING_BY_DESCENDING_FILE_SAMPLERATE:
+ result = ET_Comp_Func_Sort_File_By_Descending_File_Samplerate(ETFile1, ETFile2);
+ break;
+ }
+
+ // Frees allocated item from gtk_tree_model_get...
+ //g_free(text1);
+ //g_free(text2);
+
+ return result;
+}
+
+/*
+ * Select all files on the file list
+ */
+void Browser_List_Select_All_Files (void)
+{
+ GtkTreeSelection *selection;
+
+ if (!BrowserList) return;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList));
+ if (selection)
+ {
+ // Must block the select signal to avoid the selecting, one by one, of all files in the main files list
+ g_signal_handlers_block_by_func(G_OBJECT(selection),G_CALLBACK(Browser_List_Row_Selected),NULL);
+ gtk_tree_selection_select_all(selection);
+ g_signal_handlers_unblock_by_func(G_OBJECT(selection),G_CALLBACK(Browser_List_Row_Selected),NULL);
+ }
+}
+
+/*
+ * Unselect all files on the file list
+ */
+void Browser_List_Unselect_All_Files (void)
+{
+ GtkTreeSelection *selection;
+
+ if (!BrowserList) return;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList));
+ if (selection)
+ {
+ gtk_tree_selection_unselect_all(selection);
+ }
+}
+
+/*
+ * Invert the selection of the file list
+ */
+void Browser_List_Invert_File_Selection (void)
+{
+ GtkTreeIter iter;
+ GtkTreeSelection *selection;
+ gboolean valid;
+
+ if (!fileListModel || !BrowserList) return;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList));
+ if (selection)
+ {
+ /* Must block the select signal to avoid selecting all files (one by one) in the main files list */
+ g_signal_handlers_block_by_func(G_OBJECT(selection), G_CALLBACK(Browser_List_Row_Selected), NULL);
+ valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(fileListModel), &iter);
+ while (valid)
+ {
+ if (gtk_tree_selection_iter_is_selected(selection, &iter))
+ {
+ gtk_tree_selection_unselect_iter(selection, &iter);
+ } else
+ {
+ gtk_tree_selection_select_iter(selection, &iter);
+ }
+ valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(fileListModel), &iter);
+ }
+ g_signal_handlers_unblock_by_func(G_OBJECT(selection), G_CALLBACK(Browser_List_Row_Selected), NULL);
+ }
+}
+
+
+/*
+ * Load the list of Artists found in the tags
+ */
+gint Browser_Artist_List_Data_Comp_Func_Sort_By_Ascending_Album (ET_File *ETFile1, ET_File *ETFile2)
+{
+ gchar *current_album1 = ((File_Tag *)((GList *)ETFile1->FileTag)->data)->album;
+ gchar *current_album2 = ((File_Tag *)((GList *)ETFile2->FileTag)->data)->album;
+
+ if (!current_album1 && !current_album2)
+ return 0;
+ else if (!current_album1)
+ return -1;
+ else if (!current_album2)
+ return 1;
+ else
+ return strcmp(current_album1,current_album2);
+}
+
+void Browser_Artist_List_Load_Files (ET_File *etfile_to_select)
+{
+ GList *ArtistList;
+ GList *AlbumList;
+ GList *etfilelist;
+ ET_File *etfile;
+ GList *list;
+ GtkTreeIter iter;
+ GtkTreeSelection *selection;
+ gchar *artistname, *artist_to_select = NULL;
+
+ if (!BrowserArtistList) return;
+
+ if (etfile_to_select)
+ artist_to_select = ((File_Tag *)etfile_to_select->FileTag->data)->artist;
+
+ gtk_list_store_clear(artistListModel);
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserArtistList));
+
+ ArtistList = ETCore->ETArtistAlbumFileList;
+ while (ArtistList)
+ {
+ gint nbr_files = 0;
+
+ // Insert a line for each artist
+ AlbumList = (GList *)ArtistList->data;
+ etfilelist = (GList *)AlbumList->data;
+ etfile = (ET_File *)etfilelist->data;
+ artistname = ((File_Tag *)etfile->FileTag->data)->artist;
+
+ // Third column text : number of files
+ list = g_list_first(AlbumList);
+ while (list)
+ {
+ nbr_files += g_list_length(g_list_first((GList *)list->data));
+ list = list->next;
+ }
+
+ // Add the new row
+ gtk_list_store_append(artistListModel, &iter);
+ gtk_list_store_set(artistListModel, &iter,
+ ARTIST_NAME, artistname,
+ ARTIST_NUM_ALBUMS, g_list_length(g_list_first(AlbumList)),
+ ARTIST_NUM_FILES, nbr_files,
+ ARTIST_ALBUM_LIST_POINTER, AlbumList,
+ -1);
+
+ // Todo: Use something better than string comparison
+ if ( (!artistname && !artist_to_select)
+ || (artistname && artist_to_select && strcmp(artistname,artist_to_select) == 0) )
+ {
+ GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(artistListModel), &iter);
+
+ g_signal_handlers_block_by_func(G_OBJECT(selection),G_CALLBACK(Browser_Artist_List_Row_Selected),NULL);
+ gtk_tree_selection_select_iter(selection, &iter);
+ g_signal_handlers_unblock_by_func(G_OBJECT(selection),G_CALLBACK(Browser_Artist_List_Row_Selected),NULL);
+
+ gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(BrowserArtistList), path, NULL, FALSE, 0, 0);
+ gtk_tree_path_free(path);
+
+ Browser_Album_List_Load_Files(AlbumList, etfile_to_select);
+
+ // Now that we've found the artist, no need to continue searching
+ artist_to_select = NULL;
+ }
+
+ // Set color of the row
+ Browser_Artist_List_Set_Row_Appearance(&iter);
+
+ ArtistList = ArtistList->next;
+ }
+
+ // Select the first line if we weren't asked to select anything
+ if (!etfile_to_select && gtk_tree_model_get_iter_first(GTK_TREE_MODEL(artistListModel), &iter))
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(artistListModel), &iter,
+ ARTIST_ALBUM_LIST_POINTER, &AlbumList,
+ -1);
+ ET_Save_File_Data_From_UI(ETCore->ETFileDisplayed);
+ Browser_Album_List_Load_Files(AlbumList,NULL);
+ }
+}
+
+
+/*
+ * Callback to select-row event
+ */
+void Browser_Artist_List_Row_Selected(GtkTreeSelection* selection, gpointer data)
+{
+ GList *AlbumList;
+ GtkTreeIter iter;
+
+ // Display the relevant albums
+ if(!gtk_tree_selection_get_selected(selection, NULL, &iter))
+ return; // We might be called with no row selected
+
+ // Save the current displayed data
+ ET_Save_File_Data_From_UI(ETCore->ETFileDisplayed);
+
+ gtk_tree_model_get(GTK_TREE_MODEL(artistListModel), &iter, ARTIST_ALBUM_LIST_POINTER, &AlbumList, -1);
+ Browser_Album_List_Load_Files(AlbumList, NULL);
+}
+
+/*
+ * Set the color of the row of BrowserArtistList
+ */
+void Browser_Artist_List_Set_Row_Appearance (GtkTreeIter *iter)
+{
+ GList *AlbumList;
+ GList *etfilelist;
+
+ gtk_tree_model_get(GTK_TREE_MODEL(artistListModel), iter, ARTIST_ALBUM_LIST_POINTER, &AlbumList, -1);
+
+ // Reset the style of the row if one of the files was changed
+ while (AlbumList)
+ {
+ etfilelist = (GList *)AlbumList->data;
+ while (etfilelist)
+ {
+ if ( ET_Check_If_File_Is_Saved((ET_File *)etfilelist->data) == FALSE )
+ {
+ if (CHANGED_FILES_DISPLAYED_TO_BOLD)
+ {
+ // Set the font-style to "bold"
+ gtk_list_store_set(artistListModel, iter, ARTIST_FONT_WEIGHT, PANGO_WEIGHT_BOLD, -1);
+ } else
+ {
+ // Set the background-color to "red"
+ gtk_list_store_set(artistListModel, iter, ARTIST_FONT_WEIGHT, PANGO_WEIGHT_NORMAL, ARTIST_ROW_FOREGROUND, &RED, -1);
+ }
+ break;
+ }
+ etfilelist = etfilelist->next;
+ }
+ AlbumList = AlbumList->next;
+ }
+}
+
+
+
+/*
+ * Load the list of Albums for each Artist
+ */
+void Browser_Album_List_Load_Files (GList *albumlist, ET_File *etfile_to_select)
+{
+ GList *AlbumList;
+ GList *etfilelist = NULL;
+ ET_File *etfile;
+ GtkTreeIter iter;
+ GtkTreeSelection *selection;
+ gchar *albumname, *album_to_select = NULL;
+
+ if (!BrowserAlbumList) return;
+
+ if (etfile_to_select)
+ album_to_select = ((File_Tag *)etfile_to_select->FileTag->data)->album;
+
+ gtk_list_store_clear(albumListModel);
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserAlbumList));
+
+ // Create a first row to select all albums of the artist
+ // FIX ME : the attached list must be freed!
+ AlbumList = albumlist;
+ while (AlbumList)
+ {
+ GList *etfilelist_tmp;
+ etfilelist_tmp = (GList *)AlbumList->data;
+ // We must make a copy to not "alter" the initial list by appending an other list
+ etfilelist_tmp = g_list_copy(etfilelist_tmp);
+ etfilelist = g_list_concat(etfilelist, etfilelist_tmp);
+
+ AlbumList = AlbumList->next;
+ }
+ gtk_list_store_append(albumListModel, &iter);
+ gtk_list_store_set(albumListModel, &iter,
+ ALBUM_NAME, _("<All albums>"),
+ ALBUM_NUM_FILES, g_list_length(g_list_first(etfilelist)),
+ ALBUM_ETFILE_LIST_POINTER, etfilelist,
+ -1);
+
+ // Create a line for each album of the artist
+ AlbumList = albumlist;
+ while (AlbumList)
+ {
+ // Insert a line for each album
+ etfilelist = (GList *)AlbumList->data;
+ etfile = (ET_File *)etfilelist->data;
+ albumname = ((File_Tag *)etfile->FileTag->data)->album;
+
+ // Add the new row
+ gtk_list_store_append(albumListModel, &iter);
+ gtk_list_store_set(albumListModel, &iter,
+ ALBUM_NAME, albumname,
+ ALBUM_NUM_FILES, g_list_length(g_list_first(etfilelist)),
+ ALBUM_ETFILE_LIST_POINTER, etfilelist,
+ -1);
+
+ if ( (!albumname && !album_to_select)
+ || (albumname && album_to_select && strcmp(albumname,album_to_select) == 0) )
+ {
+ GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(albumListModel), &iter);
+
+ g_signal_handlers_block_by_func(G_OBJECT(selection),G_CALLBACK(Browser_Album_List_Row_Selected),NULL);
+ gtk_tree_selection_select_iter(selection, &iter);
+ g_signal_handlers_unblock_by_func(G_OBJECT(selection),G_CALLBACK(Browser_Album_List_Row_Selected),NULL);
+
+ gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(BrowserAlbumList), path, NULL, FALSE, 0, 0);
+ gtk_tree_path_free(path);
+
+ ET_Set_Displayed_File_List(etfilelist);
+ Browser_List_Load_File_List(etfilelist,etfile_to_select);
+
+ // Now that we've found the album, no need to continue searching
+ album_to_select = NULL;
+ }
+
+ // Set color of the row
+ Browser_Album_List_Set_Row_Appearance(&iter);
+
+ AlbumList = AlbumList->next;
+ }
+
+ // Select the first line if we werent asked to select anything
+ if (!etfile_to_select && gtk_tree_model_get_iter_first(GTK_TREE_MODEL(albumListModel), &iter))
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(albumListModel), &iter,
+ ALBUM_ETFILE_LIST_POINTER, &etfilelist,
+ -1);
+ ET_Save_File_Data_From_UI(ETCore->ETFileDisplayed);
+
+ // Set the attached list as "Displayed List"
+ ET_Set_Displayed_File_List(etfilelist);
+ Browser_List_Load_File_List(etfilelist, NULL);
+
+ // Displays the first item
+ Action_Select_Nth_File_By_Etfile((ET_File *)etfilelist->data);
+ }
+}
+
+/*
+ * Callback to select-row event
+ */
+void Browser_Album_List_Row_Selected (GtkTreeSelection *selection, gpointer data)
+{
+ GList *etfilelist;
+ GtkTreeIter iter;
+
+
+ // We might be called with no rows selected
+ if (!gtk_tree_selection_get_selected(selection, NULL, &iter))
+ return;
+
+ gtk_tree_model_get(GTK_TREE_MODEL(albumListModel), &iter, ALBUM_ETFILE_LIST_POINTER, &etfilelist, -1);
+
+ // Save the current displayed data
+ ET_Save_File_Data_From_UI(ETCore->ETFileDisplayed);
+
+ // Set the attached list as "Displayed List"
+ ET_Set_Displayed_File_List(etfilelist);
+
+ Browser_List_Load_File_List(etfilelist, NULL);
+
+ // Displays the first item
+ Action_Select_Nth_File_By_Etfile((ET_File *)etfilelist->data);
+}
+
+
+/*
+ * Set the color of the row of BrowserAlbumList
+ */
+void Browser_Album_List_Set_Row_Appearance (GtkTreeIter *iter)
+{
+ GList *etfilelist;
+
+ gtk_tree_model_get(GTK_TREE_MODEL(albumListModel), iter, ALBUM_ETFILE_LIST_POINTER, &etfilelist, -1);
+
+ // Reset the style of the row if one of the files was changed
+ while (etfilelist)
+ {
+ if ( ET_Check_If_File_Is_Saved((ET_File *)etfilelist->data) == FALSE )
+ {
+ if (CHANGED_FILES_DISPLAYED_TO_BOLD)
+ {
+ // Set the font-style to "bold"
+ gtk_list_store_set(albumListModel, iter, ALBUM_FONT_WEIGHT, PANGO_WEIGHT_BOLD, -1);
+ } else
+ {
+ // Set the background-color to "red"
+ gtk_list_store_set(albumListModel, iter, ALBUM_ROW_FOREGROUND, &RED, -1);
+ }
+ break;
+ }
+ etfilelist = etfilelist->next;
+ }
+}
+
+void Browser_Display_Tree_Or_Artist_Album_List (void)
+{
+ ET_File *etfile = ETCore->ETFileDisplayed; // ETFile to display again after changing browser view
+ GtkWidget *TBViewMode;
+
+ // Save the current displayed data
+ ET_Save_File_Data_From_UI(ETCore->ETFileDisplayed);
+
+ // Toggle button to switch view
+ TBViewMode = gtk_ui_manager_get_widget(UIManager, "/ToolBar/ViewModeToggle");
+
+ // Button pressed in the toolbar
+ if ( gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(TBViewMode)) )
+ {
+ /*
+ * Artist + Album view
+ */
+
+ // Display Artist + Album lists
+ gtk_notebook_set_current_page(GTK_NOTEBOOK(BrowserNoteBook),1);
+ ET_Create_Artist_Album_File_List();
+ Browser_Artist_List_Load_Files(etfile);
+
+ }else
+ {
+
+ /*
+ * Browser (classic) view
+ */
+ // Set the whole list as "Displayed list"
+ ET_Set_Displayed_File_List(ETCore->ETFileList);
+
+ // Display Tree Browser
+ gtk_notebook_set_current_page(GTK_NOTEBOOK(BrowserNoteBook),0);
+ Browser_List_Load_File_List(ETCore->ETFileDisplayedList, etfile);
+
+ // Displays the first file if nothing specified
+ if (!etfile)
+ {
+ GList *etfilelist = ET_Displayed_File_List_First();
+ if (etfilelist)
+ etfile = (ET_File *)etfilelist->data;
+ Action_Select_Nth_File_By_Etfile(etfile);
+ }
+ }
+
+ //ET_Display_File_Data_To_UI(etfile); // Causes a crash
+}
+
+/*
+ * Disable (FALSE) / Enable (TRUE) all user widgets in the browser area (Tree + List + Entry)
+ */
+void Browser_Area_Set_Sensitive (gboolean activate)
+{
+ gtk_widget_set_sensitive(GTK_WIDGET(BrowserEntryCombo),activate);
+ gtk_widget_set_sensitive(GTK_WIDGET(BrowserTree), activate);
+ gtk_widget_set_sensitive(GTK_WIDGET(BrowserList), activate);
+ gtk_widget_set_sensitive(GTK_WIDGET(BrowserArtistList),activate);
+ gtk_widget_set_sensitive(GTK_WIDGET(BrowserAlbumList), activate);
+ gtk_widget_set_sensitive(GTK_WIDGET(BrowserButton), activate);
+}
+
+
+/*
+ * Browser_Popup_Menu_Handler : displays the corresponding menu
+ */
+gboolean Browser_Popup_Menu_Handler (GtkMenu *menu, GdkEventButton *event)
+{
+ if (event && (event->type==GDK_BUTTON_PRESS) && (event->button == 3))
+ {
+ gtk_menu_popup(menu,NULL,NULL,NULL,NULL,event->button,event->time);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * Destroy the whole tree up to the root node
+ */
+void Browser_Tree_Initialize (void)
+{
+ GtkTreeIter parent_iter;
+ GtkTreeIter dummy_iter;
+
+ if (!directoryTreeModel) return;
+
+ gtk_tree_store_clear(directoryTreeModel);
+
+#ifdef WIN32
+
+ /* Code strangely familiar with gtkfilesystemwin32.c */
+
+ GdkPixbuf *drive_pixmap;
+ DWORD drives;
+ UINT drive_type;
+ gchar drive[4] = "A:/";
+ gchar drive_backslashed[5] = "A:\\";
+ gchar drive_slashless[3] = "A:";
+ gchar drive_label[256];
+
+ drives = GetLogicalDrives();
+ if (!drives)
+ g_warning ("GetLogicalDrives failed.");
+
+ while (drives && drive[0] <= 'Z')
+ {
+ if (drives & 1)
+ {
+ char *drive_dir_name;
+
+ drive_type = GetDriveType(drive_backslashed);
+
+ // DRIVE_REMOVABLE 2
+ // DRIVE_FIXED 3
+ // DRIVE_REMOTE 4
+ // DRIVE_CDROM 5
+ // DRIVE_RAMDISK 6
+ // DRIVE_UNKNOWN 0
+ // DRIVE_NO_ROOT_DIR 1
+ switch(drive_type)
+ {
+ case DRIVE_FIXED:
+ drive_pixmap = harddrive_pixmap;
+ break;
+ case DRIVE_REMOVABLE:
+ drive_pixmap = removable_pixmap;
+ break;
+ case DRIVE_CDROM:
+ drive_pixmap = cdrom_pixmap;
+ break;
+ case DRIVE_REMOTE:
+ drive_pixmap = network_pixmap;
+ break;
+ case DRIVE_RAMDISK:
+ drive_pixmap = ramdisk_pixmap;
+ break;
+ default:
+ drive_pixmap = closed_folder_pixmap;
+ }
+
+ drive_label[0] = 0;
+
+ GetVolumeInformation(drive_backslashed, drive_label, 256, NULL, NULL, NULL, NULL, 0);
+
+ /* Drive letter first so alphabetical drive list order works */
+ drive_dir_name = g_strconcat("(", drive_slashless, ") ", drive_label, NULL);
+
+ gtk_tree_store_append(directoryTreeModel, &parent_iter, NULL);
+ gtk_tree_store_set(directoryTreeModel, &parent_iter,
+ TREE_COLUMN_DIR_NAME, drive_dir_name,
+ TREE_COLUMN_FULL_PATH, drive_backslashed,
+ TREE_COLUMN_HAS_SUBDIR, TRUE,
+ TREE_COLUMN_SCANNED, FALSE,
+ TREE_COLUMN_PIXBUF, drive_pixmap,
+ -1);
+ // Insert dummy node
+ gtk_tree_store_append(directoryTreeModel, &dummy_iter, &parent_iter);
+
+ g_free(drive_dir_name);
+ }
+ drives >>= 1;
+ drive[0]++;
+ drive_backslashed[0]++;
+ drive_slashless[0]++;
+ }
+
+#else
+
+ gtk_tree_store_append(directoryTreeModel, &parent_iter, NULL);
+ gtk_tree_store_set(directoryTreeModel, &parent_iter,
+ TREE_COLUMN_DIR_NAME, G_DIR_SEPARATOR_S,
+ TREE_COLUMN_FULL_PATH, G_DIR_SEPARATOR_S,
+ TREE_COLUMN_HAS_SUBDIR, TRUE,
+ TREE_COLUMN_SCANNED, FALSE,
+ TREE_COLUMN_PIXBUF, closed_folder_pixmap,
+ -1);
+ // insert dummy node
+ gtk_tree_store_append(directoryTreeModel, &dummy_iter, &parent_iter);
+
+#endif
+
+}
+
+/*
+ * Browser_Tree_Rebuild: Refresh the tree browser by destroying it and rebuilding it.
+ * Opens tree nodes corresponding to 'path_to_load' if this parameter isn't NULL.
+ * If NULL, selects the current path.
+ */
+void Browser_Tree_Rebuild (gchar *path_to_load)
+{
+ gchar *current_path = NULL;
+ GtkTreeSelection *selection;
+
+ /* May be called from GtkUIManager callback */
+ if (GTK_IS_ACTION(path_to_load))
+ path_to_load = NULL;
+
+ if (path_to_load != NULL)
+ {
+ Browser_Tree_Initialize();
+ Browser_Tree_Select_Dir(path_to_load);
+ return;
+ }
+
+ /* Memorize the current path to load it again at the end */
+ current_path = Browser_Tree_Get_Path_Of_Selected_Node();
+ if (current_path==NULL && BrowserEntryCombo)
+ {
+ /* If no node selected, get path from BrowserEntry or default path */
+ if (BrowserCurrentPath != NULL)
+ current_path = g_strdup(BrowserCurrentPath);
+ else if (g_utf8_strlen(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(BrowserEntryCombo)->child)), -1) > 0)
+ current_path = filename_from_display(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(BrowserEntryCombo)->child)));
+ else
+ current_path = g_strdup(DEFAULT_PATH_TO_MP3);
+ }
+
+ Browser_Tree_Initialize();
+ /* Select again the memorized path without loading files */
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserTree));
+ if (selection)
+ {
+ g_signal_handlers_block_by_func(G_OBJECT(selection),G_CALLBACK(Browser_Tree_Node_Selected),NULL);
+ Browser_Tree_Select_Dir(current_path);
+ g_signal_handlers_unblock_by_func(G_OBJECT(selection),G_CALLBACK(Browser_Tree_Node_Selected),NULL);
+ }
+ g_free(current_path);
+
+ Update_Command_Buttons_Sensivity();
+}
+
+/*
+ * Renames a directory
+ * last_path:
+ * new_path:
+ * Parameters are non-utf8!
+ */
+void Browser_Tree_Rename_Directory (gchar *last_path, gchar *new_path)
+{
+
+ gchar **textsplit;
+ gint i;
+ GtkTreeIter iter;
+ GtkTreePath *childpath;
+ GtkTreePath *parentpath;
+ gchar *new_basename;
+ gchar *new_basename_utf8;
+ gchar *path;
+ gboolean valid;
+
+ if (!last_path || !new_path)
+ return;
+
+ /*
+ * Find the existing tree entry
+ */
+ textsplit = g_strsplit(last_path, G_DIR_SEPARATOR_S, 0);
+
+#ifdef WIN32
+ if (!Browser_Win32_Get_Drive_Root(textsplit[0], &iter, &parentpath))
+ return;
+#else
+ parentpath = gtk_tree_path_new_first();
+#endif
+
+ for (i = 1; textsplit[i] != NULL; i++)
+ {
+ valid = gtk_tree_model_get_iter(GTK_TREE_MODEL(directoryTreeModel), &iter, parentpath);
+ childpath = Find_Child_Node(&iter, textsplit[i]);
+ if (childpath == NULL)
+ {
+ // ERROR! Could not find it!
+ gchar *text_utf8 = filename_to_display(textsplit[i]);
+ Log_Print(_("Error: Searching for %s, could not find node %s in tree."), last_path, text_utf8);
+ g_strfreev(textsplit);
+ g_free(text_utf8);
+ return;
+ }
+ gtk_tree_path_free(parentpath);
+ parentpath = childpath;
+ }
+
+ valid = gtk_tree_model_get_iter(GTK_TREE_MODEL(directoryTreeModel), &iter, parentpath);
+ gtk_tree_path_free(parentpath);
+
+ /* Rename the on-screen node */
+ new_basename = g_path_get_basename(new_path);
+ new_basename_utf8 = filename_to_display(new_basename);
+ gtk_tree_store_set(directoryTreeModel, &iter,
+ TREE_COLUMN_DIR_NAME, new_basename_utf8,
+ TREE_COLUMN_FULL_PATH, new_path,
+ -1);
+
+ /* Update fullpath of child nodes */
+ Browser_Tree_Handle_Rename(&iter, last_path, new_path);
+
+ /* Update the variable of the current path */
+ path = Browser_Tree_Get_Path_Of_Selected_Node();
+ Browser_Update_Current_Path(path);
+ g_free(path);
+
+ g_strfreev(textsplit);
+ g_free(new_basename);
+ g_free(new_basename_utf8);
+}
+
+/*
+ * Recursive function to update paths of all child nodes
+ */
+void Browser_Tree_Handle_Rename (GtkTreeIter *parentnode, gchar *old_path, gchar *new_path)
+{
+ GtkTreeIter iter;
+ gchar *path;
+ gchar *path_shift;
+ gchar *path_new;
+
+ // If there are no children then nothing needs to be done!
+ if(!gtk_tree_model_iter_children(GTK_TREE_MODEL(directoryTreeModel), &iter, parentnode))
+ return;
+
+ do
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(directoryTreeModel), &iter,
+ TREE_COLUMN_FULL_PATH, &path, -1);
+ if(path == NULL)
+ continue;
+
+ path_shift = g_utf8_offset_to_pointer(path, g_utf8_strlen(old_path, -1));
+ path_new = g_strconcat(new_path, path_shift, NULL);
+
+ gtk_tree_store_set(directoryTreeModel, &iter,
+ TREE_COLUMN_FULL_PATH, path_new, -1);
+
+ g_free(path_new);
+ g_free(path);
+
+ // Recurse if necessary
+ if(gtk_tree_model_iter_has_child(GTK_TREE_MODEL(directoryTreeModel), &iter))
+ Browser_Tree_Handle_Rename(&iter, old_path, new_path);
+
+ } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(directoryTreeModel), &iter));
+
+}
+
+/*
+ * Find the child node of "parentnode" that has text of "childtext
+ * Returns NULL on failure
+ */
+GtkTreePath *Find_Child_Node (GtkTreeIter *parentnode, gchar *childtext)
+{
+ gint row;
+ GtkTreeIter iter;
+ gchar *text;
+ gchar *temp;
+
+ for (row=0; row < gtk_tree_model_iter_n_children(GTK_TREE_MODEL(directoryTreeModel), parentnode); row++)
+ {
+ if (row == 0)
+ {
+ if (gtk_tree_model_iter_children(GTK_TREE_MODEL(directoryTreeModel), &iter, parentnode) == FALSE) return NULL;
+ } else
+ {
+ if (gtk_tree_model_iter_next(GTK_TREE_MODEL(directoryTreeModel), &iter) == FALSE)
+ return NULL;
+ }
+ gtk_tree_model_get(GTK_TREE_MODEL(directoryTreeModel), &iter,
+ TREE_COLUMN_FULL_PATH, &temp, -1);
+ text = g_path_get_basename(temp);
+ g_free(temp);
+ if(strcmp(childtext,text) == 0)
+ {
+ g_free(text);
+ return gtk_tree_model_get_path(GTK_TREE_MODEL(directoryTreeModel), &iter);
+ }
+ g_free(text);
+
+ }
+
+ return NULL;
+}
+
+/*
+ * Check if path has any subdirectories
+ * Returns true if subdirectories exist.
+ * path should be in raw filename format (non-UTF8)
+ */
+static gboolean check_for_subdir (gchar *path)
+{
+ DIR *dir;
+ struct dirent *dirent;
+ struct stat statbuf;
+ gchar *npath;
+
+ if( (dir=opendir(path)) )
+ {
+ while( (dirent=readdir(dir)) )
+ {
+ // We don't read the directories '.' and '..', but may read hidden directories like '.mydir'
+ if ( (g_ascii_strcasecmp (dirent->d_name,"..") != 0)
+ && ( (g_ascii_strncasecmp(dirent->d_name,".", 1) != 0) || (BROWSE_HIDDEN_DIR && strlen(dirent->d_name) > 1)) )
+ {
+#ifdef WIN32
+ // On win32 : stat("/path/to/dir") succeed, while stat("/path/to/dir/") fails
+ npath = g_strconcat(path,dirent->d_name,NULL);
+#else
+ npath = g_strconcat(path,dirent->d_name,G_DIR_SEPARATOR_S,NULL);
+#endif
+
+ if (stat(npath,&statbuf) == -1)
+ {
+ g_free(npath);
+ continue;
+ }
+
+ g_free(npath);
+
+ if(S_ISDIR(statbuf.st_mode))
+ {
+ closedir(dir);
+ return TRUE;
+ }
+ }
+ }
+ closedir(dir);
+ }
+ return FALSE;
+}
+
+/*
+ * Check if you have access permissions for directory path. Returns 1 if ok, else 0.
+ */
+gboolean Check_For_Access_Permission (gchar *path)
+{
+ DIR *dir;
+
+ if( (dir=opendir(path)) == NULL )
+ {
+ if (errno == EACCES)
+ return FALSE;
+ } else
+ {
+ closedir(dir);
+ }
+ return TRUE;
+}
+
+
+/*
+ * Sets the selection function. If set, this function is called before any node
+ * is selected or unselected, giving some control over which nodes are selected.
+ * The select function should return TRUE if the state of the node may be toggled,
+ * and FALSE if the state of the node should be left unchanged.
+ */
+gboolean Browser_List_Select_Func (GtkTreeSelection *selection, GtkTreeModel *model, GtkTreePath *path, gboolean path_currently_selected, gpointer data)
+{
+ // This line will be selected at the end of the event.
+ // We store the last ETFile selected, as gtk_tree_selection_get_selected_rows
+ // returns the selection, in the ascending line order, instead of the real
+ // order of line selection (so we can't displayed the last selected file)
+ // FIXME : should generate a list to get the previous selected file if unselected the last selected file
+ if (!path_currently_selected)
+ {
+ GtkTreeIter iter;
+ if (gtk_tree_model_get_iter(GTK_TREE_MODEL(fileListModel), &iter, path))
+ gtk_tree_model_get(GTK_TREE_MODEL(fileListModel), &iter,
+ LIST_FILE_POINTER, &LastBrowserListETFileSelected, -1);
+ }else
+ {
+ LastBrowserListETFileSelected = NULL;
+ }
+ //g_print(">>>%s -> %d -> %x\n",gtk_tree_path_to_string(path),path_currently_selected,LastBrowserListETFileSelected);
+
+ return TRUE;
+}
+
+
+/*
+ * Open up a node on the browser tree
+ * Scanning and showing all subdirectories
+ */
+static void expand_cb (GtkWidget *tree, GtkTreeIter *iter, GtkTreePath *gtreePath, gpointer data)
+{
+ DIR *dir;
+ struct dirent *dirent;
+ gchar *path;
+ gchar *dirname_utf8;
+ struct stat statbuf;
+ gchar *fullpath_file;
+ gchar *parentPath;
+ gboolean treeScanned;
+ gboolean has_subdir = FALSE;
+ GtkTreeIter currentIter;
+ GtkTreeIter subNodeIter;
+ GdkPixbuf *pixbuf;
+
+ if (!directoryTreeModel) return;
+
+ gtk_tree_model_get(GTK_TREE_MODEL(directoryTreeModel), iter,
+ TREE_COLUMN_FULL_PATH, &parentPath,
+ TREE_COLUMN_SCANNED, &treeScanned, -1);
+
+ if (treeScanned)
+ return;
+
+ if ( (dir=opendir(parentPath)) )
+ {
+ while ( (dirent=readdir(dir)) )
+ {
+ path = g_strconcat(parentPath, dirent->d_name, NULL);
+ stat(path, &statbuf);
+
+ // We don't read the directories '.' and '..', but may read hidden directories like '.mydir'
+ if (S_ISDIR(statbuf.st_mode)
+ && ( (g_ascii_strcasecmp (dirent->d_name,"..") != 0)
+ && ((g_ascii_strncasecmp(dirent->d_name,".", 1) != 0) || (BROWSE_HIDDEN_DIR && strlen(dirent->d_name) > 1)) ) )
+ {
+
+ if (path[strlen(path)-1]!=G_DIR_SEPARATOR)
+ fullpath_file = g_strconcat(path,G_DIR_SEPARATOR_S,NULL);
+ else
+ fullpath_file = g_strdup(path);
+
+ dirname_utf8 = filename_to_display(dirent->d_name);
+ //if (!dirname_utf8)
+ //{
+ // gchar *escaped_temp = g_strescape(dirent->d_name, NULL);
+ // g_free(escaped_temp);
+ //}
+
+ if (check_for_subdir(fullpath_file))
+ has_subdir = TRUE;
+ else
+ has_subdir = FALSE;
+
+ /* Select pixmap for accessible/unaccessible directory */
+ if (Check_For_Access_Permission(path))
+ pixbuf = closed_folder_pixmap;
+ else
+ pixbuf = closed_folder_locked_pixmap;
+
+ gtk_tree_store_append(directoryTreeModel, &currentIter, iter);
+ gtk_tree_store_set(directoryTreeModel, &currentIter,
+ TREE_COLUMN_DIR_NAME, dirname_utf8,
+ TREE_COLUMN_FULL_PATH, fullpath_file,
+ TREE_COLUMN_HAS_SUBDIR, !has_subdir,
+ TREE_COLUMN_SCANNED, FALSE,
+ TREE_COLUMN_PIXBUF, pixbuf, -1);
+
+ if (has_subdir)
+ {
+ // Insert a dummy node
+ gtk_tree_store_append(directoryTreeModel, &subNodeIter, &currentIter);
+ }
+
+ g_free(fullpath_file);
+ g_free(dirname_utf8);
+ }
+ g_free(path);
+
+ }
+ closedir(dir);
+ }
+
+ // remove dummy node
+ gtk_tree_model_iter_children(GTK_TREE_MODEL(directoryTreeModel), &subNodeIter, iter);
+ gtk_tree_store_remove(directoryTreeModel, &subNodeIter);
+
+#ifdef WIN32
+ // set open folder pixmap except on drive (depth == 0)
+ if (gtk_tree_path_get_depth(gtreePath) > 1)
+ {
+ // update the icon of the node to opened folder :-)
+ gtk_tree_store_set(directoryTreeModel, iter,
+ TREE_COLUMN_SCANNED, TRUE,
+ TREE_COLUMN_PIXBUF, opened_folder_pixmap, -1);
+ }
+#else
+ // update the icon of the node to opened folder :-)
+ gtk_tree_store_set(directoryTreeModel, iter,
+ TREE_COLUMN_SCANNED, TRUE,
+ TREE_COLUMN_PIXBUF, opened_folder_pixmap, -1);
+#endif
+
+ gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(directoryTreeModel),
+ TREE_COLUMN_DIR_NAME, GTK_SORT_ASCENDING);
+
+ g_free(parentPath);
+}
+
+static void collapse_cb (GtkWidget *tree, GtkTreeIter *iter, GtkTreePath *treePath, gpointer data)
+{
+ GtkTreeIter subNodeIter;
+
+ if (!directoryTreeModel) return;
+
+ gtk_tree_model_iter_children(GTK_TREE_MODEL(directoryTreeModel),
+ &subNodeIter, iter);
+ while (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(directoryTreeModel), iter))
+ {
+ gtk_tree_model_iter_children(GTK_TREE_MODEL(directoryTreeModel), &subNodeIter, iter);
+ gtk_tree_store_remove(directoryTreeModel, &subNodeIter);
+ }
+
+#ifdef WIN32
+ // set closed folder pixmap except on drive (depth == 0)
+ if(gtk_tree_path_get_depth(treePath) > 1)
+ {
+ // update the icon of the node to closed folder :-)
+ gtk_tree_store_set(directoryTreeModel, iter,
+ TREE_COLUMN_SCANNED, FALSE,
+ TREE_COLUMN_PIXBUF, closed_folder_pixmap, -1);
+ }
+#else
+ // update the icon of the node to closed folder :-)
+ gtk_tree_store_set(directoryTreeModel, iter,
+ TREE_COLUMN_SCANNED, FALSE,
+ TREE_COLUMN_PIXBUF, closed_folder_pixmap, -1);
+#endif
+
+ // insert dummy node
+ gtk_tree_store_append(directoryTreeModel, &subNodeIter, iter);
+}
+
+/*
+ * Create item of the browser (Entry + Tree + List).
+ */
+GtkWidget *Create_Browser_Items (GtkWidget *parent)
+{
+ GtkWidget *VerticalBox;
+ GtkWidget *HBox;
+ GtkWidget *ScrollWindowDirectoryTree;
+ GtkWidget *ScrollWindowFileList;
+ GtkWidget *ScrollWindowArtistList;
+ GtkWidget *ScrollWindowAlbumList;
+ GtkWidget *Label;
+ GtkWidget *Icon;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ GtkTooltips *Tips;
+ GtkWidget *PopupMenu;
+ gchar *ArtistList_Titles[] = {N_("Artist"),N_("# Albums"),N_("# Files")};
+ gchar *AlbumList_Titles[] = {N_("Album"),N_("# Files")};
+
+ Tips = gtk_tooltips_new();
+ VerticalBox = gtk_vbox_new(FALSE,2);
+ gtk_container_set_border_width(GTK_CONTAINER(VerticalBox),2);
+
+
+ // HBox for BrowserEntry + BrowserLabel
+ HBox = gtk_hbox_new(FALSE,0);
+ gtk_box_pack_start(GTK_BOX(VerticalBox),HBox,FALSE,TRUE,0);
+
+ /*
+ * The button to go to the parent directory
+ */
+ BrowserButton = gtk_button_new();
+ Icon = gtk_image_new_from_stock("easytag-parent-folder", GTK_ICON_SIZE_SMALL_TOOLBAR); // On Win32, GTK_ICON_SIZE_BUTTON enlarge the combobox...
+ gtk_container_add(GTK_CONTAINER(BrowserButton),Icon);
+ gtk_box_pack_start(GTK_BOX(HBox),BrowserButton,FALSE,FALSE,0);
+ gtk_button_set_relief(GTK_BUTTON(BrowserButton),GTK_RELIEF_NONE);
+ g_signal_connect(G_OBJECT(BrowserButton),"clicked",G_CALLBACK(Browser_Button_Clicked),NULL);
+ gtk_tooltips_set_tip(Tips,BrowserButton,_("Go to parent directory"),NULL);
+
+ /*
+ * The entry box for displaying path
+ */
+ if (BrowserEntryModel != NULL)
+ gtk_list_store_clear(BrowserEntryModel);
+ else
+ BrowserEntryModel = gtk_list_store_new(MISC_COMBO_COUNT, G_TYPE_STRING);
+
+ BrowserEntryCombo = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(BrowserEntryModel), MISC_COMBO_TEXT);
+ /* History list */
+ Load_Path_Entry_List(BrowserEntryModel, MISC_COMBO_TEXT);
+ //gtk_combo_box_set_wrap_width(GTK_COMBO_BOX(BrowserEntryCombo),2); // Two columns to display paths
+
+ g_signal_connect(G_OBJECT(GTK_ENTRY(GTK_BIN(BrowserEntryCombo)->child)),"activate",G_CALLBACK(Browser_Entry_Activated),NULL);
+ gtk_box_pack_start(GTK_BOX(HBox),BrowserEntryCombo,TRUE,TRUE,1);
+ gtk_tooltips_set_tip(Tips,GTK_WIDGET(GTK_ENTRY(GTK_BIN(BrowserEntryCombo)->child)),_("Enter a directory to browse."),NULL);
+
+
+ /*
+ * The label for displaying number of files in path (without subdirs)
+ */
+ BrowserLabel = gtk_label_new(" ... ");
+ gtk_box_pack_start(GTK_BOX(HBox),BrowserLabel,FALSE,FALSE,2);
+
+
+ /* Create pixmaps */
+ if(!opened_folder_pixmap)
+ {
+ opened_folder_pixmap = gdk_pixbuf_new_from_xpm_data(opened_folder_xpm);
+ closed_folder_pixmap = gdk_pixbuf_new_from_xpm_data(closed_folder_xpm);
+ closed_folder_locked_pixmap = gdk_pixbuf_new_from_xpm_data(closed_folder_locked_xpm);
+
+#ifdef WIN32
+ /* get GTK's theme harddrive and removable icons and render it in a pixbuf */
+ harddrive_pixmap = gtk_icon_set_render_icon(gtk_style_lookup_icon_set(parent->style, GTK_STOCK_HARDDISK),
+ parent->style,
+ gtk_widget_get_direction (parent),
+ GTK_STATE_NORMAL,
+ GTK_ICON_SIZE_BUTTON,
+ parent, NULL);
+
+ removable_pixmap = gtk_icon_set_render_icon(gtk_style_lookup_icon_set(parent->style, GTK_STOCK_FLOPPY),
+ parent->style,
+ gtk_widget_get_direction (parent),
+ GTK_STATE_NORMAL,
+ GTK_ICON_SIZE_BUTTON,
+ parent, NULL);
+
+ cdrom_pixmap = gtk_icon_set_render_icon(gtk_style_lookup_icon_set(parent->style, GTK_STOCK_CDROM),
+ parent->style,
+ gtk_widget_get_direction (parent),
+ GTK_STATE_NORMAL,
+ GTK_ICON_SIZE_BUTTON,
+ parent, NULL);
+
+ network_pixmap = gtk_icon_set_render_icon(gtk_style_lookup_icon_set(parent->style, GTK_STOCK_NETWORK),
+ parent->style,
+ gtk_widget_get_direction (parent),
+ GTK_STATE_NORMAL,
+ GTK_ICON_SIZE_BUTTON,
+ parent, NULL);
+
+ ramdisk_pixmap = gdk_pixbuf_new_from_xpm_data(ram_disk_xpm);
+
+#endif
+ }
+
+ /* Browser NoteBook :
+ * - one tab for the BrowserTree
+ * - one tab for the BrowserArtistList and the BrowserAlbumList
+ */
+ BrowserNoteBook = gtk_notebook_new();
+ //gtk_notebook_popup_enable(GTK_NOTEBOOK(BrowserNoteBook));
+ gtk_notebook_set_show_tabs(GTK_NOTEBOOK(BrowserNoteBook),FALSE);
+ gtk_notebook_set_show_border(GTK_NOTEBOOK(BrowserNoteBook),FALSE);
+
+
+ /*
+ * The ScrollWindow and the Directory-Tree
+ */
+ ScrollWindowDirectoryTree = gtk_scrolled_window_new(NULL,NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ScrollWindowDirectoryTree),
+ GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);
+ directoryTreeModel = gtk_tree_store_new(TREE_COLUMN_COUNT,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN,
+ GDK_TYPE_PIXBUF);
+
+ Label = gtk_label_new(_("Tree"));
+ gtk_notebook_append_page(GTK_NOTEBOOK(BrowserNoteBook),ScrollWindowDirectoryTree,Label);
+
+ /* The tree view */
+ BrowserTree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(directoryTreeModel));
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(BrowserTree), FALSE);
+ renderer = gtk_cell_renderer_pixbuf_new();
+ column = gtk_tree_view_column_new();
+ gtk_tree_view_column_pack_start(column, renderer, FALSE);
+ gtk_tree_view_column_set_attributes(column, renderer,
+ "pixbuf", TREE_COLUMN_PIXBUF, NULL);
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_column_pack_start(column, renderer, FALSE);
+ gtk_tree_view_column_set_attributes(column, renderer,
+ "text", TREE_COLUMN_DIR_NAME, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(BrowserTree), column);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_container_add(GTK_CONTAINER(ScrollWindowDirectoryTree),BrowserTree);
+
+ Browser_Tree_Initialize();
+
+
+ /* Signals */
+ g_signal_connect(G_OBJECT(BrowserTree), "row-expanded", G_CALLBACK(expand_cb),NULL);
+ g_signal_connect(G_OBJECT(BrowserTree), "row-collapsed", G_CALLBACK(collapse_cb),NULL);
+ g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserTree))),
+ "changed", G_CALLBACK(Browser_Tree_Node_Selected), NULL);
+
+ g_signal_connect(G_OBJECT(BrowserTree),"key_press_event", G_CALLBACK(Browser_Tree_Key_Press),NULL);
+
+ /* Create Popup Menu on browser tree view */
+ PopupMenu = gtk_ui_manager_get_widget(UIManager, "/DirPopup");
+ g_signal_connect_swapped(G_OBJECT(BrowserTree),"button_press_event",
+ G_CALLBACK(Browser_Popup_Menu_Handler), G_OBJECT(PopupMenu));
+
+
+
+ /*
+ * The ScrollWindows with the Artist and Album Lists
+ */
+
+ ArtistAlbumVPaned = gtk_vpaned_new();
+
+ Label = gtk_label_new(_("Artist & Album"));
+ gtk_notebook_append_page(GTK_NOTEBOOK(BrowserNoteBook),ArtistAlbumVPaned,Label);
+
+ ScrollWindowArtistList = gtk_scrolled_window_new(NULL,NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ScrollWindowArtistList),
+ GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);
+
+ artistListModel = gtk_list_store_new(ARTIST_COLUMN_COUNT,
+ G_TYPE_STRING,
+ G_TYPE_UINT,
+ G_TYPE_UINT,
+ G_TYPE_POINTER,
+ PANGO_TYPE_STYLE,
+ G_TYPE_INT,
+ GDK_TYPE_COLOR);
+
+ BrowserArtistList = gtk_tree_view_new_with_model(GTK_TREE_MODEL(artistListModel));
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(BrowserArtistList), TRUE);
+ renderer = gtk_cell_renderer_text_new();
+
+ column = gtk_tree_view_column_new_with_attributes(_(ArtistList_Titles[0]), renderer,
+ "text", ARTIST_NAME,
+ "weight", ARTIST_FONT_WEIGHT,
+ "style", ARTIST_FONT_STYLE,
+ "foreground-gdk", ARTIST_ROW_FOREGROUND,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(BrowserArtistList), column);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+
+ column = gtk_tree_view_column_new_with_attributes(_(ArtistList_Titles[1]), renderer,
+ "text", ARTIST_NUM_ALBUMS,
+ "weight", ARTIST_FONT_WEIGHT,
+ "style", ARTIST_FONT_STYLE,
+ "foreground-gdk", ARTIST_ROW_FOREGROUND,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(BrowserArtistList), column);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+
+ column = gtk_tree_view_column_new_with_attributes(_(ArtistList_Titles[2]), renderer,
+ "text", ARTIST_NUM_FILES,
+ "weight", ARTIST_FONT_WEIGHT,
+ "style", ARTIST_FONT_STYLE,
+ "foreground-gdk", ARTIST_ROW_FOREGROUND,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(BrowserArtistList), column);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+
+ gtk_tree_view_set_reorderable(GTK_TREE_VIEW(BrowserArtistList), FALSE);
+ gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserArtistList)),GTK_SELECTION_SINGLE);
+ g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserArtistList))),"changed",G_CALLBACK(Browser_Artist_List_Row_Selected),NULL);
+
+ gtk_container_add(GTK_CONTAINER(ScrollWindowArtistList),BrowserArtistList);
+
+ // Create Popup Menu on browser artist list
+ PopupMenu = gtk_ui_manager_get_widget(UIManager, "/DirArtistPopup");
+ g_signal_connect_swapped(G_OBJECT(BrowserArtistList),"button_press_event",
+ G_CALLBACK(Browser_Popup_Menu_Handler), G_OBJECT(PopupMenu));
+ // Not available yet!
+ //ui_widget_set_sensitive(MENU_FILE, AM_ARTIST_OPEN_FILE_WITH, FALSE);
+
+ ScrollWindowAlbumList = gtk_scrolled_window_new(NULL,NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ScrollWindowAlbumList),
+ GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);
+
+ albumListModel = gtk_list_store_new(ALBUM_COLUMN_COUNT,
+ G_TYPE_STRING,
+ G_TYPE_UINT,
+ G_TYPE_POINTER,
+ PANGO_TYPE_STYLE,
+ G_TYPE_INT,
+ GDK_TYPE_COLOR);
+
+ BrowserAlbumList = gtk_tree_view_new_with_model(GTK_TREE_MODEL(albumListModel));
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(BrowserAlbumList), TRUE);
+ renderer = gtk_cell_renderer_text_new();
+
+ column = gtk_tree_view_column_new_with_attributes(_(AlbumList_Titles[0]), renderer,
+ "text", ALBUM_NAME,
+ "weight", ALBUM_FONT_WEIGHT,
+ "style", ALBUM_FONT_STYLE,
+ "foreground-gdk", ALBUM_ROW_FOREGROUND,
+ NULL);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(BrowserAlbumList), column);
+
+ column = gtk_tree_view_column_new_with_attributes(_(AlbumList_Titles[1]), renderer,
+ "text", ALBUM_NUM_FILES,
+ "weight", ALBUM_FONT_WEIGHT,
+ "style", ALBUM_FONT_STYLE,
+ "foreground-gdk", ALBUM_ROW_FOREGROUND,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(BrowserAlbumList), column);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+
+ gtk_tree_view_set_reorderable(GTK_TREE_VIEW(BrowserAlbumList), FALSE);
+ gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserAlbumList)), GTK_SELECTION_SINGLE);
+
+ g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserAlbumList))),"changed",G_CALLBACK(Browser_Album_List_Row_Selected),NULL);
+ gtk_container_add(GTK_CONTAINER(ScrollWindowAlbumList),BrowserAlbumList);
+
+ // Create Popup Menu on browser album list
+ PopupMenu = gtk_ui_manager_get_widget(UIManager, "/DirAlbumPopup");
+ g_signal_connect_swapped(G_OBJECT(BrowserArtistList),"button_press_event",
+ G_CALLBACK(Browser_Popup_Menu_Handler), G_OBJECT(PopupMenu));
+ // Not available yet!
+ //ui_widget_set_sensitive(MENU_FILE, AM_ALBUM_OPEN_FILE_WITH, FALSE);
+
+
+ gtk_paned_pack1(GTK_PANED(ArtistAlbumVPaned),ScrollWindowArtistList,TRUE,TRUE); // Top side
+ gtk_paned_pack2(GTK_PANED(ArtistAlbumVPaned),ScrollWindowAlbumList,TRUE,TRUE); // Bottom side
+ gtk_paned_set_position(GTK_PANED(ArtistAlbumVPaned),PANE_HANDLE_POSITION3);
+
+
+ /*
+ * The ScrollWindow and the List
+ */
+ ScrollWindowFileList = gtk_scrolled_window_new(NULL,NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ScrollWindowFileList),
+ GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);
+
+ /* The file list */
+ fileListModel = gtk_list_store_new(LIST_COLUMN_COUNT,
+ G_TYPE_STRING,
+ G_TYPE_POINTER,
+ G_TYPE_INT,
+ G_TYPE_BOOLEAN,
+ G_TYPE_INT,
+ GDK_TYPE_COLOR,
+ GDK_TYPE_COLOR);
+
+ BrowserList = gtk_tree_view_new_with_model(GTK_TREE_MODEL(fileListModel));
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(BrowserList), FALSE);
+ gtk_container_add(GTK_CONTAINER(ScrollWindowFileList), BrowserList);
+ gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(BrowserList), FALSE);
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new();
+ gtk_tree_view_column_pack_start(column, renderer, FALSE);
+ gtk_tree_view_column_set_attributes(column, renderer,
+ "text", LIST_FILE_NAME,
+ "weight", LIST_FONT_WEIGHT,
+ "background-gdk", LIST_ROW_BACKGROUND,
+ "foreground-gdk", LIST_ROW_FOREGROUND,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(BrowserList), column);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_set_reorderable(GTK_TREE_VIEW(BrowserList), FALSE);
+ gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList)),GTK_SELECTION_MULTIPLE);
+ // When selecting a line
+ gtk_tree_selection_set_select_function(gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList)), Browser_List_Select_Func, NULL, NULL);
+ // To sort list
+ //gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(fileListModel), 0, Browser_List_Sort_Func, NULL, NULL);
+ Browser_List_Refresh_Sort();
+ gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(fileListModel), 0, GTK_SORT_ASCENDING);
+
+ g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList))),
+ "changed", G_CALLBACK(Browser_List_Row_Selected), NULL);
+ g_signal_connect(G_OBJECT(BrowserList),"key_press_event", G_CALLBACK(Browser_List_Key_Press),NULL);
+ g_signal_connect(G_OBJECT(BrowserList),"button_press_event", G_CALLBACK(Browser_List_Button_Press),NULL);
+
+
+ /*
+ * Create Popup Menu on file list
+ */
+ PopupMenu = gtk_ui_manager_get_widget(UIManager, "/FilePopup");
+ g_signal_connect_swapped(G_OBJECT(BrowserList),"button_press_event",
+ G_CALLBACK(Browser_Popup_Menu_Handler), G_OBJECT(PopupMenu));
+
+ /*
+ * The list store for run program combos
+ */
+ RunProgramModel = gtk_list_store_new(MISC_COMBO_COUNT, G_TYPE_STRING);
+
+ /*
+ * The pane for the tree and list
+ */
+ BrowserHPaned = gtk_hpaned_new();
+ gtk_box_pack_start(GTK_BOX(VerticalBox),BrowserHPaned,TRUE,TRUE,0);
+ gtk_paned_pack1(GTK_PANED(BrowserHPaned),BrowserNoteBook,TRUE,TRUE); // Left side
+ gtk_paned_pack2(GTK_PANED(BrowserHPaned),ScrollWindowFileList,TRUE,TRUE); // Right side
+ gtk_paned_set_position(GTK_PANED(BrowserHPaned),PANE_HANDLE_POSITION2);
+
+ gtk_widget_show_all(VerticalBox);
+
+ /* Set home variable as current path */
+ Browser_Update_Current_Path(HOME_VARIABLE);
+
+ return VerticalBox;
+}
+
+
+
+/*
+ * The window to Rename a directory into the browser.
+ */
+void Browser_Open_Rename_Directory_Window (void)
+{
+ GtkWidget *Frame;
+ GtkWidget *VBox;
+ GtkWidget *HBox;
+ GtkWidget *Label;
+ GtkWidget *ButtonBox;
+ GtkWidget *Button;
+ GtkWidget *Separator;
+ GtkTooltips *Tips;
+ gchar *directory_parent = NULL;
+ gchar *directory_name = NULL;
+ gchar *directory_name_utf8 = NULL;
+ gchar *address = NULL;
+ gchar *string;
+
+ if (RenameDirectoryWindow != NULL)
+ {
+ gdk_window_raise(RenameDirectoryWindow->window);
+ return;
+ }
+
+ /* We get the full path but we musn't display the parent directories */
+ directory_parent = g_strdup(BrowserCurrentPath);
+ if (!directory_parent || strlen(directory_parent) == 0)
+ {
+ g_free(directory_parent);
+ return;
+ }
+
+ // Remove the last '/' in the path if it exists
+ if (strlen(directory_parent)>1 && directory_parent[strlen(directory_parent)-1]==G_DIR_SEPARATOR)
+ directory_parent[strlen(directory_parent)-1]=0;
+ // Get name of the directory to rename (without path)
+ address = strrchr(directory_parent,G_DIR_SEPARATOR);
+ if (!address) return;
+ directory_name = g_strdup(address+1);
+ *(address+1) = 0;
+
+ if (!directory_name || strlen(directory_name)==0)
+ {
+ g_free(directory_name);
+ g_free(directory_parent);
+ return;
+ }
+
+ /* The tooltips */
+ Tips = gtk_tooltips_new();
+
+ directory_name_utf8 = filename_to_display(directory_name);
+
+ RenameDirectoryWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(RenameDirectoryWindow),_("Rename the directory"));
+ gtk_window_set_transient_for(GTK_WINDOW(RenameDirectoryWindow),GTK_WINDOW(MainWindow));
+ gtk_window_set_position(GTK_WINDOW(RenameDirectoryWindow),GTK_WIN_POS_CENTER_ON_PARENT);
+
+ /* We attach usefull data to the combobox */
+ g_object_set_data(G_OBJECT(RenameDirectoryWindow), "Parent_Directory", directory_parent);
+ g_object_set_data(G_OBJECT(RenameDirectoryWindow), "Current_Directory", directory_name);
+
+ Frame = gtk_frame_new(NULL);
+ gtk_container_add(GTK_CONTAINER(RenameDirectoryWindow),Frame);
+ gtk_container_set_border_width(GTK_CONTAINER(Frame),2);
+
+ VBox = gtk_vbox_new(FALSE,4);
+ gtk_container_add(GTK_CONTAINER(Frame),VBox);
+ gtk_container_set_border_width(GTK_CONTAINER(VBox), 4);
+
+ string = g_strdup_printf(_("Rename the directory '%s' to : "),directory_name_utf8);
+ Label = gtk_label_new(_(string));
+ g_free(string);
+ gtk_box_pack_start(GTK_BOX(VBox),Label,FALSE,TRUE,0);
+ gtk_label_set_line_wrap(GTK_LABEL(Label),TRUE);
+
+ /* The combobox to rename the directory */
+ RenameDirectoryCombo = gtk_combo_box_entry_new_text();
+ gtk_box_pack_start(GTK_BOX(VBox),RenameDirectoryCombo,FALSE,FALSE,0);
+ /* Set the directory into the combobox */
+ gtk_combo_box_prepend_text(GTK_COMBO_BOX(RenameDirectoryCombo), directory_name_utf8);
+ gtk_combo_box_prepend_text(GTK_COMBO_BOX(RenameDirectoryCombo), "");
+ gtk_entry_set_text(GTK_ENTRY(GTK_BIN(RenameDirectoryCombo)->child),directory_name_utf8);
+ Attach_Popup_Menu_To_Tag_Entries(GTK_ENTRY(GTK_BIN(RenameDirectoryCombo)->child));
+
+ /* Rename directory : check box + combo box + Status icon */
+ HBox = gtk_hbox_new(FALSE,2);
+ gtk_box_pack_start(GTK_BOX(VBox),HBox,TRUE,TRUE,0);
+
+ RenameDirectoryWithMask = gtk_check_button_new_with_label(_("Use mask :"));
+ gtk_box_pack_start(GTK_BOX(HBox),RenameDirectoryWithMask,FALSE,FALSE,0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(RenameDirectoryWithMask),RENAME_DIRECTORY_WITH_MASK);
+ gtk_tooltips_set_tip(Tips,RenameDirectoryWithMask,_("If activated, it will use "
+ "masks to rename directory."),NULL);
+ g_signal_connect(G_OBJECT(RenameDirectoryWithMask),"toggled",G_CALLBACK(Rename_Directory_With_Mask_Toggled),NULL);
+
+ // Set up list model which is used by the combobox
+ /* Rename directory from mask */
+ if (!RenameDirectoryMaskModel)
+ RenameDirectoryMaskModel = gtk_list_store_new(MASK_EDITOR_COUNT, G_TYPE_STRING);
+ else
+ gtk_list_store_clear(RenameDirectoryMaskModel);
+
+ // The combo box to select the mask to apply
+ RenameDirectoryMaskCombo = gtk_combo_box_entry_new();
+ gtk_combo_box_set_model(GTK_COMBO_BOX(RenameDirectoryMaskCombo), GTK_TREE_MODEL(RenameDirectoryMaskModel));
+ gtk_combo_box_entry_set_text_column(GTK_COMBO_BOX_ENTRY(RenameDirectoryMaskCombo), MASK_EDITOR_TEXT);
+ gtk_widget_set_size_request(RenameDirectoryMaskCombo, 80, -1);
+
+ gtk_box_pack_start(GTK_BOX(HBox),RenameDirectoryMaskCombo,TRUE,TRUE,0);
+ gtk_tooltips_set_tip(Tips,GTK_WIDGET(GTK_ENTRY(GTK_BIN(RenameDirectoryMaskCombo)->child)),
+ _("Select or type in a mask using codes (see Legend in Scanner Window) to rename "
+ "the directory from tag fields."),NULL);
+ // Signal to generate preview (preview of the new directory)
+ g_signal_connect_swapped(G_OBJECT(GTK_ENTRY(GTK_BIN(RenameDirectoryMaskCombo)->child)),"changed",
+ G_CALLBACK(Scan_Rename_Directory_Generate_Preview),NULL);
+
+ // Load masks into the combobox from a file
+ Load_Rename_Directory_Masks_List(RenameDirectoryMaskModel, MASK_EDITOR_TEXT, Rename_Directory_Masks);
+ if (RENAME_DIRECTORY_DEFAULT_MASK)
+ {
+ Add_String_To_Combo_List(RenameDirectoryMaskModel, RENAME_DIRECTORY_DEFAULT_MASK);
+ gtk_entry_set_text(GTK_ENTRY(GTK_BIN(RenameDirectoryMaskCombo)->child), RENAME_DIRECTORY_DEFAULT_MASK);
+ }else
+ {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(RenameDirectoryMaskCombo), 0);
+ }
+
+ // Mask status icon
+ RenameDirectoryMaskStatusIconBox = Create_Pixmap_Icon_With_Event_Box("easytag-forbidden");
+ gtk_box_pack_start(GTK_BOX(HBox),RenameDirectoryMaskStatusIconBox,FALSE,FALSE,0);
+ gtk_tooltips_set_tip(Tips,RenameDirectoryMaskStatusIconBox,_("Invalid Scanner Mask"),NULL);
+ // Signal connection to check if mask is correct into the mask entry
+ g_signal_connect_swapped(G_OBJECT(GTK_ENTRY(GTK_BIN(RenameDirectoryMaskCombo)->child)),"changed",
+ G_CALLBACK(Scan_Check_Rename_File_Mask),G_OBJECT(RenameDirectoryMaskStatusIconBox));
+
+ // Preview label
+ RenameDirectoryPreviewLabel = gtk_label_new(_("Rename directory preview..."));
+ gtk_label_set_line_wrap(GTK_LABEL(RenameDirectoryPreviewLabel),TRUE);
+ ////gtk_widget_show(FillTagPreviewLabel);
+ gtk_box_pack_start(GTK_BOX(VBox),RenameDirectoryPreviewLabel,TRUE,TRUE,0);
+
+ /* Separator line */
+ Separator = gtk_hseparator_new();
+ gtk_box_pack_start(GTK_BOX(VBox),Separator,FALSE,FALSE,0);
+
+ ButtonBox = gtk_hbutton_box_new ();
+ gtk_box_pack_start(GTK_BOX(VBox),ButtonBox,FALSE,FALSE,0);
+ gtk_button_box_set_layout(GTK_BUTTON_BOX(ButtonBox),GTK_BUTTONBOX_END);
+ gtk_box_set_spacing(GTK_BOX(ButtonBox),10);
+
+ /* Button to cancel */
+ Button = Create_Button_With_Pixmap(BUTTON_CANCEL);
+ gtk_container_add(GTK_CONTAINER(ButtonBox),Button);
+ GTK_WIDGET_SET_FLAGS(Button,GTK_CAN_DEFAULT);
+ //gtk_widget_grab_default(Button);
+ g_signal_connect_swapped(G_OBJECT(Button),"clicked",G_CALLBACK(Destroy_Rename_Directory_Window), G_OBJECT(RenameDirectoryCombo));
+
+ /* Button to save: to rename directory */
+ Button = Create_Button_With_Pixmap(BUTTON_APPLY);
+ gtk_container_add(GTK_CONTAINER(ButtonBox),Button);
+ GTK_WIDGET_SET_FLAGS(Button,GTK_CAN_DEFAULT);
+ g_signal_connect_swapped(G_OBJECT(Button),"clicked", G_CALLBACK(Rename_Directory),NULL);
+ g_signal_connect_swapped(G_OBJECT(GTK_ENTRY(GTK_BIN(RenameDirectoryCombo)->child)),"changed",
+ G_CALLBACK(Entry_Changed_Disable_Object),G_OBJECT(Button));
+
+ g_signal_connect_swapped(G_OBJECT(RenameDirectoryWindow),"destroy", G_CALLBACK(Destroy_Rename_Directory_Window), NULL);
+ g_signal_connect_swapped(G_OBJECT(RenameDirectoryWindow),"delete_event", G_CALLBACK(Destroy_Rename_Directory_Window), NULL);
+ g_signal_connect(G_OBJECT(RenameDirectoryWindow),"key_press_event", G_CALLBACK(Rename_Directory_Window_Key_Press),NULL);
+ gtk_widget_show(RenameDirectoryWindow);
+
+ // Just center it over the main window
+ gtk_window_set_position(GTK_WINDOW(RenameDirectoryWindow), GTK_WIN_POS_CENTER_ON_PARENT);
+ gtk_window_set_policy(GTK_WINDOW(RenameDirectoryWindow),FALSE,FALSE,TRUE);
+ gtk_widget_set_size_request(GTK_WIDGET(RenameDirectoryWindow), 350, -1);
+
+ // To avoid/minimize 'flicker'
+ gtk_widget_show_all(RenameDirectoryWindow);
+
+ // To initialize the 'Use mask' check button state
+ g_signal_emit_by_name(G_OBJECT(RenameDirectoryWithMask),"toggled");
+
+ // To initialize PreviewLabel + MaskStatusIconBox
+ g_signal_emit_by_name(G_OBJECT(GTK_BIN(RenameDirectoryMaskCombo)->child),"changed");
+
+ g_free(directory_name_utf8);
+}
+
+void Destroy_Rename_Directory_Window (void)
+{
+ if (RenameDirectoryWindow)
+ {
+ g_free(g_object_get_data(G_OBJECT(RenameDirectoryWindow),"Parent_Directory"));
+ g_free(g_object_get_data(G_OBJECT(RenameDirectoryWindow),"Current_Directory"));
+
+ // Prevent recursion (double-freeing)
+ // We can't unblock after the destroy is complete, it must be done automatically
+ g_signal_handlers_block_by_func(RenameDirectoryWindow, Destroy_Rename_Directory_Window, NULL);
+
+ if (RENAME_DIRECTORY_DEFAULT_MASK) g_free(RENAME_DIRECTORY_DEFAULT_MASK);
+ RENAME_DIRECTORY_DEFAULT_MASK = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(RenameDirectoryMaskCombo)->child)));
+ Add_String_To_Combo_List(RenameDirectoryMaskModel, RENAME_DIRECTORY_DEFAULT_MASK);
+ Save_Rename_Directory_Masks_List(RenameDirectoryMaskModel, MASK_EDITOR_TEXT);
+
+ RENAME_DIRECTORY_WITH_MASK = GTK_TOGGLE_BUTTON(RenameDirectoryWithMask)->active;
+
+ gtk_list_store_clear(RenameDirectoryMaskModel);
+
+ gtk_widget_destroy(RenameDirectoryWindow);
+ RenameDirectoryWindow = (GtkWidget *)NULL;
+ }
+}
+
+void Rename_Directory (void)
+{
+ DIR *dir;
+ gchar *directory_parent;
+ gchar *directory_last_name;
+ gchar *directory_new_name;
+ gchar *directory_new_name_file;
+ gchar *last_path;
+ gchar *last_path_utf8;
+ gchar *new_path;
+ gchar *new_path_utf8;
+ gchar *tmp_path;
+ gchar *tmp_path_utf8;
+ gint fd_tmp;
+
+
+ if (!RenameDirectoryWindow)
+ return;
+
+ directory_parent = g_object_get_data(G_OBJECT(RenameDirectoryWindow),"Parent_Directory");
+ directory_last_name = g_object_get_data(G_OBJECT(RenameDirectoryWindow),"Current_Directory");
+
+ if (GTK_TOGGLE_BUTTON(RenameDirectoryWithMask)->active)
+ {
+ // Renamed from mask
+ gchar *mask = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(RenameDirectoryMaskCombo)->child)));
+ directory_new_name = Scan_Generate_New_Directory_Name_From_Mask(ETCore->ETFileDisplayed,mask,FALSE);
+ g_free(mask);
+
+ }else
+ {
+ // Renamed 'manually'
+ directory_new_name = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(RenameDirectoryCombo)->child)));
+ }
+
+ /* Check if a name for the directory have been supplied */
+ if (!directory_new_name || g_utf8_strlen(directory_new_name, -1) < 1)
+ {
+ GtkWidget *msgbox;
+
+ msgbox = msg_box_new(_("Error..."),_("You must type a directory name!"),
+ GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+ g_free(directory_new_name);
+ return;
+ }
+
+ /* Check that we can write the new directory name */
+ directory_new_name_file = filename_from_display(directory_new_name);
+ if (!directory_new_name_file)
+ {
+ GtkWidget *msgbox;
+
+ msgbox = msg_box_new(_("Error..."),_("Could not convert '%s' into filename encoding. Please use another name."),GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+ g_free(directory_new_name);
+ g_free(directory_new_name_file);
+ }
+
+ /* If the directory name haven't been changed, we do nothing! */
+ if (directory_last_name && directory_new_name_file
+ && strcmp(directory_last_name,directory_new_name_file)==0)
+ {
+ Destroy_Rename_Directory_Window();
+ g_free(directory_new_name);
+ g_free(directory_new_name_file);
+ return;
+ }
+
+ /* Build the current and new absolute paths */
+ last_path = g_strconcat(directory_parent, directory_last_name, NULL);
+ last_path_utf8 = filename_to_display(last_path);
+ new_path = g_strconcat(directory_parent, directory_new_name_file, NULL);
+ new_path_utf8 = filename_to_display(new_path);
+
+ /* Check if the new directory name doesn't already exists, and detect if
+ * it's only a case change (needed for vfat) */
+ if ( (dir=opendir(new_path))!=NULL )
+ {
+ gchar *msg;
+ GtkWidget *msgbox;
+ //gint button;
+
+ closedir(dir);
+ if (strcasecmp(last_path,new_path) != 0)
+ {
+ // TODO
+ // // The same directory already exists. So we ask if we want to move the files
+ // msg = g_strdup_printf(_("The directory already exists!\n(%s)\nDo you want "
+ // "to move the files?"),new_path_utf8);
+ // msgbox = msg_box_new(_("Confirm..."),msg,GTK_STOCK_DIALOG_QUESTION,
+ // BUTTON_NO,BUTTON_YES,0);
+ // g_free(msg);
+ // msg_box_hide_check_button(MSG_BOX(msgbox));
+ // button = msg_box_run(MSG_BOX(msgbox));
+ // gtk_widget_destroy(msgbox);
+ //
+ // switch (button)
+ // {
+ // case BUTTON_YES:
+ // // Here we must rename all files with the new location, and remove the directory
+ //
+ // Rename_File ()
+ //
+ // break;
+ // case BUTTON_NO:
+ // break;
+ // }
+
+ msg = g_strdup_printf(_("Can't rename because this directory name "
+ "already exists!\n(%s)"),new_path_utf8);
+ msgbox = msg_box_new(_("Error..."),msg,GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0);
+ g_free(msg);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+
+ g_free(directory_new_name);
+ g_free(directory_new_name_file);
+ g_free(last_path);
+ g_free(last_path_utf8);
+ g_free(new_path);
+ g_free(new_path_utf8);
+
+ return;
+ }
+ }
+
+ /* Temporary path (useful when changing only string case) */
+ tmp_path = g_strdup_printf("%s.XXXXXX",last_path);
+ tmp_path_utf8 = filename_to_display(tmp_path);
+
+ if ( (fd_tmp = mkstemp(tmp_path)) >= 0 )
+ {
+ close(fd_tmp);
+ unlink(tmp_path);
+ }
+
+ /* Rename the directory from 'last name' to 'tmp name' */
+ if ( rename(last_path,tmp_path)!=0 )
+ {
+ gchar *msg;
+ GtkWidget *msgbox;
+
+ msg = g_strdup_printf(_("Can't rename directory \n'%s'\n to \n'%s'!\n(%s)"),
+ last_path_utf8,tmp_path_utf8,g_strerror(errno));
+ msgbox = msg_box_new(_("Error..."),msg,GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0);
+ g_free(msg);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+
+ g_free(directory_new_name);
+ g_free(directory_new_name_file);
+ g_free(last_path);
+ g_free(last_path_utf8);
+ g_free(new_path);
+ g_free(new_path_utf8);
+ g_free(tmp_path);
+ g_free(tmp_path_utf8);
+
+ return;
+ }
+
+ /* Rename the directory from 'tmp name' to 'new name' (final name) */
+ if ( rename(tmp_path,new_path)!=0 )
+ {
+ gchar *msg;
+ GtkWidget *msgbox;
+
+ msg = g_strdup_printf(_("Can't rename directory \n'%s'\n to \n'%s'!\n(%s)"),
+ tmp_path_utf8,new_path_utf8,g_strerror(errno));
+ msgbox = msg_box_new(_("Error..."),msg,GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0);
+ g_free(msg);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+
+ g_free(directory_new_name);
+ g_free(directory_new_name_file);
+ g_free(last_path);
+ g_free(last_path_utf8);
+ g_free(new_path);
+ g_free(new_path_utf8);
+ g_free(tmp_path);
+ g_free(tmp_path_utf8);
+
+ return;
+ }
+
+ ET_Update_Directory_Name_Into_File_List(last_path,new_path);
+ Browser_Tree_Rename_Directory(last_path,new_path);
+
+ // To update file path in the browser entry
+ if (ETCore->ETFileDisplayedList)
+ {
+ ET_Display_File_Data_To_UI(ETCore->ETFileDisplayed);
+ }else
+ {
+ gchar *tmp = filename_to_display(Browser_Get_Current_Path());
+ Browser_Entry_Set_Text(tmp);
+ g_free(tmp);
+ }
+
+ Destroy_Rename_Directory_Window();
+ g_free(last_path);
+ g_free(last_path_utf8);
+ g_free(new_path);
+ g_free(new_path_utf8);
+ g_free(tmp_path);
+ g_free(tmp_path_utf8);
+ g_free(directory_new_name);
+ g_free(directory_new_name_file);
+ Statusbar_Message(_("Directory renamed"),TRUE);
+}
+
+gboolean Rename_Directory_Window_Key_Press (GtkWidget *window, GdkEvent *event)
+{
+ GdkEventKey *kevent;
+
+ if (event && event->type == GDK_KEY_PRESS)
+ {
+ kevent = (GdkEventKey *)event;
+ switch(kevent->keyval)
+ {
+ case GDK_Escape:
+ // Destroy_Rename_Directory_Window();
+ g_signal_emit_by_name(window, "destroy");
+ break;
+ }
+ }
+ return FALSE;
+}
+
+void Rename_Directory_With_Mask_Toggled (void)
+{
+ gtk_widget_set_sensitive(GTK_WIDGET(RenameDirectoryCombo), !GTK_TOGGLE_BUTTON(RenameDirectoryWithMask)->active);
+ gtk_widget_set_sensitive(GTK_WIDGET(RenameDirectoryMaskCombo), GTK_TOGGLE_BUTTON(RenameDirectoryWithMask)->active);
+ gtk_widget_set_sensitive(GTK_WIDGET(RenameDirectoryMaskStatusIconBox), GTK_TOGGLE_BUTTON(RenameDirectoryWithMask)->active);
+ gtk_widget_set_sensitive(GTK_WIDGET(RenameDirectoryPreviewLabel), GTK_TOGGLE_BUTTON(RenameDirectoryWithMask)->active);
+}
+
+
+/*
+ * Window where is typed the name of the program to run, which
+ * receives the current directory as parameter.
+ */
+void Browser_Open_Run_Program_Tree_Window (void)
+{
+ GtkWidget *Frame;
+ GtkWidget *VBox;
+ GtkWidget *HBox;
+ GtkWidget *Label;
+ GtkWidget *RunProgramComboBox;
+ GtkWidget *ButtonBox;
+ GtkWidget *Button;
+ GtkWidget *Separator;
+ GtkTooltips *Tips;
+ gchar *current_directory = NULL;
+
+ if (RunProgramTreeWindow != NULL)
+ {
+ gdk_window_raise(RunProgramTreeWindow->window);
+ return;
+ }
+
+ // Current directory
+ current_directory = g_strdup(BrowserCurrentPath);
+ if (!current_directory || strlen(current_directory)==0)
+ return;
+
+ RunProgramTreeWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(RunProgramTreeWindow),_("Browse Directory with ..."));
+ gtk_window_set_transient_for(GTK_WINDOW(RunProgramTreeWindow),GTK_WINDOW(MainWindow));
+ g_signal_connect(G_OBJECT(RunProgramTreeWindow),"destroy", G_CALLBACK(Destroy_Run_Program_Tree_Window),NULL);
+ g_signal_connect(G_OBJECT(RunProgramTreeWindow),"delete_event", G_CALLBACK(Destroy_Run_Program_Tree_Window),NULL);
+ g_signal_connect(G_OBJECT(RunProgramTreeWindow),"key_press_event", G_CALLBACK(Run_Program_Tree_Window_Key_Press),NULL);
+
+ // Just center it over mainwindow
+ gtk_window_set_position(GTK_WINDOW(RunProgramTreeWindow), GTK_WIN_POS_CENTER_ON_PARENT);
+
+ Tips = gtk_tooltips_new();
+
+ Frame = gtk_frame_new(NULL);
+ gtk_container_add(GTK_CONTAINER(RunProgramTreeWindow),Frame);
+ gtk_container_set_border_width(GTK_CONTAINER(Frame),2);
+
+ VBox = gtk_vbox_new(FALSE,4);
+ gtk_container_add(GTK_CONTAINER(Frame),VBox);
+ gtk_container_set_border_width(GTK_CONTAINER(VBox), 4);
+
+ Label = gtk_label_new(_("Program to run :"));
+ gtk_box_pack_start(GTK_BOX(VBox),Label,TRUE,FALSE,0);
+ gtk_label_set_line_wrap(GTK_LABEL(Label),TRUE);
+
+ HBox = gtk_hbox_new(FALSE,4);
+ gtk_box_pack_start(GTK_BOX(VBox),HBox,FALSE,FALSE,2);
+ gtk_container_set_border_width(GTK_CONTAINER(HBox), 2);
+
+ /* The combobox to enter the program to run */
+ RunProgramComboBox = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(RunProgramModel), MISC_COMBO_TEXT);
+ gtk_box_pack_start(GTK_BOX(HBox),RunProgramComboBox,TRUE,TRUE,0);
+ gtk_widget_set_size_request(GTK_WIDGET(RunProgramComboBox),250,-1);
+ gtk_tooltips_set_tip(Tips,GTK_WIDGET(GTK_ENTRY(GTK_BIN(RunProgramComboBox)->child)),_("Enter the program to run. "
+ "It will receive the current directory as parameter."),NULL);
+
+ /* History list */
+ gtk_list_store_clear(RunProgramModel);
+ Load_Run_Program_With_Directory_List(RunProgramModel, MISC_COMBO_TEXT);
+ g_signal_connect_swapped(G_OBJECT(GTK_ENTRY(GTK_BIN(RunProgramComboBox)->child)),"activate",
+ G_CALLBACK(Run_Program_With_Directory),G_OBJECT(RunProgramComboBox));
+
+ /* The button to Browse */
+ Button = Create_Button_With_Pixmap(BUTTON_BROWSE);
+ gtk_box_pack_start(GTK_BOX(HBox),Button,FALSE,FALSE,0);
+ g_signal_connect_swapped(G_OBJECT(Button),"clicked",
+ G_CALLBACK(File_Selection_Window_For_File),G_OBJECT(GTK_BIN(RunProgramComboBox)->child));
+
+ /* We attach usefull data to the combobox (into Run_Program_With_Directory) */
+ g_object_set_data(G_OBJECT(RunProgramComboBox), "Current_Directory", current_directory);
+
+ /* Separator line */
+ Separator = gtk_hseparator_new();
+ gtk_box_pack_start(GTK_BOX(VBox),Separator,FALSE,FALSE,0);
+
+ ButtonBox = gtk_hbutton_box_new ();
+ gtk_box_pack_start(GTK_BOX(VBox),ButtonBox,FALSE,FALSE,0);
+ gtk_button_box_set_layout(GTK_BUTTON_BOX(ButtonBox),GTK_BUTTONBOX_END);
+ gtk_box_set_spacing(GTK_BOX(ButtonBox), 10);
+
+ /* Button to cancel */
+ Button = Create_Button_With_Pixmap(BUTTON_CANCEL);
+ gtk_container_add(GTK_CONTAINER(ButtonBox),Button);
+ GTK_WIDGET_SET_FLAGS(Button,GTK_CAN_DEFAULT);
+ gtk_widget_grab_default(Button);
+ g_signal_connect(G_OBJECT(Button),"clicked", G_CALLBACK(Destroy_Run_Program_Tree_Window),NULL);
+
+ /* Button to execute */
+ Button = Create_Button_With_Pixmap(BUTTON_EXECUTE);
+ gtk_container_add(GTK_CONTAINER(ButtonBox),Button);
+ GTK_WIDGET_SET_FLAGS(Button,GTK_CAN_DEFAULT);
+ g_signal_connect_swapped(G_OBJECT(Button),"clicked", G_CALLBACK(Run_Program_With_Directory),G_OBJECT(RunProgramComboBox));
+ g_signal_connect_swapped(G_OBJECT(GTK_ENTRY(GTK_BIN(RunProgramComboBox)->child)),"changed", G_CALLBACK(Entry_Changed_Disable_Object),G_OBJECT(Button));
+ g_signal_emit_by_name(G_OBJECT(GTK_ENTRY(GTK_BIN(RunProgramComboBox)->child)),"changed",NULL);
+
+ gtk_widget_show_all(RunProgramTreeWindow);
+}
+
+void Destroy_Run_Program_Tree_Window (void)
+{
+ if (RunProgramTreeWindow)
+ {
+ gtk_widget_destroy(RunProgramTreeWindow);
+ RunProgramTreeWindow = (GtkWidget *)NULL;
+ }
+}
+
+gboolean Run_Program_Tree_Window_Key_Press (GtkWidget *window, GdkEvent *event)
+{
+ GdkEventKey *kevent;
+
+ if (event && event->type == GDK_KEY_PRESS)
+ {
+ kevent = (GdkEventKey *)event;
+ switch(kevent->keyval)
+ {
+ case GDK_Escape:
+ Destroy_Run_Program_Tree_Window();
+ break;
+ }
+ }
+ return FALSE;
+}
+
+void Run_Program_With_Directory (GtkObject *combobox)
+{
+ gchar *program_name;
+ gchar *current_directory;
+ GList *args_list = NULL;
+ gboolean program_ran;
+
+ if (!GTK_IS_COMBO_BOX(combobox)) return;
+
+ program_name = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(combobox)->child)));
+ current_directory = g_object_get_data(G_OBJECT(combobox), "Current_Directory");
+#ifdef WIN32
+ /* On win32 : 'winamp.exe "c:\path\to\dir"' succeed, while 'winamp.exe "c:\path\to\dir\"' fails */
+ ET_Win32_Path_Remove_Trailing_Backslash(current_directory);
+#endif
+
+ // List of parameters (here only one! : the current directory)
+ args_list = g_list_append(args_list,current_directory);
+
+ program_ran = Run_Program(program_name,args_list);
+ g_list_free(args_list);
+
+ if (program_ran)
+ {
+ // Append newest choice to the drop down list
+ Add_String_To_Combo_List(RunProgramModel, program_name);
+
+ // Save list attached to the combobox
+ Save_Run_Program_With_Directory_List(RunProgramModel, MISC_COMBO_TEXT);
+
+ Destroy_Run_Program_Tree_Window();
+ }
+ g_free(program_name);
+}
+
+/*
+ * Window where is typed the name of the program to run, which
+ * receives the current file as parameter.
+ */
+void Browser_Open_Run_Program_List_Window (void)
+{
+ GtkWidget *Frame;
+ GtkWidget *VBox;
+ GtkWidget *HBox;
+ GtkWidget *Label;
+ GtkWidget *RunProgramComboBox;
+ GtkWidget *ButtonBox;
+ GtkWidget *Button;
+ GtkWidget *Separator;
+ GtkTooltips *Tips;
+
+ if (RunProgramListWindow != NULL)
+ {
+ gdk_window_raise(RunProgramListWindow->window);
+ return;
+ }
+
+ RunProgramListWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(RunProgramListWindow),_("Open File with ..."));
+ gtk_window_set_transient_for(GTK_WINDOW(RunProgramListWindow),GTK_WINDOW(MainWindow));
+ g_signal_connect(G_OBJECT(RunProgramListWindow),"destroy", G_CALLBACK(Destroy_Run_Program_List_Window),NULL);
+ g_signal_connect(G_OBJECT(RunProgramListWindow),"delete_event", G_CALLBACK(Destroy_Run_Program_List_Window),NULL);
+ g_signal_connect(G_OBJECT(RunProgramListWindow),"key_press_event", G_CALLBACK(Run_Program_List_Window_Key_Press),NULL);
+
+ // Just center over mainwindow
+ gtk_window_set_position(GTK_WINDOW(RunProgramListWindow),GTK_WIN_POS_CENTER_ON_PARENT);
+
+ Tips = gtk_tooltips_new();
+
+ Frame = gtk_frame_new(NULL);
+ gtk_container_add(GTK_CONTAINER(RunProgramListWindow),Frame);
+ gtk_container_set_border_width(GTK_CONTAINER(Frame),2);
+
+ VBox = gtk_vbox_new(FALSE,4);
+ gtk_container_add(GTK_CONTAINER(Frame),VBox);
+ gtk_container_set_border_width(GTK_CONTAINER(VBox), 4);
+
+ Label = gtk_label_new(_("Program to run :"));
+ gtk_box_pack_start(GTK_BOX(VBox),Label,TRUE,TRUE,0);
+ gtk_label_set_line_wrap(GTK_LABEL(Label),TRUE);
+
+ HBox = gtk_hbox_new(FALSE,4);
+ gtk_box_pack_start(GTK_BOX(VBox),HBox,FALSE,FALSE,2);
+ gtk_container_set_border_width(GTK_CONTAINER(HBox), 2);
+
+ /* The combobox to enter the program to run */
+ RunProgramComboBox = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(RunProgramModel), MISC_COMBO_TEXT);
+ gtk_box_pack_start(GTK_BOX(HBox),RunProgramComboBox,TRUE,TRUE,0);
+ gtk_widget_set_size_request(GTK_WIDGET(RunProgramComboBox),250,-1);
+ gtk_tooltips_set_tip(Tips,GTK_WIDGET(GTK_ENTRY(GTK_BIN(RunProgramComboBox)->child)),_("Enter the program to run. "
+ "It will receive the current file as parameter."),NULL);
+
+ /* History list */
+ gtk_list_store_clear(RunProgramModel);
+ Load_Run_Program_With_File_List(RunProgramModel, MISC_COMBO_TEXT);
+ g_signal_connect_swapped(G_OBJECT(GTK_ENTRY(GTK_BIN(RunProgramComboBox)->child)),"activate",
+ G_CALLBACK(Run_Program_With_Selected_Files),G_OBJECT(RunProgramComboBox));
+
+ /* The button to Browse */
+ Button = Create_Button_With_Pixmap(BUTTON_BROWSE);
+ gtk_box_pack_start(GTK_BOX(HBox),Button,FALSE,FALSE,0);
+ g_signal_connect_swapped(G_OBJECT(Button),"clicked",
+ G_CALLBACK(File_Selection_Window_For_File),G_OBJECT(GTK_BIN(RunProgramComboBox)->child));
+
+ /* We attach usefull data to the combobox (into Run_Program_With_Directory) */
+ //g_object_set_data(G_OBJECT(Combo), "Current_File", current_file);
+
+ /* Separator line */
+ Separator = gtk_hseparator_new();
+ gtk_box_pack_start(GTK_BOX(VBox),Separator,FALSE,FALSE,0);
+
+ ButtonBox = gtk_hbutton_box_new ();
+ gtk_box_pack_start(GTK_BOX(VBox),ButtonBox,FALSE,FALSE,0);
+ gtk_button_box_set_layout(GTK_BUTTON_BOX(ButtonBox),GTK_BUTTONBOX_END);
+ gtk_box_set_spacing(GTK_BOX(ButtonBox), 10);
+
+ /* Button to cancel */
+ Button = Create_Button_With_Pixmap(BUTTON_CANCEL);
+ gtk_container_add(GTK_CONTAINER(ButtonBox),Button);
+ GTK_WIDGET_SET_FLAGS(Button,GTK_CAN_DEFAULT);
+ gtk_widget_grab_default(Button);
+ g_signal_connect(G_OBJECT(Button),"clicked", G_CALLBACK(Destroy_Run_Program_List_Window),NULL);
+
+ /* Button to execute */
+ Button = Create_Button_With_Pixmap(BUTTON_EXECUTE);
+ gtk_container_add(GTK_CONTAINER(ButtonBox),Button);
+ GTK_WIDGET_SET_FLAGS(Button,GTK_CAN_DEFAULT);
+ g_signal_connect_swapped(G_OBJECT(Button),"clicked", G_CALLBACK(Run_Program_With_Selected_Files),G_OBJECT(RunProgramComboBox));
+ g_signal_connect_swapped(G_OBJECT(GTK_ENTRY(GTK_BIN(RunProgramComboBox)->child)),"changed", G_CALLBACK(Entry_Changed_Disable_Object),G_OBJECT(Button));
+ g_signal_emit_by_name(G_OBJECT(GTK_ENTRY(GTK_BIN(RunProgramComboBox)->child)),"changed",NULL);
+
+ gtk_widget_show_all(RunProgramListWindow);
+}
+
+void Destroy_Run_Program_List_Window (void)
+{
+ if (RunProgramListWindow)
+ {
+ gtk_widget_destroy(RunProgramListWindow);
+ RunProgramListWindow = (GtkWidget *)NULL;
+ }
+}
+
+gboolean Run_Program_List_Window_Key_Press(GtkWidget *window, GdkEvent *event)
+{
+ GdkEventKey *kevent;
+
+ if (event && event->type == GDK_KEY_PRESS)
+ {
+ kevent = (GdkEventKey *)event;
+ switch(kevent->keyval)
+ {
+ case GDK_Escape:
+ Destroy_Run_Program_List_Window();
+ break;
+ }
+ }
+ return FALSE;
+}
+
+void Run_Program_With_Selected_Files (GtkObject *combobox)
+{
+ gchar *program_name;
+ ET_File *ETFile;
+ GList *selected_paths;
+ GList *args_list = NULL;
+ GtkTreeIter iter;
+ gboolean program_ran;
+ gboolean valid;
+
+ if (!GTK_IS_COMBO_BOX(combobox) || !ETCore->ETFileDisplayedList)
+ return;
+
+ // Programe name to run
+ program_name = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(combobox)->child)));
+
+ // List of files to pass as parameters
+ selected_paths = gtk_tree_selection_get_selected_rows(gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList)), NULL);
+ while (selected_paths)
+ {
+ valid = gtk_tree_model_get_iter(GTK_TREE_MODEL(fileListModel), &iter, (GtkTreePath*)selected_paths->data);
+ if (valid)
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(fileListModel), &iter,
+ LIST_FILE_POINTER, &ETFile,
+ -1);
+
+ args_list = g_list_append(args_list,((File_Name *)ETFile->FileNameCur->data)->value);
+ //args_list = g_list_append(args_list,((File_Name *)ETFile->FileNameCur->data)->value_utf8);
+ }
+
+ if (!selected_paths->next) break;
+ selected_paths = selected_paths->next;
+ }
+
+ program_ran = Run_Program(program_name,args_list);
+
+ g_list_foreach(selected_paths, (GFunc)gtk_tree_path_free, NULL);
+ g_list_free(selected_paths);
+ g_list_free(args_list);
+
+ if (program_ran)
+ {
+ // Append newest choice to the drop down list
+ //gtk_list_store_prepend(GTK_LIST_STORE(RunProgramModel), &iter);
+ //gtk_list_store_set(RunProgramModel, &iter, MISC_COMBO_TEXT, program_name, -1);
+ Add_String_To_Combo_List(GTK_LIST_STORE(RunProgramModel), (gchar *)program_name);
+
+ // Save list attached to the combobox
+ Save_Run_Program_With_File_List(RunProgramModel, MISC_COMBO_TEXT);
+
+ Destroy_Run_Program_List_Window();
+ }
+ g_free(program_name);
+}
+
+/*
+ * Run a program with a list of parameters
+ * - args_list : list of filename (with path)
+ */
+gboolean Run_Program (gchar *program_name, GList *args_list)
+{
+#ifdef WIN32
+ GList *filelist;
+ gchar **argv;
+ gint argv_index = 0;
+ gchar *argv_join;
+ gchar *full_command;
+ STARTUPINFO siStartupInfo;
+ PROCESS_INFORMATION piProcessInfo;
+#else
+ pid_t pid;
+#endif
+ gchar *msg;
+ gchar *program_path;
+
+
+ /* Check if a name for the program have been supplied */
+ if (!program_name || strlen(program_name)<1)
+ {
+ GtkWidget *msgbox;
+
+ msgbox = msg_box_new(_("Error..."),_("You must type a program name!"),GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+ return FALSE;
+ }
+
+ if ( !(program_path = Check_If_Executable_Exists(program_name)) )
+ {
+ GtkWidget *msgbox;
+ gchar *msg;
+
+ msg = g_strdup_printf(_("The program '%s' can't be found!"),program_name);
+ msgbox = msg_box_new(_("Error..."),msg,GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0);
+ msg_box_hide_check_button(MSG_BOX(msgbox));
+ msg_box_run(MSG_BOX(msgbox));
+ gtk_widget_destroy(msgbox);
+ g_free(msg);
+ return FALSE;
+ }
+
+
+#ifdef WIN32
+
+ filelist = args_list;
+
+ // See documentation : http://c.developpez.com/faq/vc/?page=ProcessThread and http://www.answers.com/topic/createprocess
+ ZeroMemory(&siStartupInfo, sizeof(siStartupInfo));
+ siStartupInfo.cb = sizeof(siStartupInfo);
+ ZeroMemory(&piProcessInfo, sizeof(piProcessInfo));
+
+ argv = g_new0(gchar *,g_list_length(filelist) + 2); // "+2" for 1rst arg 'foo' and last arg 'NULL'
+ //argv[argv_index++] = "foo";
+
+ // Load files as arguments
+ while (filelist)
+ {
+ // We must enclose filename between " because of possible (probable!) spaces in filenames"
+ argv[argv_index++] = g_strconcat("\"", (gchar *)filelist->data, "\"", NULL);
+ filelist = filelist->next;
+ }
+ argv[argv_index] = NULL; // Ends the list of arguments
+
+ // Make a command line with all arguments (joins strings together to form one long string separated by a space)
+ argv_join = g_strjoinv(" ", argv);
+ // Build the full command to pass to CreateProcess (FIX ME : it will ignore args of program)
+ full_command = g_strconcat("\"",program_path,"\" ",argv_join,NULL);
+
+ //if (CreateProcess(program_path, // Here it doesn't seem to load all the selected files
+ // argv_join,
+ if (CreateProcess(NULL,
+ full_command,
+ NULL,
+ NULL,
+ FALSE,
+ CREATE_DEFAULT_ERROR_MODE,
+ NULL,
+ NULL,
+ &siStartupInfo,
+ &piProcessInfo) == FALSE)
+ {
+ Log_Print(_("Can't execute %s (error %d)!\n"), program_name, GetLastError());
+ }
+
+ // Free allocated parameters (for each filename)
+ for (argv_index = 1; argv[argv_index]; argv_index++)
+ g_free(argv[argv_index]);
+
+ g_free(argv_join);
+ g_free(full_command);
+ g_free(program_path);
+
+#else
+
+ g_free(program_path); // Freed as never used
+
+ pid = fork();
+ switch (pid)
+ {
+ case -1:
+ Log_Print(_("Can't fork another process!\n"));
+ //exit(-1);
+ break;
+ case 0:
+ {
+ gchar **argv;
+ gint argv_index = 0;
+ gchar **argv_user;
+ gint argv_user_number;
+
+ argv_user = g_strsplit(program_name," ",0); // the string may contains arguments, space is the delimiter
+ // Number of arguments into 'argv_user'
+ for (argv_user_number=0;argv_user[argv_user_number];argv_user_number++);
+
+ argv = g_new0(gchar *,argv_user_number + g_list_length(args_list) + 1); // +1 for NULL
+
+ // Load 'user' arguments (program name and more...)
+ while (argv_user[argv_index])
+ {
+ argv[argv_index] = argv_user[argv_index];
+ argv_index++;
+ }
+ // Load arguments from 'args_list'
+ while (args_list)
+ {
+ argv[argv_index] = (gchar *)args_list->data;
+ argv_index++;
+ args_list = args_list->next;
+ }
+ argv[argv_index] = NULL;
+
+ // Execution ...
+ execvp(argv[0],argv);
+
+ msg = g_strdup_printf(_("Executed command : '%s %s'"),program_name,"...");
+ Statusbar_Message(msg,TRUE);
+ g_free(msg);
+ //_exit(-1);
+ break;
+ }
+ default:
+ break;
+ }
+ return TRUE;
+
+#endif
+}