package org.vorthmann.zome.ui;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.DefaultListModel;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListCellRenderer;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import org.vorthmann.ui.Controller;
import org.vorthmann.ui.ReorderableJList;
public class PagelistPanel extends JPanel implements PropertyChangeListener
{
private static final Logger logger = Logger.getLogger( "org.vorthmann.zome.thumbnails" );
private final JList<ImageIcon> list;
private DefaultListModel<ImageIcon> listModel;
private final Controller controller;
private static final String dupeString = "Add";
private static final String remString = "Remove";
private JButton removeButton;
private JButton addButton;
private final boolean isEditor;
private transient int popupItem;
private class ListMoves implements ListDataListener, ReorderableJList.ListMoveListener
{
// this object is where we can correlate the events from ReorderableJList
private boolean moving = false;
private int startIndex = -1;
@Override
public void contentsChanged( ListDataEvent lde )
{
String action = "elementChanged-" + lde .getIndex0();
PagelistPanel .this .controller .actionPerformed( new ActionEvent( PagelistPanel.this, ActionEvent.ACTION_PERFORMED, action ) );
}
@Override
public void intervalAdded( ListDataEvent lde )
{
if ( moving )
{
String action = "elementMoved-" + startIndex + ">" + lde .getIndex0();
PagelistPanel .this .controller .actionPerformed( new ActionEvent( PagelistPanel.this, ActionEvent.ACTION_PERFORMED, action ) );
}
}
@Override
public void intervalRemoved( ListDataEvent lde )
{
if ( moving )
startIndex = lde .getIndex0();
}
@Override
public void startMove()
{
moving = true;
}
@Override
public void endMove()
{
moving = false;
startIndex = -1;
}
}
private class ThumbnailSelectionRenderer extends JLabel implements ListCellRenderer<ImageIcon>
{
public ThumbnailSelectionRenderer()
{
setOpaque( true );
setHorizontalAlignment( CENTER );
setVerticalAlignment( CENTER );
setBorder( BorderFactory .createEmptyBorder( 1, 7, 1, 0 ) );
}
/*
* This method finds the image and text corresponding
* to the selected value and returns the label, set up
* to display the text and image.
*/
@Override
public Component getListCellRendererComponent( JList<? extends ImageIcon> list, ImageIcon value, int index, boolean isSelected, boolean cellHasFocus )
{
if (isSelected) {
setBackground( list .getSelectionBackground() );
setForeground( list .getSelectionForeground() );
} else {
setBackground( list .getBackground() );
setForeground( list .getForeground() );
}
setIcon( value );
return this;
}
}
private final class ContextualMenuMouseListener extends MouseAdapter
{
private final Controller controller;
private final ContextualMenu pickerPopup;
private ContextualMenuMouseListener( Controller controller, ContextualMenu pickerPopup )
{
this.controller = controller;
this.pickerPopup = pickerPopup;
}
@Override
public void mousePressed( MouseEvent e )
{
maybeShowPopup( e );
}
@Override
public void mouseReleased( MouseEvent e )
{
maybeShowPopup( e );
}
private void maybeShowPopup( MouseEvent e )
{
if ( e.isPopupTrigger() ) {
Object source = e .getSource();
popupItem = list .locationToIndex( e.getPoint() );
e .setSource( popupItem );
pickerPopup .enableActions( controller, e );
e .setSource( source );
pickerPopup .show( e.getComponent(), e.getX(), e.getY() );
}
}
}
private JMenuItem createMenuItem
(
String text,
String actionCommand
)
{
JMenuItem menuItem = new JMenuItem( text );
menuItem .setEnabled( true );
menuItem .setActionCommand( actionCommand );
return menuItem;
}
public PagelistPanel( final Controller controller )
{
super( new BorderLayout() );
controller .addPropertyListener( this );
this .controller = controller;
this.isEditor = controller .userHasEntitlement( "lesson.edit" ) && ! controller .propertyIsTrue( "reader.preview" );
ContextualMenu pageviewPopupMenu = new ContextualMenu();
pageviewPopupMenu .setLightWeightPopupEnabled( false );
if ( this .isEditor )
{
JMenuItem menuItem = createMenuItem( "Save Current View to Page", "setView" );
menuItem .addActionListener( this.controller );
pageviewPopupMenu .add( menuItem );
}
JMenuItem menuItem = createMenuItem( "Show This Page's View", "usePageView" );
menuItem .addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent ae )
{
controller .actionPerformed( new ActionEvent( PagelistPanel.this, ActionEvent.ACTION_PERFORMED, "usePageView-" + popupItem ) );
}
} );
pageviewPopupMenu .add( menuItem );
menuItem = createMenuItem( "Copy This Page's View", "copyPageView" );
menuItem .addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent ae )
{
controller .actionPerformed( new ActionEvent( PagelistPanel.this, ActionEvent.ACTION_PERFORMED, "copyPageView-" + popupItem ) );
}
} );
pageviewPopupMenu .add( menuItem );
MouseListener pageviewPopup = new ContextualMenuMouseListener( controller, pageviewPopupMenu );
listModel = new DefaultListModel<>();
int initialCount = Integer .parseInt( controller .getProperty( "num.pages" ) );
for ( int i = 0; i < initialCount; i++ )
{
ImageIcon icon = new ImageIcon( new BufferedImage( 80, 70, BufferedImage .TYPE_INT_RGB ) );
listModel .addElement( icon );
}
ListMoves moves = new ListMoves();
listModel .addListDataListener( moves );
// Create the list and put it in a scroll pane.
list = isEditor
? new ReorderableJList<>( listModel, moves, ImageIcon.class )
: new JList<>( listModel );
list .setSelectionMode( ListSelectionModel.SINGLE_SELECTION );
list .setSelectedIndex( 0 );
list .setVisibleRowCount( 12 );
list .addMouseListener( pageviewPopup );
JScrollPane listScrollPane = new JScrollPane( list );
if ( ! isEditor )
list .setCellRenderer( new ThumbnailSelectionRenderer() );
list .addListSelectionListener( new ListSelectionListener()
{
@Override
public void valueChanged( ListSelectionEvent lse )
{
if ( lse .getValueIsAdjusting() )
return;
int selected = list .getSelectedIndex();
if ( selected < 0 )
return;
String action = "elementSelected-" + selected;
PagelistPanel .this .controller .actionPerformed( new ActionEvent( PagelistPanel.this, ActionEvent.ACTION_PERFORMED, action ) );
}
} );
list .addMouseListener( new MouseAdapter()
{
// we need this so that an extra click can be used to restore the page view
@Override
public void mouseClicked( MouseEvent e )
{
if ( SwingUtilities.isRightMouseButton( e ) )
return;
int selected = list .getSelectedIndex();
if ( selected < 0 )
return;
String action = "elementSelected-" + selected;
PagelistPanel .this .controller .actionPerformed( new ActionEvent( PagelistPanel.this, ActionEvent.ACTION_PERFORMED, action ) );
}
});
add( listScrollPane, BorderLayout.CENTER );
if ( this.isEditor )
{
addButton = new JButton( dupeString );
addButton.setActionCommand( "duplicatePage" );
addButton.addActionListener( controller );
addButton.setEnabled( true );
removeButton = new JButton( remString );
removeButton.setActionCommand( "deletePage" );
removeButton.addActionListener( controller );
removeButton.setEnabled( controller .propertyIsTrue( "has.pages" ) );
// Create a panel that uses BoxLayout.
JPanel buttonPane = new JPanel();
buttonPane .setLayout( new BoxLayout( buttonPane, BoxLayout.PAGE_AXIS ) );
buttonPane .add( addButton );
buttonPane .add( removeButton );
buttonPane .setBorder( BorderFactory.createEmptyBorder( 5, 5, 5, 5 ) );
add( buttonPane, BorderLayout.PAGE_END );
}
}
@Override
public void propertyChange( PropertyChangeEvent evt )
{
if ( evt .getPropertyName() .equals( "currentPage" ) )
{
Integer pageNum = (Integer) evt .getNewValue();
int num = pageNum .intValue();
list .setSelectedIndex( num );
list .ensureIndexIsVisible( num );
}
else if ( "has.pages" .equals( evt .getPropertyName() ) )
{
if ( removeButton != null )
{
boolean enable = evt .getNewValue() .toString() .equals( "true" );
removeButton .setEnabled( enable );
}
}
else if ( evt .getPropertyName() .startsWith( "newElementAddedAt-" ) )
{
String pageNum = evt .getPropertyName() .substring( "newElementAddedAt-".length() );
int num = Integer .parseInt( pageNum );
ImageIcon icon = new ImageIcon( new BufferedImage( 80, 70, BufferedImage .TYPE_INT_RGB ) );
listModel .insertElementAt( icon, num );
list .setSelectedIndex( num );
}
else if ( evt .getPropertyName() .startsWith( "pageRemovedAt-" ) )
{
String pageNum = evt .getPropertyName() .substring( "pageRemovedAt-".length() );
int num = Integer .parseInt( pageNum );
listModel .remove( num );
}
else if ( evt .getPropertyName() .startsWith( "thumbnailChanged-" ) )
{
String editNumber = evt .getPropertyName() .substring( "thumbnailChanged-".length() );
final int num = Integer .parseInt( editNumber );
BufferedImage iconImage = (BufferedImage) evt .getNewValue();
if ( logger .isLoggable( Level.FINER ) )
logger .finer( "thumbnailRendered: " + iconImage + " for page " + num );
ImageIcon icon = new ImageIcon( iconImage );
if ( num >= listModel .size() )
listModel .insertElementAt( icon, num );
else
listModel .setElementAt( icon, num );
}
}
}