package kiyut.ekspos;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;
import javax.imageio.ImageReader;
import javax.imageio.event.IIOReadProgressListener;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFrame;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JToolBar;
import javax.swing.SwingConstants;
import kiyut.swing.util.MenuFactory;
import kiyut.ekspos.prefs.*;
import kiyut.swing.statusbar.StatusBar;
import kiyut.swing.statusbar.StatusBarItem;
/** ViewerWindow
*/
public class ViewerWindow extends JFrame {
private static final String CLOSE_ACTION_COMMAND = "Close";
private static final String NEXT_ACTION_COMMAND = "Next";
private static final String PREVIOUS_ACTION_COMMAND = "Previous";
private static final String RELOAD_ACTION_COMMAND = "Reload";
private static final String ZOOM_OUT_ACTION_COMMAND = "ZoomOut";
private static final String ZOOM_IN_ACTION_COMMAND = "ZoomIn";
private static final String FIT_TO_SCREEN_ACTION_COMMAND = "FitToScreen";
private static ResourceBundle bundle = ResourceBundle.getBundle("kiyut.ekspos.ViewerWindow");
private Map<String,Action> actionMap;
private JScrollBar vBar, hBar;
private int curX;
private int curY;
private List<File> files;
private int index;
private double curScale;
private JToolBar toolBar;
private StatusBar statusBar;
private JScrollPane scrollPane = new JScrollPane();
private ImagePane imagePane = new ImagePane();
private boolean fitToScreen;
public ViewerWindow() {
setIconImage(Application.getIconImage());
imagePane.setBackground(Color.WHITE);
imagePane.setPreferredSize(new Dimension(500, 500));
//imagePane.setFocusable(true);
//imagePane.requestFocusInWindow();
imagePane.addIIOReadProgressListener(new IIOReadProgressListener() {
public void imageComplete(ImageReader source) { onImageCompleted(source); }
public void imageProgress(ImageReader source, float percentageDone) { onImageProgress(source,percentageDone); }
public void imageStarted(ImageReader source, int imageIndex) { onImageStarted(source,imageIndex); }
public void readAborted(ImageReader source) { }
public void sequenceComplete(ImageReader source) { }
public void sequenceStarted(ImageReader source, int minIndex) { }
public void thumbnailComplete(ImageReader source) { }
public void thumbnailProgress(ImageReader source, float percentageDone) { }
public void thumbnailStarted(ImageReader source, int imageIndex, int thumbnailIndex) { }
});
scrollPane.setFocusable(true);
scrollPane.requestFocusInWindow();
scrollPane.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
@Override
public void mouseDragged(MouseEvent e) { onScrollPaneMouseDragged(e); }
});
scrollPane.addMouseListener(new java.awt.event.MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) { onScrollPaneMousePressed(e); }
@Override
public void mouseClicked(MouseEvent e) { onScrollPaneMouseClicked(e); }
});
scrollPane.addKeyListener(new java.awt.event.KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) { onScrollPaneKeyPressed(e); }
});
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
scrollPane.getViewport().setBackground(Color.black);
scrollPane.setViewportView(imagePane);
vBar = scrollPane.getVerticalScrollBar();
hBar = scrollPane.getHorizontalScrollBar();
vBar.setUnitIncrement(20);
hBar.setUnitIncrement(20);
statusBar = new StatusBar();
StatusBarItem statusBarItem = new StatusBarItem(" ",75,SwingConstants.CENTER);
statusBar.addItem(statusBarItem);
statusBarItem = new StatusBarItem(" ",100,SwingConstants.CENTER);
statusBar.addItem(statusBarItem);
statusBarItem = new StatusBarItem(" ",60,SwingConstants.CENTER);
statusBar.addItem(statusBarItem);
statusBarItem = new StatusBarItem(" ",100,SwingConstants.LEFT);
statusBar.addItem(statusBarItem);
//setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
initActions();
this.getContentPane().setBackground(Color.black);
this.getContentPane().setLayout(new BorderLayout());
this.getContentPane().add(toolBar, BorderLayout.NORTH);
this.getContentPane().add(scrollPane, BorderLayout.CENTER);
this.getContentPane().add(statusBar,BorderLayout.SOUTH);
files = new ArrayList<File>();
index = 0;
pack();
}
/**Overridden so we can exit when window is closed*/
@Override
protected void processWindowEvent(WindowEvent e) {
super.processWindowEvent(e);
if (e.getID() == WindowEvent.WINDOW_CLOSING) {
GeneralPreferences.getInstance().putBoolean(GeneralPreferences.FIT_TO_WINDOW_KEY, fitToScreen);
GeneralPreferences.getInstance().save();
}
}
/** Initialize Actions, MenuBar and ToolBar
*/
private void initActions() {
actionMap = new HashMap<String,Action>();
// create action
actionMap.put(CLOSE_ACTION_COMMAND,new ViewerAction(CLOSE_ACTION_COMMAND));
actionMap.put(PREVIOUS_ACTION_COMMAND,new ViewerAction(PREVIOUS_ACTION_COMMAND));
actionMap.put(NEXT_ACTION_COMMAND,new ViewerAction(NEXT_ACTION_COMMAND));
actionMap.put(RELOAD_ACTION_COMMAND,new ViewerAction(RELOAD_ACTION_COMMAND));
actionMap.put(ZOOM_IN_ACTION_COMMAND,new ViewerAction(ZOOM_IN_ACTION_COMMAND));
actionMap.put(ZOOM_OUT_ACTION_COMMAND,new ViewerAction(ZOOM_OUT_ACTION_COMMAND));
actionMap.put(FIT_TO_SCREEN_ACTION_COMMAND,new ViewerAction(FIT_TO_SCREEN_ACTION_COMMAND));
// inititalize action from ResourceBundle
MenuFactory.configureActionMap(bundle,actionMap);
// initialize menuBar
JMenuBar menuBar = MenuFactory.createMenuBar(bundle,actionMap);
setJMenuBar(menuBar);
boolean b = GeneralPreferences.getInstance().getBoolean(GeneralPreferences.FIT_TO_WINDOW_KEY);
JMenuItem menuItem = MenuFactory.getMenuItem(getJMenuBar().getSubElements(),FIT_TO_SCREEN_ACTION_COMMAND);
menuItem.setIcon(null);
((JCheckBoxMenuItem)menuItem).setSelected(b);
fitToScreen = b;
// initialize ToolBar
toolBar = new JToolBar();
toolBar.setFloatable(true);
toolBar.setRollover(true);
toolBar.add(createToolBarButton(actionMap.get(PREVIOUS_ACTION_COMMAND)));
toolBar.add(createToolBarButton(actionMap.get(NEXT_ACTION_COMMAND)));
toolBar.add(createToolBarButton(actionMap.get(RELOAD_ACTION_COMMAND)));
toolBar.addSeparator();
toolBar.add(createToolBarButton(actionMap.get(ZOOM_IN_ACTION_COMMAND)));
toolBar.add(createToolBarButton(actionMap.get(ZOOM_OUT_ACTION_COMMAND)));
toolBar.addSeparator();
toolBar.add(createToolBarButton(actionMap.get(FIT_TO_SCREEN_ACTION_COMMAND)));
}
/**
* Create ToolBar Button
* @param action <code>Action</code> for the button
* @return <code>JButton </code>
*/
private JButton createToolBarButton(Action action) {
JButton button = new JButton(action);
button.setText("");
button.setFocusable(false);
return button;
}
/** set Files, it also reset the index to 0
* @param list of files
*/
public void setFiles(List<File> files) {
this.files = files;
index = 0;
}
/** add files to the list
* @param list of files
*/
public void addFiles(List<File> files) {
this.files.addAll(files);
}
/** set the index for list of files to be shown first
* @param index
*/
public void setIndex(int index) {
this.index = index;
}
/** set FitToScreen
*@param b true / false
*/
protected void setFitToScreen(boolean b) {
fitToScreen = b;
// update MenuItem
JCheckBoxMenuItem menuItem = (JCheckBoxMenuItem)MenuFactory.getMenuItem(getJMenuBar().getSubElements(),FIT_TO_SCREEN_ACTION_COMMAND);
if (menuItem.isSelected() != fitToScreen) {
menuItem.setSelected(fitToScreen);
}
if (fitToScreen == true) {
BufferedImage img = imagePane.getImage();
if (img == null) {
return;
}
int width = img.getWidth();
int height = img.getHeight();
imageResized(width,height);
}
refresh();
}
/** Image has been resized, adjust the frame size
*@param width Image width
*@param height Image height
*/
protected void imageResized(int width, int height) {
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets(this.getGraphicsConfiguration());
//Point p = scrollPane.getLocation();
Point p1 = this.getLocationOnScreen();
Point p2 = scrollPane.getLocationOnScreen();
int x = (p2.x - p1.x) * 2; // currently it is only frame border
int y = (p2.y - p1.y) + statusBar.getBounds().height + (x*2); // x*2 = frameBorder
int maxWidth = screenSize.width - (screenInsets.left + screenInsets.right) - x;
int maxHeight = screenSize.height - (screenInsets.top + screenInsets.bottom) - y;
int state = JFrame.NORMAL;
if (maxWidth <= width && maxHeight <= height) {
state = JFrame.MAXIMIZED_BOTH;
} else if (maxWidth <= width) {
state = JFrame.MAXIMIZED_HORIZ;
} else if (maxHeight <= height) {
state = JFrame.MAXIMIZED_VERT;
}
if (Toolkit.getDefaultToolkit().isFrameStateSupported(state)) {
setExtendedState(state);
} else {
setExtendedState(JFrame.MAXIMIZED_BOTH);
}
Dimension imgSize = new Dimension(width,height);
Dimension areaSize = new Dimension(maxWidth,maxHeight);
Dimension newSize = null;
if (state == JFrame.NORMAL) {
curScale = 1;
newSize = imgSize;
} else {
if (fitToScreen == true ) {
curScale = kiyut.swing.shell.image.ImageUtilities.scaleToFit(imgSize,areaSize);
newSize = areaSize;
if (state == JFrame.MAXIMIZED_HORIZ) {
newSize.height = imgSize.height;
} else if (state == JFrame.MAXIMIZED_VERT) {
newSize.width = imgSize.width;
}
} else {
curScale = 1;
newSize = imgSize;
}
}
final Dimension fNewSize = newSize;
final int fState = state;
EventQueue.invokeLater(new Runnable() {
public void run() {
imagePane.setImageRescale(curScale,curScale);
if ( fNewSize != null && !imagePane.getSize().equals(fNewSize)) {
imagePane.setSize(fNewSize);
imagePane.setPreferredSize(fNewSize);
imagePane.revalidate();
}
if (fState == JFrame.NORMAL) {
pack();
}
repaint();
}
});
}
/** Refresh this window/frame
*/
public void refresh() {
File file = files.get(index);
setTitle(file.toString() + " - " + Application.getName());
statusBar.getItem(0).setText((index + 1) + "/" + files.size());
curScale = 1;
imagePane.setProgressRepaint(GeneralPreferences.getInstance().getDouble(GeneralPreferences.PROGRESS_REPAINT_KEY));
imagePane.view(file);
}
/** show next image on the list
*/
public void showNext() {
if (index < files.size()-1) {
index++;
} else {
index = 0;
}
refresh();
}
/** show previous image on the list
*/
public void showPrevious() {
if (index > 0) {
index--;
} else {
index = files.size() - 1;
}
refresh();
}
/** Zoom In / Zoom Out
* @param bigger true/false
*/
protected void zoom(boolean bigger) {
double increment = 0.1;
double scale = curScale;
if (bigger == true) {
scale = scale + increment;
} else {
scale = scale + (-increment);
}
if (scale < increment) {
scale = increment;
return;
} else if (scale > 16) {
scale = 16;
return;
}
zoom(scale);
}
/** Zoom by Scale Factor
* @param scale the Scale Factor
*/
protected void zoom(double scale) {
curScale = scale;
imagePane.setImageRescale(curScale,curScale);
//imagePane.repaint();
BufferedImage img = imagePane.getImage();
if (img == null) {
return;
}
int width = (int)Math.ceil(img.getWidth() * scale);
int height = (int)Math.ceil(img.getHeight() * scale);
Dimension imgSize = new Dimension(width,height);
imagePane.setSize(imgSize);
imagePane.setPreferredSize(imgSize);
imagePane.revalidate();
}
///////////////////////////////
// Event Handler
///////////////////////////////
protected void onImageStarted(ImageReader source, int imageIndex) {
int width = 0;
int height = 0;
try {
width = source.getWidth(0);
height = source.getHeight(0);
} catch (IOException ex) { }
statusBar.getItem(1).setText(width + "x" + height);
statusBar.getItem(2).setText("0%");
imageResized(width,height);
}
protected void onImageProgress(ImageReader source, final float percentageDone) {
EventQueue.invokeLater(new Runnable() {
public void run() {
statusBar.getItem(2).setText(Math.round(percentageDone) + "%");
}
});
}
protected void onImageCompleted(ImageReader source) {
EventQueue.invokeLater(new Runnable() {
public void run() {
statusBar.getItem(2).setText("100%");
}
});
}
protected void onScrollPaneKeyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER
|| e.getKeyCode() == KeyEvent.VK_ESCAPE ) {
Action action = actionMap.get(CLOSE_ACTION_COMMAND);
action.actionPerformed(new ActionEvent(this,ActionEvent.ACTION_PERFORMED,CLOSE_ACTION_COMMAND));
} else if (e.getKeyCode()==KeyEvent.VK_SPACE
|| e.getKeyCode()==KeyEvent.VK_PAGE_DOWN) {
Action action = actionMap.get(NEXT_ACTION_COMMAND);
action.actionPerformed(new ActionEvent(this,ActionEvent.ACTION_PERFORMED,NEXT_ACTION_COMMAND));
} else if (e.getKeyCode()==KeyEvent.VK_BACK_SPACE
|| e.getKeyCode()==KeyEvent.VK_PAGE_UP) {
Action action = actionMap.get(PREVIOUS_ACTION_COMMAND);
action.actionPerformed(new ActionEvent(this,ActionEvent.ACTION_PERFORMED,PREVIOUS_ACTION_COMMAND));
} else if (e.getKeyCode()==KeyEvent.VK_HOME) {
index = 0;
refresh();
} else if (e.getKeyCode()==KeyEvent.VK_END) {
index = files.size()-1;
refresh();
} else if (e.getKeyCode()==KeyEvent.VK_PLUS
|| e.getKeyCode()==KeyEvent.VK_ADD) {
Action action = actionMap.get(ZOOM_IN_ACTION_COMMAND);
action.actionPerformed(new ActionEvent(this,ActionEvent.ACTION_PERFORMED,ZOOM_IN_ACTION_COMMAND));
} else if (e.getKeyCode()==KeyEvent.VK_MINUS
|| e.getKeyCode()==KeyEvent.VK_SUBTRACT) {
Action action = actionMap.get(ZOOM_OUT_ACTION_COMMAND);
action.actionPerformed(new ActionEvent(this,ActionEvent.ACTION_PERFORMED,ZOOM_OUT_ACTION_COMMAND));
} else if (e.getKeyCode()==KeyEvent.VK_UP){
vBar.setValue(vBar.getValue() - vBar.getBlockIncrement());
} else if (e.getKeyCode()==KeyEvent.VK_DOWN){
vBar.setValue(vBar.getValue() + vBar.getBlockIncrement());
} else if (e.getKeyCode()==KeyEvent.VK_LEFT){
hBar.setValue(hBar.getValue() - hBar.getBlockIncrement());
} else if (e.getKeyCode()==KeyEvent.VK_RIGHT ){
hBar.setValue(hBar.getValue() + hBar.getBlockIncrement());
}
e.consume();
}
protected void onScrollPaneMouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
Action action = actionMap.get(CLOSE_ACTION_COMMAND);
action.actionPerformed(new ActionEvent(this,ActionEvent.ACTION_PERFORMED,CLOSE_ACTION_COMMAND));
}
}
protected void onScrollPaneMouseDragged(MouseEvent e) {
final int newX = e.getX();
final int newY = e.getY();
EventQueue.invokeLater(new Runnable() {
public void run() {
if (curX < newX) {
hBar.setValue(hBar.getValue() - (newX-curX));
} else {
hBar.setValue(hBar.getValue() + (curX-newX));
}
if (curY < newY) {
vBar.setValue(vBar.getValue() - (newY-curY));
} else {
vBar.setValue(vBar.getValue() + (curY-newY));
}
curX = newX;
curY = newY;
}
});
}
protected void onScrollPaneMousePressed(MouseEvent e) {
curX = e.getX();
curY = e.getY();
}
private class ViewerAction extends AbstractAction {
public ViewerAction(String actionCommand) {
putValue(Action.ACTION_COMMAND_KEY,actionCommand);
}
/** {@inheritDoc} */
public void actionPerformed(ActionEvent evt) {
final ActionEvent fEvt = evt;
EventQueue.invokeLater(new Runnable() {
public void run() {
actionPerformedImpl(fEvt);
}
});
}
/** Make it easy to run under EDT */
private void actionPerformedImpl(ActionEvent evt) {
String actionCommand = evt.getActionCommand();
if (actionCommand.equals(CLOSE_ACTION_COMMAND)) {
ViewerWindow.this.dispatchEvent(new WindowEvent(ViewerWindow.this,WindowEvent.WINDOW_CLOSING));
} else if(actionCommand.equals(PREVIOUS_ACTION_COMMAND)) {
showPrevious();
} else if(actionCommand.equals(NEXT_ACTION_COMMAND)) {
showNext();
} else if(actionCommand.equals(RELOAD_ACTION_COMMAND)) {
refresh();
} else if(actionCommand.equals(ZOOM_OUT_ACTION_COMMAND)) {
zoom(false);
} else if(actionCommand.equals(ZOOM_IN_ACTION_COMMAND)) {
zoom(true);
} else if(actionCommand.equals(FIT_TO_SCREEN_ACTION_COMMAND)) {
if (evt.getSource() instanceof JCheckBoxMenuItem) {
JCheckBoxMenuItem menuItem = (JCheckBoxMenuItem)evt.getSource();
setFitToScreen(menuItem.isSelected());
} else {
//setFitToScreen(true);
setFitToScreen(!fitToScreen);
}
}
}
}
}