[jsword-svn] bibledesktop/java/main/org/crosswire/bibledesktop/book/install s

jswordcvs at crosswire.org jswordcvs at crosswire.org
Mon Feb 14 19:09:01 MST 2005


Update of /cvs/jsword/bibledesktop/java/main/org/crosswire/bibledesktop/book/install
In directory www.crosswire.org:/tmp/cvs-serv5688/java/main/org/crosswire/bibledesktop/book/install

Added Files:
	SitesPane.properties IndexResolver.java 
	BookMetaDataTableModel.java InstallerFactoryComboBoxModel.java 
	EditSitePane.java SitePane.java 
	InstallManagerComboBoxModel.java EditSitePane.properties 
	Msg.java SitesPane.java BookNode.java SitePane.properties 
	Msg.properties 
Log Message:
Refactored the installer to a new package.
Fixed a bug in the indexer to use OSIS names.
Fixed a bug in the English names.
Did some optimizing.

--- NEW FILE: SitePane.properties ---

InstalledBooksLabel.Enabled=true
InstalledBooksLabel.Name=Installed Books:
InstalledBooksLabel.ShortDescription=Books that are installed.
InstalledBooksLabel.LongDescription=Show books that are installed.
InstalledBooksLabel.SmallIcon=
InstalledBooksLabel.LargeIcon=
InstalledBooksLabel.MnemonicKey=B
InstalledBooksLabel.AcceleratorKey=
InstalledBooksLabel.AcceleratorKey.Modifiers=

AvailableBooksLabel.Enabled=true
AvailableBooksLabel.Name=Available Books:
AvailableBooksLabel.ShortDescription=Books that are available.
AvailableBooksLabel.LongDescription=Show books that are available.
AvailableBooksLabel.SmallIcon=
AvailableBooksLabel.LargeIcon=
AvailableBooksLabel.MnemonicKey=B
AvailableBooksLabel.AcceleratorKey=
AvailableBooksLabel.AcceleratorKey.Modifiers=

SelectedBookLabel.Enabled=true
SelectedBookLabel.Name=Selected Book:
SelectedBookLabel.ShortDescription=The selected book.
SelectedBookLabel.LongDescription=The selected book.
SelectedBookLabel.SmallIcon=
SelectedBookLabel.LargeIcon=
SelectedBookLabel.MnemonicKey=S
SelectedBookLabel.AcceleratorKey=
SelectedBookLabel.AcceleratorKey.Modifiers=

Install.Enabled=false
Install.Name=Install
Install.ShortDescription=Install a book.
Install.LongDescription=Install the selected book
Install.MnemonicKey=I

InstallSearch.Enabled=false
InstallSearch.Name=Install with Search
InstallSearch.ShortDescription=Download a book and search index.
InstallSearch.LongDescription=Install the selected book along with a search index.
InstallSearch.MnemonicKey=S

Refresh.Enabled=true
Refresh.Name=Update Available Books
Refresh.ShortDescription=Refresh the list of books.
Refresh.LongDescription=Download a current listing of books.
Refresh.SmallIcon=
Refresh.LargeIcon=
Refresh.MnemonicKey=R
Refresh.AcceleratorKey=
Refresh.AcceleratorKey.Modifiers=

--- NEW FILE: BookNode.java ---
package org.crosswire.bibledesktop.book.install;

import java.util.Iterator;
import java.util.Set;

import javax.swing.tree.DefaultMutableTreeNode;

import org.crosswire.jsword.book.BookMetaDataSet;

/**
 * A Node for a book in a tree. It may be a property of a BookMetaData
 * or the BookMetaData itself.
 * 
 * <p><table border='1' cellPadding='3' cellSpacing='0'>
 * <tr><td bgColor='white' class='TableRowColor'><font size='-7'>
 *
 * Distribution Licence:<br />
 * JSword is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License,
 * version 2 as published by the Free Software Foundation.<br />
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.<br />
 * The License is available on the internet
 * <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, or by writing to:
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA<br />
 * The copyright to this program is held by it's authors.
 * </font></td></tr></table>
 * @see gnu.gpl.Licence
 * @author DM Smith [dmsmith555 at yahoo dot com]
 * @version $Id: BookNode.java,v 1.1 2005/02/15 02:08:59 dmsmith Exp $
 */
public class BookNode extends DefaultMutableTreeNode
{

    public BookNode(Object node, BookMetaDataSet books, Object [] grouping, int level)
    {
        setUserObject(node);
        if (level < grouping.length)
        {
            String key = (String) grouping[level];
            Set group = books.getGroup(key);
            Iterator iter = group.iterator();
            while (iter.hasNext())
            {
                String value = iter.next().toString();
                BookMetaDataSet subBooks = books.filter(key, value);
                add(new BookNode(value, subBooks, grouping, level + 1));
            }
        }
        else if (books != null)
        {
            Iterator iter = books.iterator();
            while (iter.hasNext())
            {
                add(new BookNode(iter.next(), null, grouping, level + 1));
            }
        }
    }

    /**
     * Serialization ID
     */
    private static final long serialVersionUID = 3256442525387602231L;
}

--- NEW FILE: SitePane.java ---
package org.crosswire.bibledesktop.book.install;

import java.awt.BorderLayout;
import java.awt.Component;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTable;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;

import org.crosswire.common.swing.ActionFactory;
import org.crosswire.common.swing.FixedSplitPane;
import org.crosswire.common.swing.MapTable;
import org.crosswire.common.swing.MapTableModel;
import org.crosswire.common.util.NetUtil;
import org.crosswire.common.util.Reporter;
import org.crosswire.jsword.book.BookList;
import org.crosswire.jsword.book.BookMetaData;
import org.crosswire.jsword.book.BookMetaDataSet;
import org.crosswire.jsword.book.Books;
import org.crosswire.jsword.book.BooksEvent;
import org.crosswire.jsword.book.BooksListener;
import org.crosswire.jsword.book.install.InstallException;
import org.crosswire.jsword.book.install.Installer;
import org.crosswire.jsword.util.IndexDownloader;

/**
 * A panel for use within a SitesPane to display one set of Books that are
 * installed or could be installed.
 *
 * <p><table border='1' cellPadding='3' cellSpacing='0'>
 * <tr><td bgColor='white' class='TableRowColor'><font size='-7'>
 *
 * Distribution Licence:<br />
 * JSword is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License,
 * version 2 as published by the Free Software Foundation.<br />
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.<br />
 * The License is available on the internet
 * <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, or by writing to:
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA<br />
 * The copyright to this program is held by it's authors.
 * </font></td></tr></table>
 * @see gnu.gpl.Licence
 * @author Joe Walker [joe at eireneh dot com]
 * @version $Id: SitePane.java,v 1.1 2005/02/15 02:08:59 dmsmith Exp $
 */
public class SitePane extends JPanel
{
    /**
     * For local installations
     */
    public SitePane()
    {
        this(null, INSTALLED_BOOKS_LABEL);
    }

    /**
     * For remote installations
     */
    public SitePane(Installer bookListInstaller)
    {
        this(bookListInstaller, AVAILABLE_BOOKS_LABEL);
    }

    /**
     * Internal ctor
     */
    private SitePane(Installer bookListInstaller, String labelAcronymn)
    {
        installer = bookListInstaller;

        actions = new ActionFactory(SitePane.class, this);

        BookList bl = installer;
        if (bl == null)
        {
            bl = Books.installed();
            bl.addBooksListener(new CustomBooksListener());
        }

        initialize(labelAcronymn, bl);
    }

