net.pms.medialibrary.gui.dialogs.folderdialog.DisplayPanel.java Source code

Java tutorial

Introduction

Here is the source code for net.pms.medialibrary.gui.dialogs.folderdialog.DisplayPanel.java

Source

/*
 * PS3 Media Server, for streaming any medias to your PS3.
 * Copyright (C) 2012  Ph.Waeber
 *
 * 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; version 2
 * of the License only.
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package net.pms.medialibrary.gui.dialogs.folderdialog;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.List;

import javax.swing.ButtonGroup;
import javax.swing.DropMode;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jgoodies.forms.builder.PanelBuilder;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;

import net.pms.Messages;
import net.pms.PMS;
import net.pms.medialibrary.commons.dataobjects.DOFileEntryBase;
import net.pms.medialibrary.commons.dataobjects.DOFileEntryFile;
import net.pms.medialibrary.commons.dataobjects.DOFileEntryFolder;
import net.pms.medialibrary.commons.dataobjects.DOFileEntryInfo;
import net.pms.medialibrary.commons.dataobjects.DOFileEntryPlugin;
import net.pms.medialibrary.commons.dataobjects.DOMediaLibraryFolder;
import net.pms.medialibrary.commons.dataobjects.DOTemplate;
import net.pms.medialibrary.commons.dataobjects.DOThumbnailPriority;
import net.pms.medialibrary.commons.dataobjects.FileDisplayProperties;
import net.pms.medialibrary.commons.dataobjects.comboboxitems.ConditionTypeCBItem;
import net.pms.medialibrary.commons.dataobjects.comboboxitems.SortOptionCBItem;
import net.pms.medialibrary.commons.dataobjects.comboboxitems.TemplateCBItem;
import net.pms.medialibrary.commons.enumarations.ConditionType;
import net.pms.medialibrary.commons.enumarations.DialogActionType;
import net.pms.medialibrary.commons.enumarations.FileDisplayType;
import net.pms.medialibrary.commons.enumarations.FileType;
import net.pms.medialibrary.commons.enumarations.SortOption;
import net.pms.medialibrary.commons.enumarations.ThumbnailPrioType;
import net.pms.medialibrary.commons.events.ConfigureFileDialogListener;
import net.pms.medialibrary.commons.events.FileEntryPluginDialogActionEvent;
import net.pms.medialibrary.commons.events.FileEntryPluginDialogActionListener;
import net.pms.medialibrary.commons.events.FilterFileDialogDialogEventArgs;
import net.pms.medialibrary.commons.events.FolderDialogActionListener;
import net.pms.medialibrary.commons.exceptions.TemplateException;
import net.pms.medialibrary.commons.helpers.FolderHelper;
import net.pms.medialibrary.commons.helpers.GUIHelper;
import net.pms.medialibrary.commons.interfaces.IMediaLibraryStorage;
import net.pms.medialibrary.gui.dialogs.ConfigureFileEntryDialog;
import net.pms.medialibrary.gui.dialogs.FileEntryPluginDialog;
import net.pms.medialibrary.gui.shared.FileDisplayPanel;
import net.pms.notifications.NotificationCenter;
import net.pms.notifications.NotificationSubscriber;
import net.pms.notifications.types.PluginEvent;
import net.pms.plugins.FileDetailPlugin;
import net.pms.plugins.PluginsFactory;

class DisplayPanel extends JPanel {
    private static final Logger log = LoggerFactory.getLogger(DisplayPanel.class);
    private static final long serialVersionUID = 4154536836952807722L;
    private IMediaLibraryStorage storage;
    private DOMediaLibraryFolder folder;
    private List<FolderDialogActionListener> folderDialogActionListeners;

    private JCheckBox cbDisplayItems;
    private JTextField tfMaxFiles;
    private JCheckBox cbInheritSort;
    private JCheckBox cbInheritDisplayFileAs;
    private JComboBox cbTemplate;
    private JButton bNewTemplate;
    private JButton bDeleteTemplate;
    private JButton bEditTemplate;
    private JButton bTemplateApply;
    private JButton bTemplateCancel;
    private JRadioButton rbDisplayItemAsFile;
    private JRadioButton rbDisplayIemAsFolder;
    private JScrollPane spTree;
    private DefaultTreeModel treeModel;
    private JLabel lTemplate;
    private JComboBox cbSortOption;
    private JComboBox cbSortType;
    private JRadioButton rbSortAsc;
    private JRadioButton rbSortDesc;
    private FileDisplayPanel pFileDispay;

    private JPanel pTemplate;
    private JPopupMenu treeContextMenu;

    private FolderHelper folderHelper = new FolderHelper();
    private boolean isCreatingTemplate = false;
    private boolean isEditingTemplate = false;

    protected long editingTemplateId;

    private JTree tree;

    private JMenuItem miRemove;
    private JMenuItem miRename;
    private JMenuItem miEdit;
    private JMenuItem miAddFile;
    private JMenuItem miAddFolder;
    private JMenuItem miAddInfo;
    private JMenu mAdd;
    private JLabel lMaxFiles;

    DisplayPanel(DOMediaLibraryFolder f, IMediaLibraryStorage storage,
            List<FolderDialogActionListener> folderDialogActionListeners) {
        this.folderDialogActionListeners = folderDialogActionListeners;
        this.storage = storage;
        folder = f;

        setLayout(new GridLayout());
        initContextMenu();
        init();
        initPluginChangeListener();
    }

    private void initPluginChangeListener() {
        NotificationCenter.getInstance(PluginEvent.class).subscribe(new NotificationSubscriber<PluginEvent>() {
            @Override
            public void onMessage(PluginEvent obj) {
                refreshAddMenu(true);
            }
        });
    }

    void setFileType(FileType fileType) {
        pFileDispay.setFileType(fileType);

        // update sort type combo box
        Object selectedItem = cbSortType.getSelectedItem();
        cbSortType.removeAllItems();
        for (ConditionTypeCBItem item : FolderHelper.getHelper().getSortByConditionTypes(Arrays.asList(fileType))) {
            cbSortType.addItem(item);
        }
        cbSortType.setSelectedItem(selectedItem);

        if (fileType != FileType.VIDEO) {
            rbDisplayItemAsFile.setSelected(true);
        }

        cbDisplayItems.setText(Messages.getString("ML.DisplayPanel.DisplayItems." + fileType.toString()));
        lMaxFiles.setText(Messages.getString("ML.DisplayPanel.lMaxFiles." + fileType.toString()));

        updateGUI();
    }

    void canInheritSortOrder(boolean inherit) {
        cbInheritSort.setEnabled(inherit);
        updateGUI();
    }

    void canInheritDisplayAs(boolean inherit) {
        cbInheritDisplayFileAs.setEnabled(inherit);
        updateGUI();
    }

    boolean isDisplayItems() {
        return cbDisplayItems.isSelected();
    }

    int getMaxFiles() {
        int res = 0;
        try {
            res = Integer.parseInt(tfMaxFiles.getText());
        } catch (Exception ex) {
        }
        return res;
    }

    boolean isInheritDisplayFileAs() {
        return cbInheritDisplayFileAs.isSelected();
    }

    boolean isInheritSort() {
        return cbInheritSort.isSelected();
    }

    FileDisplayProperties getDisplayProperties() throws TemplateException {
        if (isEditingTemplate) {
            throw new TemplateException(Messages.getString("ML.DisplayPanel.ForceSaveTemplateMsg"));
        }

        FileDisplayProperties props = new FileDisplayProperties();
        props.setDisplayNameMask(getDisdplayNameMask());
        props.setFileDisplayType(getFileDisplayType());
        props.getTemplate().setName(getTemplateName());
        props.getTemplate().setId(getTemplateId());
        props.setSortAscending(getSortAscending());
        props.setSortType(getSortType());
        props.setSortOption(getSortOption());
        props.setThumbnailPriorities(pFileDispay.getThumbnailPriorities());
        return props;
    }

    SortOption getSortOption() {
        SortOptionCBItem item = (SortOptionCBItem) cbSortOption.getSelectedItem();
        return item.getSortOption();
    }

    boolean getSortAscending() {
        return rbSortAsc.isSelected();
    }

    ConditionType getSortType() {
        if (cbSortType.getSelectedItem() instanceof ConditionTypeCBItem) {
            return ((ConditionTypeCBItem) cbSortType.getSelectedItem()).getConditionType();
        } else {
            return ConditionType.FILE_DATEINSERTEDDB;
        }
    }

    private String getDisdplayNameMask() {
        return pFileDispay.getDisplaynameMask();
    }

    private FileDisplayType getFileDisplayType() {
        FileDisplayType fdt = FileDisplayType.UNKNOWN;
        if (rbDisplayItemAsFile.isSelected()) {
            fdt = FileDisplayType.FILE;
        } else {
            fdt = FileDisplayType.FOLDER;
        }
        return fdt;
    }

    private String getTemplateName() {
        return cbTemplate.getSelectedItem() == null ? "" : cbTemplate.getSelectedItem().toString();
    }

    private long getTemplateId() {
        long id = -1;
        if (cbTemplate.getSelectedItem() != null && cbTemplate.getSelectedItem() instanceof TemplateCBItem) {
            id = ((TemplateCBItem) cbTemplate.getSelectedItem()).getId();
        }
        return id;
    }

    private void init() {
        // display items
        cbDisplayItems = new JCheckBox(
                Messages.getString("ML.DisplayPanel.DisplayItems." + folder.getFileType().toString()));
        cbDisplayItems.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                tfMaxFiles.setVisible(cbDisplayItems.isSelected());
                lMaxFiles.setVisible(cbDisplayItems.isSelected());
            }
        });
        tfMaxFiles = new JTextField();
        tfMaxFiles.setPreferredSize(new Dimension(50, tfMaxFiles.getPreferredSize().height));
        lMaxFiles = new JLabel();

        // create sort line
        cbSortOption = new JComboBox();
        for (SortOption so : SortOption.values()) {
            if (so == SortOption.Unknown)
                continue;

            cbSortOption.addItem(new SortOptionCBItem(so, Messages.getString("ML.SortOption." + so.toString())));
        }
        cbSortOption.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                if (cbSortOption.getSelectedItem() == null)
                    return;

                boolean isVisible = ((SortOptionCBItem) cbSortOption.getSelectedItem())
                        .getSortOption() == SortOption.FileProperty;
                cbSortType.setVisible(isVisible);
                rbSortAsc.setVisible(isVisible);
                rbSortDesc.setVisible(isVisible);
            }
        });
        ButtonGroup bgSort = new ButtonGroup();
        rbSortAsc = new JRadioButton(Messages.getString("ML.DisplayPanel.rbSortAsc"));
        rbSortDesc = new JRadioButton(Messages.getString("ML.DisplayPanel.rbSortDesc"));
        bgSort.add(rbSortAsc);
        bgSort.add(rbSortDesc);
        cbSortType = new JComboBox(
                FolderHelper.getHelper().getSortByConditionTypes(Arrays.asList(folder.getFileType())));
        cbInheritSort = new JCheckBox(Messages.getString("ML.DisplayPanel.cbInheritSort"));
        cbInheritSort.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                folder.setInheritSort(cbInheritSort.isSelected());
                updateGUI();
            }
        });

        // create display as (file or folder)
        ButtonGroup rbGroupDisplayItemAs = new ButtonGroup();
        rbDisplayItemAsFile = new JRadioButton(Messages.getString("ML.DisplayPanel.rbDisplayItemAsFile"));
        rbDisplayItemAsFile.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                updateGUI();
            }
        });
        rbDisplayIemAsFolder = new JRadioButton(Messages.getString("ML.DisplayPanel.rbDisplayIemAsFolder"));
        rbDisplayIemAsFolder.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                updateGUI();
            }
        });
        cbInheritDisplayFileAs = new JCheckBox(Messages.getString("ML.DisplayPanel.cbInheritDisplayFileAs"));
        cbInheritDisplayFileAs.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                cbInheritDisplayFileAsSelected();
            }
        });
        rbGroupDisplayItemAs.add(rbDisplayItemAsFile);
        rbGroupDisplayItemAs.add(rbDisplayIemAsFolder);

        // create file display type
        DOFileEntryBase fileEntry = new DOFileEntryBase(-1, null, 0,
                folder.getDisplayProperties().getDisplayNameMask(),
                folder.getDisplayProperties().getThumbnailPriorities(),
                folder.getDisplayProperties().getFileDisplayType(), 0, null, null);
        pFileDispay = new FileDisplayPanel(fileEntry, folder.getFileType());
        pFileDispay.setFileDisplayModeVisible(false);
        pFileDispay.setMaxLineLengthVisible(false);

        spTree = new JScrollPane();
        spTree.setPreferredSize(new Dimension(50, 115));

        // create folder display type
        initFolderDisplayType();

        refreshDisplayPanel();
    }

    private void cbInheritDisplayFileAsSelected() {
        if (isEditingTemplate) {
            JOptionPane.showMessageDialog(this,
                    Messages.getString("ML.DisplayPanel.SaveTemplateOnInheritDisplayAs"));
            cbInheritDisplayFileAs.setSelected(false);
        } else {
            folder.setInheritDisplayFileAs(cbInheritDisplayFileAs.isSelected());
            updateGUI();
        }
    }

    private void initFolderDisplayType() {

        lTemplate = new JLabel(Messages.getString("ML.DisplayPanel.lTemplate"));
        cbTemplate = new JComboBox();
        cbTemplate.setPreferredSize(new Dimension(150, cbTemplate.getPreferredSize().height));

        bNewTemplate = new JButton(Messages.getString("ML.DisplayPanel.bNewTemplate"));
        bNewTemplate.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                isCreatingTemplate = true;

                bNewTemplate.setVisible(false);
                bEditTemplate.setVisible(false);
                bDeleteTemplate.setVisible(false);
                bTemplateApply.setVisible(true);
                bTemplateCancel.setVisible(true);

                cbTemplate.setEditable(true);
                cbTemplate.removeAllItems();
                cbTemplate.requestFocus();

                List<DOThumbnailPriority> thumbnailPriorities = new ArrayList<DOThumbnailPriority>();
                thumbnailPriorities.add(new DOThumbnailPriority(-1, ThumbnailPrioType.THUMBNAIL, 10, 0));
                thumbnailPriorities.add(new DOThumbnailPriority(-1, ThumbnailPrioType.GENERATED, 10, 1));
                DOFileEntryFolder ff = new DOFileEntryFolder(new ArrayList<DOFileEntryBase>(), -1, null, 1,
                        "%title (%year) - %rating_percent/100", thumbnailPriorities, 0);
                showTree(ff);

                tree.setTransferHandler(new TemplateTreeTransferHandler());
                tree.setDragEnabled(true);
                tree.addKeyListener(new InternalKeyAdapter());
                tree.setEditable(true);
            }
        });

        bEditTemplate = new JButton(Messages.getString("ML.DisplayPanel.bEditTemplate"));
        bEditTemplate.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                if (cbTemplate.getSelectedItem() != null) {
                    editingTemplateId = ((TemplateCBItem) cbTemplate.getSelectedItem()).getId();

                    tree.setTransferHandler(new TemplateTreeTransferHandler());
                    tree.setDragEnabled(true);
                    tree.addKeyListener(new InternalKeyAdapter());

                    isEditingTemplate = true;

                    bNewTemplate.setVisible(false);
                    bEditTemplate.setVisible(false);
                    bDeleteTemplate.setVisible(false);
                    bTemplateApply.setVisible(true);
                    bTemplateCancel.setVisible(true);

                    cbTemplate.setEditable(true);
                    tree.setEditable(true);

                    String currTemplateName = cbTemplate.getSelectedItem().toString();
                    cbTemplate.removeAllItems();
                    cbTemplate.addItem(currTemplateName);
                    cbTemplate.requestFocus();
                }
            }
        });

        bDeleteTemplate = new JButton(Messages.getString("ML.DisplayPanel.bDeleteTemplate"));
        bDeleteTemplate.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                if (cbTemplate.getSelectedItem() != null) {
                    long selTemplateId = ((TemplateCBItem) cbTemplate.getSelectedItem()).getId();
                    if (storage.isTemplateIdInUse(selTemplateId)) {
                        JOptionPane.showMessageDialog(SwingUtilities.getRoot(cbTemplate),
                                Messages.getString("ML.DisplayPanel.TemplateInUse"));
                    } else if (JOptionPane.showConfirmDialog(SwingUtilities.getRoot(cbTemplate),
                            String.format(Messages.getString("ML.DisplayPanel.ConfirmDeleteTemplateMsg"),
                                    cbTemplate.getSelectedItem()),
                            Messages.getString("ML.DisplayPanel.ConfirmDeleteTemplateTitle"),
                            JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
                        storage.deleteTemplate(selTemplateId);
                        spTree.setViewportView(null);
                        updateTemplateList();
                    }
                }
            }
        });

        bTemplateApply = new JButton(Messages.getString("ML.DisplayPanel.bTemplateApply"));
        bTemplateApply.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                if (cbTemplate.getSelectedItem() == null) {
                    JOptionPane.showMessageDialog(bTemplateApply,
                            Messages.getString("ML.DisplayPanel.SetNameForTemplateMsg"));
                    return;
                }
                tree.setTransferHandler(null);
                tree.setDragEnabled(false);
                for (KeyListener kl : tree.getKeyListeners()) {
                    tree.removeKeyListener(kl);
                }

                bNewTemplate.setVisible(true);
                bEditTemplate.setVisible(true);
                bDeleteTemplate.setVisible(true);
                bTemplateApply.setVisible(false);
                bTemplateCancel.setVisible(false);

                cbTemplate.setEditable(false);
                tree.setEditable(false);

                DOTemplate currTemplate = null;
                if (isCreatingTemplate) {
                    currTemplate = new DOTemplate(cbTemplate.getSelectedItem().toString(), -1);
                    DOFileEntryFolder fileFolder = getFileFolder();
                    storage.insertTemplate(currTemplate, fileFolder);
                } else if (isEditingTemplate) {
                    currTemplate = new DOTemplate(cbTemplate.getSelectedItem().toString(), editingTemplateId);
                    DOFileEntryFolder fileFolder = getFileFolder();
                    storage.updateTemplate(currTemplate, fileFolder);
                }

                updateTemplateList();

                isEditingTemplate = false;
                isCreatingTemplate = false;

                cbTemplate.setSelectedItem(new TemplateCBItem(currTemplate.getId(), currTemplate.getName()));

                for (FolderDialogActionListener l : folderDialogActionListeners) {
                    l.templateUpdatePerformed();
                }
            }
        });

        bTemplateCancel = new JButton(Messages.getString("ML.DisplayPanel.bTemplateCancel"));
        bTemplateCancel.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                tree.setTransferHandler(null);
                tree.setDragEnabled(false);
                for (KeyListener kl : tree.getKeyListeners()) {
                    tree.removeKeyListener(kl);
                }

                bNewTemplate.setVisible(true);
                bEditTemplate.setVisible(true);
                bDeleteTemplate.setVisible(true);
                bTemplateApply.setVisible(false);
                bTemplateCancel.setVisible(false);
                cbTemplate.setEditable(false);
                cbTemplate.removeAllItems();
                spTree.setViewportView(null);

                for (DOTemplate template : storage.getAllTemplates()) {
                    cbTemplate.addItem(new TemplateCBItem(template.getId(), template.getName()));
                }

                isCreatingTemplate = false;
                isEditingTemplate = false;
            }
        });

        switch (folder.getDisplayProperties().getFileDisplayType()) {
        case FILE:
            rbDisplayItemAsFile.setSelected(true);
            break;
        case FOLDER:
            rbDisplayIemAsFolder.setSelected(true);
        }

        if (folder.getDisplayProperties().isSortAscending()) {
            rbSortAsc.setSelected(true);
        } else {
            rbSortDesc.setSelected(true);
        }

        cbTemplate.addItemListener(new ItemListener() {
            @Override
            public void itemStateChanged(ItemEvent e) {
                if (!isCreatingTemplate && cbTemplate.getSelectedItem() instanceof TemplateCBItem) {
                    clearTree();
                    showTree(((TemplateCBItem) cbTemplate.getSelectedItem()).getId());
                }

            }
        });

        // select configured values
        updateTemplateList();
        bTemplateApply.setVisible(false);
        bTemplateCancel.setVisible(false);
        tfMaxFiles.setVisible(folder.isDisplayItems());
        lMaxFiles.setVisible(folder.isDisplayItems());

        cbInheritDisplayFileAs.setSelected(folder.isInheritDisplayFileAs());
        cbDisplayItems.setSelected(folder.isDisplayItems());
        tfMaxFiles.setText(String.valueOf(folder.getMaxFiles()));
        cbInheritSort.setSelected(folder.isInheritSort());
        cbTemplate.setSelectedItem(new TemplateCBItem(folder.getDisplayProperties().getTemplate().getId(),
                folder.getDisplayProperties().getTemplate().getName()));
        cbSortType
                .setSelectedItem(folderHelper.getConditionTypeCBItem(folder.getDisplayProperties().getSortType()));
    }

    /**
     * Initializes the context menu The menu item event listeners also live here
     */
    private void initContextMenu() {
        String iconsFolder = "/resources/images/";

        treeContextMenu = new JPopupMenu();
        treeContextMenu.setEnabled(true);

        mAdd = new JMenu(Messages.getString("ML.DisplayPanel.Menu.Add"));
        mAdd.setIcon(new ImageIcon(getClass().getResource(iconsFolder + "add-16.png")));

        miAddInfo = new JMenuItem(Messages.getString("ML.DisplayPanel.Menu.Info"));
        miAddInfo.setIcon(new ImageIcon(getClass().getResource(iconsFolder + "filefolderentry-16.png")));
        miAddInfo.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                addNode(FileDisplayType.INFO);
            }
        });

        miAddFolder = new JMenuItem(Messages.getString("ML.DisplayPanel.Menu.AddFolder"));
        miAddFolder.setIcon(new ImageIcon(getClass().getResource(iconsFolder + "filefolder-16.png")));
        miAddFolder.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                addNode(FileDisplayType.FOLDER);
            }
        });

        miAddFile = new JMenuItem(Messages.getString("ML.DisplayPanel.Menu.AddFile"));
        miAddFile.setIcon(new ImageIcon(getClass().getResource(iconsFolder + "filefolderfile_single-16.png")));
        miAddFile.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                addNode(FileDisplayType.FILE);
            }
        });

        refreshAddMenu(false);

        miRename = new JMenuItem(Messages.getString("ML.DisplayPanel.Menu.Rename"));
        miRename.setIcon(new ImageIcon(getClass().getResource(iconsFolder + "rename-16.png")));
        miRename.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                renameSelectedNode();
            }
        });
        miRemove = new JMenuItem(Messages.getString("ML.DisplayPanel.Menu.Delete"));
        miRemove.setIcon(new ImageIcon(getClass().getResource(iconsFolder + "delete-16.png")));
        miRemove.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                deleteSelectedNodeRequested();
            }
        });
        miEdit = new JMenuItem(Messages.getString("ML.DisplayPanel.Menu.Edit"));
        miEdit.setIcon(new ImageIcon(getClass().getResource(iconsFolder + "edit-16.png")));
        miEdit.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                editNodeRequested();
            }
        });

        treeContextMenu.add(mAdd);
        treeContextMenu.add(miEdit);
        treeContextMenu.add(miRename);
        treeContextMenu.add(miRemove);
    }

    private void refreshAddMenu(boolean refreshPlugins) {
        mAdd.add(miAddInfo);
        mAdd.add(miAddFolder);
        mAdd.add(miAddFile);

        if (refreshPlugins) {
            //Get, sort and add plugins to context menu
            List<FileDetailPlugin> plugins = PluginsFactory.getFileDetailPlugins();
            if (plugins.size() > 0) {
                mAdd.addSeparator();

                Collections.sort(plugins, new Comparator<FileDetailPlugin>() {
                    @Override
                    public int compare(FileDetailPlugin o1, FileDetailPlugin o2) {
                        return o1.getName().compareTo(o2.getName());
                    }
                });

                for (FileDetailPlugin entry : plugins) {
                    if (entry.isPluginAvailable() && entry.isInstanceAvailable()) {
                        FileEntryPluginMenuItem dynItem = new FileEntryPluginMenuItem(entry);
                        dynItem.addActionListener(new ActionListener() {

                            @Override
                            public void actionPerformed(ActionEvent e) {
                                if (tree.getSelectionPath() != null) {
                                    DefaultMutableTreeNode parent = (DefaultMutableTreeNode) tree.getSelectionPath()
                                            .getLastPathComponent();
                                    if (parent.getUserObject() instanceof DOFileEntryFolder) {
                                        DOFileEntryFolder parentFolder = (DOFileEntryFolder) parent.getUserObject();
                                        FileDetailPlugin plugin = ((FileEntryPluginMenuItem) e.getSource())
                                                .getPlugin();

                                        String configDir = PMS.getConfiguration().getProfileDirectory()
                                                + File.separatorChar + "mlx_fileentry_plugin_configs"
                                                + File.separatorChar;
                                        File cfgDir = new File(configDir);
                                        if (!cfgDir.isDirectory()) {
                                            cfgDir.mkdirs();
                                        }

                                        File configFile;
                                        int i = 1;
                                        do {
                                            configFile = new File(configDir + plugin.getClass().getSimpleName()
                                                    + "_" + i++ + ".cfg");
                                        } while (configFile.exists());

                                        DOFileEntryPlugin fileEntry = new DOFileEntryPlugin(-1, parentFolder, 0, "",
                                                parentFolder.getThumbnailPriorities(),
                                                parentFolder.getMaxLineLength(), plugin,
                                                configFile.getAbsolutePath());

                                        if (plugin.getConfigurationPanel() != null) {
                                            if (configFile.exists()) {
                                                try {
                                                    plugin.loadConfiguration(configFile.getAbsolutePath());
                                                } catch (IOException ex) {
                                                    log.error(String.format(
                                                            "Failed to load configuration file %s for plugin of type %s",
                                                            configFile.getAbsoluteFile(),
                                                            plugin.getClass().getName()), ex);
                                                }
                                            }
                                            showPluginDialog(fileEntry, true);
                                        } else {
                                            DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(fileEntry);
                                            treeModel.insertNodeInto(newNode, (MutableTreeNode) tree
                                                    .getSelectionPath().getLastPathComponent(), 0);
                                            tree.setSelectionPath(new TreePath(newNode.getPath()));
                                            tree.startEditingAtPath(tree.getSelectionPath());
                                        }
                                    }
                                }
                            }
                        });
                        mAdd.add(dynItem);
                    }
                }
            }
        }
    }

    private void showPluginDialog(DOFileEntryPlugin plugin, boolean isNew) {
        if (plugin == null) {
            return;
        }

        FileEntryPluginDialog d = new FileEntryPluginDialog(plugin, isNew);
        d.addFileEntryPluginDialogActionListener(new FileEntryPluginDialogActionListener() {

            @Override
            public void fileEntryPluginDialogActionReceived(FileEntryPluginDialogActionEvent e) {
                if (e.getActionType() == DialogActionType.APPLY || e.getActionType() == DialogActionType.OK) {
                    try {
                        e.getFileEntryPlugin().getPlugin()
                                .saveConfiguration(e.getFileEntryPlugin().getPluginConfigFilePath());
                    } catch (IOException e1) {
                        log.error(String.format("Failed to save config file %s for plugin %s",
                                e.getFileEntryPlugin().getPluginConfigFilePath(),
                                e.getFileEntryPlugin().getClass().getName()), e1);
                    }

                    DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(e.getFileEntryPlugin());
                    DefaultMutableTreeNode selNode = (DefaultMutableTreeNode) tree.getSelectionPath()
                            .getLastPathComponent();
                    if (e.isNew()) {
                        treeModel.insertNodeInto(newNode, selNode,
                                getNewNodeInsertPosition(e.getFileEntryPlugin(), selNode));
                        tree.setSelectionPath(new TreePath(newNode.getPath()));
                        ((FileEntryPluginDialog) e.getSource()).setIsNew(false);
                    } else {
                        selNode.setUserObject(e.getFileEntryPlugin());
                        treeModel.reload(selNode);
                    }
                }

                if (e.getActionType() == DialogActionType.OK || e.getActionType() == DialogActionType.CANCEL) {
                    ((FileEntryPluginDialog) e.getSource()).dispose();
                }
            }
        });
        d.setMinimumSize(new Dimension(600, 300));
        d.setModal(true);
        d.setLocation(GUIHelper.getCenterDialogOnParentLocation(getSize(), this));
        d.setVisible(true);
    }

    private void addNode(FileDisplayType fileDisplayType) {
        if (tree.getSelectionPath() != null
                && tree.getSelectionPath().getLastPathComponent() instanceof DefaultMutableTreeNode) {
            DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) tree.getSelectionPath()
                    .getLastPathComponent();
            if (parentNode.getUserObject() instanceof DOFileEntryFolder) {
                DOFileEntryFolder parentFolder = (DOFileEntryFolder) parentNode.getUserObject();
                DOFileEntryBase fileEntry = new DOFileEntryBase(-1, parentFolder, 0, "",
                        parentFolder.getThumbnailPriorities(), fileDisplayType, parentFolder.getMaxLineLength(),
                        null, null);
                ConfigureFileEntryDialog d = new ConfigureFileEntryDialog(fileEntry, parentFolder,
                        folder.getFileType());
                d.addConfigureFileDialogDialogListener(new ConfigureFileDialogListener() {

                    @Override
                    public void configureFileDialogAction(FilterFileDialogDialogEventArgs e) {
                        switch (e.getActionType()) {
                        case OK:
                            DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(e.getEntry());
                            DefaultMutableTreeNode nodeToDropOnto = (DefaultMutableTreeNode) tree.getSelectionPath()
                                    .getLastPathComponent();
                            treeModel.insertNodeInto(newNode, nodeToDropOnto,
                                    getNewNodeInsertPosition(e.getEntry(), nodeToDropOnto));
                            tree.expandPath(tree.getSelectionPath());
                            break;
                        }
                        ((ConfigureFileEntryDialog) e.getSource()).dispose();
                    }
                });
                d.setModal(true);
                d.pack();
                d.setLocation(GUIHelper.getCenterDialogOnParentLocation(d.getSize(), tree));
                d.setVisible(true);
            }
        }
    }

    private int getNewNodeInsertPosition(DOFileEntryBase fileEntry, DefaultMutableTreeNode nodeToDropOnto) {
        int res = 0;
        if (!isFolder(fileEntry)) {
            for (int i = 0; i < nodeToDropOnto.getChildCount(); i++) {
                if (!isFolder((DOFileEntryBase) ((DefaultMutableTreeNode) nodeToDropOnto.getChildAt(i))
                        .getUserObject())) {
                    res = i;
                    break;
                }
            }
            if (res == -1) {
                res = nodeToDropOnto.getChildCount();
            }
        }
        return res;
    }

    private boolean isFolder(DOFileEntryBase fileEntry) {
        boolean res = true;
        if (fileEntry instanceof DOFileEntryFile
                || (fileEntry instanceof DOFileEntryPlugin
                        && !((DOFileEntryPlugin) fileEntry).getPlugin().isFolder())
                || (fileEntry instanceof DOFileEntryInfo)) {
            res = false;
        }
        return res;
    }

    private void renameSelectedNode() {
        tree.startEditingAtPath(tree.getSelectionPath());
    }

    private void deleteSelectedNodeRequested() {
        if (tree.getSelectionPath().getLastPathComponent() instanceof DefaultMutableTreeNode) {
            DefaultMutableTreeNode nodeToRemove = (DefaultMutableTreeNode) tree.getSelectionPath()
                    .getLastPathComponent();
            if (nodeToRemove.getParent() == null) {
                JOptionPane.showMessageDialog(getTopLevelAncestor(),
                        Messages.getString("ML.DisplayPanel.DontDeleteRootMsg"));
                return;
            }

            treeModel.removeNodeFromParent(nodeToRemove);
        }
    }

    private void editNodeRequested() {
        if (tree.getSelectionPath() != null
                && tree.getSelectionPath().getLastPathComponent() instanceof DefaultMutableTreeNode) {
            DefaultMutableTreeNode dtn = (DefaultMutableTreeNode) tree.getSelectionPath().getLastPathComponent();
            if (dtn.getUserObject() instanceof DOFileEntryPlugin) {
                showPluginDialog((DOFileEntryPlugin) dtn.getUserObject(), false);
            } else if (dtn.getUserObject() instanceof DOFileEntryBase) {
                DOFileEntryBase f = (DOFileEntryBase) dtn.getUserObject();

                ConfigureFileEntryDialog d = new ConfigureFileEntryDialog(f, f.getParent(), folder.getFileType());
                d.addConfigureFileDialogDialogListener(new ConfigureFileDialogListener() {

                    @Override
                    public void configureFileDialogAction(FilterFileDialogDialogEventArgs e) {
                        if (tree.getSelectionPath() != null && tree.getSelectionPath()
                                .getLastPathComponent() instanceof DefaultMutableTreeNode) {
                            DefaultMutableTreeNode dtn = (DefaultMutableTreeNode) tree.getSelectionPath()
                                    .getLastPathComponent();
                            switch (e.getActionType()) {
                            case OK:
                                dtn.setUserObject(e.getEntry());
                                break;
                            }
                            ((ConfigureFileEntryDialog) e.getSource()).dispose();
                        }
                    }
                });
                d.setModal(true);
                d.pack();
                d.setLocation(GUIHelper.getCenterDialogOnParentLocation(d.getSize(), tree));
                d.setVisible(true);
            }
        }
    }

    private void clearTree() {
        if (treeModel != null && treeModel.getRoot() != null) {
            ((DefaultMutableTreeNode) treeModel.getRoot()).removeAllChildren();
        }
    }

    private DOFileEntryFolder getFileFolder() {
        DOFileEntryFolder rootFolder = null;
        if (treeModel != null && treeModel.getRoot() instanceof DefaultMutableTreeNode) {
            DefaultMutableTreeNode rootNode = (DefaultMutableTreeNode) treeModel.getRoot();
            if (rootNode.getUserObject() instanceof DOFileEntryFolder) {
                rootFolder = (DOFileEntryFolder) rootNode.getUserObject();
                rootFolder = addChildFolders(rootNode, rootFolder);
            }
        }
        return rootFolder;
    }

    private DOFileEntryFolder addChildFolders(DefaultMutableTreeNode node, DOFileEntryFolder folder) {
        int positionInParent = 0;
        DOFileEntryFolder parentFolder;
        if (node.getUserObject() instanceof DOFileEntryFolder) {
            parentFolder = (DOFileEntryFolder) node.getUserObject();
            parentFolder.getChildren().clear();
        } else {
            return folder;
        }

        for (int i = 0; i < node.getChildCount(); i++) {

            if (node.getChildAt(i) instanceof DefaultMutableTreeNode) {
                DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) node.getChildAt(i);
                if (childNode.getUserObject() instanceof DOFileEntryBase) {
                    DOFileEntryBase childFile = (DOFileEntryBase) childNode.getUserObject();
                    childFile.setParent(parentFolder);
                    childFile.setPositionInParent(positionInParent++);

                    folder.getChildren().add(childFile);
                    if (childFile instanceof DOFileEntryFolder) {
                        addChildFolders(childNode, (DOFileEntryFolder) childFile);
                    }
                }
            }
        }

        return folder;
    }

    private void updateTemplateList() {
        cbTemplate.removeAllItems();

        for (DOTemplate template : storage.getAllTemplates()) {
            cbTemplate.addItem(new TemplateCBItem(template.getId(), template.getName()));
        }
    }

    private void refreshDisplayPanel() {
        PanelBuilder builder;
        CellConstraints cc = new CellConstraints();

        FormLayout layout = new FormLayout("3px, fill:500:grow, 3px", // columns
                "2dlu, p, 3px, p, 3px, p, 3px, p, 3px, p, 3px, p, 3px, fill:p:grow, 2dlu"); // rows
        builder = new PanelBuilder(layout);
        builder.setOpaque(true);

        builder.add(getDisplayItemsPanel(), cc.xy(2, 2));
        builder.addSeparator(Messages.getString("ML.DisplayPanel.sSorting"), cc.xy(2, 4));
        builder.add(refreshSortPanel(), cc.xy(2, 6));
        builder.addSeparator(Messages.getString("ML.DisplayPanel.sDisplayAs"), cc.xy(2, 8));

        builder.add(refreshDisplayFileAsPanel(), cc.xy(2, 10));

        // file
        builder.add(pFileDispay, cc.xy(2, 14));

        // folder
        pTemplate = refreshTemplatePanel();
        builder.add(pTemplate, cc.xy(2, 12));
        builder.add(spTree, cc.xy(2, 14));

        removeAll();
        add(builder.getPanel());
        setFileType(folder.getFileType());
        validate();
    }

    private Component getDisplayItemsPanel() {
        PanelBuilder builder;
        CellConstraints cc = new CellConstraints();

        FormLayout layout = new FormLayout("3px, p, 3px, fill:10:grow, 3px, p, 3px", // columns
                "p"); // rows
        builder = new PanelBuilder(layout);
        builder.setOpaque(true);

        builder.add(cbDisplayItems, cc.xy(2, 1));
        builder.add(lMaxFiles, cc.xy(4, 1, CellConstraints.RIGHT, CellConstraints.CENTER));
        builder.add(tfMaxFiles, cc.xy(6, 1));

        return builder.getPanel();
    }

    private JPanel refreshSortPanel() {
        PanelBuilder builder;
        CellConstraints cc = new CellConstraints();

        FormLayout layout = new FormLayout("3px, p, 3px, p, 3px, p, 7px, p, 3px, r:p:grow, 3px", // columns
                "p"); // rows
        builder = new PanelBuilder(layout);
        builder.setOpaque(true);

        builder.add(cbSortOption, cc.xy(2, 1));
        builder.add(cbSortType, cc.xy(4, 1));
        builder.add(rbSortAsc, cc.xy(6, 1));
        builder.add(rbSortDesc, cc.xy(8, 1));
        builder.add(cbInheritSort, cc.xy(10, 1));

        return builder.getPanel();
    }

    private JPanel refreshTemplatePanel() {
        PanelBuilder builder;
        CellConstraints cc = new CellConstraints();

        FormLayout layout = new FormLayout("3px, p, 3px, p, 3px, p, 3px, p, 3px, p, 3px, p, 3px, p, 3px", // columns
                "p"); // rows
        builder = new PanelBuilder(layout);
        builder.setOpaque(true);

        builder.add(lTemplate, cc.xy(2, 1));
        builder.add(cbTemplate, cc.xy(4, 1));
        builder.add(bNewTemplate, cc.xy(6, 1));
        builder.add(bEditTemplate, cc.xy(8, 1));
        builder.add(bDeleteTemplate, cc.xy(10, 1));
        builder.add(bTemplateApply, cc.xy(12, 1));
        builder.add(bTemplateCancel, cc.xy(14, 1));

        return builder.getPanel();
    }

    private JPanel refreshDisplayFileAsPanel() {
        PanelBuilder builder;
        CellConstraints cc = new CellConstraints();

        FormLayout layout = new FormLayout("3px, p, 10px, p, 3px, r:p:grow, 3px", // columns
                "p"); // rows
        builder = new PanelBuilder(layout);
        builder.setOpaque(true);

        builder.add(rbDisplayItemAsFile, cc.xy(2, 1));
        builder.add(rbDisplayIemAsFolder, cc.xy(4, 1));
        builder.add(cbInheritDisplayFileAs, cc.xy(6, 1));

        return builder.getPanel();
    }

    private void updateGUI() {
        if (cbInheritSort.isEnabled()) {
            cbInheritSort.setSelected(folder.isInheritSort());
        } else {
            cbInheritSort.setSelected(false);
        }

        if (cbInheritDisplayFileAs.isEnabled()) {
            cbInheritDisplayFileAs.setSelected(folder.isInheritDisplayFileAs());
        } else {
            cbInheritDisplayFileAs.setSelected(false);
        }

        cbSortOption.setSelectedItem(new SortOptionCBItem(folder.getDisplayProperties().getSortOption(),
                Messages.getString("ML.SortOption." + folder.getDisplayProperties().getSortOption().toString())));

        FileDisplayProperties fdp = folder.getDisplayProperties();
        if (cbInheritSort.isSelected()) {
            // update the fields that have to be
            cbSortType.setSelectedItem(folderHelper.getConditionTypeCBItem(fdp.getSortType()));
            cbSortType.setEnabled(false);
            rbSortAsc.setSelected(fdp.isSortAscending());
            rbSortAsc.setEnabled(false);
            rbSortDesc.setEnabled(false);
            cbSortOption.setEnabled(false);
        } else {
            cbSortType.setEnabled(true);
            rbSortAsc.setEnabled(true);
            rbSortDesc.setEnabled(true);
            cbSortOption.setEnabled(true);
        }

        if (cbInheritDisplayFileAs.isSelected()) {
            if (fdp.getFileDisplayType() == FileDisplayType.FILE) {
                rbDisplayItemAsFile.setSelected(true);
            } else {
                rbDisplayIemAsFolder.setSelected(true);
            }
            pFileDispay.setThumbnailPriorities(fdp.getThumbnailPriorities());
            pFileDispay.setDisplayNameMask(fdp.getDisplayNameMask());
            cbTemplate.setSelectedItem(new TemplateCBItem(fdp.getTemplate().getId(), fdp.getTemplate().getName()));
            pFileDispay.setEnabled(false);
            rbDisplayItemAsFile.setEnabled(false);
            rbDisplayIemAsFolder.setEnabled(false);
            lTemplate.setEnabled(false);
            cbTemplate.setEnabled(false);
            bNewTemplate.setVisible(false);
            bEditTemplate.setVisible(false);
            bDeleteTemplate.setVisible(false);
        } else {
            pFileDispay.setEnabled(true);
            rbDisplayItemAsFile.setEnabled(true);
            rbDisplayIemAsFolder.setEnabled(true);
            lTemplate.setEnabled(true);
            if (!isCreatingTemplate && !isEditingTemplate) {
                cbTemplate.setEnabled(true);
                bNewTemplate.setVisible(true);
                bEditTemplate.setVisible(true);
                bDeleteTemplate.setVisible(true);
            }
        }

        if (rbDisplayItemAsFile.isSelected()) {
            pFileDispay.setVisible(true);

            pTemplate.setVisible(false);
            spTree.setVisible(false);
        } else if (rbDisplayIemAsFolder.isSelected()) {
            pFileDispay.setVisible(false);

            pTemplate.setVisible(true);
            spTree.setVisible(true);
        }

        if (folder.getFileType() != FileType.VIDEO) {
            rbDisplayIemAsFolder.setVisible(false);
            rbDisplayItemAsFile.setVisible(false);
        } else {
            rbDisplayIemAsFolder.setVisible(true);
            rbDisplayItemAsFile.setVisible(true);
        }
    }

    private void showTree(long templateId) {
        showTree(storage.getFileFolder(templateId));
        expandAllNodes(tree, new TreePath((TreeNode) treeModel.getRoot()), true);
    }

    @SuppressWarnings("unchecked")
    private void expandAllNodes(JTree tree, TreePath parent, boolean expand) {
        // Traverse children
        TreeNode node = (TreeNode) parent.getLastPathComponent();
        if (node.getChildCount() >= 0) {
            for (Enumeration<TreeNode> e = node.children(); e.hasMoreElements();) {
                TreeNode n = e.nextElement();
                TreePath path = parent.pathByAddingChild(n);
                expandAllNodes(tree, path, expand);
            }
        }

        // Expand or collapse must be done bottom-up
        if (expand) {
            tree.expandPath(parent);
        } else {
            tree.collapsePath(parent);
        }
    }

    private void showTree(DOFileEntryFolder fileFolder) {
        if (fileFolder != null) {
            DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(fileFolder);
            TemplateTreeCellRenderer cellRenderer = new TemplateTreeCellRenderer();
            treeModel = new DefaultTreeModel(rootNode);
            tree = new JTree(treeModel);
            tree.setCellRenderer(cellRenderer);
            tree.setRootVisible(true);
            tree.setAutoscrolls(true);
            tree.setEditable(false);

            tree.addMouseListener(new MouseAdapter() {

                @Override
                public void mouseClicked(MouseEvent e) {
                    JTree tree = (JTree) e.getSource();
                    TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());
                    tree.setSelectionPath(selPath);

                    if (e.getButton() == MouseEvent.BUTTON3) {
                        Object o = tree.getLastSelectedPathComponent();
                        if (tree.isEditable() && o != null && o instanceof DefaultMutableTreeNode) {
                            DefaultMutableTreeNode node = (DefaultMutableTreeNode) o;
                            if (node.getUserObject() instanceof DOFileEntryFolder) {
                                mAdd.setVisible(true);
                                miEdit.setEnabled(true);
                            } else if (node.getUserObject() instanceof DOFileEntryFile) {
                                mAdd.setVisible(false);
                                miEdit.setEnabled(true);
                            } else if (node.getUserObject() instanceof DOFileEntryInfo) {
                                mAdd.setVisible(false);
                                miEdit.setEnabled(true);
                            } else if (node.getUserObject() instanceof DOFileEntryPlugin) {
                                mAdd.setVisible(false);
                                if (((DOFileEntryPlugin) node.getUserObject()).getPlugin()
                                        .getConfigurationPanel() == null) {
                                    miEdit.setEnabled(false);
                                } else {
                                    miEdit.setEnabled(true);
                                }
                            }
                            treeContextMenu.show(tree, e.getX(), e.getY());
                        }
                    }
                }
            });

            tree.addKeyListener(new KeyAdapter() {
                @Override
                public void keyPressed(KeyEvent e) {
                    if (tree.getSelectionPath() != null && tree.getSelectionPath().getLastPathComponent() != null
                            && tree.getSelectionPath().getLastPathComponent() instanceof DefaultMutableTreeNode) {
                        DefaultMutableTreeNode selNode = (DefaultMutableTreeNode) tree.getSelectionPath()
                                .getLastPathComponent();
                        if (selNode != null) {
                            if (e.getKeyCode() == (KeyEvent.VK_DELETE | KeyEvent.VK_D)) {
                                deleteSelectedNodeRequested();
                            } else if (e.getKeyCode() == KeyEvent.VK_R) {
                                renameSelectedNode();
                            } else if (e.getKeyCode() == KeyEvent.VK_I) {
                                addNode(FileDisplayType.INFO);
                            } else if (e.getKeyCode() == KeyEvent.VK_O) {
                                addNode(FileDisplayType.FOLDER);
                            } else if (e.getKeyCode() == KeyEvent.VK_F) {
                                addNode(FileDisplayType.FILE);
                            }
                        }
                    }
                }
            });

            tree.setDropMode(DropMode.ON_OR_INSERT);

            tree.setCellEditor(new TemplateTreeCellEditor(tree, cellRenderer));

            showTree(rootNode);

            spTree.setViewportView(tree);
        }
    }

    private void showTree(DefaultMutableTreeNode folderNode) {
        assert (folderNode != null);

        if (folderNode.getUserObject() instanceof DOFileEntryFolder) {
            DOFileEntryFolder folder = (DOFileEntryFolder) folderNode.getUserObject();
            int nbMissing = 0;
            for (int i = 0; i < folder.getChildren().size(); i++) {
                DOFileEntryBase currFile = folder.getChildren().get(i);
                //don't show unavailable plugins
                if (currFile instanceof DOFileEntryPlugin && (((DOFileEntryPlugin) currFile).getPlugin() == null
                        || !((DOFileEntryPlugin) currFile).getPlugin().isInstanceAvailable())) {
                    nbMissing++;
                    continue;
                }

                DefaultMutableTreeNode newFolderNode = new DefaultMutableTreeNode(currFile);
                treeModel.insertNodeInto(newFolderNode, folderNode, currFile.getPositionInParent() - nbMissing);
                if (currFile instanceof DOFileEntryFolder) {
                    showTree(newFolderNode);
                }
            }
        }
    }

    private class InternalKeyAdapter extends KeyAdapter {

        @Override
        public void keyPressed(KeyEvent e) {
            if (tree.getSelectionPath() != null && tree.getSelectionPath().getLastPathComponent() != null
                    && tree.getSelectionPath().getLastPathComponent() instanceof DefaultMutableTreeNode) {
                DefaultMutableTreeNode currentNode = (DefaultMutableTreeNode) tree.getSelectionPath()
                        .getLastPathComponent();
                if (currentNode != null) {
                    if (e.getKeyCode() == 127) { // delete
                        treeModel.removeNodeFromParent(currentNode);
                    }
                }
            }
        }
    }
}