/*
* Copyright (c) 2012, Fromentin Xavier, Schnell Michaël, Dervin Cyrielle, Brabant Quentin
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * The names of its contributors may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL Fromentin Xavier, Schnell Michaël, Dervin Cyrielle OR Brabant Quentin
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package kameleon.gui.view;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JList;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.border.BevelBorder;
import javax.swing.border.LineBorder;
import javax.swing.border.TitledBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import kameleon.exception.KameleonException;
import kameleon.gui.exception.UnknownKeyException;
import kameleon.gui.language.SwitchLanguage;
import kameleon.gui.model.FileInfo;
import kameleon.gui.model.GenerationModel;
import kameleon.gui.model.Model;
import kameleon.gui.model.Observer;
import kameleon.gui.util.LanguageConstants;
/**
* View showing the recently added files as well as the currently
* selected file. Also offers the means to add or delete files from
* the history (= recently used) list.
*
* @author Fromentin Xavier, Schnell Michaël
* @version 1.0
*/
public class HistoryView extends JPanel
implements Observer, LanguageConstants {
/**
* Needed to serialize this class.
*
* @see java.io.Serializable
*/
private static final long serialVersionUID = 3914306504859458701L ;
/**
* Font used by plain text.
*/
private final Font PLAIN = new Font(
this.getFont().getName(), Font.PLAIN, this.getFont().getSize()) ;
/**
* Font used by italic text.
*/
private final Font ITALIC = new Font(
this.getFont().getName(), Font.ITALIC, this.getFont().getSize()) ;
/**
* Model of this view.
*/
protected Model model ;
/**
* Component containing this view.
*/
protected Component container ;
/**
* List of the recently added/used files.
*/
protected JList recentFiles ;
/**
* Dialog used to add new files.
*/
protected JFileChooser fileChooser ;
/**
* Path of the currently selected file.
*/
protected JTextField path ;
/**
* Button used to add new files.
*/
protected JButton browse ;
/**
* Adapter used by the list of recently added/used files.
*/
protected DefaultListModel adapter ;
/**
* Contextual menu for the item of the list. USed to remove files
* from the list
*/
protected JPopupMenu popupMenu ;
/**
* Menu item used to remove files from the list of recently added/used files.
*/
protected JMenuItem delete;
/**
* Sole constructor.
*
* @param model
* model of this view
*
* @param container
* parent frame of this instance
*
* @throws KameleonException
* if an error occurred while building this instance
*/
public HistoryView(Model model, Component container)
throws KameleonException {
super() ;
this.model = model ;
this.model.addObserver(this) ;
this.container = container ;
this.fileChooser = new JFileChooser() ;
this.build() ;
}// HistoryView(Model, Component)
/**
* Builds the content of this view.
*
* @throws UnknownKeyException
* if a key for a displayed text could not be found
*/
protected void build() throws UnknownKeyException {
final SwitchLanguage sl = SwitchLanguage.getInstance() ;
this.setLayout(new BorderLayout()) ;
this.setBorder(BorderFactory.createTitledBorder(
sl.getText(LanguageConstants.HISTORY_TITLE_BORDER))) ;
JPanel north = new JPanel() ;
north.setLayout(new BoxLayout(north, BoxLayout.X_AXIS)) ;
this.path = new JTextField(sl.getText(LanguageConstants.NO_FILE)) ;
this.path.setFont(this.ITALIC) ;
this.path.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED)) ;
north.add(this.path) ;
this.browse = new JButton(
sl.getText(LanguageConstants.BROWSE_BUTTON)) ;
this.browse.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int browseResult = HistoryView.this.fileChooser.
showOpenDialog(HistoryView.this.container) ;
if (browseResult == JFileChooser.APPROVE_OPTION) {
HistoryView.this.model.addFile(
HistoryView.this.fileChooser.getSelectedFile()) ;
}// if
}// actionPerformed(ActionEvent)
}) ;
north.add(this.browse) ;
this.add(north, BorderLayout.NORTH) ;
this.adapter = new RecentFiles(this.model.getRecentFileInfo()) ;
this.recentFiles = new JList(this.adapter) ;
this.recentFiles.addListSelectionListener(new FileSelectionListener()) ;
this.recentFiles.setSelectionMode(ListSelectionModel.SINGLE_SELECTION) ;
this.recentFiles.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent me) {
// if right mouse button clicked (or me.isPopupTrigger())
if (SwingUtilities.isRightMouseButton(me)
&& !HistoryView.this.recentFiles.isSelectionEmpty()
&& HistoryView.this.recentFiles.locationToIndex(me.getPoint())
== HistoryView.this.recentFiles.getSelectedIndex()) {
HistoryView.this.popupMenu.show(
HistoryView.this.recentFiles, me.getX(), me.getY()) ;
}// if
}// mouseClicked(MouseEvent)
}) ;
JScrollPane scroll = new JScrollPane(this.recentFiles) ;
scroll.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED)) ;
this.add(scroll, BorderLayout.CENTER) ;
this.delete = new JMenuItem(sl.getText(LanguageConstants.DELETE_BUTTON)) ;
this.delete.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
HistoryView.this.model.deleteCurrentSelection() ;
}// actionPerformed(ActionEvent)
}) ;
this.popupMenu = new JPopupMenu() ;
this.popupMenu.setBorder(new LineBorder(Color.LIGHT_GRAY)) ;
this.popupMenu.add(this.delete) ;
}// build()
/**
* Updates the displayed path for the currently selected file.
*
* @throws UnknownKeyException
* if an error occurred while updating the text of this view
*/
@Override
public void update() throws UnknownKeyException {
// A file was selected, update displayed path
if (this.model.fileIsSelected()) {
if (this.model.selectionHasChanged()) {
this.recentFiles.setSelectedValue(
this.model.getSelectedFileInfo().getPath(), true) ;
}
this.path.setText(this.model.getSelectedFileInfo().getPath()) ;
this.path.setFont(this.PLAIN) ;
}// No file is selected, display default text
else {
this.path.setText(SwitchLanguage.getInstance().getText(
LanguageConstants.NO_FILE)) ;
this.path.setFont(this.ITALIC) ;
}// if
}// update()
/**
* Updates the text to match the currently selected language.
*
* @throws UnknownKeyException
* if an error occurred while updating the text of this view
*/
@Override
public void reloadLanguage() throws UnknownKeyException {
SwitchLanguage sl = SwitchLanguage.getInstance() ;
TitledBorder border = (TitledBorder) this.getBorder() ;
border.setTitle(sl.getText(LanguageConstants.HISTORY_TITLE_BORDER)) ;
this.browse.setText(sl.getText(LanguageConstants.BROWSE_BUTTON)) ;
this.fileChooser.setApproveButtonText(sl.getText(LanguageConstants.ADD_FILE)) ;
if (!this.model.fileIsSelected()) {
this.path.setText(sl.getText(LanguageConstants.NO_FILE)) ;
}/// if
this.delete.setText(sl.getText(LanguageConstants.DELETE_BUTTON)) ;
}// reloadLanguage()
/**
* Model handling the data for the list of recently added/used files.
*
* @author Fromentin Xavier, Schnell Michaël
* @version 1.0
*/
class RecentFiles extends DefaultListModel implements Observer {
/**
* Needed to serialize this class.
*
* @see java.io.Serializable
*/
private static final long serialVersionUID = -5071053784542177604L;
/**
* Sole constructor.
*
* @param files
* initial files contained by the list
*/
public RecentFiles(FileInfo[] files) {
super() ;
for(FileInfo file : files) {
this.addElement(file.getPath()) ;
}// for
HistoryView.this.model.addObserver(this) ;
}// RecentFiles(FileInfo[])
/**
* Updates the list of recently used/added files.
*/
@Override
public void update() {
GenerationModel gModel = HistoryView.this.model ;
if (gModel.newFileAdded()) {
FileInfo fileInfo = gModel.getSelectedFileInfo() ;
this.add(0, fileInfo.getPath()) ;
HistoryView.this.recentFiles.setSelectedIndex(0) ;
this.fireContentsChanged(this, 0, this.getSize()-1) ;
} else if (gModel.fileRemoved()){
this.remove(gModel.getDeletedIndex()) ;
}// if
}// update()
/**
* Does nothing, only file paths are displayed.
*/
@Override
public void reloadLanguage() {/* No text to update. */}
}// class RecentFiles
/**
* Listener handling the clicks on the list items. Selects
* or "de-selects" the file displayed by the clicked list item.
*
* @author Fromentin Xavier, Schnell Michaël
* @version 1.0
*/
class FileSelectionListener implements ListSelectionListener {
/**
* {@inheritDoc}
*/
@Override
public void valueChanged(ListSelectionEvent e) {
if (!HistoryView.this.model.selectionHasChanged()
&& !HistoryView.this.model.newFileAdded()) {
if (!e.getValueIsAdjusting()) {
int indice = HistoryView.this.recentFiles.getSelectedIndex() ;
if (indice != -1) {
HistoryView.this.model.selectFile(
(String) HistoryView.this.adapter.get(indice)) ;
} else {
HistoryView.this.model.clearSelection() ;
}// if
}// if
}// if
}// valueChanged(ListSelectionEvent)
}// class FileSelectionListener
}// class HistoryView