    /**
     * Build the GUI components
     */
    private void initialize(String labelAcronymn, BookList books)
    {
        lblDesc = new JLabel();
        lblDesc.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 0));

        Component left = createAvailablePanel(labelAcronymn, books);
        Component right = createSelectedPanel();
        this.setLayout(new BorderLayout());
        this.add(lblDesc, BorderLayout.NORTH);
        this.add(createSplitPane(left, right), BorderLayout.CENTER);

        updateDescription();
    }

    /**
     *
     */
    private void updateDescription()
    {
        String desc = "#ERROR#"; //$NON-NLS-1$

        if (installer == null)
        {
            int bookCount = Books.installed().getBookMetaDatas().size();
            desc = Msg.INSTALLED_DESC.toString(new Object[] { new Integer(bookCount) });
        }
        else
        {
            int bookCount = installer.getBookMetaDatas().size();
            if (bookCount == 0)
            {
                desc = Msg.NONE_AVAILABLE_DESC.toString();
            }
            else
            {
                desc = Msg.AVAILABLE_DESC.toString(new Object[] { new Integer(bookCount) });
            }
        }

        lblDesc.setText(desc);
    }

    /**
     *
     */
    private Component createSplitPane(Component left, Component right)
    {
        JSplitPane split = new FixedSplitPane();
        split.setDividerLocation(0.3D);
        split.setResizeWeight(0.3D);
        split.setOrientation(JSplitPane.HORIZONTAL_SPLIT);
        split.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        split.setDividerSize(10);
        split.setLeftComponent(left);
        split.setRightComponent(right);
        return split;
    }

    /**
     *
     */
    private Component createAvailablePanel(String labelAcronymn, BookList books)
    {
        JLabel lblAvailable = actions.createJLabel(labelAcronymn);

        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout());
        panel.add(lblAvailable, BorderLayout.PAGE_START);
        panel.add(createScrolledTree(books), BorderLayout.CENTER);
        panel.add(createPanelActions(), BorderLayout.PAGE_END);

        // Tie the label's mnemonic to the tree
        lblAvailable.setLabelFor(treAvailable);

        return panel;
    }

    /**
     *
     */
    private Component createSelectedPanel()
    {
        emptyTableModel = new MapTableModel(null);
        tblSelected = new MapTable(emptyTableModel);
        JLabel lblSelected = actions.createJLabel(SELECTED_BOOK_LABEL);
        lblSelected.setLabelFor(tblSelected);

        JScrollPane scrSelected = new JScrollPane();
        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout());
        panel.add(lblSelected, BorderLayout.PAGE_START);
        panel.add(scrSelected, BorderLayout.CENTER);
        scrSelected.getViewport().add(tblSelected);
        return panel;
    }

    /**
     *
     */
    private Component createScrolledTree(BookList books)
    {
        treAvailable = new JTree();
        setTreeModel(books);
        // Add lines if viewed in Java Look & Feel
        treAvailable.putClientProperty("JTree.lineStyle", "Angled"); //$NON-NLS-1$ //$NON-NLS-2$
        treAvailable.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
        treAvailable.setCellEditor(null);
        treAvailable.setRootVisible(false);
        treAvailable.setShowsRootHandles(true);
        treAvailable.addTreeSelectionListener(new TreeSelectionListener()
        {
            public void valueChanged(TreeSelectionEvent ev)
            {
                selected();
            }
        });

        JScrollPane scroller = new JScrollPane();
        scroller.getViewport().add(treAvailable);

        return scroller;
    }

    private TreeModel createTreeModel(BookList books)
    {
        // return new BooksTreeModel(books);
        BookMetaDataSet bmds = new BookMetaDataSet(books.getBookMetaDatas());
        TreeNode bookRoot = new BookNode("root", bmds, new Object[] { BookMetaData.KEY_TYPE, BookMetaData.KEY_LANGUAGE }, 0); //$NON-NLS-1$
        return new DefaultTreeModel(bookRoot);  
    }

    // provide for backward compatibility
    private BookMetaData getBookMetaData(Object obj)
    {
        // new way
        if (obj instanceof DefaultMutableTreeNode)
        {
            obj = ((DefaultMutableTreeNode)obj).getUserObject();
        }
        // Old way
        if (obj instanceof BookMetaData)
        {
            return (BookMetaData) obj;
        }
        return null;
    }

    /**
     *
     */
    private Component createPanelActions()
    {
        JPanel panel = new JPanel();
        if (installer != null)
        {
            panel.add(new JButton(actions.getAction(INSTALL)));
            panel.add(new JButton(actions.getAction(INSTALL_SEARCH)));
            panel.add(new JButton(actions.getAction(REFRESH)));
        }
//        else
//        {
//            pnlActions.add(new JButton(actions.getAction(DELETE)));
//        }
        return panel;
    }

    /**
     * Delete the current book
     */
    public void doDelete()
    {
    }

    /**
     * Reload and redisplay the list of books
     */
    public void doRefresh()
    {
        if (installer != null)
        {
            try
            {
                installer.reloadBookList();
                setTreeModel(installer);
            }
            catch (InstallException ex)
            {
                Reporter.informUser(this, ex);
            }
        }
    }

    /**
     * Kick off the installer
     */
    public void doInstall()
    {
        if (installer != null)
        {
            TreePath path = treAvailable.getSelectionPath();
            if (path != null)
            {
                Object last = path.getLastPathComponent();
                BookMetaData name = getBookMetaData(last);

                try
                {
                    // Is the book already installed? Then nothing to do.
                    BookMetaData bmd = Books.installed().getBookMetaData(name.getName());
                    if (bmd != null && !installer.isNewer(bmd))
                    {
                        Reporter.informUser(this, Msg.INSTALLED, name.getName());
                        return;
                    }

                    float size = NetUtil.getSize(installer.toRemoteURL(name)) / 1024.0F;
                    if (JOptionPane.showConfirmDialog(this, Msg.SIZE.toString(new Object[] {name.getName(), new Float(size)}),
                                    Msg.CONFIRMATION_TITLE.toString(),
                                    JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION)
                    {
                        installer.install(name);
                    }
                }
                catch (InstallException ex)
                {
                    Reporter.informUser(this, ex);
                }
            }
        }
    }

    /**
     * Kick off the installer
     */
    public void doInstallSearch()
    {
        doInstall();

        TreePath path = treAvailable.getSelectionPath();
        if (path != null)
        {
            try
            {
                Object last = path.getLastPathComponent();
                BookMetaData bmd = getBookMetaData(last);
                IndexDownloader.downloadIndex(bmd, installer);
            }
            catch (Exception ex)
            {
                Reporter.informUser(this, ex);
            }
        }
    }

    /**
     * Something has been (un)selected in the tree
     */
    protected void selected()
    {
        TreePath path = treAvailable.getSelectionPath();

        boolean bookSelected = false;
        MapTableModel mtm = emptyTableModel;
        if (path != null)
        {
            Object last = path.getLastPathComponent();
            BookMetaData bmd = getBookMetaData(last);
            if (bmd != null)
            {
                mtm = new BookMetaDataTableModel(bmd);
                bookSelected = true;
            }
        }
        tblSelected.setModel(mtm);

        //actions.getAction(DELETE).setEnabled(bookSelected);
        actions.getAction(INSTALL).setEnabled(bookSelected);
        actions.getAction(INSTALL_SEARCH).setEnabled(bookSelected);
    }

    public void setTreeModel(BookList books)
    {
        treAvailable.setModel(createTreeModel(books));
    }
    
    /**
     * When new books are added we need to relfect the change in this tree.
     */
    private final class CustomBooksListener implements BooksListener
    {
        /* (non-Javadoc)
         * @see org.crosswire.jsword.book.BooksListener#bookAdded(org.crosswire.jsword.book.BooksEvent)
         */
        public void bookAdded(BooksEvent ev)
        {
            setTreeModel((BookList) ev.getSource());
        }

        /* (non-Javadoc)
         * @see org.crosswire.jsword.book.BooksListener#bookRemoved(org.crosswire.jsword.book.BooksEvent)
         */
        public void bookRemoved(BooksEvent ev)
        {
            setTreeModel((BookList) ev.getSource());
        }
    }


    private static final String INSTALLED_BOOKS_LABEL = "InstalledBooksLabel"; //$NON-NLS-1$
    private static final String AVAILABLE_BOOKS_LABEL = "AvailableBooksLabel"; //$NON-NLS-1$
    private static final String SELECTED_BOOK_LABEL = "SelectedBookLabel"; //$NON-NLS-1$
    private static final String REFRESH = "Refresh"; //$NON-NLS-1$
    private static final String INSTALL = "Install"; //$NON-NLS-1$
    private static final String INSTALL_SEARCH = "InstallSearch"; //$NON-NLS-1$
    //private static final String DELETE = "Delete"; //$NON-NLS-1$

    /**
     * From which we get our list of installable modules
     */
    protected Installer installer;

    /**
     * actions are held by this ActionFactory
     */
    private ActionFactory actions;

    /*
     * GUI Components
     */
    private JTree treAvailable = null;
    private JTable tblSelected = null;
    private MapTableModel emptyTableModel = null;
    private JLabel lblDesc = null;

    /**
     * Serialization ID
     */
    private static final long serialVersionUID = 3616445692051075634L;
}
--- NEW FILE: Msg.java ---
package org.crosswire.bibledesktop.book.install;

