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

scan.c

/* scan.c - 2000/06/16 */
/*
 *  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.
 */

#include <config.h>

#include <gtk/gtk.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <gdk/gdkkeysyms.h>
#include <config.h>
#include <glib/gi18n-lib.h>

#include "scan.h"
#include "easytag.h"
#include "prefs.h"
#include "setting.h"
#include "id3_tag.h"
#include "bar.h"
#include "browser.h"
#include "misc.h"
#include "et_core.h"
#include "crc32.h"
#include "msgbox.h"
#include "charset.h"


#define step(a,b) (b-a)+1

/****************
 * Declarations *
 ****************/
GtkWidget *DummyEntry = NULL;    /* Used to simulate a gtkentry widget for mask code '%i' */
GtkWidget *ScanTagMaskCombo    = NULL;
GtkWidget *RenameFileMaskCombo = NULL;
GtkWidget *ScannerOptionCombo  = NULL;
GtkWidget *RenameFilePrefixPathButton = NULL;

GtkWidget *ScanTagFrame;
GtkWidget *RenameFileFrame;
GtkWidget *ProcessFieldsFrame;
GtkWidget *FillTagPreviewLabel        = NULL;
GtkWidget *RenameFilePreviewLabel     = NULL;

GtkListStore *RenameFileListModel;
GtkListStore *ScanTagListModel;

GtkWidget *ProcessFileNameField;
GtkWidget *ProcessTitleField;
GtkWidget *ProcessArtistField;
GtkWidget *ProcessAlbumField;
GtkWidget *ProcessGenreField;
GtkWidget *ProcessCommentField;
GtkWidget *ProcessComposerField;
GtkWidget *ProcessOrigArtistField;
GtkWidget *ProcessCopyrightField;
GtkWidget *ProcessURLField;
GtkWidget *ProcessEncodedByField;
GtkWidget *ProcessFieldsConvertIntoSpace      = NULL;
GtkWidget *ProcessFieldsConvertSpace          = NULL;
GtkWidget *ProcessFieldsConvert               = NULL;
GtkWidget *ProcessFieldsConvertLabelTo;
GtkWidget *ProcessFieldsConvertTo             = NULL;
GtkWidget *ProcessFieldsConvertFrom           = NULL;
GtkWidget *ProcessFieldsAllUppercase          = NULL;
GtkWidget *ProcessFieldsAllDowncase           = NULL;
GtkWidget *ProcessFieldsFirstLetterUppercase  = NULL;
GtkWidget *ProcessFieldsFirstLettersUppercase = NULL;
GtkWidget *ProcessFieldsRemoveSpace           = NULL;
GtkWidget *ProcessFieldsInsertSpace           = NULL;
GtkWidget *ProcessFieldsOnlyOneSpace          = NULL;

GtkWidget *LegendFrame    = NULL;
GtkWidget *LegendButton   = NULL;

GtkWidget *MaskEditorButton = NULL;
GtkWidget *MaskEditorFrame  = NULL;
GtkWidget *MaskEditorVBox;
GtkWidget *MaskEditorHBox;
GtkWidget *MaskEditorScrollWindow;
GtkWidget *MaskEditorList;
GtkWidget *MaskEditorEntry;
GtkWidget *MaskEditorNewButton;
GtkWidget *MaskEditorCopyButton;
GtkWidget *MaskEditorAddButton;
GtkWidget *MaskEditorRemoveButton;
GtkWidget *MaskEditorUpButton;
GtkWidget *MaskEditorDownButton;
GtkWidget *MaskEditorSaveButton;

/* Some predefined masks -- IMPORTANT: Null-terminate me! */
gchar *Scan_Masks [] = 
{
    "%a - %b/%n - %t",
    "%a_-_%b/%n_-_%t",
    "%a - %b (%y)/%n - %a - %t",
    "%a_-_%b_(%y)/%n_-_%a_-_%t",
    "%a - %b (%y) - %g/%n - %a - %t",
    "%a_-_%b_(%y)_-_%g/%n_-_%a_-_%t",
    "%a - %b/%n. %t",
    "%a_-_%b/%n._%t",
    "%a-%b/%n-%t",
    "%b/%n. %a - %t",
    "%b/%n._%a_-_%t",
    "%b/%n - %a - %t",
    "%b/%n_-_%a_-_%t",
    "%b/%n-%a-%t",
    "%a-%b/%n-%t",
    "%a/%b/%n. %t",
    "%g/%a/%b/%t",
    "%a_-_%b-%n-%t-%y",
    "%a - %b/%n. %t(%c)",
    "%t",
    "Track%n",
    "Track%i %n",
    NULL
};

gchar *Rename_File_Masks [] = 
{
    "%n - %a - %t",
    "%n_-_%a_-_%t",
    "%n. %a - %t",
    "%n._%a_-_%t",
    "%n - %t",
    "%n_-_%t",
    "%n. %t",
    "%n._%t",
    "%n - %a - %b - %t",
    "%n_-_%a_-_%b_-_%t",
    "%a - %b - %t",
    "%a_-_%b_-_%t",
    "%a - %b - %n - %t",
    "%a_-_%b_-_%n_-_%t",
    "%a - %t",
    "%a_-_%t",
    "Track %n",
    NULL
};

/**gchar *Rename_Directory_Masks [] = 
{
    "%a - %b",
    "%a_-_%b",
    "%a - %b (%y) - %g",
    "%a_-_%b_(%y)_-_%g",
    "VA - %b (%y)",
    "VA_-_%b_(%y)",
    NULL
};**/


gchar *Scanner_Option_Menu_Items [] = 
{
    N_("Fill Tag"),
    N_("Rename File and Directory"),
    N_("Process Fields")
};

typedef enum
{
    UNKNOWN = 0,           /* Default value when initialized */
    LEADING_SEPARATOR,     /* characters before the first code */
    TRAILING_SEPARATOR,    /* characters after the last code */
    SEPARATOR,             /* item is a separator between two codes */
    DIRECTORY_SEPARATOR,   /* item is a separator between two codes with character '/' */
    FIELD,                 /* item contains text (not empty) of entry */
    EMPTY_FIELD            /* item when entry contains no text */
} Mask_Item_Type;



/*
 * Used into Rename File Scanner
 */
typedef struct _File_Mask_Item File_Mask_Item;
struct _File_Mask_Item
{
    Mask_Item_Type  type;
    gchar          *string;
};

/*
 * Used into Scan Tag Scanner
 */
typedef struct _Scan_Mask_Item Scan_Mask_Item;
struct _Scan_Mask_Item
{
    gchar  code;   // The code of the mask without % (ex: %a => a)
    gchar *string; // The string found by the scanner for the code defined the line above
};



/**************
 * Prototypes *
 **************/
void     ScannerWindow_Quit      (void);
gboolean ScannerWindow_Key_Press (GtkWidget *window, GdkEvent *event);
void     Scan_Toggle_Legend_Button      (void);
void     Scan_Toggle_Mask_Editor_Button (void);
gchar   *Scan_Replace_String (gchar *string, gchar *last, gchar *new);
void     Scan_Option_Button (void);
gboolean Scan_Check_Scan_Tag_Mask    (GtkObject *widget_to_show_hide, GtkEntry *widget_source);
gboolean Scan_Check_Rename_File_Mask (GtkObject *widget_to_show_hide, GtkEntry *widget_source);
gboolean Scan_Check_Editor_Mask      (GtkObject *widget_to_show_hide, GtkEntry *widget_source);

gchar   *Scan_Generate_New_Filename_From_Mask (ET_File *ETFile, gchar *mask, gboolean no_dir_check_or_conversion);
GList   *Scan_Generate_New_Tag_From_Mask      (ET_File *ETFile, gchar *mask);
void     Scan_Rename_File_Generate_Preview    (void);
void     Scan_Rename_File_Prefix_Path         (void);
void     Scan_Fill_Tag_Generate_Preview       (void);
void     Scan_Free_File_Rename_List           (GList *list);
void     Scan_Free_File_Fill_Tag_List         (GList *list);
void     Scan_Rename_Directory_Generate_Preview (void);

gchar  **Scan_Return_File_Tag_Field_From_Mask_Code (File_Tag *FileTag, gchar code);
void     Scan_Process_Fields_Functions (gchar *string);

void Process_Fields_Check_Button_Toggled (GtkObject *object, GList *list);
void Process_Fields_Convert_Check_Button_Toggled (GtkObject *object);
void Select_Fields_Invert_Selection    (void);
void Select_Fields_Select_Unselect_All (void);
void Select_Fields_Set_Sensitive       (void);

void Mask_Editor_List_Row_Selected  (GtkTreeSelection* selection, gpointer data);
void Mask_Editor_List_New           (void);
void Mask_Editor_List_Duplicate     (void);
void Mask_Editor_List_Add           (void);
void Mask_Editor_List_Remove        (void);
void Mask_Editor_List_Move_Up       (void);
void Mask_Editor_List_Move_Down     (void);
void Mask_Editor_List_Save_Button   (void);
void Mask_Editor_Entry_Changed      (void);
gboolean Mask_Editor_List_Key_Press (GtkWidget *widget, GdkEvent *event);

void Mask_Editor_Clean_Up_Masks_List (void);

void Scanner_Option_Menu_Activate_Item (GtkWidget *widget, gpointer data);

void Populate_Scan_Tag_Masks();
void Populate_Rename_File_Masks();


/*************
 *           * 
 * Functions *
 *           * 
 *************/

void Init_ScannerWindow (void)
{
    ScannerWindow     = (GtkWidget *)NULL;
    ScannerOptionCombo= (GtkWidget *)NULL;
    SWScanButton      = (GtkWidget *)NULL;
}


/*
 * Uses the filename and path to fill tag informations
 * Note: mask and source are read from the right to the left
 */
void Scan_Tag_With_Mask (ET_File *ETFile)
{
    GList *fill_tag_list = NULL;
    gchar **dest = NULL;
    gchar *mask; // The 'mask' in the entry
    gchar *filename;
    gchar *filename_utf8;
    File_Tag *FileTag;

    if (!ScannerWindow || !ScanTagMaskCombo || !ETFile) return;
    mask = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(ScanTagMaskCombo)->child)));
    if (!mask) return;

    // Create a new File_Tag item
    FileTag = ET_File_Tag_Item_New();
    ET_Copy_File_Tag_Item(ETFile,FileTag);

    // Process this mask with file
    fill_tag_list = Scan_Generate_New_Tag_From_Mask(ETFile,mask);
    while (fill_tag_list)
    {
        Scan_Mask_Item *mask_item = fill_tag_list->data;

        // Get the target entry for this code
        dest = Scan_Return_File_Tag_Field_From_Mask_Code(FileTag,mask_item->code);

        // We display the text affected to the code
        if ( dest && ( OVERWRITE_TAG_FIELD || *dest==NULL || strlen(*dest)==0 ) )
            ET_Set_Field_File_Tag_Item(dest,mask_item->string);

        if (!fill_tag_list->next) break;
            fill_tag_list = fill_tag_list->next;
    }
    Scan_Free_File_Fill_Tag_List(fill_tag_list);

    // Set the default text to comment
    if (SET_DEFAULT_COMMENT && (OVERWRITE_TAG_FIELD || FileTag->comment==NULL || strlen(FileTag->comment)==0 ) )
        ET_Set_Field_File_Tag_Item((void *)&FileTag->comment,DEFAULT_COMMENT);

    // Set CRC-32 value as default comment (for files with ID3 tag only ;-)
    if (SET_CRC32_COMMENT && (OVERWRITE_TAG_FIELD || FileTag->comment==NULL || strlen(FileTag->comment)==0 ) )
    {
        gulong crc32_value = 0;
        gchar *buffer;
        ET_File_Description *ETFileDescription;

        ETFileDescription = ETFile->ETFileDescription;
        switch (ETFileDescription->TagType)
        {
            case ID3_TAG:
                crc32_file_with_ID3_tag( ((File_Name *)((GList *)ETFile->FileNameNew)->data)->value, &crc32_value);

            if (crc32_value > 0)
                {
                    buffer = g_strdup_printf("%.8lx",crc32_value);
                    ET_Set_Field_File_Tag_Item((void *)&FileTag->comment,buffer);
                    g_free(buffer);
                }
                break;
            default:
                break;
        }
    }


    // Save changes of the 'File_Tag' item
    ET_Manage_Changes_Of_File_Data(ETFile,NULL,FileTag);

    g_free(mask);
    Statusbar_Message(_("Tag successfully scanned..."),TRUE);
    filename = g_path_get_basename( ((File_Name *)ETFile->FileNameNew->data)->value );
    filename_utf8 = filename_to_display(filename);
    g_print(_("Tag successfully scanned...(%s)\n"),filename_utf8);
    g_free(filename);
    g_free(filename_utf8);
}

GList *Scan_Generate_New_Tag_From_Mask (ET_File *ETFile, gchar *mask)
{
    GList *fill_tag_list = NULL;
    gchar *filename = NULL;
    gchar *filename_utf8;
    gchar *tmp;
    gchar buf[256];
    gchar separator[256];
    gchar string[1024];
    gint  len, i, loop=0;
    gchar **mask_splitted;
    gchar **file_splitted;
    guint mask_splitted_number;
    guint file_splitted_number;
    guint mask_splitted_index;
    guint file_splitted_index;
    Scan_Mask_Item *mask_item;

    if (!ETFile || !mask) return NULL;

    filename = g_strdup(((File_Name *)((GList *)ETFile->FileNameNew)->data)->value);
    if (!filename) return NULL;

    // Remove extension of file (if found)
    tmp = strrchr(filename,'.');
    for (i=0; i<=(gint)ET_FILE_DESCRIPTION_SIZE; i++)
    {
        if ( strcasecmp(tmp,ETFileDescription[i].Extension)==0 )
        {
            *tmp = 0; //strrchr(source,'.') = 0;
            break;
        }
    }
    
    filename_utf8 = filename_to_display(filename);

    if (i==ET_FILE_DESCRIPTION_SIZE)
    {
        gchar *tmp1 = g_path_get_basename(filename_utf8);
        g_print("Tag scanner: strange..., the extension '%s' was not found into filename '%s'!\n",tmp,tmp1);
        g_free(tmp1);
    }

    // Replace characters into mask and filename before parsing
    if (FTS_CONVERT_UNDERSCORE_AND_P20_INTO_SPACE)
    {
        Scan_Convert_Underscore_Into_Space(mask);
        Scan_Convert_Underscore_Into_Space(filename_utf8);
        Scan_Convert_P20_Into_Space(mask);
        Scan_Convert_P20_Into_Space(filename_utf8);
    }
    if (FTS_CONVERT_SPACE_INTO_UNDERSCORE)
    {
        Scan_Convert_Space_Into_Undescore(mask);
        Scan_Convert_Space_Into_Undescore(filename_utf8);
    }


    // Split the Scanner mask
    mask_splitted = g_strsplit(mask,"/",0);
    // Get number of arguments into 'mask_splitted'
    for (mask_splitted_number=0;mask_splitted[mask_splitted_number];mask_splitted_number++);

    // Split the File Path
    file_splitted = g_strsplit(filename_utf8,"/",0);
    // Get number of arguments into 'file_splitted'
    for (file_splitted_number=0;file_splitted[file_splitted_number];file_splitted_number++);

    // Set the starting position for each tab
    if (mask_splitted_number <= file_splitted_number)
    {
        mask_splitted_index = 0;
        file_splitted_index = file_splitted_number - mask_splitted_number;
    }else
    {
        mask_splitted_index = mask_splitted_number - file_splitted_number;
        file_splitted_index = 0;
    }

    loop = 0;
    while ( mask_splitted[mask_splitted_index]!= NULL && file_splitted[file_splitted_index]!=NULL )
    {
        gchar *mask_seq = mask_splitted[mask_splitted_index];
        gchar *file_seq = file_splitted[file_splitted_index];
        gchar *file_seq_utf8 = filename_to_display(file_seq);

        //g_print(">%d> seq '%s' '%s'\n",loop,mask_seq,file_seq);
        while ( mask_seq && strlen(mask_seq)>0 )
        {

            /*
             * Determine (first) code and destination
             */
            if ( (tmp=strchr(mask_seq,'%')) == NULL || strlen(tmp) < 2 )
            {
                break;
            }

            /*
             * Allocate a new iten for the fill_tag_list
             */
            mask_item = g_malloc0(sizeof(Scan_Mask_Item));

            // Get the code (used to determine the corresponding target entry)
            mask_item->code = tmp[1];

            /*
             * Delete text before the code
             */
            if ( (len = strlen(mask_seq) - strlen(tmp)) > 0 )
            {
                // Get this text in 'mask_seq'
                strncpy(buf,mask_seq,len);
                buf[len] = 0;
                // We remove it in 'mask_seq'
                mask_seq = mask_seq + len;
                // Find the same text at the begining of 'file_seq' ?
                if ( (strstr(file_seq,buf)) == file_seq )
                {
                    file_seq = file_seq + len; // We remove it
                }else
                {
                    g_print(_("Scan Error: can't find separator '%s' within '%s'\n"),buf,file_seq_utf8);
                }
            }

            // Remove the current code into 'mask_seq'
            mask_seq = mask_seq + 2;

            /*
             * Determine separator between two code or trailing text (after code)
             */
            if ( mask_seq && strlen(mask_seq)>0 )
            {
                if ( (tmp=strchr(mask_seq,'%')) == NULL || strlen(tmp) < 2 )
                {
                    // No more code found
                    strcpy(separator,mask_seq);
                    separator[strlen(mask_seq)] = 0;
                }else
                {
                    len = strlen(mask_seq) - strlen(tmp);
                    strncpy(separator,mask_seq,len);
                    separator[len] = 0;
                }

                // Remove the current separator in 'mask_seq'
                mask_seq = mask_seq + strlen(separator);

                // Try to find the separator in 'file_seq'
                if ( (tmp=strstr(file_seq,separator)) == NULL )
                {
                    g_print(_("Scan Error: can't find separator '%s' within '%s'\n"),separator,file_seq_utf8);
                    separator[0] = 0; // Needed to avoid error when calculting 'len' below
                }

                // Get the string affected to the code (or the corresponding entry field)
                len = strlen(file_seq) - (tmp!=NULL?strlen(tmp):0);
                strncpy(string,file_seq,len);
                string[len] = 0;

                // Remove the current separator in 'file_seq'
                file_seq = file_seq + strlen(string) + strlen(separator);

                // We get the text affected to the code
                mask_item->string = g_strdup(string);
            }else
            {
                // We display the remaining text, affected to the code (no more data in 'mask_seq')
                mask_item->string = g_strdup(file_seq);
            }

            // Add the filled mask_iten to the list
            fill_tag_list = g_list_append(fill_tag_list,mask_item);
        }

        g_free(file_seq_utf8);

        // Next sequences
        mask_splitted_index++;
        file_splitted_index++;
        loop++;
    }

    g_free(filename);
    g_free(filename_utf8);
    g_strfreev(mask_splitted);
    g_strfreev(file_splitted);

    // The 'fill_tag_list' must be freed after use
    return fill_tag_list;
}

