/**
 * Copyright (c) 2011, Martin Foster <martin@ethereal-realms.org>
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met:
 * 
 *     * Redistributions of source code must retain the above copyright notice, 
 *       this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright 
 *       notice, this list of conditions and the following disclaimer in the 
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Ethereal-Realms.org nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.lyra.Interface;

import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Point;
import java.awt.SystemTray;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;

import javax.swing.Box;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JFrame;
import javax.swing.JSplitPane;
import javax.swing.BoxLayout;
import javax.swing.JMenuBar;
import javax.swing.JTree;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.ToolTipManager;

import java.text.NumberFormat;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.TimeUnit;

import javax.swing.JScrollPane;
import javax.swing.JButton;
import javax.swing.border.SoftBevelBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableColumnModelEvent;
import javax.swing.event.TableColumnModelListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import javax.swing.JLabel;
import javax.swing.JToolBar;
import javax.swing.JTable;

import org.lyra.Ledger;
import org.lyra.Lyra;
import org.lyra.Database.Comptroller;
import org.lyra.Host.Options;
import org.lyra.Host.Pendulum;
import org.lyra.Host.Playback;
import org.lyra.Host.Playlist;
import org.lyra.Host.Status;
import org.lyra.Host.Terminus;
import org.lyra.Host.UpdateEvent;
import org.lyra.Host.UpdateListener;
import org.lyra.Host.Updater;
import org.lyra.Interface.PlaylistSaveDialog.PlaylistSave;
import org.lyra.LastDotFm.Demarcation;
import org.lyra.Listener.WindowSizeListener;
import org.lyra.Widgets.CustomColorRenderer;
import org.lyra.Widgets.DateTableCellRenderer;
import org.lyra.Widgets.EnhancedListSelectionModel;
import org.lyra.Widgets.LyraMutableTreeNode;
import org.lyra.Widgets.OrganiserTableModel;
import org.lyra.Widgets.PositionCellRenderer;
import org.lyra.Widgets.RatingCellEditor;
import org.lyra.Widgets.RatingCellRenderer;
import org.lyra.Widgets.ResizeIcon;

/**
 * The main display centre for the interface.   Permits a user to narrow down
 * songs using genre, album and artists.   Also a primary interface the playlist
 * and player controls, enabling the user to populate and manage playlist.
 * <p>
 * @author Martin Foster
 * @version 1.0.4, %Revision, 147%, %LastChangedDate, 06/11/11 4:57PM%
 */
@SuppressWarnings("unused")
public class PlayerOrganiserFrame extends JFrame {

	// A serial shame
	private static final long serialVersionUID = 1L;
	
	/*
	 * INTERFACE ELEMENTS
	 */
	private JPanel jContentPane = null;
	private PlayerOrganiserMenu menuControl = null;
	private JSplitPane splitGlobal = null;
	private JPanel panelControl = null;
	
	// Search tool bar
	private JToolBar barSearch = null;
	
	
	// Library and Filtering Container
	private JPanel panelLibrary = null;
	private JButton headerLibrary = null;
	private JComponent splitLibrary = null;
	
	// Song Display
	private JPanel panelSongs = null;
	private JScrollPane scrollSongs = null;
	private JTable tableSongs = null;
	
	// Global Song Filtering
	private JPanel panelFilter = null;
	
	// Artist Filtering
	private JPanel panelArtist = null;
	private JScrollPane scrollArtist = null;
	private JTable tableArtist = null;
	
	// Album Name Filtering
	private JPanel panelAlbum = null;
	private JScrollPane scrollAlbum = null;
	private JTable tableAlbum = null;
	
	// Genre Filtering
	private JPanel panelGenre = null;
	private JScrollPane scrollGenre = null;
	private JTable tableGenre = null;

	
	// Control group
	private JPanel panelPlayer = null;
	
	// Window Control
	private JPanel panelWindow = null;
	private JLabel iconResize = null;	
	
	
	// Playlist and Current Song Listing
	private JComponent splitLists = null;
	
	// Playlist Management
	private JPanel panelPlaylist = null;
	private JButton headerPlaylists = null;
	private JScrollPane scrollPlaylist = null;
	private JTree treePlaylist = null;
	private JPanel panelPlaylistControl = null;
	
	// Current Song Management
	private JPanel panelCurrent = null;
	private JButton headerCurrent = null;
	private JScrollPane scrollCurrent = null;	
	private JTable tableCurrent = null;
	
	// Status Line
	private JPanel panelStatus   = null;
	private JLabel labelSongs    = null;
	private JLabel labelRuntime  = null;
	private JLabel labelSelect   = null;
	private JLabel statusSongs   = null;
	private JLabel statusRuntime = null;
	private JLabel statusSelect  = null;
	
	// Current Playlist Control
	private JPanel panelCurrentControl = null;
	private JButton buttonShuffle = null;
	private JButton buttonSave = null;
	private JButton buttonRemove = null;
	private JButton buttonDelete = null;
	private JButton buttonClear = null;
	
	/*
	 * PACKAGE INSTANCES
	 */
	private Ledger Logger       = null;
	private Comptroller Manager = null;
	private Updater Watcher     = null;
	private Status Info         = null;
	private Playlist Lists		= null;
	private Playback Control	= null;
	private Options Modes       = null;
	
	private Map<String,String> Lang;
	private Map<String,String> Global;
	
	
	/*
	 * CLASS INSTANCES
	 */
	private Interact Interface;
	
	// General Renderer
	private DateTableCellRenderer TimeElapsedRenderer;
	private CustomColorRenderer RowRenderer;
	private RatingCellRenderer RatingRenderer;
	
	// Current playlistSpecific
	private PositionCellRenderer CurrentPositionRenderer;
	private CustomColorRenderer CurrentRowRenderer;
	private DateTableCellRenderer CurrentTimeElapsedRenderer;
	
	// Player component
	private PlayerPanel PlayerDisplay;
	
	// Storage elements
	private Map<String,String> Status;
	private Set<String> Enabled;
	
	// Reference to self
	private Frame Source        = this;

	// Blind updates
	private boolean BlindUpdate = false;
	
	
	/*
	 * CONSTANTS
	 */
	private static final String RUNTIME_DEF = "00:00";
	private static final String SELECT_DEF = "0";
	private static final String SONGS_DEF = "0";
	