import org.crosswire.common.util.MsgBase;

/**
 * Compile safe Msg resource settings.
 * 
 * <p><table border='1' cellPadding='3' cellSpacing='0'>
 * <tr><td bgColor='white' class='TableRowColor'><font size='-7'>
 *
 * Distribution Licence:<br />
 * JSword is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License,
 * version 2 as published by the Free Software Foundation.<br />
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.<br />
 * The License is available on the internet
 * <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, or by writing to:
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA<br />
 * The copyright to this program is held by it's authors.
 * </font></td></tr></table>
 * @see gnu.gpl.Licence
 * @author Joe Walker [joe at eireneh dot com]
 * @version $Id: Msg.java,v 1.1 2005/02/15 02:08:59 dmsmith Exp $
 */
class Msg extends MsgBase
{
    static final Msg AVAILABLE_BOOKS = new Msg("SitesPane.AvailableBooks"); //$NON-NLS-1$
    static final Msg LOCAL_BOOKS = new Msg("SitesPane.Local"); //$NON-NLS-1$

    static final Msg SIZE = new Msg("SitePane.Size"); //$NON-NLS-1$
    static final Msg CONFIRMATION_TITLE = new Msg("SitePane.ConfirmationTitle"); //$NON-NLS-1$
    static final Msg INSTALLED = new Msg("SitePane.Installed"); //$NON-NLS-1$
    static final Msg INSTALLED_DESC = new Msg("SitePane.InstalledDesc"); //$NON-NLS-1$
    static final Msg AVAILABLE_DESC = new Msg("SitePane.AvailableDesc"); //$NON-NLS-1$
    static final Msg NONE_AVAILABLE_DESC = new Msg("SitePane.NoneAvailableDesc"); //$NON-NLS-1$

    static final Msg EDIT_SITE_TITLE = new Msg("EditSitePane.EditSitesTitle"); //$NON-NLS-1$
    static final Msg MISSING_SITE = new Msg("EditSitePane.MissingSite"); //$NON-NLS-1$
    static final Msg DUPLICATE_SITE = new Msg("EditSitePane.DuplicateSite"); //$NON-NLS-1$
    static final Msg NO_SELECTED_SITE = new Msg("EditSitePane.NoSelectedSite"); //$NON-NLS-1$
    static final Msg NO_SITE = new Msg("EditSitePane.NoSite"); //$NON-NLS-1$
    static final Msg CONFIRM_DELETE_SITE = new Msg("EditSitePane.ConfirmDeleteSite"); //$NON-NLS-1$
    static final Msg DELETE_SITE = new Msg("EditSitePane.DeleteSite"); //$NON-NLS-1$

    static final Msg HOW_MESSAGE_TITLE = new Msg("IndexResolver.HowMessageTitle"); //$NON-NLS-1$
    static final Msg HOW_MESSAGE = new Msg("IndexResolver.HowMessage"); //$NON-NLS-1$
    static final Msg HOW_GENERATE_TITLE = new Msg("IndexResolver.HowGenerateTitle"); //$NON-NLS-1$
    static final Msg HOW_GENERATE = new Msg("IndexResolver.HowGenerate"); //$NON-NLS-1$
    static final Msg HOW_SITE_TITLE = new Msg("IndexResolver.HowSiteTitle"); //$NON-NLS-1$
    static final Msg HOW_SITE = new Msg("IndexResolver.HowSite"); //$NON-NLS-1$
    static final Msg OPTION_DOWNLOAD = new Msg("IndexResolver.OptionDownload"); //$NON-NLS-1$
    static final Msg OPTION_GENERATE = new Msg("IndexResolver.OptionGenerate"); //$NON-NLS-1$
    static final Msg OPTION_CANCEL = new Msg("IndexResolver.OptionCancel"); //$NON-NLS-1$

    /**
     * Passthrough ctor
     */
    private Msg(String name)
    {
        super(name);
    }
}

--- NEW FILE: InstallerFactoryComboBoxModel.java ---
package org.crosswire.bibledesktop.book.install;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import javax.swing.AbstractListModel;
import javax.swing.ComboBoxModel;

import org.crosswire.jsword.book.install.InstallManager;

/**
 * A ComboBoxModel that displays all the known InstallerFactory names.
 *
 * <p><table border='1' cellPadding='3' cellSpacing='0'>
 * <tr><td bgColor='white' class='TableRowColor'><font size='-7'>
 *
 * Distribution Licence:<br />
 * JSword is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License,
 * version 2 as published by the Free Software Foundation.<br />
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.<br />
 * The License is available on the internet
 * <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, or by writing to:
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA<br />
 * The copyright to this program is held by it's authors.
 * </font></td></tr></table>
 * @see gnu.gpl.Licence
 * @author Joe Walker [joe at eireneh dot com]
 * @version $Id: InstallerFactoryComboBoxModel.java,v 1.1 2005/02/15 02:08:59 dmsmith Exp $
 */
public class InstallerFactoryComboBoxModel extends AbstractListModel implements ComboBoxModel
{
	/**
     * Simple ctor
     */
    public InstallerFactoryComboBoxModel(InstallManager imanager)
    {
        Set nameset = imanager.getInstallerFactoryNames();
        names = new ArrayList();
        names.addAll(nameset);
        Collections.sort(names);
    }