void Scan_Fill_Tag_Generate_Preview (void)
{
    gchar *mask = NULL;
    gchar *preview_text = NULL;
    GList *fill_tag_list = NULL;

    if (!ETCore->ETFileDisplayedList
    ||  !ScannerWindow || !RenameFileMaskCombo || !FillTagPreviewLabel
    ||  gtk_combo_box_get_active(GTK_COMBO_BOX(ScannerOptionCombo)) != SCANNER_FILL_TAG)
        return;

    mask = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(ScanTagMaskCombo)->child)));
    if (!mask)
        return;

    preview_text = g_strdup("");
    fill_tag_list = Scan_Generate_New_Tag_From_Mask(ETCore->ETFileDisplayed,mask);
    while (fill_tag_list)
    {
        Scan_Mask_Item *mask_item = fill_tag_list->data;
        gchar *tmp_code   = g_strdup_printf("%c",mask_item->code);
        gchar *tmp_string = g_markup_printf_escaped("%s",mask_item->string); // To avoid problem with strings containing characters like '&'
        gchar *tmp_preview_text = preview_text;

        preview_text = g_strconcat(tmp_preview_text,"<b>","%",tmp_code," = ",
                                   "</b>","<i>",tmp_string,"</i>",NULL);
        g_free(tmp_code);
        g_free(tmp_string);
        g_free(tmp_preview_text);

        if (!fill_tag_list->next) break;
        fill_tag_list = fill_tag_list->next;

        tmp_preview_text = preview_text;
        preview_text = g_strconcat(tmp_preview_text,"  ||  ",NULL);
        g_free(tmp_preview_text);
    }
    Scan_Free_File_Fill_Tag_List(fill_tag_list);

    if (GTK_IS_LABEL(FillTagPreviewLabel))
    {
        if (preview_text)
        {
            //gtk_label_set_text(GTK_LABEL(FillTagPreviewLabel),preview_text);
            gtk_label_set_markup(GTK_LABEL(FillTagPreviewLabel),preview_text);
        } else
        {
            gtk_label_set_text(GTK_LABEL(FillTagPreviewLabel),"");
        }

        // Force the window to be redrawed
        gtk_widget_queue_resize(ScannerWindow);
    }

    g_free(mask);
    g_free(preview_text);
}

void Scan_Free_File_Fill_Tag_List (GList *list)
{
    // Free the list
    list = g_list_first(list);
    while (list)
    {
        if (list->data)
        {
            g_free(((Scan_Mask_Item *)list->data)->string);
            g_free( (Scan_Mask_Item *)list->data );
        }
        if (!list->next) break;
        list = list->next;
    }
    g_list_free(list);
    list = (GList *)NULL;
}



/**************************
 * Scanner To Rename File *
 **************************/
/*
 * Uses tag informations (displayed into tag entries) to rename file
 * Note: mask and source are read from the right to the left.
 * Note1: a mask code may be used severals times...
 */
void Scan_Rename_File_With_Mask (ET_File *ETFile)
{
    gchar *filename_generated_utf8 = NULL;
    gchar *filename_generated = NULL;
    gchar *filename_new_utf8 = NULL;
    gchar *filename_new = NULL;
    gchar *mask = NULL;
    gchar *temp;
    File_Name *FileName;

    if (!ScannerWindow || !RenameFileMaskCombo || !ETFile) return;

    mask = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(RenameFileMaskCombo)->child)));
    if (!mask) return;

    // Note : if the first character is '/', we have a path with the filename, 
    // else we have only the filename. The both are in UTF-8.
    filename_generated_utf8 = Scan_Generate_New_Filename_From_Mask(ETFile,mask,FALSE);
    g_free(mask);

    if (!filename_generated_utf8)
        return;
    if (g_utf8_strlen(filename_generated_utf8,-1)<1)
    {
        g_free(filename_generated_utf8);
        return;
    }

    // Convert filename to file-system encoding
    filename_generated = filename_from_display(filename_generated_utf8);
    if (!filename_generated)
    {
        GtkWidget *msgbox;
        gchar *msg = g_strdup_printf(_("Could not convert filename '%s' into system filename encoding."), filename_generated_utf8);
        msgbox = msg_box_new(_("Filename translation"),msg,GTK_STOCK_DIALOG_ERROR,BUTTON_OK,0);
        msg_box_run(MSG_BOX(msgbox));
        gtk_widget_destroy(msgbox);
        g_free(msg);
        g_free(filename_generated_utf8);
        return;
    }

    /* Build the filename with the full path or relative to old path */
    filename_new_utf8 = ET_File_Name_Generate(ETFile,filename_generated_utf8);
    ////filename_new = ET_File_Name_Generate(ETFile,filename_generated_utf8);
    g_free(filename_generated);
    g_free(filename_generated_utf8);

    /* Set the new filename */
    // Create a new 'File_Name' item
    FileName = ET_File_Name_Item_New();
    // Save changes of the 'File_Name' item
    filename_new = filename_from_display(filename_new_utf8);
    ET_Set_Field_File_Name_Item(&FileName->value,filename_new);
    ET_Manage_Changes_Of_File_Data(ETFile,FileName,NULL);
    g_free(filename_new_utf8);
    g_free(filename_new);

    Statusbar_Message(_("New file name successfully scanned..."),TRUE);

    temp = g_path_get_basename(((File_Name *)ETFile->FileNameNew->data)->value);
    filename_new_utf8 = filename_to_display(temp);
    g_print(_("New file name successfully scanned...(%s)\n"),filename_new_utf8);
    g_free(filename_new_utf8);
    g_free(temp);

    return;
}

/*
 * Build the new filename using tag + mask
 * Used also to rename the directory (from the browser)
 * @param ETFile                     : the etfile to process
 * @param mask                       : the pattern to parse
 * @param no_dir_check_or_conversion : if FALSE, disable checking of a directory
 *      in the mask, and don't convert "illegal" characters. This is used in the
 *      function "Write_Playlist" for the content of the playlist.
 * Returns filename in UTF-8
 */
gchar *Scan_Generate_New_Filename_From_Mask (ET_File *ETFile, gchar *mask, gboolean no_dir_check_or_conversion)
{
    gchar *tmp;
    gchar **source = NULL;
    gchar *filename_utf8_cur = NULL;
    gchar *path_utf8_cur = NULL;
    gchar *filename_new = NULL;
    gchar *filename_tmp = NULL;
    GList *rename_file_list = NULL;
    File_Mask_Item *mask_item;
    File_Mask_Item *mask_item_prev;
    File_Mask_Item *mask_item_next;
    gint counter = 0;


    if (!ETFile || !mask) return NULL;

    /*
     * Check for a directory in the mask
     */
    if (!no_dir_check_or_conversion)
    {
        if (g_path_is_absolute(mask))
        {
            // Absolute directory
        }else if (strrchr(mask,'/')!=NULL)
        {
            // Relative path => set beginning of the path
            filename_utf8_cur = filename_to_display( ((File_Name *)ETFile->FileNameCur->data)->value );
            path_utf8_cur = g_path_get_dirname(filename_utf8_cur);
            g_free(filename_utf8_cur);
        }
    }


    /*
     * Parse the codes to generate a list (1rst item = 1rst code)
     */
    while ( mask!=NULL && (tmp=strrchr(mask,'%'))!=NULL && strlen(tmp)>1 )
    {
        // Mask contains some characters after the code ('%b__')
        if (strlen(tmp)>2)
        {
            mask_item = g_malloc0(sizeof(File_Mask_Item));
            if (counter)
            {
                if (strchr(tmp+2,'/'))
                    mask_item->type = DIRECTORY_SEPARATOR;
                else
                    mask_item->type = SEPARATOR;
            } else
            {
                mask_item->type = TRAILING_SEPARATOR;
            }
            mask_item->string = g_strdup(tmp+2);
            rename_file_list = g_list_prepend(rename_file_list,mask_item);
        }

        // Now, parses the code to get the corresponding string (from tag)
        source = Scan_Return_File_Tag_Field_From_Mask_Code((File_Tag *)ETFile->FileTag->data,tmp[1]);
        mask_item = g_malloc0(sizeof(File_Mask_Item));
        if (source && *source && strlen(*source)>0)
        {
            mask_item->type = FIELD;
            mask_item->string = g_strdup(*source);
            
            // Replace invalid characters for this field
            // Note : shouldn't be done always as for the content of a playlist, we don't nedd to replace...
            if (!no_dir_check_or_conversion)
            {
                ET_File_Name_Convert_Character(mask_item->string);
            }
            
            // Replace characters (rules) (!! don't convert in directory path_utf8_cur)
            if (RFS_CONVERT_UNDERSCORE_AND_P20_INTO_SPACE)
            {
                Scan_Convert_Underscore_Into_Space(mask_item->string);
                Scan_Convert_P20_Into_Space(mask_item->string);
            }
            if (RFS_CONVERT_SPACE_INTO_UNDERSCORE)
                Scan_Convert_Space_Into_Undescore(mask_item->string);
        }else
        {
            mask_item->type = EMPTY_FIELD;
            mask_item->string = NULL;
        }
        rename_file_list = g_list_prepend(rename_file_list,mask_item);
        *tmp = '\0'; // Cut parsed data of mask
        counter++; // To indicate that we made at least one loop to identifiate 'separator' or 'trailing_separator'
    }

    // It may have some characters before the last remaining code ('__%a')
    if (mask!=NULL && strlen(mask)>0)
    {
        mask_item = g_malloc0(sizeof(File_Mask_Item));
        mask_item->type = LEADING_SEPARATOR;
        mask_item->string = g_strdup(mask);
        rename_file_list = g_list_prepend(rename_file_list,mask_item);
    }

    if (!rename_file_list) return NULL;

    /*
     * For Debugging : display the "rename_file_list" list
     */
    /*{
        GList *list = g_list_first(rename_file_list);
        gint i = 0;
        g_print("## rename_file_list - start\n");
        while (list)
        {
            File_Mask_Item *mask_item = (File_Mask_Item *)list->data;
            Mask_Item_Type  type      = mask_item->type;
            gchar          *string    = mask_item->string;
            
            //g_print("item %d : \n",i++);
            //g_print("  - type   : '%s'\n",type==UNKNOWN?"UNKNOWN":type==LEADING_SEPARATOR?"LEADING_SEPARATOR":type==TRAILING_SEPARATOR?"TRAILING_SEPARATOR":type==SEPARATOR?"SEPARATOR":type==DIRECTORY_SEPARATOR?"DIRECTORY_SEPARATOR":type==FIELD?"FIELD":type==EMPTY_FIELD?"EMPTY_FIELD":"???");
            //g_print("  - string : '%s'\n",string);
            g_print("%d -> %s (%s) | ",i++,type==UNKNOWN?"UNKNOWN":type==LEADING_SEPARATOR?"LEADING_SEPARATOR":type==TRAILING_SEPARATOR?"TRAILING_SEPARATOR":type==SEPARATOR?"SEPARATOR":type==DIRECTORY_SEPARATOR?"DIRECTORY_SEPARATOR":type==FIELD?"FIELD":type==EMPTY_FIELD?"EMPTY_FIELD":"???",string);
            
            list = list->next;
        }
        g_print("\n## rename_file_list - end\n\n");
    }*/

    /*
     * Build the new filename with items placed into the list
     * (read the list from the end to the beginning)
     */
    rename_file_list = g_list_last(rename_file_list);
    filename_new = g_strdup("");

    while (rename_file_list)
    {
        File_Mask_Item *mask_item = rename_file_list->data;

        if ( mask_item->type==TRAILING_SEPARATOR ) // Trailing characters of mask
        {
            // Doesn't write it if previous field is empty
            if (rename_file_list->prev && ((File_Mask_Item *)rename_file_list->prev->data)->type!=EMPTY_FIELD)
            {
                filename_tmp = filename_new;
                filename_new = g_strconcat(mask_item->string,filename_new,NULL);
                g_free(filename_tmp);
            }
        }else
        if ( mask_item->type==EMPTY_FIELD )
        // We don't concatenate the field value (empty) and the previous 
        // separator (except leading separator) to the filename.
        // If the empty field is the 'first', we don't concatenate it, and the 
        // next separator too.
        {
            if (rename_file_list->prev)
            {
                // The empty field isn't the first
                // If previous string is a separator, we don't use it
                mask_item_prev = rename_file_list->prev->data;
                if ( mask_item_prev->type==SEPARATOR )
                {
                    rename_file_list = rename_file_list->prev;
                }
            }else
            if (rename_file_list->next && (mask_item_next=rename_file_list->next->data)
            &&  mask_item_next->type==SEPARATOR)
            // We are at the 'beginning' of the mask (so empty field is the first) 
            // and next field is a separator. As the separator may have been already added, we remove it
            {
                if ( filename_new && mask_item_next->string && (strncmp(filename_new,mask_item_next->string,strlen(mask_item_next->string))==0) ) // To avoid crash if filename_new is 'empty'
                {
                    filename_tmp = filename_new;
                    filename_new = g_strdup(filename_new+strlen(mask_item_next->string));
                    g_free(filename_tmp);
                 }
            }

        }else // SEPARATOR, FIELD, LEADING_SEPARATOR, DIRECTORY_SEPARATOR
        {
            filename_tmp = filename_new;
            filename_new = g_strconcat(mask_item->string,filename_new,NULL);
            g_free(filename_tmp);
        }

        if (!rename_file_list->prev) break;
        rename_file_list = rename_file_list->prev;
    }

    // Free the list
    Scan_Free_File_Rename_List(rename_file_list);


    // Add current path if relative path entered
    if (path_utf8_cur)
    {
        filename_tmp = filename_new; // in UTF-8!
        filename_new = g_strconcat(path_utf8_cur,"/",filename_new,NULL);
        g_free(filename_tmp);
        g_free(path_utf8_cur);
    }

    return filename_new; // in UTF-8!
}

void Scan_Rename_File_Generate_Preview (void)
{
    gchar *preview_text = NULL;
    gchar *mask = NULL;

    if (!ETCore->ETFileDisplayed
    ||  !ScannerWindow || !RenameFileMaskCombo || !RenameFilePreviewLabel)
        return;

    if (gtk_combo_box_get_active(GTK_COMBO_BOX(ScannerOptionCombo)) != SCANNER_RENAME_FILE)
        return;

    mask = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(RenameFileMaskCombo)->child)));
    if (!mask)
        return;

    preview_text = Scan_Generate_New_Filename_From_Mask(ETCore->ETFileDisplayed,mask,FALSE);

    if (GTK_IS_LABEL(RenameFilePreviewLabel))
    {
        if (preview_text)
        {
            //gtk_label_set_text(GTK_LABEL(RenameFilePreviewLabel),preview_text);
            gchar *tmp_string = g_markup_printf_escaped("%s",preview_text); // To avoid problem with strings containing characters like '&'
            gchar *str = g_strdup_printf("<i>%s</i>",tmp_string);
            gtk_label_set_markup(GTK_LABEL(RenameFilePreviewLabel),str);
            g_free(tmp_string);
            g_free(str);
        } else
        {
            gtk_label_set_text(GTK_LABEL(RenameFilePreviewLabel),"");
        }

        // Force the window to be redrawed
        gtk_widget_queue_resize(ScannerWindow);
    }

    g_free(mask);
    g_free(preview_text);
}


void Scan_Free_File_Rename_List (GList *list)
{
    // Free the list
    list = g_list_last(list);
    while (list)
    {
        if (list->data)
        {
            g_free(((File_Mask_Item *)list->data)->string);
            g_free( (File_Mask_Item *)list->data );
        }
        if (!list->prev) break;
        list = list->prev;
    }
    g_list_free(list);
    list = (GList *)NULL;
}

/*
 * Adds the current path of the file to the mask on the "Rename File Scanner" entry
 */
void Scan_Rename_File_Prefix_Path (void)
{
    gchar *path_tmp;
    gchar *combo_text = NULL;
    gchar *combo_tmp;
    ET_File *ETFile          = ETCore->ETFileDisplayed;
    gchar *filename_utf8_cur = filename_to_display( ((File_Name *)ETFile->FileNameCur->data)->value );
    gchar *path_utf8_cur;
    

    // The path to prefix
    path_utf8_cur = g_path_get_dirname(filename_utf8_cur);
    
    // The current text in the combobox
    combo_text = (gchar *)gtk_entry_get_text(GTK_ENTRY(GTK_BIN(RenameFileMaskCombo)->child));
    if (!g_utf8_validate(combo_text, -1, NULL))
    {
        combo_tmp = convert_to_utf8(combo_text);
    }else
    {
        combo_tmp = g_strdup(combo_text);
    }
    
    // If the path already exists we don't add it again
    // Use g_utf8_collate_key instead of strncmp
    if (combo_tmp && path_utf8_cur && strncmp(combo_tmp,path_utf8_cur,strlen(path_utf8_cur))!=0)
    {
        if (g_path_is_absolute(combo_tmp))
        {
            path_tmp = g_strdup(path_utf8_cur);
        } else
        {
            path_tmp = g_strconcat(path_utf8_cur,"/",NULL);
        }
        gtk_entry_prepend_text(GTK_ENTRY(GTK_BIN(RenameFileMaskCombo)->child),path_tmp);
        g_free(path_tmp);
    }
    
    g_free(filename_utf8_cur);
    g_free(path_utf8_cur);
}


/*******************************
 * Scanner To Rename Directory *
 *******************************/
