/*
* Copyright 2003 (C) Ross M. Lodge
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package plugin.dicebag.gui;
import pcgen.core.SettingsHandler;
import plugin.dicebag.DiceBagPlugin;
import javax.swing.JDesktopPane;
import javax.swing.JOptionPane;
import javax.swing.WindowConstants;
import javax.swing.event.InternalFrameAdapter;
import javax.swing.event.InternalFrameEvent;
import java.awt.Color;
import java.awt.Component;
import java.io.File;
import java.util.Observable;
import java.util.Observer;
/**
*
* <p>The view class for the DiceBag plugin. Should manage and initialize
* all GUI components. Should delegate all user actions to the controller class.</p>
* @author Ross M. Lodge
*/
public class DiceBagPluginView implements Observer
{
/** The model */
private DiceBagPluginModel m_model;
/** Listener for internal frame events */
private InternalFrameAdapter listener = new ChildListener();
/** The desktop pane */
private JDesktopPane theDesktop = null;
/** Coords for a new bag */
private int newX = 0;
private int newY = 0;
/**
* <p>Default (and only) constructor. Initializes the components.</p>
*
* @param o The observable object.
*/
public DiceBagPluginView(DiceBagPluginModel o)
{
super();
o.addObserver(this);
m_model = o;
initComponents();
}
/**
* <p>Returns the root component, the one that will
* be placed in the main tab pane.</p>
*
* @return The main or root component for this view.
*/
public Component getMainComponent()
{
return theDesktop;
}
/**
* <p>Handles the close all message; requests save for all bags.</p>
*/
public void closeAll()
{
Component[] frames = theDesktop.getComponents();
StringBuilder files = new StringBuilder();
for (int i = 0; i < frames.length; i++)
{
if (frames[i] instanceof DiceBagView)
{
DiceBagModel bag = ((DiceBagView) frames[i]).getBag();
askSaveBag(bag, JOptionPane.YES_NO_OPTION);
if (!bag.isChanged() && !bag.isBagEmpty())
{
files.append(bag.getFilePath() + "|");
}
}
}
SettingsHandler.setGMGenOption(DiceBagPlugin.LOG_NAME + "closeFiles",
files.toString());
}
/**
* <p>Handler for frame activation -- manages the currently active bag
* information.</p>
*
* @param e The event that fired this handler.
*/
public void internalFrameActivated(InternalFrameEvent e)
{
if ((e.getInternalFrame() != null)
&& e.getInternalFrame() instanceof DiceBagView)
{
m_model.setActiveBag(((DiceBagView) e.getInternalFrame()).getBag());
}
}
/**
* <p>Handles closing events -- calls the model {@code closeDiceBag()}
* code.</p>
*
* @param e The event that fired this handler.
*/
public void internalFrameClosed(InternalFrameEvent e)
{
if ((e.getInternalFrame() != null)
&& e.getInternalFrame() instanceof DiceBagView)
{
m_model.closeDiceBag(((DiceBagView) e.getInternalFrame()).getBag());
}
}
/**
* <p>Handles the frame closing event; alows the user to choose
* whether or not to save or cancel, and vetoes the close if cancel.</p>
*
* @param e The event which fired this handler.
*/
public void internalFrameClosing(InternalFrameEvent e)
{
if ((e.getInternalFrame() != null)
&& e.getInternalFrame() instanceof DiceBagView)
{
final int answer =
askSaveBag(((DiceBagView) e.getInternalFrame()).getBag(),
JOptionPane.YES_NO_CANCEL_OPTION);
if (answer == JOptionPane.CANCEL_OPTION)
{
e.getInternalFrame().setDefaultCloseOperation(
WindowConstants.DO_NOTHING_ON_CLOSE);
}
else if ((answer == JOptionPane.NO_OPTION)
&& (answer == JOptionPane.YES_OPTION))
{
e.getInternalFrame().setDefaultCloseOperation(
WindowConstants.HIDE_ON_CLOSE);
}
}
}
/* (non-Javadoc)
* @see java.util.Observer#update(java.util.Observable, java.lang.Object)
*
* Forwards update messages on to the appropriate handlers.
*/
@Override
public void update(Observable o, Object arg)
{
if ((o != null) && o instanceof DiceBagPluginModel && (arg != null)
&& arg instanceof DiceBagMessage)
{
DiceBagMessage msg = (DiceBagMessage) arg;
switch (msg.getType())
{
case DiceBagMessage.ALL_DICE_BAGS_REMOVED:
allDiceBagsRemoved();
break;
case DiceBagMessage.DICE_BAG_ADDED:
diceBagAdded(msg.getDiceBag());
break;
case DiceBagMessage.DICE_BAG_REMOVED:
diceBagRemoved(msg.getDiceBag());
break;
case DiceBagMessage.DICE_BAG_SAVED:
diceBagSaved(msg.getDiceBag());
break;
case DiceBagMessage.MODEL_INITIALIZED:
modelInitialized();
break;
default:
break;
}
}
}
/**
* <p>Handles the all dice bags removed message.</p>
*/
private void allDiceBagsRemoved()
{
Component[] frames = theDesktop.getComponents();
for (int i = 0; i < frames.length; i++)
{
if (frames[i] instanceof DiceBagView)
{
((DiceBagView) frames[i]).hide();
}
}
}
/**
* <p>Displays an option dialog with the specified {@code option}
* value and either saves the dice bag or not based on the response.
* If the cancel option is chosen or the user aborts the save dialog,
* {@code JOptionPane.CANCEL_OPTION} is returned instead
* of yes or no. If the bag has not been changed since creation or
* loading, {@code JOptionPane.NO_OPTION} is returned.</p>
*
* @param bag The bag that needs saving.
* @param option One of the JOptionPane constants (like {@code YES_NO_OPTION}
* for display in the option pane.
* @return The selection option
*/
private int askSaveBag(DiceBagModel bag, int option)
{
int returnValue = JOptionPane.CANCEL_OPTION;
if (bag.isChanged())
{
returnValue =
JOptionPane.showConfirmDialog(getMainComponent(),
"Do you want to save your changes to dicebag "
+ bag.getName() + "?", "Save?", option);
if (returnValue == JOptionPane.YES_OPTION)
{
if ((bag.getFilePath() != null)
&& (!bag.getFilePath().isEmpty()))
{
m_model.saveDiceBag(bag);
}
else
{
final File saveFile =
DiceBagPluginController.chooseSaveFile(bag);
if (saveFile != null)
{
m_model.saveDiceBag(bag, saveFile);
}
else
{
//Use cancel here because the user chose to abort the save dialog
returnValue = JOptionPane.CANCEL_OPTION;
}
}
}
}
else
{
returnValue = JOptionPane.NO_OPTION;
}
return returnValue;
}
/**
* <p>Handles the dice bag added message; instantiates a new
* internal frame.</p>
*
* @param model
*/
private void diceBagAdded(DiceBagModel model)
{
DiceBagView view = new DiceBagView(model);
view.addInternalFrameListener(listener);
theDesktop.add(view);
view.setLocation(newX, newY);
newX += 20;
newY += 20;
if (!theDesktop.getBounds().contains(newX + 40, newY + 40))
{
newX = 0;
newY = 0;
}
view.setVisible(true);
}
/**
* <p>Handles the dice bag removed message; removes
* the frame if its open.</p>
*
* @param model
*/
private void diceBagRemoved(DiceBagModel model)
{
Component[] frames = theDesktop.getComponents();
for (int i = 0; i < frames.length; i++)
{
if (frames[i] instanceof DiceBagView)
{
if (((DiceBagView) frames[i]).getBag() == model)
{
((DiceBagView) frames[i]).hide();
}
}
}
}
/**
* <p>Does nothing.</p>
*
* @param model
*/
private void diceBagSaved(DiceBagModel model)
{
// Do nothing . . .
}
/**
* <p>Initializes all the components of the view.</p>
*/
private void initComponents()
{
theDesktop = new JDesktopPane();
theDesktop.setBackground(Color.LIGHT_GRAY);
}
/**
* <p>Does nothing.</p>
*/
private void modelInitialized()
{
// Do nothing . . .
}
/**
* <p>Listener for events on the internal frame children of this view.</p>
*
* @author Ross M. Lodge
*/
private class ChildListener extends InternalFrameAdapter
{
/* (non-Javadoc)
* @see javax.swing.event.InternalFrameListener#internalFrameActivated(javax.swing.event.InternalFrameEvent)
*/
@Override
public void internalFrameActivated(InternalFrameEvent e)
{
DiceBagPluginView.this.internalFrameActivated(e);
}
/* (non-Javadoc)
* @see javax.swing.event.InternalFrameListener#internalFrameClosed(javax.swing.event.InternalFrameEvent)
*/
@Override
public void internalFrameClosed(InternalFrameEvent e)
{
DiceBagPluginView.this.internalFrameClosed(e);
}
/* (non-Javadoc)
* @see javax.swing.event.InternalFrameListener#internalFrameClosing(javax.swing.event.InternalFrameEvent)
*/
@Override
public void internalFrameClosing(InternalFrameEvent e)
{
DiceBagPluginView.this.internalFrameClosing(e);
}
}
}