net.sf.jhylafax.addressbook.AddressBook.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.jhylafax.addressbook.AddressBook.java

Source

/**
 * JHylaFax - A java client for HylaFAX.
 *
 * Copyright (C) 2005 by Steffen Pingel <steffenp@gmx.de>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
package net.sf.jhylafax.addressbook;

import static net.sf.jhylafax.JHylaFAX.i18n;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Insets;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.JToolBar;
import javax.swing.JTree;
import javax.swing.ListSelectionModel;
import javax.swing.WindowConstants;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import net.sf.jhylafax.JHylaFAX;
import net.sf.jhylafax.LocaleChangeListener;
import net.sf.jhylafax.Settings;
import net.wimpi.pim.Pim;
import net.wimpi.pim.contact.db.ContactDatabase;
import net.wimpi.pim.contact.db.ContactGroup;
import net.wimpi.pim.contact.facades.SimpleContact;
import net.wimpi.pim.contact.io.ContactMarshaller;
import net.wimpi.pim.contact.io.ContactUnmarshaller;
import net.wimpi.pim.contact.model.Contact;
import net.wimpi.pim.factory.ContactIOFactory;
import net.wimpi.pim.factory.ContactModelFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xnap.commons.gui.Builder;
import org.xnap.commons.gui.ColoredTable;
import org.xnap.commons.gui.Dialogs;
import org.xnap.commons.gui.EraseTextFieldAction;
import org.xnap.commons.gui.ErrorDialog;
import org.xnap.commons.gui.ToolBarButton;
import org.xnap.commons.gui.action.AbstractXNapAction;
import org.xnap.commons.gui.table.StringCellRenderer;
import org.xnap.commons.gui.table.TableLayoutManager;
import org.xnap.commons.gui.table.TableSorter;
import org.xnap.commons.gui.util.DoubleClickListener;
import org.xnap.commons.gui.util.GUIHelper;
import org.xnap.commons.gui.util.IconHelper;
import org.xnap.commons.gui.util.PopupListener;
import org.xnap.commons.io.FileExtensionFilter;
import org.xnap.commons.settings.SettingStore;
import com.jgoodies.forms.builder.DefaultFormBuilder;
import com.jgoodies.forms.layout.FormLayout;

public class AddressBook extends JFrame implements ListSelectionListener, LocaleChangeListener {

    private final static Log logger = LogFactory.getLog(AddressBook.class);
    private static final String[] DEFAULT_CONTACT_TABLE_COLUMNS = new String[] { "displayName", "company",
            "faxNumber" };

    /**
     * @param args
     */
    public static void main(String[] args) {
        final AddressBook app = new AddressBook();
        app.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        app.restoreLayout(new SettingStore(Settings.backstore));

        try {
            File file = JHylaFAX.getAddressBookFile();
            if (file.exists()) {
                app.load(file);
            }
        } catch (Exception e) {
            ErrorDialog.showError(null, i18n.tr("Could not load address book"), i18n.tr("JHylaFAX Error"), e);
        }

        app.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent event) {
                try {
                    File file = JHylaFAX.getAddressBookFile();
                    app.store(file);
                } catch (Exception e) {
                    ErrorDialog.showError(null, i18n.tr("Could not store address book"), i18n.tr("JHylaFAX Error"),
                            e);
                }
            }
        });

        app.setVisible(true);
    }

    private JMenu addressBookMenu;
    private JTree addressBookTree;
    private DefaultTreeModel addressBookTreeModel;
    private CloseAction closeAction;
    private JTable contactTable;
    private AddressTableModel contactTableModel;
    private DeleteAction deleteAction;
    private Action doubleClickAction;
    private EditAction editAction;
    private ExportAction exportAction;
    private FilterAction filterAction;
    private ImportAction importAction;
    private ContactCollection localAddressBook;
    private JToolBar mainToolBar;
    private NewAction newAction;
    private DefaultMutableTreeNode rootNode;
    private JTextField searchTextField;
    private JSplitPane horizontalSplitPane;
    private JLabel filterLabel;
    private TableLayoutManager contactTableLayoutManager;

    public AddressBook() {
        ContactModelFactory cmf = Pim.getContactModelFactory();

        JHylaFAX.initializeToolkit();
        initialize();

        ContactDatabase contactDatabase = Pim.getContactDBFactory().createContactDatabase();

        // initialize tree content
        DefaultMutableTreeNode localAddressBookTreeNode = new DefaultMutableTreeNode();
        localAddressBook = new ContactCollection(contactDatabase, i18n.tr("Local Address Book"));
        localAddressBookTreeNode.setUserObject(localAddressBook);
        rootNode.add(localAddressBookTreeNode);
        addressBookTree.expandPath(new TreePath(rootNode));
        addressBookTree.setSelectionRow(0);

        updateActions();
    }

    public void exportContacts(File file, SimpleContact[] contacts) throws IOException {
        FileOutputStream out = new FileOutputStream(file);
        try {
            ContactIOFactory factory = Pim.getContactIOFactory();
            ContactMarshaller marshaller = factory.createContactMarshaller();
            marshaller.setEncoding("UTF-8");
            for (SimpleContact contact : contacts) {
                marshaller.marshallContact(out, contact.getContact());
            }
        } finally {
            out.close();
        }
    }

    public SimpleContact[] getAlllContacts() {
        TableSorter sorter = (TableSorter) contactTable.getModel();
        SimpleContact[] result = new SimpleContact[sorter.getRowCount()];
        for (int i = 0; i < result.length; i++) {
            int row = sorter.mapToIndex(i);
            result[i] = contactTableModel.getContact(row);
        }
        return result;
    }

    public ContactCollection getSelectedContactCollection() {
        DefaultMutableTreeNode node = (DefaultMutableTreeNode) addressBookTree.getSelectionPath()
                .getLastPathComponent();
        return (ContactCollection) node.getUserObject();
    }

    public SimpleContact[] getSelectedContacts() {
        int[] rows = contactTable.getSelectedRows();
        if (rows.length == 0) {
            return new SimpleContact[0];
        }
        SimpleContact[] result = new SimpleContact[rows.length];
        for (int i = 0; i < rows.length; i++) {
            int row = ((TableSorter) contactTable.getModel()).mapToIndex(rows[i]);
            result[i] = contactTableModel.getContact(row);
        }
        return result;
    }

    public SimpleContact[] importVCardContacts(File file) throws IOException {
        FileInputStream in = new FileInputStream(file);
        try {
            ContactIOFactory factory = Pim.getContactIOFactory();
            ContactUnmarshaller unmarshaller = factory.createContactUnmarshaller();
            unmarshaller.setEncoding("UTF-8");
            Contact[] contacts = unmarshaller.unmarshallContacts(in);
            if (contacts != null) {
                SimpleContact[] result = new SimpleContact[contacts.length];
                for (int i = 0; i < contacts.length; i++) {
                    SimpleContact contact = new SimpleContact(contacts[i]);
                    getSelectedContactCollection().add(contact);
                    result[i] = contact;
                }
                return result;
            }
            return new SimpleContact[0];
        } finally {
            in.close();
        }
    }

    public SimpleContact[] importCSVContacts(File file, String separator) throws IOException {
        BufferedReader in = new BufferedReader(new FileReader(file));
        try {
            List<SimpleContact> contacts = new ArrayList<SimpleContact>();
            String line;
            while ((line = in.readLine()) != null) {
                SimpleContact contact = new SimpleContact();
                StringTokenizer t = new StringTokenizer(line, separator);
                if (t.hasMoreTokens())
                    contact.setFirstname(t.nextToken());
                if (t.hasMoreTokens())
                    contact.setLastname(t.nextToken());
                if (t.hasMoreTokens())
                    contact.setFaxNumber(t.nextToken());
                if (t.hasMoreTokens())
                    contact.setCompany(t.nextToken());
                getSelectedContactCollection().add(contact);
                contacts.add(contact);
            }
            return contacts.toArray(new SimpleContact[0]);
        } finally {
            in.close();
        }
    }

    private void initialize() {
        initializeActions();
        initializeShortCuts();
        initializeMenuBar();
        initializeContent();
        initializeToolBar();

        updateLabels();
    }

    private void initializeActions() {
        newAction = new NewAction();
        editAction = new EditAction();
        deleteAction = new DeleteAction();
        importAction = new ImportAction();
        exportAction = new ExportAction();
        closeAction = new CloseAction();
        filterAction = new FilterAction();
    }

    private void initializeContent() {
        horizontalSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
        horizontalSplitPane.setBorder(GUIHelper.createEmptyBorder(5));

        rootNode = new DefaultMutableTreeNode();
        addressBookTreeModel = new DefaultTreeModel(rootNode);
        addressBookTree = new JTree(addressBookTreeModel);
        addressBookTree.setRootVisible(false);
        addressBookTree.setCellRenderer(new ContactCollectionCellRenderer());
        horizontalSplitPane.add(new JScrollPane(addressBookTree));

        JPanel contactPanel = new JPanel();
        contactPanel.setLayout(new BorderLayout(0, 10));
        horizontalSplitPane.add(contactPanel);

        DefaultFormBuilder builder = new DefaultFormBuilder(
                new FormLayout("min, 3dlu, min, 3dlu, pref:grow, 3dlu, min", ""));
        contactPanel.add(builder.getPanel(), BorderLayout.NORTH);

        searchTextField = new JTextField(10);
        EraseTextFieldAction eraseAction = new EraseTextFieldAction(searchTextField) {
            public void actionPerformed(ActionEvent event) {
                super.actionPerformed(event);
                filterAction.actionPerformed(event);
            };
        };
        builder.append(new TabTitleButton(eraseAction));
        filterLabel = new JLabel();
        builder.append(filterLabel);
        builder.append(searchTextField);
        GUIHelper.bindEnterKey(searchTextField, filterAction);

        builder.append(Builder.createButton(filterAction));

        JPopupMenu tablePopupMenu = new JPopupMenu();
        tablePopupMenu.add(Builder.createMenuItem(newAction));
        tablePopupMenu.addSeparator();
        tablePopupMenu.add(Builder.createMenuItem(editAction));
        tablePopupMenu.addSeparator();
        tablePopupMenu.add(Builder.createMenuItem(deleteAction));

        contactTableModel = new AddressTableModel();
        TableSorter sorter = new TableSorter(contactTableModel);
        contactTable = new ColoredTable(sorter);
        contactTableLayoutManager = new TableLayoutManager(contactTable);
        contactTableLayoutManager.addColumnProperties("displayName", "", 180, true);
        contactTableLayoutManager.addColumnProperties("company", "", 80, true);
        contactTableLayoutManager.addColumnProperties("faxNumber", "", 60, true);
        contactTableLayoutManager.initializeTableLayout();
        contactPanel.add(new JScrollPane(contactTable), BorderLayout.CENTER);

        contactTable.setShowVerticalLines(true);
        contactTable.setShowHorizontalLines(false);
        contactTable.setAutoCreateColumnsFromModel(true);
        contactTable.setIntercellSpacing(new java.awt.Dimension(2, 1));
        contactTable.setBounds(0, 0, 50, 50);
        contactTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
        contactTable.getSelectionModel().addListSelectionListener(this);
        contactTable.addMouseListener(new PopupListener(tablePopupMenu));
        contactTable.addMouseListener(new DoubleClickListener(new TableDoubleClickAction()));
        contactTable.setTransferHandler(new ContactTransferHandler());
        contactTable.setDragEnabled(true);

        contactTable.setDefaultRenderer(String.class, new StringCellRenderer());

        getContentPane().setLayout(new BorderLayout());
        getContentPane().add(horizontalSplitPane, BorderLayout.CENTER);
    }

    private void initializeMenuBar() {
        JMenuBar menuBar = new JMenuBar();
        setJMenuBar(menuBar);

        addressBookMenu = new JMenu();
        menuBar.add(addressBookMenu);
        addressBookMenu.add(Builder.createMenuItem(newAction));
        addressBookMenu.addSeparator();
        addressBookMenu.add(Builder.createMenuItem(editAction));
        addressBookMenu.addSeparator();
        addressBookMenu.add(Builder.createMenuItem(deleteAction));
        addressBookMenu.addSeparator();
        addressBookMenu.add(Builder.createMenuItem(importAction));
        addressBookMenu.add(Builder.createMenuItem(exportAction));
        addressBookMenu.addSeparator();
        addressBookMenu.add(Builder.createMenuItem(closeAction));
    }

    private void initializeShortCuts() {
    }

    private void initializeToolBar() {
        mainToolBar = new JToolBar();
        //mainToolBar.setBorderPainted(false);
        //mainToolBar.setRollover(true);
        getContentPane().add(mainToolBar, BorderLayout.NORTH);

        mainToolBar.add(Builder.createToolBarButton(newAction));
        mainToolBar.addSeparator();
        mainToolBar.add(Builder.createToolBarButton(editAction));
        mainToolBar.add(Builder.createToolBarButton(deleteAction));
    }

    public void load(File file) throws IOException, ClassNotFoundException {
        ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
        try {
            ContactDatabase contactDatabase = (ContactDatabase) in.readObject();
            localAddressBook.setDatabase(contactDatabase);
        } finally {
            in.close();
        }
    }

    public void saveLayout(SettingStore store) {
        contactTableLayoutManager.saveLayout(store, "addressbook.contact");
        store.saveWindow("addressbook.window", this);
        store.saveSplitPane("addressbook.horizontalSplit", horizontalSplitPane);
    }

    public void restoreLayout(SettingStore store) {
        contactTableLayoutManager.restoreLayout(store, "addressbook.contact");
        store.restoreWindow("addressbook.window", 40, 40, 550, 300, this);
        store.restoreSplitPane("addressbook.horizontalSplit", 150, horizontalSplitPane);
    }

    public void setDoubleClickAction(Action doubleClickAction) {
        this.doubleClickAction = doubleClickAction;
    }

    public void store(File file) throws IOException {
        if (!localAddressBook.isChanged()) {
            logger.info("Address book unchanged");
            return;
        }

        ObjectOutputStream in = new ObjectOutputStream(new FileOutputStream(file));
        try {
            in.writeObject(localAddressBook.getDatabase());
            logger.info("Stored address book in " + file.getAbsolutePath());
        } finally {
            in.close();
        }
    }

    public void updateLabels() {
        setTitle(i18n.tr("JHylaFAX Address Book"));

        addressBookMenu.setText(i18n.tr("Address Book"));

        newAction.putValue(Action.NAME, i18n.tr("New Contact..."));
        editAction.putValue(Action.NAME, i18n.tr("Edit Contact..."));
        deleteAction.putValue(Action.NAME, i18n.tr("Delete Contact"));

        importAction.putValue(Action.NAME, i18n.tr("Import..."));
        exportAction.putValue(Action.NAME, i18n.tr("Export..."));

        closeAction.putValue(Action.NAME, i18n.tr("Close"));

        filterLabel.setText(i18n.tr("Search for"));
        filterAction.putValue(Action.NAME, i18n.tr("Go"));

        contactTableLayoutManager.getTableLayout()
                .setColumnNames(new String[] { i18n.tr("Name"), i18n.tr("Company"), i18n.tr("Fax"), });

        GUIHelper.setMnemonics(getJMenuBar());
    }

    public void valueChanged(ListSelectionEvent e) {
        updateActions();
    }

    public void updateActions() {
        boolean selected = (contactTable.getSelectedRow() != -1);
        editAction.setEnabled(selected);
        deleteAction.setEnabled(selected);
    }

    private static class AddressTableModel extends AbstractTableModel {

        private static final Class[] columnClasses = { String.class, String.class, String.class, };

        private List<SimpleContact> data = new ArrayList<SimpleContact>();

        public AddressTableModel() {
        }

        public void add(SimpleContact contact) {
            data.add(contact);
            fireTableRowsInserted(data.size() - 1, data.size() - 1);
        }

        public Class<?> getColumnClass(int column) {
            return columnClasses[column];
        }

        public int getColumnCount() {
            return columnClasses.length;
        }

        public SimpleContact getContact(int row) {
            return data.get(row);
        }

        public int getRowCount() {
            return data.size();
        }

        public Object getValueAt(int row, int column) {
            SimpleContact contact = data.get(row);
            switch (column) {
            case 0:
                return contact.getFirstname() + " " + contact.getLastname();
            case 1:
                return contact.getCompany();
            case 2:
                return contact.getFaxNumber();
            default:
                return null;
            }
        }

        public int indexOf(SimpleContact contact) {
            for (int i = 0; i < data.size(); i++) {
                if (data.get(i).getContact().equals(contact.getContact())) {
                    return i;
                }
            }
            return -1;
        }

        public void remove(SimpleContact contact) {
            // XXX work around broken SimpleContant.equals() method
            int i = indexOf(contact);
            if (i != -1) {
                data.remove(i);
                fireTableRowsDeleted(i, i);
            }
        }

        public void setData(List<SimpleContact> data) {
            this.data = data;
            fireTableDataChanged();
        }

        public void update(SimpleContact contact) {
            // XXX work around broken SimpleContant.equals() method
            int i = indexOf(contact);
            if (i != -1) {
                fireTableRowsUpdated(i, i);
            }
        }

    }

    private class CloseAction extends AbstractXNapAction {

        public CloseAction() {
        }

        public void actionPerformed(ActionEvent event) {
            AddressBook.this.setVisible(false);
        }
    }

    private class ContactCollection {

        private ContactDatabase database;
        private String filterText;
        private ContactGroup group;
        private String name;
        private ContactCollection parent;
        private boolean changed;

        public ContactCollection(ContactCollection parent, ContactGroup group) {
            this.parent = parent;
            this.group = group;
        }

        public ContactCollection(ContactDatabase database, String name) {
            this.database = database;
            this.name = name;
        }

        public void add(SimpleContact contact) {
            getDatabase().getContactCollection().add(contact.getContact());
            if (group != null) {
                group.addContact(contact.getContact());
            }
            if (matches(contact)) {
                contactTableModel.add(contact);
            }
            changed = true;
        }

        public void changed(SimpleContact contact) {
            if (!matches(contact)) {
                contactTableModel.remove(contact);
            } else {
                contactTableModel.update(contact);
            }
            changed = true;
        }

        public ContactDatabase getDatabase() {
            return (parent != null) ? parent.getDatabase() : database;
        }

        public ContactGroup getGroup() {
            return group;
        }

        public boolean isChanged() {
            return changed;
        }

        private boolean matches(SimpleContact contact) {
            if (filterText == null || filterText.length() == 0) {
                return true;
            }
            return contact.getFirstname().toLowerCase().contains(filterText)
                    || contact.getLastname().toLowerCase().contains(filterText)
                    || contact.getCompany().toLowerCase().contains(filterText)
                    || contact.getFaxNumber().toLowerCase().contains(filterText);
        }

        public void remove(SimpleContact contact) {
            getDatabase().getContactCollection().remove(contact.getContact());
            if (group != null) {
                group.removeContact(contact.getContact());
            }
            if (matches(contact)) {
                contactTableModel.remove(contact);
            }
            changed = true;
        }

        public void resync() {
            Contact[] contacts = (group != null) ? group.listContacts() : database.getContactCollection().toArray();
            List<SimpleContact> data = new ArrayList<SimpleContact>(contacts.length);
            for (int i = 0; i < contacts.length; i++) {
                SimpleContact contact = new SimpleContact(contacts[i]);
                if (matches(contact)) {
                    data.add(contact);
                }
            }
            contactTableModel.setData(data);
            valueChanged(null);
        }

        public void setDatabase(ContactDatabase database) {
            this.database = database;
            changed = false;
            resync();
        }

        public void setFilterText(String filterText) {
            this.filterText = filterText.toLowerCase();
            resync();
        }

        @Override
        public String toString() {
            return (group != null) ? group.getName() : name;
        }

    }

    private static class ContactCollectionCellRenderer extends DefaultTreeCellRenderer {
        Icon bookIcon = IconHelper.getTreeIcon("contents.png");
        Icon groupIcon = IconHelper.getTreeIcon("kdmconfig.png");

        @Override
        public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded,
                boolean leaf, int row, boolean hasFocus) {
            super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);

            DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
            if (node.getUserObject() instanceof ContactCollection) {
                ContactCollection collection = (ContactCollection) node.getUserObject();
                setIcon((collection.getGroup() != null) ? groupIcon : bookIcon);
            }

            return this;
        }
    }

    private class DeleteAction extends AbstractXNapAction {

        public DeleteAction() {
            putValue(ICON_FILENAME, "editdelete.png");
        }

        public void actionPerformed(ActionEvent e) {
            SimpleContact[] contacts = getSelectedContacts();
            if (Dialogs.showConfirmDialog(AddressBook.this,
                    i18n.tr("Do you really want to delete the selected contacts?"), i18n.tr("Delete Contacts"),
                    JOptionPane.YES_NO_OPTION, Settings.CONFIRM_DELETE) == JOptionPane.YES_OPTION) {
                for (int i = 0; i < contacts.length; i++) {
                    getSelectedContactCollection().remove(contacts[i]);
                }
            }
        }

    }

    private class EditAction extends AbstractXNapAction {

        public EditAction() {
            putValue(ICON_FILENAME, "edit.png");
        }

        public void actionPerformed(ActionEvent e) {
            SimpleContact[] contact = getSelectedContacts();
            EditContactDialog dialog = new EditContactDialog(AddressBook.this, contact[0]);
            dialog.setModal(true);
            dialog.setLocationRelativeTo(AddressBook.this);
            dialog.setVisible(true);
            if (dialog.isOkay()) {
                getSelectedContactCollection().changed(contact[0]);
            }
        }

    }

    private class ExportAction extends AbstractXNapAction {

        public ExportAction() {
            putValue(ICON_FILENAME, "fileexport.png");
        }

        public void actionPerformed(ActionEvent event) {
            if (contactTableModel.getRowCount() == 0) {
                Dialogs.showInfo(AddressBook.this, i18n.tr("No data to export"),
                        i18n.tr("JHylaFAX Addressbook Error"));
            }

            JFileChooser fileChooser = new JFileChooser();
            fileChooser.addChoosableFileFilter(new FileExtensionFilter(i18n.tr("vCards (*.vcf)"), ".vcf"));
            if (fileChooser.showSaveDialog(AddressBook.this) == JFileChooser.APPROVE_OPTION) {
                try {
                    SimpleContact[] contacts = getSelectedContacts();
                    if (contacts.length == 0) {
                        contacts = getAlllContacts();
                    }
                    exportContacts(fileChooser.getSelectedFile(), contacts);
                    Dialogs.showInfo(AddressBook.this, i18n.tr("Exported {0} contacts", contacts.length),
                            i18n.tr("JHylaFAX Address Book"));
                } catch (Exception e) {
                    ErrorDialog.showError(AddressBook.this, i18n.tr("Could not export to file \"{0}\""),
                            i18n.tr("JHylaFAX Error"), e);
                }
            }
        }
    }

    private class FilterAction extends AbstractXNapAction {

        public FilterAction() {
            putValue(ICON_FILENAME, "filter.png");
        }

        public void actionPerformed(ActionEvent e) {
            getSelectedContactCollection().setFilterText(searchTextField.getText());
        }

    }

    private class ImportAction extends AbstractXNapAction {

        public ImportAction() {
            putValue(ICON_FILENAME, "fileimport.png");
        }

        /*
              public void actionPerformed1(ActionEvent event)
              {
                 JFileChooser fileChooser = new JFileChooser();
                 fileChooser.addChoosableFileFilter(new FileExtensionFilter(I18n.tr("vCards (*.vcf)"), ".vcf"));
                 if (fileChooser.showOpenDialog(AddressBook.this) == JFileChooser.APPROVE_OPTION) {
        try {
           SimpleContact[] contacts = importContacts(fileChooser.getSelectedFile());
           Dialogs.showInfo(AddressBook.this, 
                 I18n.tr("Imported {0} contacts", contacts.length), 
                 I18n.tr("JHylaFAX Address Book"));
            
        }
        catch (Exception e) {
           ErrorDialog.showError(AddressBook.this, 
                 I18n.tr("Could not import from file \"{0}\""), 
                 I18n.tr("JHylaFAX Addressbook Error"), e);               
        }
                 }
              }
        */
        public void actionPerformed(ActionEvent e) {
            ImportWizard wizard = new ImportWizard(AddressBook.this);
            wizard.setLocationRelativeTo(AddressBook.this);
            wizard.setVisible(true);
        }

    }

    private class NewAction extends AbstractXNapAction {

        public NewAction() {
            putValue(ICON_FILENAME, "filenew.png");
        }

        public void actionPerformed(ActionEvent e) {
            SimpleContact contact = new SimpleContact();
            EditContactDialog dialog = new EditContactDialog(AddressBook.this, contact);
            dialog.setModal(true);
            dialog.setLocationRelativeTo(AddressBook.this);
            dialog.setVisible(true);
            if (dialog.isOkay()) {
                getSelectedContactCollection().add(contact);
            }
        }

    }

    private class TableDoubleClickAction extends AbstractXNapAction {

        public TableDoubleClickAction() {
        }

        public void actionPerformed(ActionEvent event) {
            if (doubleClickAction == null) {
                if (editAction.isEnabled()) {
                    editAction.actionPerformed(event);
                }
            } else if (doubleClickAction.isEnabled()) {
                doubleClickAction.actionPerformed(event);
            }
        }

    }

    private class TabTitleButton extends ToolBarButton {
        public TabTitleButton(Action action) {
            super(action);
            String iconName = (String) action.getValue(AbstractXNapAction.ICON_FILENAME);
            setIcon(IconHelper.getTabTitleIcon(iconName));
            setMargin(new Insets(0, 0, 0, 0));
        }
    }

    private class ContactTransferHandler extends AbstractContactTransferHandler {

        @Override
        protected Transferable createTransferable(JComponent c) {
            SimpleContact[] contacts = getSelectedContacts();
            if (contacts.length == 0) {
                return super.createTransferable(c);
            }

            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ContactIOFactory factory = Pim.getContactIOFactory();
            ContactMarshaller marshaller = factory.createContactMarshaller();
            marshaller.setEncoding("UTF-8");
            for (SimpleContact contact : contacts) {
                marshaller.marshallContact(out, contact.getContact());
            }
            return new ContactTransferable(new ByteArrayInputStream(out.toByteArray()));
        }

        @Override
        public void importData(Contact[] contacts) {
            for (int i = 0; i < contacts.length; i++) {
                SimpleContact contact = new SimpleContact(contacts[i]);
                getSelectedContactCollection().add(contact);
            }
        }

    }

}