void Scan_Rename_Directory_Generate_Preview (void)
{
    gchar *preview_text = NULL;
    gchar *mask = NULL;

    if (!ETCore->ETFileDisplayed
    ||  !RenameDirectoryWindow || !RenameDirectoryMaskCombo || !RenameDirectoryPreviewLabel)
        return;

    mask = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(RenameDirectoryMaskCombo)->child)));
    if (!mask)
        return;

    preview_text = Scan_Generate_New_Filename_From_Mask(ETCore->ETFileDisplayed,mask,FALSE);

    if (GTK_IS_LABEL(RenameDirectoryPreviewLabel))
    {
        if (preview_text)
        {
            //gtk_label_set_text(GTK_LABEL(RenameFilePreviewLabel),preview_text);
            gchar *tmp_string = g_markup_printf_escaped("%s",preview_text); // To avoid problem with strings containing characters like '&'
            gchar *str = g_strdup_printf("<i>%s</i>",tmp_string);
            gtk_label_set_markup(GTK_LABEL(RenameDirectoryPreviewLabel),str);
            g_free(tmp_string);
            g_free(str);
        } else
        {
            gtk_label_set_text(GTK_LABEL(RenameDirectoryPreviewLabel),"");
        }

        // Force the window to be redrawed else the preview label may be not placed correctly
        gtk_widget_queue_resize(RenameDirectoryWindow);
    }

    g_free(mask);
    g_free(preview_text);
}

gchar *Scan_Generate_New_Directory_Name_From_Mask (ET_File *ETFile, gchar *mask, gboolean no_dir_check_or_conversion)
{
    return Scan_Generate_New_Filename_From_Mask(ETFile,mask,no_dir_check_or_conversion);
}



/*****************************
 * Scanner To Process Fields *
 *****************************/
/* See also functions : Convert_P20_And_Undescore_Into_Spaces, ... in easytag.c */
void Scan_Process_Fields (ET_File *ETFile)
{
    File_Name *FileName = NULL;
    File_Tag  *FileTag  = NULL;
    File_Name *st_filename;
    File_Tag  *st_filetag;
    gchar     *filename_utf8;
    //GString *string2process;
    guint  string_length;
    gchar *string;
    gchar *temp;


    if (!ScannerWindow || !ETFile) return;

    st_filename      = (File_Name *)ETFile->FileNameNew->data;
    st_filetag       = (File_Tag  *)ETFile->FileTag->data;
    //string2process = g_string_sized_new(512);

    /* Process the filename */
    if (st_filename != NULL)
    {
        if (st_filename->value && GTK_TOGGLE_BUTTON(ProcessFileNameField)->active) // File name field
        {
            gchar *string_utf8;
            gchar *pos;

            filename_utf8 = filename_to_display(st_filename->value);
            
            if (!FileName)
                FileName = ET_File_Name_Item_New();

            // FIX ME : we suppose that it will not grow more than 2 times its size...
            temp = g_path_get_basename(filename_utf8);
            string_length = 2 * g_utf8_strlen(temp,-1);
            string        = g_malloc(string_length+1);
            strncpy(string,temp,string_length);
            g_free(temp);
            g_free(filename_utf8);
            string[string_length]='\0';
            // Remove the extension to set it to lower case (to avoid problem with undo)
            if ((pos=strrchr(string,'.'))!=NULL) *pos = 0;

            Scan_Process_Fields_Functions(string);

            string_utf8 = ET_File_Name_Generate(ETFile,string);
            g_free(string);
            string = filename_from_display(string_utf8);
            ET_Set_Field_File_Name_Item(&FileName->value,string);

            g_free(string_utf8);
            g_free(string);
        }
    }

    /* Process data of the tag */
    if (st_filetag != NULL)
    {
        // Title field
        if (st_filetag->title && GTK_TOGGLE_BUTTON(ProcessTitleField)->active)
        {
            if (!FileTag)
            {
                FileTag = ET_File_Tag_Item_New();
                ET_Copy_File_Tag_Item(ETFile,FileTag);
            }

            // FIX ME : we suppose that it will not grow more than 2 times its size...
            string_length = 2 * strlen(st_filetag->title);
            string        = g_malloc(string_length+1);
            strncpy(string,st_filetag->title,string_length);
            string[string_length]='\0';

            Scan_Process_Fields_Functions(string);

            ET_Set_Field_File_Tag_Item(&FileTag->title,string);

            g_free(string);
        }

        // Artist field
        if (st_filetag->artist && GTK_TOGGLE_BUTTON(ProcessArtistField)->active)
        {
            if (!FileTag)
            {
                FileTag = ET_File_Tag_Item_New();
                ET_Copy_File_Tag_Item(ETFile,FileTag);
            }

            // FIX ME : we suppose that it will not grow more than 2 times its size...
            string_length = 2 * strlen(st_filetag->artist);
            string        = g_malloc(string_length+1);
            strncpy(string,st_filetag->artist,string_length);
            string[string_length]='\0';

            Scan_Process_Fields_Functions(string);

            ET_Set_Field_File_Tag_Item(&FileTag->artist,string);

            g_free(string);
        }

        // Album field
        if (st_filetag->album && GTK_TOGGLE_BUTTON(ProcessAlbumField)->active)
        {
            if (!FileTag)
            {
                FileTag = ET_File_Tag_Item_New();
                ET_Copy_File_Tag_Item(ETFile,FileTag);
            }

            // FIX ME : we suppose that it will not grow more than 2 times its size...
            string_length = 2 * strlen(st_filetag->album);
            string        = g_malloc(string_length+1);
            strncpy(string,st_filetag->album,string_length);
            string[string_length]='\0';

            Scan_Process_Fields_Functions(string);

            ET_Set_Field_File_Tag_Item(&FileTag->album,string);

            g_free(string);
        }

        // Genre field
        if (st_filetag->genre && GTK_TOGGLE_BUTTON(ProcessGenreField)->active)
        {
            if (!FileTag)
            {
                FileTag = ET_File_Tag_Item_New();
                ET_Copy_File_Tag_Item(ETFile,FileTag);
            }

            // FIX ME : we suppose that it will not grow more than 2 times its size...
            string_length = 2 * strlen(st_filetag->genre);
            string        = g_malloc(string_length+1);
            strncpy(string,st_filetag->genre,string_length);
            string[string_length]='\0';

            Scan_Process_Fields_Functions(string);

            ET_Set_Field_File_Tag_Item(&FileTag->genre,string);

            g_free(string);
        }

        // Comment field
        if (st_filetag->comment && GTK_TOGGLE_BUTTON(ProcessCommentField)->active)
        {
            if (!FileTag)
            {
                FileTag = ET_File_Tag_Item_New();
                ET_Copy_File_Tag_Item(ETFile,FileTag);
            }

            // FIX ME : we suppose that it will not grow more than 2 times its size...
            string_length = 2 * strlen(st_filetag->comment);
            string        = g_malloc(string_length+1);
            strncpy(string,st_filetag->comment,string_length);
            string[string_length]='\0';

            Scan_Process_Fields_Functions(string);

            ET_Set_Field_File_Tag_Item(&FileTag->comment,string);

            g_free(string);
        }

        // Composer field
        if (st_filetag->composer && GTK_TOGGLE_BUTTON(ProcessComposerField)->active)
        {
            if (!FileTag)
            {
                FileTag = ET_File_Tag_Item_New();
                ET_Copy_File_Tag_Item(ETFile,FileTag);
            }

            // FIX ME : we suppose that it will not grow more than 2 times its size...
            string_length = 2 * strlen(st_filetag->composer);
            string        = g_malloc(string_length+1);
            strncpy(string,st_filetag->composer,string_length);
            string[string_length]='\0';

            Scan_Process_Fields_Functions(string);

            ET_Set_Field_File_Tag_Item(&FileTag->composer,string);

            g_free(string);
        }

        // Original artist field
        if (st_filetag->orig_artist && GTK_TOGGLE_BUTTON(ProcessOrigArtistField)->active)
        {
            if (!FileTag)
            {
                FileTag = ET_File_Tag_Item_New();
                ET_Copy_File_Tag_Item(ETFile,FileTag);
            }

            // FIX ME : we suppose that it will not grow more than 2 times its size...
            string_length = 2 * strlen(st_filetag->orig_artist);
            string        = g_malloc(string_length+1);
            strncpy(string,st_filetag->orig_artist,string_length);
            string[string_length]='\0';

            Scan_Process_Fields_Functions(string);

            ET_Set_Field_File_Tag_Item(&FileTag->orig_artist,string);

            g_free(string);
        }

        // Copyright field
        if (st_filetag->copyright && GTK_TOGGLE_BUTTON(ProcessCopyrightField)->active)
        {
            if (!FileTag)
            {
                FileTag = ET_File_Tag_Item_New();
                ET_Copy_File_Tag_Item(ETFile,FileTag);
            }

            // FIX ME : we suppose that it will not grow more than 2 times its size...
            string_length = 2 * strlen(st_filetag->copyright);
            string        = g_malloc(string_length+1);
            strncpy(string,st_filetag->copyright,string_length);
            string[string_length]='\0';

            Scan_Process_Fields_Functions(string);

            ET_Set_Field_File_Tag_Item(&FileTag->copyright,string);

            g_free(string);
        }

        // URL field
        if (st_filetag->url && GTK_TOGGLE_BUTTON(ProcessURLField)->active)
        {
            if (!FileTag)
            {
                FileTag = ET_File_Tag_Item_New();
                ET_Copy_File_Tag_Item(ETFile,FileTag);
            }

            // FIX ME : we suppose that it will not grow more than 2 times its size...
            string_length = 2 * strlen(st_filetag->url);
            string        = g_malloc(string_length+1);
            strncpy(string,st_filetag->url,string_length);
            string[string_length]='\0';

            Scan_Process_Fields_Functions(string);

            ET_Set_Field_File_Tag_Item(&FileTag->url,string);

            g_free(string);
        }

        // 'Encoded by' field
        if (st_filetag->encoded_by && GTK_TOGGLE_BUTTON(ProcessEncodedByField)->active)
        {
            if (!FileTag)
            {
                FileTag = ET_File_Tag_Item_New();
                ET_Copy_File_Tag_Item(ETFile,FileTag);
            }

            // FIX ME : we suppose that it will not grow more than 2 times its size...
            string_length = 2 * strlen(st_filetag->encoded_by);
            string        = g_malloc(string_length+1);
            strncpy(string,st_filetag->encoded_by,string_length);
            string[string_length]='\0';

            Scan_Process_Fields_Functions(string);

            ET_Set_Field_File_Tag_Item(&FileTag->encoded_by,string);

            g_free(string);
        }
    }

    if (FileName && FileTag)
    {
        // Synchronize undo key of the both structures (used for the
        // undo functions, as they are generated as the same time)
        FileName->key = FileTag->key;
    }
    ET_Manage_Changes_Of_File_Data(ETFile,FileName,FileTag);

}


void Scan_Process_Fields_Functions (gchar *string)
{

    if (GTK_TOGGLE_BUTTON(ProcessFieldsConvertIntoSpace)->active)
    {
        Scan_Convert_Underscore_Into_Space(string);
        Scan_Convert_P20_Into_Space(string);
    }

    if (GTK_TOGGLE_BUTTON(ProcessFieldsConvertSpace)->active)
        Scan_Convert_Space_Into_Undescore(string);

    if (GTK_TOGGLE_BUTTON(ProcessFieldsInsertSpace)->active)
        Scan_Process_Fields_Insert_Space(string);

    if (GTK_TOGGLE_BUTTON(ProcessFieldsOnlyOneSpace)->active)
        Scan_Process_Fields_Keep_One_Space(string);

    if (GTK_TOGGLE_BUTTON(ProcessFieldsConvert)->active)
        Scan_Convert(string);

    if (GTK_TOGGLE_BUTTON(ProcessFieldsAllUppercase)->active)
        Scan_Process_Fields_All_Uppercase(string);

    if (GTK_TOGGLE_BUTTON(ProcessFieldsAllDowncase)->active)
        Scan_Process_Fields_All_Downcase(string);

    if (GTK_TOGGLE_BUTTON(ProcessFieldsFirstLetterUppercase)->active)
         Scan_Process_Fields_Letter_Uppercase(string);

    if (GTK_TOGGLE_BUTTON(ProcessFieldsFirstLettersUppercase)->active)
        Scan_Process_Fields_First_Letters_Uppercase(string);

    if (GTK_TOGGLE_BUTTON(ProcessFieldsRemoveSpace)->active)
        Scan_Process_Fields_Remove_Space(string);

}

void Scan_Process_Fields_All_Uppercase (gchar *text)
{
    gchar *temp;
    gchar temp2[6]; // Must have at least 6 bytes of space
    gunichar c;
    
    for (temp = text; *temp; temp = g_utf8_next_char(temp))
    {
        c = g_utf8_get_char(temp);
        if (g_unichar_islower(c))
            strncpy(temp, temp2, g_unichar_to_utf8(g_unichar_toupper(c), temp2));
    }
}

void Scan_Process_Fields_All_Downcase (gchar *text)
{
    gchar *temp;
    gchar temp2[6];
    gunichar c;
    
    for (temp = text; *temp; temp = g_utf8_next_char(temp))
    {
        c = g_utf8_get_char(temp);
        if (g_unichar_isupper(c))
            strncpy(temp, temp2, g_unichar_to_utf8(g_unichar_tolower(c), temp2));
    }
}

void Scan_Process_Fields_Letter_Uppercase (gchar *text)
{
    gchar *temp;
    gchar temp2[6];
    gboolean set_to_upper_case = TRUE;
    gunichar c;
    gchar utf8_character[6];
    gchar *word, *word1, *word2;
    
    for (temp = text; *temp; temp = g_utf8_next_char(temp))
    {
        c = g_utf8_get_char(temp);
        if (set_to_upper_case && g_unichar_islower(c))
            strncpy(temp, temp2, g_unichar_to_utf8(g_unichar_toupper(c), temp2));
        else if (!set_to_upper_case && g_unichar_isupper(c))
            strncpy(temp, temp2, g_unichar_to_utf8(g_unichar_tolower(c), temp2));
        set_to_upper_case = FALSE; // After the first time, all will be down case
    }
    
    temp = text;
    
    // Uppercase again the word 'I' in english
    while ( temp )
    {
        word1 = g_utf8_strchr(temp,-1,' ');
        word2 = g_utf8_strchr(temp,-1,'_');
        
        // Take the first string found (near beginning of string)
        if (word1 && word2)
            word = MIN(word1,word2);
        else if (word1)
            word = word1;
        else if (word2)
            word = word2;
        else
            break;
        
        // Go to first character of the word (char. after ' ' or '_')
        word = word+1;
        
        // Set uppercase word 'I'
        if (g_ascii_strncasecmp("I ", word, strlen("I ")) == 0)
        {
            c = g_utf8_get_char(word);
            strncpy(word, utf8_character, g_unichar_to_utf8(g_unichar_toupper(c), utf8_character));
        }
        
        temp = word;
    }
}

void Scan_Process_Fields_First_Letters_Uppercase (gchar *text)
{
/**** DANIEL TEST *****
    gchar *iter;
    gchar utf8_character[6];
    gboolean set_to_upper_case = TRUE;
    gunichar c;
    
    for (iter = text; *iter; iter = g_utf8_next_char(iter))
    {
        c = g_utf8_get_char(iter);
        if (set_to_upper_case && g_unichar_islower(c))
            strncpy(iter, utf8_character, g_unichar_to_utf8(g_unichar_toupper(c), utf8_character));
        else if (!set_to_upper_case && g_unichar_isupper(c))
            strncpy(iter, utf8_character, g_unichar_to_utf8(g_unichar_tolower(c), utf8_character));
            
        set_to_upper_case = (g_unichar_isalpha(c)
                            || c == (gunichar)'.'
                            || c == (gunichar)'\''
                            || c == (gunichar)'`') ? FALSE : TRUE;
    }
****/
/**** Barış Çiçek version ****/
    gchar *word, *word1, *word2, *temp;
    gint i;
    gchar utf8_character[6];
    gunichar c;
    gboolean set_to_upper_case, set_to_upper_case_tmp;
    // There have to be space at the end of words to seperate them from prefix
    gchar *exempt[] =
    {
        "a ",       "a_",
        "an ",      "an_",
        "and ",     "and_",
        "at ",      "at_",
        "but ",     "but_",
        "feat. ",   "feat._",
        "for ",     "for_",
        "in ",      "in_",
        "nor ",     "nor_",
        "of ",      "of_",
        "off ",     "off_",
        "on ",      "on_",
        "or ",      "or_",
        "over ",    "over_",
        "so ",      "so_",
        "the ",     "the_",
        "to ",      "to_",
        "with ",    "with_",
        NULL
    };
    
    if (!PFS_DONT_UPPER_SOME_WORDS)
    {
        exempt[0] = NULL;
    }
    Scan_Process_Fields_All_Downcase(text);

    if (!g_utf8_validate(text,-1,NULL))
    {
        g_print("Scan_Process_Fields_First_Letters_Uppercase : Not a valid utf8! quiting");
        return;
    }
    // Removes trailing whitespace
    text = g_strchomp(text);
    
    temp = text;
    
    // Set first character to uppercase
    c = g_utf8_get_char(temp);
    strncpy(text, utf8_character, g_unichar_to_utf8(g_unichar_toupper(c), utf8_character));
    
    // Uppercase first character of each word, except for 'exempt[]' words lists
    while ( temp )
    {
        word1 = g_utf8_strchr(temp,-1,' ');
        word2 = g_utf8_strchr(temp,-1,'_');
        
        // Take the first string found (near beginning of string)
        if (word1 && word2)
            word = MIN(word1,word2);
        else if (word1)
            word = word1;
        else if (word2)
            word = word2;
        else
            break;
        
        // Go to first character of the word (char. after ' ' or '_')
        word = word+1;
        
        // Set uppercase the first character of this word
        c = g_utf8_get_char(word);
        strncpy(word, utf8_character, g_unichar_to_utf8(g_unichar_toupper(c), utf8_character));
        
        // Set lowercase the first character of this word if found in the exempt words list
        for (i=0; exempt[i]!=NULL; i++)
        {
            if (g_ascii_strncasecmp(exempt[i], word, strlen(exempt[i])) == 0)
            {
                c = g_utf8_get_char(word);
                strncpy(word, utf8_character, g_unichar_to_utf8(g_unichar_tolower(c), utf8_character));
                break;
            }
        }
        
        temp = word;
    }

    // Uppercase letter placed after some characters like '(', '[', '{'
    set_to_upper_case = FALSE;
    for (temp = text; *temp; temp = g_utf8_next_char(temp))
    {
        c = g_utf8_get_char(temp);
        set_to_upper_case_tmp = (  c == (gunichar)'('
                                || c == (gunichar)'['
                                || c == (gunichar)'{'
                                || c == (gunichar)'"'
                                ) ? TRUE : FALSE;

        if (set_to_upper_case && g_unichar_islower(c))
            strncpy(temp, utf8_character, g_unichar_to_utf8(g_unichar_toupper(c), utf8_character));

        set_to_upper_case = set_to_upper_case_tmp;
    }
    
}

