package com.limegroup.gnutella.gui.library; import java.awt.BorderLayout; import java.awt.CardLayout; import java.awt.Color; import java.awt.Component; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionListener; import java.io.File; import java.io.IOException; import java.util.Vector; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.SwingUtilities; import com.limegroup.gnutella.FileManagerEvent; import com.limegroup.gnutella.RouterService; import com.limegroup.gnutella.gui.ButtonRow; import com.limegroup.gnutella.gui.FileChooserHandler; import com.limegroup.gnutella.gui.GUIConstants; import com.limegroup.gnutella.gui.GUIMediator; import com.limegroup.gnutella.gui.options.ConfigureOptionsAction; import com.limegroup.gnutella.gui.themes.ThemeFileHandler; import com.limegroup.gnutella.gui.themes.ThemeMediator; import com.limegroup.gnutella.gui.themes.ThemeObserver; import com.limegroup.gnutella.gui.util.DividerLocationSettingUpdater; import com.limegroup.gnutella.settings.UISettings; import com.limegroup.gnutella.util.CommonUtils; /** * This class functions as an initializer for all of the elements * of the library and as a mediator between library objects. */ //2345678|012345678|012345678|012345678|012345678|012345678|012345678|012345678| public final class LibraryMediator implements ThemeObserver { /** * The primary panel that contains all of the library elements. */ private static final JPanel MAIN_PANEL = new JPanel(new GridBagLayout()); private static final CardLayout viewLayout = new CardLayout(); private static final JPanel viewPanel = new JPanel(viewLayout); /** * Constant handle to the <tt>LibraryTree</tt> library controller. */ private static final LibraryTree LIBRARY_TREE = LibraryTree.instance(); static { LIBRARY_TREE.setBorder(BorderFactory.createEmptyBorder(2,0,0,0)); } private static final JScrollPane TREE_SCROLL_PANE = new JScrollPane(LIBRARY_TREE); /** * Constant handle to the <tt>LibraryTable</tt> that displays the files * in a given directory. */ private static final LibraryTableMediator LIBRARY_TABLE = LibraryTableMediator.instance(); private static final String TABLE_KEY = "LIBRARY_TABLE"; private static final String SHARED_KEY = "SHARED"; /** * Constant handle to the file update handler. */ private final HandleFileUpdate FILE_UPDATER = new HandleFileUpdate(); /** Panel for the Shared Files node. */ private static JPanel jpShared = null; /////////////////////////////////////////////////////////////////////////// // Singleton Pattern /////////////////////////////////////////////////////////////////////////// /** * Singleton instance of this class. */ private static final LibraryMediator INSTANCE = new LibraryMediator(); /** * @return the <tt>LibraryMediator</tt> instance */ public static LibraryMediator instance() { return INSTANCE; } /** * Constructs a new <tt>LibraryMediator</tt> instance to manage calls * between library components. */ private LibraryMediator() { GUIMediator.setSplashScreenString( GUIMediator.getStringResource("SPLASH_STATUS_LIBRARY_WINDOW")); ThemeMediator.addThemeObserver(this); addView(LIBRARY_TABLE.getScrolledTablePane(), TABLE_KEY); // Create split pane JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, TREE_SCROLL_PANE, viewPanel); splitPane.setOneTouchExpandable(true); new DividerLocationSettingUpdater(splitPane, UISettings.UI_LIBRARY_TREE_DIVIDER_LOCATION); // Create refresh and explore buttons String[] refreshKey = { "LIBRARY_REFRESH_BUTTON_LABEL" }; String[] refreshTip = { "LIBRARY_REFRESH_BUTTON_TIP" }; ActionListener[] refreshListener = { new RefreshListener() }; String[] refreshName = { "LIBRARY_REFRESH"}; ButtonRow refreshButton = new ButtonRow(refreshKey, refreshTip, refreshListener, refreshName); String[] exploreKey = { "LIBRARY_EXPLORE_BUTTON_LABEL" }; String[] exploreTip = { "LIBRARY_EXPLORE_BUTTON_TIP" }; ActionListener[] exploreListener = { new ExploreListener() }; String[] exploreName = { "LIBRARY_EXPLORE" }; ButtonRow exploreButton = new ButtonRow(exploreKey, exploreTip, exploreListener, exploreName); // Add refresh button and, if Win or Mac, explore button JPanel buttonPanel = new JPanel(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); buttonPanel.add(refreshButton, gbc); gbc.gridx = GridBagConstraints.RELATIVE; if(CommonUtils.isWindows() || CommonUtils.isMacOSX()) buttonPanel.add(exploreButton, gbc); // Add table's buttons gbc.weightx = 1; gbc.anchor = GridBagConstraints.CENTER; gbc.fill = GridBagConstraints.HORIZONTAL; buttonPanel.add(LIBRARY_TABLE.getButtonRow(), gbc); // Layout main panel gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 0; gbc.weightx = 1; gbc.weighty = 1; gbc.fill = GridBagConstraints.BOTH; gbc.insets = new Insets(GUIConstants.SEPARATOR, GUIConstants.SEPARATOR, GUIConstants.SEPARATOR, GUIConstants.SEPARATOR); MAIN_PANEL.add(splitPane, gbc); gbc.gridy = 1; gbc.weighty = 0; gbc.fill = GridBagConstraints.HORIZONTAL; gbc.insets = new Insets(0, GUIConstants.SEPARATOR, GUIConstants.SEPARATOR, GUIConstants.SEPARATOR); MAIN_PANEL.add(buttonPanel, gbc); updateTheme(); // Set the initial selection in the LibraryTree LIBRARY_TREE.setInitialSelection(); } // inherit doc comment public void updateTheme() { LIBRARY_TREE.updateTheme(); Color tableColor = ThemeFileHandler.TABLE_BACKGROUND_COLOR.getValue(); TREE_SCROLL_PANE.getViewport().setBackground(tableColor); } /** * Returns the <tt>JComponent</tt> that contains all of the elements of * the library. * * @return the <tt>JComponent</tt> that contains all of the elements of * the library. */ public JComponent getComponent() { return MAIN_PANEL; } /** * Tells the library to launch the application associated with the * selected row in the library. */ public void launchLibraryFile() { LIBRARY_TABLE.launch(); } /** * Deletes the currently selected rows in the table. */ public void deleteLibraryFile() { LIBRARY_TABLE.removeSelection(); } /** * Removes the gui elements of the library tree and table. */ public void clearLibrary() { LIBRARY_TREE.clear(); //LIBRARY_TABLE.clearTable(); } /** * Quickly refreshes the library. * * This is only done if a saved or incomplete folder is selected, * incase an incomplete file was deleted or a new file (not shared) * was added to a save directory. */ public void quickRefresh() { DirectoryHolder dh = LIBRARY_TREE.getSelectedDirectoryHolder(); if(dh instanceof SavedFilesDirectoryHolder || dh instanceof IncompleteDirectoryHolder) updateTableFiles(dh); } /** * Launches explorer on PC in selected Shared directory */ public static void launchExplorer() { File exploreDir = LIBRARY_TREE.getSelectedDirectory(); if (exploreDir == null) return; String explorePath = exploreDir.getPath(); try { explorePath = exploreDir.getCanonicalPath(); } catch(IOException ioe) { } try { String cmdStr = ""; if (CommonUtils.isWindows()) cmdStr = "explorer"; else if (CommonUtils.isMacOSX()) cmdStr = "open"; Runtime.getRuntime().exec(new String[] { cmdStr, explorePath }); } catch(SecurityException se) { } catch(IOException ieo) { } } /** * Handles events created by the FileManager. Passes these events on to * the LibraryTableMediator or LibraryTree as necessary. */ public void handleFileManagerEvent(final FileManagerEvent evt) { LIBRARY_TREE.handleFileManagerEvent(evt); LIBRARY_TABLE.handleFileManagerEvent(evt, LIBRARY_TREE.getSelectedDirectoryHolder()); } /** * Displays a file chooser for selecting a new folder to share and * adds that new folder to the settings and FileManager. */ public void addSharedLibraryFolder() { File dir = FileChooserHandler.getInputDirectory(); if (dir == null) return; addSharedLibraryFolder(dir); } public void addSharedLibraryFolder(final File dir) { if(dir == null || !dir.isDirectory() || !dir.canRead()) { GUIMediator.showError("ERROR_INVALID_SHARED_DIRECTORY"); return; } GUIMediator.instance().schedule(new Runnable() { public void run() { RouterService.getFileManager().addSharedFolder(dir); } }); } /** * Update the this file's statistic */ public void updateSharedFile(final File file) { // if the library table is visible, and // if the selected directory is null // or if we the file exists in a directory // other than the one we selected, then there // is no need to update. // the user will see the newest stats when he/she // selects the directory. DirectoryHolder dh = LIBRARY_TREE.getSelectedDirectoryHolder(); if(LIBRARY_TABLE.getTable().isShowing() && dh != null && dh.accept(file)) { // pass the update off to the file updater // this way, only one Runnable is ever created, // instead of allocating a new one every single time // a query is hit. // Very useful for large libraries and generic searches (ala: mp3) FILE_UPDATER.addFileUpdate(file); } } public void setAnnotateEnabled(boolean enabled) { LIBRARY_TABLE.setAnnotateEnabled(enabled); } /** * Removes the selected folder from the shared folder group.. */ public void unshareLibraryFolder() { LIBRARY_TREE.unshareLibraryFolder(); } /** * Adds a file to the playlist. */ void addFileToPlayList(File toAdd) { GUIMediator.getPlayList().addFileToPlaylist(toAdd); } /** * Obtains the shared files for the given directory and updates the * table accordingly. * * @param selectedDir the currently selected directory in * the library */ static void updateTableFiles(DirectoryHolder dirHolder) { LIBRARY_TABLE.updateTableFiles(dirHolder); showView(TABLE_KEY); } /** Returns true if this is showing the special incomplete directory, * false if showing normal files. */ public static boolean incompleteDirectoryIsSelected() { return LIBRARY_TREE.incompleteDirectoryIsSelected(); } /** * Class to handle updates to shared file stats * without creating tons of runnables. * Idea taken from HandleQueryString in VisualConnectionCallback */ private static final class HandleFileUpdate implements Runnable { private Vector list; private boolean active; public HandleFileUpdate( ) { list = new Vector(); active = false; } public void addFileUpdate(File f) { list.addElement(f); if(active == false) { active = true; SwingUtilities.invokeLater(this); } } public void run() { try { File f; while (list.size() > 0) { f = (File) list.firstElement(); list.removeElementAt(0); LIBRARY_TABLE.update(f); } } catch (IndexOutOfBoundsException e) { //this really should never happen, but //who really cares if we're not sharing it? } catch (Exception e) { //no other errors could happen, so if one does, something's wrong GUIMediator.showInternalError(e); } active = false; } } /** * Shows the Shared Files view in response to selection of the Shared Files * in the LibraryTree. */ public static void showSharedFiles() { if (jpShared == null) { jpShared = new JPanel(new BorderLayout()); JPanel jpInternal = new JPanel(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); gbc.insets = new Insets(0, 0, ButtonRow.BUTTON_SEP, 0); jpInternal.add(new JLabel(GUIMediator.getStringResource("LIBRARY_SHARED_FILES_CONFIGURE_EXPLAIN")), gbc); gbc.gridy = 1; jpInternal.add(new JButton(new ConfigureOptionsAction( "OPTIONS_SHARED_MAIN_TITLE", "LIBRARY_SHARED_FILES_CONFIGURE", "LIBRARY_SHARED_FILES_CONFIGURE_EXPLAIN")), gbc); jpShared.add(jpInternal, BorderLayout.CENTER); jpShared.setBorder(BorderFactory.createEtchedBorder()); addView(jpShared, SHARED_KEY); } showView(SHARED_KEY); } public static void showView(String key) { viewLayout.show(viewPanel, key); } public static void addView(Component c, String key) { viewPanel.add(c, key); } /** * Sets the selected directory in the LibraryTree. * * @return true if the directory exists in the tree and could be selected */ public static boolean setSelectedDirectory(File dir) { return LIBRARY_TREE.setSelectedDirectory(dir); } /** * Updates the Library GUI based on whether the player is enabled. */ public void setPlayerEnabled(boolean value) { LIBRARY_TABLE.setPlayerEnabled(value); LIBRARY_TREE.setPlayerEnabled(value); } }