public class IOFactory
{
    //ISO3 language codes
    public static String[] iso3Codes;
    //File type extensions
    public static String[] archiveExt;// = {"zip", "rar", "jar", "iso"};
    public static String[] exeExt;// = {"exe", "jar", "cmd", "sh", "bat"};
    public static String[] setupExt;// = {"msi"};
    public static String[] docExt;// = {"txt", "pdf", "doc", "docx", "xml", "rtf", "odt", "xls", "xlsx", "csv"};
    public static String[] imgExt;// = {"bmp", "jpg", "jpeg", "gif", "png", "ico"};
    public static String[] webExt;// = {"html", "htm", "php", "js", "asp", "aspx", "css"};
    public static String[] soundExt;// = {"wav", "mp3", "ogg", "wma"};
    public static String[] vidExt;// = {"mp4", "mpg", "mpeg", "wmv", "avi", "mkv"};
    public static String[] custExt;//User defined custom extensions
    
    /**
     * Returns file type of a file
     * @param f: file
     * @return FILE_TYPE
     */
    public static FILE_TYPE getFileType(File f) {
        if (f.isDirectory()) return FILE_TYPE.Folder;
        
        String NAME = f.getName();
        if (IOFactory.isFileType(NAME, FILE_TYPE.Archive))//Archive
            return FILE_TYPE.Archive;
        else if (IOFactory.isFileType(NAME, FILE_TYPE.Executable))//Executable
            return FILE_TYPE.Executable;
        else if (IOFactory.isFileType(NAME, FILE_TYPE.Setup))//Setup file
            return FILE_TYPE.Setup;
        else if (IOFactory.isFileType(NAME, FILE_TYPE.Document))//Text Document file
            return FILE_TYPE.Document;
        else if (IOFactory.isFileType(NAME, FILE_TYPE.Image))//Image file
            return FILE_TYPE.Image;
        else if (IOFactory.isFileType(NAME, FILE_TYPE.Web))//Web file
            return FILE_TYPE.Web;
        else if (IOFactory.isFileType(NAME, FILE_TYPE.Sound))//Sound file
            return FILE_TYPE.Sound;
        else if (IOFactory.isFileType(NAME, FILE_TYPE.Video))//Video file
            return FILE_TYPE.Video;
        else if (IOFactory.isFileType(NAME, FILE_TYPE.Custom))//Custom extension
            return FILE_TYPE.Custom;
        else//Simple File
            return FILE_TYPE.File;
    }
    
    /**
     * Tests if a file's extension is of a type
     * (folders and files without extension are automatically valid)
     * @param file_name
     * @param file_type
     * @return boolean: success
     */
    public static boolean isFileType(String file_name, FILE_TYPE file_type) {
        // Accept all (unix) files without extensions
        if (!file_name.contains("."))
            return true;
        
        String[] exts = null;
        switch (file_type) {
        case Archive:
            exts = archiveExt;
            break;
        case Executable:
            exts = exeExt;
            break;
        case Setup:
            exts = setupExt;
            break;
        case Document:
            exts = docExt;
            break;
        case Image:
            exts = imgExt;
            break;
        case Web:
            exts = webExt;
            break;
        case Sound:
            exts = soundExt;
            break;
        case Video:
            exts = vidExt;
            break;
        case Custom:
            exts = custExt;
            break;
        case File:
            return true;
        default:
            break;
        }
        assert exts != null;
        
        for(String S : exts)
            if (file_name.toLowerCase().endsWith("."+S)) {
                return true;
            }
        return false;
    }
    
    
    // Cached icons
    private static final String iconsPath = "/com/dcp/sm/gui/icons/";
    public static Image imgFolder;//Folder icon
    public static Image imgFile;//File icon
    public static Image imgZip;//Archive icon
    public static Image imgExe;//Executable icon
    public static Image imgSetup;//Setup icon
    public static Image imgDoc;//Text file icon
    public static Image imgImg;//Image file icon
    public static Image imgWeb;//Web file icon
    public static Image imgSound;//Audio file icon
    public static Image imgVid;//Video file icon
    public static Image imgCust;//Custom filter file icon
    public static Image imgHistory;//History file icon
    public static Image imgClose;//Close file icon
    // Menu icons
    public static Image imgAdd;//Add menu icon
    public static Image imgRight;//Right arrow menu icon
    public static Image imgEdit;//Edit menu icon
    public static Image imgDelete;//Delete menu icon
    public static Image imgImport;//Import menu icon
    public static Image imgCopy;//Copy menu icon
    public static Image imgPaste;//Paste menu icon
    
    //Filters
    public final static Filter<File> imgFilter = new Filter<File>() {
        @Override public boolean include(File file)
        {
            if (file.isDirectory()) return false;
            else if (file.isFile() && IOFactory.isFileType(file.getName(), FILE_TYPE.Image) )
                return false;
            return true;
        }
    };
    public final static Filter<File> docFilter = new Filter<File>() {
        @Override public boolean include(File file)
        {
            if (file.isDirectory()) return false;
            else if (file.isFile() && (IOFactory.isFileType(file.getName(), FILE_TYPE.Document)
                                        || IOFactory.isFileType(file.getName(), FILE_TYPE.Web)))
                return false;
            return true;
        }
    };
    
    //JSON Files
    public static final String jsonSettings = "settings.json";
    //Directories Paths
    public static String resPath;// = "res";
    public static String xmlPath;// = resPath+"/xml";
    public static String langpackPath;// = resPath+"/langpacks";
    public static String batPath;// = resPath+"/bat";
    public static String antPath;// = resPath+"/ant";
    public static String psPath;// = resPath+"ps/";
    public static String libPath;// = "lib/dcp";
    public static String targetPath;// = "target";
    public static String savePath;// = "saves";
    public static String exeTargetDir;// = "~tmp/";
    //File Paths
    public static String confFile;// = "conf.dcp";
    public static String defDirFile;// = resPath+"/default-dir.txt";
    public static String xmlIzpackInstall;// = "install.xml";
    public final static String dcpFileExt = "dcp";
    //Options
    public static boolean izpackWrite;// = true; 
    public static boolean izpackCompile;// = true;
    public static String apikey;// = Admin:Admin
    
    //Jar libraries
    //public static String jarExecutable;// = libPath+"/dcp-executable.jar";
    //public static String jarListeners;// = libPath+"/dcp-listeners.jar";
    public static String jarResources;// = libPath+"/dcp-resources.jar";
    //Ant build files
    public static String xmlIzpackAntBuild;// = antPath+"/izpack-target.xml";
    public static String xmlRunAntBuild;// = antPath+"/run-target.xml";
    public static String xmlDebugAntBuild;// = antPath+"/debug-target.xml";
    //Bat files
    public static String batClean;// = batPath+"/clean.bat";
    //izpack spec files
    public static String xmlProcessPanelSpec;// = xmlPath+"/ProcessPanelSpec.xml";
    public static String xmlShortcutPanelSpec;// = xmlPath+"/ShortcutPanelSpec.xml";
    public static String xmlRegistrySpec;// = xmlPath+"/RegistrySpec.xml";
    // chocolatey files
    public static String psChocolateyInstall;// = psPath+"/ChocolateyInstall.ps1"
    public static String psChocolateyUninstall;// = psPath+"/ChocolateyUninstall.ps1"
    
    public static String saveFile = "";//"save.dcp";
    public static void setSaveFile(String canonicalPath) {
        if (canonicalPath.length() > 0 && !canonicalPath.endsWith("."+IOFactory.dcpFileExt)) {//add file extension if not given
            canonicalPath = canonicalPath.concat("."+IOFactory.dcpFileExt);
        }
        saveFile = canonicalPath;
    }
    
    /**
     * Load settings from JSON file: settings.json
     * @throws ParseException 
     * @throws IOException 
     */
    private static boolean loadSettings(String json_file) throws IOException, ParseException {
        JsonSimpleReader json_ext = new JsonSimpleReader(json_file, "extensions");
        archiveExt = json_ext.readStringArray("archiveExt");
        exeExt = json_ext.readStringArray("exeExt");
        setupExt = json_ext.readStringArray("setupExt");
        docExt = json_ext.readStringArray("docExt");
        imgExt = json_ext.readStringArray("imgExt");
        webExt = json_ext.readStringArray("webExt");
        soundExt = json_ext.readStringArray("soundExt");
        vidExt = json_ext.readStringArray("vidExt");
        custExt = json_ext.readStringArray("custExt");
        json_ext.close();

        JsonSimpleReader json_paths = new JsonSimpleReader(json_file, "paths");
        savePath = json_paths.readString("savePath");
        resPath = json_paths.readString("resPath");
        xmlPath = json_paths.readString("xmlPath");
        langpackPath = json_paths.readString("langpackPath");
        batPath = json_paths.readString("batPath");
        antPath = json_paths.readString("antPath");
        psPath = json_paths.readString("psPath");
        libPath = json_paths.readString("libPath");
        targetPath = json_paths.readString("targetPath");
        exeTargetDir = json_paths.readString("exeTargetDir");
        json_paths.close();

        JsonSimpleReader json_files = new JsonSimpleReader(json_file, "files");
        confFile = json_files.readString("confFile");
        defDirFile = json_files.readString("defDirFile");
        xmlIzpackInstall = json_files.readString("xmlIzpackInstall");
        json_files.close();

        JsonSimpleReader json_options = new JsonSimpleReader(json_file, "options");
        izpackWrite = (json_options.readString("izpackWrite").equalsIgnoreCase("yes") ||
                json_options.readString("izpackWrite").equalsIgnoreCase("true"));
        izpackCompile = (json_options.readString("izpackCompile").equalsIgnoreCase("yes") ||
                            json_options.readString("izpackCompile").equalsIgnoreCase("true"));
        apikey = json_options.readString("apikey");
        json_options.close();
        
        JsonSimpleReader json_codes = new JsonSimpleReader(json_file, "codes");
        iso3Codes = json_codes.readStringArray("iso3");
        json_codes.close();
        
        return true;
    }
    