void Scan_Process_Fields_Remove_Space (gchar *text)
{
    gchar *tmp, *tmp1;

    tmp = tmp1 = text;
    while (*tmp)
    {
        while (*tmp == ' ')
            tmp++;
        if (*tmp)
            *(tmp1++) = *(tmp++);
    }
    *tmp1 = '\0';
}

void Scan_Process_Fields_Insert_Space (gchar *text)
{
    gchar *iter;
    gunichar c;
    gint j;

    // FIXME: try to use realloc
    for (iter = g_utf8_next_char(text); *iter; iter = g_utf8_next_char(iter))    // i=1 to not consider first "uppercase" letter
    {
        c = g_utf8_get_char(iter);

        if (g_unichar_isupper(c))
        {
            for (j = strlen(iter); j > 0; j--)
                *(iter + j) = *(iter + j - 1);
            *iter = ' ';
            iter++;
        }
    }
}
void Scan_Process_Fields_Keep_One_Space (gchar *text)
{
    gchar *tmp, *tmp1;
    tmp = tmp1 = text;

    // Remove multiple consecutive underscores and spaces.
    while (*tmp1)
    {
        while (*tmp1 && *tmp1 != ' ' && *tmp1 != '_')
            *(tmp++) = *(tmp1++);
        if (!*tmp1)
            break;
        *(tmp++) = *(tmp1++);
        while (*tmp1 == ' ' || *tmp1 == '_')
            *tmp1++;
    }
    *tmp = '\0';
}

/*
 * Function to replace underscore '_' by a space
 */
void Scan_Convert_Underscore_Into_Space (gchar *string)
{
    gchar *tmp;

    while ((tmp=strchr(string,'_'))!=NULL)
        *tmp = ' ';
}

/*
 * Function to replace %20 by a space
 */
void Scan_Convert_P20_Into_Space (gchar *string)
{
    gchar *tmp, *tmp1;

    while ((tmp=strstr(string,"%20"))!=NULL)
    {
        tmp1 = tmp + 3;
        *(tmp++) = ' ';
        while (*tmp1)
            *(tmp++) = *(tmp1++);
        *tmp = '\0';
    }
}

/*
 * Function to replace space by '_'
 */
void Scan_Convert_Space_Into_Undescore (gchar *string)
{
    gchar *tmp;

    while ((tmp=strchr(string,' '))!=NULL)
        *tmp = '_';
}

/*
 * Replace something with something else ;)
 * Currently this only works with one character for each
 */
void Scan_Convert (gchar *string)
{
    gchar *tmp;
    gchar *from = gtk_editable_get_chars(GTK_EDITABLE(ProcessFieldsConvertFrom),0,-1 );
    gchar *to   = gtk_editable_get_chars(GTK_EDITABLE(ProcessFieldsConvertTo),0,-1 );

    if ( from && to && strlen(from)>0 && strlen(to)>0 )
        while ((tmp=strchr(string,*from))!=NULL)
            *tmp = *to;
}



/*
 * Return the field of a 'File_Tag' structure corresponding to the mask code
 */
gchar **Scan_Return_File_Tag_Field_From_Mask_Code (File_Tag *FileTag, gchar code)
{
    switch (code)
    {
        case 't':    /* Title */
            return &FileTag->title;
        case 'a':    /* Artist */
            return &FileTag->artist;
        case 'b':    /* Album */
            return &FileTag->album;
        case 'd':    /* Disc Number */
            return &FileTag->disc_number;
        case 'y':    /* Year */
            return &FileTag->year;
        case 'n':    /* Track */
            return &FileTag->track;
        case 'l':    /* Track Total */
            return &FileTag->track_total;
        case 'g':    /* Genre */
            return &FileTag->genre;
        case 'c':    /* Comment */
            return &FileTag->comment;
        case 'p':    /* Composer */
            return &FileTag->composer;
        case 'o':    /* Orig. Artist */
            return &FileTag->orig_artist;
        case 'r':    /* Copyright */
            return &FileTag->copyright;
        case 'u':    /* URL */
            return &FileTag->url;
        case 'e':    /* Encoded by */
            return &FileTag->encoded_by;
        case 'i':    /* Ignored */
            return NULL;
        default:
            g_print("Scanner: Invalid code '%%%c' found!\n",code);
            return NULL;
    }
}



/******************
 * Scanner Window *
 ******************/
