phex.gui.tabs.library.LibraryTab.java Source code

Java tutorial

Introduction

Here is the source code for phex.gui.tabs.library.LibraryTab.java

Source

/*
 *  PHEX - The pure-java Gnutella-servent.
 *  Copyright (C) 2001 - 2005 Phex Development Group
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package phex.gui.tabs.library;

import java.awt.*;
import java.awt.datatransfer.StringSelection;
import java.awt.event.*;
import java.io.File;
import java.io.IOException;

import javax.swing.*;
import javax.swing.event.*;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.xml.bind.JAXBException;

import org.apache.commons.lang.SystemUtils;

import phex.common.ServiceManager;
import phex.common.ThreadPool;
import phex.common.URN;
import phex.common.format.NumberFormatUtils;
import phex.event.ShareChangeListener;
import phex.gui.actions.FWAction;
import phex.gui.actions.GUIActionPerformer;
import phex.gui.common.*;
import phex.gui.common.table.FWSortedTableModel;
import phex.gui.common.table.FWTable;
import phex.gui.common.table.FWTableColumnModel;
import phex.gui.dialogs.ExportDialog;
import phex.gui.dialogs.FilterLibraryDialog;
import phex.gui.tabs.FWTab;
import phex.share.*;
import phex.utils.*;
import phex.xml.*;

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

public class LibraryTab extends FWTab {
    private static final String SHARED_FILES_TABLE_IDENTIFIER = "SharedFilesTable";

    private JLabel sharedFilesLabel;

    private FWTable sharedFilesTable;

    private JTree mainTree;

    //private FileSystemTreeModel fileSystemTreeModel;
    private SharingTreeModel sharingTreeModel;

    private FWPopupMenu fileTreePopup;
    private FWPopupMenu fileTablePopup;

    private JScrollPane sharedFilesTableScrollPane;

    private SharedFilesTableModel sharedFilesModel;

    private FWTableColumnModel sharedFilesColumnModel;

    public LibraryTab() {
        super(MainFrame.LIBRARY_TAB_ID, Localizer.getString("Library"),
                GUIRegistry.getInstance().getIconFactory().getIcon("Library"), Localizer.getString("TTTLibrary"),
                Localizer.getChar("LibraryMnemonic"),
                KeyStroke.getKeyStroke(Localizer.getString("LibraryAccelerator")), MainFrame.LIBRARY_TAB_INDEX);
    }

    public void initComponent(XJBGUISettings guiSettings) {
        CellConstraints cc = new CellConstraints();
        FormLayout tabLayout = new FormLayout("2dlu, fill:d:grow, 2dlu", // columns
                "2dlu, fill:p:grow, 2dlu"); //rows
        PanelBuilder tabBuilder = new PanelBuilder(tabLayout, this);
        JPanel contentPanel = new JPanel();
        FWElegantPanel elegantPanel = new FWElegantPanel(Localizer.getString("Library"), contentPanel);
        tabBuilder.add(elegantPanel, cc.xy(2, 2));

        FormLayout contentLayout = new FormLayout("fill:d:grow", // columns
                "fill:d:grow"); //rows
        PanelBuilder contentBuilder = new PanelBuilder(contentLayout, contentPanel);

        MouseHandler mouseHandler = new MouseHandler();

        JPanel treePanel = createTreePanel(mouseHandler);
        JPanel tablePanel = createTablePanel(guiSettings, mouseHandler);

        JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, treePanel, tablePanel);
        splitPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
        splitPane.setDividerSize(4);
        splitPane.setOneTouchExpandable(false);
        contentBuilder.add(splitPane, cc.xy(1, 1));

        sharedFilesLabel = new JLabel(" ");
        sharedFilesLabel.setHorizontalAlignment(JLabel.RIGHT);
        elegantPanel.addHeaderPanelComponent(sharedFilesLabel, BorderLayout.EAST);
        ShareManager.getInstance().getSharedFilesService()
                .addSharedFilesChangeListener(new SharedFilesChangeHandler());

        fileTreePopup = new FWPopupMenu();
        fileTablePopup = new FWPopupMenu();

        FWAction action;

        action = getTabAction(ADD_SHARE_FOLDER_ACTION_KEY);
        fileTreePopup.addAction(action);
        action = getTabAction(REMOVE_SHARE_FOLDER_ACTION_KEY);
        fileTreePopup.addAction(action);

        if (SystemUtils.IS_OS_WINDOWS || SystemUtils.IS_OS_MAC_OSX) {
            action = getTabAction(EXPLORE_FOLDER_ACTION_KEY);
            fileTreePopup.addAction(action);
        }

        action = getTabAction(OPEN_FILE_ACTION_KEY);
        fileTablePopup.addAction(action);

        action = getTabAction(VIEW_BITZI_ACTION_KEY);
        fileTablePopup.addAction(action);

        fileTablePopup.addSeparator();
        fileTreePopup.addSeparator();

        action = getTabAction(RESCAN_ACTION_KEY);
        fileTablePopup.addAction(action);
        fileTreePopup.addAction(action);

        action = getTabAction(EXPORT_ACTION_KEY);
        fileTablePopup.addAction(action);
        fileTreePopup.addAction(action);

        action = getTabAction(FILTER_ACTION_KEY);
        fileTablePopup.addAction(action);
        fileTreePopup.addAction(action);
    }

    private JPanel createTreePanel(MouseHandler mouseHandler) {
        JPanel panel = new JPanel();
        CellConstraints cc = new CellConstraints();
        FormLayout layout = new FormLayout("fill:d:grow", // columns
                "fill:d:grow, 1dlu, p"); //rows
        PanelBuilder tabBuilder = new PanelBuilder(layout, panel);

        sharingTreeModel = new SharingTreeModel();
        mainTree = new JTree(sharingTreeModel);
        mainTree.setMinimumSize(new Dimension(0, 0));
        mainTree.setRowHeight(0);
        mainTree.setCellRenderer(new SharingTreeRenderer());
        mainTree.addMouseListener(mouseHandler);

        mainTree.getSelectionModel().addTreeSelectionListener(new SelectionHandler());
        ToolTipManager.sharedInstance().registerComponent(mainTree);

        // open up first level of nodes
        TreeNode root = (TreeNode) sharingTreeModel.getRoot();
        int count = root.getChildCount();
        for (int i = 0; i < count; i++) {
            mainTree.expandPath(new TreePath(new Object[] { root, root.getChildAt(i) }));
        }

        JScrollPane treeScrollPane = new JScrollPane(mainTree);
        tabBuilder.add(treeScrollPane, cc.xywh(1, 1, 1, 1));

        FWToolBar shareToolbar = new FWToolBar(FWToolBar.HORIZONTAL);
        shareToolbar.setBorderPainted(false);
        shareToolbar.setFloatable(false);
        tabBuilder.add(shareToolbar, cc.xy(1, 3));

        FWAction action = new AddShareFolderAction();
        addTabAction(ADD_SHARE_FOLDER_ACTION_KEY, action);
        shareToolbar.addAction(action);

        action = new RemoveShareFolderAction();
        addTabAction(REMOVE_SHARE_FOLDER_ACTION_KEY, action);
        shareToolbar.addAction(action);

        if (SystemUtils.IS_OS_WINDOWS || SystemUtils.IS_OS_MAC_OSX) {
            action = new ExploreFolderAction();
            addTabAction(EXPLORE_FOLDER_ACTION_KEY, action);
        }

        return panel;
    }

    private JPanel createTablePanel(XJBGUISettings guiSettings, MouseHandler mouseHandler) {
        JPanel panel = new JPanel();
        CellConstraints cc = new CellConstraints();
        FormLayout layout = new FormLayout("fill:d:grow", // columns
                "fill:d:grow, 1dlu, p"); //rows
        PanelBuilder tabBuilder = new PanelBuilder(layout, panel);

        sharedFilesModel = new SharedFilesTableModel();
        XJBGUITable xjbTable = GUIUtils.getXJBGUITableByIdentifier(guiSettings, SHARED_FILES_TABLE_IDENTIFIER);
        buildSharedFilesTableColumnModel(xjbTable);
        sharedFilesTable = new FWTable(new FWSortedTableModel(sharedFilesModel), sharedFilesColumnModel);
        sharedFilesTable.activateAllHeaderActions();
        sharedFilesTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        sharedFilesTable.addMouseListener(mouseHandler);
        sharedFilesTable.getSelectionModel().addListSelectionListener(new SelectionHandler());
        sharedFilesTableScrollPane = FWTable.createFWTableScrollPane(sharedFilesTable);

        tabBuilder.add(sharedFilesTableScrollPane, cc.xy(1, 1));

        FWToolBar shareToolbar = new FWToolBar(FWToolBar.HORIZONTAL);
        shareToolbar.setBorderPainted(false);
        shareToolbar.setFloatable(false);
        tabBuilder.add(shareToolbar, cc.xy(1, 3));

        FWAction action;

        action = new OpenFileAction();
        addTabAction(OPEN_FILE_ACTION_KEY, action);
        shareToolbar.addAction(action);

        action = new ViewBitziTicketAction();
        addTabAction(VIEW_BITZI_ACTION_KEY, action);
        shareToolbar.addAction(action);

        shareToolbar.addSeparator();

        action = new RescanAction();
        addTabAction(RESCAN_ACTION_KEY, action);
        shareToolbar.addAction(action);

        action = new ExportAction();
        addTabAction(EXPORT_ACTION_KEY, action);
        shareToolbar.addAction(action);

        action = new FilterAction();
        addTabAction(FILTER_ACTION_KEY, action);
        shareToolbar.addAction(action);

        return panel;
    }

    /**
     * This is overloaded to update the table size for the progress bar on
     * every UI update. Like font size change!
     */
    public void updateUI() {
        super.updateUI();
        if (sharedFilesTableScrollPane != null) {
            FWTable.updateFWTableScrollPane(sharedFilesTableScrollPane);
        }
    }

    private LibraryNode getSelectedTreeComponent() {
        TreePath path = mainTree.getSelectionPath();
        if (path == null) {
            return null;
        }
        LibraryNode node = (LibraryNode) path.getLastPathComponent();
        return node;
    }

    //////////////////////////////////////////////////////////////////////////
    /// XML serializing and deserializing
    //////////////////////////////////////////////////////////////////////////

    private void buildSharedFilesTableColumnModel(XJBGUITable tableSettings) {
        int[] columnIds = SharedFilesTableModel.getColumnIdArray();
        XJBGUITableColumnList columnList = null;
        if (tableSettings != null) {
            columnList = tableSettings.getTableColumnList();
        }

        sharedFilesColumnModel = new FWTableColumnModel(sharedFilesModel, columnIds, columnList);
    }

    public void appendXJBGUISettings(XJBGUISettings xjbSettings) throws JAXBException {
        super.appendXJBGUISettings(xjbSettings);

        XJBGUITableColumnList xjbList = sharedFilesColumnModel.createXJBGUITableColumnList();
        ObjectFactory objFactory = new ObjectFactory();
        XJBGUITable xjbTable = objFactory.createXJBGUITable();
        xjbTable.setTableColumnList(xjbList);
        xjbTable.setTableIdentifier(SHARED_FILES_TABLE_IDENTIFIER);
        xjbSettings.getTableList().getTableList().add(xjbTable);
    }

    //////////////////////////////////////////////////////////////////////////
    /// Actions
    //////////////////////////////////////////////////////////////////////////

    private static final String ADD_SHARE_FOLDER_ACTION_KEY = "AddShareFolderAction";
    private static final String REMOVE_SHARE_FOLDER_ACTION_KEY = "RemoveShareFolderAction";
    private static final String RESCAN_ACTION_KEY = "RescanAction";
    private static final String VIEW_BITZI_ACTION_KEY = "ViewBitziTicketAction";
    private static final String EXPORT_ACTION_KEY = "ExportAction";
    private static final String FILTER_ACTION_KEY = "FilterAction";
    private static final String OPEN_FILE_ACTION_KEY = "OpenFileAction";
    private static final String EXPLORE_FOLDER_ACTION_KEY = "ExploreFolderAction";

    class RescanAction extends FWAction {
        RescanAction() {
            super(Localizer.getString("LibraryTab_Rescan"),
                    GUIRegistry.getInstance().getIconFactory().getIcon("Refresh"),
                    Localizer.getString("LibraryTab_TTTRescan"));
        }

        public void actionPerformed(ActionEvent e) {
            sharingTreeModel.updateFileSystem();
            GUIActionPerformer.rescanSharedFiles();
        }

        /**
         * @see phex.gui.actions.FWAction#refreshActionState()
         */
        public void refreshActionState() {
        }
    }

    private class ExportAction extends FWAction {
        ExportAction() {
            super(Localizer.getString("LibraryTab_Export"),
                    GUIRegistry.getInstance().getIconFactory().getIcon("Export"),
                    Localizer.getString("LibraryTab_TTTExport"));
        }

        public void actionPerformed(ActionEvent e) {
            ExportDialog dialog = new ExportDialog();
            dialog.setVisible(true);
        }

        /**
         * @see phex.gui.actions.FWAction#refreshActionState()
         */
        public void refreshActionState() {
        }
    }

    private class OpenFileAction extends FWAction {
        OpenFileAction() {
            super(Localizer.getString("LibraryTab_OpenFile"),
                    GUIRegistry.getInstance().getIconFactory().getIcon("Open"),
                    Localizer.getString("LibraryTab_TTTOpenFile"));
            refreshActionState();
        }

        public void actionPerformed(ActionEvent e) {
            try {
                int row = sharedFilesTable.getSelectedRow();
                row = sharedFilesTable.convertRowIndexToModel(row);
                if (row < 0) {
                    return;
                }
                Object obj = sharedFilesModel.getValueAt(row, SharedFilesTableModel.FILE_MODEL_INDEX);
                if (obj == null) {
                    return;
                }
                final File file;
                if (obj instanceof ShareFile) {
                    ShareFile sFile = (ShareFile) obj;
                    file = sFile.getSystemFile();

                } else if (obj instanceof File) {
                    file = (File) obj;
                } else {
                    return;
                }

                Runnable runnable = new Runnable() {
                    public void run() {
                        try {
                            SystemShellExecute.launchFile(file);
                        } catch (IOException exp) {// ignore and do nothing..
                        } catch (Throwable th) {
                            NLogger.error(NLoggerNames.USER_INTERFACE, th, th);
                        }
                    }
                };
                ThreadPool.getInstance().addJob(runnable, "SystenShellExecute");
            } catch (Throwable th) {
                NLogger.error(NLoggerNames.USER_INTERFACE, th, th);
            }
        }

        /**
         * @see phex.gui.actions.FWAction#refreshActionState()
         */
        public void refreshActionState() {
            int row = sharedFilesTable.getSelectedRow();
            row = sharedFilesTable.convertRowIndexToModel(row);
            if (row < 0) {
                setEnabled(false);
                return;
            }
            Object obj = sharedFilesModel.getValueAt(row, SharedFilesTableModel.FILE_MODEL_INDEX);
            if (obj == null) {
                setEnabled(false);
                return;
            }
            if (obj instanceof ShareFile || obj instanceof File) {
                setEnabled(true);
            } else {
                setEnabled(false);
            }
        }
    }

    private class ViewBitziTicketAction extends FWAction {
        public ViewBitziTicketAction() {
            super(Localizer.getString("ViewBitziTicket"),
                    GUIRegistry.getInstance().getIconFactory().getIcon("Bitzi"),
                    Localizer.getString("TTTViewBitziTicket"));
            refreshActionState();
        }

        public void actionPerformed(ActionEvent e) {
            int row = sharedFilesTable.getSelectedRow();
            row = sharedFilesTable.convertRowIndexToModel(row);
            if (row < 0) {
                return;
            }

            Object obj = sharedFilesModel.getValueAt(row, SharedFilesTableModel.FILE_MODEL_INDEX);

            if (obj == null || !(obj instanceof ShareFile)) {
                return;
            }
            ShareFile sFile = (ShareFile) obj;
            URN urn = sFile.getURN();
            String url = URLUtil.buildBitziLookupURL(urn);
            try {
                BrowserLauncher.openURL(url);
            } catch (IOException exp) {
                NLogger.warn(NLoggerNames.USER_INTERFACE, exp);

                Object[] dialogOptions = new Object[] { Localizer.getString("Yes"), Localizer.getString("No") };

                int choice = JOptionPane.showOptionDialog(LibraryTab.this,
                        Localizer.getString("FailedToLaunchBrowserURLInClipboard"),
                        Localizer.getString("FailedToLaunchBrowser"), JOptionPane.YES_NO_OPTION,
                        JOptionPane.WARNING_MESSAGE, null, dialogOptions, Localizer.getString("Yes"));
                if (choice == 0) {
                    Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(url), null);
                }
            }
        }

        public void refreshActionState() {
            int row = sharedFilesTable.getSelectedRow();
            row = sharedFilesTable.convertRowIndexToModel(row);
            if (row < 0) {
                setEnabled(false);
                return;
            }
            Object obj = sharedFilesModel.getValueAt(row, SharedFilesTableModel.FILE_MODEL_INDEX);
            if (obj == null || !(obj instanceof ShareFile)) {
                setEnabled(false);
            } else {
                setEnabled(true);
            }
        }
    }

    private class FilterAction extends FWAction {
        public FilterAction() {
            super(Localizer.getString("LibraryTab_Filter"),
                    GUIRegistry.getInstance().getIconFactory().getIcon("Filter"),
                    Localizer.getString("LibraryTab_TTTFilter"));
        }

        public void actionPerformed(ActionEvent e) {
            FilterLibraryDialog dialog = new FilterLibraryDialog();
            dialog.setVisible(true);
        }

        /**
         * @see phex.gui.actions.FWAction#refreshActionState()
         */
        public void refreshActionState() {
        }
    }

    private class ExploreFolderAction extends FWAction {
        ExploreFolderAction() {
            super(Localizer.getString("LibraryTab_Explore"),
                    GUIRegistry.getInstance().getIconFactory().getIcon("Explore"),
                    Localizer.getString("LibraryTab_TTTExplore"));
        }

        public void actionPerformed(ActionEvent e) {
            TreePath selectionPath = mainTree.getSelectionPath();
            if (selectionPath == null) {
                return;
            }
            Object lastPathComponent = selectionPath.getLastPathComponent();
            if (!(lastPathComponent instanceof LibraryNode)) {
                return;

            }
            final File dir = ((LibraryNode) lastPathComponent).getSystemFile();
            if (dir == null) {
                return;
            }

            try {
                SystemShellExecute.exploreFolder(dir);
            } catch (IOException exp) {// ignore and do nothing..
            }
        }

        /**
         * @see phex.gui.actions.FWAction#refreshActionState()
         */
        public void refreshActionState() {
            TreePath selectionPath = mainTree.getSelectionPath();
            if (selectionPath == null) {
                setEnabled(false);
                return;
            }
            Object lastPathComponent = selectionPath.getLastPathComponent();
            if (!(lastPathComponent instanceof LibraryNode)) {
                setEnabled(false);
                return;
            }
            File file = ((LibraryNode) lastPathComponent).getSystemFile();
            if (file == null) {
                setEnabled(false);
                return;
            }
            setEnabled(true);
        }
    }

    /**
     * Starts a download.
     */
    private class AddShareFolderAction extends FWAction {
        AddShareFolderAction() {
            super(Localizer.getString("LibraryTab_Share"),
                    GUIRegistry.getInstance().getIconFactory().getIcon("ShareFolder"),
                    Localizer.getString("LibraryTab_TTTShare"));
        }

        public void actionPerformed(ActionEvent e) {
            try {
                File currentDirectory = null;
                TreePath selectionPath = mainTree.getSelectionPath();
                if (selectionPath != null) {
                    Object lastPathComponent = selectionPath.getLastPathComponent();
                    if (lastPathComponent instanceof LibraryNode) {
                        currentDirectory = ((LibraryNode) lastPathComponent).getSystemFile();
                    }
                }

                final File[] files = FileDialogHandler.openMultipleDirectoryChooser(LibraryTab.this,
                        Localizer.getString("LibraryTab_SelectDirectoryToShare"),
                        Localizer.getString("LibraryTab_Select"), Localizer.getChar("LibraryTab_SelectMnemonic"),
                        currentDirectory, Localizer.getString("LibraryTab_CopyrightWarnTitle"),
                        Localizer.getString("LibraryTab_CopyrightWarnMessage"));
                if (files == null) {
                    return;
                }
                Runnable runner = new Runnable() {
                    public void run() {
                        try {
                            for (int i = 0; i < files.length; i++) {
                                if (files[i] != null) {
                                    shareDirRecursive(files[i]);
                                }
                            }
                            GUIActionPerformer.rescanSharedFiles();
                        } catch (Throwable th) {
                            NLogger.error(NLoggerNames.USER_INTERFACE, th, th);
                        }
                    }
                };
                ThreadPool.getInstance().addJob(runner, "AddShareFolderAction");
            } catch (Throwable th) {
                NLogger.error(NLoggerNames.USER_INTERFACE, th, th);
            }
        }

        public void refreshActionState() {
        }

        private void shareDirRecursive(File file) {
            if (!file.isDirectory()) {
                return;
            }
            ServiceManager.sCfg.sharedDirectoriesSet.add(file.getAbsolutePath());
            File[] dirs = file.listFiles(new DirectoryOnlyFileFilter());
            for (int i = 0; i < dirs.length; i++) {
                shareDirRecursive(dirs[i]);
            }
        }
    }

    private class RemoveShareFolderAction extends FWAction {
        RemoveShareFolderAction() {
            super(Localizer.getString("LibraryTab_StopShare"),
                    GUIRegistry.getInstance().getIconFactory().getIcon("ShareFolderClear"),
                    Localizer.getString("LibraryTab_TTTStopShare"));
            refreshActionState();
        }

        public void actionPerformed(ActionEvent e) {
            TreePath selectionPath = mainTree.getSelectionPath();
            if (selectionPath == null) {
                return;
            }
            Object lastPathComponent = selectionPath.getLastPathComponent();
            if (!(lastPathComponent instanceof LibraryNode)) {
                return;

            }
            final File file = ((LibraryNode) lastPathComponent).getSystemFile();
            if (file == null) {
                return;
            }
            Runnable runner = new Runnable() {
                public void run() {
                    stopShareDirRecursive(file);
                    GUIActionPerformer.rescanSharedFiles();
                }
            };
            ThreadPool.getInstance().addJob(runner, "RemoveShareFolderAction");
            refreshActionState();
        }

        private void stopShareDirRecursive(File file) {
            if (!file.isDirectory()) {
                return;
            }
            ServiceManager.sCfg.sharedDirectoriesSet.remove(file.getAbsolutePath());
            SharedFilesService sharedFilesService = ShareManager.getInstance().getSharedFilesService();
            SharedDirectory directory = sharedFilesService.getSharedDirectory(file);
            if (directory == null) {
                // in case there is no shared directory here..
                // we can assume there is no shared subdirectory available.
                return;
            }

            File[] dirs = file.listFiles(new DirectoryOnlyFileFilter());
            for (int i = 0; i < dirs.length; i++) {
                stopShareDirRecursive(dirs[i]);
            }
        }

        public void refreshActionState() {
            TreePath selectionPath = mainTree.getSelectionPath();
            if (selectionPath == null) {
                setEnabled(false);
                return;
            }
            Object lastPathComponent = selectionPath.getLastPathComponent();
            if (!(lastPathComponent instanceof LibraryNode)) {
                setEnabled(false);
                return;
            }
            File file = ((LibraryNode) lastPathComponent).getSystemFile();
            if (file == null) {
                setEnabled(false);
                return;
            }
            setEnabled(true);
        }
    }

    //////////////////////////////////////////////////////////////////////////
    /// Listeners
    //////////////////////////////////////////////////////////////////////////

    private class SelectionHandler implements ListSelectionListener, TreeSelectionListener {
        public void valueChanged(ListSelectionEvent e) {
            if (!e.getValueIsAdjusting()) {
                refreshTabActions();
            }
        }

        public void valueChanged(TreeSelectionEvent e) {
            final Object treeRoot = sharingTreeModel.getRoot();
            final Object lastPathComponent = e.getPath().getLastPathComponent();
            if (lastPathComponent == treeRoot) {
                EventQueue.invokeLater(new Runnable() {
                    public void run() {
                        mainTree.setSelectionPath(
                                new TreePath(new Object[] { treeRoot, sharingTreeModel.getChild(treeRoot, 0) }));
                    }
                });
                return;
            }

            // run in separate thread.. not event thread to make sure tree selection
            // changes immediately while table needs a little more to update.
            ThreadPool.getInstance().addJob(new Runnable() {
                public void run() {
                    if (lastPathComponent instanceof LibraryNode) {
                        // then fill data..
                        sharedFilesModel.setDisplayDirectory(((LibraryNode) lastPathComponent).getSystemFile());
                    } else {
                        sharedFilesModel.setDisplayDirectory(null);
                    }
                }
            }, "LibraryTableUpdate");
            refreshTabActions();
        }
    }

    private class SharedFilesChangeHandler implements ShareChangeListener {
        /**
         * Called if a shared files changed.
         */
        public void sharedDirectoriesChanged() {
            updateLabel();
        }

        private void updateLabel() {
            SharedFilesService sharedFilesService = ShareManager.getInstance().getSharedFilesService();
            StringBuffer buffer = new StringBuffer();
            buffer.append('(');
            buffer.append(sharedFilesService.getFileCount());
            buffer.append(" / ");
            buffer.append(NumberFormatUtils
                    .formatSignificantByteSize(((long) sharedFilesService.getTotalFileSizeInKb()) * 1024L))
                    .append(')');

            sharedFilesLabel.setText(buffer.toString());
        }
    }

    private class MouseHandler extends MouseAdapter implements MouseListener {
        public void mouseClicked(MouseEvent e) {
        }

        public void mouseReleased(MouseEvent e) {
            if (e.isPopupTrigger()) {
                popupMenu((Component) e.getSource(), e.getX(), e.getY());
            }
        }

        public void mousePressed(MouseEvent e) {
            if (e.isPopupTrigger()) {
                popupMenu((Component) e.getSource(), e.getX(), e.getY());
            }
        }

        private void popupMenu(Component source, int x, int y) {
            if (source == mainTree) {
                refreshTabActions();
                fileTreePopup.show(source, x, y);
            } else if (source == sharedFilesTable) {
                refreshTabActions();
                fileTablePopup.show(source, x, y);
            }
        }
    }
}