	// Column numbers
	private static final int SONG_RATING      = 7;
	private static final int SONG_TIME        = 3;
	private static final int CURRENT_POSITION = 0;
	private static final int CURRENT_TIME     = 4;
	
	
	/**
	 * This is the default constructor for visual editors.  Elements required
	 * for instantiation such as language elements will be set to defaults
	 * only.
	 */
	@SuppressWarnings("serial")
	public PlayerOrganiserFrame() {
		
		// Let the super class do its thing
		super();
		
		/*
		 * Under normal circumstances this section will be passed on from the
		 * database.   However Visual Editors do not have this ability to we
		 * create a HashMap that can be used either way.
		 */
		Lang = new HashMap<String,String>(){
			{
				// Titles and Sections
	        	put("Organizer.Window.Title","Lyra - Organiser");
	        	
	        	// Section Identifiers
	        	put("Organizer.Section.Playlist","Playlists");
	        	put("Organizer.Section.Current","What's Playing");
	        	put("Organizer.Section.Library","Collection");
	        	
	        	// Table Displays
	        	put("Organizer.Table.Album","Album");
	        	put("Organizer.Table.Artist","Artist");
	        	put("Organizer.Table.Genre","Genre");
	        	
	        	// Song Elements    	
	        	put("Organizer.Table.Song.Artist","Artist");
	        	put("Organizer.Table.Song.Album","Album");
	        	put("Organizer.Table.Song.Title","Title");
	        	put("Organizer.Table.Song.Genre","Genre");
	        	put("Organizer.Table.Song.Time","Time");
	        	put("Organizer.Table.Song.Track","Track");
	        	put("Organizer.Table.Song.Date","Date");
	        	put("Organizer.Table.Song.Rating","Rating");

	        	// Playlist Elements
	        	put("Organizer.Table.Playlist.Artist","Artist");
	        	put("Organizer.Table.Playlist.Album","Album");
	        	put("Organizer.Table.Playlist.Title","Title");
	        	put("Organizer.Table.Playlist.Genre","Genre");
	        	put("Organizer.Table.Playlist.Time","Time");
	        	put("Organizer.Table.Playlist.Track","Track");
	        	put("Organizer.Table.Playlist.Date","Date");
	        	put("Organizer.Table.Playlist.Pos","Pos");
	        	
	        	// Status Identifiers
	        	put("Organizer.Status.Select","Selected: ");
	        	put("Organizer.Status.Songs","Total: ");
	        	put("Organizer.Status.Runtime","Play Time: ");
	        	put("Organizer.Status.Time",":");
	        	put("Organizer.Status.Format","MHRS Hours, MMINS Minutes and MSECS Seconds");

	        	// Current Playlist Control
	        	put("Organizer.Control.Shuffle","Shuffle current playlist...");
	        	put("Organizer.Control.Remove","Remove selected entry...");
	        	put("Organizer.Control.Clear","Clear contents of current playlist...");
	        	put("Organizer.Control.ShiftUp","Shift selected song up...");
	        	put("Organizer.Control.ShiftDown","Shift selected song down...");
			}
		};
		
		// Establish baseline
		initFrame();
	}
	// End of Constructor Class

	
	/**
	 * Main constructor class for the application itself.  The only difference
	 * from the default constructor is a language element which will permit a
	 * base construction of the skeleton.
	 * <p>
	 * @param lang Language Map for language independence.
	 * @param global Global settings for the application.
	 */
	public PlayerOrganiserFrame(Map<String,String> lang, Map<String,String> global) {

		// Let the super class do its thing
		super();
		
		// Pass Up
		Lang   = lang;
		Global = global;

		
		// Create instances
		Interface = new Interact();
		
		// General Renderer
		TimeElapsedRenderer = new DateTableCellRenderer(Global, Lang.get("Organizer.Status.Time"));
		RowRenderer         = new CustomColorRenderer(Global);
		RatingRenderer      = new RatingCellRenderer(Global);

		// Current Playlist Renderer
		CurrentPositionRenderer    = new PositionCellRenderer(Global);
		CurrentRowRenderer         = new CustomColorRenderer(Global);
		CurrentTimeElapsedRenderer = new DateTableCellRenderer(Global, Lang.get("Organizer.Status.Time"));

		
		/*
		 * The default TooltipText elements tend to trigger too quickly and
		 * can get in the way of normal browsing and navigation.   So we delay
		 * the appearance and shorten its display.
		 */
		ToolTipManager ttm = ToolTipManager.sharedInstance();
		
		// Adjust based on constants
		ttm.setInitialDelay(
				Constants.getTipInitialDelay());
		ttm.setReshowDelay(
				Constants.getTipReshowDelay());
		ttm.setDismissDelay(
				Constants.getTipDismissDelay());

		
		// Establish baseline
		initFrame();
	}
	// End of Constructor Class
	
	
	/* (non-Javadoc)
	 * @see java.awt.Window#setVisible(boolean)
	 */
	public void setVisible(boolean state) {
		
		/*
		 * We force additional rules to try and force the window back in cases
		 * where multiple screens may confuse it.
		 */
		if (state) {
			
			// Use the current location
			super.setLocationByPlatform(false);
			
			// Force visibility
			super.setVisible(state);
			
			// Force focus
			super.toFront();
			
			// Force missed updates
			if (BlindUpdate) {
			
				// Log changes
				Logger.Info(
					"org.Lyra.Interface.PlayerOrganiserFrame", 
					"updateTime", 
					Lang.get("Organizer.Log.Skip.Time")
				  );				
				// Update current playlist
				Interface.updateCurrent();
				
				// Update the panel display
				PlayerDisplay.updateDisplay();
			}
		}
		
		/*
		 * No special processing is required for this component.  However it
		 * is necessary to still do the operation.
		 */
		else {
			
			// Hide window
			super.setVisible(state);
		}
	}
	// End of setVisible Method
	
	
	/**
	 * This method initialises this
	 */
	private void initFrame() {
		
		/*
		 * This part establishes the most general look and feel structure
		 * available.   Elements here are specified outside of user settings 
		 * stating structure and so forth.
		 */
		
		// Establish look and feel elements
		this.setMinimumSize(new Dimension(750, 550));
		this.setTitle(Lang.get("Organizer.Window.Title"));
				
		// Adjust Icon
		this.setIconImage(new ImageIcon(
				getClass().getResource(
						Lyra.IMG_SOURCE + Lyra.LYRA)).getImage());
		
		// Insert the Content
		this.setContentPane(getJContentPane());
		
		// Window Closing and Listener
		this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
		this.addWindowListener(Interface);
		
		
		/*
		 * In most cases we will recover user based elements after the data has
		 * been extracted from the host.  However it was decided that elements
		 * related to size and JSplitPane position are specified here.
		 */
		
		// State of the window excluding ICONIZED
		this.setState(
			Integer.parseInt(Global.get("State.Organizer.Window.State"))
		  );
		
		// Extract point
		Point pos = new Point(
			Integer.parseInt(Global.get("State.Organizer.Window.XCoord")),
			Integer.parseInt(Global.get("State.Organizer.Window.YCoord"))
		  );
		
		// Position window
		this.setLocation(pos);
		
		
		// Physical size of the window
		this.setSize(
			Integer.parseInt(Global.get("State.Organizer.Window.Width")),
			Integer.parseInt(Global.get("State.Organizer.Window.Height"))
		  );
		

		/*
		 * Now we can set the most basic elements of the user selection which
		 * is the separation of components done through the JSplitPane
		 * Instances.
		 */
		
		// Global separation between lists and library
		getSplitGlobal().setDividerLocation(
			Integer.parseInt(Global.get("State.Organizer.Split.Global"))
		  );
		
		// Separation for library and filter elements
		if (getSplitLibrary() instanceof JSplitPane)
			((JSplitPane)getSplitLibrary()).setDividerLocation(
				Integer.parseInt(Global.get("State.Organizer.Split.Library"))
			  );
		
		// Separation for playlists and currently playing
		if (getSplitLists() instanceof JSplitPane)
			((JSplitPane)getSplitLists()).setDividerLocation(
				Integer.parseInt(Global.get("State.Organizer.Split.Lists"))
			  );
	}
	// End of initFrame Method

	
	/**
	 * This method establishes the basic interface population and interaction
	 * capability.   This is what will make the song filtering and playlist
	 * management possible.
	 * <p>
	 * @param logger Ledger instances for debugging and information.
	 * @param manager Database access manager instance.
	 * @param watcher Updater instance to be notified when things change.
	 * @param clock Pendulum instance to provide updates to song minutes.
	 * @param info Host status instance.
	 * @param lists Playlist instance which controls play lists.
	 * @param control Playback control instance.
	 * @param modes Options and settings on the host.
	 */
	public void initUpdate(
			Ledger logger, 
			Comptroller manager, 
			Updater watcher, 
			Pendulum clock, 
			Status info, 
			Playlist lists, 
			Playback control,
			Options modes) {
		
		// Pass up
		this.Logger  = logger;
		this.Manager = manager;
		this.Watcher = watcher;
		this.Info    = info;
		this.Lists   = lists;
		this.Control = control;
		this.Modes   = modes;
		
		
		/*
		 * In most circumstances we enable/disable a component as soon as
		 * the instance is created.  However since interface components exist
		 * prior to host configuration we have to do it now.
		 * 
		 * We also only deal with current playlist items.   The playlist can
		 * save and remove local lists which is what we will be managed
		 * later.
		 */
		
		// Extract permissible commands
		Enabled = Info.getEnabledAsSet();
		
		// Clear Current List
		getButtonClear().setEnabled(Enabled.contains(
			Terminus.PROTOCOL.get("Current.Clear")));
		
		// Delete From Current List
		getButtonRemove().setEnabled(Enabled.contains(
			Terminus.PROTOCOL.get("Current.Delete")));
		
		// Shuffle the Current List
		getButtonShuffle().setEnabled(Enabled.contains(
			Terminus.PROTOCOL.get("Current.Shuffle")));
		
		
		/*
		 * We create a system tray icon that will appear on operating systems
		 * which support the feature.
		 */
		if ((SystemTray.isSupported())
		 && (Boolean.parseBoolean(Global.get("Setting.Interface.Tray")))) {
			
			try {
				
				// Extract Instance
				SystemTray tray = SystemTray.getSystemTray();

				// Create Icon Image
				ImageIcon ico = new ImageIcon(new ImageIcon(
						getClass().getResource(
								Lyra.IMG_SOURCE + Lyra.LYRA)).getImage());
			
				// Create Structure
				tray.add(
					LyraTrayIcon.getTrayIcon(
						ico.getImage(), 
						Info, 
						Control, 
						Watcher, 
						Lang, 
						this
					)
				  );
			} 
			
			// We could log, however there is no real loss
			catch (AWTException e) {}
		}
		
		/*
		 * The search bar is a fairly straight forward capability.  However we
		 * need to insert it at this point due to some elements requiring
		 * more information prior to use.
		 */
		getJContentPane().add(getBarSearch(), BorderLayout.NORTH);
		
		
		/*
		 * The menu structure due to its control capability requires a fair
		 * amount of modules to be in place prior to its inception.  As a 
		 * result we create the instance here and add it in.
		 */
		PlayerOrganiserMenu menu 
			= new PlayerOrganiserMenu(
					Logger, 
					Manager, 
					Watcher,
					clock,
					Info, 
					Control, 
					Lists, 
					Modes, 
					Lang, 
					Global,
					getBarSearch(),
					this
				);
		
		// Extract the menu bar and attach
		this.setJMenuBar(menu.getJMenuBar());

		
		/*
		 * Now that we have core elements in place the only aspect missing is
		 * the built-in player.   We will create an instance and then throw the
		 * player into the mix.
		 */
		
		// Create new instance
		PlayerDisplay = new PlayerPanel(
				Logger,
				Manager,
				Info, 
				Control, 
				Modes, 
				Watcher, 
				clock,
				Global,
				Lang,
				Source
			);
		
		// Remove Existing
		getPanelControl().remove(getPanelPlayer());
		
		// Insert Element
		getPanelControl().add(
			PlayerDisplay,
			BorderLayout.CENTER
		  );
		getPanelControl().revalidate();
		
		
		/*
		 * There is a certain amount of time lag which exists between the
		 * interface elements being created.   I found the menu and player
		 * panel being delayed more jarring when the rest of the interface
		 * was already up.  Hence why information shows up last.
		 */
		Interface.initTables();
	}
	// End of initUpdate Method
	
	
	/**
	 * This method initialises jContentPane.  Core elements added here are:
	 * <ul>
	 * <li>barFeatures - Quick access JToolBar</li>
	 * <li>splitGlobal - Encompassing JSplitPane containing playlist and songs</li>
	 * <li>panelControl - Player control capability</li>
	 * </ul>
	 * <p>
	 * @return javax.swing.JPanel
	 */
	private JPanel getJContentPane() {
		
		// Check for instance
		if (jContentPane == null) {
			
			// Generate new instance
			jContentPane = new JPanel();
			
			// Establish layout
			jContentPane.setLayout(new BorderLayout());
			
			// Add in elements
			jContentPane.add(getSplitGlobal(), BorderLayout.CENTER);
			jContentPane.add(getPanelControl(), BorderLayout.SOUTH);
		}
		
		// Return instance
		return jContentPane;
	}
	// End of getJContentPane Method

	
	/**
	 * This method initialises barSearch.  Contains the basic capability for
	 * enabling more advanced searches of the data set.
	 * <p>
	 * @return javax.swing.JToolBar	
	 */
	private JToolBar getBarSearch() {
		
		// Check for instance
		if (barSearch == null) {
			
			// Generate new instance
			barSearch = new PlayerSearchToolBar(Logger, Lang, Interface);
			
			// Adjust visibility
			barSearch.setVisible(
				Boolean.parseBoolean(Global.get("State.Organizer.Search.Display")));
		}
		
		// Return instance
		return barSearch;
	}
	// End of getBarSearch Method
	
	
	/**
	 * This method initialises splitLibrary. An instance of JSplitPane which
	 * provides a space for both <i>panelFiler</i> a song filter and
	 * <i>panelSongs</i> which outlines the resulting songs.	
	 * <p>
	 * @return javax.swing.JComponent	
	 */
	private JComponent getSplitLibrary() {
		
		// Check for instance
		if (splitLibrary == null) {
			
			/*
			 * If a user does not wish to view any of these elements then the
			 * entire panel will not be loaded. 
			 */
			if ((!Boolean.parseBoolean(Global.get("Setting.Interface.Artist")))
			 && (!Boolean.parseBoolean(Global.get("Setting.Interface.Album")))
			 && (!Boolean.parseBoolean(Global.get("Setting.Interface.Genre")))) {

				/*
				 * The Song component will always be loaded no matter the users
				 * choice.  Primarily due to the fact that it would be hard to
				 * view the library without it. 
				 * 
				 * As a result we return the Songs panel directly.
				 */
				splitLibrary = getPanelSongs();
			}
			
			/*
			 * The user still has specific options within.  As a result we show
			 * the available panels for use.
			 */
			else {
				
				// Generate instance
				splitLibrary = new JSplitPane();
				
				// Songs Library
				((JSplitPane)splitLibrary)
					.setRightComponent(getPanelSongs());
				
				// Filtering Panel
				((JSplitPane)splitLibrary)
					.setLeftComponent(getPanelFilter());
			}
		}
		
		// Return Instance
		return splitLibrary;
	}
	// End of getSplitLibrary Method

	
	/**
	 * This method initialises panelSongs.  Container panel which contains a
	 * <i>JScrollPane</i> and within a <i>JTable</i> containing table
	 * information.	
	 * <p>
	 * @return javax.swing.JPanel	
	 */
	private JPanel getPanelSongs() {
		
		// Check for instance
		if (panelSongs == null) {
			
			// Generate new instance
			panelSongs = new JPanel();
			
			// Establish layout
			panelSongs.setLayout(new BorderLayout());
			
			// Add in element
			panelSongs.add(getScrollSongs(), BorderLayout.CENTER);
		}
		
		// Return instance
		return panelSongs;
	}
	// End of getPanelSongs Method

	
	/**
	 * This method initialises panelFilter.  This panel contains three other
	 * <i>JPanel</i> instances which deal with filtering by:
	 * <ul>
	 * <li>getPanelArtist - Artist name selection</li>	
	 * <li>getPanelAlbum - Album name selection</li>
	 * <li>getPanelGenre - Genre based selection</li>
	 * </ul>	
	 * @return javax.swing.JPanel	
	 */
	private JPanel getPanelFilter() {
		
		// Check for instance
		if (panelFilter == null) {
			
			// Create new instance
			panelFilter = new JPanel();
			
			// Establish layout
			panelFilter.setLayout(new BoxLayout(getPanelFilter(), BoxLayout.Y_AXIS));
			
			
			/*
			 * For users who do not wish to see certain components related to
			 * filtering they can choose to not load that specific visual
			 * component. 
			 */
			
			// Artist Filtering
			if (Boolean.parseBoolean(Global.get("Setting.Interface.Artist")))
				panelFilter.add(getPanelArtist(), null);
			
			// Album Filtering
			if (Boolean.parseBoolean(Global.get("Setting.Interface.Album")))
				panelFilter.add(getPanelAlbum(), null);
			
			// Genre Filtering
			if (Boolean.parseBoolean(Global.get("Setting.Interface.Genre")))
				panelFilter.add(getPanelGenre(), null);
		}
		
		// Return instance
		return panelFilter;
	}
	// End of getPanelFilter Method

	
	/**
	 * This method initialises panelArtist.  Panel witch associated 
	 * <i>JScrollPane<i> and <i>JList</i> instances.	
	 * <p>
	 * @return javax.swing.JPanel	
	 */
	private JPanel getPanelArtist() {
		
		// Check for instance
		if (panelArtist == null) {
			
			// Create new instance
			panelArtist = new JPanel();
			
			// Establish layout
			panelArtist.setLayout(new BorderLayout());
			
			// Insert element
			panelArtist.add(getScrollArtist(), BorderLayout.CENTER);
		}
		
		// Return element
		return panelArtist;
	}
	// End of getPanelArtist Method
	
	
	/**
	 * This method initialises panelAlbum.  Panel with associated 
	 * <i>JScrollPane<i> and <i>JList</i> instances.	
	 * <p>
	 * @return javax.swing.JPanel	
	 */
	private JPanel getPanelAlbum() {
		
		// Check for instance
		if (panelAlbum == null) {
			
			// Generate new instance
			panelAlbum = new JPanel();
			
			// Establish layout
			panelAlbum.setLayout(new BorderLayout());
			
			// Insert element
			panelAlbum.add(getScrollAlbum(), BorderLayout.CENTER);
		}
		
		// Return instance
		return panelAlbum;
	}
	// End of getPanelAlbum Method

	
	/**
	 * This method initialises panelGenre. Panel with associated 
	 * <i>JScrollPane<i> and <i>JList</i> instances.	
	 * <p>
	 * @return javax.swing.JPanel	
	 */
	private JPanel getPanelGenre() {
		
		// Verify for existence
		if (panelGenre == null) {
			
			// Generate new instance
			panelGenre = new JPanel();
			
			// Establish layout
			panelGenre.setLayout(new BorderLayout());
			
			// Insert elements
			panelGenre.add(getScrollGenre(), BorderLayout.CENTER);
		}
		
		// Return instance
		return panelGenre;
	}
	// End of getPanelGenre Method

	
	/**
	 * This method initialises panelControl.  Panel which deals mostly with 
	 * importing in <i>panelPlayer</i> but will also bring in <i>panelWindow</i>
	 * in order to implement window resizing capabilities.	
	 * <p>
	 * @return javax.swing.JPanel	
	 */
	private JPanel getPanelControl() {
		
		// Verify for existence
		if (panelControl == null) {
			
			// Create new instance
			panelControl = new JPanel();
			
			// Establish layout
			panelControl.setLayout(new BorderLayout());
			
			// Insert elements
			panelControl.add(getPanelPlayer(), BorderLayout.CENTER);
			panelControl.add(getPanelWindow(), BorderLayout.EAST);
		}
		
		// Return instance
		return panelControl;
	}
	// End of getPanelControl Method


	/**
	 * This method initialises splitGlobal.  Main <i>JSplitPane</i> which 
	 * separates the play/current lists and the library filtering/display.	
	 * 	
	 * @return javax.swing.JSplitPane	
	 */
	private JSplitPane getSplitGlobal() {
		
		// Verify instance
		if (splitGlobal == null) {
			
			// Create new instance
			splitGlobal = new JSplitPane();
			
			// Insert Components
			splitGlobal.setLeftComponent(getSplitLists());
			splitGlobal.setRightComponent(getPanelLibrary());
		}
		
		// Return Instance
		return splitGlobal;
	}
	// End of getSplitGlobal Method

	
	/**
	 * This method initialises splitLists.  <i>JSplitPane</i> which exists to
	 * separate playlist selection and the currently playing song list.
	 * <p>
	 * @return javax.swing.JComponent	
	 */
	private JComponent getSplitLists() {
		
		// Check for instance
		if (splitLists == null) {
			
			/*
			 * In cases where users have made the choice to not see playlist
			 * management.  Only the current playlist will be listed.
			 */
			if (!Boolean.parseBoolean(Global.get("Setting.Interface.Playlist"))) {
				
				// Copy the element and use it exclusively
				splitLists = getPanelCurrent();
			}
			
			
			/*
			 * We will create a normal interface if the user wishes to see
			 * both elements. 
			 */
			else {
				
				// Generate new instance
				splitLists = new JSplitPane();
				
				// We require a vertical split pane
				((JSplitPane)splitLists)
					.setOrientation(JSplitPane.VERTICAL_SPLIT);
				
				// Playlist Selection
				((JSplitPane)splitLists)
					.setTopComponent(getPanelPlaylist());
				
				// Currently Playing Playlist
				((JSplitPane)splitLists)
					.setBottomComponent(getPanelCurrent());
			}
		}
		
		// Return Instance
		return splitLists;
	}
	// End of getSplitLists Method

	
	/**
	 * This method initialises panelPlaylist.   <i>JPanel</i> which deals 	
	 * with the display of play lists.	
	 * <p>
	 * @return javax.swing.JPanel	
	 */
	private JPanel getPanelPlaylist() {
		
		// Check for instance
		if (panelPlaylist == null) {
			
			// Create instance
			panelPlaylist = new JPanel();
			
			// Establish Layout
			panelPlaylist.setLayout(new BorderLayout());
			
			// Insert Elements
			panelPlaylist.add(getScrollPlaylist(), BorderLayout.CENTER);
			panelPlaylist.add(getHeaderPlaylists(), BorderLayout.NORTH);
			panelPlaylist.add(getControlPlaylist(), BorderLayout.SOUTH);
		}
		
		// Return Instance
		return panelPlaylist;
	}
	// End of getPanelPlaylist Method

	
	/**
	 * This method initialises panelPlaylistControl.  This panel is responsible
	 * for basic control over the playlist tree.	
	 * <p>
	 * @return javax.swing.JPanel	
	 */
	private JPanel getControlPlaylist() {
		
		// Check for Existence
		if (panelPlaylistControl == null) {
			
			// Create Instance
			panelPlaylistControl = new JPanel();
			panelPlaylistControl.setLayout(new BoxLayout(getControlPlaylist(), BoxLayout.X_AXIS));

			panelPlaylistControl.add(getButtonDelete(), null);
		}
		
		// Return Instance
		return panelPlaylistControl;
	}
	// End of getPanelPlaylistControl Method
	
	
	/**
	 * This method initialises panelCurrent. <i>JPanel</i> which deals 	
	 * with the display of currently playing songs.
	 * 	
	 * @return javax.swing.JPanel	
	 */
	private JPanel getPanelCurrent() {
		
		// Check for Instance
		if (panelCurrent == null) {
			
			// Create Instance
			panelCurrent = new JPanel();
			
			// Establish Layout
			panelCurrent.setLayout(new BorderLayout());
			
			// Insert Elements
			panelCurrent.add(getHeaderCurrent(), BorderLayout.NORTH);
			panelCurrent.add(getScrollCurrent(), BorderLayout.CENTER);
			panelCurrent.add(getControlCurrent(), BorderLayout.SOUTH);
		}
		
		// Return Instance
		return panelCurrent;
	}
	// End of getPanelCCurrent Method
	