#include "../pixmaps/black.xpm"
#include "../pixmaps/blackwhite.xpm"
void Open_ScannerWindow (gint scanner_type)
{
    GtkWidget *ScanVBox;
    GtkWidget *HBox1, *HBox2, *HBox4, *VBox, *hbox, *vbox;
    GtkWidget *Table;
    GtkWidget *Label;
    GtkWidget *Button;
    GtkWidget *Separator;
    GtkWidget *Icon;
    GtkWidget *EventBox;
    GtkTooltips *Tips;
    GtkWidget *MaskStatusIconBox;
    GList *pf_cb_group1 = NULL;
    GList *pf_cb_group2 = NULL;
    GList *pf_cb_group3 = NULL;
    GtkTreeViewColumn * column;
    GtkCellRenderer *renderer;

    /* Check if already opened */
    if (ScannerWindow)
    {
        //gdk_window_show(ScannerWindow->window);
        gdk_window_raise(ScannerWindow->window);
        if (ScannerOptionCombo)
        {
            gtk_combo_box_set_active(GTK_COMBO_BOX(ScannerOptionCombo), scanner_type);
        }
        return;
    }

    if ( scanner_type < SCANNER_FILL_TAG
    ||   scanner_type > SCANNER_PROCESS_FIELDS)
        scanner_type = SCANNER_FILL_TAG;

    /* The window */
    ScannerWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    /* Config */
    gtk_container_set_border_width(GTK_CONTAINER(ScannerWindow), 5);
    gtk_window_set_resizable(GTK_WINDOW(ScannerWindow), FALSE);
    gtk_window_set_wmclass(GTK_WINDOW(ScannerWindow),"EasyTag_Scanner","easytag"); // Patch from Nikolai Prokoschenko (Debian)
    if (SCANNER_WINDOW_ON_TOP)
        gtk_window_set_transient_for(GTK_WINDOW(ScannerWindow),GTK_WINDOW(MainWindow));

    /* The init position is define below, cause the scanner window must be showed before
     * to be able to move it. */

    /* Title */
    gtk_window_set_title(GTK_WINDOW(ScannerWindow),_("Tag and File Name scan"));

    /* Signals connection */
    g_signal_connect(G_OBJECT(ScannerWindow),"destroy",G_CALLBACK(ScannerWindow_Quit),NULL);
    g_signal_connect(G_OBJECT(ScannerWindow),"delete_event",G_CALLBACK(ScannerWindow_Quit),NULL);
    g_signal_connect(G_OBJECT(ScannerWindow),"key_press_event",G_CALLBACK(ScannerWindow_Key_Press),NULL);

    /* The tooltips */
    Tips = gtk_tooltips_new_1();

    /* The main vbox */
    ScanVBox = gtk_vbox_new(FALSE,2);
    gtk_container_add(GTK_CONTAINER(ScannerWindow),ScanVBox);


    /*
     * The hbox for mode buttons + buttons + what to scan
     */
    HBox1 = gtk_hbox_new(FALSE,0);
    gtk_box_pack_start(GTK_BOX(ScanVBox),HBox1,FALSE,FALSE,0);

    /* Option Menu */
    Label = gtk_label_new(_("Scanner :"));
    gtk_box_pack_start(GTK_BOX(HBox1),Label,FALSE,FALSE,0);

    EventBox = gtk_event_box_new();
    ScannerOptionCombo = gtk_combo_box_new_text();
    gtk_container_add(GTK_CONTAINER(EventBox),ScannerOptionCombo);
    gtk_box_pack_start(GTK_BOX(HBox1),EventBox,TRUE,TRUE,2);
    gtk_widget_set_size_request(ScannerOptionCombo, 160, -1);

    /* Option for Tag */
    gtk_combo_box_append_text(GTK_COMBO_BOX(ScannerOptionCombo), _(Scanner_Option_Menu_Items[SCANNER_FILL_TAG]));

    /* Option for FileName */
    gtk_combo_box_append_text(GTK_COMBO_BOX(ScannerOptionCombo), _(Scanner_Option_Menu_Items[SCANNER_RENAME_FILE]));

    /* Option for ProcessFields */
    gtk_combo_box_append_text(GTK_COMBO_BOX(ScannerOptionCombo), _(Scanner_Option_Menu_Items[SCANNER_PROCESS_FIELDS]));

    // Selection of the item made at the end of the function
    gtk_tooltips_set_tip(Tips, EventBox, _("Select the type of scanner to use"), NULL);
    g_signal_connect(G_OBJECT(ScannerOptionCombo), "changed", G_CALLBACK(Scanner_Option_Menu_Activate_Item), NULL);

    /* 'Scan selected files' button */
    SWScanButton = gtk_button_new();
    Icon = gtk_image_new_from_stock("easytag-scan", GTK_ICON_SIZE_BUTTON);
    gtk_container_add(GTK_CONTAINER(SWScanButton),Icon);
    gtk_box_pack_start(GTK_BOX(HBox1),SWScanButton,FALSE,FALSE,0);
    gtk_button_set_relief(GTK_BUTTON(SWScanButton),GTK_RELIEF_NONE);
    gtk_tooltips_set_tip(Tips,SWScanButton,_("Open scanner window / Scan selected files"),NULL);
    g_signal_connect(G_OBJECT(SWScanButton),"clicked",G_CALLBACK(Action_Scan_Selected_Files),NULL);

    /* Separator line */
    Separator = gtk_vseparator_new();
    gtk_box_pack_start(GTK_BOX(HBox1),Separator,FALSE,FALSE,2);

    /* Options button */
    Button = gtk_button_new();
    Icon = gtk_image_new_from_stock(GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_BUTTON);
    gtk_container_add(GTK_CONTAINER(Button),Icon);
    gtk_box_pack_start(GTK_BOX(HBox1),Button,FALSE,FALSE,0);
    gtk_button_set_relief(GTK_BUTTON(Button),GTK_RELIEF_NONE);
    gtk_tooltips_set_tip(Tips,Button,_("Scanner Options"),NULL);
    g_signal_connect(G_OBJECT(Button),"clicked",G_CALLBACK(Scan_Option_Button),NULL);

    /* Mask Editor button */
    MaskEditorButton = gtk_toggle_button_new();
    Icon = gtk_image_new_from_stock("easytag-mask", GTK_ICON_SIZE_BUTTON);
    gtk_container_add(GTK_CONTAINER(MaskEditorButton),Icon);
    gtk_box_pack_start(GTK_BOX(HBox1),MaskEditorButton,FALSE,FALSE,0);
    gtk_button_set_relief(GTK_BUTTON(MaskEditorButton),GTK_RELIEF_NONE);
    gtk_tooltips_set_tip(Tips,MaskEditorButton,_("Show / Hide Masks Editor"),NULL);
    g_signal_connect(G_OBJECT(MaskEditorButton),"toggled",G_CALLBACK(Scan_Toggle_Mask_Editor_Button),NULL);

    /* Legend button */
    LegendButton = gtk_toggle_button_new();
    Icon = gtk_image_new_from_stock(GTK_STOCK_HELP, GTK_ICON_SIZE_BUTTON);
    gtk_container_add(GTK_CONTAINER(LegendButton),Icon);
    gtk_box_pack_start(GTK_BOX(HBox1),LegendButton,FALSE,FALSE,0);
    gtk_button_set_relief(GTK_BUTTON(LegendButton),GTK_RELIEF_NONE);
    gtk_tooltips_set_tip(Tips,LegendButton,_("Show / Hide Legend"),NULL);
    g_signal_connect(G_OBJECT(LegendButton),"toggled",G_CALLBACK(Scan_Toggle_Legend_Button),NULL);

    /* Close button */
    Button = gtk_button_new();
    Icon = gtk_image_new_from_stock(GTK_STOCK_QUIT, GTK_ICON_SIZE_BUTTON);
    gtk_container_add(GTK_CONTAINER(Button),Icon);
    gtk_box_pack_start(GTK_BOX(HBox1),Button,FALSE,FALSE,0);
    gtk_button_set_relief(GTK_BUTTON(Button),GTK_RELIEF_NONE);
    gtk_tooltips_set_tip(Tips,Button,_("Close this window"),NULL);
    g_signal_connect(G_OBJECT(Button),"clicked",G_CALLBACK(ScannerWindow_Quit),NULL);

    /*
     * Frame for Scan Tag
     */
    ScanTagFrame = gtk_frame_new (_(Scanner_Option_Menu_Items[0]));
    gtk_box_pack_start(GTK_BOX(ScanVBox),ScanTagFrame,FALSE,FALSE,0);

    vbox = gtk_vbox_new(FALSE,4);
    gtk_container_add(GTK_CONTAINER(ScanTagFrame),vbox);
    gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
    gtk_widget_show(vbox);

    /* The combo box + Status icon */
    HBox2 = gtk_hbox_new(FALSE,2);
    gtk_box_pack_start(GTK_BOX(vbox),HBox2,TRUE,TRUE,0);
    
    // Set up list model which is used both by the combobox and the editor
    ScanTagListModel = gtk_list_store_new(MASK_EDITOR_COUNT, G_TYPE_STRING);

    // The combo box to select the mask to apply
    ScanTagMaskCombo = gtk_combo_box_entry_new();
    gtk_combo_box_set_model(GTK_COMBO_BOX(ScanTagMaskCombo), GTK_TREE_MODEL(ScanTagListModel));
    gtk_combo_box_entry_set_text_column(GTK_COMBO_BOX_ENTRY(ScanTagMaskCombo), MASK_EDITOR_TEXT);

    gtk_box_pack_start(GTK_BOX(HBox2),ScanTagMaskCombo,TRUE,TRUE,2);
    gtk_tooltips_set_tip(Tips,GTK_WIDGET(GTK_ENTRY(GTK_BIN(ScanTagMaskCombo)->child)),
        _("Select or type in a mask using codes (see Legend) to parse file name and "
        "path. Used to fill in tag fields."),NULL);
    // Signal to generate preview (preview of the new tag values)
    g_signal_connect_swapped(G_OBJECT(GTK_ENTRY(GTK_BIN(ScanTagMaskCombo)->child)),"changed",
        G_CALLBACK(Scan_Fill_Tag_Generate_Preview),NULL);

    // Load masks into the combobox from a file
    Load_Scan_Tag_Masks_List(ScanTagListModel, MASK_EDITOR_TEXT, Scan_Masks);
    if (SCAN_TAG_DEFAULT_MASK)
    {
        Add_String_To_Combo_List(ScanTagListModel, SCAN_TAG_DEFAULT_MASK);
        gtk_entry_set_text(GTK_ENTRY(GTK_BIN(ScanTagMaskCombo)->child), SCAN_TAG_DEFAULT_MASK);
    }else
    {
        gtk_combo_box_set_active(GTK_COMBO_BOX(ScanTagMaskCombo), 0);
    }

    // Mask status icon
    MaskStatusIconBox = Create_Pixmap_Icon_With_Event_Box("easytag-forbidden");
    gtk_box_pack_start(GTK_BOX(HBox2),MaskStatusIconBox,FALSE,FALSE,0);
    gtk_tooltips_set_tip(Tips,MaskStatusIconBox,_("Invalid Scanner Mask"),NULL);
    // Signal connection to check if mask is correct into the mask entry
    g_signal_connect_swapped(G_OBJECT(GTK_BIN(ScanTagMaskCombo)->child),"changed",
        G_CALLBACK(Scan_Check_Scan_Tag_Mask),GTK_OBJECT(MaskStatusIconBox));

    // Preview label
    FillTagPreviewLabel = gtk_label_new(_("Fill tag preview..."));
    gtk_label_set_line_wrap(GTK_LABEL(FillTagPreviewLabel),TRUE);
    gtk_widget_show(FillTagPreviewLabel);
    gtk_box_pack_start(GTK_BOX(vbox),FillTagPreviewLabel,TRUE,TRUE,0);

    /*
     * Frame for Rename File
     */
    RenameFileFrame = gtk_frame_new (_(Scanner_Option_Menu_Items[1]));
    gtk_box_pack_start(GTK_BOX(ScanVBox),RenameFileFrame,FALSE,FALSE,0);

    vbox = gtk_vbox_new(FALSE,4);
    gtk_container_add(GTK_CONTAINER(RenameFileFrame),vbox);
    gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
    gtk_widget_show(vbox);

    /* The button to prefix path + combo box + Status icon */
    HBox4 = gtk_hbox_new(FALSE,2);
    gtk_box_pack_start(GTK_BOX(vbox),HBox4,TRUE,TRUE,0);
    
    // Button to prefix path
    RenameFilePrefixPathButton = gtk_button_new();
    Icon = gtk_image_new_from_stock("easytag-add-folder", GTK_ICON_SIZE_BUTTON);
    gtk_container_add(GTK_CONTAINER(RenameFilePrefixPathButton),Icon);
    gtk_box_pack_start(GTK_BOX(HBox4),RenameFilePrefixPathButton,FALSE,FALSE,0);
    gtk_button_set_relief(GTK_BUTTON(RenameFilePrefixPathButton),GTK_RELIEF_NONE);
    g_signal_connect(G_OBJECT(RenameFilePrefixPathButton),"clicked",G_CALLBACK(Scan_Rename_File_Prefix_Path),NULL);
    gtk_tooltips_set_tip(Tips,RenameFilePrefixPathButton,_("Prefix mask with current path"),NULL);
    
    // Set up list model which is used both by the combobox and the editor
    RenameFileListModel = gtk_list_store_new(MASK_EDITOR_COUNT, G_TYPE_STRING);

    // The combo box to select the mask to apply
    RenameFileMaskCombo = gtk_combo_box_entry_new();
    gtk_combo_box_set_model(GTK_COMBO_BOX(RenameFileMaskCombo), GTK_TREE_MODEL(RenameFileListModel));
    gtk_combo_box_entry_set_text_column(GTK_COMBO_BOX_ENTRY(RenameFileMaskCombo), MASK_EDITOR_TEXT);

    gtk_box_pack_start(GTK_BOX(HBox4),RenameFileMaskCombo,TRUE,TRUE,2);
    gtk_container_set_border_width(GTK_CONTAINER(HBox4), 2);
    gtk_tooltips_set_tip(Tips,GTK_WIDGET(GTK_ENTRY(GTK_BIN(RenameFileMaskCombo)->child)),
        _("Select or type in a mask using codes (see Legend) to parse tag fields. "
        "Used to rename the file.\nUse / to make directories. If the first character "
        "is /, it's a absolute path, otherwise is relative to the old path."),NULL);
    // Signal to generate preview (preview of the new filename)
    g_signal_connect_swapped(G_OBJECT(GTK_ENTRY(GTK_BIN(RenameFileMaskCombo)->child)),"changed",
        G_CALLBACK(Scan_Rename_File_Generate_Preview),NULL);

    // Load masks into the combobox from a file
    Load_Rename_File_Masks_List(RenameFileListModel, MASK_EDITOR_TEXT, Rename_File_Masks);
    if (RENAME_FILE_DEFAULT_MASK)
    {
        Add_String_To_Combo_List(RenameFileListModel, RENAME_FILE_DEFAULT_MASK);
        gtk_entry_set_text(GTK_ENTRY(GTK_BIN(RenameFileMaskCombo)->child), RENAME_FILE_DEFAULT_MASK);
    }else
    {
        gtk_combo_box_set_active(GTK_COMBO_BOX(RenameFileMaskCombo), 0);
    }

    // Mask status icon
    MaskStatusIconBox = Create_Pixmap_Icon_With_Event_Box("easytag-forbidden");
    gtk_box_pack_start(GTK_BOX(HBox4),MaskStatusIconBox,FALSE,FALSE,0);
    gtk_tooltips_set_tip(Tips,MaskStatusIconBox,_("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(RenameFileMaskCombo)->child)),"changed",
        G_CALLBACK(Scan_Check_Rename_File_Mask),G_OBJECT(MaskStatusIconBox));

    /* Preview label */
    RenameFilePreviewLabel = gtk_label_new(_("Rename file preview..."));
    gtk_label_set_line_wrap(GTK_LABEL(RenameFilePreviewLabel),TRUE);
    gtk_widget_show(RenameFilePreviewLabel);
    gtk_box_pack_start(GTK_BOX(vbox),RenameFilePreviewLabel,TRUE,TRUE,0);

    /*
     * Frame for Processing Fields
     */
    ProcessFieldsFrame = gtk_frame_new (_(Scanner_Option_Menu_Items[2]));
    gtk_box_pack_start(GTK_BOX(ScanVBox),ProcessFieldsFrame,FALSE,FALSE,0);

    VBox = gtk_vbox_new(FALSE,0);
    gtk_container_add(GTK_CONTAINER(ProcessFieldsFrame),VBox);
    gtk_container_set_border_width(GTK_CONTAINER(VBox), 4);
    gtk_widget_show(VBox);

    /* Group: select entry fields to process */
    hbox = gtk_hbox_new(FALSE,0);
    gtk_box_pack_start(GTK_BOX(VBox),hbox,FALSE,FALSE,2);
    EventBox = gtk_event_box_new();
    Label = gtk_label_new(_("Select fields:"));
    gtk_box_pack_start(GTK_BOX(hbox),EventBox,FALSE,FALSE,2);
    gtk_container_add(GTK_CONTAINER(EventBox),Label);
    gtk_tooltips_set_tip(Tips,EventBox,_("The buttons on the right represent the fields which can "
        "be processed. Select those who interest you."),NULL);
    // Advice for Translators : set the first letter of filename translated
    ProcessFileNameField = gtk_toggle_button_new_with_label(   _("F"));    
    gtk_tooltips_set_tip(Tips,ProcessFileNameField,            _("Process file name field"),NULL);
    // Advice for Translators : set the first letter of title translated
    ProcessTitleField = gtk_toggle_button_new_with_label(      _("T"));    
    gtk_tooltips_set_tip(Tips,ProcessTitleField,               _("Process title field"),NULL);
    // Advice for Translators : set the first letter of artist translated
    ProcessArtistField = gtk_toggle_button_new_with_label(     _("Ar"));    
    gtk_tooltips_set_tip(Tips,ProcessArtistField,              _("Process file artist field"),NULL);
    // Advice for Translators : set the first letter of album translated
    ProcessAlbumField = gtk_toggle_button_new_with_label(      _("Al"));    
    gtk_tooltips_set_tip(Tips,ProcessAlbumField,               _("Process album field"),NULL);
    // Advice for Translators : set the first letter of genre translated
    ProcessGenreField = gtk_toggle_button_new_with_label(      _("G"));    
    gtk_tooltips_set_tip(Tips,ProcessGenreField,               _("Process genre field"),NULL);
    // Advice for Translators : set the first letter of comment translated
    ProcessCommentField = gtk_toggle_button_new_with_label(    _("Cm"));    
    gtk_tooltips_set_tip(Tips,ProcessCommentField,             _("Process comment field"),NULL);
    // Advice for Translators : set the first letter of composer translated
    ProcessComposerField = gtk_toggle_button_new_with_label(   _("Cp"));    
    gtk_tooltips_set_tip(Tips,ProcessComposerField,            _("Process composer field"),NULL);
    // Advice for Translators : set the first letter of orig artist translated
    ProcessOrigArtistField = gtk_toggle_button_new_with_label( _("O"));    
    gtk_tooltips_set_tip(Tips,ProcessOrigArtistField,          _("Process original artist field"),NULL);
    // Advice for Translators : set the first letter of copyright translated
    ProcessCopyrightField = gtk_toggle_button_new_with_label(  _("Cr"));    
    gtk_tooltips_set_tip(Tips,ProcessCopyrightField,           _("Process copyright field"),NULL);
    // Advice for Translators : set the first letter of URL translated
    ProcessURLField = gtk_toggle_button_new_with_label(        _("U"));    
    gtk_tooltips_set_tip(Tips,ProcessURLField,                 _("Process URL field"),NULL);
    // Advice for Translators : set the first letter of encoder name translated
    ProcessEncodedByField = gtk_toggle_button_new_with_label(  _("E"));    
    gtk_tooltips_set_tip(Tips,ProcessEncodedByField,           _("Process encoder name field"),NULL);
    gtk_box_pack_start(GTK_BOX(hbox),ProcessFileNameField,   TRUE,TRUE,2);
    gtk_box_pack_start(GTK_BOX(hbox),ProcessTitleField,      TRUE,TRUE,2);
    gtk_box_pack_start(GTK_BOX(hbox),ProcessArtistField,     TRUE,TRUE,2);
    gtk_box_pack_start(GTK_BOX(hbox),ProcessAlbumField,      TRUE,TRUE,2);
    gtk_box_pack_start(GTK_BOX(hbox),ProcessGenreField,      TRUE,TRUE,2);
    gtk_box_pack_start(GTK_BOX(hbox),ProcessCommentField,    TRUE,TRUE,2);
    gtk_box_pack_start(GTK_BOX(hbox),ProcessComposerField,   TRUE,TRUE,2);
    gtk_box_pack_start(GTK_BOX(hbox),ProcessOrigArtistField, TRUE,TRUE,2);
    gtk_box_pack_start(GTK_BOX(hbox),ProcessCopyrightField,  TRUE,TRUE,2);
    gtk_box_pack_start(GTK_BOX(hbox),ProcessURLField,        TRUE,TRUE,2);
    gtk_box_pack_start(GTK_BOX(hbox),ProcessEncodedByField,  TRUE,TRUE,2);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessFileNameField),   PROCESS_FILENAME_FIELD);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessTitleField),      PROCESS_TITLE_FIELD);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessArtistField),     PROCESS_ARTIST_FIELD);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessAlbumField),      PROCESS_ALBUM_FIELD);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessGenreField),      PROCESS_GENRE_FIELD);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessCommentField),    PROCESS_COMMENT_FIELD);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessComposerField),   PROCESS_COMPOSER_FIELD);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessOrigArtistField), PROCESS_ORIG_ARTIST_FIELD);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessCopyrightField),  PROCESS_COPYRIGHT_FIELD);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessURLField),        PROCESS_URL_FIELD);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessEncodedByField),  PROCESS_ENCODED_BY_FIELD);
    g_signal_connect(G_OBJECT(ProcessFileNameField),   "toggled",G_CALLBACK(Select_Fields_Set_Sensitive),NULL);
    g_signal_connect(G_OBJECT(ProcessTitleField),      "toggled",G_CALLBACK(Select_Fields_Set_Sensitive),NULL);
    g_signal_connect(G_OBJECT(ProcessArtistField),     "toggled",G_CALLBACK(Select_Fields_Set_Sensitive),NULL);
    g_signal_connect(G_OBJECT(ProcessAlbumField),      "toggled",G_CALLBACK(Select_Fields_Set_Sensitive),NULL);
    g_signal_connect(G_OBJECT(ProcessGenreField),      "toggled",G_CALLBACK(Select_Fields_Set_Sensitive),NULL);
    g_signal_connect(G_OBJECT(ProcessCommentField),    "toggled",G_CALLBACK(Select_Fields_Set_Sensitive),NULL);
    g_signal_connect(G_OBJECT(ProcessComposerField),   "toggled",G_CALLBACK(Select_Fields_Set_Sensitive),NULL);
    g_signal_connect(G_OBJECT(ProcessOrigArtistField), "toggled",G_CALLBACK(Select_Fields_Set_Sensitive),NULL);
    g_signal_connect(G_OBJECT(ProcessCopyrightField),  "toggled",G_CALLBACK(Select_Fields_Set_Sensitive),NULL);
    g_signal_connect(G_OBJECT(ProcessURLField),        "toggled",G_CALLBACK(Select_Fields_Set_Sensitive),NULL);
    g_signal_connect(G_OBJECT(ProcessEncodedByField),  "toggled",G_CALLBACK(Select_Fields_Set_Sensitive),NULL);
    /* The small buttons */
    vbox = gtk_vbox_new(FALSE,2);
    gtk_box_pack_start(GTK_BOX(hbox),vbox,FALSE,FALSE,0);
    Button = gtk_button_new();
    g_signal_connect(G_OBJECT(Button),"clicked",G_CALLBACK(Select_Fields_Invert_Selection),NULL);
    gtk_box_pack_start(GTK_BOX(vbox),Button,FALSE,FALSE,0);
    gtk_widget_set_size_request(Button, 12, 12);
    //Icon = gtk_image_new_from_stock("easytag-blackwhite", GTK_ICON_SIZE_BUTTON);
    Icon = Create_Xpm_Image((const char **)blackwhite_xpm);
    GTK_WIDGET_UNSET_FLAGS(Button,GTK_CAN_DEFAULT); // To have enought space to display the icon
    GTK_WIDGET_UNSET_FLAGS(Button,GTK_CAN_FOCUS);
    gtk_container_add(GTK_CONTAINER(Button),Icon);
    gtk_tooltips_set_tip(Tips,Button,_("Invert Selection"),NULL);
    Button = gtk_button_new();
    g_signal_connect(G_OBJECT(Button),"clicked",G_CALLBACK(Select_Fields_Select_Unselect_All),NULL);
    gtk_box_pack_start(GTK_BOX(vbox),Button,FALSE,FALSE,0);
    gtk_widget_set_size_request(Button, 12, 12);
    Icon = Create_Xpm_Image((const char **)black_xpm);
    GTK_WIDGET_UNSET_FLAGS(Button,GTK_CAN_DEFAULT); // To have enought space to display the icon
    GTK_WIDGET_UNSET_FLAGS(Button,GTK_CAN_FOCUS);
    gtk_container_add(GTK_CONTAINER(Button),Icon);
    gtk_tooltips_set_tip(Tips,Button,_("Select/Unselect All."),NULL);

    /* Separator line */
    Separator = gtk_hseparator_new();
    gtk_box_pack_start(GTK_BOX(VBox),Separator,FALSE,FALSE,0);

    /* Group: character conversion */
    ProcessFieldsConvertIntoSpace = gtk_check_button_new_with_label(_("Convert '_' and '%20' to ' '"));
    ProcessFieldsConvertSpace     = gtk_check_button_new_with_label(_("Convert ' ' to '_'"));
    gtk_box_pack_start(GTK_BOX(VBox),ProcessFieldsConvertIntoSpace,FALSE,FALSE,0);
    gtk_box_pack_start(GTK_BOX(VBox),ProcessFieldsConvertSpace,    FALSE,FALSE,0);
    hbox = gtk_hbox_new(FALSE,2);
    ProcessFieldsConvert          = gtk_check_button_new_with_label(_("Convert :"));  // Patch from Ben Hearsum, Oct. 3, 2003
    ProcessFieldsConvertTo        = gtk_entry_new();
    ProcessFieldsConvertLabelTo   = gtk_label_new(_("to : ")); // A "space" at the end to allow an other traduction for "to :" (needed in French!)
    ProcessFieldsConvertFrom      = gtk_entry_new();
    gtk_entry_set_max_length(GTK_ENTRY(ProcessFieldsConvertTo), 1);
    gtk_entry_set_max_length(GTK_ENTRY(ProcessFieldsConvertFrom), 1);
    gtk_widget_set_size_request(ProcessFieldsConvertTo,40,-1);
    gtk_widget_set_size_request(ProcessFieldsConvertFrom,40,-1);
    gtk_box_pack_start(GTK_BOX(hbox),ProcessFieldsConvert,       FALSE,FALSE,0);
    gtk_box_pack_start(GTK_BOX(hbox),ProcessFieldsConvertFrom,   FALSE,FALSE,0);
    gtk_box_pack_start(GTK_BOX(hbox),ProcessFieldsConvertLabelTo,FALSE,FALSE,4);
    gtk_box_pack_start(GTK_BOX(hbox),ProcessFieldsConvertTo,     FALSE,FALSE,0);
    gtk_box_pack_start(GTK_BOX(VBox),hbox,FALSE,FALSE,0);
    /* List creation for check buttons in group */
    pf_cb_group1 = g_list_append (pf_cb_group1,ProcessFieldsConvertIntoSpace);
    pf_cb_group1 = g_list_append (pf_cb_group1,ProcessFieldsConvertSpace);
    pf_cb_group1 = g_list_append (pf_cb_group1,ProcessFieldsConvert);
    /* Toggled signals */
    g_signal_connect(G_OBJECT(ProcessFieldsConvertIntoSpace),"toggled",G_CALLBACK(Process_Fields_Check_Button_Toggled),pf_cb_group1);
    g_signal_connect(G_OBJECT(ProcessFieldsConvertSpace),    "toggled",G_CALLBACK(Process_Fields_Check_Button_Toggled),pf_cb_group1);
    g_signal_connect(G_OBJECT(ProcessFieldsConvert),         "toggled",G_CALLBACK(Process_Fields_Check_Button_Toggled),pf_cb_group1);
    g_signal_connect(G_OBJECT(ProcessFieldsConvert),         "toggled",G_CALLBACK(Process_Fields_Convert_Check_Button_Toggled),NULL);
    /* Set check buttons to init value */
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessFieldsConvertIntoSpace),PF_CONVERT_INTO_SPACE);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessFieldsConvertSpace),PF_CONVERT_SPACE);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessFieldsConvert),PF_CONVERT);
    /* Tooltips */
    gtk_tooltips_set_tip(Tips,ProcessFieldsConvertIntoSpace,
        _("The underscore character or the string '%20' are replaced by one space. "
          "Example, before: 'Text%20In%20An_Entry', after: 'Text In An Entry'."),NULL);
    gtk_tooltips_set_tip(Tips,ProcessFieldsConvertSpace,
        _("The space character is replaced by one underscore character. "
          "Example, before: 'Text In An Entry', after: 'Text_In_An_Entry'."),NULL);
    gtk_tooltips_set_tip(Tips,ProcessFieldsConvert,_("Replace a character by an other one."),NULL);

    /* Separator line */
    Separator = gtk_hseparator_new();
    gtk_box_pack_start(GTK_BOX(VBox),Separator,FALSE,FALSE,0);

    /* Group: capitalize, ... */
    ProcessFieldsAllUppercase = gtk_check_button_new_with_label         (_("All uppercase"));
    ProcessFieldsAllDowncase  = gtk_check_button_new_with_label         (_("All downcase"));
    ProcessFieldsFirstLetterUppercase  = gtk_check_button_new_with_label(_("First letter uppercase"));
    ProcessFieldsFirstLettersUppercase = gtk_check_button_new_with_label(_("First letter uppercase of each word"));
    gtk_box_pack_start(GTK_BOX(VBox),ProcessFieldsAllUppercase,         FALSE,FALSE,0);
    gtk_box_pack_start(GTK_BOX(VBox),ProcessFieldsAllDowncase,          FALSE,FALSE,0);
    gtk_box_pack_start(GTK_BOX(VBox),ProcessFieldsFirstLetterUppercase, FALSE,FALSE,0);
    gtk_box_pack_start(GTK_BOX(VBox),ProcessFieldsFirstLettersUppercase,FALSE,FALSE,0);
    /* List creation for check buttons in group */
    pf_cb_group2 = g_list_append(pf_cb_group2,ProcessFieldsAllUppercase);
    pf_cb_group2 = g_list_append(pf_cb_group2,ProcessFieldsAllDowncase);
    pf_cb_group2 = g_list_append(pf_cb_group2,ProcessFieldsFirstLetterUppercase);
    pf_cb_group2 = g_list_append(pf_cb_group2,ProcessFieldsFirstLettersUppercase);
    /* Toggled signals */
    g_signal_connect(G_OBJECT(ProcessFieldsAllUppercase),"toggled",G_CALLBACK(Process_Fields_Check_Button_Toggled),pf_cb_group2);
    g_signal_connect(G_OBJECT(ProcessFieldsAllDowncase), "toggled",G_CALLBACK(Process_Fields_Check_Button_Toggled),pf_cb_group2);
    g_signal_connect(G_OBJECT(ProcessFieldsFirstLetterUppercase),"toggled",G_CALLBACK(Process_Fields_Check_Button_Toggled),pf_cb_group2);
    g_signal_connect(G_OBJECT(ProcessFieldsFirstLettersUppercase),"toggled",G_CALLBACK(Process_Fields_Check_Button_Toggled),pf_cb_group2);
    /* Set check buttons to init value */
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessFieldsAllUppercase),PF_CONVERT_ALL_UPPERCASE);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessFieldsAllDowncase),PF_CONVERT_ALL_DOWNCASE);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessFieldsFirstLetterUppercase),PF_CONVERT_FIRST_LETTER_UPPERCASE);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessFieldsFirstLettersUppercase),PF_CONVERT_FIRST_LETTERS_UPPERCASE);
    /* Tooltips */
    gtk_tooltips_set_tip(Tips,ProcessFieldsAllUppercase,
        _("Convert all words in all fields to upper case. "
          "Example, before: 'Text IN AN entry', after: 'TEXT IN AN ENTRY'."),NULL);
    gtk_tooltips_set_tip(Tips,ProcessFieldsAllDowncase,
        _("Convert all words in all fields to lower case. "
          "Example, before: 'TEXT IN an entry', after: 'text in an entry'."),NULL);
    gtk_tooltips_set_tip(Tips,ProcessFieldsFirstLetterUppercase,
        _("Convert the initial of the first word in all fields to upper case. "
          "Example, before: 'text IN An ENTRY', after: 'Text in an entry'."),NULL);
    gtk_tooltips_set_tip(Tips,ProcessFieldsFirstLettersUppercase,
        _("Convert the initial of each word in all fields to upper case. "
          "Example, before: 'Text in an ENTRY', after: 'Text In An Entry'."),NULL);

    /* Separator line */
    Separator = gtk_hseparator_new();
    gtk_box_pack_start(GTK_BOX(VBox),Separator,FALSE,FALSE,0);

    /* Group: insert/remove spaces */
    ProcessFieldsRemoveSpace = gtk_check_button_new_with_label(_("Remove spaces"));
    ProcessFieldsInsertSpace = gtk_check_button_new_with_label(_("Insert a space before an uppercase letter"));
    ProcessFieldsOnlyOneSpace = gtk_check_button_new_with_label(_("Remove duplicates of space or underscore"));
    gtk_box_pack_start(GTK_BOX(VBox),ProcessFieldsRemoveSpace,FALSE,FALSE,0);
    gtk_box_pack_start(GTK_BOX(VBox),ProcessFieldsInsertSpace,FALSE,FALSE,0);
    gtk_box_pack_start(GTK_BOX(VBox),ProcessFieldsOnlyOneSpace,FALSE,FALSE,0);
    /* List creation for check buttons in group */
    pf_cb_group3 = g_list_append (pf_cb_group3,ProcessFieldsRemoveSpace);
    pf_cb_group3 = g_list_append (pf_cb_group3,ProcessFieldsInsertSpace);
    pf_cb_group3 = g_list_append (pf_cb_group3,ProcessFieldsOnlyOneSpace);
    /* Toggled signals */
    g_signal_connect(G_OBJECT(ProcessFieldsRemoveSpace), "toggled",G_CALLBACK(Process_Fields_Check_Button_Toggled),pf_cb_group3);
    g_signal_connect(G_OBJECT(ProcessFieldsInsertSpace), "toggled",G_CALLBACK(Process_Fields_Check_Button_Toggled),pf_cb_group3);
    g_signal_connect(G_OBJECT(ProcessFieldsOnlyOneSpace),"toggled",G_CALLBACK(Process_Fields_Check_Button_Toggled),pf_cb_group3);
    /* Set check buttons to init value */
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessFieldsRemoveSpace),PF_REMOVE_SPACE);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessFieldsInsertSpace),PF_INSERT_SPACE);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessFieldsOnlyOneSpace),PF_ONLY_ONE_SPACE);
    /* Tooltips */
    gtk_tooltips_set_tip(Tips,ProcessFieldsRemoveSpace,
        _("All spaces between words are removed. "
          "Example, before: 'Text In An Entry', after: 'TextInAnEntry'."),NULL);
    gtk_tooltips_set_tip(Tips,ProcessFieldsInsertSpace,
        _("A space is inserted before each upper case letter. "
          "Example, before: 'TextInAnEntry', after: 'Text In An Entry'."),NULL);
    gtk_tooltips_set_tip(Tips,ProcessFieldsOnlyOneSpace,
        _("Duplicated spaces or underscores are removed. "
          "Example, before: 'Text__In__An   Entry', after: 'Text_In_An Entry'."),NULL);

    /*
     * Frame to display codes legend
     */
    LegendFrame = gtk_frame_new (_("Legend"));
    gtk_box_pack_start(GTK_BOX(ScanVBox),LegendFrame,FALSE,FALSE,0);
    /* Legend labels */
    Table = gtk_table_new(3,3,FALSE);
    gtk_container_add(GTK_CONTAINER(LegendFrame),Table);
    gtk_container_set_border_width(GTK_CONTAINER(Table),4);
    Label = gtk_label_new(_("%a : artist"));
    gtk_table_attach_defaults(GTK_TABLE(Table),Label,0,1,0,1);
    gtk_misc_set_alignment(GTK_MISC(Label),0,0.5);
    Label = gtk_label_new(_("%b : album"));
    gtk_table_attach_defaults(GTK_TABLE(Table),Label,0,1,1,2);
    gtk_misc_set_alignment(GTK_MISC(Label),0,0.5);
    Label = gtk_label_new(_("%c : comment"));
    gtk_table_attach_defaults(GTK_TABLE(Table),Label,0,1,2,3);
    gtk_misc_set_alignment(GTK_MISC(Label),0,0.5);
    Label = gtk_label_new(_("%p : composer"));
    gtk_table_attach_defaults(GTK_TABLE(Table),Label,0,1,3,4);
    gtk_misc_set_alignment(GTK_MISC(Label),0,0.5);
    Label = gtk_label_new(_("%r : copyright"));
    gtk_table_attach_defaults(GTK_TABLE(Table),Label,0,1,4,5);
    gtk_misc_set_alignment(GTK_MISC(Label),0,0.5);
    Label = gtk_label_new(_("%d : disc number"));
    gtk_table_attach_defaults(GTK_TABLE(Table),Label,1,2,0,1);
    gtk_misc_set_alignment(GTK_MISC(Label),0,0.5);
    Label = gtk_label_new(_("%e : encoded by"));
    gtk_table_attach_defaults(GTK_TABLE(Table),Label,1,2,1,2);
    gtk_misc_set_alignment(GTK_MISC(Label),0,0.5);
    Label = gtk_label_new(_("%g : genre"));
    gtk_table_attach_defaults(GTK_TABLE(Table),Label,1,2,2,3);
    gtk_misc_set_alignment(GTK_MISC(Label),0,0.5);
    Label = gtk_label_new(_("%i : ignored"));
    gtk_table_attach_defaults(GTK_TABLE(Table),Label,1,2,3,4);
    gtk_misc_set_alignment(GTK_MISC(Label),0,0.5);
    Label = gtk_label_new(_("%l : number of tracks"));
    gtk_table_attach_defaults(GTK_TABLE(Table),Label,1,2,4,5);
    gtk_misc_set_alignment(GTK_MISC(Label),0,0.5);
    Label = gtk_label_new(_("%o : orig. artist"));
    gtk_table_attach_defaults(GTK_TABLE(Table),Label,2,3,0,1);
    gtk_misc_set_alignment(GTK_MISC(Label),0,0.5);
    Label = gtk_label_new(_("%n : track"));
    gtk_table_attach_defaults(GTK_TABLE(Table),Label,2,3,1,2);
    gtk_misc_set_alignment(GTK_MISC(Label),0,0.5);
    Label = gtk_label_new(_("%t : title"));
    gtk_table_attach_defaults(GTK_TABLE(Table),Label,2,3,2,3);
    gtk_misc_set_alignment(GTK_MISC(Label),0,0.5);
    Label = gtk_label_new(_("%u : URL"));
    gtk_table_attach_defaults(GTK_TABLE(Table),Label,2,3,3,4);
    gtk_misc_set_alignment(GTK_MISC(Label),0,0.5);
    Label = gtk_label_new(_("%y : year"));
    gtk_table_attach_defaults(GTK_TABLE(Table),Label,2,3,4,5);
    gtk_misc_set_alignment(GTK_MISC(Label),0,0.5);

    /*
     * Masks Editor
     */
    MaskEditorFrame = gtk_frame_new (_("Mask Editor"));
    gtk_box_pack_start(GTK_BOX(ScanVBox),MaskEditorFrame,FALSE,FALSE,0);
    MaskEditorHBox = gtk_hbox_new(FALSE,4);
    gtk_container_add(GTK_CONTAINER(MaskEditorFrame),MaskEditorHBox);
    gtk_container_set_border_width(GTK_CONTAINER(MaskEditorHBox), 4);

    /* The editor part */
    MaskEditorVBox = gtk_vbox_new(FALSE,2);
    gtk_box_pack_start(GTK_BOX(MaskEditorHBox),MaskEditorVBox,TRUE,TRUE,0);
    MaskEditorScrollWindow = gtk_scrolled_window_new(NULL,NULL);
    gtk_box_pack_start(GTK_BOX(MaskEditorVBox),MaskEditorScrollWindow,TRUE,TRUE,0);
    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(MaskEditorScrollWindow),
                                   GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);
    gtk_widget_set_size_request(GTK_WIDGET(MaskEditorScrollWindow), -1, 101);

    /* The list */
    MaskEditorList = gtk_tree_view_new();
    gtk_tree_view_set_model(GTK_TREE_VIEW(MaskEditorList), GTK_TREE_MODEL(ScanTagListModel));

    renderer = gtk_cell_renderer_text_new();
    column = gtk_tree_view_column_new_with_attributes(NULL,
            renderer, "text", MASK_EDITOR_TEXT, NULL);
    gtk_tree_view_append_column(GTK_TREE_VIEW(MaskEditorList), column);
    gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(MaskEditorList), FALSE);
    gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(MaskEditorList)),
                GTK_SELECTION_MULTIPLE);
    gtk_tree_view_set_reorderable(GTK_TREE_VIEW(MaskEditorList), TRUE);
    gtk_container_add(GTK_CONTAINER(MaskEditorScrollWindow), MaskEditorList);
    g_signal_connect_after(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(MaskEditorList))),
            "changed", G_CALLBACK(Mask_Editor_List_Row_Selected), NULL);
    g_signal_connect(G_OBJECT(MaskEditorList), "key-press-event",
        G_CALLBACK(Mask_Editor_List_Key_Press), NULL);
    /* The entry */
    hbox = gtk_hbox_new(FALSE,2);
    gtk_box_pack_start(GTK_BOX(MaskEditorVBox),hbox,FALSE,FALSE,0);
    MaskEditorEntry = gtk_entry_new();
    gtk_box_pack_start(GTK_BOX(hbox),MaskEditorEntry,TRUE,TRUE,2);
    g_signal_connect(G_OBJECT(MaskEditorEntry),"changed",
        G_CALLBACK(Mask_Editor_Entry_Changed),NULL);
    // Mask status icon
    MaskStatusIconBox = Create_Pixmap_Icon_With_Event_Box("easytag-forbidden");
    gtk_box_pack_start(GTK_BOX(hbox),MaskStatusIconBox,FALSE,FALSE,0);
    gtk_tooltips_set_tip(Tips,MaskStatusIconBox,_("Invalid Scanner Mask"),NULL);
    // Signal connection to check if mask is correct into the mask entry
    g_signal_connect_swapped(G_OBJECT(MaskEditorEntry),"changed",
        G_CALLBACK(Scan_Check_Editor_Mask),G_OBJECT(MaskStatusIconBox));

    /* The buttons part */
    MaskEditorVBox = gtk_vbox_new(FALSE,0);
    gtk_box_pack_start(GTK_BOX(MaskEditorHBox),MaskEditorVBox,FALSE,FALSE,0);

    /* New mask button */
    MaskEditorNewButton = gtk_button_new();
    Icon = gtk_image_new_from_stock(GTK_STOCK_NEW, GTK_ICON_SIZE_SMALL_TOOLBAR);
    gtk_container_add(GTK_CONTAINER(MaskEditorNewButton),Icon);
    gtk_box_pack_start(GTK_BOX(MaskEditorVBox),MaskEditorNewButton,FALSE,FALSE,0);
    gtk_button_set_relief(GTK_BUTTON(MaskEditorNewButton),GTK_RELIEF_NONE);
    gtk_tooltips_set_tip(Tips,MaskEditorNewButton,_("Create New Mask"),NULL);
    g_signal_connect(G_OBJECT(MaskEditorNewButton),"clicked",
        G_CALLBACK(Mask_Editor_List_New),NULL);

    /* Move up mask button */
    MaskEditorUpButton = gtk_button_new();
    Icon = gtk_image_new_from_stock(GTK_STOCK_GO_UP, GTK_ICON_SIZE_SMALL_TOOLBAR);
    gtk_container_add(GTK_CONTAINER(MaskEditorUpButton),Icon);
    gtk_box_pack_start(GTK_BOX(MaskEditorVBox),MaskEditorUpButton,FALSE,FALSE,0);
    gtk_button_set_relief(GTK_BUTTON(MaskEditorUpButton),GTK_RELIEF_NONE);
    gtk_tooltips_set_tip(Tips,MaskEditorUpButton,_("Move Up this Mask"),NULL);
    g_signal_connect(G_OBJECT(MaskEditorUpButton),"clicked",
        G_CALLBACK(Mask_Editor_List_Move_Up),NULL);

    /* Move down mask button */
    MaskEditorDownButton = gtk_button_new();
    Icon = gtk_image_new_from_stock(GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_SMALL_TOOLBAR);
    gtk_container_add(GTK_CONTAINER(MaskEditorDownButton),Icon);
    gtk_box_pack_start(GTK_BOX(MaskEditorVBox),MaskEditorDownButton,FALSE,FALSE,0);
    gtk_button_set_relief(GTK_BUTTON(MaskEditorDownButton),GTK_RELIEF_NONE);
    gtk_tooltips_set_tip(Tips,MaskEditorDownButton,_("Move Down this Mask"),NULL);
    g_signal_connect(G_OBJECT(MaskEditorDownButton),"clicked",
        G_CALLBACK(Mask_Editor_List_Move_Down),NULL);

    /* Copy mask button */
    MaskEditorCopyButton = gtk_button_new();
    Icon = gtk_image_new_from_stock(GTK_STOCK_COPY, GTK_ICON_SIZE_SMALL_TOOLBAR);
    gtk_container_add(GTK_CONTAINER(MaskEditorCopyButton),Icon);
    gtk_box_pack_start(GTK_BOX(MaskEditorVBox),MaskEditorCopyButton,FALSE,FALSE,0);
    gtk_button_set_relief(GTK_BUTTON(MaskEditorCopyButton),GTK_RELIEF_NONE);
    gtk_tooltips_set_tip(Tips,MaskEditorCopyButton,_("Duplicate Mask"),NULL);
    g_signal_connect(G_OBJECT(MaskEditorCopyButton),"clicked",
        G_CALLBACK(Mask_Editor_List_Duplicate),NULL);

    /* Add mask button */
    MaskEditorAddButton = gtk_button_new();
    Icon = gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_SMALL_TOOLBAR);
    gtk_container_add(GTK_CONTAINER(MaskEditorAddButton),Icon);
    gtk_box_pack_start(GTK_BOX(MaskEditorVBox),MaskEditorAddButton,FALSE,FALSE,0);
    gtk_button_set_relief(GTK_BUTTON(MaskEditorAddButton),GTK_RELIEF_NONE);
    gtk_tooltips_set_tip(Tips,MaskEditorAddButton,_("Add Default Masks"),NULL);
    g_signal_connect(G_OBJECT(MaskEditorAddButton),"clicked",
        G_CALLBACK(Mask_Editor_List_Add),NULL);

    /* Remove mask button */
    MaskEditorRemoveButton = gtk_button_new();
    Icon = gtk_image_new_from_stock(GTK_STOCK_REMOVE, GTK_ICON_SIZE_SMALL_TOOLBAR);
    gtk_container_add(GTK_CONTAINER(MaskEditorRemoveButton),Icon);
    gtk_box_pack_start(GTK_BOX(MaskEditorVBox),MaskEditorRemoveButton,FALSE,FALSE,0);
    gtk_button_set_relief(GTK_BUTTON(MaskEditorRemoveButton),GTK_RELIEF_NONE);
    gtk_tooltips_set_tip(Tips,MaskEditorRemoveButton,_("Remove Mask"),NULL);
    g_signal_connect(G_OBJECT(MaskEditorRemoveButton),"clicked",
        G_CALLBACK(Mask_Editor_List_Remove),NULL);

    /* Save mask button */
    MaskEditorSaveButton = gtk_button_new();
    Icon = gtk_image_new_from_stock(GTK_STOCK_SAVE, GTK_ICON_SIZE_SMALL_TOOLBAR);
    gtk_container_add(GTK_CONTAINER(MaskEditorSaveButton),Icon);
    gtk_box_pack_end(GTK_BOX(MaskEditorVBox),MaskEditorSaveButton,FALSE,FALSE,0);
    gtk_button_set_relief(GTK_BUTTON(MaskEditorSaveButton),GTK_RELIEF_NONE);
    gtk_tooltips_set_tip(Tips,MaskEditorSaveButton,_("Save Masks"),NULL);
    g_signal_connect(G_OBJECT(MaskEditorSaveButton),"clicked",
        G_CALLBACK(Mask_Editor_List_Save_Button),NULL);

    gtk_widget_show(ScanVBox);
    gtk_widget_show_all(HBox1);
    gtk_widget_show_all(HBox2);
    gtk_widget_show_all(HBox4);
    gtk_widget_show(ScannerWindow);

    /* Init position of the scanner window */
    Scan_Set_Scanner_Window_Init_Position();

    /* To initialize the mask status icon and visibility */
    g_signal_emit_by_name(G_OBJECT(GTK_ENTRY(GTK_BIN(ScanTagMaskCombo)->child)),"changed");
    g_signal_emit_by_name(G_OBJECT(GTK_ENTRY(GTK_BIN(RenameFileMaskCombo)->child)),"changed");
    g_signal_emit_by_name(G_OBJECT(MaskEditorEntry),"changed");
    g_signal_emit_by_name(G_OBJECT(LegendButton),"toggled");        /* To hide legend frame */
    g_signal_emit_by_name(G_OBJECT(MaskEditorButton),"toggled");    /* To hide mask editor frame */
    g_signal_emit_by_name(G_OBJECT(ProcessFieldsConvert),"toggled");/* To enable / disable entries */

    // Activate the current menu in the option menu
    gtk_combo_box_set_active(GTK_COMBO_BOX(ScannerOptionCombo), scanner_type);
}

