au.org.ala.delta.editor.ui.ImageDetailsPanel.java Source code

Java tutorial

Introduction

Here is the source code for au.org.ala.delta.editor.ui.ImageDetailsPanel.java

Source

/*******************************************************************************
 * Copyright (C) 2011 Atlas of Living Australia
 * All Rights Reserved.
 * 
 * The contents of this file are subject to the Mozilla Public
 * License Version 1.1 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.mozilla.org/MPL/
 * 
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 ******************************************************************************/
package au.org.ala.delta.editor.ui;

import au.org.ala.delta.editor.DeltaEditor;
import au.org.ala.delta.editor.model.EditorViewModel;
import au.org.ala.delta.editor.ui.dnd.SimpleTransferHandler;
import au.org.ala.delta.editor.ui.util.MessageDialogHelper;
import au.org.ala.delta.model.Illustratable;
import au.org.ala.delta.model.image.Image;
import au.org.ala.delta.model.image.ImageOverlay;
import au.org.ala.delta.model.image.ImageSettings;
import au.org.ala.delta.model.image.OverlayType;
import au.org.ala.delta.model.observer.AbstractDataSetObserver;
import au.org.ala.delta.model.observer.DeltaDataSetChangeEvent;
import au.org.ala.delta.model.observer.DeltaDataSetObserver;
import au.org.ala.delta.ui.image.AudioPlayer;
import au.org.ala.delta.ui.image.SupportedFileTypes;
import au.org.ala.delta.ui.rtf.RtfEditorPane;
import org.apache.commons.lang.StringUtils;
import org.jdesktop.application.Action;
import org.jdesktop.application.Application;
import org.jdesktop.application.ResourceMap;

import javax.swing.*;
import javax.swing.GroupLayout.Alignment;
import javax.swing.LayoutStyle.ComponentPlacement;
import javax.swing.border.TitledBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.filechooser.FileFilter;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.io.File;
import java.net.URL;
import java.util.List;

/**
 * Displays the details of images associated with an Item or Character.
 */
public class ImageDetailsPanel extends JPanel {

    private static final long serialVersionUID = -1973824161019895786L;

    private EditorViewModel _dataSet;

    /** The object that any images will be attached to */
    private Illustratable _illustratable;

    /** The currently selected image */
    private Image _selectedImage;

    /** Helper class for displaying messages */
    private MessageDialogHelper _messageHelper;

    private ActionMap _actions;
    private ResourceMap _resources;

    // UI components
    private ImageList imageList;
    private RtfEditorPane subjectTextPane;
    private RtfEditorPane developerNotesTextPane;
    private JButton btnDisplay;
    private JButton btnDelete;
    private JButton btnSettings;
    private JButton btnAdd;
    private JButton deleteSoundButton;
    private JButton playSoundButton;
    private JButton insertSoundButton;
    private JComboBox soundComboBox;

    public ImageDetailsPanel() {
        _actions = Application.getInstance().getContext().getActionMap(this);
        _resources = Application.getInstance().getContext().getResourceMap();
        _messageHelper = new MessageDialogHelper();
        createUI();
        addEventHandlers();
    }