    /* (non-Javadoc)
     * @see javax.swing.ComboBoxModel#getSelectedItem()
     */
    public Object getSelectedItem()
    {
        return selection;
    }

    /* (non-Javadoc)
     * @see javax.swing.ComboBoxModel#setSelectedItem(java.lang.Object)
     */
    public void setSelectedItem(Object selection)
    {
        this.selection = selection;
    }

    /* (non-Javadoc)
     * @see javax.swing.ListModel#getSize()
     */
    public int getSize()
    {
        return names.size();
    }

    /* (non-Javadoc)
     * @see javax.swing.ListModel#getElementAt(int)
     */
    public Object getElementAt(int index)
    {
        return names.get(index);
    }

    /**
     * The list of installer names
     */
    private List names;

    /**
     * The currently selected object
     */
    private Object selection;

    /**
     * Serialization ID
     */
    private static final long serialVersionUID = 3258134643734885174L;
}

--- NEW FILE: EditSitePane.properties ---

Add.Enabled=true
Add.Name=Add
Add.ShortDescription=Add a new installation site.
Add.LongDescription=Add a site installation from which books can be downloaded.
Add.SmallIcon=
Add.LargeIcon=
Add.MnemonicKey=A
Add.AcceleratorKey=
Add.AcceleratorKey.Modifiers=

Edit.Enabled=true
Edit.Name=Edit
Edit.ShortDescription=Edit the current installation site.
Edit.LongDescription=Edit the details of the current download site.
Edit.SmallIcon=
Edit.LargeIcon=
Edit.MnemonicKey=E
Edit.AcceleratorKey=
Edit.AcceleratorKey.Modifiers=

Delete.Enabled=true
Delete.Name=Delete
Delete.ShortDescription=Delete the current installation site.
Delete.LongDescription=Delete the current installation site.
Delete.SmallIcon=
Delete.LargeIcon=
Delete.MnemonicKey=D
Delete.AcceleratorKey=
Delete.AcceleratorKey.Modifiers=

Name.Enabled=true
Name.Name=Site Name:
Name.ShortDescription=
Name.LongDescription=
Name.SmallIcon=
Name.LargeIcon=
Name.MnemonicKey=N
Name.AcceleratorKey=
Name.AcceleratorKey.Modifiers=

Type.Enabled=true
Type.Name=Site Type
Type.ShortDescription=
Type.LongDescription=
Type.SmallIcon=
Type.LargeIcon=
Type.MnemonicKey=Y
Type.AcceleratorKey=
Type.AcceleratorKey.Modifiers=

Reset.Enabled=true
Reset.Name=Reset
Reset.ShortDescription=Reset the details.
Reset.LongDescription=Reset the details.
Reset.SmallIcon=
Reset.LargeIcon=
Reset.MnemonicKey=R
Reset.AcceleratorKey=
Reset.AcceleratorKey.Modifiers=

Save.Enabled=true
Save.Name=Save
Save.ShortDescription=Save the current changes.
Save.LongDescription=Save the current changes.
Save.SmallIcon=
Save.LargeIcon=
Save.MnemonicKey=S
Save.AcceleratorKey=
Save.AcceleratorKey.Modifiers=

Close.Enabled=true
Close.Name=Close
Close.ShortDescription=
Close.LongDescription=
Close.SmallIcon=
Close.LargeIcon=
Close.MnemonicKey=C
Close.AcceleratorKey=
Close.AcceleratorKey.Modifiers=

--- NEW FILE: InstallManagerComboBoxModel.java ---
package org.crosswire.bibledesktop.book.install;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.swing.AbstractListModel;
import javax.swing.ComboBoxModel;

import org.crosswire.jsword.book.install.InstallManager;
import org.crosswire.jsword.book.install.InstallerEvent;
import org.crosswire.jsword.book.install.InstallerListener;

/**
 * A ListModel for a JList that uses the list of Installers given by the
 * InstallManager.
 *
 * <p><table border='1' cellPadding='3' cellSpacing='0'>
 * <tr><td bgColor='white' class='TableRowColor'><font size='-7'>
 *
 * Distribution Licence:<br />
 * JSword is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License,
 * version 2 as published by the Free Software Foundation.<br />
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.<br />
 * The License is available on the internet
 * <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, or by writing to:
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA<br />
 * The copyright to this program is held by it's authors.
 * </font></td></tr></table>
 * @see gnu.gpl.Licence
 * @author Joe Walker [joe at eireneh dot com]
 * @version $Id: InstallManagerComboBoxModel.java,v 1.1 2005/02/15 02:08:59 dmsmith Exp $
 */
public class InstallManagerComboBoxModel extends AbstractListModel implements ComboBoxModel
{
	/**
     * Simple ctor
     */
    public InstallManagerComboBoxModel(InstallManager imanager)
    {
        this.imanager = imanager;

        update(null);
        selection = getElementAt(0);

        imanager.addInstallerListener(new CustomInstallerListener());
    }

    /* (non-Javadoc)
     * @see javax.swing.ComboBoxModel#getSelectedItem()
     */
    public Object getSelectedItem()
    {
        return selection;
    }

    /* (non-Javadoc)
     * @see javax.swing.ComboBoxModel#setSelectedItem(java.lang.Object)
     */
    public void setSelectedItem(Object selection)
    {
        this.selection = selection;
    }

    /* (non-Javadoc)
     * @see javax.swing.ListModel#getSize()
     */
    public int getSize()
    {
        return names.size();
    }

    /* (non-Javadoc)
     * @see javax.swing.ListModel#getElementAt(int)
     */
    public final Object getElementAt(int index)
    {
        return names.get(index);
    }

    /**
     * Listens to the InstallManager for Installer changes
     */
    private class CustomInstallerListener implements InstallerListener
    {
        /* (non-Javadoc)
         * @see org.crosswire.jsword.book.install.InstallerListener#installerAdded(org.crosswire.jsword.book.install.InstallerEvent)
         */
        public void installerAdded(InstallerEvent ev)
        {
            update(ev);
        }

        /* (non-Javadoc)
         * @see org.crosswire.jsword.book.install.InstallerListener#installerRemoved(org.crosswire.jsword.book.install.InstallerEvent)
         */
        public void installerRemoved(InstallerEvent ev)
        {
            update(ev);
        }
    }

    /**
     * Simple way to avoid eclipse private/protected warning
     */
    protected final void update(InstallerEvent ev)
    {
        int oldmax = names.size();

        names.clear();
        names.addAll(imanager.getInstallers().keySet());
        Collections.sort(names);

        if (ev != null)
        {
            fireContentsChanged(ev.getSource(), 0, oldmax);
        }
    }

    /**
     * The currently selected object
     */
    private Object selection;

    /**
     * A cache of the names in the Install Manager
     */
    private List names = new ArrayList();

    /**
     * The install manager that we are representing
     */
    private InstallManager imanager;

    /**
     * Serialization ID
     */
    private static final long serialVersionUID = 3256725082729756980L;
}

--- NEW FILE: IndexResolver.java ---
package org.crosswire.bibledesktop.book.install;

import java.awt.BorderLayout;
import java.awt.Component;
import java.util.Iterator;
import java.util.Map;

import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

import org.crosswire.common.util.Logger;
import org.crosswire.jsword.book.BookMetaData;
import org.crosswire.jsword.book.install.InstallManager;
import org.crosswire.jsword.book.install.Installer;
import org.crosswire.jsword.book.search.IndexManagerFactory;
import org.crosswire.jsword.util.IndexDownloader;