gboolean ScannerWindow_Key_Press (GtkWidget *window, GdkEvent *event)
{
    GdkEventKey *kevent;

    if (event && event->type == GDK_KEY_PRESS)
    {
        kevent = (GdkEventKey *)event;
        switch(kevent->keyval)
        {
            case GDK_Escape:
                ScannerWindow_Quit();
                break;
        }
    }
    return FALSE;
}

/*
 * Select the scanner to run for the current ETFile
 */
void Scan_Select_Mode_And_Run_Scanner (ET_File *ETFile)
{
    if (!ScannerWindow || !ETFile) return;

    if (gtk_combo_box_get_active(GTK_COMBO_BOX(ScannerOptionCombo)) == SCANNER_FILL_TAG)
    {
        /* Run Scanner Type: Scan Tag */
        Scan_Tag_With_Mask(ETFile);
    } else if (gtk_combo_box_get_active(GTK_COMBO_BOX(ScannerOptionCombo)) == SCANNER_RENAME_FILE)
    {
        /* Run Scanner Type: Rename File */
        Scan_Rename_File_With_Mask(ETFile);
    } else if (gtk_combo_box_get_active(GTK_COMBO_BOX(ScannerOptionCombo)) == SCANNER_PROCESS_FIELDS)
    {
        /* Run Scanner Type: Process Fields */
        Scan_Process_Fields(ETFile);
    }
}