	/**
	 * This method initialises scrollPlaylist.  <i>JScrollPane</i> method
	 * which generates the scroll pane and associated content.
	 * <p>	<p>
	 * @return javax.swing.JScrollPane	
	 */
	private JScrollPane getScrollPlaylist() {
		
		//	Check for instance
		if (scrollPlaylist == null) {
			
			// Generate instance and attach to content
			scrollPlaylist = new JScrollPane();
			scrollPlaylist.setViewportView(getTreePlaylist());
		}
		
		// Return instance
		return scrollPlaylist;
	}
	// End of getScrollPlayList Method

	
	/**
	 * This method initialises scrollCurrent. <i>JScrollPane</i> method
	 * which generates the scroll pane and associated content.	
	 * <p>
	 * @return javax.swing.JScrollPane	
	 */
	private JScrollPane getScrollCurrent() {
		
		// Check for Instance
		if (scrollCurrent == null) {
			
			// Create Instance and Associate Content
			scrollCurrent = new JScrollPane();
			scrollCurrent.setViewportView(getTableCurrent());
		}
		
		// Return Instance
		return scrollCurrent;
	}
	// End of getScrollCurrent Method

	
	/**
	 * This method initialises headerPlaylists.  Generates a title element for
	 * the Playlist selection section.	
	 * <p>
	 * @return javax.swing.JButton	
	 */
	private JButton getHeaderPlaylists() {
		
		// Check for instance
		if (headerPlaylists == null) {
			
			// Generate instance and associate text
			headerPlaylists = new JButton();
			headerPlaylists.setText(Lang.get("Organizer.Section.Playlist"));
		}
		
		// Return instance
		return headerPlaylists;
	}
	// End of getHeaderPlaylist
	

	/**
	 * This method initialises headerCurrent.  Generates a title element for
	 * the Playlist selection section.	
	 * <p>
	 * @return javax.swing.JButton	
	 */
	private JButton getHeaderCurrent() {
		
		// Check for Instance
		if (headerCurrent == null) {
			
			// Create instance and associate content
			headerCurrent = new JButton();
			headerCurrent.setText(Lang.get("Organizer.Section.Current"));
		}
		
		// Return Instance
		return headerCurrent;
	}
	// End of getHeaderCurrent Method
	

	/**
	 * This method initialises panelGroup.  Main container for the Library area
	 * in general, contains the header, content split and a status bar.	
	 * <p>
	 * @return javax.swing.JPanel	
	 */
	private JPanel getPanelLibrary() {
		
		// Check for Instance
		if (panelLibrary == null) {
			
			// Generate new Instance
			panelLibrary = new JPanel();
			
			// Establish Layout
			panelLibrary.setLayout(new BorderLayout());
			
			// Insert Elements
			panelLibrary.add(getHeaderLibrary(), BorderLayout.NORTH);
			panelLibrary.add(getSplitLibrary(), BorderLayout.CENTER);
			panelLibrary.add(getPanelStatus(), BorderLayout.SOUTH);
		}
		
		// Return Instance
		return panelLibrary;
	}
	// End of getPanelLibrary Method

	
	/**
	 * This method initialises headerGroup.  Generates a title element for
	 * the Playlist selection section.	
	 * <p>	
	 * @return javax.swing.JButton	
	 */
	private JButton getHeaderLibrary() {
		
		// Check for Instance
		if (headerLibrary == null) {
			
			// Create instance and assign content
			headerLibrary = new JButton();
			headerLibrary.setText(Lang.get("Organizer.Section.Library"));
		}
		
		// Return Instance
		return headerLibrary;
	}
	// End of getHeaderLibrary Method

	
	/**
	 * This method initialises panelStatus.  An instance of <i>JPanel</i> which
	 * contains certain pieces of information related to the information
	 * displayed.	
	 * <p>
	 * @return javax.swing.JPanel	
	 */
	private JPanel getPanelStatus() {
		
		// Check for Instance
		if (panelStatus == null) {

			// Create Instance
			panelStatus = new JPanel();
			
			// Establish Layout and Border
			panelStatus.setLayout(new BoxLayout(getPanelStatus(), BoxLayout.X_AXIS));
			panelStatus.setBorder(new SoftBevelBorder(SoftBevelBorder.LOWERED));
			
			// Insert Elements
			panelStatus.add(Box.createHorizontalGlue());
			panelStatus.add(getLabelSongs(), null);
			panelStatus.add(getStatusSongs(), null);
			
			panelStatus.add(Box.createHorizontalStrut(10));
			panelStatus.add(getLabelSelect(), null);
			panelStatus.add(getStatusSelect(), null);
			
			panelStatus.add(Box.createHorizontalStrut(10));
			panelStatus.add(getLabelRuntime(), null);
			panelStatus.add(getStatusRuntime(), null);
		}
		
		// Return Instance
		return panelStatus;
	}
	// End of getPanelStatus Method

	
	/**
	 * This method alters status elements within the <i>JPanel</i> bearing its
	 * name.
	 * <p>
	 * @param status Map of strings containing all information for status display.
	 */
	public void setPanelStatus(Map<String,String> status) {

			// Pass up the Map
			this.Status = status;
			
			/*
			 * Larger numbers will be format according to LOCALE information
			 * which is a great way of providing internalisation without
			 * massive complexity.
			 */
			
			// Create instance
			NumberFormat num = NumberFormat.getNumberInstance();
			
			// Format both
			setStatusSelect(num.format(Integer.parseInt(status.get("Selected"))));
			setStatusSongs(num.format(Integer.parseInt(status.get("Songs"))));
		
			
			/*
			 * The more complicated element for status display is the sum of
			 * combined runtime for all songs listed in the Songs display.  We
			 * have to figure out individual elements and then format the
			 * String instance to pass on.
			 */
			
			// String to pass on
			String pass;
			
			// Quick number crunching
			long total  = Long.parseLong(status.get("Runtime"));
			long ihrs   = TimeUnit.SECONDS.toHours(total);
			long imin   = TimeUnit.SECONDS.toMinutes((long)(total - (ihrs * Math.pow(60, 2))));
			long isec   = TimeUnit.SECONDS.toSeconds((long)(total - (ihrs * Math.pow(60, 2)) - (imin * 60)));
			
			
			// Format the hours section
			String shrs = num.format(ihrs);
			pass = Lang.get("Organizer.Status.Format").replaceAll("MHRS", shrs);
			
			
			// Format the minutes appending 0 if necessary
			String smin = (imin < 10) 
			  ? "0" + Long.toString(imin)  
			  : Long.toString(imin);
			pass = pass.replaceAll("MMINS", smin);
			
			
			// Format the seconds appending 0 if necessary
			String ssec = (isec < 10) 
			  ? "0" + Long.toString(isec)  
			  : Long.toString(isec);
			pass = pass.replaceAll("MSECS", ssec);
			
			
			// Permit the super class to continue with its processing
    		setStatusRuntime(pass);
	}
	// End of setPanelStatus Method
	

	/**
	 * This method initialises tableCurrent.  <i>JTable</i> containing the
	 * content structure of section.	
	 * <p>
	 * @return javax.swing.JTable	
	 */
	private JTable getTableCurrent() {
		
		// Check for instance
		if (tableCurrent == null) {
			
			// Create empty instance
			tableCurrent = new JTable();
			
			/*
			 *  Establish a few baseline elements.  These should carry forth
			 *  regardless of the actual model associated.
			 */

			// We never want column based selection
			tableCurrent.setColumnSelectionAllowed(false);
			tableCurrent.setRowSelectionAllowed(true);
			
			// Single Item Selection
			tableCurrent.setSelectionMode(
				ListSelectionModel.SINGLE_INTERVAL_SELECTION);
			
			// Break out of the JScrollPane
			tableCurrent.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
			
			
			// Identifier and mouse listener
			tableCurrent.setName("CURRENT");
			tableCurrent.addMouseListener(Interface);
		}
		
		// Return instance
		return tableCurrent;
	}
	// End of getTableCurrent Method
	

	/**
	 * This method alters tableCurrent's data model.  A <i>JTable</i> containing
	 * the content structure of section.	
	 * <p>
	 * @param contents Multi-dimensional array containing new content.
	 * @param cols Amount of columns in the group.
	 */
	public void setTableCurrent(Object[][] contents, int cols) {
		
		// Generate a new model
		TableModel repl = new OrganiserTableModel(contents, cols);
		
		// Replace the existing model
		getTableCurrent().setModel(repl);
		
		// Adjust rendering of columns
		getTableCurrent().getColumnModel()
		                 .getColumn(CURRENT_POSITION)
		                 .setCellRenderer(CurrentPositionRenderer);
		getTableCurrent().getColumnModel()
		                 .getColumn(CURRENT_TIME)
		                 .setCellRenderer(CurrentTimeElapsedRenderer);
		
		// Default Renderer
		getTableCurrent().setDefaultRenderer(
			getTableCurrent().getColumnClass(0), 
			CurrentRowRenderer
		  );
	}
	// End of setTableCurrent Method
	
	
	/**
	 * This method initialises tablePlaylist.  <i>JTable</i> containing the
	 * content structure of section.	
	 * <p>
	 * @return javax.swing.JTree		
	 */
	private JTree getTreePlaylist() {
		
		// Check for Instance
		if (treePlaylist == null) {
			
			/*
			 * There is a transition time between startup and shutdown in which
			 * a default and unwanted tree is shown.  So we create a temporary
			 * place holder below in order to make the transition seem more
			 * natural.
			 */
			
			// Root Node
			DefaultMutableTreeNode root = new DefaultMutableTreeNode();
			
			// Remote Lists
			DefaultMutableTreeNode remote = new DefaultMutableTreeNode(
				Lang.get("Organizer,Playlists.Remote")
			  );
			
			// Local Lists
			DefaultMutableTreeNode local = new DefaultMutableTreeNode(
				Lang.get("Organizer.Playlists.Local")
			  );
			
			// Smart Playlists
			DefaultMutableTreeNode smart = new DefaultMutableTreeNode(
				Lang.get("Organizer.Playlists.Smart")
			  );		
			
			// Add in all elements
			root.add(remote);
			root.add(local);
			root.add(smart);
			
			
			/*
			 * Now that we have created the place holder element.  We can now
			 * opt to create the actual Tree.
			 */
			
			// Create Empty Instance
			treePlaylist = new JTree(root);
			
			// Prevent the display of root
			treePlaylist.setRootVisible(false);
			
			// Single selections only
			treePlaylist.getSelectionModel().setSelectionMode(
				TreeSelectionModel.SINGLE_TREE_SELECTION
			  );			
			
			// Identifier and Listener
			treePlaylist.setName("PLAYLISTS");
			treePlaylist.addMouseListener(Interface);
		}
		
		// Return Instance
		return treePlaylist;
	}
	// End of getTreePlayList Method

	/**
	 * Replaces the existing tree with a newly generated variant.
	 * <p>
	 * @param node Node structure set to replace existing.
	 */
	public void setTreePlaylist(LyraMutableTreeNode node) {
		
		// Create new model instance
		DefaultTreeModel model = new DefaultTreeModel(node);
		
		// Replace Model
		getTreePlaylist().setModel(model);
	}
	// End of setTreePlaylist Method
	
	
	/**
	 * This method initialises tableArtist.  <i>JTable</i> containing the
	 * content structure of section.	
	 * <p>
	 * @return javax.swing.JTable	
	 */
	private JTable getTableArtist() {
		
		// Check for Instance
		if (tableArtist == null) {
			
			// Create Empty Instance
			tableArtist = new JTable();
			
			/*
			 *  Establish a few baseline elements.  These should carry forth
			 *  regardless of the actual model associated.
			 */
			
			// We never want column based selection
			tableArtist.setColumnSelectionAllowed(false);
			
			// Identifier
			tableArtist.setName("ARTIST");
			
			// Selection capability and listener
			EnhancedListSelectionModel selection = new EnhancedListSelectionModel(tableArtist); 
			selection.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
			selection.addListSelectionListener(Interface);
			
			// Double click and Key listener
			tableArtist.addMouseListener(Interface);
			tableArtist.addKeyListener(Interface);
		}
		
		// Return Instance
		return tableArtist;
	}
	// End of getTableArtist Method
	

