Logo Search packages:      
Sourcecode: easytag version File versions  Download package

browser.c

/* 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 "misc.h"
#include "setting.h"
#include "charset.h"
#include "dlm.h"

#include <assert.h>

#ifdef WIN32
#   include <windows.h>
#   include <win32dep.h>
#endif

/* Pixmaps */
#include "../pixmaps/opened_folder.xpm"
#include "../pixmaps/closed_folder.xpm"
#include "../pixmaps/closed_folder_locked.xpm"



/****************
 * 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;
#endif

static 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 Directpry 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;


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 (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);

    if (strcmp("/",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
        gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserTree)));
        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]=='/' )
            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;
    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);

            gtk_tree_model_get_iter(GTK_TREE_MODEL(fileListModel), &currentIter, currentPath);
            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;

        // 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)
        {
            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 )
            {
                Browser_List_Select_File_By_Etfile((ET_File *)etfilelist->data,TRUE);
            }
            etfilelist = g_list_next(etfilelist);
            g_free(patch_check);
        }
        g_free(path_ref);
    }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 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("/path/to/dir") succeed, while stat("/path/to/dir/") fails */
    ET_Win32_Path_Remove_Trailing_Slash(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, "/", 0);

    // Expand root node
#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
    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);
        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);
    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)
        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 = ((File_Name *)((ET_File *)etfilelist->data)->FileNameCur->data)->value;
        gchar *current_filename_utf8 = filename_to_display(current_filename);
        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 = ((File_Name *)((ET_File *)etfilelist->prev->data)->FileNameCur->data)->value;
            gchar *previous_filename_utf8 = filename_to_display(previous_filename);

            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);
            g_free(previous_filename_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(current_filename_utf8);
        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_filename;
    gchar *current_filename_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_filename = g_path_get_basename( ((File_Name *)ETFile->FileNameCur->data)->value );
        current_filename_utf8 = filename_to_display(current_filename);
        gtk_list_store_set(fileListModel, &iter, LIST_FILE_NAME, current_filename_utf8, -1);
        g_free(current_filename);
        g_free(current_filename_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_filename;
    gchar   *current_filename_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");

    // Found the row of the modified file (when found: row_found=TRUE)
    // 1/3. Try with the selected file in list
    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList));
    selectedRow = gtk_tree_selection_get_selected_rows(selection, NULL);
    if (selectedRow && selectedRow->data != NULL)
    {
        gtk_tree_model_get_iter(GTK_TREE_MODEL(fileListModel), &selectedIter,
                                (GtkTreePath*) selectedRow->data);
        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);

    // 2bis. We should add an other stage faster than 3/3:
    // Get position of ETFile in ETFileList : row=ETFileList_Length - g_list_length(ETFile in list)

    // 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;

    current_filename = g_path_get_basename( ((File_Name *)file->FileNameCur->data)->value );
    current_filename_utf8 = filename_to_display(current_filename);
    gtk_list_store_set(fileListModel, &selectedIter, LIST_FILE_NAME, current_filename_utf8, -1);
    g_free(current_filename);
    g_free(current_filename_utf8);

    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);

            gtk_tree_model_get_iter(GTK_TREE_MODEL(artistListModel), &selectedIter, currentPath);
            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);

            gtk_tree_model_get_iter(GTK_TREE_MODEL(albumListModel), &selectedIter, currentPath);
            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;

    // 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 (iter == NULL)
        return;

    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;

    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);

        gtk_tree_model_get_iter(GTK_TREE_MODEL(fileListModel), &currentIter, currentPath);
        gtk_tree_model_get(GTK_TREE_MODEL(fileListModel), &currentIter, LIST_FILE_POINTER, &currentETFile, -1);

        if (currentETFile == searchETFile)
        {
            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)
{
    gint row;
    GtkTreePath *currentPath = NULL;
    GtkTreeIter currentIter;
    ET_File *currentETFile;

     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);

        gtk_tree_model_get_iter(GTK_TREE_MODEL(fileListModel), &currentIter, currentPath);
        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);
            break;
        }
    }
    gtk_tree_path_free(currentPath);
}

/*
 * 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));
        
        g_signal_handlers_block_by_func(G_OBJECT(selection),G_CALLBACK(Browser_List_Row_Selected),NULL);
        gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList)), 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...
            //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));

            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;

    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);

        gtk_tree_model_get_iter(GTK_TREE_MODEL(fileListModel), &currentIter, currentPath);
        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));
    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));
    // 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(gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList)));
    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)
{
    if (!BrowserList) return;

    gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(GTK_TREE_VIEW(BrowserList)));
}

/*
 * 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));
    /* 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;
    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));

    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;

    if (!gtk_tree_selection_get_selected(selection, NULL, &iter))
        return; /* We might be called with no rows selected */

    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);

            //if (drive_type != DRIVE_CDROM && drive_type != DRIVE_REMOTE )
            //{
                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;
                    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,
                           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, "/",
                       TREE_COLUMN_FULL_PATH, "/",
                       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;

    /* 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 */
    // FIX ME : it seems to not block the event...!
    g_signal_handlers_block_by_func(G_OBJECT(BrowserTree),G_CALLBACK(Browser_Tree_Node_Selected),NULL);
    Browser_Tree_Select_Dir(current_path);
    g_signal_handlers_unblock_by_func(G_OBJECT(BrowserTree),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;

    if (!last_path || !new_path)
        return;

    /*
     * Find the existing tree entry
     */
    textsplit = g_strsplit(last_path, "/", 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++)
    {
        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]);
            g_print(_("Error: Searching for %s, could not find node %s in tree.\n"), last_path, text_utf8);
            g_strfreev(textsplit);
            g_free(text_utf8);
            return;
        }
        gtk_tree_path_free(parentpath);
        parentpath = childpath;
    }

    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,"/",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)) ) )
            {

                fullpath_file = g_strconcat(path,"/",NULL);
                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 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_1();
    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 tot go to the parent directory
     */
    BrowserButton = gtk_button_new();
    Icon = gtk_image_new_from_stock("easytag-parent-folder", GTK_ICON_SIZE_BUTTON);
    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);
#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]=='/')
        directory_parent[strlen(directory_parent)-1]=0;
    // Get name of the directory to rename (without path)
    address = strrchr(directory_parent,'/');
    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_1();

    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;

        closedir(dir);
        if (strcasecmp(last_path,new_path) != 0)
        {
            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 *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_1();

    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);

    /* 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(VBox),RunProgramComboBox,TRUE,TRUE,0);
    gtk_widget_set_size_request(GTK_WIDGET(RunProgramComboBox),150,-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));

    /* 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");

    // 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 *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_1();

    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);

    /* 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(VBox),RunProgramComboBox,TRUE,TRUE,0);
    gtk_widget_set_size_request(GTK_WIDGET(RunProgramComboBox),150,-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));

    /* 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;

    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)
    {
        gtk_tree_model_get_iter(GTK_TREE_MODEL(fileListModel), &iter, (GtkTreePath*)selected_paths->data);
        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);

        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
 */
gboolean Run_Program (gchar *program_name, GList *args_list)
{
#ifdef WIN32

    return FALSE;

#else

    gchar *msg;
    pid_t pid;


    /* 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 ( !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;
    }
    
    pid = fork();
    switch(pid)
    {
        case -1:
            g_warning(_("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
}

Generated by  Doxygen 1.6.0   Back to index