void Scan_Use_Fill_Tag_Scanner (void)
{
    if (!ScannerWindow || gtk_combo_box_get_active(GTK_COMBO_BOX(ScannerOptionCombo)) != SCANNER_FILL_TAG)
        Open_ScannerWindow(SCANNER_FILL_TAG);
    else
        Action_Scan_Selected_Files();
}


void Scan_Use_Rename_File_Scanner (void)
{
    if (!ScannerWindow || gtk_combo_box_get_active(GTK_COMBO_BOX(ScannerOptionCombo)) != SCANNER_RENAME_FILE)
        Open_ScannerWindow(SCANNER_RENAME_FILE);
    else
        Action_Scan_Selected_Files();
}

void Scan_Use_Process_Fields_Scanner (void)
{
    if (!ScannerWindow || gtk_combo_box_get_active(GTK_COMBO_BOX(ScannerOptionCombo)) != SCANNER_PROCESS_FIELDS)
        Open_ScannerWindow(SCANNER_PROCESS_FIELDS);
    else
        Action_Scan_Selected_Files();
}


/* Callback from Open_ScannerWindow */
void ScannerWindow_Quit (void)
{
    if (ScannerWindow)
    {
        if (SCAN_TAG_DEFAULT_MASK) g_free(SCAN_TAG_DEFAULT_MASK);
        SCAN_TAG_DEFAULT_MASK = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(ScanTagMaskCombo)->child)));
        Add_String_To_Combo_List(ScanTagListModel, SCAN_TAG_DEFAULT_MASK);
        Save_Rename_File_Masks_List(ScanTagListModel, MASK_EDITOR_TEXT);

        if (RENAME_FILE_DEFAULT_MASK) g_free(RENAME_FILE_DEFAULT_MASK);
        RENAME_FILE_DEFAULT_MASK = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(RenameFileMaskCombo)->child)));
        Add_String_To_Combo_List(RenameFileListModel, RENAME_FILE_DEFAULT_MASK);
        Save_Rename_File_Masks_List(RenameFileListModel, MASK_EDITOR_TEXT);

        ScannerWindow_Apply_Changes();

        gtk_widget_destroy(ScannerWindow);
        gtk_list_store_clear(ScanTagListModel);
        gtk_list_store_clear(RenameFileListModel);
        ScannerWindow     = (GtkWidget *)NULL;
        ScannerOptionCombo= (GtkWidget *)NULL;
        SWScanButton      = (GtkWidget *)NULL;

        // To avoid crashs after tests
        ScanTagMaskCombo              = (GtkWidget *)NULL;
        RenameFileMaskCombo           = (GtkWidget *)NULL;
        MaskEditorEntry               = (GtkWidget *)NULL;
        LegendFrame                   = (GtkWidget *)NULL;
        ProcessFieldsConvertIntoSpace = (GtkWidget *)NULL;
        ProcessFieldsConvertSpace     = (GtkWidget *)NULL;
        FillTagPreviewLabel           = (GtkWidget *)NULL;
        RenameFilePreviewLabel        = (GtkWidget *)NULL;
    }
}


/*
 * For the configuration file...
 */
void ScannerWindow_Apply_Changes (void)
{
    if (ScannerWindow)
    {
        gint x, y;//, width, height;

        if ( ScannerWindow->window!=NULL && gdk_window_is_visible(ScannerWindow->window)
        &&   gdk_window_get_state(ScannerWindow->window)!=GDK_WINDOW_STATE_MAXIMIZED )
        {
            // Position and Origin of the scanner window
            gdk_window_get_root_origin(ScannerWindow->window,&x,&y);
            SCANNER_WINDOW_X = x;
            SCANNER_WINDOW_Y = y;
            //gdk_window_get_size(ScannerWindow->window,&width,&height);
            //SCANNER_WINDOW_WIDTH  = width;
            //SCANNER_WINDOW_HEIGHT = height;
        }

        // The scanner selected
        SCANNER_TYPE = gtk_combo_box_get_active(GTK_COMBO_BOX(ScannerOptionCombo));
        
        /* Group: select entries to process */
        PROCESS_FILENAME_FIELD    = GTK_TOGGLE_BUTTON(ProcessFileNameField)->active;
        PROCESS_TITLE_FIELD       = GTK_TOGGLE_BUTTON(ProcessTitleField)->active;
        PROCESS_ARTIST_FIELD      = GTK_TOGGLE_BUTTON(ProcessArtistField)->active;
        PROCESS_ALBUM_FIELD       = GTK_TOGGLE_BUTTON(ProcessAlbumField)->active;
        PROCESS_GENRE_FIELD       = GTK_TOGGLE_BUTTON(ProcessGenreField)->active;
        PROCESS_COMMENT_FIELD     = GTK_TOGGLE_BUTTON(ProcessCommentField)->active;
        PROCESS_COMPOSER_FIELD    = GTK_TOGGLE_BUTTON(ProcessComposerField)->active;
        PROCESS_ORIG_ARTIST_FIELD = GTK_TOGGLE_BUTTON(ProcessOrigArtistField)->active;
        PROCESS_COPYRIGHT_FIELD   = GTK_TOGGLE_BUTTON(ProcessCopyrightField)->active;
        PROCESS_URL_FIELD         = GTK_TOGGLE_BUTTON(ProcessURLField)->active;
        PROCESS_ENCODED_BY_FIELD  = GTK_TOGGLE_BUTTON(ProcessEncodedByField)->active;
        
        /* Group: convert one character */
        PF_CONVERT_INTO_SPACE     = GTK_TOGGLE_BUTTON(ProcessFieldsConvertIntoSpace)->active;
        PF_CONVERT_SPACE          = GTK_TOGGLE_BUTTON(ProcessFieldsConvertSpace)->active;
        PF_CONVERT                = GTK_TOGGLE_BUTTON(ProcessFieldsConvert)->active;
        
        /* Group: capitalize */
        PF_CONVERT_ALL_UPPERCASE           = GTK_TOGGLE_BUTTON(ProcessFieldsAllUppercase)->active;
        PF_CONVERT_ALL_DOWNCASE            = GTK_TOGGLE_BUTTON(ProcessFieldsAllDowncase)->active;
        PF_CONVERT_FIRST_LETTER_UPPERCASE  = GTK_TOGGLE_BUTTON(ProcessFieldsFirstLetterUppercase)->active;
        PF_CONVERT_FIRST_LETTERS_UPPERCASE = GTK_TOGGLE_BUTTON(ProcessFieldsFirstLettersUppercase)->active;
        
        /* Group: remove/insert space */
        PF_REMOVE_SPACE   = GTK_TOGGLE_BUTTON(ProcessFieldsConvertIntoSpace)->active;
        PF_INSERT_SPACE   = GTK_TOGGLE_BUTTON(ProcessFieldsConvertSpace)->active;
        PF_ONLY_ONE_SPACE = GTK_TOGGLE_BUTTON(ProcessFieldsOnlyOneSpace)->active;
    }
}


/* Callback from Option button */
void Scan_Option_Button (void)
{
    Open_OptionsWindow();
    gtk_notebook_set_current_page(GTK_NOTEBOOK(OptionsNoteBook), OptionsNoteBook_Scanner_Page_Num);
}



/*
 * Check if mask of the "Scan Tag" is valid. Return TRUE if valid, else FALSE.
 */
gboolean Scan_Check_Scan_Tag_Mask (GtkObject *widget_to_show_hide, GtkEntry *widget_source)
{
    gchar *tmp  = NULL;
    gchar *mask = NULL;
    gint loop = 0;


    if (!widget_to_show_hide || !widget_source)
        goto Bad_Mask;

    mask = g_strdup(gtk_entry_get_text(GTK_ENTRY(widget_source)));
    if (!mask || strlen(mask)<1)
        goto Bad_Mask;

    while (mask)
    {
        if ( (tmp=strrchr(mask,'%'))==NULL )
        {
            if (loop==0)
                /* There is no code the first time => not accepted */
                goto Bad_Mask;
            else
                /* There is no more code => accepted */
                goto Good_Mask;
        }
        if ( strlen(tmp)>1
        && (tmp[1]=='a' || tmp[1]=='b' || tmp[1]=='c' || tmp[1]=='d' || tmp[1]=='p' ||
            tmp[1]=='r' || tmp[1]=='e' || tmp[1]=='g' || tmp[1]=='i' || tmp[1]=='l' ||
            tmp[1]=='o' || tmp[1]=='n' || tmp[1]=='t' || tmp[1]=='u' || tmp[1]=='y' ) )
        {
            /* Code is correct */
            *(mask+strlen(mask)-strlen(tmp)) = '\0';
        }else
        {
            goto Bad_Mask;
        }

        /* Check the following code and separator */
        if ( (tmp=strrchr(mask,'%'))==NULL )
            /* There is no more code => accepted */
            goto Good_Mask;

        if ( strlen(tmp)>2
        && (tmp[1]=='a' || tmp[1]=='b' || tmp[1]=='c' || tmp[1]=='d' || tmp[1]=='p' ||
            tmp[1]=='r' || tmp[1]=='e' || tmp[1]=='g' || tmp[1]=='i' || tmp[1]=='l' ||
            tmp[1]=='o' || tmp[1]=='n' || tmp[1]=='t' || tmp[1]=='u' || tmp[1]=='y' ) )
        {
            /* There is a separator and code is correct */
            *(mask+strlen(mask)-strlen(tmp)) = '\0';
        }else
        {
            goto Bad_Mask;
        }
        loop++;
    }

    Bad_Mask:
        g_free(mask);
        gtk_widget_show(GTK_WIDGET(widget_to_show_hide));
        return FALSE;

    Good_Mask:
        g_free(mask);
        gtk_widget_hide(GTK_WIDGET(widget_to_show_hide));
        return TRUE;
}
/*
 * Check if mask of the "Rename File" is valid. Return TRUE if valid, else FALSE.
 */
gboolean Scan_Check_Rename_File_Mask (GtkObject *widget_to_show_hide, GtkEntry *widget_source)
{
    gchar *tmp = NULL;
    gchar *mask = NULL;


    if (!widget_to_show_hide || !widget_source)
        goto Bad_Mask;

    mask = g_strdup(gtk_entry_get_text(GTK_ENTRY(widget_source)));
    if (!mask || strlen(mask)<1)
        goto Bad_Mask;

    // Since version 1.99.4, it is available!
    /*if ( strchr(mask,'/')!=NULL ) // Renaming directory is not yet available
        goto Bad_Mask;*/
    // Not a valid path....
    if ( strstr(mask,"//") != NULL
    ||   strstr(mask,"./") != NULL
    ||   strstr(mask,"../") != NULL)
        goto Bad_Mask;

    while (mask)
    {
        if ( (tmp=strrchr(mask,'%'))==NULL )
        {
            /* There is no more code. */
            /* No code in mask is accepted. */
            goto Good_Mask;
        }
        if ( strlen(tmp)>1
        && (tmp[1]=='a' || tmp[1]=='b' || tmp[1]=='c' || tmp[1]=='d' || tmp[1]=='p' ||
            tmp[1]=='r' || tmp[1]=='e' || tmp[1]=='g' || tmp[1]=='i' || tmp[1]=='l' ||
            tmp[1]=='o' || tmp[1]=='n' || tmp[1]=='t' || tmp[1]=='u' || tmp[1]=='y' ) )
        {
            /* The code is valid. */
            /* No separator is accepted. */
            *(mask+strlen(mask)-strlen(tmp)) = '\0';
        }else
        {
            goto Bad_Mask;
        }
    }

    Bad_Mask:
        g_free(mask);
        gtk_widget_show(GTK_WIDGET(widget_to_show_hide));
        return FALSE;

    Good_Mask:
        g_free(mask);
        gtk_widget_hide(GTK_WIDGET(widget_to_show_hide));
        return TRUE;
}


/*
 * Check if the selected mask in the Mask Editor is valid, else display the mask status icon.
 */
gboolean Scan_Check_Editor_Mask (GtkObject *widget_to_show_hide, GtkEntry *widget_source)
{
    /* Select and get result of check scanner */
    if (gtk_combo_box_get_active(GTK_COMBO_BOX(ScannerOptionCombo)) == SCANNER_FILL_TAG)
    {
        return Scan_Check_Scan_Tag_Mask(widget_to_show_hide,widget_source);
    } else if (gtk_combo_box_get_active(GTK_COMBO_BOX(ScannerOptionCombo)) == SCANNER_RENAME_FILE)
    {
        return Scan_Check_Rename_File_Mask(widget_to_show_hide,widget_source);
    } else
        return FALSE;
}


void Scan_Toggle_Legend_Button (void)
{
    if (!LegendButton || !LegendFrame) return;

    if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(LegendButton)) )
        gtk_widget_show_all(LegendFrame);
    else
        gtk_widget_hide(LegendFrame);
}


void Scan_Toggle_Mask_Editor_Button (void)
{
    GtkTreeModel *treemodel;
    GtkTreeSelection *selection;
    GtkTreeIter iter;

    if (!MaskEditorButton || !MaskEditorFrame || !MaskEditorList) return;

    if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(MaskEditorButton)) )
    {
        gtk_widget_show_all(MaskEditorFrame);

        // Select first row in list
        treemodel = gtk_tree_view_get_model(GTK_TREE_VIEW(MaskEditorList));
        if (gtk_tree_model_get_iter_first(treemodel, &iter))
        {
            selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(MaskEditorList));
            gtk_tree_selection_unselect_all(selection);
            gtk_tree_selection_select_iter(selection, &iter);
        }

        // Update status of the icon box cause prev instruction show it for all cases
        g_signal_emit_by_name(GTK_OBJECT(MaskEditorEntry),"changed");
    }else
    {
        gtk_widget_hide_all(MaskEditorFrame);
    }
}



/*
 * Manage/Toggle check buttons into 'Process Fields' frame
 */
void Process_Fields_Check_Button_Toggled (GtkObject *object, GList *list)
{
    gint i = 0;

    if ( GTK_TOGGLE_BUTTON(object)->active )
    {
        while (list)
        {
            if ( list->data!=NULL && GTK_OBJECT(list->data)!=object )
                gtk_toggle_button_set_active((GtkToggleButton *)list->data,FALSE);
            i++;
            if (!list->next) break;
            list = list->next;
        }
    }
}


void Process_Fields_Convert_Check_Button_Toggled (GtkObject *object)
{
    gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsConvertTo),GTK_TOGGLE_BUTTON(object)->active);
    gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsConvertFrom),GTK_TOGGLE_BUTTON(object)->active);
}


/*
 * Small buttons of Process Fields scanner
 */
void Select_Fields_Invert_Selection (void)
{
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessFileNameField),
                                !GTK_TOGGLE_BUTTON(ProcessFileNameField)->active);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessTitleField),
                                !GTK_TOGGLE_BUTTON(ProcessTitleField)->active);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessArtistField),
                                !GTK_TOGGLE_BUTTON(ProcessArtistField)->active);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessAlbumField),
                                !GTK_TOGGLE_BUTTON(ProcessAlbumField)->active);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessGenreField),
                                !GTK_TOGGLE_BUTTON(ProcessGenreField)->active);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessCommentField),
                                !GTK_TOGGLE_BUTTON(ProcessCommentField)->active);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessComposerField),
                                !GTK_TOGGLE_BUTTON(ProcessComposerField)->active);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessOrigArtistField),
                                !GTK_TOGGLE_BUTTON(ProcessOrigArtistField)->active);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessCopyrightField),
                                !GTK_TOGGLE_BUTTON(ProcessCopyrightField)->active);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessURLField),
                                !GTK_TOGGLE_BUTTON(ProcessURLField)->active);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessEncodedByField),
                                !GTK_TOGGLE_BUTTON(ProcessEncodedByField)->active);
}
void Select_Fields_Select_Unselect_All (void)
{
    static gboolean state = 1;

    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessFileNameField),   state);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessTitleField),      state);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessArtistField),     state);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessAlbumField),      state);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessGenreField),      state);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessCommentField),    state);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessComposerField),   state);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessOrigArtistField), state);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessCopyrightField),  state);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessURLField),        state);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ProcessEncodedByField),  state);
    state = !state;
}

/*
 * Set sensitive state of the processing check boxes : if no one is selected => all disabled
 */
