package org.vorthmann.zome.ui;
import static org.vorthmann.zome.ui.ApplicationUI.getLogFileName;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.DisplayMode;
import java.awt.FileDialog;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.AbstractButton;
import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JColorChooser;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JRadioButton;
import javax.swing.JTabbedPane;
import javax.swing.ToolTipManager;
import org.vorthmann.j3d.J3dComponentFactory;
import org.vorthmann.j3d.Platform;
import org.vorthmann.ui.Controller;
import org.vorthmann.ui.DefaultController;
import org.vorthmann.ui.ExclusiveAction;
import com.vzome.desktop.controller.ViewPlatformControlPanel;
public class DocumentFrame extends JFrame implements PropertyChangeListener, ControlActions
{
private static final long serialVersionUID = 1L;
protected final Controller mController, toolsController;
private final ModelPanel modelPanel;
private final JTabbedPane tabbedPane = new JTabbedPane();
private final ExclusiveAction.Excluder mExcluder = new ExclusiveAction.Excluder( this );
private boolean isEditor, fullPower, readerPreview, canSave;
private CardLayout modelArticleCardLayout;
private static final Logger logger = Logger.getLogger( "org.vorthmann.zome.ui" );
private LessonPanel lessonPanel;
private JFrame zomicFrame, pythonFrame;
private JButton snapshotButton;
private JRadioButton articleButton, modelButton;
private JPanel viewControl, modelArticleEditPanel;
private JLabel statusText;
private Controller viewPlatform;
private Controller lessonController;
private JDialog polytopesDialog;
private final boolean developerExtras;
private final ActionListener localActions;
private File mFile = null;
private final Controller.ErrorChannel errors;
private Snapshot2dFrame snapshot2dFrame;
private ControllerFileAction saveAsAction;
private PropertyChangeListener appUI;
public ExclusiveAction.Excluder getExcluder()
{
return mExcluder;
}
private void createLessonPanel()
{
if ( lessonPanel != null )
return;
lessonPanel = new LessonPanel( lessonController );
modelArticleEditPanel .add( lessonPanel, "article" );
}
void setAppUI( PropertyChangeListener appUI )
{
this .appUI = appUI;
}
public DocumentFrame( final Controller controller )
{
mController = controller;
mController .addPropertyListener( this );
toolsController = mController .getSubController( "tools" );
String path = mController .getProperty( "window.file" );
if ( path != null )
this .mFile = new File( path ); // this enables "save" in localActions
// TODO: compute these booleans once here, and don't recompute in DocumentMenuBar
this.readerPreview = mController .propertyIsTrue( "reader.preview" );
this.isEditor = mController .userHasEntitlement( "model.edit" ) && ! readerPreview;
this.canSave = mController .userHasEntitlement( "save.files" );
this.fullPower = isEditor ;//&& controller .userHasEntitlement( "all.tools" );
this.developerExtras = fullPower && mController .userHasEntitlement( "developer.extras" );
errors = new Controller.ErrorChannel()
{
@Override
public void reportError( String errorCode, Object[] arguments )
{
if ( Controller.USER_ERROR_CODE.equals( errorCode ) ) {
errorCode = ( (Exception) arguments[0] ).getMessage();
// don't want a stack trace for a user error
logger.log( Level.WARNING, errorCode );
} else if ( Controller.UNKNOWN_ERROR_CODE.equals( errorCode ) ) {
errorCode = ( (Exception) arguments[0] ).getMessage();
logger.log( Level.WARNING, "internal error: " + errorCode, ( (Exception) arguments[0] ) );
errorCode = "internal error, see the log file at " + getLogFileName();
} else {
logger.log( Level.WARNING, "reporting error: " + errorCode, arguments );
// TODO use resources
}
if ( statusText != null )
statusText .setText( errorCode );
else
JOptionPane .showMessageDialog( DocumentFrame.this, errorCode, "Command Failure", JOptionPane .ERROR_MESSAGE );
}
@Override
public void clearError()
{
if ( statusText != null )
statusText .setText( "" );
}
};
mController .setErrorChannel( errors );
// ---- catch-all ActionListener for locally-handled actions
final String initSystem = mController .getProperty( "symmetry" );
localActions = new ActionListener()
{
private String system = initSystem;
private final Map<String, JDialog> shapesDialogs = new HashMap<>();
private final Map<String, JDialog> directionsDialogs = new HashMap<>();
@Override
public void actionPerformed( ActionEvent e )
{
Controller delegate = mController;
String cmd = e.getActionCommand();
switch ( cmd ) {
case "close":
closeWindow();
break;
// case "openURL":
// String url = JOptionPane .showInputDialog( DocumentFrame.this, "Enter the URL for an online .vZome file.", "Open URL",
// JOptionPane.PLAIN_MESSAGE );
// try {
// mController .doAction( cmd, new ActionEvent( DocumentFrame.this, ActionEvent.ACTION_PERFORMED, url ) );
// } catch ( Exception ex )
// {
// ex .printStackTrace();
// // TODO better error report
// errors .reportError( Controller.USER_ERROR_CODE, new Object[]{ ex } );
// }
// break;
//
case "save":
if ( mFile == null ) {
saveAsAction .actionPerformed( e );
}
else {
mController .doFileAction( "save", mFile );
}
break;
case "saveDefault":
// this is basically "save a copy...", with a hard-coded file path.
String fieldName = mController.getProperty( "field.name" );
String fieldLabel = mController.getProperty( "field.label" );
File prototype = new File( Platform.getPreferencesFolder(), "Prototypes/" + fieldName + ".vZome" );
try {
String path = prototype.getCanonicalPath();
int response = JOptionPane.showConfirmDialog(
DocumentFrame.this,
"Save as the template for new models in the " + fieldLabel + " field?"
+ "\n\nTemplate file: " + path,
"Save Template?",
JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE );
if ( response == JOptionPane.YES_OPTION ) {
logger.config("Saving default template to " + path);
mController .doFileAction( "save", prototype );
}
else if ( prototype.exists() ) {
response = JOptionPane.showConfirmDialog(
DocumentFrame.this,
"Delete the existing template for new models in the " + fieldLabel + " field?"
+ "\n\nTemplate file: " + path,
"Delete Template?",
JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE );
if ( response == JOptionPane.YES_OPTION ) {
logger.config("Deleting default template at " + path);
prototype.delete();
}
}
} catch (IOException ex) {
errors .reportError( Controller.USER_ERROR_CODE, new Object[]{ ex } );
}
break;
case "snapshot.2d":
if ( snapshot2dFrame == null ) {
snapshot2dFrame = new Snapshot2dFrame( mController.getSubController( "snapshot.2d" ), new FileDialog( DocumentFrame.this ) );
}
snapshot2dFrame.setPanelSize( modelPanel .getRenderedSize() );
snapshot2dFrame.pack();
if ( ! snapshot2dFrame .isVisible() )
snapshot2dFrame.repaint();
snapshot2dFrame.setVisible( true );
break;
case "redoUntilEdit":
String number = JOptionPane.showInputDialog( null, "Enter the edit number.", "Set Edit Number",
JOptionPane.PLAIN_MESSAGE );
try {
mController .doAction( "redoUntilEdit." + number, new ActionEvent( DocumentFrame.this, ActionEvent.ACTION_PERFORMED, "redoUntilEdit." + number ) );
} catch ( Exception e1 ) {
errors .reportError( Controller.USER_ERROR_CODE, new Object[]{ e1 } );
}
break;
case "showToolsPanel":
tabbedPane .setSelectedIndex( 1 ); // should be "tools" tab
break;
case "showPolytopesDialog":
Controller polytopesController = mController .getSubController( "polytopes" );
if ( polytopesDialog == null )
polytopesDialog = new PolytopesDialog( DocumentFrame.this, polytopesController );
try {
polytopesController .doAction( "setQuaternion", new ActionEvent( DocumentFrame.this, ActionEvent.ACTION_PERFORMED, "setQuaternion" ) );
} catch ( Exception e1 ) {
errors .reportError( Controller.USER_ERROR_CODE, new Object[]{ e1 } );
}
polytopesDialog .setVisible( true );
break;
case "showPythonWindow":
if ( pythonFrame == null ) {
pythonFrame = new JFrame( "Python Scripting" );
pythonFrame .setContentPane( new PythonConsolePanel( pythonFrame, mController ) );
}
pythonFrame .pack();
pythonFrame .setVisible( true );
break;
case "showZomicWindow":
if ( zomicFrame == null ) {
zomicFrame = new JFrame( "Zomic Scripting" );
zomicFrame .setContentPane( new ZomicEditorPanel( zomicFrame, mController ) );
}
zomicFrame .pack();
zomicFrame .setVisible( true );
break;
case "setItemColor":
Color color = JColorChooser.showDialog( DocumentFrame.this, "Choose Object Color", null );
if ( color == null )
return;
int rgb = color .getRGB() & 0xffffff;
int alpha = color .getAlpha() & 0xff;
String command = "setItemColor/" + Integer.toHexString( ( rgb << 8 ) | alpha );
mController .actionPerformed( new ActionEvent( e .getSource(), e.getID(), command ) );
break;
case "setBackgroundColor":
color = JColorChooser.showDialog( DocumentFrame.this, "Choose Background Color", null );
if ( color != null )
mController .setProperty( "backgroundColor", Integer.toHexString( color.getRGB() & 0xffffff ) );
break;
case "usedOrbits":
mController .actionPerformed( e ); // TO DO exclusive
break;
case "rZomeOrbits":
case "predefinedOrbits":
case "setAllDirections":
delegate = mController .getSubController( "symmetry." + system );
delegate .actionPerformed( e );
break;
case "configureShapes":
JDialog shapesDialog = shapesDialogs.get( system );
if ( shapesDialog == null ) {
delegate = mController .getSubController( "symmetry." + system );
shapesDialog = new ShapesDialog( DocumentFrame.this, delegate );
shapesDialogs .put( system, shapesDialog );
}
shapesDialog .setVisible( true );
break;
case "configureDirections":
JDialog symmetryDialog = directionsDialogs.get( system );
if ( symmetryDialog == null ) {
delegate = mController .getSubController( "symmetry." + system );
symmetryDialog = new SymmetryDialog( DocumentFrame.this, delegate );
directionsDialogs .put( system, symmetryDialog );
}
symmetryDialog .setVisible( true );
break;
case "addBookmark":
String numStr = toolsController .getProperty( "next.tool.number" );
int bookmarkNum = Integer .parseInt( numStr );
String bookmarkId = "bookmark." + bookmarkNum;
String bookmarkName = (String) JOptionPane .showInputDialog( (Component) e .getSource(),
"Name the new bookmark:", "New Selection Bookmark",
JOptionPane.PLAIN_MESSAGE, null, null, bookmarkId );
if ( ( bookmarkName == null ) || ( bookmarkName .length() == 0 ) ) {
return;
}
mController .actionPerformed( new ActionEvent( e .getSource(), e.getID(), "newTool/" + bookmarkId + "/" + bookmarkName ) );
break;
default:
if ( cmd .startsWith( "setSymmetry." ) )
{
system = cmd .substring( "setSymmetry.".length() );
mController .actionPerformed( e ); // TODO getExclusiveAction
}
else if ( cmd .startsWith( "showProperties-" ) )
{
String key = cmd .substring( "showProperties-" .length() );
Controller subc = mController .getSubController( key + "Picking" );
JOptionPane .showMessageDialog( DocumentFrame.this, subc .getProperty( "objectProperties" ), "Object Properties",
JOptionPane.PLAIN_MESSAGE );
}
else
mController .actionPerformed( e );
break;
}
}
};
// -------------------------------------- create panels and tools
viewPlatform = mController .getSubController( "viewPlatform" );
lessonController = mController .getSubController( "lesson" );
lessonController .addPropertyListener( this );
// Now the component containment hierarchy
JPanel outerPanel = new JPanel( new BorderLayout() );
setContentPane( outerPanel );
{
JPanel leftCenterPanel = new JPanel( new BorderLayout() );
{
if ( this .isEditor )
{
JPanel modeAndStatusPanel = new JPanel( new BorderLayout() );
JPanel articleButtonsPanel = new JPanel();
modeAndStatusPanel .add( articleButtonsPanel, BorderLayout.LINE_START );
ButtonGroup group = new ButtonGroup();
modelButton = new JRadioButton( "Model" );
modelButton .setSelected( true );
articleButtonsPanel .add( modelButton );
group .add( modelButton );
modelButton .setActionCommand( "switchToModel" );
modelButton .addActionListener( mController );
snapshotButton = new JButton( "-> capture ->" );
// String imgLocation = "/icons/snapshot.png";
// URL imageURL = getClass().getResource( imgLocation );
// if ( imageURL != null ) {
// Icon icon = new ImageIcon( imageURL, "capture model snapshot" );
// snapshotButton .setIcon( icon );
// Dimension dim = new Dimension( 50, 36 );
// snapshotButton .setPreferredSize( dim );
// snapshotButton .setMaximumSize( dim );
// }
articleButtonsPanel .add( snapshotButton );
snapshotButton .setActionCommand( "takeSnapshot" );
snapshotButton .addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
mController .actionPerformed( e ); // sends thumbnailChanged propertyChange, but no listener...
articleButton .doClick(); // switch to article mode, so now there's a listener
// now trigger the propertyChange again
lessonController .actionPerformed( new ActionEvent( DocumentFrame.this, ActionEvent.ACTION_PERFORMED, "setView" ) );
}
} );
articleButton = new JRadioButton( "Article" );
articleButton .setEnabled( lessonController .propertyIsTrue( "has.pages" ) );
articleButton .setSelected( false );
articleButtonsPanel .add( articleButton );
group .add( articleButton );
articleButton .setActionCommand( "switchToArticle" );
articleButton .addActionListener( mController );
JPanel statusPanel = new JPanel( new BorderLayout() );
statusPanel .setBorder( BorderFactory .createEmptyBorder( 4, 4, 4, 4 ) );
statusText = new JLabel( "", JLabel.CENTER );
statusText .setBorder( BorderFactory .createLineBorder( Color.LIGHT_GRAY ) );
statusText .setForeground( Color .RED );
statusPanel .add( statusText );
modeAndStatusPanel .add( statusPanel, BorderLayout.CENTER );
leftCenterPanel .add( modeAndStatusPanel, BorderLayout.PAGE_START );
}
modelPanel = new ModelPanel( mController, this, this .isEditor, fullPower );
leftCenterPanel .add( modelPanel, BorderLayout.CENTER );
}
outerPanel.add( leftCenterPanel, BorderLayout.CENTER );
// String mag = props .getProperty( "default.magnification" );
// if ( mag != null ) {
// float magnification = Float .parseFloat( mag );
// // TODO this seems to work, but ought not to!
// viewControl .zoomAdjusted( (int) magnification );
// }
JPanel rightPanel = new JPanel( new BorderLayout() );
{
Component trackballCanvas = ( (J3dComponentFactory) mController ) .createJ3dComponent( "controlViewer" );
viewControl = new ViewPlatformControlPanel( trackballCanvas, viewPlatform );
// this is probably moot for reader mode
rightPanel .add( viewControl, BorderLayout.PAGE_START );
modelArticleEditPanel = new JPanel();
modelArticleCardLayout = new CardLayout();
modelArticleEditPanel .setLayout( modelArticleCardLayout );
if ( this .isEditor )
{
JPanel buildPanel = new StrutBuilderPanel( DocumentFrame.this, mController .getProperty( "symmetry" ), mController, this );
if ( this .fullPower )
{
tabbedPane .addTab( "build", buildPanel );
if ( mController .propertyIsTrue( "original.tools" ) )
{
ToolsPanel toolsPanel = new ToolsPanel( DocumentFrame.this, toolsController );
tabbedPane .addTab( "tools", toolsPanel );
}
JPanel bomPanel = new PartsPanel( mController .getSubController( "parts" ) );
tabbedPane .addTab( "parts", bomPanel );
modelArticleEditPanel .add( tabbedPane, "model" );
}
else
modelArticleEditPanel .add( buildPanel, "model" );
}
if ( this .isEditor )
{
modelArticleCardLayout .show( modelArticleEditPanel, "model" );
modelArticleEditPanel .setMinimumSize( new Dimension( 300, 500 ) );
modelArticleEditPanel .setPreferredSize( new Dimension( 300, 800 ) );
}
else
{
createLessonPanel();
modelArticleCardLayout .show( modelArticleEditPanel, "article" );
modelArticleEditPanel .setMinimumSize( new Dimension( 400, 500 ) );
modelArticleEditPanel .setPreferredSize( new Dimension( 400, 800 ) );
}
rightPanel .add( modelArticleEditPanel, BorderLayout.CENTER );
}
outerPanel.add( rightPanel, BorderLayout.LINE_END );
}
JPopupMenu.setDefaultLightWeightPopupEnabled( false );
ToolTipManager ttm = ToolTipManager.sharedInstance();
ttm .setLightWeightPopupEnabled( false );
Controller saveAsController = new DefaultController()
{
@Override
public void doFileAction( String command, final File file )
{
mController .doFileAction( command, file );
mFile = file;
String newTitle = file .getAbsolutePath();
mController .setProperty( "name", newTitle );
setTitle( newTitle );
}
};
this .saveAsAction = new ControllerFileAction( new FileDialog( DocumentFrame.this ), false, "save", "vZome", saveAsController );
this .setJMenuBar( new DocumentMenuBar( mController, this ) );
this.setDefaultCloseOperation( JFrame.DO_NOTHING_ON_CLOSE );
this.addWindowListener( new WindowAdapter()
{
@Override
public void windowClosing( WindowEvent we )
{
closeWindow();
}
} );
// Find the screen with the largest area if this is a multi-monitor system.
// Set the frame size to just a bit smaller than the screen
// so the frame will fit on the screen if the user un-maximizes it.
// Default to opening the window as maximized on the selected (or default) monitor.
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice[] gs = ge.getScreenDevices();
if(gs.length > 0) {
int bestIndex = 0;
GraphicsDevice bestDevice = gs[bestIndex];
DisplayMode bestMode = bestDevice.getDisplayMode();
int bestArea = bestMode.getHeight() * bestMode.getWidth();
for (int i = bestIndex+1; i < gs.length; i++) {
GraphicsDevice testDevice = gs[i];
DisplayMode testMode = testDevice.getDisplayMode();
int testArea = testMode.getHeight() * testMode.getWidth();
if(bestArea < testArea) {
bestArea = testArea;
bestMode = testMode;
bestDevice = testDevice;
}
}
Rectangle bounds = bestDevice.getDefaultConfiguration().getBounds();
this.setLocation(bounds.x, bounds.y);
int n = 15, d = n + 1; // set size to 15/16 of full screen size then maximize it
this.setSize(bestMode.getWidth() * n/d, bestMode.getHeight() * n/d);
}
this.setExtendedState(java.awt.Frame.MAXIMIZED_BOTH);
this.pack();
this.setVisible( true );
this.setFocusable( true );
new ExclusiveAction( this .getExcluder() )
{
@Override
protected void doAction( ActionEvent e ) throws Exception
{
mController .doAction( "finish.load", e );
String title = mController .getProperty( "window.title" );
boolean migrated = mController .propertyIsTrue( "migrated" );
boolean asTemplate = mController .propertyIsTrue( "as.template" );
URL url = null; // TODO
if ( ! mController .userHasEntitlement( "model.edit" ) )
{
mController .doAction( "switchToArticle", e );
if ( url != null )
title = url .toExternalForm();
migrated = false;
}
if ( ! asTemplate && migrated ) { // a migration
final String NL = System .getProperty( "line.separator" );
if ( mController .propertyIsTrue( "autoFormatConversion" ) )
{
if ( mController .propertyIsTrue( "formatIsSupported" ) )
JOptionPane .showMessageDialog( DocumentFrame.this,
"This document was created by an older version." + NL +
"If you save it now, it will be converted automatically" + NL +
"to the current format. It will no longer open using" + NL +
"the older version.",
"Automatic Conversion", JOptionPane.INFORMATION_MESSAGE );
else
{
title = null;
DocumentFrame.this .makeUnnamed();
JOptionPane .showMessageDialog( DocumentFrame.this,
"You have \"autoFormatConversion\" turned on," + NL +
"but the behavior is disabled until this version of vZome" + NL +
"is stable. This converted document is being opened as" + NL +
"a new document.",
"Automatic Conversion Disabled", JOptionPane.INFORMATION_MESSAGE );
}
}
else
{
title = null;
DocumentFrame.this .makeUnnamed();
JOptionPane .showMessageDialog( DocumentFrame.this,
"This document was created by an older version." + NL +
"It is being opened as a new document, so you can" + NL +
"still open the original using the older version.",
"Outdated Format", JOptionPane.INFORMATION_MESSAGE );
}
}
if ( title == null )
title = mController .getProperty( "untitled.title" );
DocumentFrame.this .setTitle( title );
}
@Override
protected void showError( Exception e )
{
JOptionPane .showMessageDialog( DocumentFrame.this,
e .getLocalizedMessage(),
"Error Loading Document", JOptionPane.ERROR_MESSAGE );
DocumentFrame.this .dispose();
}
} .actionPerformed( null );
}
private ExclusiveAction getExclusiveAction( final String action )
{
return new ExclusiveAction( getExcluder() )
{
@Override
protected void doAction( ActionEvent e ) throws Exception
{
mController.doAction( action, e );
}
@Override
protected void showError( Exception e )
{
JOptionPane.showMessageDialog( DocumentFrame.this, e.getMessage(), "Command failure", JOptionPane.ERROR_MESSAGE );
}
};
}
@Override
public AbstractButton setButtonAction( String command, AbstractButton control )
{
control .setActionCommand( command );
boolean enable = true;
switch ( command ) {
// TODO: find a better way to disable these... they are found in OrbitPanel.java
case "predefinedOrbits":
case "usedOrbits":
case "setAllDirections":
enable = fullPower;
break;
case "save":
case "saveAs":
case "saveDefault":
enable = this .canSave;
break;
default:
if ( command .startsWith( "export." ) ) {
enable = this .canSave;
}
}
control .setEnabled( enable );
if ( control .isEnabled() )
{
ActionListener actionListener = this .mController;
switch ( command ) {
// these can fall through to the ApplicationController
case "quit":
case "new":
case "new-rootTwo":
case "new-rootThree":
case "new-heptagon":
case "new-snubDodec":
case "openURL":
case "showAbout":
// these will be handled by the DocumentController
case "toggleWireframe":
case "toggleOrbitViews":
case "toggleStrutScales":
case "toggleFrameLabels":
case "toggleOutlines":
actionListener = this .mController;
break;
case "open":
case "newFromTemplate":
case "openDeferringRedo":
actionListener = new ControllerFileAction( new FileDialog( this ), true, command, "vZome", mController );
break;
case "import.vef":
actionListener = new ControllerFileAction( new FileDialog( this ), true, command, "vef", mController );
break;
case "saveAs":
actionListener = saveAsAction;
break;
case "save":
case "saveDefault":
case "close":
case "snapshot.2d":
case "showToolsPanel":
case "setItemColor":
case "setBackgroundColor":
case "showPolytopesDialog":
case "showZomicWindow":
case "showPythonWindow":
case "rZomeOrbits":
case "predefinedOrbits":
case "usedOrbits":
case "setAllDirections":
case "configureShapes":
case "configureDirections":
case "redoUntilEdit":
case "addBookmark":
actionListener = this .localActions;
break;
case "capture-animation":
actionListener = new ControllerFileAction( new FileDialog( this ), false, command, "png", mController );
break;
default:
if ( command .startsWith( "openResource-" ) ) {
actionListener = this .mController;
}
else if ( command .startsWith( "setSymmetry." ) ) {
actionListener = this .localActions;
}
else if ( command .startsWith( "showProperties-" ) ) {
actionListener = this .localActions;
}
else if ( command .startsWith( "capture." ) ) {
String ext = command .substring( "capture." .length() );
actionListener = new ControllerFileAction( new FileDialog( this ), false, command, ext, mController );
}
else if ( command .startsWith( "export." ) ) {
String ext = command .substring( "export." .length() );
switch ( ext ) {
case "vrml": ext = "wrl"; break;
case "size": ext = "txt"; break;
case "partslist": ext = "txt"; break;
case "partgeom": ext = "vef"; break;
default:
break;
}
actionListener = new ControllerFileAction( new FileDialog( this ), false, command, ext, mController );
}
else {
actionListener = getExclusiveAction( command );
this .mExcluder .addExcludable( control );
}
break;
}
control .addActionListener( actionListener );
}
return control;
}
@Override
public JMenuItem setMenuAction( String command, JMenuItem menuItem )
{
return (JMenuItem) this .setButtonAction( command, menuItem );
}
@Override
public void propertyChange( PropertyChangeEvent e )
{
switch ( e .getPropertyName() ) {
case "command.status":
if ( statusText != null && ! this .developerExtras )
statusText .setText( (String) e .getNewValue() );
break;
case "current.edit.xml":
if ( statusText != null && this .developerExtras )
statusText .setText( (String) e .getNewValue() );
break;
case "editor.mode":
String mode = (String) e .getNewValue();
int width = 0;
if ( "article" .equals( mode ) )
{
width = 400;
if ( snapshotButton != null ) // this gets called just once for the Reader
snapshotButton .setEnabled( false );
// viewControl .setVisible( false );
mExcluder .grab();
}
else
{
width = 300;
snapshotButton .setEnabled( true );
// viewControl .setVisible( true );
mExcluder .release();
}
createLessonPanel();
modelArticleCardLayout .show( modelArticleEditPanel, mode );
modelArticleEditPanel .setMinimumSize( new Dimension( width, 500 ) );
modelArticleEditPanel .setPreferredSize( new Dimension( width, 800 ) );
break;
case "has.pages":
if ( articleButton != null )
{
boolean enable = e .getNewValue() .toString() .equals( "true" );
articleButton .setEnabled( enable );
if ( ! enable )
modelButton .doClick();
}
break;
case "window.title":
this .setTitle( e .getNewValue() .toString() );
break;
case "visible":
if ( Boolean.TRUE .equals( e .getNewValue() ) ) {
this .appUI .propertyChange( e ); // remove this window from the UI's collection
this .setVisible( true );
}
break;
}
}
// called from ApplicationUI on quit
//
boolean closeWindow()
{
if ( "true".equals( mController.getProperty( "edited" ) ) && ( isEditor && canSave ) )
{
// TODO replace showConfirmDialog() with use of EscapeDialog, or something similar...
// see http://java.sun.com/docs/books/tutorial/uiswing/components/dialog.html
int response = JOptionPane.showConfirmDialog( DocumentFrame.this, "Do you want to save your changes?",
"file is changed", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE );
if ( response == JOptionPane.CANCEL_OPTION )
return false;
if ( response == JOptionPane.YES_OPTION )
try {
localActions .actionPerformed( new ActionEvent( DocumentFrame.this, ActionEvent.ACTION_PERFORMED, "save" ) );
return false;
} catch ( RuntimeException re ) {
logger.log( Level.WARNING, "did not save due to error", re );
return false;
}
}
dispose();
mController .setProperty( "visible", Boolean.FALSE );
return true;
}
public void makeUnnamed()
{
this .mFile = null;
}
}