/**
 * A class to prompt the user to download or create a search index and to do
 * carry out the users wishes.
 * 
 * <p><table border='1' cellPadding='3' cellSpacing='0'>
 * <tr><td bgColor='white' class='TableRowColor'><font size='-7'>
 *
 * Distribution Licence:<br />
 * JSword is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License,
 * version 2 as published by the Free Software Foundation.<br />
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.<br />
 * The License is available on the internet
 * <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, or by writing to:
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA<br />
 * The copyright to this program is held by it's authors.
 * </font></td></tr></table>
 * @see gnu.gpl.Licence
 * @author Joe Walker [joe at eireneh dot com]
 * @version $Id: IndexResolver.java,v 1.1 2005/02/15 02:08:59 dmsmith Exp $
 */
public class IndexResolver
{
    /**
     * Prevent instansiation
     */
    private IndexResolver()
    {
    }

    /**
     * The options that we show to the user for how to get a search index
     */
    private static Object[] options = new Object[]
    {
        Msg.OPTION_DOWNLOAD,
        Msg.OPTION_GENERATE,
        Msg.OPTION_CANCEL,
    };

    /**
     * @param parent
     * 
     */
    public static void scheduleIndex(BookMetaData bmd, Component parent)
    {
        String title = Msg.HOW_MESSAGE_TITLE.toString();
        Msg msg = Msg.HOW_MESSAGE;
        int choice = JOptionPane.showOptionDialog(parent, msg, title, JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]);

        switch (choice)
        {
            case 0: // download
                Installer installer = selectInstaller(parent);
                if (installer != null)
                {
                    try
                    {
                        IndexDownloader.downloadIndex(bmd, installer);
                    }
                    catch (Exception ex)
                    {
                        log.error("index download failed: ", ex); //$NON-NLS-1$
                        //Reporter.informUser(parent, ex);

                        String gtitle = Msg.HOW_GENERATE_TITLE.toString();
                        Msg gmsg = Msg.HOW_GENERATE;
                        int yn = JOptionPane.showConfirmDialog(parent, gmsg, gtitle, JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);

                        if (yn == JOptionPane.YES_OPTION)
                        {
                            IndexManagerFactory.getIndexManager().scheduleIndexCreation(bmd.getBook());
                        }
                    }
                }
                break;

            case 1: // generate
                IndexManagerFactory.getIndexManager().scheduleIndexCreation(bmd.getBook());
                break;

            default: // cancel
                break;
        }
    }
    
    /**
     * Pick an installer
     * @param parent A component to tie dialogs to
     * @return The chosen installer or null if the user cancelled.
     */
    private static Installer selectInstaller(Component parent)
    {
        // Pick an installer
        InstallManager insman = new InstallManager();
        Map installers = insman.getInstallers();
        Installer installer = null;
        if (installers.size() == 1)
        {
            Iterator it = installers.values().iterator();
            boolean hasNext = it.hasNext();
            assert hasNext;
            installer = (Installer) it.next();
        }
        else
        {
            JComboBox choice = new JComboBox(new InstallManagerComboBoxModel(insman));
            JLabel label = new JLabel(Msg.HOW_SITE.toString());
            JPanel panel = new JPanel(new BorderLayout());
            panel.add(label, BorderLayout.NORTH);
            panel.add(choice, BorderLayout.CENTER);

            String title = Msg.HOW_SITE_TITLE.toString();

            int yn = JOptionPane.showConfirmDialog(parent, panel, title, JOptionPane.YES_OPTION);
            if (yn == JOptionPane.YES_OPTION)
            {
                installer = (Installer) choice.getSelectedItem();
            }
        }

        return installer;
    }

    /**
     * The log stream
     */
    private static final Logger log = Logger.getLogger(IndexResolver.class);
}

--- NEW FILE: Msg.properties ---
# The naming convention for the keys in the file is ClassName.MessageName
# Where ClassName is the name of the class using the property.
# When the resource is used by more than one class it should be the one
# that the resource is most closely associated.
# The MessageName should be mixed case, with a leading capital.
# It should have no spaces or other punctuation (e.g. _, -, ', ...)

BookChooser.Cancel=Cancel
BookChooser.OK=OK
BookChooser.Title=Select a Bible

SitesPane.AvailableBooks=Available Books
SitesPane.Local=Installed Books