void Select_Fields_Set_Sensitive (void)
{
    if (GTK_TOGGLE_BUTTON(ProcessFileNameField)->active
    ||  GTK_TOGGLE_BUTTON(ProcessTitleField)->active
    ||  GTK_TOGGLE_BUTTON(ProcessArtistField)->active
    ||  GTK_TOGGLE_BUTTON(ProcessAlbumField)->active
    ||  GTK_TOGGLE_BUTTON(ProcessGenreField)->active
    ||  GTK_TOGGLE_BUTTON(ProcessCommentField)->active
    ||  GTK_TOGGLE_BUTTON(ProcessComposerField)->active
    ||  GTK_TOGGLE_BUTTON(ProcessOrigArtistField)->active
    ||  GTK_TOGGLE_BUTTON(ProcessCopyrightField)->active
    ||  GTK_TOGGLE_BUTTON(ProcessURLField)->active
    ||  GTK_TOGGLE_BUTTON(ProcessEncodedByField)->active)
    {
        gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsConvertIntoSpace),     TRUE);
        gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsConvertSpace),         TRUE);
        gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsConvert),              TRUE);
        gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsConvertLabelTo),       TRUE);
        // Activate the two entries only if the check box is actived, esle keep them disabled
        if (GTK_TOGGLE_BUTTON(ProcessFieldsConvert)->active)
        {
            gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsConvertTo),        TRUE);
            gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsConvertFrom),      TRUE);
        }
        gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsAllUppercase),         TRUE);
        gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsAllDowncase),          TRUE);
        gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsFirstLetterUppercase), TRUE);
        gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsFirstLettersUppercase),TRUE);
        gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsRemoveSpace),          TRUE);
        gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsInsertSpace),          TRUE);
        gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsOnlyOneSpace),         TRUE);
    }else
    {
        gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsConvertIntoSpace),     FALSE);
        gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsConvertSpace),         FALSE);
        gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsConvert),              FALSE);
        gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsConvertTo),            FALSE);
        gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsConvertLabelTo),       FALSE);
        gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsConvertFrom),          FALSE);
        gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsAllUppercase),         FALSE);
        gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsAllDowncase),          FALSE);
        gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsFirstLetterUppercase), FALSE);
        gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsFirstLettersUppercase),FALSE);
        gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsRemoveSpace),          FALSE);
        gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsInsertSpace),          FALSE);
        gtk_widget_set_sensitive(GTK_WIDGET(ProcessFieldsOnlyOneSpace),         FALSE);
    }
}

/*
 * Callbacks from Mask Editor buttons
 */

/*
 * Callback from the mask edit list
 * Previously known as Mask_Editor_List_Select_Row
 */
void Mask_Editor_List_Row_Selected (GtkTreeSelection* selection, gpointer data)
{
    GList *selectedRows;
    gchar *text = NULL;
    GtkTreePath *lastSelected;
    GtkTreeIter lastFile;
    GtkTreeModel *treemodel;

    treemodel = gtk_tree_view_get_model(GTK_TREE_VIEW(MaskEditorList));

    /* We must block the function, else the previous selected row will be modified */
    g_signal_handlers_block_by_func(G_OBJECT(MaskEditorEntry),
                                    G_CALLBACK(Mask_Editor_Entry_Changed),NULL);

    selectedRows = gtk_tree_selection_get_selected_rows(selection, NULL);

    /*
     * At some point, we might get called when no rows are selected?
     */
    if (g_list_length(selectedRows) == 0)
    {
        g_list_foreach(selectedRows, (GFunc) gtk_tree_path_free, NULL);
        g_list_free(selectedRows);
        g_signal_handlers_unblock_by_func(G_OBJECT(MaskEditorEntry),
                                          G_CALLBACK(Mask_Editor_Entry_Changed),NULL);
        return;
    }

    /* Get the text of the last selected row */
    lastSelected = (GtkTreePath *)g_list_last(selectedRows)->data;

    gtk_tree_model_get_iter(treemodel, &lastFile, lastSelected);
    gtk_tree_model_get(treemodel, &lastFile, MASK_EDITOR_TEXT, &text, -1);

    if (text)
    {
        gtk_entry_set_text(GTK_ENTRY(MaskEditorEntry),text);
        g_free(text);
    }

    g_signal_handlers_unblock_by_func(G_OBJECT(MaskEditorEntry),
                                      G_CALLBACK(Mask_Editor_Entry_Changed),NULL);
}


/*
 * Add a new mask to the list
 */
void Mask_Editor_List_New (void)
{
    gchar *text = _("New_mask");
    GtkTreeIter iter;
    GtkTreeSelection *selection;
    GtkTreeModel *treemodel;

    if (!MaskEditorList) return;

    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(MaskEditorList));
    treemodel = gtk_tree_view_get_model(GTK_TREE_VIEW(MaskEditorList));

    gtk_list_store_insert(GTK_LIST_STORE(treemodel), &iter, 0);
    gtk_list_store_set(GTK_LIST_STORE(treemodel), &iter, MASK_EDITOR_TEXT, text, -1);

    gtk_tree_selection_unselect_all(selection);
    gtk_tree_selection_select_iter(selection, &iter);
}

/*
 * Duplicate a mask on the list
 */
void Mask_Editor_List_Duplicate (void)
{
    gchar *text = NULL;
    GList *selectedRows;
    GList *toInsert = NULL;
    GtkTreeSelection *selection;
    GtkTreeIter row;
    GtkTreeModel *treemodel;

    if (!MaskEditorList) return;

    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(MaskEditorList));
    selectedRows = gtk_tree_selection_get_selected_rows(selection, NULL);
    treemodel = gtk_tree_view_get_model(GTK_TREE_VIEW(MaskEditorList));

    if (g_list_length(selectedRows) == 0)
    {
        g_print(_("Copy: No row selected!\n"));
        g_list_foreach(selectedRows, (GFunc) gtk_tree_path_free, NULL);
        g_list_free(selectedRows);
        return;
    }

    /* Loop through selected rows, duplicating them into a GList */
    /* We cannot directly insert because the paths in selectedRows get out of date after an insertion */
    while(selectedRows)
    {
        gtk_tree_model_get_iter(treemodel, &row, (GtkTreePath*) selectedRows->data);
        gtk_tree_model_get(treemodel, &row, MASK_EDITOR_TEXT, &text, -1);
        toInsert = g_list_append(toInsert, text);

        selectedRows = selectedRows->next;
        if (!selectedRows) break;
    }

    /* Duplicate the relevant entries, by looping through the list backwards (to preserve original order) */
    toInsert = g_list_last(toInsert);
    while(toInsert)
    {
        gtk_list_store_insert(GTK_LIST_STORE(treemodel), &row, 0);
        gtk_list_store_set(GTK_LIST_STORE(treemodel), &row, MASK_EDITOR_TEXT, (gchar*) toInsert->data, -1);
        g_free(toInsert->data);

        toInsert = toInsert->prev;
        if (!toInsert) break;
    }

    /* Free data no longer needed */
    selectedRows = g_list_first(selectedRows);
    toInsert = g_list_first(toInsert);
    g_list_foreach(selectedRows, (GFunc) gtk_tree_path_free, NULL);
    g_list_free(selectedRows);
    g_list_foreach(toInsert, (GFunc) g_free, NULL);
    g_list_free(toInsert);
}

void Mask_Editor_List_Add(void)
{
    gint i = 0;
    GtkTreeIter iter;
    GtkTreeModel *treemodel;
    gchar *temp;

    treemodel = gtk_tree_view_get_model(GTK_TREE_VIEW(MaskEditorList));

    if (gtk_combo_box_get_active(GTK_COMBO_BOX(ScannerOptionCombo)) == SCANNER_FILL_TAG)
    {
        while(Scan_Masks[i])
        {
            if (!g_utf8_validate(Scan_Masks[i], -1, NULL))
                temp = convert_to_utf8(Scan_Masks[i]);
            else
                temp = g_strdup(Scan_Masks[i]);

            gtk_list_store_append(GTK_LIST_STORE(treemodel), &iter);
            gtk_list_store_set(GTK_LIST_STORE(treemodel), &iter,
                               MASK_EDITOR_TEXT, temp, -1);
            g_free(temp);
            i++;
        }
    } else if (gtk_combo_box_get_active(GTK_COMBO_BOX(ScannerOptionCombo)) == SCANNER_RENAME_FILE)
    {
        while(Rename_File_Masks[i])
        {
            if (!g_utf8_validate(Rename_File_Masks[i], -1, NULL))
                temp = convert_to_utf8(Rename_File_Masks[i]);
            else
                temp = g_strdup(Scan_Masks[i]);

            gtk_list_store_append(GTK_LIST_STORE(treemodel), &iter);
            gtk_list_store_set(GTK_LIST_STORE(treemodel), &iter,
                               MASK_EDITOR_TEXT, temp, -1);
            g_free(temp);
            i++;
        }
    }
}

/*
 * Remove the selected rows from the mask editor list
 */
void Mask_Editor_List_Remove (void)
{
    GtkTreeSelection *selection;
    GtkTreeIter iter;
    GtkTreeModel *treemodel;

    if (!MaskEditorList) return;

    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(MaskEditorList));
    treemodel = gtk_tree_view_get_model(GTK_TREE_VIEW(MaskEditorList));

    if (gtk_tree_selection_count_selected_rows(selection) == 0) {
        g_print(_("Remove: No row selected!\n"));
        return;
    }

    if (!gtk_tree_model_get_iter_first(treemodel, &iter))
        return;

    while (TRUE)
    {
        if (gtk_tree_selection_iter_is_selected(selection, &iter))
        {
            if (!gtk_list_store_remove(GTK_LIST_STORE(treemodel), &iter)) 
            {
                break;
            }
        } else
        {
            if (!gtk_tree_model_iter_next(treemodel, &iter))
            {
                break;
            }
        }
    }
}

/*
 * Move all selected rows up one place in the mask list
 */
void Mask_Editor_List_Move_Up (void)
{
    GtkTreeSelection *selection;
    GList *selectedRows;
    GList *selectedRowsCopy;
    GtkTreeIter currentFile;
    GtkTreeIter nextFile;
    GtkTreePath *currentPath;
    GtkTreeModel *treemodel;

    if (!MaskEditorList) return;

    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(MaskEditorList));
    treemodel = gtk_tree_view_get_model(GTK_TREE_VIEW(MaskEditorList));
    selectedRows = gtk_tree_selection_get_selected_rows(selection, NULL);

    if (g_list_length(selectedRows) == 0)
    {
        g_print(_("Move Up: No row selected!\n"));
        g_list_foreach(selectedRows, (GFunc)gtk_tree_path_free, NULL);
        g_list_free(selectedRows);
        return;
    }

    selectedRowsCopy = selectedRows;

    while(selectedRows)
    {
        currentPath = (GtkTreePath*) selectedRows->data;
        gtk_tree_model_get_iter(treemodel, &currentFile, currentPath);

        /* Find the entry above the node... */
        if (gtk_tree_path_prev(currentPath)) {
            /* ...and if it exists, swap the two rows by iter */
            gtk_tree_model_get_iter(treemodel, &nextFile, currentPath);
            gtk_list_store_swap(GTK_LIST_STORE(treemodel), &currentFile, &nextFile);
        }

        selectedRows = selectedRows->next;
        if (!selectedRows) break;
    }

    g_list_foreach(selectedRowsCopy, (GFunc)gtk_tree_path_free, NULL);
    g_list_free(selectedRowsCopy);
}

/*
 * Move all selected rows down one place in the mask list
 */
void Mask_Editor_List_Move_Down (void)
{
    GtkTreeSelection *selection;
    GList *selectedRows;
    GList *selectedRowsCopy;
    GtkTreeIter currentFile;
    GtkTreeIter nextFile;
    GtkTreePath *currentPath;
    GtkTreeModel *treemodel;

    if (!MaskEditorList) return;

    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(MaskEditorList));
    treemodel = gtk_tree_view_get_model(GTK_TREE_VIEW(MaskEditorList));
    selectedRows = gtk_tree_selection_get_selected_rows(selection, NULL);

    if (g_list_length(selectedRows) == 0) {
        g_print(_("Move Down: No row selected!\n"));
        g_list_foreach(selectedRows, (GFunc)gtk_tree_path_free, NULL);
        g_list_free(selectedRows);
        return;
    }

    selectedRowsCopy = selectedRows;

    while (selectedRows)
    {
        currentPath = (GtkTreePath*) selectedRows->data;
        gtk_tree_model_get_iter(treemodel, &currentFile, currentPath);

        /* Find the entry below the node and swap the two nodes by iter */
        gtk_tree_path_next(currentPath);
        gtk_tree_model_get_iter(treemodel, &nextFile, currentPath);
        gtk_list_store_swap(GTK_LIST_STORE(treemodel), &currentFile, &nextFile);

        if (!selectedRows->next) break;
        selectedRows = selectedRows->next;
    }

    g_list_foreach(selectedRowsCopy, (GFunc)gtk_tree_path_free, NULL);
    g_list_free(selectedRowsCopy);
}

/*
 * Save the currently displayed mask list in the mask editor
 */
void Mask_Editor_List_Save_Button (void)
{
    Mask_Editor_Clean_Up_Masks_List();

    if (gtk_combo_box_get_active(GTK_COMBO_BOX(ScannerOptionCombo)) == SCANNER_FILL_TAG)
    {
        Save_Scan_Tag_Masks_List(ScanTagListModel, MASK_EDITOR_TEXT);
    } else if (gtk_combo_box_get_active(GTK_COMBO_BOX(ScannerOptionCombo)) == SCANNER_RENAME_FILE)
    {
        Save_Rename_File_Masks_List(RenameFileListModel, MASK_EDITOR_TEXT);
    }
}

/*
 * Clean up the currently displayed masks lists, ready for saving
 */
void Mask_Editor_Clean_Up_Masks_List (void)
{
    gchar *text = NULL;
    gchar *text1 = NULL;
    GtkTreeIter currentIter;
    GtkTreeIter itercopy;
    GtkTreeModel *treemodel;

    treemodel = gtk_tree_view_get_model(GTK_TREE_VIEW(MaskEditorList));

    /* Remove blank and duplicate items */
    if (gtk_tree_model_get_iter_first(treemodel, &currentIter))
    {

        while(TRUE)
        {
            gtk_tree_model_get(treemodel, &currentIter, MASK_EDITOR_TEXT, &text, -1);

            /* Check for blank entry */
            if (text && g_utf8_strlen(text, -1) == 0)
            {
                g_free(text);

                if (!gtk_list_store_remove(GTK_LIST_STORE(treemodel), &currentIter))
                    break; /* No following entries */
                else
                    continue; /* Go on to next entry, which the remove function already moved onto for us */
            }

            /* Check for duplicate entries */
            itercopy = currentIter;
            if (!gtk_tree_model_iter_next(treemodel, &itercopy))
            {
                g_free(text);
                break;
            }

            while(TRUE)
            {
                gtk_tree_model_get(treemodel, &itercopy, MASK_EDITOR_TEXT, &text1, -1);
                if (text1 && g_utf8_collate(text,text1) == 0)
                {
                    g_free(text1);

                    if (!gtk_list_store_remove(GTK_LIST_STORE(treemodel), &itercopy))
                        break; /* No following entries */
                    else
                        continue; /* Go on to next entry, which the remove function already set iter to for us */

                }
                g_free(text1);
                if (!gtk_tree_model_iter_next(treemodel, &itercopy))
                    break;
            }

            g_free(text);

            if (!gtk_tree_model_iter_next(treemodel, &currentIter))
                break;
        }
    }
}

/*
 * Update the Mask List with the new value of the entry box
 */
void Mask_Editor_Entry_Changed (void)
{
    GtkTreeSelection *selection;
    GtkTreePath *firstSelected;
    GtkTreeModel *treemodel;
    GList *selectedRows;
    GtkTreeIter row;
    const gchar* text;

    if (!MaskEditorList) return;

    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(MaskEditorList));
    treemodel = gtk_tree_view_get_model(GTK_TREE_VIEW(MaskEditorList));
    selectedRows = gtk_tree_selection_get_selected_rows(selection, NULL);

    if (g_list_length(selectedRows) == 0)
    {
        g_list_foreach(selectedRows, (GFunc) gtk_tree_path_free, NULL);
        g_list_free(selectedRows);
        return;
    }

    firstSelected = (GtkTreePath *)g_list_first(selectedRows)->data;
    text = gtk_entry_get_text(GTK_ENTRY(MaskEditorEntry));

    gtk_tree_model_get_iter(treemodel, &row, firstSelected);
    gtk_list_store_set(GTK_LIST_STORE(treemodel), &row, MASK_EDITOR_TEXT, text, -1);

    g_list_foreach(selectedRows, (GFunc) gtk_tree_path_free, NULL);
    g_list_free(selectedRows);
}

/*
 * Actions when the a key is pressed into the masks editor clist
 */
gboolean Mask_Editor_List_Key_Press (GtkWidget *widget, GdkEvent *event)
{
    if (event && event->type == GDK_KEY_PRESS) {
        GdkEventKey *kevent = (GdkEventKey *)event;

        switch(kevent->keyval) {
        case GDK_Delete:
            Mask_Editor_List_Remove();
            break;
/*            case GDK_Up:
            Mask_Editor_Clist_Move_Up();
            break;
        case GDK_Down:
            Mask_Editor_Clist_Move_Down();
            break;
*/      }
    }
    return TRUE;
}

/*
 * Function when you select an item of the option menu
 */
void Scanner_Option_Menu_Activate_Item (GtkWidget *combo, gpointer data)
{
    switch (gtk_combo_box_get_active(GTK_COMBO_BOX(combo)))
    {
        case SCANNER_FILL_TAG:
            gtk_widget_show(MaskEditorButton);
            gtk_widget_show(LegendButton);
            gtk_widget_show(ScanTagFrame);
            gtk_widget_hide(RenameFileFrame);
            gtk_widget_hide(ProcessFieldsFrame);
            gtk_tree_view_set_model(GTK_TREE_VIEW(MaskEditorList), GTK_TREE_MODEL(ScanTagListModel));
            Scan_Fill_Tag_Generate_Preview();
            break;
        
        case SCANNER_RENAME_FILE:
            gtk_widget_show(MaskEditorButton);
            gtk_widget_show(LegendButton);
            gtk_widget_hide(ScanTagFrame);
            gtk_widget_show(RenameFileFrame);
            gtk_widget_hide(ProcessFieldsFrame);
            gtk_tree_view_set_model(GTK_TREE_VIEW(MaskEditorList), GTK_TREE_MODEL(RenameFileListModel));
            Scan_Rename_File_Generate_Preview();
            break;
        
        case SCANNER_PROCESS_FIELDS:
            gtk_widget_hide(MaskEditorButton);
            gtk_widget_hide(LegendButton);
            gtk_widget_hide(ScanTagFrame);
            gtk_widget_hide(RenameFileFrame);
            gtk_widget_show_all(ProcessFieldsFrame);
            gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(MaskEditorButton),FALSE);
            gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(LegendButton),FALSE);
            gtk_tree_view_set_model(GTK_TREE_VIEW(MaskEditorList), NULL);
            break;
    }
}

/*
 * Init the position of the scanner window
 */
void Scan_Set_Scanner_Window_Init_Position (void)
{
    if (ScannerWindow && SET_SCANNER_WINDOW_POSITION)
    {
        gtk_widget_realize(ScannerWindow);
        gdk_window_move(ScannerWindow->window,SCANNER_WINDOW_X,SCANNER_WINDOW_Y);
    }
}

Generated by  Doxygen 1.6.0   Back to index