	/**
	 * This method initialises scrollArtist. <i>JScrollPane</i> method
	 * which generates the scroll pane and associated content.	
	 * <p>	
	 * 	
	 * @return javax.swing.JScrollPane	
	 */
	private JScrollPane getScrollArtist() {
		
		// Check for Instance
		if (scrollArtist == null) {
			
			// Create Instance and Set Contents
			scrollArtist = new JScrollPane();
			scrollArtist.setViewportView(getTableArtist());
		}
		
		// Return Instance
		return scrollArtist;
	}
	// End of getScrollArist Method

	
	/**
	 * This method alters tableArtist's data model.  A <i>JTable</i> containing
	 * the content structure of section.	
	 * <p>
	 * @param contents Multi-dimensional array containing new content.
	 */
	public void setTableArtist(Object[][] contents) {
		
		/*
		 * For these simpler single row tables it is far simpler to actually
		 * define the column name directly.
		 */
		Object[] rows = {Lang.get("Organizer.Table.Artist")};

		// Generate a new model
		TableModel repl = new OrganiserTableModel(contents, rows);
		
		// Replace the existing model
		getTableArtist().setModel(repl);
		
		// Default Renderer
		getTableArtist().setDefaultRenderer(
			getTableArtist().getColumnClass(0), 
			RowRenderer
		  );
	}
	// End of setTableArtist Method
	
	
	/**
	 * This method initialises scrollAlbum. <i>JScrollPane</i> method
	 * which generates the scroll pane and associated content.	
	 * <p>	
	 * @return javax.swing.JScrollPane	
	 */
	private JScrollPane getScrollAlbum() {
		
		// Check for Instance
		if (scrollAlbum == null) {
			
			// Create Instance and Set Contents
			scrollAlbum = new JScrollPane();
			scrollAlbum.setViewportView(getTableAlbum());
		}
		
		// Return Instance
		return scrollAlbum;
	}
	// End of getScrollAlbum Instance

	
	/**
	 * This method initialises scrollGenre. <i>JScrollPane</i> method
	 * which generates the scroll pane and associated content.	
	 * <p>	
	 * @return javax.swing.JScrollPane	
	 */
	private JScrollPane getScrollGenre() {
		
		// Check for Instance
		if (scrollGenre == null) {
			
			// Create Instance and Set Contents
			scrollGenre = new JScrollPane();
			scrollGenre.setViewportView(getTableGenre());
		}
		
		// Return Instance
		return scrollGenre;
	}
	// End of getScrollGenre Method

	
	/**
	 * This method alters tableGenre's data model.  A <i>JTable</i> containing
	 * the content structure of section.	
	 * <p>
	 * @param contents Multi-dimensional array containing new content.
	 */
	public void setTableGenre(Object[][] contents) {
		
		/*
		 * For these simpler single row tables it is far simpler to actually
		 * define the column name directly.
		 */
		Object[] rows = {Lang.get("Organizer.Table.Genre")};

		// Generate a new model
		TableModel repl = new OrganiserTableModel(contents, rows);
		
		// Replace the existing model
		getTableGenre().setModel(repl);
		
		// Default Renderer
		getTableGenre().setDefaultRenderer(
			getTableGenre().getColumnClass(0), 
			RowRenderer
		  );
	}
	// End of setTableGenre Method
	
	
	/**
	 * This method initialises scrollSongs. <i>JScrollPane</i> method
	 * which generates the scroll pane and associated content.	
	 * <p>	
	 * @return javax.swing.JScrollPane	
	 */
	private JScrollPane getScrollSongs() {
		
		// Check for Instance
		if (scrollSongs == null) {
			
			// Create Instance and Set Content
			scrollSongs = new JScrollPane();
			scrollSongs.setViewportView(getTableSongs());
		}
		
		// Return Instance
		return scrollSongs;
	}
	// End of getScrollSongs Method
	

	/**
	 * This method initialises tableAlbum.  <i>JTable</i> containing the
	 * content structure of section.	
	 * <p>
	 * @return javax.swing.JTable	
	 */
	private JTable getTableAlbum() {
		
		// Check for Instance
		if (tableAlbum == null) {
			
			// Create Empty Instance
			tableAlbum = new JTable();
			
			/*
			 *  Establish a few baseline elements.  These should carry forth
			 *  regardless of the actual model associated.
			 */
			
			// We never want column based selection
			tableAlbum.setColumnSelectionAllowed(false);
						
			// Identifier and Listeners
			tableAlbum.setName("ALBUM");
			
			// Selection capability and listener
			EnhancedListSelectionModel selection = new EnhancedListSelectionModel(tableAlbum); 
			selection.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
			selection.addListSelectionListener(Interface);
			
			// Click and Key listener
			tableAlbum.addMouseListener(Interface);
			tableAlbum.addKeyListener(Interface);
		}
		
		// Return Instance
		return tableAlbum;
	}
	// End of getTableAlbum Method
	
	
	/**
	 * This method alters tableAlbum's data model.  A <i>JTable</i> containing
	 * the content structure of section.	
	 * <p>
	 * @param contents Multi-dimensional array containing new content.
	 */
	public void setTableAlbum(Object[][] contents) {
		
		/*
		 * For these simpler single row tables it is far simpler to actually
		 * define the column name directly.
		 */
		Object[] rows = {Lang.get("Organizer.Table.Album")};

		// Generate a new model
		TableModel repl = new OrganiserTableModel(contents, rows);
		
		// Replace the existing model
		getTableAlbum().setModel(repl);
		
		// Default Renderer
		getTableAlbum().setDefaultRenderer(
			getTableAlbum().getColumnClass(0), 
			RowRenderer
		  );
	}
	// End of setTableAlbum Method
	
	
	/**
	 * This method initialises tableGenre.  <i>JTable</i> containing the
	 * content structure of section.	
	 * <p>
	 * @return javax.swing.JTable	
	 */
	private JTable getTableGenre() {
		
		// Check for Instance
		if (tableGenre == null) {
			
			// Create Empty Instance
			tableGenre = new JTable();
			
			/*
			 *  Establish a few baseline elements.  These should carry forth
			 *  regardless of the actual model associated.
			 */

			// We never want column based selection
			tableGenre.setColumnSelectionAllowed(false);
			
			// Identifier and listener
			tableGenre.setName("GENRE");
			
			// Selection capability and listener
			EnhancedListSelectionModel selection = new EnhancedListSelectionModel(tableGenre); 
			selection.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
			selection.addListSelectionListener(Interface);
			
			
			// Double click and Key listener
			tableGenre.addMouseListener(Interface);
			tableGenre.addKeyListener(Interface);
		}
		
		// Return Instance
		return tableGenre;
	}
	// End of getTableGenre Method

	
	/**
	 * This method initialises tableSongs.  <i>JTable</i> containing the
	 * content structure of section.	
	 * <p>
	 * @return javax.swing.JTable	
	 */
	private JTable getTableSongs() {
		
		// Check for instance
		if (tableSongs == null) {
			
			// Create Instance
			tableSongs = new JTable();

			// Break out of the JScrollPane
			tableSongs.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
			
			// Identifier
			tableSongs.setName("SONGS");
			
			// Selection capability and listener
			EnhancedListSelectionModel selection = new EnhancedListSelectionModel(tableSongs); 
			selection.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
			selection.addListSelectionListener(Interface);
			
			// Double click listener
			tableSongs.addMouseListener(Interface);
		}
		
		// Return Instance
		return tableSongs;
	}
	// End of getTableSongs Method
	
	
	/**
	 * This method alters tableSongs's data model.  A <i>JTable</i> containing
	 * the content structure of section.	
	 * <p>
	 * @param contents Multi-dimensional array containing new content.
	 * @param cols Amount of columns in the group.
	 */
	public void setTableSongs(Object[][] contents, int cols) {

		// Generate a new model
		TableModel repl = new OrganiserTableModel(
				Manager, 
				contents, 
				cols, 
				SONG_RATING);
		
		// Replace the existing model
		getTableSongs().setModel(repl);
		
		// Adjust rendering of time elements
		getTableSongs().getColumnModel()
		               .getColumn(SONG_TIME)
		               .setCellRenderer(TimeElapsedRenderer);
		
		// Adjust renderer and editor of rating elements
		getTableSongs().getColumnModel()
		               .getColumn(SONG_RATING)
		               .setCellEditor(new RatingCellEditor());
		getTableSongs().getColumnModel()
		       		   .getColumn(SONG_RATING)
		       		   .setCellRenderer(RatingRenderer);              
		               
		// Default Renderer
		getTableSongs().setDefaultRenderer(
			getTableSongs().getColumnClass(0), 
			RowRenderer
		  );
	}
	// End of setTableSongs Method
	

	/**
	 * This method initialises panelPlayer.  <i>JTable</i> containing the
	 * content structure of section.	
	 * <p>
	 * @return javax.swing.JTable	
	 */
	private JPanel getPanelPlayer() {
		
		// Check for Instance
		if (panelPlayer == null) {
			
			// Create an empty instance
			panelPlayer = new JPanel();
		}
		
		// Return Instance
		return panelPlayer;
	}
	// End of getPanelPlayer Method
	

	/**
	 * This method initialises panelWindow	
	 * 	
	 * @return javax.swing.JPanel	
	 */
	private JPanel getPanelWindow() {
		
		// Check Instance
		if (panelWindow == null) {
			
			// Create Instance
			panelWindow = new JPanel();
			
			// Establish Layout
			panelWindow.setLayout(new BoxLayout(getPanelWindow(), BoxLayout.Y_AXIS));
			
			// Insert Elements
			panelWindow.add(Box.createVerticalGlue());
			panelWindow.add(getIconResize());
		}
		
		// Return Instance
		return panelWindow;
	}
	// End of getPanelWindow Method

	
	/**
	 * This method initialises labelSelect.  Status information amplification.	
	 * <p>
	 * @return javax.swing.JLabel	
	 */
	private JLabel getLabelSelect() {
		
		// Check for instance
		if (labelSelect == null) {
			
			// Generate Instance and Set Content
			labelSelect = new JLabel();
			labelSelect.setText(Lang.get("Organizer.Status.Select"));
		}
		
		// Return instance
		return labelSelect;
	}
	// End of getLabelSelect Method	
		

	/**
	 * This method initialises labelRuntime.  Status information amplification.	
	 * <p>
	 * @return javax.swing.JLabel	
	 */
	private JLabel getLabelRuntime() {
		
		// Check for instance
		if (labelRuntime == null) {
			
			// Generate Instance and Set Content
			labelRuntime = new JLabel();
			labelRuntime.setText(Lang.get("Organizer.Status.Runtime"));
		}
		
		// Return instance
		return labelRuntime;
	}
	// End of getLabelRuntime Method	

	
	/**
	 * This method initialises labelSongs.  Status information amplification.	
	 * <p>
	 * @return javax.swing.JLabel	
	 */
	private JLabel getLabelSongs() {
		
		// Check for instance
		if (labelSongs == null) {
			
			// Generate Instance and Set Content
			labelSongs = new JLabel();
			labelSongs.setText(Lang.get("Organizer.Status.Songs"));
		}
		
		// Return instance
		return labelSongs;
	}
	// End of getLabelSongs Method	

	
	/**
	 * This method initialises iconResize.
	 * <p>
	 * @return javax.swing.JLabel
	 */
	private JLabel getIconResize() {
		
		// Verify existence of element
		if (iconResize == null) {

			// Create and establish baseline
			iconResize = new JLabel(new ResizeIcon());
			iconResize.setOpaque(false);
			
			/*
			 * Making this icon do something of use is one of the core reasons
			 * why it was added to the display itself.  Here we create an 
			 * instance and add the listener, since it is not used anywhere else
			 * we don't worry out creating an instance earlier.
			 */
			WindowSizeListener sizer = new WindowSizeListener(this);
			
			// Add in listener elements
			iconResize.addMouseListener(sizer);
			iconResize.addMouseMotionListener(sizer);
		}
		
		// Return label
		return iconResize;
	}
	// End of getIconResize Method
	
	
	/**
	 * This method initialises statusSelect.  Status information amplification.	
	 * <p>
	 * @return javax.swing.JLabel	
	 */
	private JLabel getStatusSelect() {
		
		// Check for instance
		if (statusSelect == null) {
			
			// Generate Instance and Set Content
			statusSelect = new JLabel();
			statusSelect.setText(SELECT_DEF);
		}
		
		// Return instance
		return statusSelect;
	}
	// End of getStatusSelect Method	
		
	
	/**
	 * Sets the text value of the <i>statsSelect</i> <i>JLabel</i> instance.
	 * <p>
	 * @param text String containing new text to replace.
	 */
	public void setStatusSelect(String text) {
		
		// Straight forward setter
		getStatusSelect().setText(text);
	}
	// End of setStatusSelect Method
	