SitePane.Size={0} is {1,number,###,###,###.#}KB. Continue?
SitePane.ConfirmationTitle=Download Book
SitePane.Installed=Book already installed: {0}
SitePane.InstalledDesc={0} books installed.
SitePane.AvailableDesc={0} books available for download.
SitePane.NoneAvailableDesc=<html><b>Click ''Update Available Books'' to download an up to date book list.</b>

EditSitePane.EditSitesTitle=Edit Update Sites
EditSitePane.MissingSite=Missing site name
EditSitePane.DuplicateSite=Duplicate site name
EditSitePane.NoSelectedSite=No selected site to edit
EditSitePane.NoSite=No Site
EditSitePane.ConfirmDeleteSite=Are you sure you want to delete {0}
EditSitePane.DeleteSite=Delete Site?

IndexResolver.HowMessageTitle=Download or generate?
IndexResolver.HowMessage=Do you wish to try to download and index or generate one for yourself?\n\
	Downloading (2-3Mb) will take around 10 mins on a modem or 1 min on broadband.\n\
	Generating an index will take around 5 mins on an average computer.\n\
	Not all books have indexes so download may not be possible.
IndexResolver.HowSiteTitle=Which download site?
IndexResolver.HowSite=Which download site do you wish to use?
IndexResolver.HowGenerateTitle=Generate an index anyway?
IndexResolver.HowGenerate=Downloading failed.\n\
	Do you wish to generate an index anyway?
IndexResolver.OptionDownload=Download
IndexResolver.OptionGenerate=Generate
IndexResolver.OptionCancel=Cancel

--- NEW FILE: SitesPane.properties ---

SitesClose.Enabled=true
SitesClose.Name=OK
SitesClose.MnemonicKey=O

ManageSites.Enabled=true
ManageSites.Name=Edit Sites ...
ManageSites.MnemonicKey=S

--- NEW FILE: BookMetaDataTableModel.java ---
package org.crosswire.bibledesktop.book.install;

import org.crosswire.common.swing.MapTableModel;
import org.crosswire.jsword.book.BookMetaData;

/**
 * A TableModel that displays the data in a BookMetaData object.
 *
 * <p><table border='1' cellPadding='3' cellSpacing='0'>
 * <tr><td bgColor='white' class='TableRowColor'><font size='-7'>
 *
 * Distribution Licence:<br />
 * JSword is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License,
 * version 2 as published by the Free Software Foundation.<br />
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.<br />
 * The License is available on the internet
 * <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, or by writing to:
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA<br />
 * The copyright to this program is held by it's authors.
 * </font></td></tr></table>
 * @see gnu.gpl.Licence
 * @author Joe Walker [joe at eireneh dot com]
 * @version $Id: BookMetaDataTableModel.java,v 1.1 2005/02/15 02:08:59 dmsmith Exp $
 */
public class BookMetaDataTableModel extends MapTableModel
{
	/**
     * Simple ctor
     */
    public BookMetaDataTableModel()
    {
        setBookMetaData(null);
    }

    /**
     * Simple ctor with default BookMetaData
     */
    public BookMetaDataTableModel(BookMetaData bmd)
    {
        setBookMetaData(bmd);
    }

    /**
     * @return Returns the BookMetaData.
     */
    public BookMetaData getBookMetaData()
    {
        return bmd;
    }

    /**
     * @param bmd The BookMetaData to set.
     */
    public final void setBookMetaData(BookMetaData bmd)
    {
        if (bmd != this.bmd)
        {
            if (bmd == null)
            {
                setMap(null);
            }
            else
            {
                setMap(bmd.getProperties());
            }

            this.bmd = bmd;
        }
    }

    /**
     * The meta data that we are displaying
     */
    private BookMetaData bmd;

    /**
     * Serialization ID
     */
    private static final long serialVersionUID = 3257566222043460664L;
}

--- NEW FILE: EditSitePane.java ---
package org.crosswire.bibledesktop.book.install;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.beans.IntrospectionException;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JSplitPane;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.ListSelectionModel;
import javax.swing.WindowConstants;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import org.crosswire.common.swing.ActionFactory;
import org.crosswire.common.swing.BeanPanel;
import org.crosswire.common.swing.FixedSplitPane;
import org.crosswire.common.swing.GuiUtil;
import org.crosswire.common.util.Reporter;
import org.crosswire.jsword.book.install.InstallManager;
import org.crosswire.jsword.book.install.Installer;
import org.crosswire.jsword.book.install.InstallerFactory;

/**
 * An editor for the list of available update sites.
 *
 * <p><table border='1' cellPadding='3' cellSpacing='0'>
 * <tr><td bgColor='white' class='TableRowColor'><font size='-7'>
 *
 * Distribution Licence:<br />
 * JSword is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License,
 * version 2 as published by the Free Software Foundation.<br />
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.<br />
 * The License is available on the internet
 * <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, or by writing to:
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA<br />
 * The copyright to this program is held by it's authors.
 * </font></td></tr></table>
 * @see gnu.gpl.Licence
 * @author Joe Walker [joe at eireneh dot com]
 * @version $Id: EditSitePane.java,v 1.1 2005/02/15 02:08:59 dmsmith Exp $
 */
public class EditSitePane extends JPanel
{
    /**
     * This is the default constructor
     */
    public EditSitePane(InstallManager imanager)
    {
        this.imanager = imanager;
        userInitiated = true;

        init();
        setState(STATE_DISPLAY, null);
        select();
        setPreferredSize(new Dimension(480, 320));
    }

    /**
     * GUI init
     */
    private void init()
    {
        actions = new ActionFactory(EditSitePane.class, this);

        lstSite = new JList(new InstallManagerComboBoxModel(imanager));
        JScrollPane scrSite = new JScrollPane();
        scrSite.add(lstSite, null);
        scrSite.getViewport().add(lstSite, null);
        lstSite.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        lstSite.addListSelectionListener(new ListSelectionListener()
        {
            public void valueChanged(ListSelectionEvent ev)
            {
                if (ev.getValueIsAdjusting())
                {
                    return;
                }

                select();
            }
        });

        JButton btnAdd = new JButton(actions.getAction(ADD));
        JButton btnEdit = new JButton(actions.getAction(EDIT));
        JButton btnDelete = new JButton(actions.getAction(DELETE));

        JPanel pnlBtn1 = new JPanel();
        pnlBtn1.add(btnAdd, null);
        pnlBtn1.add(btnEdit, null);
        pnlBtn1.add(btnDelete, null);

        JPanel pnlSite = new JPanel();
        pnlSite.setLayout(new BorderLayout());
        pnlSite.add(scrSite, BorderLayout.CENTER);
        pnlSite.add(pnlBtn1, BorderLayout.SOUTH);

        txtName = new JTextField();
        txtName.setColumns(10);
        txtName.getDocument().addDocumentListener(new DocumentListener()
        {
            public void changedUpdate(DocumentEvent ev)
            {
                siteUpdate();
            }

            public void insertUpdate(DocumentEvent ev)
            {
                siteUpdate();
            }

            public void removeUpdate(DocumentEvent ev)
            {
                siteUpdate();
            }
        });

        JLabel lblName = actions.createJLabel(NAME);
        lblName.setLabelFor(txtName);

        cboType = new JComboBox(new InstallerFactoryComboBoxModel(imanager));
        cboType.setEditable(false);
        cboType.setSelectedIndex(0);
        cboType.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent ev)
            {
                newType();
            }
        });

        JLabel lblType = actions.createJLabel(TYPE);
        lblType.setLabelFor(cboType);

        lblMesg = new JLabel();
        lblMesg.setText(BLANK_STRING);

        JButton btnReset = new JButton(actions.getAction(RESET));

        JButton btnSave = new JButton(actions.getAction(SAVE));

        JPanel pnlBtn2 = new JPanel();
        pnlBtn2.add(btnSave, null);
        pnlBtn2.add(btnReset, null);

        pnlBean = new BeanPanel();
        JPanel pnlMain = new JPanel();
        pnlMain.setLayout(new GridBagLayout());
        pnlMain.add(lblMesg, new GridBagConstraints(0, 0, 2, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(10, 10, 10, 10), 0, 0));
        pnlMain.add(lblName, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(2, 10, 2, 2), 0, 0));
        pnlMain.add(txtName, new GridBagConstraints(1, 1, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 10), 0, 0));
        pnlMain.add(lblType, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(2, 10, 2, 2), 0, 0));
        pnlMain.add(cboType, new GridBagConstraints(1, 2, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(2, 2, 2, 10), 0, 0));
        pnlMain.add(new JSeparator(), new GridBagConstraints(0, 3, 2, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(10, 10, 10, 10), 0, 0));
        pnlMain.add(pnlBean, new GridBagConstraints(0, 4, 2, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
        pnlMain.add(pnlBtn2, new GridBagConstraints(0, 5, 2, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));

        JSplitPane sptMain = new FixedSplitPane();
        sptMain.setOrientation(JSplitPane.HORIZONTAL_SPLIT);
        // Make resizing affect the right only
        sptMain.setResizeWeight(0.0D);
        sptMain.setLeftComponent(pnlSite);
        sptMain.setRightComponent(pnlMain);

        this.setLayout(new BorderLayout());
        this.add(sptMain, BorderLayout.CENTER);

        btnClose = new JButton(actions.getAction(CLOSE));

        pnlAction = new JPanel();
        pnlAction.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        pnlAction.setLayout(new FlowLayout(FlowLayout.RIGHT));
        pnlAction.add(btnClose, null);
    }

    /**
     * Open us in a new modal dialog window
     * @param parent The component to which to attach the new dialog
     */
    public void showInDialog(Component parent)
    {
        dlgMain = new JDialog(JOptionPane.getFrameForComponent(parent));

        ActionListener closer = new ActionListener()
        {
            public void actionPerformed(ActionEvent ev)
            {
                doClose();
            }
        };

        KeyStroke esc = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);

        dlgMain.getContentPane().setLayout(new BorderLayout());
        dlgMain.getContentPane().add(new JPanel(), BorderLayout.NORTH);
        dlgMain.getContentPane().add(pnlAction, BorderLayout.SOUTH);
        dlgMain.getContentPane().add(this, BorderLayout.CENTER);
        dlgMain.getContentPane().add(new JPanel(), BorderLayout.EAST);
        dlgMain.getContentPane().add(new JPanel(), BorderLayout.WEST);
        dlgMain.getRootPane().setDefaultButton(btnClose);
        dlgMain.getRootPane().registerKeyboardAction(closer, esc, JComponent.WHEN_IN_FOCUSED_WINDOW);
        dlgMain.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        dlgMain.setTitle(Msg.EDIT_SITE_TITLE.toString());
        dlgMain.setModal(true);

        GuiUtil.restrainedPack(dlgMain, 0.5f, 0.75f);
        GuiUtil.centerWindow(dlgMain);
        dlgMain.setVisible(true);
    }

    /**
     * Close the window, and save the install manager state
     */
    public void doClose()
    {
        imanager.save();
        dlgMain.dispose();
    }

    /**
     * The name field has been updated, so we need to check the entry is valid
     */
    protected void siteUpdate()
    {
        if (txtName.isEditable())
        {
            String name = txtName.getText().trim();

            if (name.length() == 0)
            {
                setState(STATE_EDIT_ERROR, Msg.MISSING_SITE.toString());
                return;
            }

            if (imanager.getInstaller(name) != null)
            {
                setState(STATE_EDIT_ERROR, Msg.DUPLICATE_SITE.toString());
                return;
            }

            setState(STATE_EDIT_OK, EMPTY_STRING);
        }
    }

    /**
     * The installer type combo box has been changed
     */
    protected void newType()
    {
        if (userInitiated)
        {
            String type = (String) cboType.getSelectedItem();
            InstallerFactory ifactory = imanager.getInstallerFactory(type);
            Installer installer = ifactory.createInstaller();

            setBean(installer);
        }
    }

    /**
     * Someone has picked a new installer
     */
    protected final void select()
    {
        String name = (String) lstSite.getSelectedValue();
        if (name == null)
        {
            actions.getAction(EDIT).setEnabled(false);
            clear();
        }
        else
        {
            actions.getAction(EDIT).setEnabled(true);

            Installer installer = imanager.getInstaller(name);
            display(name, installer);
        }

        // Since setting the display undoes any work done to set the edit state
        // of the bean panel we need to redo it here. Since we are always in
        // display mode at this point, this is fairly easy.
        pnlBean.setEditable(false);
    }

    /**
     * Add a new installer to the list
     */
    public void doAdd()
    {
        newType();

        editName = null;
        editInstaller = null;

        // We need to call setState() to enable the text boxes so that
        // siteUpdate() works properly
        setState(STATE_EDIT_OK, null);
        siteUpdate();

        Window window = GuiUtil.getWindow(this);
        GuiUtil.restrainedRePack(window);
    }

    /**
     * Move the selected installer to the installer edit panel
     */
    public void doEdit()
    {
        String name = (String) lstSite.getSelectedValue();
        if (name == null)
        {
            JOptionPane.showMessageDialog(this, Msg.NO_SELECTED_SITE.toString(), Msg.NO_SITE.toString(), JOptionPane.INFORMATION_MESSAGE);
            return;
        }

        editName = name;
        editInstaller = imanager.getInstaller(name);

        imanager.removeInstaller(name);

        setState(STATE_EDIT_OK, null);
        siteUpdate();

        txtName.grabFocus();
    }

    /**
     * Delete the selected installer from the list (on the left hand side)
     */
    public void doDelete()
    {
        String name = (String) lstSite.getSelectedValue();
        if (name == null)
        {
            return;
        }

        if (JOptionPane.showConfirmDialog(this, Msg.CONFIRM_DELETE_SITE.toString(name), Msg.DELETE_SITE.toString(), JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION)
        {
            imanager.removeInstaller(name);
        }

        clear();
        setState(STATE_DISPLAY, null);
    }

    /**
     * End editing the current installer
     */
    public void doReset()
    {
        if (editName != null)
        {
            imanager.addInstaller(editName, editInstaller);
        }

        clear();
        editName = null;
        editInstaller = null;

        setState(STATE_DISPLAY, EMPTY_STRING);
        select();
    }

    /**
     * Save the current installer to the list of installers
     */
    public void doSave()
    {
        String name = txtName.getText();
        Installer installer = (Installer) pnlBean.getBean();
        imanager.addInstaller(name, installer);

        clear();
        editName = null;
        editInstaller = null;

        setState(STATE_DISPLAY, EMPTY_STRING);
        select();
    }

    /**
     * Set the various gui elements depending on the current edit mode
     */
    private void setState(int state, String message)
    {
        switch (state)
        {
        case STATE_DISPLAY:
            actions.getAction(ADD).setEnabled(true);
            actions.getAction(DELETE).setEnabled(true);
            actions.getAction(EDIT).setEnabled(true);
            lstSite.setEnabled(true);

            actions.getAction(RESET).setEnabled(false);
            actions.getAction(SAVE).setEnabled(false);

            actions.getAction(CLOSE).setEnabled(true);

            txtName.setEditable(false);
            cboType.setEnabled(false);
            pnlBean.setEditable(false);

            break;

        case STATE_EDIT_OK:
        case STATE_EDIT_ERROR:
            actions.getAction(ADD).setEnabled(false);
            actions.getAction(DELETE).setEnabled(false);
            actions.getAction(EDIT).setEnabled(false);
            lstSite.setEnabled(false);

            actions.getAction(RESET).setEnabled(true);
            actions.getAction(SAVE).setEnabled(state == STATE_EDIT_OK);
            pnlBean.setEditable(true);

            actions.getAction(CLOSE).setEnabled(false);

            txtName.setEditable(true);
            cboType.setEnabled(true);
            pnlBean.setEditable(true);

            break;

        default:
            assert false : state;
        }

        if (message == null || message.trim().length() == 0)
        {
            lblMesg.setText(BLANK_STRING);
        }
        else
        {
            lblMesg.setText(message);
        }
    }

    /**
     * Set the display in the RHS to the given installer
     */
    private void display(String name, Installer installer)
    {
        txtName.setText(name);

        String type = imanager.getFactoryNameForInstaller(installer);
        userInitiated = false;
        cboType.setSelectedItem(type);
        userInitiated = true;

        setBean(installer);
    }

    /**
     * Clear the display in the RHS of any installers
     */
    private void clear()
    {
        try
        {
            txtName.setText(EMPTY_STRING);
            pnlBean.setBean(null);
        }
        catch (IntrospectionException ex)
        {
            Reporter.informUser(this, ex);
        }
    }

    /**
     * Convenience method to allow us to change the type of the current
     * installer.
     * @param installer The new installer to introspect
     */
    private void setBean(Installer installer)
    {
        try
        {
            pnlBean.setBean(installer);
        }
        catch (IntrospectionException ex)
        {
            Reporter.informUser(this, ex);
        }

        Window window = GuiUtil.getWindow(this);
        GuiUtil.restrainedRePack(window);
    }

    private static final String ADD = "Add"; //$NON-NLS-1$
    private static final String EDIT = "Edit"; //$NON-NLS-1$
    private static final String DELETE = "Delete"; //$NON-NLS-1$
    private static final String NAME = "Name"; //$NON-NLS-1$
    private static final String TYPE = "Type"; //$NON-NLS-1$
    private static final String RESET = "Reset"; //$NON-NLS-1$
    private static final String SAVE = "Save"; //$NON-NLS-1$
    private static final String CLOSE = "Close"; //$NON-NLS-1$
    private static final String EMPTY_STRING = ""; //$NON-NLS-1$
    private static final String BLANK_STRING = " "; //$NON-NLS-1$

    /**
     * The state is viewing a site
     */
    private static final int STATE_DISPLAY = 0;

    /**
     * The state is editing a site (syntactically valid)
     */
    private static final int STATE_EDIT_OK = 1;

    /**
     * The state is editing a site (syntactically invalid)
     */
    private static final int STATE_EDIT_ERROR = 2;

    /**
     * The model that we are providing a view/controller for
     */
    private InstallManager imanager;

    /**
     * If we are editing an installer, we need to know it's original name
     * in case someone clicks cancel.
     */
    private String editName;

    /**
     * If we are editing an installer, we need to know it's original value
     * in case someone clicks cancel.
     */
    private Installer editInstaller;

    /**
     * Edits to the type combo box mean different things depending on
     * whether it was triggered by the user or the application.
     */
    private boolean userInitiated;

    /*
     * The ActionFactory holding the actions used by this
     * EditSite.
     */
    private ActionFactory actions;

    /*
     * GUI Components for the list of sites
     */
    private JList lstSite;

    /*
     * GUI Components for the site view/edit area
     */
    private JLabel lblMesg;
    private JTextField txtName;
    private JComboBox cboType;
    private BeanPanel pnlBean;

    /*
     * Components for the dialog box including the button bar at the bottom.
     * These are separaed in this way in case this component is reused in a
     * larger context.
     */
    protected JDialog dlgMain;
    private JButton btnClose;
    private JPanel pnlAction;

    /**
     * Serialization ID
     */
    private static final long serialVersionUID = 3256446910616057650L;
}