    /**
     * Data load
     */
    public static void init() {
        try {
            
            //Import file extensions from settings.json
            loadSettings(IOFactory.jsonSettings);
            //jarExecutable = libPath+"/dcp-executable.jar";
            //jarListeners = libPath+"/dcp-listeners.jar";
            jarResources = libPath+"/dcp-resources.jar";
            xmlIzpackAntBuild = antPath+"/izpack-target.xml";
            xmlRunAntBuild = antPath+"/run-target.xml";
            xmlDebugAntBuild = antPath+"/debug-target.xml";
            batClean = batPath+"/clean.bat";
            xmlProcessPanelSpec = xmlPath+"/ProcessPanelSpec.xml";
            xmlShortcutPanelSpec = xmlPath+"/ShortcutPanelSpec.xml";
            xmlRegistrySpec = xmlPath+"/RegistrySpec.xml";
            psChocolateyInstall = psPath+"/ChocolateyInstall.ps1";
            psChocolateyUninstall = psPath+"/ChocolateyUninstall.ps1";
            
            //Images Resources
            imgFolder = Image.load(IOFactory.class.getClass().getResource(iconsPath + "folder.png"));
            imgFile = Image.load(IOFactory.class.getClass().getResource(iconsPath + "file.png"));
            imgZip = Image.load(IOFactory.class.getClass().getResource(iconsPath + "archive.png"));
            imgExe = Image.load(IOFactory.class.getClass().getResource(iconsPath + "executable.png"));
            imgSetup = Image.load(IOFactory.class.getClass().getResource(iconsPath + "setup.png"));
            imgDoc = Image.load(IOFactory.class.getClass().getResource(iconsPath + "document.png"));
            imgImg = Image.load(IOFactory.class.getClass().getResource(iconsPath + "image.png"));
            imgWeb = Image.load(IOFactory.class.getClass().getResource(iconsPath + "webfile.png"));
            imgSound = Image.load(IOFactory.class.getClass().getResource(iconsPath + "sound.png"));
            imgVid = Image.load(IOFactory.class.getClass().getResource(iconsPath + "video.png"));
            imgCust = Image.load(IOFactory.class.getClass().getResource(iconsPath + "filter.png"));
            imgHistory = Image.load(IOFactory.class.getClass().getResource(iconsPath + "history.png"));
            imgClose = Image.load(IOFactory.class.getClass().getResource(iconsPath + "close.png"));
            imgAdd = Image.load(IOFactory.class.getClass().getResource(iconsPath + "add.png"));
            
            imgRight = Image.load(IOFactory.class.getClass().getResource(iconsPath + "right.png"));
            imgEdit = Image.load(IOFactory.class.getClass().getResource(iconsPath + "edit.png"));
            imgDelete = Image.load(IOFactory.class.getClass().getResource(iconsPath + "delete.png"));
            imgImport = Image.load(IOFactory.class.getClass().getResource(iconsPath + "import.png"));
            imgCopy = Image.load(IOFactory.class.getClass().getResource(iconsPath + "copy.png"));
            imgPaste = Image.load(IOFactory.class.getClass().getResource(iconsPath + "paste.png"));
            
            //Make needed directories
            String[] dirs = new String[] {resPath, antPath, batPath, xmlPath, targetPath, savePath};
            for(String dir:dirs) {
                File f = new File(dir);
                if (!f.exists())
                    f.mkdir();
            }
            
        } catch (TaskExecutionException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
    
} public static enum FILE_TYPE {
    File,
    Folder,
    Archive,
    Executable,
    Setup,
    Document,
    Image,
    Web,
    Sound,
    Video,
    Custom
} public enum LOG_LEVEL {
    DEBUG(0, "DEBUG"),
    INFO(1, "INFO"),
    WARN(2, "WARNING"),
    ERR(3, "ERROR");
        
    private int _level;
    private String _tag;
    private LOG_LEVEL(int level, String tag)
    {
        _level = level;
        _tag = tag;
    }
    @Override
    public String toString()
    {
        return this._tag;
    }
        
    public int value()
    {
        return this._level;
    }
} public static enum SCAN_FOLDER {
    DEFAULT("pack"),
    PACK_FOLDER("pack"),//Default
    GROUP_FOLDER("group");
        
    private String _value;
    private SCAN_FOLDER(String value)
    {
        _value = value;
    }
    @Override
    public String toString()
    {
        return this._value;
    }
} public static enum SCAN_MODE {
    DEFAULT,
    SIMPLE_SCAN,//Default
    RECURSIVE_SCAN
} public class Master extends Window implements Application, Bindable
{
    // Constant Application Values
    public final static String AppName = "DCP Setup Maker";
    public static String AppVersion;
    static {
        PropertiesFactory props;
        try
        {
            props = new PropertiesFactory("app.properties");
            AppVersion = props.getProperty("version");
        }
        catch (IOException e) { e.printStackTrace(); }
    }
    
    // Class Data
    private static Window window;// Main application window
    public static Facade facade;// Application data facade
    
    // Helpers
    @BXML static public HelperFacade helper;
    // Display
    @BXML private Label info;
    @BXML private TabPane tabPane;
    @BXML private Label statusBarStep;
    @BXML private Label statusBarNPacks;
    @BXML private Label statusBarNGroups;
    // Browsers
    @BXML private FileBrowserSheet saveFileBrowserSheet;//File Browser
    @BXML private FileBrowserSheet loadFileBrowserSheet;//File Browser
    // Buttons
    @BXML private PushButton btBack;
    @BXML private PushButton btNext;
    // Menu
    @BXML private Menu.Item btSave;
    @BXML private Menu.Item btSaveAs;
    @BXML private Menu.Item btLoad;
    @BXML private Menu.Item btUndo;
    @BXML private Menu.Item btDefault;
    @BXML private Menu.Item btHelp;
    //@BXML private PushButton btInfo;
    
    //Actions
    private Action ASave;//Save project on file
    private Action ALoad;//Load project from file
    
    /**
     * Update title with save file and edit flag '*'
     */
    private void titleUpdate() {
        if (!Master.this.getTitle().contains("-")) {// No save file defined
            if (Master.this.getTitle().contains("*"))// remove * flag from title
                Master.this.setTitle(Master.this.getTitle().substring(0, Master.this.getTitle().length()-1));
            if (IOFactory.saveFile.length() > 0)
                Master.this.setTitle(Master.this.getTitle().concat(" - " + IOFactory.saveFile ));
        }
        else {// Already saved on a file
            if (IOFactory.saveFile.length() > 0) // Save file set
                Master.this.setTitle(Master.this.getTitle().
                        substring(0, Master.this.getTitle().indexOf('-') + 2).
                        concat(IOFactory.saveFile));
            else // Save file unset
                Master.this.setTitle(Master.this.getTitle().
                        substring(0, Master.this.getTitle().indexOf('-') - 1));
        }
    }
    
    public Master() {
        if (facade == null)
        {
            IOFactory.init();// Factory memory data initialize
            facade = new Facade(AppName, AppVersion);
        }
        else {
            facade.setWindow(this); // bind window application to facade

            //Save action
            ASave = new Action() {// Project Save Action
                @Override public void perform(Component source) {
                    if (facade.save(IOFactory.saveFile)) {
                        // Enable Save button
                        if (!btSave.isEnabled()) {
                            btSave.setEnabled(true);
                            //btDefault.setEnabled(false);
                        }
                        setUndo(true);
                    }
                } };
            //Load Action
            ALoad = new Action() {// Project Load Action
                @Override
                public void perform(Component c)
                {
                    if (facade.load(IOFactory.saveFile)) {
                        facade.tabsInit();
                        // Go to Set tab
                        tabPane.setSelectedIndex(0);
                        for(int i=1; i<tabPane.getTabs().getLength(); i++)
                            tabPane.getTabs().get(i).setEnabled(false);
                        tabPane.getTabs().get(0).setEnabled(true);
                        //if (tabPane.getSelectedIndex() == 1)// If already on Set tab
                        //    ScanFrame.setLoaded(false);// Disable scan loaded flag*
                        btBack.setEnabled(false);
                        btNext.setEnabled(true);
                        // Enable Save button
                        if (!btSave.isEnabled()) {
                            btSave.setEnabled(true);
                        }
                        setUndo(true);
                    }
                }
            };
        }
    }
    @Override public void resume() throws Exception { }
    @Override public void suspend() throws Exception { }

    @Override public void startup(Display display, Map<String, String> properties) throws Exception// App start
    {
        Out.print(LOG_LEVEL.DEBUG, "Data loaded to memory");
        
        Locale.setDefault(Locale.ENGLISH);// Set default UI language to English
        BXMLSerializer bxmlSerializer = new BXMLSerializer();
        window = (Window) bxmlSerializer.readObject(getClass().getResource("master.bxml"));
        window.open(display);

        // Helper launch if first time
        if (facade.appConfig.isHelp()) {
            helper.open(window);
            facade.appConfig.setHelp(false);
        }
        
        Out.print(LOG_LEVEL.DEBUG, "Window open");
    }

    @Override public boolean shutdown(boolean optional) throws Exception//Window close
    {
        if (window != null) {
            window.close();
            if (facade.appConfig.isModified()) { // Save configuration if modified
                facade.confSave();
                Out.print(LOG_LEVEL.INFO, "Workspace saved");
            }
            Out.print(LOG_LEVEL.DEBUG, "BYE");
        }
        return false;
    }
    
    /**
     * Load a project file in the GUI
     * @param file to load (dcp file)
     */
    public void loadProject(String file)
    {
        IOFactory.setSaveFile(file);
        ALoad.perform(null);
        Prompt.prompt(MessageType.INFO, "Data loaded from saved project.", getWindow());
    }
    
    /**
     * enable/disable undo
     * @param enable: true|false
     */
    public void setUndo(boolean enable)
    {
        btUndo.setEnabled(enable);
        titleUpdate();
    }

    @Override public void initialize(Map<String, Object> args, URL url, Resources res)
    {
        facade.framesInit(this);// Tabs singletons init
        facade.tabsInit();// Tabs display init
        
        //Shortcuts definition
        this.getComponentKeyListeners().add(new ComponentKeyListener.Adapter() {
            @Override
            public boolean keyPressed(Component component, int keyCode, KeyLocation keyLocation)
            {
                switch(keyCode) {
                case Keyboard.KeyCode.F1://[F1] - Help
                    if (btHelp.isEnabled()) btHelp.press();
                    return true;
                default: break;
                }
                
                if (Keyboard.isPressed(Modifier.CTRL)) {//CTRL + [keycode]
                    switch(keyCode) {
                    case Keyboard.KeyCode.O://[ctrl+O] - load project
                        if (btLoad.isEnabled()) btLoad.press();
                        return true;
                    case Keyboard.KeyCode.S://[ctrl+S] - save project
                        if (Keyboard.isPressed(Modifier.SHIFT) || !btSave.isEnabled())
                            btSaveAs.press();//Save As
                        else btSave.press();//Save
                        return true;
                    case Keyboard.KeyCode.Z://[ctrl+Z] - undo all
                        if (btUndo.isEnabled()) btUndo.press();
                        return true;
                    case Keyboard.KeyCode.LEFT://[ctrl+LEFT] - previous step
                        if (btBack.isEnabled()) btBack.press();
                        return true;
                    case Keyboard.KeyCode.RIGHT://[ctrl+RIGHT] - next step
                        if (btNext.isEnabled()) btNext.press();
                        return true;
                    default:
                        break;
                    }   
                }
                else if (Keyboard.isPressed(Modifier.ALT)) {//ALT + [keycode]
                    switch(keyCode) {
                    case Keyboard.KeyCode.F4://[alt+F4] - quit
                        break;
                    default:
                        break;
                    }
                }
                
                return false;
            }
        });
        
        //Version display
        //info.setText(AppName + " " + AppVersion + " ");
        info.setText(AppVersion + " ");
        
        //Actions binding
        btSaveAs.setAction(new Action() {
            @Override public void perform(Component source)
            {
                saveFileBrowserSheet.open(getWindow());
            }
        });
        btLoad.setAction(new Action() {
            @Override public void perform(Component source)
            {
                loadFileBrowserSheet.open(getWindow());
            }
        });
        
        //Set default directory selection in File Browser (packs/)
        try {//Set working directory as root directory for file browser
            File saveDir = new File(IOFactory.savePath); 
            if (!saveDir.exists())
                saveDir = new File(".");
            saveFileBrowserSheet.setRootDirectory(saveDir.getCanonicalFile());
            loadFileBrowserSheet.setRootDirectory(saveDir.getCanonicalFile());
        } catch (IOException e) {
            e.printStackTrace();
        }
        saveFileBrowserSheet.setMode(FileBrowserSheet.Mode.SAVE_AS);//FileBrowser Mode to File save
        loadFileBrowserSheet.setMode(FileBrowserSheet.Mode.OPEN);//FileBrowser Mode to File select
        
        loadFileBrowserSheet.setDisabledFileFilter(new Filter<File>() {//Filter for dcp files only
            @Override
            public boolean include(File file)
            {
                if (file.isDirectory() || file.getName().endsWith("."+IOFactory.dcpFileExt))
                    return false;
                return true;
            }
        });
        
        //File select in File Browser Event
        saveFileBrowserSheet.getFileBrowserSheetListeners().add(new FileBrowserSheetListener.Adapter() {
            @Override
            public void selectedFilesChanged(FileBrowserSheet fileBrowserSheet, Sequence<File> previousSelectedFiles)
            {
                //If save file changed
                if (fileBrowserSheet.getSelectedFile() != null) {
                    try {
                        String s = fileBrowserSheet.getSelectedFile().getCanonicalPath();
                        if (!s.equals(IOFactory.saveFile)) {
                            IOFactory.setSaveFile(s);
                            ASave.perform(fileBrowserSheet);
                        }
                    }
                    catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        loadFileBrowserSheet.getFileBrowserSheetListeners().add(new FileBrowserSheetListener.Adapter() {
            @Override
            public void selectedFilesChanged(FileBrowserSheet fileBrowserSheet, Sequence<File> previousSelectedFiles)
            {
                //If save file changed
                if (fileBrowserSheet.getSelectedFile() != null) {
                    try {
                        String s = fileBrowserSheet.getSelectedFile().getCanonicalPath();
                        if (!s.equals(IOFactory.saveFile)) {
                            loadProject(s);
                        }
                    }
                    catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        
        //Buttons listeners
        btBack.getButtonPressListeners().add(new ButtonPressListener() {
            @Override public void buttonPressed(Button bt)
            {
                if (!btNext.isEnabled()) btNext.setEnabled(true);
                
                if (tabPane.getSelectedIndex()>0) {
                    tabPane.getTabs().get(tabPane.getSelectedIndex()).setEnabled(false);
                    tabPane.setSelectedIndex(tabPane.getSelectedIndex()-1);
                    tabPane.getTabs().get(tabPane.getSelectedIndex()).setEnabled(true);
                }
                
                if (tabPane.getSelectedIndex() == 0)
                    bt.setEnabled(false);
            }
        });
        btNext.getButtonPressListeners().add(new ButtonPressListener() {
            @Override public void buttonPressed(Button bt)
            {
                if (!btBack.isEnabled()) btBack.setEnabled(true);
                
                if (tabPane.getSelectedIndex()<tabPane.getTabs().getLength()-1) {
                    tabPane.getTabs().get(tabPane.getSelectedIndex()).setEnabled(false);
                    tabPane.setSelectedIndex(tabPane.getSelectedIndex()+1);
                    tabPane.getTabs().get(tabPane.getSelectedIndex()).setEnabled(true);
                }
                
                if (tabPane.getSelectedIndex() == tabPane.getTabs().getLength()-1)
                    bt.setEnabled(false);
            }
        });
        btSave.getButtonPressListeners().add(new ButtonPressListener() {
            @Override public void buttonPressed(Button bt)
            {
                ASave.perform(bt);
                Prompt.prompt(MessageType.INFO, "Project data saved.", getWindow());
            }
        });
        btUndo.getButtonPressListeners().add(new ButtonPressListener() {
            @Override public void buttonPressed(Button bt)
            {
                if (Master.this.getTitle().contains("-") && Master.this.getTitle().contains("*")) { // Modified save file
                    ALoad.perform(bt);
                    Out.print(LOG_LEVEL.INFO, "Back to save file");
                }
                else { // back to default factory setup
                    facade.factoryReset();
                    titleUpdate();
                    bt.setEnabled(false);
                    Out.print(LOG_LEVEL.INFO, "Back to factory setup configuration");
                }
            }
        });
        btDefault.getButtonPressListeners().add(new ButtonPressListener() {
            @Override public void buttonPressed(Button bt)
            {
                facade.saveDefault();
                Out.print(LOG_LEVEL.INFO, "Default Workspace data saved.");
                Prompt.prompt(MessageType.INFO, "Workspace data saved as default.", getWindow());
            }
        });
        btHelp.getButtonPressListeners().add(new ButtonPressListener() {
            @Override public void buttonPressed(Button bt)
            {
                helper.setIndex(tabPane.getSelectedIndex());
                helper.open(Master.this, null);
            }
        });
        
        //Tabs selection listener
        tabPane.getTabPaneSelectionListeners().add(new TabPaneSelectionListener.Adapter() {
            @Override public void selectedIndexChanged(TabPane tabPane, int previousSelectedIndex)
            {
                try {
                    facade.setOpenTab(tabPane.getSelectedIndex());
                }
                catch (IOException e) {
                    e.printStackTrace();
                }
                
                updateStatusBar();
            }
        });
        
        updateStatusBar();
    }
    
    /**
     * Updates status bar info
     * step nbr, nbr of packs, nbr of groups
     */
    public void updateStatusBar()
    {
        statusBarStep.setText(String.valueOf(tabPane.getSelectedIndex()+1) + "/" +
                                String.valueOf(tabPane.getTabs().getLength()) + ",");
        statusBarNPacks.setText(String.valueOf(PackFactory.getPacks().getLength()));
        statusBarNGroups.setText(String.valueOf(GroupFactory.getGroups().getLength()));
    }

} public class BrowseAction extends Action
{

    private FileBrowserSheet fileBrowserSheet = null;

    private File rootDirectory = null;

    public BrowseAction(FileBrowserSheet fileBrowserSheet)
    {
        this.fileBrowserSheet = fileBrowserSheet;
        this.fileBrowserSheet.setRootDirectory(new File("").getAbsoluteFile());
    }

    public BrowseAction(FileBrowserSheet fileBrowserSheet, String rootDirectory)
    {
        this.fileBrowserSheet = fileBrowserSheet;
        this.rootDirectory = new File(rootDirectory);
    }

    @Override
    public void perform(Component component)
    {
        if (rootDirectory != null && rootDirectory.exists())
        {
            if (rootDirectory.isDirectory())
                fileBrowserSheet.setRootDirectory(rootDirectory.getAbsoluteFile());
            else
            {
                fileBrowserSheet.setRootDirectory(rootDirectory.getParentFile().getAbsoluteFile());
                fileBrowserSheet.setSelectedFile(rootDirectory.getAbsoluteFile());
            }
        }
        fileBrowserSheet.open(component.getWindow());
    }

} public class ScanFacade
{
    // Configuration
    private SCAN_MODE mode = SCAN_MODE.DEFAULT;// scan mode
    
    // Model
    private List<Pack> packs;//List of scanned packs
    private List<Group> groups;//List of scanned directories
    private List<TreeNode> treeData;//Tree view data

    
    public ScanFacade()
    {
        treeData = new ArrayList<TreeNode>();
        packs = new ArrayList<Pack>();
        groups = new ArrayList<Group>();
    }
    
    //// METHODS
    
    // Configuration ----------------------------------------------------
    public SCAN_MODE getScanMode()
    {
        return mode;
    }
    public void setScanMode(SCAN_MODE mode)
    {
        this.mode = mode;
        Master.facade.setupConfig.setScanMode(mode);
    }

    public SCAN_FOLDER getFolderScan()
    {
        return Master.facade.setupConfig.getScanFolder();
    }
    public void setFolderScan(SCAN_FOLDER folder)
    {
        Master.facade.setupConfig.setScanFolder(folder);
    }

    public boolean getFolderTarget()
    {
        return Master.facade.setupConfig.getScanTarget();
    }
    public void setFolderTarget(boolean folderTarget)
    {
        Master.facade.setupConfig.setScanTarget(folderTarget);
    }

    // Model ---------------------------------------------------
    public List<TreeNode> getTreeData()
    {
        return treeData;
    }
    
    // Pack
    public List<Pack> getPacks()
    {
        return packs;
    }
    
    // Group
    public List<Group> getGroups()
    {
        return groups;
    }
    
    /**
     * Get only packs checked in treeview
     * @param sequence of checked paths in treeview
     * @return list of checked packs
     */
    public List<Pack> getCheckedPacks(Sequence<Path> list) {
        List<Pack> selected = new ArrayList<Pack>();

        if (list.getLength() > 0) {
            Path p;
            for(int i = 0; i < list.getLength(); i++) {
                p = list.get(i);
                TreeNode node = treeData.get(p.get(0));
                for(Pack P:packs) {
                    if (node.getParent() == null && P.getName().equalsIgnoreCase(node.getText()) ) {
                        selected.add(P);
                        break;
                    }
                }
            }
        }
        return selected;
    }
    
    /**
     * Clear data from display + models
     */
    public void clear()
    {
        treeData.clear();
        packs.clear();
        groups.clear();
    }

} public class TaskDirScan extends Task<Integer>
{
    //Constants
    private final static int INFINITE_DEPTH = 20;//Maximum recursive scan depth if depth is '-'
    //Attributes
    private ScanFrame scanFrame;
    private String path;
    private List<TreeNode> treeData;
    private String depthValue;
    //Data
    private List<Group> groups;
    private List<Pack> packs;
    //Filter
    private boolean folderFilter;
    private FilenameFilter filter;
    
    
    public TaskDirScan(ScanFrame source, String path, List<TreeNode> treeData, FilenameFilter filter,
                       String depthValue, boolean folderFilter)
    {
        this.scanFrame = source;
        // point to data models lists
        this.groups = scanFrame.facade.getGroups();
        this.packs = scanFrame.facade.getPacks();
        
        this.path = path;
        this.treeData = treeData;
        this.filter= filter;
        
        this.depthValue = depthValue;
        this.folderFilter = folderFilter;
    }
    
    
    /**
     * Recursive Scan Function + Groups create
     * Scan DIR to treeBranch, directories to PARENT
     */
    private void recursivScan(File directory, TreeBranch treeBranch, Group PARENT, int DEPTH) {
        for(File f:directory.listFiles(filter)) {
            if (f.isDirectory()) {//Directory
                if (DEPTH>0) {
                    Group G = new Group(f.getName(),PARENT);
                    groups.add(G);//Group model add
                    TreeBranch T = new TreeBranch(IOFactory.imgFolder, f.getName());
                    recursivScan(f, T, G, DEPTH-1);//@-
                    treeBranch.add(T);//Tree Branch node add
                }
                else if (folderFilter) {//Get folders as packs
                    Pack P = CastFactory.fileToPack(f);
                    P.setPriority(packs.getLength());//Priority initialize
                    P.setGroup(PARENT);
                    packs.add(P);
                    treeBranch.add(new TreeNode(P.getIcon(), P.getName()));//Tree node add
                }
            }
            else {//File
                Pack P = CastFactory.fileToPack(f);
                P.setPriority(packs.getLength());//Priority initialize
                P.setGroup(PARENT);//Setting Parent
                packs.add(P);
                treeBranch.add(new TreeNode(P.getIcon(), P.getName()));//Tree node add
            }
        }
    }
    
    /**
     * Simple Scan Functions
     * @param directory to scan
     * @param folder folder filter is selected
     */
    private void simpleScan(File directory) {
        for(File f : directory.listFiles(filter)) {
            if (f.isDirectory()) {//Directory
                if (folderFilter) {//Directory filter cb selected (not active)
                    Pack P = CastFactory.fileToPack(f);
                    P.setPriority(packs.getLength());//Priority initialize
                    packs.add(P);
                    treeData.add(new TreeNode(P.getIcon(), P.getName()));
                }
            }
            else if (f.isFile()) {//File
                Pack P = CastFactory.fileToPack(f);
                P.setPriority(packs.getLength());//Priority initialize
                packs.add(P);
                treeData.add(new TreeNode(P.getIcon(), P.getName()));
            }
        }
    }
    
    @Override
    public Integer execute()
    {
        scanFrame.facade.clear();// Clear data
        
        if (!path.equals("")) {//If a path is entered
            File dir = new File(path);
            
            if (dir.exists()) {
                scanFrame.setModified(true);//Flag Modified
                
                TreeBranch treeBranch = new TreeBranch(dir.getName());
                SCAN_MODE scanMode = ScanFrame.getSingleton().facade.getScanMode();
                
                if (scanMode == SCAN_MODE.SIMPLE_SCAN) {//Simple Scan
                    simpleScan(dir);
                }
                else if (scanMode == SCAN_MODE.RECURSIVE_SCAN) {//Recursive Scan
                    String DEPTH = depthValue;
                    if (DEPTH.equals("-"))//Infinite depth
                        recursivScan(dir, treeBranch, null, INFINITE_DEPTH);
                    else//Limited depth [1-5]
                        recursivScan(dir, treeBranch, null, Integer.parseInt(DEPTH));
                }
                //Populate the treeData with nodes of the resulting directory TreeBranch
                for(int i=0; i<treeBranch.getLength(); i++)
                    treeData.add(treeBranch.get(i));
                
            }
            else {//Path doesn't exist
                return 2;
            }
        }
        else {//Path value empty
            return 1;
        }
        
        return 0;//Success
    }

} public class AppearTransition extends Transition {
    private Component component;
    private FadeDecorator fadeDecorator = new FadeDecorator();

    public AppearTransition(Component component, int duration, int rate) {
        super(duration, rate, false);
        this.component = component;
    }

    public Component getComponent() {
        return component;
    }

    @Override
    public void start(TransitionListener transitionListener) {
        component.getDecorators().add(fadeDecorator);

        super.start(transitionListener);
    }

    @Override
    public void stop() {
        component.getDecorators().remove(fadeDecorator);

        super.stop();
    }

    @Override
    protected void update() {
        fadeDecorator.setOpacity(getPercentComplete());
        component.repaint();
    }
} public class PathValidator implements Validator
{
    private Component component;
    private String tooltipText;
    private boolean required;

    public PathValidator(Component component, boolean required)
    {
        assert component != null;
        this.component = component;
        this.tooltipText = component.getTooltipText();
        this.required = required;
    }
    
    /**
     * Returns if a path string exists or empty string
     * @param path: to validate
     * @param required: if path must be defined
     * @return int: error code
     * 0 - no error
     * 1 - required path not defined
     * 2 - path doesn't exist on disk
     */
    private static int pathValidate(String path, boolean required) {
        assert path != null;
        if (path.length() == 0) return (required == true)?1:0;
        File f = new File(path);
        if (f.exists())
            return 0;
        return 2;
    }
    
    @Override
    public boolean isValid(String s)
    {
        int err = pathValidate(s, required);
        if (err != 0) {
            switch(err)
            {
            case 1:
                component.setTooltipText("Path is required");
                break;
            case 2:
                component.setTooltipText("Path doesn't exist on this filesystem!");
                break;
            }
            Out.print(LOG_LEVEL.DEBUG, "Path error: " + s);
            return false;
        }
        component.setTooltipText(this.tooltipText);
        return true;
    }

} public class Group implements Comparable<Group>, Serializable
{
    /**
     * Class written into save file
     */
    private static final long serialVersionUID = 7019635543387148548L;
    
    //Groups
    private String installGroups = "";//Install Groups *
    //Attributes
    private String name;//Group name
    private Group parent = null;//Parent group, if any
    private String description = "";//Group's description
    //Childs
    private List<Group> children = new ArrayList<Group>();//Childs, if is parent
    public void addChild(Group G) { children.add(G); }//Add child to Group
    public void removeChild(Group G) { children.remove(G); }//Delete Child Cascade
    public List<Group> getChildren() { return children; }//Returns the childs
    
    //Constructors
    public Group() {
    }
    public Group(String name) {
        this.name = name;
    }
    public Group(String name, Group parent) {
        this.name = name;
        setParent(parent);
    }
    public Group(Group group) {
        this.name = group.getName();
        if (group.getParent() != null)
        	setParent(group.getParent());
        setDescription(group.getDescription());
        setInstallGroups(group.getInstallGroups());
    }
    
    /**
     * Cast group model from Non-Maven data (<v1.2.1)
     * @param group
     */
    public Group(dcp.logic.model.Group group) {
        assert group != null;
        this.name = group.name;
        if (group.parent != null)
        	setParent(new Group(group.parent));
        setDescription(group.description);
        setInstallGroups(group.installGroups);
	}
	//Getters & Setters
    public String getInstallGroups() { return installGroups; }
    public void setInstallGroups(String installGroups) { this.installGroups = installGroups; }
    
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    
    public Group getParent() { return parent; }
    public void setParent(Group PARENT) { this.parent = PARENT; }
    public boolean hasParent(Group PARENT) {
        return GroupFactory.hasParent(this, PARENT);
    }
    
    public String getDescription() { return description; }
    public void setDescription(String description) { this.description = description; }
    
    public String getPath() {
        return CastFactory.pathToString(GroupFactory.getPath(this));
    }
    
    //Comparison function
    @Override
    public boolean equals(Object obj)//Groups compare
    {
        if (obj==null) return false;
        if (obj instanceof Group) {
            Group G = (Group) obj;
            if (G.getParent() != null)
                return this.name.equalsIgnoreCase(G.getName()) && this.getPath().equalsIgnoreCase(G.getPath());
            else//Group has no parent
                return this.name.equalsIgnoreCase(G.getName()) && this.parent == null;
        }
        else if (obj instanceof TreeBranch) {
            TreeBranch branch = (TreeBranch) obj;
            return this.name.equalsIgnoreCase(branch.getText()) &&
                    ( (this.getParent() != null && branch.getParent() != null && this.getParent().equals(branch.getParent())) ||
                            (this.getParent() == null && branch.getParent() == null) );
        }
        else return false;
    }
    
    @Override
    public String toString()
    {
        return this.getPath();
    }
    @Override
    public int compareTo(Group o)
    {
        if (this.equals(o))
            return 0;//Equal
        else {
            int string_compare = (this.getName().compareTo(o.getName()));//Name comparison
            if (string_compare != 0)
                return string_compare;
            else
                return -1;
        }
    }
    
} public class Pack implements Serializable
{
    /**
     * Write Pack data to save file
     */
    private static final long serialVersionUID = -8775694196832301518L;
    
    //Relations
    private Group group = null;//Pack member of this group
    private String installGroups = "";//Install Groups *
    private Group dependsOnGroup = null;//Group dependency
    private Pack dependsOnPack = null;//Pack dependency
    //Attributes
    private String name = "";//File Name
    private FILE_TYPE fileType = FILE_TYPE.File;//File type
    private double size = 0;//File Size in bytes
    private String path = "";//File Absolute Path
    private INSTALL_TYPE installType = INSTALL_TYPE.COPY;//Install Type
    private PLATFORM installOs = PLATFORM.ALL;//Install OS
    private int arch = 0;// Platform Architecture (32/64 bits|0 = all)
    private String installPath = "";//Install Path, after the $INSTALL_PATH defined
    private String shortcutPath = "";//Relative path for shortcut
    //Info
    private int priority = 0;//Pack install priority
    private String installName = "";//Pack Install Name
    private String installVersion = "1.0.0";//Pack Install Version
    private String description = "";//Pack description
    //Booleans
    private boolean silent_install = false;//If Setup install is passive
    private boolean required = false;//Is required
    private boolean selected = true;//Is selected
    private boolean hidden = false;//Is hidden
    private boolean override = true;//If pack will override existing pack
    private boolean shortcut = false;//If pack will have a shortcut created for it
    //Icon
    private String icon = "";//Icon file path
    private transient Image imgIcon = null;//Icon Image
    
    //Functions
    public String getBaseName() {//Name without extension
        if (name.contains("."))
            return name.substring(0,name.lastIndexOf("."));
        return name;
    }
    // extract version number from file name<
    public static String getVersionFromName(String name) {
        try {
            if (Pattern.compile(".*[0-9]+([._\\-a-zA-Z][0-9]+)+.*").matcher(name).find()) {
                String[] borders = name.split("[0-9]+([._\\-a-zA-Z][0-9]+)+[a-zA-Z]?");
                if (borders.length > 0)
                    return name.substring(borders[0].length(), name.length() - (borders.length > 1?borders[1].length():0));
                else
                    return name;
            }
            return "1.0.0";
        }
        catch(IllegalStateException e) {
            System.out.println(e);
            return "1.0.0";
        }
    }
    
    //Constructors
    public Pack(String NAME, Image ICON) {
        this.name = NAME;
        this.description = name;
        this.installName = name;
        this.installVersion = getVersionFromName(name);
        setIcon(ICON);
    }
    public Pack(Pack pack) {//Copy another Pack data
        name = pack.name;
        priority = pack.priority;
        fileType = pack.fileType;
        installName = pack.installName;
        installVersion = pack.installVersion; // 1.1
        size = pack.size;
        path = pack.path;
        group = pack.group;
        installType = pack.installType;
        if (pack.installOs != null) installOs = pack.installOs;
        arch = pack.arch; // 1.2
        installPath = pack.installPath;
        shortcutPath = pack.shortcutPath;
        installGroups = pack.installGroups;
        description = pack.description;
        if (pack.dependsOnGroup!=null) dependsOnGroup = pack.dependsOnGroup;
        if (pack.dependsOnPack!=null) dependsOnPack = pack.dependsOnPack;
        required = pack.required;
        selected = pack.selected;
        hidden = pack.hidden;
        override = pack.override;
        shortcut = pack.shortcut;
        silent_install = pack.silent_install;
        icon = pack.icon;
        imgIcon = pack.imgIcon;
    }
    
    /**
     * Cast pack model from Non-Maven data (<v1.2.1)
     * @param obj
     */
    public Pack(dcp.logic.model.Pack pack) {
        name = pack.name;
        priority = pack.priority;
        fileType = pack.fileType.cast();
        installName = pack.installName;
        installVersion = pack.installVersion; // 1.1
        size = pack.size;
        path = pack.path;
        if (pack.group != null) group = new Group(pack.group);
        installType = pack.installType.cast();
        if (pack.installOs != null) installOs = pack.installOs.cast();
        arch = pack.arch; // 1.2
        installPath = pack.installPath;
        shortcutPath = pack.shortcutPath;
        installGroups = pack.installGroups;
        description = pack.description;
        if (pack.dependsOnGroup != null) dependsOnGroup = GroupFactory.get(new Group(pack.dependsOnGroup).getPath());
        if (pack.dependsOnPack != null) dependsOnPack = new Pack(pack.dependsOnPack);
        required = pack.required;
        selected = pack.selected;
        hidden = pack.hidden;
        override = pack.override;
        shortcut = pack.shortcut;
        silent_install = pack.silent_install;
        icon = pack.icon;
        setIcon(imgIcon);
	}
    
    
	//Overrides
    @Override public boolean equals(Object obj)//Packs compare
    {
        if (obj==null) return false;
        if (obj instanceof Pack) {//With a Pack model
            Pack P = (Pack) obj;
            return this.name.toLowerCase().equals(P.getName().toLowerCase())//Name
                    && this.path.toLowerCase().equals(P.getPath().toLowerCase());//Path
        }
        else if (obj instanceof TreeNode) {//With a TreeView Node
            TreeNode node = (TreeNode) obj;
            return this.name.toLowerCase().equals(node.getText().toLowerCase())//Name
                    && this.group.equals(node.getParent());//Group
        }
        else return super.equals(obj);
    }
    @Override
    public String toString()
    {
        return this.name;
    }
    
    /**
     * Update pack name and path from new file
     * @param new_file
     */
    public void updatePack(File new_file) {
        name = new_file.getName();
        path = new_file.getAbsolutePath();
    }
    
    //Relation functions
    public Group getGroup() { return group; }
    public String getGroupName() {
        if (group != null)
            return group.getName();
        return "";
    }
    public String getGroupPath() {
        if (group != null)
            return group.getPath();
        return "";
    }
    public void setGroup(Group group) { this.group = group; }
    
    public String getInstallGroups() { return installGroups; }
    public void setInstallGroups(String installGroups) { this.installGroups = installGroups; }
    
    public Group getGroupDependency() { return dependsOnGroup; }
    public void setGroupDependency(Group group) { this.dependsOnGroup = group; this.dependsOnPack = null; }
    public Pack getPackDependency() { return dependsOnPack; }
    public void setPackDependency(Pack pack) { this.dependsOnPack = pack; this.dependsOnGroup = null; }
    
    
    //Getters & Setters
    public String getName() { return name; }
    
    public double getSize() { return size; }
    public void setSize(double size) { this.size = size; }

    public String getIconPath() { return icon; }
    public Image getIcon() { return imgIcon; }
    public void setIcon(String ICON)
    {
        this.icon = ICON;
        try {
            setIcon( Image.load(getClass().getResource(icon)) );
        } catch (TaskExecutionException e) {
            e.printStackTrace();
        }
    }
    public void setIcon(Image ICON) { imgIcon = ICON; }
    
    public String getPath() { return path; }
    public void setPath(String path) { this.path = path; }
    
    public String getInstallPath() { return installPath; }
    public void setInstallPath(String installPath) { this.installPath = installPath; }
    
    public String getShortcutPath() { return shortcutPath; }
    public void setShortcutPath(String shortcutPath) {
        if (shortcutPath.length() > 0 && !(shortcutPath.substring(0, 1).equals("/") || shortcutPath.substring(0, 1).equals("\\")))
            shortcutPath = "/" + shortcutPath;
        this.shortcutPath = shortcutPath;
    }
    
    
    //Info functions
    public String getInstallName() { return installName; }
    public void setInstallName(String installName) { this.installName = installName; }

    public String getInstallVersion() { return installVersion; }
    public void setInstallVersion(String installVersion) { this.installVersion = installVersion; }
    
    public String getDescription() { return description; }
    public void setDescription(String description) { this.description = description; }
    
    public INSTALL_TYPE getInstallType() { return installType; }
    public void setInstallType(INSTALL_TYPE IT) { installType = IT; }

    public PLATFORM getInstallOs() { return installOs; }
    public void setInstallOs(PLATFORM OS) { installOs = OS; }
    
    public int getArch() { return arch; }
    public void setArch(int arch) {
        assert arch == 0 || arch == 32 || arch == 64;
        this.arch = arch;
    }
    
    public int getPriority() { return priority+1; }
    public void setPriority(int priority) { this.priority = priority; }
    
    
    //Boolean functions
    public FILE_TYPE getFileType() { return fileType; }
    public void setFileType(FILE_TYPE fileType) { this.fileType = fileType; }
    public boolean isSilentInstall() { return this.silent_install; }
    public void setSilentInstall(boolean silent_install) { this.silent_install = silent_install; }
    
    public boolean isRequired() { return required; }
    public void setRequired(boolean required) {
        this.required = required;
        if (required == true && this.getPackDependency() != null) {
            this.getPackDependency().setRequired(true);
        }
    }
    public boolean isSelected() { return selected; }
    public void setSelected(boolean selected) {
        this.selected = selected;
        if (selected == true && this.getPackDependency() != null) {
            this.getPackDependency().setSelected(true);
        }
    }
    public boolean isHidden() { return hidden; }
    public void setHidden(boolean hidden) { this.hidden = hidden; }
    public boolean isOverride() { return override; }
    public void setOverride(boolean override) { this.override = override; }
    public boolean isShortcut() { return shortcut; }
    public void setShortcut(boolean shortcut) { this.shortcut = shortcut; }
    
} public class AppConfig implements Serializable
{
    /**
     * class written to workspace file: conf.dcp
     */
    private static final long serialVersionUID = 1194986597451209924L;

    // Constants
    private transient final static int MAX_RECENT_DIRECTORIES = 5;
    private transient final static int MAX_RECENT_PROJECTS = 3;
    // Flag
    transient private boolean modified = false;
    public boolean isModified() { return modified; }
    public void setModified(boolean modified) { this.modified = modified; }
    // Assistant flag
    private boolean help = true;
    // Attributes
    private String appName;
    private String appVersion;
    // Data
    private LinkedList<File> recentDirs = new LinkedList<File>();
    private LinkedList<File> recentProjects = new LinkedList<File>();
    // Workspace
    private float scanHorSplitPaneRatio = 0.25f;
    private float setVerSplitPaneRatio = 0.6f;
    private float setHorSplitPaneRatio = 0.7f;
    // Modes
    private BUILD_MODE buildMode = BUILD_MODE.IZPACK_BUILD;
    // Default Configurations
    private SetupConfig defaultSetupConfig;
    private IzpackConfig defaultIzpackConf;
    private NugetConfig defaultNugetConf;
    

    public AppConfig(String app_name, String app_version)
    {
        this.appName = app_name;
        this.appVersion = app_version;
        this.defaultSetupConfig = new SetupConfig("Package", "1.0.0");
        this.defaultIzpackConf = new IzpackConfig();
        this.defaultNugetConf = new NugetConfig();
    }
    
    // Attributes
    public String getAppName() { return appName; }
    public void setAppName(String app_name) { this.appName = app_name; setModified(true); }
    public String getAppVersion() { return appVersion; }
    public void setAppVersion(String app_version) { this.appVersion = app_version; setModified(true); }
    
    // Recent Scanned directories
    public List<File> getRecentDirs() {
        if (recentDirs.size() > 0) {
            List<File> list = new ArrayList<File>();
            Iterator<File> it = recentDirs.descendingIterator();
            while(it.hasNext()) {
                list.add(it.next());
            }
            return list;
        }
        else return null;
    }
    public void addRecentDir(File directory) {
        assert !directory.getAbsolutePath().equals("");
        if (recentDirs.size() == 0 || !directory.equals(recentDirs.getLast())) {
            if (recentDirs.contains(directory)) {
                recentDirs.remove(directory);
            }
            else {
                if (recentDirs.size() == MAX_RECENT_DIRECTORIES)
                    recentDirs.removeFirst();
            }
            recentDirs.addLast(directory);
            setModified(true);
        }
    }
    public boolean removeRecentDir(File directory) {
        assert !directory.getName().equals("");
        setModified(true);
        return recentDirs.remove(directory);
    }
    
    // Recent projects
    public List<File> getRecentProjects() {
        if (recentProjects.size() > 0) {
            List<File> list = new ArrayList<File>();
            Iterator<File> it = recentProjects.descendingIterator();
            while(it.hasNext()) {
                list.add(it.next());
            }
            return list;
        }
        else return null;
    }
    public void addRecentProject(File file) {
        assert !file.getAbsolutePath().equals("");
        if (recentProjects.size() == 0 || !file.equals(recentProjects.getLast())) {
            if (recentProjects.contains(file)) {
                recentProjects.remove(file);
            }
            else {
                if (recentProjects.size() == MAX_RECENT_PROJECTS)
                    recentProjects.removeFirst();
            }
            recentProjects.addLast(file);
            setModified(true);
        }
    }
    public boolean removeRecentProject(File file) {
        assert !file.getName().equals("");
        setModified(true);
        return recentProjects.remove(file);
    }
    
    // Workspace methods
    public float getScanHorSplitPaneRatio() { return scanHorSplitPaneRatio; } 
    public void setScanHorSplitPaneRatio(float scanHorSplitPaneRatio) { this.scanHorSplitPaneRatio = scanHorSplitPaneRatio; setModified(true); }
    
    public float getSetVerSplitPaneRatio() { return setVerSplitPaneRatio; } 
    public void setSetVerSplitPaneRatio(float setVerSplitPaneRatio) { this.setVerSplitPaneRatio = setVerSplitPaneRatio; setModified(true); }

    public float getSetHorSplitPaneRatio() { return setHorSplitPaneRatio; }
    public void setSetHorSplitPaneRatio(float setHorSplitPaneRatio) { this.setHorSplitPaneRatio = setHorSplitPaneRatio; setModified(true); }
    
    // Helpers methods
    public boolean isHelp() { return help; }
    public void setHelp(boolean help) { this.help = help; setModified(true); }
    
    // Default configurations methods
    public BUILD_MODE getBuildMode() { return buildMode; }
    public void setBuildMode(BUILD_MODE buildMode) { this.buildMode = buildMode;  setModified(true); }
    
    // Default Configurations methods
    public IzpackConfig getDefaultIzpackConfig() { return defaultIzpackConf; }
    public void setDefaultIzpackConfig(IzpackConfig izpackConf)
    {
        this.defaultIzpackConf = izpackConf;
        setModified(true);
    }
    
    public NugetConfig getDefaultNugetConfig() { return defaultNugetConf; }
    public void setDefaultNugetConfig(NugetConfig nugetConf)
    {
        this.defaultNugetConf = nugetConf;
        setModified(true);
    }
    
    public SetupConfig getDefaultSetupConfig() { return defaultSetupConfig; }
    public void setDefaultSetupConfig(SetupConfig defaultSetupConfig)
    {
        this.defaultSetupConfig = defaultSetupConfig;
        setModified(true);
    }
    
    
} public class SetupConfig implements Serializable
{
    /**
     * Generated serial for file save
     */
    private static final long serialVersionUID = 4289372637132381636L;
    
    // Application
    private String appName="";//Application name
    private String appVersion="";//Application version
    private int appWidth=800, appHeight=600 ;//Application dimensions
    private boolean resizable = false;//If window is resizable
    // Author
    private String authorName="";//Author Name
    private String authorEmail="";//Author Email
    private String appURL="";//Application URL
    // Resources
    private String readmePath="";//Readme Path
    private String licensePath="";//License Path
    private String logoPath="";//Logo Path
    private String sideLogoPath="";//Side Logo Path
    // Langpacks
    private boolean lp_eng = true;
    private boolean lp_fra = false;
    private boolean lp_deu = false;
    private boolean lp_spa = false;
    private boolean lp_cus = false;
    private String lp_iso = "", lp_path = "";
    // Scan
    private String srcPath = "";//Packs source path
    private SCAN_MODE scanMode = SCAN_MODE.SIMPLE_SCAN;// Scan mode (v1.2)
    private SCAN_FOLDER scanFolder = SCAN_FOLDER.PACK_FOLDER;// Scan folders as packs (v1.2)
    private boolean scanFolderTarget = false;// folder target mode (v1.2)
    // Configuration
    private String installPath = "";//Default install directory path
    private boolean forcePath = false;//Whether to force an install path or not
    private boolean registryCheck = false;//Activate registry check for installed version
    private boolean scriptGen = false;//Activate script generation at end of Setup
    // Shortcuts
    private boolean shortcuts = true;//Activate shortcuts creation
    private boolean folderShortcut = false;//Make a shortcut for global install path
    private boolean shToStartMenu = true;//Install shortcuts to start menu
    private boolean shToDesktop = false;//Install shortcuts to desktop

    
    public SetupConfig(String APPNAME, String APPVERSION)
    {
        appName = APPNAME;
        appVersion = APPVERSION;
        Map<String, String> sysEnv = System.getenv();
        if (OSValidator.isWindows()) {// Windows
            authorName = sysEnv.get("USERNAME");// From env var 'USERNAME'
            String USERDNSDOMAIN = sysEnv.get("USERDNSDOMAIN");
            authorEmail = authorName + (USERDNSDOMAIN != null?"@"+USERDNSDOMAIN:"");// From env var 'USERDNSDOMAIN'
            installPath = sysEnv.get("ProgramFiles") + "\\" + appName;// From env var 'ProgramFiles'
        }
        else if (OSValidator.isUnix() || OSValidator.isMac()) {// Unix & Mac
            authorName = sysEnv.get("USER");// From env var 'USER'
            if (authorName == null) {
                authorName = sysEnv.get("HOME");// From env var 'HOME'
                if (authorName != null && authorName.contains("/")) authorName = authorName.substring(authorName.lastIndexOf("/")+1);
            }
            installPath = sysEnv.get("HOME") + "/" + appName;// From env var 'HOME'
        }
        /*else {// Other OS
            authorName = "";
            installPath = "";
        }*/
        authorName = authorName.toUpperCase();
    }
    public SetupConfig(SetupConfig setupConfig)
    {
        appName = setupConfig.appName;
        appVersion = setupConfig.appVersion;
        appWidth = setupConfig.appWidth;
        appHeight = setupConfig.appHeight;
        resizable = setupConfig.resizable;
        
        authorName = setupConfig.authorName;
        authorEmail = setupConfig.authorEmail;
        appURL = setupConfig.appURL;
        
        readmePath = setupConfig.readmePath;
        licensePath = setupConfig.licensePath;
        logoPath = setupConfig.logoPath;
        sideLogoPath = setupConfig.sideLogoPath;
        
        lp_eng = setupConfig.lp_eng;
        lp_fra = setupConfig.lp_fra;
        lp_deu = setupConfig.lp_deu;
        lp_spa = setupConfig.lp_spa;
        lp_cus = setupConfig.lp_cus;
        
        srcPath = setupConfig.srcPath;
        scanMode = setupConfig.scanMode;// 1.2
        scanFolder = setupConfig.scanFolder;// 1.2
        scanFolderTarget = setupConfig.scanFolderTarget;// 1.2
        
        installPath = setupConfig.installPath;
        forcePath = setupConfig.forcePath;
        registryCheck = setupConfig.registryCheck;
        scriptGen = setupConfig.scriptGen;
        
        shortcuts = setupConfig.shortcuts;
        folderShortcut = setupConfig.folderShortcut;
        shToStartMenu = setupConfig.shToStartMenu;
        shToDesktop = setupConfig.shToDesktop;
    }
    
    /**
     * Cast setupConfig model from Non-Maven data (<v1.2.1)
     * @param setupConfig
     */
    public SetupConfig(dcp.logic.model.config.SetupConfig setupConfig)
    {
        appName = setupConfig.appName;
        appVersion = setupConfig.appVersion;
        appWidth = setupConfig.appWidth;
        appHeight = setupConfig.appHeight;
        resizable = setupConfig.resizable;
        
        authorName = setupConfig.authorName;
        authorEmail = setupConfig.authorEmail;
        appURL = setupConfig.appURL;
        
        readmePath = setupConfig.readmePath;
        licensePath = setupConfig.licensePath;
        logoPath = setupConfig.logoPath;
        sideLogoPath = setupConfig.sideLogoPath;
        
        lp_eng = setupConfig.lp_eng;
        lp_fra = setupConfig.lp_fra;
        lp_deu = setupConfig.lp_deu;
        lp_spa = setupConfig.lp_spa;
        lp_cus = setupConfig.lp_cus;
        
        srcPath = setupConfig.srcPath;
        if (setupConfig.scanMode != null)
        	scanMode = setupConfig.scanMode.cast();// 1.2.1 cast
        if (setupConfig.scanFolder != null)
        	scanFolder = setupConfig.scanFolder.cast();// 1.2.1 cast
        scanFolderTarget = setupConfig.scanFolderTarget;
        
        installPath = setupConfig.installPath;
        forcePath = setupConfig.forcePath;
        registryCheck = setupConfig.registryCheck;
        scriptGen = setupConfig.scriptGen;
        
        shortcuts = setupConfig.shortcuts;
        folderShortcut = setupConfig.folderShortcut;
        shToStartMenu = setupConfig.shToStartMenu;
        shToDesktop = setupConfig.shToDesktop;
	}
	//Application
    public String getAppName() { return appName; }
    public void setAppName(String appName) { this.appName = appName; }
    public String getAppVersion() { return appVersion; }
    public void setAppVersion(String appVersion) { this.appVersion = appVersion; }
    public int getAppWidth() { return appWidth; }
    public void setAppWidth(int appWidth) { this.appWidth = appWidth; }
    public int getAppHeight() { return appHeight; }
    public void setAppHeight(int appHeight) { this.appHeight = appHeight; }
    public boolean isResizable() { return resizable; }
    public void setResizable(boolean resizable) { this.resizable = resizable; }
    
    //Author
    public String getAuthorName() { return authorName; }
    public void setAuthorName(String authorName) { this.authorName = authorName; }
    public String getAuthorEmail() { return authorEmail; }
    public void setAuthorEmail(String authorEmail) { this.authorEmail = authorEmail; }
    public String getAppURL() { return appURL; }
    public void setAppURL(String URL) { appURL = URL; }
    
    //Resources
    public void setReadmePath(String path) { readmePath = path; }
    public String getReadmePath() { return readmePath; }
    public void setLicensePath(String path) { licensePath = path; }
    public String getLicensePath() { return licensePath; }
    public String getLogoPath() { return logoPath; }
    public void setLogoPath(String logoPath) { this.logoPath = logoPath; }
    public String getSideLogoPath() { return sideLogoPath; }
    public void setSideLogoPath(String sideLogoPath) { this.sideLogoPath = sideLogoPath; }
    
    //Langpacks
    public void setEnglish(boolean ENG) { lp_eng = ENG; }
    public boolean isEnglish() { return lp_eng; }
    public void setFrench(boolean FRA) { lp_fra = FRA; }
    public boolean isFrench() { return lp_fra; }
    public void setGerman(boolean DEU) { lp_deu = DEU; }
    public boolean isGerman() { return lp_deu; }
    public void setSpanish(boolean SPA) { lp_spa = SPA; }
    public boolean isSpanish() { return lp_spa; }
    public void setCustomLang(boolean CUS) { lp_cus = CUS; }
    public boolean isCustomLang() { return lp_cus; }
    public void setCustomLangISO(String ISO) { lp_iso = ISO; }
    public String getCustomLangISO() { return lp_iso; }
    public void setCustomLangPath(String PATH) { lp_path = PATH; }
    public String getCustomLangPath() { return lp_path; }
    
    //Scan
    public SCAN_MODE getScanMode() { return this.scanMode; }
    public void setScanMode(SCAN_MODE scanMode) { this.scanMode = scanMode; }
    public SCAN_FOLDER getScanFolder() { return this.scanFolder; }
    public void setScanFolder(SCAN_FOLDER scanFolder) { this.scanFolder = scanFolder; }
    public boolean getScanTarget() { return this.scanFolderTarget; }
    public void setScanTarget(boolean scanTarget) { this.scanFolderTarget = scanTarget; }
    
    //Configuration
    public String getSrcPath() { return srcPath; }
    public void setSrcPath(String srcPath) { this.srcPath = srcPath; }
    public String getInstallPath() { return installPath; }
    public void setInstallPath(String installPath) { this.installPath = installPath; }
    public boolean isForcePath() { return forcePath; }
    public void setForcePath(boolean forcePath) { this.forcePath = forcePath; }
    public boolean isRegistryCheck() { return registryCheck; }
    public void setRegistryCheck(boolean registryCheck) { this.registryCheck = registryCheck; }
    public boolean isScriptGen() { return scriptGen; }
    public void setScriptGen(boolean scriptGen) { this.scriptGen = scriptGen; }
    
    //Shortcuts
    public boolean isShortcuts() { return shortcuts; }
    public void setShortcuts(boolean shortcuts) { this.shortcuts = shortcuts; }
    public boolean isFolderShortcut() { return folderShortcut; }
    public void setFolderShortcut(boolean folderShortcut) { this.folderShortcut = folderShortcut; }
    public boolean isShToStartMenu() { return shToStartMenu; }
    public void setShToStartMenu(boolean shToStartMenu) { this.shToStartMenu = shToStartMenu; }
    public boolean isShToDesktop() { return shToDesktop; }
    public void setShToDesktop(boolean shToDesktop) { this.shToDesktop = shToDesktop; }
    
} public class Out
{
    //Log
    private static List<String> log = new ArrayList<String>();//Global Log
    public static List<String> getLog() { return log; }
    private static int displayLevel = 1;//logs to display must be >= than this
    
    private static List<String> compileLog = new ArrayList<String>();//Izpack compile Log
    public static List<String> getCompileLog() { return compileLog; }
    
    //Pivot GUI Component Repaint for log update
    private static Component logger = null;
    public static Component getLogger() { return logger; }
    public static void setLogger(Component logger) { Out.logger = logger; }
    
    //Clear the stream cache
    public static void clearCompileLog() {
        compileLog.clear();
    }
    
    //Add a new empty line
    public static void newLine() {
        print(LOG_LEVEL.INFO, "");
    }
    
    /**
     * Prints a string to the output stream
     * @param text: log text
     * @param outStream: output stream
     */
    public static void print(String text, PrintStream outStream) {
        outStream.println(text);
    }
    
    /**
     * print text with a tag
     * format: [TAG] TXT
     * @param TAG: tag name
     * @param TXT: text to log
     */
    public static void print(LOG_LEVEL level, String msg) {
        print("["+level.toString()+"] "+msg, System.out);
        log.add(msg);
        final String log_TXT = msg;
        
        if (level.value() >= displayLevel)// display if >= than threshold
            log(log_TXT);
    }
    
    public static void log(final String msg) {
        ApplicationContext.queueCallback(new Runnable() {//Enqueue GUI display repaint
            @Override public void run()
            {
                compileLog.add(msg);
                if (getLogger() != null) getLogger().repaint();//Component update
            }
        });
    }
    
}
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.net.URL;
import java.util.regex.PatternSyntaxException;
import org.apache.pivot.beans.BXML;
import org.apache.pivot.beans.Bindable;
import org.apache.pivot.collections.List;
import org.apache.pivot.collections.Map;
import org.apache.pivot.collections.Sequence;
import org.apache.pivot.collections.Sequence.Tree.Path;
import org.apache.pivot.serialization.SerializationException;
import org.apache.pivot.util.Resources;
import org.apache.pivot.wtk.Action;
import org.apache.pivot.wtk.Alert;
import org.apache.pivot.wtk.BoxPane;
import org.apache.pivot.wtk.Button;
import org.apache.pivot.wtk.ButtonPressListener;
import org.apache.pivot.wtk.ButtonStateListener;
import org.apache.pivot.wtk.Checkbox;
import org.apache.pivot.wtk.Component;
import org.apache.pivot.wtk.ComponentMouseListener;
import org.apache.pivot.wtk.ComponentStateListener;
import org.apache.pivot.wtk.FileBrowserSheet;
import org.apache.pivot.wtk.FileBrowserSheetListener;
import org.apache.pivot.wtk.FillPane;
import org.apache.pivot.wtk.LinkButton;
import org.apache.pivot.wtk.Menu;
import org.apache.pivot.wtk.MenuHandler;
import org.apache.pivot.wtk.Orientation;
import org.apache.pivot.wtk.PushButton;
import org.apache.pivot.wtk.RadioButton;
import org.apache.pivot.wtk.Spinner;
import org.apache.pivot.wtk.SpinnerSelectionListener;
import org.apache.pivot.wtk.SplitPane;
import org.apache.pivot.wtk.SplitPaneListener;
import org.apache.pivot.wtk.TablePane;
import org.apache.pivot.wtk.TextInputContentListener;
import org.apache.pivot.wtk.TreeViewNodeStateListener;
import org.apache.pivot.wtk.TablePane.Row;
import org.apache.pivot.wtk.TreeView.NodeCheckState;
import org.apache.pivot.wtk.TextInput;
import org.apache.pivot.wtk.TreeView;
import org.apache.pivot.wtk.Button.State;
import org.apache.pivot.wtk.content.ButtonData;
import org.apache.pivot.wtk.content.TreeNode;
import org.apache.pivot.wtk.effects.FadeTransition;
import org.apache.pivot.wtk.effects.Transition;
import org.apache.pivot.wtk.effects.TransitionListener;
import com.dcp.sm.config.io.IOFactory;
import com.dcp.sm.logic.factory.TypeFactory.FILE_TYPE;
import com.dcp.sm.logic.factory.TypeFactory.LOG_LEVEL;
import com.dcp.sm.logic.factory.TypeFactory.SCAN_FOLDER;
import com.dcp.sm.logic.factory.TypeFactory.SCAN_MODE;
import com.dcp.sm.gui.pivot.Master;
import com.dcp.sm.gui.pivot.actions.BrowseAction;
import com.dcp.sm.gui.pivot.facades.ScanFacade;
import com.dcp.sm.gui.pivot.tasks.TaskDirScan;
import com.dcp.sm.gui.pivot.transitions.AppearTransition;
import com.dcp.sm.gui.pivot.validators.PathValidator;
import com.dcp.sm.logic.model.Group;
import com.dcp.sm.logic.model.Pack;
import com.dcp.sm.logic.model.config.AppConfig;
import com.dcp.sm.logic.model.config.SetupConfig;
import com.dcp.sm.main.log.Out;
        // Depth transitions
        final AppearTransition depthAppTrans = new AppearTransition(depthPane, 150, 20);
        final FadeTransition depthFadeTrans = new FadeTransition(depthPane, 100, 20);
        
        // Validators
        inPath.setValidator(new PathValidator(inPath, false));
        
        // Data Binding
        treeView.setMenuHandler(MHTreeView);
        treeView.setTreeData(facade.getTreeData());// Bind Tree view to data
        treeView.getTreeViewNodeStateListeners().add(new TreeViewNodeStateListener() {
            @Override public void nodeCheckStateChanged(TreeView tv, Path arg1, NodeCheckState arg2)
            {
                setModified(true);
            }
        });
        
        // Action Binding
        btRefresh.setAction(ADirScan);
        btRefresh.setButtonDataKey("ENTER");
        btBrowse.setAction(new BrowseAction(fileBrowserSheet));
        
        // Set default directory selection in File Browser (packs/)
        try {// Set working directory as root directory for file browser
            fileBrowserSheet.setRootDirectory(new File(".").getCanonicalFile());
        } catch (IOException e) {
            e.printStackTrace();
        }
        fileBrowserSheet.setMode(FileBrowserSheet.Mode.SAVE_TO);// FileBrowser Mode to Directory selection
        
        // Directory select in File Browser Event
        fileBrowserSheet.getFileBrowserSheetListeners().add(new FileBrowserSheetListener.Adapter() {
            @Override
            public void selectedFilesChanged(FileBrowserSheet fileBrowserSheet, Sequence<File> previousSelectedFiles)
            {
                // If path changed
                if (!ScanFrame.this.inPath.getText().equals(fileBrowserSheet.getSelectedFile().getAbsolutePath())) {
                    boolean error = false;
                    if (!ScanFrame.this.inPath.getValidator().isValid(inPath.getText()) && ScanFrame.this.inPath.getText().length() > 0) {
                        error = true;
                    }
                    ScanFrame.this.inPath.setText(fileBrowserSheet.getSelectedFile().getAbsolutePath());
                    ADirScan.perform(fileBrowserSheet);// Scan Action
                    if (error == true) {
                        Out.print(LOG_LEVEL.INFO, "Updated folder path for packs to: "+ScanFrame.this.inPath.getText());
                        //setModified(false);
                    }
                }
            }
        });
        
        // update workspace scan horizontal splitpane ratio
        hSplitPane.getSplitPaneListeners().add(new SplitPaneListener.Adapter() {
            @Override public void splitRatioChanged(SplitPane sp, float ratio)
            {
                appConfig.setScanHorSplitPaneRatio(sp.getSplitRatio());
            }
        });
        
        // Update setup source path from inPath
        inPath.getTextInputContentListeners().add(new TextInputContentListener.Adapter() {
            @Override public void textChanged(TextInput TI)
            {
                if (TI.isValid())// path exists
                    setupConfig.setSrcPath(TI.getText());
            }
        });
        // Set root directory from written path to file browser
        inPath.getComponentStateListeners().add(new ComponentStateListener.Adapter() {
            @Override public void focusedChanged(Component component, Component obverseComponent)
            {
                if (!component.isFocused()) {
                    File root = new File(inPath.getText());
                    if (root.exists() && root.isDirectory())
                        fileBrowserSheet.setRootDirectory(root);
                }
            }
        });
        
        //Simple scan activate radio button listener
        btRadSimple.getButtonPressListeners().add(new ButtonPressListener() {
            @Override public void buttonPressed(Button bt)
            {
                if (facade.getScanMode() == SCAN_MODE.RECURSIVE_SCAN) {
                    setScanMode(SCAN_MODE.SIMPLE_SCAN);
                    btSelect.setVisible(true);
                    btSelect.setSelected(false);
                    selectExpTrans.start();//Select button expand transition
                    depthFadeTrans.start(new TransitionListener() {//Depth Fade transition
                        @Override public void transitionCompleted(Transition tr)
                        {
                            depthPane.setVisible(false);
                            btCollapse.setEnabled(false);
                            btExpand.setEnabled(false);
                            cbFolderScan.setEnabled(false);
                            cbFolderTarget.setEnabled(false);
                        }
                    });
                    ADirScan.perform(bt);
                }
            }
        });
        //Recursive scan activate radio button listener
        btRadRecursiv.getButtonPressListeners().add(new ButtonPressListener() {
            @Override public void buttonPressed(Button bt)
            {
                if (facade.getScanMode() == SCAN_MODE.SIMPLE_SCAN) {
                    setScanMode(SCAN_MODE.RECURSIVE_SCAN);
                    btCollapse.setEnabled(true);
                    btExpand.setEnabled(true);
                    selectCollTrans.start(new TransitionListener() {//Select button collapse transition
                        @Override public void transitionCompleted(Transition arg0)
                        {
                            btSelect.setVisible(false);
                            btSelectAll.setEnabled(false);
                            btSelectNone.setEnabled(false);
                            cbFolderScan.setEnabled(true);
                            cbFolderTarget.setEnabled(true);
                            // enable Group folder options by default
                            cbFolderScan.setSelected(true);
                            setFolderScan(SCAN_FOLDER.GROUP_FOLDER);