	/**
	 * This method initialises statusRuntime.  Status information amplification.	
	 * <p>
	 * @return javax.swing.JLabel
	 */
	private JLabel getStatusRuntime() {
		
		// Check for instance
		if (statusRuntime == null) {
			
			// Generate Instance and Set Content
			statusRuntime = new JLabel();
			statusRuntime.setText(RUNTIME_DEF);
		}
		
		// Return instance
		return statusRuntime;
	}
	// End of getStatusRuntime Method	

	
	/**
	 * Sets the text value of the <i>statsRuntime</i> <i>JLabel</i> instance.
	 * <p>
	 * @param text String containing new text to replace.
	 */
	public void setStatusRuntime(String text) {
		
		// Straight forward setter
		getStatusRuntime().setText(text);
	}
	// End of setStatusRuntime Method
	
	
	/**
	 * This method initialises statusSongs.  Status information amplification.	
	 * <p>
	 * @return javax.swing.JStatus	
	 */
	private JLabel getStatusSongs() {
		
		// Check for instance
		if (statusSongs == null) {
			
			// Generate Instance and Set Content
			statusSongs = new JLabel();
			statusSongs.setText(SONGS_DEF);
		}
		
		// Return instance
		return statusSongs;
	}
	// End of getStatusSongs Method	
	
	
	/**
	 * Sets the text value of the <i>statsSongs</i> <i>JLabel</i> instance.
	 * <p>
	 * @param text String containing new text to replace.
	 */
	public void setStatusSongs(String text) {
		
		// Straight forward setter
		getStatusSongs().setText(text);
	}
	// End of setStatusSongs Method
	
	
	/**
	 * This method initialises panelControl.  This panel is responsible for
	 * basic control over the current playlist.	
	 * <p>
	 * @return javax.swing.JPanel	
	 */
	private JPanel getControlCurrent() {
		
		// Verify Existence
		if (panelCurrentControl == null) {
			
			// Create New Instance and Layout
			panelCurrentControl = new JPanel();
			panelCurrentControl.setLayout(new BoxLayout(getControlCurrent(), BoxLayout.X_AXIS));

			// Create Shuffle Button
			panelCurrentControl.add(getButtonShuffle(), null);
			
			// Save Button
			panelCurrentControl.add(getButtonSave(), null);
			
			// Remove Buttons
			panelCurrentControl.add(getButtonRemove(), null);
			panelCurrentControl.add(getButtonClear(), null);
		}
		
		// Return Instance
		return panelCurrentControl;
	}
	// End of getPanelCurrentControl Method

	
	/**
	 * This method initialises buttonShuffle.	
	 * <p>
	 * @return javax.swing.JButton	
	 */
	private JButton getButtonShuffle() {
		
		// Verify Existence
		if (buttonShuffle == null) {
			
			// Create Instance
			buttonShuffle = new JButton();

			// Language and Icons
			buttonShuffle.setToolTipText(Lang.get("Organizer.Control.Shuffle"));
			
			buttonShuffle.setIcon(new ImageIcon(
					getClass().getResource(
							Lyra.getIconByMap("Control-Shuffle"))));

			// Identifier and Listener
			buttonShuffle.setName("CONTROL.SHUFFLE");
			buttonShuffle.addActionListener(Interface);
		}
		
		// Return Instance
		return buttonShuffle;
	}
	// End of getButtonShuffle Method

	
	/**
	 * This method initialises buttonRemove.	
	 * <p>
	 * @return javax.swing.JButton	
	 */
	private JButton getButtonRemove() {
		
		// Verify Existence
		if (buttonRemove == null) {
			
			// Create Instance
			buttonRemove = new JButton();
			
			// Language and Icons
			buttonRemove.setIcon(new ImageIcon(
					getClass().getResource(
							Lyra.getIconByMap("Control-Remove"))));
			buttonRemove.setToolTipText(Lang.get("Organizer.Control.Remove"));

			// Identifier and Action
			buttonRemove.setName("CONTROL.REMOVE");
			buttonRemove.addActionListener(Interface);
		}
		
		// Return Instance
		return buttonRemove;
	}
	// End of getButtonRemove Method

	
	/**
	 * This method initialises buttonDelete.	
	 * <p>
	 * @return javax.swing.JButton	
	 */
	private JButton getButtonDelete() {
		
		// Verify Existence
		if (buttonDelete == null) {
			
			// Create Instance
			buttonDelete = new JButton();
			
			// Language and Icons
			buttonDelete.setIcon(new ImageIcon(
					getClass().getResource(
							Lyra.getIconByMap("Control-Delete"))));
			buttonDelete.setToolTipText(Lang.get("Organizer.Control.Delete"));

			// Identifier and Action
			buttonDelete.setName("CONTROL.DELETE");
			buttonDelete.addActionListener(Interface);
		}
		
		// Return Instance
		return buttonDelete;
	}
	// End of getButtonDelete Method
	
	
	/**
	 * This method initialises buttonClear.
	 * <p>
	 * @return javax.swing.JButton	
	 */
	private JButton getButtonClear() {
		
		// Check for Instance
		if (buttonClear == null) {
			
			// Create Instance
			buttonClear = new JButton();
			
			// Language and Icons
			buttonClear.setToolTipText(Lang.get("Organizer.Control.Clear"));
			buttonClear.setIcon(new ImageIcon(
					getClass().getResource(
							Lyra.getIconByMap("Control-Clear"))));
			
			// Identifier and Listener
			buttonClear.setName("CONTROL.CLEAR");
			buttonClear.addActionListener(Interface);
		}
		
		// Return Instance
		return buttonClear;
	}
	// End of getButtonClear Method
	
	
	/**
	 * This method initialises buttonSave.
	 * <p>
	 * @return javax.swing.JButton	
	 */
	private JButton getButtonSave() {
		
		// Check for Instance
		if (buttonSave == null) {
			
			// Create Instance
			buttonSave = new JButton();
			
			// Language and Icons
			buttonSave.setToolTipText(Lang.get("Organizer.Control.Save"));
			buttonSave.setIcon(new ImageIcon(
					getClass().getResource(
							Lyra.getIconByMap("Control-Save"))));
			
			// Identifier and Listener
			buttonSave.setName("CONTROL.SAVE");
			buttonSave.addActionListener(Interface);
		}
		
		// Return Instance
		return buttonSave;
	}
	// End of getButtonSave Method
	
	
	/**
	 * The code separation from the user interface.   This Class handles all
	 * of the complexities of making the interface interactive.
	 * <p>
	 * @author Martin Foster
	 */
	public class Interact 
	   implements ActionListener,
	              KeyListener,
	   			  ListSelectionListener,
	   			  MouseListener,
	              WindowListener,
	              UpdateListener {
	
		 
		/*
		 * Contains filtering elements for narrowing down results in various
		 * Library elements.
		 */
		private Map<String,String> Filter = new HashMap<String,String>();
		
		
		/*
		 * This array contains a list of all columns that we care about to show
		 * for the currently playing songs list.   It may not be the best place
		 * for it, so it may have to move.
		 */
		private final String[] Current = {
				"Id",
				"Title",
				"Artist",
				"Album",
				"Time",
				"Track",
				"Genre",
				"Date"
		};
		
		// Proper titles
		private final String[] Current_Titles  = {
				Lang.get("Organizer.Table.Playlist.Id"),
				Lang.get("Organizer.Table.Playlist.Title"),
				Lang.get("Organizer.Table.Playlist.Artist"),
				Lang.get("Organizer.Table.Playlist.Album"),
				Lang.get("Organizer.Table.Playlist.Time"),
				Lang.get("Organizer.Table.Playlist.Track"),
				Lang.get("Organizer.Table.Playlist.Genre"),
				Lang.get("Organizer.Table.Playlist.Date"),
		};;
		
		
		// String Identifier
		private final String CurrentPrecusor = "State.Organizer.Current.";

		/*
		 * Similar to the element above, this one is used to define column
		 * names for the Songs table.  Used in the export and import of
		 * column names.
		 */
		private final String[] Songs = {
				"Title", 
				"Artist", 
				"Album", 
				"Time", 
				"Track", 
				"Genre", 
				"Date", 
				"Rating", 
				"file"
			};

		// Titles for the Columns
		private String[] Songs_Titles = {
				Lang.get("Organizer.Table.Song.Title"),
	        	Lang.get("Organizer.Table.Song.Artist"),
				Lang.get("Organizer.Table.Song.Album"),
				Lang.get("Organizer.Table.Song.Time"),
				Lang.get("Organizer.Table.Song.Track"),
				Lang.get("Organizer.Table.Song.Genre"),
				Lang.get("Organizer.Table.Song.Date"),
				Lang.get("Organizer.Table.Song.Rating"),
				Lang.get("Organizer.Table.Song.File")
		};;
		
		
		// Update block
		boolean Initialise = false;
		
		// Size and position identifier
		private static final String SIZE = "Width.";
		private static final String POSITION = "Position.";
		
		// String Identifier
		private static final String SongsPrecusor = "State.Organizer.Songs.";
		
    	// List generation
    	private static final String LIST_REMOTE   = "REMOTE";
    	private static final String LIST_LOCAL    = "LOCAL";
    	private static final String LIST_SMART    = "SMART";
    	private static final String LIST_DECADE   = "DECADE";
    	private static final String LIST_FAVORITE = "FAVORITES";
    	
    	// Smart Playlist Selections
    	private static final String SMART_ARTIST  = "ARTIST";
    	private static final String SMART_ALBUM   = "ALBUM";
    	private static final String SMART_GENRE   = "GENRE";
    	private static final String SMART_SONG    = "SONGS";
    	private static final String SMART_RATING  = "RATING";
    	
    	// List Roots
    	private TreePath listRemote;
    	private TreePath listLocal;
    	private TreePath listSmart;
    	
		// Class Logger Identifier
    	private static final String CLASS = "org.lyra.Interface.PlayerOrganiserFrame.Interact";

    	
		/**
		 *  Base constructor for the class.    
		 */
		public Interact() {};
		
		
		/**
		 * Establishes the baseline for the two core tables.  Library songs and
		 * the current playlist.
		 */
		public void initTables() {
			
			// We initialise the tables with baseline information
			updateLibrary();
			
			// Current Playlist
			updateCurrent();
			
			// Playlist Listing
			updatePlaylists();
			
			
			/*
			 * We update the state based on database information.   This
			 * permits the user to keep state between uses.
			 */
			
			// Currently playing music
			setColumnState(
					getTableCurrent(), 
					Current,
					Current_Titles,
					CurrentPrecusor
				  );
			
			// Current songs
			setColumnState(
					getTableSongs(), 
					Songs,
					Songs_Titles,
					SongsPrecusor
				  );
			
			
			/*
			 * Recovery of collapsed tree information for playlists is handled
			 * here.   While fairly straight forward requires the existence of
			 * paths which are created when initiated.
			 */
			
			if (Boolean.parseBoolean(Global.get("State.Organizer.Playlist.Remote")))
				getTreePlaylist().expandPath(listRemote);
			
			if (Boolean.parseBoolean(Global.get("State.Organizer.Playlist.Local")))
				getTreePlaylist().expandPath(listLocal);
			
			if (Boolean.parseBoolean(Global.get("State.Organizer.Playlist.Smart")))
				getTreePlaylist().expandPath(listSmart);
			
			
			/* 
			 * Now that we have managed to get all the information put into 
			 * place it means that we can now accept updates.
			 */
			Watcher.addUpdateListener(this);
		}
		// End of initTables Method
		
		
		/**
		 * Triggers the generation of the baseline for the current playlist
		 * table.
		 */
		public void updateCurrent() {
			
			/*
			 * To make the operation more efficient, we will not redraw 
			 * elements from the current playlist until we are visible
			 * again.
			 */
			if (Source.isVisible()) {
			
				// Update the renderer's used
				updateRenderers();
			
				// Capture current state
				List<Map<String,Object>> widths = getColumnState(getTableCurrent().getColumnModel());
			
				// Current Playlist
				setTableCurrent(getOrganiserCurrent(), Current.length);
				setColumnState(getTableCurrent().getColumnModel(), widths);
			}
			
			/*
			 * Blind updates would occur when the panel is hidden from view so
			 * what we do is track that and handle it when the window is
			 * returned to visibility.
			 */
			else {
				
				// Log changes
				Logger.Info(
					CLASS, 
					"ppdateCurrent", 
					Lang.get("Organizer.Log.Skip.Current")
				  );
				
				// Forcing to true quicker then checking
				BlindUpdate = true;
			}
		}
		// End of updateCurrent Method
		
		
		/**
		 * Triggers the generation of baseline information for the Songs
		 * listing table.
		 */
		public void updateLibrary() {
			
			// Pass up with a false identifier
			updateLibrary("NONE");
		}
		// End of updateLibrary Method
		
		
		/**
		 * Triggers the generation of baseline information for the Songs
		 * listing table.
		 * <p>
		 * @param ident Identity of the element managing.
		 */
		public void updateLibrary(String ident) {
			
			// Setup block
			Initialise = true;
			
			/*
			 * Initial setup is fairly straight forward an operation and
			 * deals with no real logic.  It simply fetches all of the
			 * information from the database directly where applicable.
			 */
			
			// Album Filtering Element
			if (!ident.equals(getTableAlbum().getName())) {
				
				// Update Listing
				setTableAlbum(Manager.getOrganiserAlbum(
						Filter, 
						Lang.get("Organizer.Filter.Album"), 
						Lang.get("Organizer.Filter.Unknown")
				  ));
				setSelection(getTableAlbum(), getTableAlbum().getName());
			}

			// Artist Filtering Element
			if (!ident.equals(getTableArtist().getName())) {
				
				// Update Listing
				setTableArtist(Manager.getOrganiserArtist(
						Filter, 
						Lang.get("Organizer.Filter.Artist"), 
						Lang.get("Organizer.Filter.Unknown")
				  ));
				setSelection(getTableArtist(), getTableArtist().getName());
			}

			// Genre Filtering Element
			if (!ident.equals(getTableArtist().getName())) {
				
				// Update Listing
				setTableGenre(Manager.getOrganiserGenre(
						Filter, 
						Lang.get("Organizer.Filter.Genre"), 
						Lang.get("Organizer.Filter.Unknown")
				  ));
				setSelection(getTableGenre(), getTableGenre().getName());
			}

			
			// Capture current state
			List<Map<String,Object>> widths = getColumnState(getTableSongs().getColumnModel());
			
			// Song Library
			setTableSongs(Manager.getOrganiserSongs(Filter), Songs.length);
			setColumnState(getTableSongs().getColumnModel(), widths);
			
			// Update the status with information
			setPanelStatus(Manager.getOrganiserStatus(Filter));
			
			// Disable block
			Initialise = false;
		}
		// End of updateLibrary Method
		
		
		/**
		 * Triggers the generation of baseline information for the Songs
		 * listing table.
		 * <p>
	     * @param fields String containing fields to search from.
	     * @param criteria String containing search criteria.
		 */
		public void updateLibrary(String fields, String criteria) {
			
			// Setup block
			Initialise = true;
			
			/*
			 * Initial setup is fairly straight forward an operation and
			 * deals with no real logic.  It simply fetches all of the
			 * information from the database directly where applicable.
			 */
			
			// Filter looses its place
			Filter = new HashMap<String,String>();
			
			
			// Album Filtering Element
			setTableAlbum(Manager.getOrganiserAlbum(
					Filter, 
					Lang.get("Organizer.Filter.Album"), 
					Lang.get("Organizer.Filter.Unknown")
			  ));
			setSelection(getTableAlbum(), getTableAlbum().getName());

			
			// Artist Filtering Element
			setTableArtist(Manager.getOrganiserArtist(
					Filter, 
					Lang.get("Organizer.Filter.Artist"), 
					Lang.get("Organizer.Filter.Unknown")
			  ));
			setSelection(getTableArtist(), getTableArtist().getName());


			// Genre Filtering Element
			setTableGenre(Manager.getOrganiserGenre(
					Filter, 
					Lang.get("Organizer.Filter.Genre"), 
					Lang.get("Organizer.Filter.Unknown")
			  ));
			setSelection(getTableGenre(), getTableGenre().getName());


			// Capture current state
			List<Map<String,Object>> widths = getColumnState(getTableSongs().getColumnModel());
			
			// Song Library
			setTableSongs(Manager.getOrganiserSongs(fields, criteria), Songs.length);
			setColumnState(getTableSongs().getColumnModel(), widths);
			
			// Update the status with information
			setPanelStatus(Manager.getOrganiserStatus(fields, criteria));
			
			// Disable block
			Initialise = false;
		}
		// End of updateLibrary Method
		
		
		/**
		 * Triggers baseline information generation for the playlist listing.
		 */
		public void updatePlaylists() {
			
			/*
			 * First we create a new node structure and only after that point
			 * will we make sure that all of the lists are properly imported.
			 */
			
			// Root Node
			LyraMutableTreeNode root = new LyraMutableTreeNode();
			
			// Remote Lists
			LyraMutableTreeNode remote = new LyraMutableTreeNode(
				Lang.get("Organizer,Playlists.Remote"),
				LIST_REMOTE
			  );
			
			// Local Lists
			LyraMutableTreeNode local = new LyraMutableTreeNode(
				Lang.get("Organizer.Playlists.Local"),
				LIST_LOCAL
			  );

			
			/*
			 * Smart Playlists have additional sub-sections embedded within
			 * in order to create more of a logical split.
			 */
			
			// Smart Playlists
			LyraMutableTreeNode smart = new LyraMutableTreeNode(
				Lang.get("Organizer.Playlists.Smart"),
				LIST_SMART
			  );
			
			// Decades Filtering
			LyraMutableTreeNode decade = new LyraMutableTreeNode(
				Lang.get("Organizer.Playlists.Smart.Decade"),
				LIST_DECADE 
			  );

			// Favourite Grouping
			LyraMutableTreeNode favs = new LyraMutableTreeNode(
				Lang.get("Organizer.Playlists.Smart.Favorite"),
				LIST_FAVORITE 
			  );

			// Add over to parent
			smart.add(decade);
			smart.add(favs);
			
			
			/*
			 * Additionally, while decades will be generated later certain
			 * elements of smart playlists are already know ahead of time.
			 */
			
			// Artist Favourites
			favs.add(new LyraMutableTreeNode(
				Lang.get("Organizer.Playlists.Smart.Favorite.Artist"),
				LIST_FAVORITE,
				SMART_ARTIST
			  ));
			
			// Album Favourites
			favs.add(new LyraMutableTreeNode(
				Lang.get("Organizer.Playlists.Smart.Favorite.Album"),
				LIST_FAVORITE,
				SMART_ALBUM
			  ));
			
			// Genre Favourites
			favs.add(new LyraMutableTreeNode(
				Lang.get("Organizer.Playlists.Smart.Favorite.Genre"),
				LIST_FAVORITE,
				SMART_GENRE
			  ));
			
			// Song Favourites
			favs.add(new LyraMutableTreeNode(
				Lang.get("Organizer.Playlists.Smart.Favorite.Song"),
				LIST_FAVORITE,
				SMART_SONG
			  ));
			
			// Song Rated
			favs.add(new LyraMutableTreeNode(
				Lang.get("Organizer.Playlists.Smart.Favorite.Rating"),
				LIST_FAVORITE,
				SMART_RATING
			  ));
			
			
			/*
			 * Remote playlists are some of the easier ones to generate.  Their
			 * listing is always updated through the Status class and is nothing
			 * more then a straight list of strings.
			 */
			List<String> playlist_remote = Info.getPlaylist();
			
			// Sort the list
			Collections.sort(playlist_remote);
			
			// Loop through and create new elements
			for (String name: playlist_remote) {
				
				// Create node
				LyraMutableTreeNode child = new LyraMutableTreeNode(
					name,
					LIST_REMOTE,
					name
				  );
				
				// Append to node
				remote.add(child);
			}
				
			
			/*
			 * Local lists require some database querying but nothing overly
			 * taxing to accomplish.
			 */
			List<String> playlist_local = Manager.getOrganiserPlaylists();
				
			// Loop through and create new elements
			for (String name: playlist_local) {
				
				// Create node
				LyraMutableTreeNode child = new LyraMutableTreeNode(
					name,
					LIST_LOCAL,
					name
				  );
				
				// Append to node
				local.add(child);
			}
			
			/*
			 * Decades Playlists are governed by the database which requires us
			 * to query the database and extract the decades.
			 */
			List<String> playlist_decade = Manager.getPlaylistDecade();
			
			// Loop through and create new elements
			for (String name: playlist_decade) {
				
				// Create node
				LyraMutableTreeNode child = new LyraMutableTreeNode(
					name,
					LIST_DECADE,
					name
				  );
				
				// Append to node
				decade.add(child);
			}
			
			
			/*
			 * Capture the initial states of expansion in order to recover
			 * them in between refreshes.
			 */
			
			boolean state_remote = (listRemote != null)
				? getTreePlaylist().isExpanded(listRemote)
				: false;
				
			boolean state_local = (listLocal != null)
				? getTreePlaylist().isExpanded(listLocal)
				: false;
			
			boolean state_smart = (listSmart != null)
				? getTreePlaylist().isExpanded(listSmart)
				: false;
				
				
			/*
			 * Generate a series of paths so that we can expand and control
			 * the state of a few elements.
			 */
			
			// Temporary arrays
			Object[] arr_remote = { root, remote };
			Object[] arr_local  = { root, local };
			Object[] arr_smart  = { root, smart };
			
			// Generate Paths
			listRemote = new TreePath(arr_remote);
			listLocal  = new TreePath(arr_local);
			listSmart  = new TreePath(arr_smart);
			
			
			/*
			 * Add in all of the elements together and in turn add those to
			 * the root element.
			 */
			root.add(remote);
			root.add(local);
			root.add(smart);
			
			
			/*
			 * With a fully generated series of nodes, time to update the
			 * existing tree.   This will be done at model level.
			 */
			setTreePlaylist(root);
			
			/*
			 * Adjust state based on what we captured right before we
			 * re-generated the elements.
			 */
			if (state_remote) getTreePlaylist().expandPath(listRemote);
			if (state_local)  getTreePlaylist().expandPath(listLocal);
			if (state_smart)  getTreePlaylist().expandPath(listSmart);
		}
		// End of updatePlaylists Method
		
		
		/**
		 * Adjusts the renderer to a specific value for highlighting a
		 * specific row when required.
		 */
		public void updateRenderers() {
			
			// What is currently playing
			String s_active = Info.getState().get("song");
			
			// Convert of pass on nothing
			Integer i_active = (s_active != null)
				? Integer.parseInt(s_active)
				: null;
			
			// Adjust Renderer
			CurrentRowRenderer.setActive(i_active);
			CurrentPositionRenderer.setActive(i_active);
			CurrentTimeElapsedRenderer.setActive(i_active);
			
			
			/*
			 * We have the opportunity to set the focus when it is required
			 * normally applicable when the Current Playlist gets abnormally
			 * large.
			 */
			
			// Set current location of where playback is not occurring
			if ((i_active != null)
			 && (getTableCurrent().getSelectedRowCount() < 1))
				getTableCurrent().scrollRectToVisible(
					getTableCurrent().getCellRect(
							i_active, 
							0, 
							true
					  ));
			
			// Since someone is selecting keep their choices
			else if (getTableCurrent().getSelectedRow() >= 0)
				getTableCurrent().scrollRectToVisible(
					getTableCurrent().getCellRect(
							getTableCurrent().getSelectedRow(), 
							0, 
							true
						));
		}
		// End of updateRenderers Method
		
		
		/**
		 * This is a very specific implementation which is used to populate
		 * the playlist section.  This method is not a database element
		 * but one which translates from the host directly.
		 * <p>
		 * @return Multilevel array containing the contents of the Current Playlist.
		 */
		public String[][] getOrganiserCurrent() {
			
			// Grab a copy of the generated list
			List<Map<String,String>> playlist = Info.getCurrent();
			
			// Generate new array
			String[][] dataset = new String[playlist.size()][Current.length];
			
			// Loop through and insert
			for (int rCount=0; rCount < playlist.size(); rCount++) {
				
				// Extract singular element
				Map<String,String> record = playlist.get(rCount);
				
				// Loop through and append columns
				for (int cCount=0; cCount < Current.length; cCount++)
					dataset[rCount][cCount] = record.get(Current[cCount]);
			}

			// Return what was generated
			return dataset;
		}
		// End of getOrganiserPlaylist Method	

		
		/**
		 * State information gathering is a fairly large and complex entity.
		 * So what we do is separate it out to make the listeners a bit cleaner
		 * to review.  In other words, a proverbial rug to sweep this code
		 * under.
		 * <p>
		 * @param source JFrame Instance that we will extract state from.
		 */
		public void getOrganiserState(JFrame source) {
			
			/*
			 * This will bypass saving state outright in situations where
			 * the user interface has not been fully initialised.   This will
			 * alleviate loosing full state information it is not desired.
			 */
			if (!Lyra.StateSave)
				return;
			
			
			/*
			 * We can only process window information if we are not minimised
			 * or maximised.  Otherwise restoring dimensions will simply affect
			 * matters negatively.
			 */
			if (source.getState() == JFrame.NORMAL) {
			
				// X Position of the screen for the window
				Global.put(
					"State.Organizer.Window.XCoord", 
					Integer.toString((int)source.getLocationOnScreen().getX())
				  );

				// Y Position on the screen the window
				Global.put(
					"State.Organizer.Window.YCoord", 
					Integer.toString((int)source.getLocationOnScreen().getY())
				  );
			
				// Width of the window when not maximised
				Global.put(
					"State.Organizer.Window.Width", 
					Integer.toString(source.getWidth())
				  );
				
				// Height of the window when not maximised
				Global.put(
					"State.Organizer.Window.Height", 
					Integer.toString(source.getHeight())
				  );
			}
			
			/*
			 * GENERAL ITEMS
			 * 
			 * Now we can start the process of general components which are
			 * not outwardly affected by the state of the window.  First we
			 * save the state itself then work our way into the individual
			 * frames and scroll panels which dominate the look and feel. 
			 */
			
			// State information
			if (source.getState() != JFrame.ICONIFIED) {
				
				// We do not care about returning to an icon
				Global.put(
					"State.Organizer.Window.State", 
					Integer.toString(source.getState())
				  );
			}
			
			
			/*
			 * JSplitPane
			 * 
			 * JSplitPanes form the core separation of elements which exist
			 * in the Organiser.  As a result, we capture their information
			 * first and then move on to the table specifics.
			 */
			
			// Central split which separates the Lists and Library
			Global.put(
				"State.Organizer.Split.Global", 
				Integer.toString(getSplitGlobal().getDividerLocation())
			  );
			
			// Separates the Filters and Song Listing
			if (getSplitLibrary() instanceof JSplitPane)
				Global.put(
					"State.Organizer.Split.Library", 
					Integer.toString(((JSplitPane)getSplitLibrary()).getDividerLocation())
				  );
			
			// Separates the Playlist and Currently Playing List
			if (getSplitLists() instanceof JSplitPane)
				Global.put(
					"State.Organizer.Split.Lists", 
					Integer.toString(((JSplitPane)getSplitLists()).getDividerLocation())
				  );
			
			
			/*
			 * The current songs playlist has a series of columns that can be
			 * controlled in width.   We need to track this specifically.
			 */
			getColumnState(
				getTableCurrent(), 
				Current, 
				CurrentPrecusor
			  );

			
			/*
			 * This element applies to the Songs JTable element.   We record the
			 * designated sizes for use.
			 */
			getColumnState(
				getTableSongs(), 
				Songs, 
				SongsPrecusor
			  );

				
			/*
			 * The search capability can be enabled and disabled at will
			 * unlike certain other components.  
			 */
			Global.put(
				"State.Organizer.Search.Display", 
				Boolean.toString(getBarSearch().isVisible())
			  );
			
			
			/*
			 * Lastly we have a series of playlists that are either remote,
			 * local or generated using database information.   The only
			 * aspect we care about are their expansion states.
			 */
			Global.put(
				"State.Organizer.Playlist.Remote", 
				Boolean.toString(getTreePlaylist().isExpanded(listRemote))
			  );
			
			Global.put(
				"State.Organizer.Playlist.Local", 
				Boolean.toString(getTreePlaylist().isExpanded(listLocal))
			  );
			
			Global.put(
				"State.Organizer.Playlist.Smart", 
				Boolean.toString(getTreePlaylist().isExpanded(listSmart))
			  );
			
			
			/*
			 * Now that we have gotten all the states we need to recover the
			 * look and feel we now move to actually saving this information for
			 * future use.
			 */
			Manager.updateSettings(Global, "State.Organizer.");
			Manager.updateSettings(Global, "State.Compact.");
		}
		// End of getOrganiserState Method
		
		
		/**
		 * Fetches state information for specific columns in JTables which
		 * have adjustable columns.  This variant will store it to a Map
		 * which will be recorded to the database at shutdown.
		 * <p>
		 * @param table JTable containing all rows and columns.
		 * @param names Column names and titles.
		 * @param precursor String containing tag precursor.
		 */
		public void getColumnState(JTable table, String[] names, String precursor) {

			// Extract Column Model
			TableColumnModel column = table.getColumnModel();
			
			// Loop and record positions for later use
			for (int count=0; count < column.getColumnCount(); count++) {
				
				// Safety
				if (count >= names.length) break;
				
				/*
				 * Update the entry.  Note that we have to convert the Index of
				 * the column in order to avoid problems when columns are mixed
				 * up.
				 */
				int conv = table.convertColumnIndexToView(count);
				
				// Width of a column
				Global.put(
					precursor + SIZE + names[count], 
					Integer.toString(column.getColumn(conv).getWidth())
				  );
				
				// Positioning of a column
				Global.put(
					precursor + POSITION + names[count], 
					Integer.toString(conv)
				  );
			}
		}
		// End of getColumnState Method
		

		/**
		 * Fetches state information for specific columns in JTables which
		 * have adjustable columns.  This variant will return it as an array.
		 * <p>
		 * @param model ColumnModel containing all information.
		 * @return Array of integers containing width
		 */
		public List<Map<String,Object>> getColumnState(TableColumnModel model) {

			// Passage Element
			List<Map<String,Object>> pass = new LinkedList<Map<String,Object>>();
			
			
			// Loop and record positions for later use
			for (int count=0; count < model.getColumnCount(); count++) {
				
				// Extract Column Model
				TableColumn column = model.getColumn(count);
				
				// Create Instance
				Map<String,Object> values = new HashMap<String,Object>();
				
				// Capture elements
				values.put("Width", column.getWidth());
				values.put("Index", column.getModelIndex());
				values.put("Title", column.getHeaderValue());
				
				// Add in element
				pass.add(values);
			}	
			
			// Return generated array
			return pass;
		}
		// End of getColumnState Method
		
		
		/**
		 * Adjusts the column state for a specific table.  Allowing the users
		 * options to be reflected.
		 * <p>
		 * @param table JTable element containing all the rows and columns.
		 * @param names Column names.
		 * @param titles Column titles.
		 * @param precursor String containing tag precursor.
		 */
		public void setColumnState(JTable table, String[] names, String[] titles, String precursor) {
			
			// Extract Model
			TableColumnModel model = table.getColumnModel();
			
			/*
			 * We need to map the difference between columns in the model
			 * and how they relate here specifically.
			 */
			Map<Integer,Integer> relate = new HashMap<Integer,Integer>();
			
			// Column correlation
			for (int count=0; count < names.length; count++) {

				// We need to ensure this key exists
				if (Global.containsKey(precursor + POSITION + names[count]))
					relate.put(
						Integer.parseInt(Global.get(precursor + POSITION + names[count])),
						count
					  );
			}
			
			/*
			 * Now that we have managed to correlate the true position of the
			 * columns, we can now begin the process of assigning the details
			 * appropriately.
			 */
			for (int count: relate.keySet()) {
								
				// Safety
				if (Global.containsKey(precursor + SIZE + names[count])) {
					
					// Extract column model
					TableColumn column = model.getColumn(count);
					
					// Connect to the model column
					column.setModelIndex(relate.get(count));
					
					// Adjust title
					column.setHeaderValue(titles[relate.get(count)]);
					
					// Adjust width
					column.setPreferredWidth(
							Integer.parseInt(Global.get(precursor + SIZE + names[relate.get(count)]))
					  );
				}
			}	
		}
		// End of setColumnState Method
		
		
		/**
		 * Adjusts the column state for a specific table.  Allowing the users
		 * options to be reflected, uses an array to copy and pass on.
		 * <p>
		 * @param model ColumnModel containing all information.
		 * @param prefs List containing all necessary attributes for cloning.
		 */
		public void setColumnState(TableColumnModel model, List<Map<String,Object>> prefs) {
			
			// Safety
			if (model.getColumnCount() != prefs.size()) return;
			
			// Column loop
			for (int count=0; count < prefs.size(); count++) {
				
				// Extract column
				TableColumn column = model.getColumn(count);
				
				// Extract Map
				Map<String,Object> values = prefs.get(count);
				
				
				/*
				 * Adjusting elements here requires casting so we will make
				 * sure that they are what we think they are.
				 */
				
				// Adjust Width of Element
				if (values.get("Width") instanceof Integer)
					column.setPreferredWidth((Integer)values.get("Width"));
				
				// Adjust Model to Visible Index
				if (values.get("Index") instanceof Integer)
					column.setModelIndex((Integer)values.get("Index"));

				// Adjust title
				column.setHeaderValue(values.get("Title"));
			}
		}
		// End of setColumnState Method
		
		
		/**
		 * Adjusts the selection of the item to display what is currently being
		 * filtered.   That way the user understands what is taking place when
		 * filtering occurs.
		 * <p>
		 * @param current JTable that will be adjusted based on selection.
		 * @param key String identifier tied to the Filter Map. 
		 */
		public void setSelection(JTable current, String key) {

			/*
			 * If the key is missing outright then we know there is no 
			 * filtering taking place on this one.
			 */
			if (Filter.containsKey(key)) {
				
				/*
				 * If a key is NULL then we know that it is related exclusively
				 * to element that are unknown for that grouping.   When they
				 * are not null, we have to search for the entity and select
				 * it.
				 */
				if (Filter.get(key) != null) {
					
					// Loop through the rows and search
					for (int row=0; row < current.getRowCount(); row++) {
						
						// Capture string
						String cell = current.getValueAt(row, 0).toString();
						
						// If we have a match select and leave
						if (cell.equals(Filter.get(key))) {
							
							// Select the row
							current.addRowSelectionInterval(row, row);
							
							// This will set the visible portion to include this selection
							current.scrollRectToVisible(
								current.getCellRect(row, 0, true));
							
							// Return from this method
							return;
						}
					}
				}
				
				/*
				 * Since this one is NULL we now know exactly where the element
				 * is.  Hence the hard coding.
				 */
				else {
					
					// We select the top element
					current.addRowSelectionInterval(1, 1);
					
					// Return from this method
					return;
				}
			}
			
			/*
			 * All else failed, as a result we hard code the first element to be
			 * selected.  This will always be the all inclusive non-selection
			 * element.
			 */
			current.addRowSelectionInterval(0, 0);	
		}
		// End of setSelection Method
		
		
		/*
		 * LISTENERS
		 * 
		 * Various listeners and other elements.  All other interface elements
		 * are located above.
		 */
		
		/* (non-Javadoc)
		 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
		 */
		public void actionPerformed(ActionEvent evt) {
			
			// We only want to deal with buttons
			if (evt.getSource() instanceof JButton) {
				
				// Extract Instance
				String ident = ((JButton)evt.getSource()).getName();
				
				/*
				 * Single element commands which will do little more then call
				 * the appropriate command from the class instance they are in
				 * business with.
				 */
				
				// Shuffle
				if (ident.equals(getButtonShuffle().getName()))
					Lists.shuffleCurrent();
				
				// Clear out the entire list
				else if (ident.equals(getButtonClear().getName()))
					Lists.clearCurrent();
				
				// Clear out selected list
				else if (ident.equals(getButtonRemove().getName()))
					Lists.clearCurrent(getTableCurrent().getSelectedRows());
			
				/*
				 * More complicated elements fall under this area.  Essentially
				 * these require more confirmation of what is taking place and
				 * more intelligently route.
				 */
				else if (ident.equals(getButtonSave().getName())) {
										
					// Capture filtering values
					Set<String> local = Manager.getPlaylistLocal();
					Set<String> remote = new TreeSet<String>(Info.getPlaylist());
					
					// Integration with Last.fm service.
					boolean integrate = (!Global.get("LastDotFm.Session").equals("0"));
					
					// Time to extract user choice
					PlaylistSave details 
						= PlaylistSaveDialog.getVerdict(Lang, local, remote, integrate, Source);
					
					
					// Confirm if local or remote 
					if ((details.isLocal())
					 && (details.getName() != null)) {
						
						/*
						 * Local saving is actually more complex the remote saving
						 * due to the fact that every song will have to be added
						 * to the database one by one.
						 */
						
						// Remove and re-insert all elements
						Manager.deletePlaylistLocal(details.getName());
						Manager.insertPlaylistLocal(details.getName());
						
						// Loop through and insert each song
						Manager.insertPlaylistSong(
							details.getName(), 
							Info.getCurrent()
						  );
						
						
						/*
						 * When we have Last.fm integration we complete the
						 * following section and send the songs over to the
						 * Last.fm service.
						 */
						if (details.isService()) {
							
							// Create Instance
							Demarcation deploy = 
								Demarcation.getInstance(Logger, Global, Lang);
							
							// Create Playlist
							deploy.transmitPlaylistCreate(
								Info.getCurrent(), 
								details.getName()
							 );
						}
												
						// Make sure to update the Tree
						SwingUtilities.invokeLater(new Runnable() {
							
							/* (non-Javadoc)
							 * @see java.lang.Runnable#run()
							 */
							public void run() {
								
								// This will refresh the lists
								updatePlaylists();
							}
						});
					}
					
					/*
					 * Remote entries are just a simple command away. 
					 */
					else {
						
						// Save the remote list
						if (Enabled.contains(Terminus.PROTOCOL.get("Playlist.Save")))
							Lists.saveRemote(details.getName());
					}
				}
				
				/*
				 * Playlists are fairly complicated elements in the sense that
				 * they require knowledge of what you are looking for prior to
				 * actually committing the change.
				 */
				else if (ident.equals(getButtonDelete().getName())) {
					
					// Safety
					if (getTreePlaylist().getSelectionPath().getLastPathComponent() instanceof LyraMutableTreeNode) {
						
						// Capture Element
						LyraMutableTreeNode select = 
							(LyraMutableTreeNode)getTreePlaylist().getSelectionPath().getLastPathComponent();

					
						// Leaf elements only
						if ((select.getGroup() != null)
						 && (select.getName()  != null)) {
							
							/*
							 * NOTE: Smart playlists are automatically generated
							 * and will not be removed.
							 */
							
							// Remote Lists
							if ((select.getGroup().equals(LIST_REMOTE))
							 && (Enabled.contains(Terminus.PROTOCOL.get("Playlist.Remove"))))
								Lists.removeRemote(select.getName());
								
							// Local Lists
							else if (select.getGroup().equals(LIST_LOCAL)) {
								
								// Remove Playlist
								Manager.deletePlaylistLocal(select.getName());
								
								// Initiate an update of the tree
								SwingUtilities.invokeLater(new Runnable() {
									
									/* (non-Javadoc)
									 * @see java.lang.Runnable#run()
									 */
									public void run() {
										
										// Initiate the change
										updatePlaylists();
									}
								});
							}
						}
					}
				}
			}
		}
		// End of actionPerformed Method

		
		/*
		 * HOST UPDATES
		 */
		
		/* (non-Javadoc)
		 * @see org.lyra.Host.UpdateListener#hostUpdated(org.lyra.Host.UpdateEvent)
		 */
		public void hostUpdated(UpdateEvent update) {
			
			
			// Is this a player update
			if ((update.isPlayer())
			 && (!update.isCurrent())) {
								
				/*
				 * In this section we look at what is currently playing and then
				 * adjust the highlight value accordingly.  Since almost all 
				 * changes bring about an update to the state 
				 */
				SwingUtilities.invokeLater(new Runnable() {

					/* (non-Javadoc)
					 * @see java.lang.Runnable#run()
					 */
					public void run() {
						
						// Update Renderer's
						updateRenderers();
						
						// Playlist
						getTableCurrent().repaint();
					}
				});
			}

			
			// Is there an update to the current playlist?
			if (update.isCurrent()) {
				
				/*
				 * Playlist updates are nice and simple things.   However in order
				 * to avoid bizarre errors we need to have it done using the main
				 * Swing thread.
				 */
				SwingUtilities.invokeLater(new Runnable() {

					/* (non-Javadoc)
					 * @see java.lang.Runnable#run()
					 */
					public void run() {
						
						// Playlist
						updateCurrent();
					}
				});
			}
			
			// Was there and update to the Library?
			if ((update.isLibrary()) 
			 && (update.isDatabase())) {
				
				/*
				 * Like the current playlist, songs are straight forward to update
				 * and in order to avoid bizarre error, we push it to the swing
				 * thread for handling.
				 */
				SwingUtilities.invokeLater(new Runnable() {

					/* (non-Javadoc)
					 * @see java.lang.Runnable#run()
					 */
					public void run() {
						
						// Playlist
						updateLibrary();
					}
				});
			}
			
			// Was there an update to the playlists
			if (update.isPlaylist()) {
				
				/*
				 * Like the other updates, the changes that are made are fairly
				 * straight forward.   However, we need to run it in the worker
				 * thread or we have issues otherwise.
				 */
				SwingUtilities.invokeLater(new Runnable() {

					/* (non-Javadoc)
					 * @see java.lang.Runnable#run()
					 */
					public void run() {
						
						// Playlist
						updatePlaylists();
					}
				});
			}
		}
		// End of hostUpdated Method
		
		
		/*
		 * SELECTION LISTENER
		 */
		
		/* (non-Javadoc)
		 * @see javax.swing.event.ListSelectionListener#valueChanged(javax.swing.event.ListSelectionEvent)
		 */
		public void valueChanged(ListSelectionEvent e) {
			
			// The block will prevent future overrides
			if (Initialise) return;
			
			/* 
			 * We do not care about what happens in between.  Once the user has
			 * done with his selection we can move on.
			 */
			if (!e.getValueIsAdjusting()) {
				
				// We only deal with elements that allow us to track them
				if (e.getSource() instanceof EnhancedListSelectionModel) {
					
					// Extract instance
					EnhancedListSelectionModel model =
						(EnhancedListSelectionModel)e.getSource();
				
					// Identifier
					String ident = model.getName();
					
					
					// Filter Management
					if (!ident.equals(getTableSongs().getName()))
						updateFilter(model, ident);
					
					// Song Status Bar
					else updateStatus();
				}
			}
		}
		// End of valueChanged Method
		
		
		/**
		 * Responsible for updating the status information that is shown under
		 * the songs table.   Since only one table makes use of this calculations
		 * are easy to make.
		 */
		private void updateStatus() {
			
			// Capture initial information
			Map<String,String> status = Manager.getOrganiserStatus(Filter);
			
			// Amount of rows captured
			int selected = getTableSongs().getSelectedRowCount();
			int playtime = 0;
			
			// Amount of selections
			status.put(
				"Selected",
				Integer.toString(selected)
			  );
			
			/*
			 * If we are provided with multiple rows, then we will be able to
			 * count up the amount of time for those selected songs.
			 */
			if (selected > 0) {
				
				// Extract model to access information
				OrganiserTableModel model = 
					(OrganiserTableModel)getTableSongs().getModel();
				
				// Loop through rows start the total
				for (int curr: getTableSongs().getSelectedRows())
					playtime += Integer.parseInt(
									model.getValueAt(
										getTableSongs().convertRowIndexToView(curr), 
										SONG_TIME
									  ).toString()
								  );
				
				/* 
				 * Now that we have a proper total pass it on the
				 * amount of selections
				 */
				status.put(
					"Runtime",
					Integer.toString(playtime)
				  );
				
				// Log event
				Logger.Info(
					CLASS, 
					"updateStatus",
					Lang.get("Organizer.Log.Select.Status")
						.replaceAll("MSONGS", status.get("Selected"))
						.replaceAll("MPLAYTIME", status.get("Runtime"))
				  );
				
			}
			
			// Pass on the database defaults
			setPanelStatus(status);
		}
		// End of updateStatus Method
		
		
		/**
		 * Responsible for updating the main song selection filter.  This
		 * filter is key to actually cutting down the amount of songs shown
		 * on the organiser.
		 * <p>
		 * @param model Instance of the captured model.
		 * @param ident String identifier of the table.
		 */
		private void updateFilter(EnhancedListSelectionModel model, String ident) {
			
			// No selection means no filtering
			if (model.isSelectionEmpty()) {
				
				// Remove key from filter
				Filter.remove(ident);
			}
			
			// Selection means more processing
			else {
				
				// Extract associated table
				JTable source = model.getSource();
				
				// String to pass on
				String pass = null;
				
				/*
				 * Under normal circumstances since this is called by
				 * JTable elements who only permit single selection
				 * hence we will loop and always replace the value
				 * with the last specified in the filter.
				 */
				for (int index: source.getSelectedRows()) {
					
					// Adjust passage of information
					pass = (source.getValueAt(index, 0) != null)
					  ? source.getValueAt(index, 0).toString()
					  : null;
				}
				
				
				// Safety check
				if (pass != null) {

					// Log event
					Logger.Info(
						CLASS, 
						"updateFilter",
						Lang.get("Organizer.Log.Select.Filter")
							.replaceAll("MELEMENT", pass)
							.replaceAll("MTYPE", ident)
					  );
				
				
					/*
					 * Time to confirm that the element is not something
					 * we need to deal with.  Only two will alter
					 * behaviour.
					 */
					
					// Elements which are NULL in nature
					if (pass.equals(Lang.get("Organizer.Filter.Unknown"))) {
						
						// We will search for NULL
						Filter.put(ident,null);
					}
					
					// These elements nullify selection
					else if ((pass.equals(Lang.get("Organizer.Filter.Genre")))
					 || (pass.equals(Lang.get("Organizer.Filter.Artist")))
					 || (pass.equals(Lang.get("Organizer.Filter.Album")))) {

						// Removal drops the searching
						Filter.remove(ident);
					 }
					
					// Normal elements are dealt with
					else {
						
						// Replace existing element
						Filter.put(ident,pass);
					}
				}
				
				else {
					
					// Remove key from filter
					Filter.remove(ident);
				}
			}
			
			// Update elements in the tables
			updateLibrary(ident);
			
		}
		
		
		/*
		 * WINDOW LISTENERS
		 */

		/* (non-Javadoc)
		 * @see java.awt.event.WindowListener#windowClosing(java.awt.event.WindowEvent)
		 */
		public void windowClosing(WindowEvent e) {
			
			/*
			 * There is no need to process if we are dealing with some other
			 * entity which is sending its information to us.
			 */
			if (e.getSource() instanceof JFrame) {
				
				/*
				 * We are always interested in saving the state of
				 * information.  Even if the user changes his mind.
				 */
				
				// Extract source
				JFrame source = (JFrame)e.getSource();
				
				// We care mostly about saving state information
				getOrganiserState(source);
				
				
				/*
				 * We will look into passing on the information of this window
				 * and close the application.
				 */
				Lyra.closeApplication(Source);
			}
		}
		// End of windowClosing Method
		
		
		/* (non-Javadoc)
		 * @see java.awt.event.WindowListener#windowClosed(java.awt.event.WindowEvent)
		 */
		public void windowClosed(WindowEvent e) {}
		
		/*
		 * (non-Javadoc)
		 * @see java.awt.event.WindowListener#windowActivated(java.awt.event.WindowEvent)
		 */
		public void windowActivated(WindowEvent e) {}

		/* (non-Javadoc)
		 * @see java.awt.event.WindowListener#windowDeactivated(java.awt.event.WindowEvent)
		 */
		public void windowDeactivated(WindowEvent e) {}

		/* (non-Javadoc)
		 * @see java.awt.event.WindowListener#windowDeiconified(java.awt.event.WindowEvent)
		 */
		public void windowDeiconified(WindowEvent e) {}

		/* (non-Javadoc)
		 * @see java.awt.event.WindowListener#windowIconified(java.awt.event.WindowEvent)
		 */
		public void windowIconified(WindowEvent evt) {
			
			/*
			 * With this variant we will essentially remove the icon element
			 * and rely on the System Tray icon instead. 
			 */
			if ((evt.getSource() instanceof JFrame)
			 && (LyraTrayIcon.isInstanced())) {
				
				// Capture source
				JFrame source = (JFrame)evt.getSource();
				
				// Return to normal
				source.setState(JFrame.NORMAL);
				
				// Hide window instead
				source.setVisible(false);
			}
		}
		// End of windowIconified

		/* (non-Javadoc)
		 * @see java.awt.event.WindowListener#windowOpened(java.awt.event.WindowEvent)
		 */
		public void windowOpened(WindowEvent e) {}

		
		/*
		 * MOUSE LISTENERS
		 */

		/* (non-Javadoc)
		 * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
		 */
		public void mouseClicked(MouseEvent e) {

			// We only deal with double clicks and instance of JTable
		    if ((e.getClickCount() == 2)
		     && (e.getSource() instanceof JTable)) {

	    		// Extract element
	    		JTable caller = (JTable)e.getSource();
	    		
	    		
	    		/*
	    		 * The bulk of our operations have to do with the filter
	    		 * elements.  They will be individually dealt with in this
	    		 * section. 
	    		 * 
	    		 * The current playlist will also pass through this element
	    		 * and while its behaviour differs from the others the 
	    		 * requirement to be able to play a song in the playlist
	    		 * is critical.
	    		 */
	    		if (!caller.getName().equals(getTableSongs().getName())) {
	    			
	    			// Is the selection valid
	    			if ((caller.getSelectedRow() >= 0)
	    			 && (caller.getValueAt(caller.getSelectedRow(), 0) != null)) {

	    				// Extract value
	    				String value = caller.getValueAt(caller.getSelectedRow(), 0).toString();
	    				
	    				// Safety
	    				if (value.equals(Lang.get("Organizer.Filter.Unknown"))) return;

	    				
	    				/*
	    				 * We got this far, now we need to identify the
	    				 * actual type and make use of that to insert
	    				 * appropriately.
	    				 */
	    				
	    				// Albums
	    				if (caller.getName().equals(getTableAlbum().getName())) {

	    					// Additional safety
	    					if (value.equals(Lang.get("Organizer.Filter.Album"))) return;

	    					// Insert album
	    					Lists.addCurrentAlbum(Filter);
	    				}
	    				
	    				// Artist
	    				else if (caller.getName().equals(getTableArtist().getName())) {

	    					// Additional safety
	    					if (value.equals(Lang.get("Organizer.Filter.Artist"))) return;
	    					
	    					// Insert album
	    					Lists.addCurrentArtist(Filter);
	    				}
	    			
	    				// Genre
	    				else if (caller.getName().equals(getTableGenre().getName())) {

	    					// Additional safety
	    					if (value.equals(Lang.get("Organizer.Filter.Genre"))) return;
	    					
	    					// Insert album
	    					Lists.addCurrentGenre(Filter);
	    				}
	    				
	    				
	    				/*
	    				 * The current playlist has a simple task to complete. 
	    				 * Just pass in the position in the list and the song
	    				 * will begin playing. 
	    				 */
	    				else if (caller.getName().equals(getTableCurrent().getName())) {
	    					
	    					// Play Song
	    					Control.playStart(caller.getSelectedRow());
	    				}
	    			}
	    		}
	    		
	    		
	    		/*
	    		 * The Songs table contains all of the information necessary to
	    		 * make things work our way.  So we don't really need to worry
	    		 * about processing and just select the column directly.
	    		 */
	    		else if (getTableSongs().getSelectedRowCount() > 0) {
    				
    				// Storage list
    				List<String> pass = new LinkedList<String>();
    				
    				/*
    				 * We need to do a little bit of translation due to the
    				 * fact that someone may have shifted rows about.
    				 */
    				for (int index: getTableSongs().getSelectedRows())
    					pass.add(getTableSongs().getValueAt(
    							index, 
    							getTableSongs().convertColumnIndexToView(Songs.length - 1)
    						).toString());
    				
					// Insert songs
					Lists.addCurrent(pass);
	    		}
	    	}
		    
		    /*
		     * Other elements we care about are the playlists themselves.  They
		     * have multiple options accessible to them, as a result we use the
		     * nodes to determine proper processing.
		     */
		    else if ((e.getClickCount() == 2)
		      && (e.getSource() instanceof JTree)) {
		    	
		    	// Extract element
		    	JTree source = (JTree)e.getSource();
		    	
		    	// Make sure these are properly defined nodes
		    	if (source.getSelectionPath().getLastPathComponent() instanceof LyraMutableTreeNode) {
		    		
		    		// Extract Node
		    		LyraMutableTreeNode node = 
		    			(LyraMutableTreeNode)source.getSelectionPath().getLastPathComponent();

		    		// We only want to deal with children
		    		if ((node.getName() != null) 
		    		 && (node.getGroup() != null)) {
		    			
		    			// Local Lists
		    			if (node.getGroup().equals(LIST_LOCAL))
		    				Lists.loadLocal(
		    					Manager.getPlaylistLocal(node.getName())
		    				  );
		    			
		    			// Remote Lists
		    			else if (node.getGroup().equals(LIST_REMOTE))
		    				Lists.loadRemote(node.getName());
		    			
		    			
		    			/*
		    			 * Smart Playlists.  Require a certain amount of
		    			 * thought, so we extract the values as necessary.
		    			 */
		    			
		    			// Smart Playlist - Decades
		    			else if (node.getGroup().equals(LIST_DECADE)) {
		    				
		    				Lists.loadLocal(
		    					Manager.getPlaylistDecade(node.getName())
		    				  );
		    			}
		    			
		    			// Smart Playlist - Favourite Artist
		    			else if ((node.getGroup().equals(LIST_FAVORITE))
		    			      && (node.getName().equals(SMART_ARTIST)))
		    				Lists.loadLocal(
		    					Manager.getPlaylistFArtist()
		    				  );
		    			
		    			// Smart Playlist - Favourite Album
		    			else if ((node.getGroup().equals(LIST_FAVORITE))
		    			      && (node.getName().equals(SMART_ALBUM)))
		    				Lists.loadLocal(
		    					Manager.getPlaylistFAlbum()
		    				  );
		    			
		    			// Smart Playlist - Favourite Genres
		    			else if ((node.getGroup().equals(LIST_FAVORITE))
		    			      && (node.getName().equals(SMART_GENRE)))
		    				Lists.loadLocal(
		    					Manager.getPlaylistFGenre()
		    				  );
		    			
		    			// Smart Playlist - Favourite Songs
		    			else if ((node.getGroup().equals(LIST_FAVORITE))
		    			      && (node.getName().equals(SMART_SONG)))
		    				Lists.loadLocal(
		    					Manager.getPlaylistFSong()
		    				  );
		    			
		    			// Smart Playlist - Favourite Songs
		    			else if ((node.getGroup().equals(LIST_FAVORITE))
		    			      && (node.getName().equals(SMART_RATING)))
		    				Lists.loadLocal(
		    					Manager.getPlaylistFRating()
		    				  );
		    		}
		    	}
		    }
		}

		/* (non-Javadoc)
		 * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
		 */
		public void mouseEntered(MouseEvent e) {}

		/* (non-Javadoc)
		 * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
		 */
		public void mouseExited(MouseEvent e) {}

		/* (non-Javadoc)
		 * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
		 */
		public void mousePressed(MouseEvent e) {}

		/* (non-Javadoc)
		 * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
		 */
		public void mouseReleased(MouseEvent e) {}

		
		/*
		 * KEY LISTENERS
		 * 
		 * Used in the key back finding of elements.   Typing a key will trigger
		 * a search for the element.
		 */

		
		/* (non-Javadoc)
		 * @see java.awt.event.KeyListener#keyPressed(java.awt.event.KeyEvent)
		 */
		public void keyPressed(KeyEvent evt) {

			// We only deal with JTable Elements
			if (evt.getSource() instanceof JTable) {

				// Extract Instance
				JTable source = (JTable)evt.getSource();
				
				/*
				 * Additionally we only care to deal with elements that make
				 * use of the OrganiserTableModel instance.
				 */
				if (source.getModel() instanceof OrganiserTableModel) {
					
					// Extract model
					OrganiserTableModel model = 
						(OrganiserTableModel)source.getModel();
					
					// Capture code
					int code = evt.getKeyCode();
					
					/*
					 * The ESCAPE character does one of two things, it will reset
					 * the selection and move us back to the top where no filtering
					 * will take place.  Secondly it will reset the search
					 * capability if enabled.
					 */
					if (evt.getKeyCode() == KeyEvent.VK_ESCAPE) {
					
						// Reset selection to top
						source.getSelectionModel().
							setSelectionInterval(0, 0);
						
						// Reset filter
						model.clearFindFirst();
						
						// Adjust focus
						source.scrollRectToVisible(
							source.getCellRect(
									0, 
									0, 
									true
							  ));
					}
					
					/*
					 * Here we enable the search based on certain characters
					 * entered.  This permits us the searching capability.
					 */
					else if (((code >= KeyEvent.VK_A)
					       && (code <= KeyEvent.VK_Z))
					      || ((code >= KeyEvent.VK_0)
						   && (code <= KeyEvent.VK_9))){
						
						// Search the model
						int row = model.charFindFirst(0, evt.getKeyChar());
						
						// Reset selection to top
						source.getSelectionModel().
							setSelectionInterval(row, row);
						
						// Adjust focus
						source.scrollRectToVisible(
							source.getCellRect(
									row, 
									0, 
									true
							  ));
					}
					
					/*
					 * Here we will truncate a character off the back and
					 * search again.
					 */
					else if (code == KeyEvent.VK_BACK_SPACE){
							
							// Search the model
							int row = model.chopFindFirst(0);
							
							// Reset selection to top
							source.getSelectionModel().
								setSelectionInterval(row, row);
							
							// Adjust focus
							source.scrollRectToVisible(
								source.getCellRect(
										row, 
										0, 
										true
								  ));
						}
				}
				

			}			
		}
		// End of keyPressed


		/* (non-Javadoc)
		 * @see java.awt.event.KeyListener#keyReleased(java.awt.event.KeyEvent)
		 */
		public void keyReleased(KeyEvent evt) {}

		
		/* (non-Javadoc)
		 * @see java.awt.event.KeyListener#keyTyped(java.awt.event.KeyEvent)
		 */
		public void keyTyped(KeyEvent evt) {}
	}
	// End of Interact Class

}