--- NEW FILE: SitesPane.java ---
package org.crosswire.bibledesktop.book.install;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Iterator;
import java.util.Map;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;

import org.crosswire.common.progress.swing.JobsProgressBar;
import org.crosswire.common.swing.ActionFactory;
import org.crosswire.jsword.book.install.InstallManager;
import org.crosswire.jsword.book.install.Installer;
import org.crosswire.jsword.book.install.InstallerEvent;
import org.crosswire.jsword.book.install.InstallerListener;

/**
 * A panel for use within a SitesPane to display one set of Books that are
 * installed or could be installed.
 * <p>so start one of these call:
 * <pre>
 * sites = new SitesPane();
 * sites.showInDialog(parent);
 * </pre>
 *
 * <p><table border='1' cellPadding='3' cellSpacing='0'>
 * <tr><td bgColor='white' class='TableRowColor'><font size='-7'>
 *
 * Distribution Licence:<br />
 * JSword is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public License,
 * version 2 as published by the Free Software Foundation.<br />
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.<br />
 * The License is available on the internet
 * <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, or by writing to:
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA<br />
 * The copyright to this program is held by it's authors.
 * </font></td></tr></table>
 * @see gnu.gpl.Licence
 * @author Joe Walker [joe at eireneh dot com]
 * @version $Id: SitesPane.java,v 1.1 2005/02/15 02:08:59 dmsmith Exp $
 */