    private void addEventHandlers() {

        btnDisplay.setAction(_actions.get("displayImage"));
        btnAdd.setAction(_actions.get("addImage"));
        btnDelete.setAction(_actions.get("deleteImage"));
        btnSettings.setAction(_actions.get("displayImageSettings"));
        imageList.addListSelectionListener(new ListSelectionListener() {

            @Override
            public void valueChanged(ListSelectionEvent e) {
                _selectedImage = (Image) imageList.getSelectedValue();
                _dataSet.setSelectedImage(_selectedImage);
                updateDisplay();
            }
        });
        imageList.setSelectionAction(_actions.get("displayImage"));
        imageList.setDragEnabled(true);
        imageList.setDropMode(DropMode.INSERT);
        imageList.setTransferHandler(new ImageTransferHandler());

        playSoundButton.setAction(_actions.get("playSound"));
        deleteSoundButton.setAction(_actions.get("deleteSound"));
        insertSoundButton.setAction(_actions.get("addSound"));
        FocusAdapter focusAdaptor = new FocusAdapter() {

            @Override
            public void focusLost(FocusEvent e) {
                if (e.getComponent() == subjectTextPane) {
                    updateSubjectText();
                } else if (e.getComponent() == developerNotesTextPane) {
                    updateDeveloperNotes();
                }
            }

        };
        soundComboBox.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                updateSoundActions();
            }
        });
        subjectTextPane.addFocusListener(focusAdaptor);
        developerNotesTextPane.addFocusListener(focusAdaptor);
    }

    private void createUI() {
        JPanel panel = new JPanel();

        JPanel buttonPanel = new JPanel();

        JPanel panel_2 = new JPanel();
        GroupLayout groupLayout = new GroupLayout(this);
        groupLayout.setHorizontalGroup(groupLayout.createParallelGroup(Alignment.LEADING)
                .addGroup(groupLayout.createSequentialGroup().addContainerGap()
                        .addComponent(panel, GroupLayout.PREFERRED_SIZE, 262, GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(ComponentPlacement.RELATED)
                        .addComponent(buttonPanel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE,
                                GroupLayout.PREFERRED_SIZE)
                        .addPreferredGap(ComponentPlacement.RELATED)
                        .addComponent(panel_2, GroupLayout.PREFERRED_SIZE, 265, Short.MAX_VALUE)
                        .addContainerGap()));
        groupLayout.setVerticalGroup(groupLayout.createParallelGroup(Alignment.TRAILING)
                .addGroup(groupLayout.createSequentialGroup().addContainerGap()
                        .addGroup(groupLayout.createParallelGroup(Alignment.LEADING)
                                .addComponent(panel, GroupLayout.DEFAULT_SIZE, 238, Short.MAX_VALUE)
                                .addComponent(buttonPanel, 0, 0, Short.MAX_VALUE)
                                .addComponent(panel_2, 0, 0, Short.MAX_VALUE))
                        .addContainerGap()));

        btnSettings = new JButton();
        btnDisplay = new JButton();
        btnAdd = new JButton();
        btnDelete = new JButton();

        GroupLayout gl_buttonPanel = new GroupLayout(buttonPanel);
        gl_buttonPanel.setHorizontalGroup(gl_buttonPanel.createParallelGroup(Alignment.TRAILING).addGroup(
                Alignment.LEADING,
                gl_buttonPanel.createSequentialGroup().addContainerGap().addGroup(gl_buttonPanel
                        .createParallelGroup(Alignment.LEADING)
                        .addComponent(btnSettings, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE,
                                Short.MAX_VALUE)
                        .addComponent(btnDisplay, Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 83, Short.MAX_VALUE)
                        .addComponent(btnAdd, GroupLayout.DEFAULT_SIZE, 83, Short.MAX_VALUE)
                        .addComponent(btnDelete, GroupLayout.DEFAULT_SIZE, 83, Short.MAX_VALUE))
                        .addContainerGap()));
        gl_buttonPanel.setVerticalGroup(gl_buttonPanel.createParallelGroup(Alignment.TRAILING)
                .addGroup(gl_buttonPanel.createSequentialGroup().addContainerGap(119, Short.MAX_VALUE)
                        .addComponent(btnSettings).addPreferredGap(ComponentPlacement.RELATED)
                        .addComponent(btnDisplay).addPreferredGap(ComponentPlacement.RELATED).addComponent(btnAdd)
                        .addPreferredGap(ComponentPlacement.RELATED).addComponent(btnDelete).addContainerGap()));
        buttonPanel.setLayout(gl_buttonPanel);

        JLabel lblSubjectText = new JLabel();
        lblSubjectText.setName("imageDetailsSubjectLabel");

        JScrollPane scrollPane_1 = new JScrollPane();

        JLabel lblDevelopersNotes = new JLabel();
        lblDevelopersNotes.setName("imageDetailsDevelopersNotesLabel");

        JScrollPane scrollPane_2 = new JScrollPane();

        JPanel panel_3 = new JPanel();

        String title = _resources.getString("imageDetailsSoundTitle");

        panel_3.setBorder(new TitledBorder(null, title, TitledBorder.LEADING, TitledBorder.TOP, null, null));

        GroupLayout gl_panel_2 = new GroupLayout(panel_2);
        gl_panel_2.setHorizontalGroup(gl_panel_2.createParallelGroup(Alignment.TRAILING)
                .addGroup(gl_panel_2.createSequentialGroup().addContainerGap()
                        .addGroup(gl_panel_2.createParallelGroup(Alignment.LEADING)
                                .addComponent(scrollPane_2, GroupLayout.DEFAULT_SIZE, 245, Short.MAX_VALUE)
                                .addComponent(scrollPane_1, GroupLayout.DEFAULT_SIZE, 245, Short.MAX_VALUE)
                                .addComponent(lblDevelopersNotes)
                                .addComponent(panel_3, GroupLayout.DEFAULT_SIZE, 245, Short.MAX_VALUE)
                                .addComponent(lblSubjectText))
                        .addContainerGap()));
        gl_panel_2.setVerticalGroup(gl_panel_2.createParallelGroup(Alignment.LEADING)
                .addGroup(gl_panel_2.createSequentialGroup().addContainerGap().addComponent(lblSubjectText)
                        .addPreferredGap(ComponentPlacement.RELATED)
                        .addComponent(scrollPane_1, GroupLayout.DEFAULT_SIZE, 41, Short.MAX_VALUE)
                        .addPreferredGap(ComponentPlacement.RELATED).addComponent(lblDevelopersNotes)
                        .addPreferredGap(ComponentPlacement.RELATED)
                        .addComponent(scrollPane_2, GroupLayout.DEFAULT_SIZE, 54, Short.MAX_VALUE)
                        .addPreferredGap(ComponentPlacement.RELATED)
                        .addComponent(panel_3, GroupLayout.PREFERRED_SIZE, 66, GroupLayout.PREFERRED_SIZE)
                        .addContainerGap()));

        developerNotesTextPane = new RtfEditorPane();
        scrollPane_2.setViewportView(developerNotesTextPane);

        subjectTextPane = new RtfEditorPane();
        scrollPane_1.setViewportView(subjectTextPane);

        soundComboBox = new JComboBox();
        soundComboBox.setRenderer(new DefaultListCellRenderer() {
            private static final long serialVersionUID = 1L;

            @Override
            public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
                    boolean cellHasFocus) {
                String text = "";
                ImageOverlay overlay = (ImageOverlay) value;
                if (overlay != null) {
                    text = overlay.overlayText;
                }
                super.getListCellRendererComponent(list, text, index, isSelected, cellHasFocus);
                return this;
            }
        });

        deleteSoundButton = new JButton();

        playSoundButton = new JButton();

        insertSoundButton = new JButton();
        GroupLayout gl_panel_3 = new GroupLayout(panel_3);
        gl_panel_3.setHorizontalGroup(gl_panel_3.createParallelGroup(Alignment.LEADING)
                .addGroup(gl_panel_3.createSequentialGroup().addComponent(soundComboBox, 0, 0, Short.MAX_VALUE)
                        .addPreferredGap(ComponentPlacement.RELATED).addComponent(deleteSoundButton)
                        .addPreferredGap(ComponentPlacement.RELATED).addComponent(playSoundButton)
                        .addPreferredGap(ComponentPlacement.RELATED).addComponent(insertSoundButton)
                        .addContainerGap()));
        gl_panel_3.setVerticalGroup(gl_panel_3.createParallelGroup(Alignment.TRAILING)
                .addGroup(gl_panel_3.createSequentialGroup().addContainerGap(5, Short.MAX_VALUE)
                        .addGroup(gl_panel_3.createParallelGroup(Alignment.BASELINE)
                                .addComponent(soundComboBox, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE,
                                        GroupLayout.PREFERRED_SIZE)
                                .addComponent(deleteSoundButton).addComponent(playSoundButton)
                                .addComponent(insertSoundButton))));
        panel_3.setLayout(gl_panel_3);

        panel_2.setLayout(gl_panel_2);

        JLabel lblImageFiles = new JLabel();
        lblImageFiles.setName("imageDetailsImageFilesLabel");
        lblImageFiles.setAlignmentY(Component.TOP_ALIGNMENT);

        JScrollPane scrollPane = new JScrollPane();
        GroupLayout gl_panel = new GroupLayout(panel);
        gl_panel.setHorizontalGroup(gl_panel.createParallelGroup(Alignment.LEADING)
                .addGroup(gl_panel.createSequentialGroup()
                        .addGroup(gl_panel.createParallelGroup(Alignment.LEADING).addComponent(lblImageFiles)
                                .addComponent(scrollPane, GroupLayout.DEFAULT_SIZE, 262, Short.MAX_VALUE))
                        .addContainerGap()));
        gl_panel.setVerticalGroup(gl_panel.createParallelGroup(Alignment.LEADING)
                .addGroup(gl_panel.createSequentialGroup().addContainerGap().addComponent(lblImageFiles)
                        .addPreferredGap(ComponentPlacement.RELATED)
                        .addComponent(scrollPane, GroupLayout.DEFAULT_SIZE, 236, Short.MAX_VALUE)
                        .addContainerGap()));

        imageList = new ImageList();

        scrollPane.setViewportView(imageList);
        panel.setLayout(gl_panel);
        setLayout(groupLayout);
    }

    /**
     * Binds the supplied Illustratable to the user interface provided by this 
     * class.
     * @param target
     */
    public void bind(EditorViewModel model, Illustratable target) {
        _illustratable = target;
        setDataSet(model);
        List<Image> images = _illustratable.getImages();
        imageList.setImages(images);
    }

    private File getImageFile() {
        return getMediaFile(SupportedFileTypes.getSupportedImageFilesFilter());
    }

    private File getSoundFile() {
        return getMediaFile(SupportedFileTypes.getSupportedSoundFilesFilter());
    }

    private File getMediaFile(FileFilter filter) {
        String imagePath = _dataSet.getImagePath();
        JFileChooser chooser = new JFileChooser(imagePath);
        chooser.setFileFilter(filter);

        if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
            File imageFile = chooser.getSelectedFile();

            ImageSettings settings = _dataSet.getImageSettings();
            if (settings.isOnResourcePath(imageFile)) {

                String name = imageFile.getName();
                boolean exists = false;

                if (_selectedImage != null) {
                    List<ImageOverlay> existingSounds = _selectedImage.getSounds();
                    for (ImageOverlay sound : existingSounds) {
                        if (name.equals(sound.overlayText)) {
                            exists = true;
                            break;
                        }
                    }
                }
                if (exists) {
                    int result = _messageHelper.confirmDuplicateFileName();
                    if (result == JOptionPane.YES_OPTION) {
                        imageFile = new File(imageFile.getAbsolutePath());
                    } else if (result == JOptionPane.NO_OPTION) {
                        return getMediaFile(filter);
                    } else {
                        imageFile = null;
                    }
                } else {
                    // Turn the file into a relative one.
                    imageFile = new File(name);
                }
            } else {
                // Ask about it or copy it to the image path.
                int result = _messageHelper.confirmNotOnImagePath();
                if (result == JOptionPane.YES_OPTION) {
                    imageFile = new File(imageFile.getAbsolutePath());
                } else if (result == JOptionPane.NO_OPTION) {
                    return getMediaFile(filter);
                } else {
                    imageFile = null;
                }
            }

            return imageFile;
        }

        return null;
    }

    /**
     * Displays the currently selected image.
     */
    @Action
    public void displayImage() {
        if (_selectedImage == null) {
            return;
        }

        ActionMap actions = Application.getInstance().getContext().getActionMap();
        actions.get("viewImageEditor").actionPerformed(null);
    }

    /**
     * Prompts the user to select a file and adds it as an image to the Illustratable object.
     */
    @Action
    public void addImage() {
        File file = getImageFile();
        if (file == null) {
            return;
        }

        _illustratable.addImage(file.getPath(), "");

    }

    /**
     * Deletes the currently selected image 
     */
    @Action
    public void deleteImage() {
        if (_selectedImage == null) {
            return;
        }
        boolean delete = _messageHelper.confirmDeleteImage();
        if (delete) {
            _illustratable.deleteImage(_selectedImage);
        }
    }

    @Action
    public void displayImageSettings() {
        DeltaEditor editor = (DeltaEditor) Application.getInstance();
        editor.viewImageSettings();
    }

    /**
     * Deletes the sound currently selected in the image sound combo box.
     */
    @Action
    public void deleteSound() {
        ImageOverlay soundFile = (ImageOverlay) soundComboBox.getSelectedItem();
        _selectedImage.deleteOverlay(soundFile);
        soundComboBox.removeItem(soundFile);
    }

    /**
     * Plays the sound currently selected in the image sound combo box.
     */
    @Action
    public void playSound() {
        ImageOverlay soundFile = (ImageOverlay) soundComboBox.getSelectedItem();
        try {
            URL soundUrl = _dataSet.getImageSettings().findFileOnResourcePath(soundFile.overlayText, false);
            AudioPlayer.playClip(soundUrl);
        } catch (Exception e) {
            _messageHelper.errorPlayingSound(soundFile.overlayText);
        }
    }

    /**
     * Allows the user to select a sound file to be added to the image.
     */
    @Action
    public void addSound() {
        File soundFile = getSoundFile();
        if (soundFile != null) {
            ImageOverlay soundOverlay = new ImageOverlay(OverlayType.OLSOUND);
            soundOverlay.overlayText = soundFile.getPath();
            _selectedImage.addOverlay(soundOverlay);
            soundComboBox.addItem(soundOverlay);
        }
    }

    private void updateSubjectText() {

        String subjectText = subjectTextPane.getText();

        updateOverlayText(OverlayType.OLSUBJECT, subjectText);
    }

    private void updateDeveloperNotes() {

        String subjectText = developerNotesTextPane.getText();

        updateOverlayText(OverlayType.OLCOMMENT, subjectText);
    }

    private void updateSoundActions() {
        boolean enabled = soundComboBox.getSelectedItem() != null;
        _actions.get("playSound").setEnabled(enabled);
        _actions.get("deleteSound").setEnabled(enabled);

    }

    /**
     * Updates the overlay text of the supplied overlay.  If the overlay is null,
     * a new one of the supplied type is created.  If the text is null and the overlay
     * is not, the overlay is deleted.
     * @param type the type of overlay to edit.
     * @param text the new text for the overlay.
     */
    private void updateOverlayText(int type, String text) {
        if (_selectedImage == null) {
            return;
        }
        ImageOverlay overlay = _selectedImage.getOverlay(type);
        if (overlay == null) {
            if (StringUtils.isNotEmpty(text)) {
                overlay = new ImageOverlay(type);
                overlay.overlayText = text;
                _selectedImage.addOverlay(overlay);
            }
        } else {
            if (StringUtils.isNotEmpty(text)) {
                overlay.overlayText = text;
                _selectedImage.updateOverlay(overlay);
            } else {
                _selectedImage.deleteOverlay(overlay);
            }
        }
    }

    private void updateDisplay() {

        DefaultComboBoxModel model = (DefaultComboBoxModel) soundComboBox.getModel();
        model.removeAllElements();
        if (_selectedImage == null) {
            subjectTextPane.setText("");
            developerNotesTextPane.setText("");
            btnDisplay.setEnabled(false);
            btnDelete.setEnabled(false);
        } else {
            btnDisplay.setEnabled(true);
            btnDelete.setEnabled(true);
            List<ImageOverlay> overlays = _selectedImage.getOverlays();
            for (ImageOverlay overlay : overlays) {

                if (overlay.isType(OverlayType.OLSUBJECT)) {
                    subjectTextPane.setText(overlay.overlayText);
                } else if (overlay.isType(OverlayType.OLCOMMENT)) {
                    developerNotesTextPane.setText(overlay.overlayText);
                } else if (overlay.isType(OverlayType.OLSOUND)) {
                    soundComboBox.addItem(overlay);
                }
            }
        }
        updateSoundActions();
    }

    /**
     * Handles drag and drop of Items in the ItemList.
     */
    class ImageTransferHandler extends SimpleTransferHandler<Image> {

        private static final long serialVersionUID = 889705892088002277L;

        public ImageTransferHandler() {
            super(Image.class);
        }

        public int getSourceActions(JComponent c) {
            return TransferHandler.MOVE;
        }

        @Override
        protected Image getTransferObject() {
            return (Image) imageList.getSelectedValue();
        }

        @Override
        protected int getStartIndex() {
            return imageList.getSelectedIndex();
        }

        @Override
        protected int getDropLocationIndex(DropLocation dropLocation) {
            return ((javax.swing.JList.DropLocation) dropLocation).getIndex();
        }

        @Override
        protected void move(Image image, int targetIndex) {
            _illustratable.moveImage(image, targetIndex);
            imageList.setSelectedIndex(targetIndex);
        }

        @Override
        protected void copy(Image item, int targetIndex) {
            throw new UnsupportedOperationException();
        }
    }

    private DeltaDataSetObserver observer = new AbstractDataSetObserver() {
        @Override
        public void itemEdited(DeltaDataSetChangeEvent event) {
            if (event.getItem().equals(_illustratable)) {
                updateImageList();
            }
        }

        private void updateImageList() {
            int selection = -1;
            if (_selectedImage != null) {
                selection = imageList.getSelectedIndex();
            }
            bind(_dataSet, _illustratable);
            if (selection != -1) {
                imageList.setSelectedIndex(selection);
            }
        }

        @Override
        public void characterEdited(DeltaDataSetChangeEvent event) {
            if (event.getCharacter().equals(_illustratable)) {
                updateImageList();
            }
        }
    };

    public void setDataSet(EditorViewModel dataSet) {

        if (_dataSet != null) {
            _dataSet.removeDeltaDataSetObserver(observer);
        }
        _dataSet = dataSet;
        _dataSet.addDeltaDataSetObserver(observer);
    }

}