public class SitesPane extends JPanel
{
    /**
     * Simple ctor
     */
    public SitesPane()
    {
        init();

        imanager = new InstallManager();
        installers = imanager.getInstallers();

        addAllInstallers();

        imanager.addInstallerListener(new InstallerListener()
        {
            /* (non-Javadoc)
             * @see org.crosswire.jsword.book.install.InstallerListener#installerAdded(org.crosswire.jsword.book.install.InstallerEvent)
             */
            public void installerAdded(InstallerEvent ev)
            {
                Installer installer = ev.getInstaller();
                String name = imanager.getInstallerNameForInstaller(installer);

                SitePane site = new SitePane(installer);
                tabMain.add(name, site);
            }

            /* (non-Javadoc)
             * @see org.crosswire.jsword.book.install.InstallerListener#installerRemoved(org.crosswire.jsword.book.install.InstallerEvent)
             */
            public void installerRemoved(InstallerEvent ev)
            {
                // This gets tricky because if you add a site with a new name
                // but the same details as an old one, then the old name goes
                // so we can't get the old name to remove it's tab (and anyway
                // we would have to do a search through all the tabs to find it
                // by name)
                // So we just nuke all the tabs and re-create them
                removeAllInstallers();
                addAllInstallers();
            }
        });
    }

    /**
     * Build the GUI components
     */
    private void init()
    {
        actions = new ActionFactory(SitesPane.class, this);

        tabMain = new JTabbedPane();
        this.setLayout(new BorderLayout());
        this.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        this.add(tabMain, BorderLayout.CENTER);
        this.add(new JobsProgressBar(true), BorderLayout.SOUTH);
    }

    /**
     * Re-create the list of installers
     */
    protected final void addAllInstallers()
    {
        // Now add panels for book installation sites
        for (Iterator it = installers.keySet().iterator(); it.hasNext(); )
        {
            String name = (String) it.next();
            Installer installer = (Installer) installers.get(name);

            SitePane site = new SitePane(installer);
            tabMain.add(name, site);
        }

        // Add the panel for the locally installed books
        tabMain.add(Msg.LOCAL_BOOKS.toString(), new SitePane());
    }

    /**
     * Remove all the non-local installers
     */
    protected void removeAllInstallers()
    {
        tabMain.removeAll();
    }

    /**
     * Add a site to the list of install sources.
     */
    public void doManageSites()
    {
        EditSitePane edit = new EditSitePane(imanager);
        edit.showInDialog(this);
    }

    /**
     * We are done, close the window
     */
    public void doSitesClose()
    {
        if (dlgMain != null)
        {
            dlgMain.setVisible(false);
        }
    }

    /**
     * Open this Panel in it's own dialog box.
     */
    public void showInDialog(Component parent)
    {
        dlgMain = new JDialog(JOptionPane.getFrameForComponent(parent));
        dlgMain.setSize(new Dimension(750, 500));
        dlgMain.getContentPane().setLayout(new BorderLayout());
        dlgMain.getContentPane().add(this, BorderLayout.CENTER);
        dlgMain.getContentPane().add(createButtons(), BorderLayout.SOUTH);
        dlgMain.setTitle(Msg.AVAILABLE_BOOKS.toString());
        dlgMain.setModal(true);
        dlgMain.addWindowListener(new WindowAdapter()
        {
            public void windowClosed(WindowEvent ev)
            {
                doSitesClose();
            }
        });
        dlgMain.setLocationRelativeTo(parent);
        dlgMain.setVisible(true);
    }

    /**
     *
     */
    private Component createButtons()
    {
        if (pnlButtons == null)
        {
            JButton btnOK = new JButton(actions.getAction(CLOSE));

            JButton btnAdd = new JButton(actions.getAction(EDIT_SITE));

            pnlButtons = new JPanel();
            pnlButtons.setLayout(new FlowLayout(FlowLayout.RIGHT));
            pnlButtons.add(btnAdd, null);
            pnlButtons.add(btnOK);
        }
        return pnlButtons;

    }

    private static final String CLOSE = "SitesClose"; //$NON-NLS-1$
    private static final String EDIT_SITE = "ManageSites"; //$NON-NLS-1$

    /**
     * The known installers fetched from InstallManager
     */
    private Map installers;

    /**
     * The current installer
     */
    protected InstallManager imanager;

    private ActionFactory actions;

    /*
     * GUI Components
     */
    private JDialog dlgMain;
    private JPanel pnlButtons;
    protected JTabbedPane tabMain;

    /**
     * Serialization ID
     */
    private static final long serialVersionUID = 3258126947069605936L;
}



More information about the jsword-svn mailing list