package org.vorthmann.zome.ui;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import javax.swing.AbstractButton;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JToolBar;
import javax.swing.SwingConstants;
import org.vorthmann.j3d.J3dComponentFactory;
import org.vorthmann.ui.Controller;
public class ModelPanel extends JPanel implements PropertyChangeListener
{
private static final String TOOLTIP_PREFIX = "<html><b>";
private static final String TOOLTIP_SUFFIX = "</b><br><br><p>Right-click to configure this tool.</p></html>";
private final Component monocularCanvas, leftEyeCanvas, rightEyeCanvas;
private MouseListener monocularClicks, leftEyeClicks, rightEyeClicks;
private final JToolBar oldToolBar, firstToolbar, secondToolbar, bookmarkBar; // TODO these don't need to be fields
private final JScrollPane firstScroller, secondScroller, bookmarkScroller;
private final boolean isEditor;
private final Controller controller, view;
private final JPanel mMonocularPanel;
private final ToolConfigDialog toolConfigDialog;
private Map<String, AbstractButton> toolCreationButtons = new HashMap<String, AbstractButton>();
public ModelPanel( Controller controller, ControlActions enabler, boolean isEditor, boolean fullPower )
{
super( new BorderLayout() );
this .controller = controller;
this .view = controller .getSubController( "viewPlatform" );
this .isEditor = isEditor;
Controller monoController = controller .getSubController( "monocularPicking" );
Controller leftController = controller .getSubController( "leftEyePicking" );
Controller rightController = controller .getSubController( "rightEyePicking" );
controller .addPropertyListener( this );
JPanel monoStereoPlusToolbar = new JPanel();
monoStereoPlusToolbar .setLayout( new BorderLayout() );
this .add( monoStereoPlusToolbar, BorderLayout.CENTER );
JPanel monoStereoPanel = new JPanel();
monoStereoPlusToolbar .add( monoStereoPanel, BorderLayout.CENTER );
CardLayout monoStereoCardLayout = new CardLayout();
monoStereoPanel .setLayout( monoStereoCardLayout );
boolean showStereo = "true" .equals( view .getProperty( "stereo" ) );
mMonocularPanel = new JPanel( new BorderLayout() );
{
mMonocularPanel .setPreferredSize( new Dimension( 2000, 2000 ) );
monocularCanvas = ( (J3dComponentFactory) controller ) .createJ3dComponent( "mainViewer-monocular" );
mMonocularPanel .add( monocularCanvas, BorderLayout.CENTER );
}
monoStereoPanel .add( mMonocularPanel, "mono" );
JPanel stereoPanel = new JPanel();
{
GridLayout grid = new GridLayout( 1, 2 );
stereoPanel .setLayout( grid );
leftEyeCanvas = ( (J3dComponentFactory) controller ) .createJ3dComponent( "mainViewer-leftEye" );
stereoPanel .add( leftEyeCanvas );
rightEyeCanvas = ( (J3dComponentFactory) controller ) .createJ3dComponent( "mainViewer-rightEye" );
stereoPanel .add( rightEyeCanvas );
}
monoStereoPanel .add( stereoPanel, "stereo" );
if ( showStereo )
monoStereoCardLayout .show( monoStereoPanel, "stereo" );
else
monoStereoCardLayout .show( monoStereoPanel, "mono" );
view .addPropertyListener( new PropertyChangeListener()
{
@Override
public void propertyChange( PropertyChangeEvent chg )
{
if ( "stereo" .equals( chg .getPropertyName() ) )
if ( ((Boolean) chg .getNewValue()) )
monoStereoCardLayout .show( monoStereoPanel, "stereo" );
else
monoStereoCardLayout .show( monoStereoPanel, "mono" );
}
} );
this .toolConfigDialog = new ToolConfigDialog( (JFrame) this.getParent() );
if ( isEditor )
{
if ( ! controller .propertyIsTrue( "no.toolbar" ) )
{
// -------------------- Create the dynamic toolbar
boolean hasOldToolBar = controller .propertyIsTrue( "original.tools" );
this .firstToolbar = new JToolBar();
this .firstToolbar .setFloatable( false );
this .firstToolbar .setOrientation( JToolBar.HORIZONTAL );
// this .firstToolbar .setToolTipText( "Click on objects to select them, and enable creation of new tools accordingly." );
this .firstScroller = new JScrollPane( this .firstToolbar, JScrollPane.VERTICAL_SCROLLBAR_NEVER, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED );
this .firstScroller .setBorder( null );
this .add( this .firstScroller, BorderLayout .NORTH );
this .bookmarkBar = new JToolBar();
this .bookmarkBar .setFloatable( false );
this .bookmarkBar .setOrientation( JToolBar.VERTICAL );
this .bookmarkBar .setToolTipText( "Selection bookmarks" );
this .bookmarkScroller = new JScrollPane( this .bookmarkBar, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER );
this .bookmarkScroller .setBorder( null );
monoStereoPlusToolbar .add( this .bookmarkScroller, BorderLayout .LINE_START );
final Controller toolsController = controller .getSubController( "tools" );
AbstractButton button;
if ( controller .propertyIsTrue( "supports.symmetry.icosahedral" ) ) {
button = newToolButton( toolsController, "icosahedral", "Create an icosahedral symmetry tool",
"<p>" +
"Each tool produces up to 59 copies of the input<br>" +
"selection, using the rotation symmetries of an<br>" +
"icosahedron. To create a tool, select a single<br>" +
"ball that defines the center of symmetry.<br>" +
"<br>" +
"Combine with a point reflection tool to achieve<br>" +
"all 120 symmetries of the icosahedron, including<br>" +
"reflections.<br>" +
"</p>" );
firstToolbar .add( button );
button = newToolButton( toolsController, "octahedral", "Create an octahedral symmetry tool",
"<p>" +
"Each tool produces up to 23 copies of the input<br>" +
"selection, using the rotation symmetries of a<br>" +
"cube or octahedron. To create a tool, select a<br>" +
"ball that defines the center of symmetry, and<br>" +
"a single blue or green strut, defining one of<br>" +
"five possible orientations for the symmetry.<br>" +
"<br>" +
"Combine with a point reflection tool to achieve<br>" +
"all 48 symmetries of the octahedron, including<br>" +
"reflections.<br>" +
"</p>" );
firstToolbar .add( button );
button = newToolButton( toolsController, "tetrahedral", "Create a tetrahedral symmetry tool",
"<p>" +
"Each tool produces up to 11 copies of the input<br>" +
"selection, using the rotation symmetries of a<br>" +
"tetrahedron. To create a tool, select a ball<br>" +
"that defines the center of symmetry, and a single<br>" +
"blue or green strut, defining one of five<br>" +
"possible orientations for the symmetry.<br>" +
"<br>" +
"Combine with a point reflection tool to achieve<br>" +
"all 24 symmetries of the tetrahedron, including<br>" +
"reflections.<br>" +
"</p>" );
firstToolbar .add( button );
} else {
button = newToolButton( toolsController, "octahedral", "Create an octahedral symmetry tool",
"<p>" +
"Each tool produces up to 23 copies of the input<br>" +
"selection, using the rotation symmetries of a<br>" +
"cube or octahedron. To create a tool, select a<br>" +
"ball that defines the center of symmetry.<br>" +
"<br>" +
"Combine with a point reflection tool to achieve<br>" +
"all 48 symmetries of the octahedron, including<br>" +
"reflections.<br>" +
"</p>" );
firstToolbar .add( button );
button = newToolButton( toolsController, "tetrahedral", "Create a tetrahedral symmetry tool",
"<p>" +
"Each tool produces up to 11 copies of the input<br>" +
"selection, using the rotation symmetries of a<br>" +
"tetrahedron. To create a tool, select a ball<br>" +
"that defines the center of symmetry.<br>" +
"<br>" +
"Combine with a point reflection tool to achieve<br>" +
"all 24 symmetries of the tetrahedron, including<br>" +
"reflections.<br>" +
"</p>" );
firstToolbar .add( button );
}
button = newToolButton( toolsController, "point reflection", "Create a point reflection tool",
"<p>" +
"Each tool duplicates the selection by reflecting<br>" +
"each point through the defined center. To create a<br>" +
"tool, select a single ball that defines that center.<br>" +
"</p>" );
firstToolbar .add( button );
button = newToolButton( toolsController, "mirror", "Create a mirror reflection tool",
"<p>" +
"Each tool duplicates the selection by reflecting<br>" +
"each object in a mirror plane. To create a<br>" +
"tool, define the mirror plane by selecting a single<br>" +
"panel, or by selecting a strut orthogonal to the<br>" +
"plane and a ball lying in the plane.<br>" +
"</p>" );
firstToolbar .add( button );
button = newToolButton( toolsController, "axial symmetry", "Create a rotational symmetry tool",
"<p>" +
"Each tool creates enough copies of the selected objects to<br>" +
"create rotational symmetry around an axis. To create a tool,<br>" +
"select a strut that defines that axis, You can also define<br>" +
"the direction and center independently, by selecting a ball<br>" +
"for the center and a strut for the axis. Note: not all struts<br>" +
"correspond to rotational symmetries!<br>" +
"<br>" +
"Combine with a point reflection or mirror reflection tool to<br>" +
"achieve more symmetries.<br>" +
"</p>" );
firstToolbar .add( button );
firstToolbar .addSeparator();
button = newToolButton( toolsController, "scaling", "Create a scaling tool",
"<p>" +
"Each tool enlarges or shrinks the selected objects,<br>" +
"relative to a central point. To create a tool,<br>" +
"select a ball representing the central point, and<br>" +
"two struts from the same orbit (color) with different<br>" +
"sizes.<br>" +
"<br>" +
"The selection order matters. First select a strut<br>" +
"that you want to enlarge or shrink, then select a<br>" +
"strut that has the desired target size.<br>" +
"</p>" );
firstToolbar .add( button );
button = newToolButton( toolsController, "rotation", "Create a rotation tool",
"<p>" +
"Each tool rotates the selected objects around an axis<br>" +
"of symmetry. To create a tool, select a strut that<br>" +
"defines that axis. You can also define the direction<br>" +
"and center independently, by selecting a ball for the<br>" +
"center and a strut for the axis. Note: not all struts<br>" +
"correspond to rotational symmetries!<br>" +
"<br>" +
"The direction of rotation depends on the strut<br>" +
"orientation, which is hard to discover, but easy to<br>" +
"control, by dragging out a new strut.<br>" +
"<br>" +
"By default, the input selection will be moved to the new,<br>" +
"rotated orientation. After creating a tool, you can<br>" +
"right-click to configure the tool to create a copy, instead.<br>" +
"</p>" );
firstToolbar .add( button );
button = newToolButton( toolsController, "translation", "Create a translation tool",
"<p>" +
"Each tool moves the selected objects to a new location.<br>" +
"To create a tool, select two balls that are separated by<br>" +
"your desired translation offset. Order of selection<br>" +
"matters: the first ball selected is the \"from\" location,<br>" +
"and the second is the \"to\" location.<br>" +
"<br>" +
"By default, the input selection will be moved to the new<br>" +
"location. If you want to copy rather than move, you can<br>" +
"right-click after creating the tool, to configure it.<br>" +
"</p>" );
firstToolbar .add( button );
firstToolbar .addSeparator();
if ( controller .propertyIsTrue( "supports.symmetry.icosahedral" ) ) {
button = newToolButton( toolsController, "redsquash1", "Create a weak red squash tool",
"<p>" +
"Each tool applies a \"squash\" transformation to the<br>" +
"selected objects, compressing along a red axis. To create<br>" +
"a tool, select a ball as the center of the mapping, and a<br>" +
"red strut as the direction of the compression. The ball and<br>" +
"strut need not be collinear.<br>" +
"<br>" +
"The mapping comes from the usual Zome projection of the<br>" +
"120-cell. It is the mapping that transforms the central,<br>" +
"blue dodecahedron into the compressed form in the next<br>" +
"layer outward.<br>" +
"<br>" +
"By default, the input selection will be removed, and replaced<br>" +
"with the squashed equivalent. If you want to keep the inputs,<br>" +
"you can right-click after creating the tool, to configure it.<br>" +
"</p>" );
firstToolbar .add( button );
button = newToolButton( toolsController, "redstretch1", "Create a weak red stretch tool",
"<p>" +
"Each tool applies a \"stretch\" transformation to the<br>" +
"selected objects, stretching along a red axis. To create<br>" +
"a tool, select a ball as the center of the mapping, and a<br>" +
"red strut as the direction of the stretch. The ball and<br>" +
"strut need not be collinear.<br>" +
"<br>" +
"The mapping comes from the usual Zome projection of the<br>" +
"120-cell. It is the inverse of the mapping that transforms<br>" +
"the central, blue dodecahedron into the compressed form in<br>" +
"the next layer outward.<br>" +
"<br>" +
"By default, the input selection will be removed, and replaced<br>" +
"with the stretched equivalent. If you want to keep the inputs,<br>" +
"you can right-click after creating the tool, to configure it.<br>" +
"</p>" );
firstToolbar .add( button );
button = newToolButton( toolsController, "yellowsquash", "Create a yellow squash tool",
"<p>" +
"Each tool applies a \"squash\" transformation to the<br>" +
"selected objects, compressing along a yellow axis. To create<br>" +
"a tool, select a ball as the center of the mapping, and a<br>" +
"yellow strut as the direction of the compression. The ball and<br>" +
"strut need not be collinear.<br>" +
"<br>" +
"The mapping comes from the usual Zome projection of the<br>" +
"120-cell. It is the mapping that transforms the central,<br>" +
"blue dodecahedron into the compressed form along a yellow axis.<br>" +
"<br>" +
"By default, the input selection will be removed, and replaced<br>" +
"with the squashed equivalent. If you want to keep the inputs,<br>" +
"you can right-click after creating the tool, to configure it.<br>" +
"</p>" );
firstToolbar .add( button );
button = newToolButton( toolsController, "yellowstretch", "Create a yellow stretch tool",
"<p>" +
"Each tool applies a \"stretch\" transformation to the<br>" +
"selected objects, stretching along a yellow axis. To create<br>" +
"a tool, select a ball as the center of the mapping, and a<br>" +
"yellow strut as the direction of the stretch. The ball and<br>" +
"strut need not be collinear.<br>" +
"<br>" +
"The mapping comes from the usual Zome projection of the<br>" +
"120-cell. It is the inverse of the mapping that transforms<br>" +
"the central, blue dodecahedron into the compressed form along<br>" +
"a yellow axis.<br>" +
"<br>" +
"By default, the input selection will be removed, and replaced<br>" +
"with the stretched equivalent. If you want to keep the inputs,<br>" +
"you can right-click after creating the tool, to configure it.<br>" +
"</p>" );
firstToolbar .add( button );
button = newToolButton( toolsController, "redsquash2", "Create a strong red squash tool",
"<p>" +
"Each tool applies a \"squash\" transformation to the<br>" +
"selected objects, compressing along a red axis. To create<br>" +
"a tool, select a ball as the center of the mapping, and a<br>" +
"red strut as the direction of the compression. The ball and<br>" +
"strut need not be collinear.<br>" +
"<br>" +
"The mapping comes from the usual Zome projection of the<br>" +
"120-cell. It is the mapping that transforms the central,<br>" +
"blue dodecahedron into the compressed form in the second<br>" +
"layer outward along a red axis.<br>" +
"<br>" +
"By default, the input selection will be removed, and replaced<br>" +
"with the squashed equivalent. If you want to keep the inputs,<br>" +
"you can right-click after creating the tool, to configure it.<br>" +
"</p>" );
firstToolbar .add( button );
button = newToolButton( toolsController, "redstretch2", "Create a strong red stretch tool",
"<p>" +
"Each tool applies a \"stretch\" transformation to the<br>" +
"selected objects, stretching along a red axis. To create<br>" +
"a tool, select a ball as the center of the mapping, and a<br>" +
"red strut as the direction of the stretch. The ball and<br>" +
"strut need not be collinear.<br>" +
"<br>" +
"The mapping comes from the usual Zome projection of the<br>" +
"120-cell. It is the inverse of the mapping that transforms<br>" +
"the central, blue dodecahedron into the compressed form in<br>" +
"the second layer outward along a red axis.<br>" +
"<br>" +
"By default, the input selection will be removed, and replaced<br>" +
"with the stretched equivalent. If you want to keep the inputs,<br>" +
"you can right-click after creating the tool, to configure it.<br>" +
"</p>" );
firstToolbar .add( button );
}
button = newToolButton( toolsController, "linear map", "Create a linear map tool",
"<p>" +
"<b>For experts and Linear Algebra students...</b><br>" +
"<br>" +
"Each tool applies a linear transformation to the selected<br>" +
"objects, possibly rotating, stretching, and compressing. To<br>" +
"create a tool, select a ball as the center of the mapping,<br>" +
"three struts (in order) to define the input basis, and three<br>" +
"more struts to define the output basis.<br>" +
"<br>" +
"You can omit the input basis if it would consist of three<br>" +
"identical blue struts at right angles; the three struts you<br>" +
"select will be interpreted as the output basis.<br>" +
"<br>" +
"By default, the input selection will be removed, and replaced<br>" +
"with the transformed equivalent. If you want to keep the inputs,<br>" +
"you can right-click after creating the tool, to configure it.<br>" +
"</p>" );
firstToolbar .add( button );
this .secondToolbar = new JToolBar();
this .secondToolbar .setFloatable( false );
this .secondToolbar .setOrientation( JToolBar.HORIZONTAL );
// this .secondToolbar .setToolTipText( "All commands and tools apply to the currently selected objects." );
this .secondScroller = new JScrollPane( this .secondToolbar, JScrollPane.VERTICAL_SCROLLBAR_NEVER, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED );
this .secondScroller .setBorder( null );
monoStereoPlusToolbar .add( this .secondScroller, BorderLayout .NORTH );
button = newBookmarkButton( toolsController );
bookmarkBar .add( button );
bookmarkBar .addSeparator();
addBookmark( toolsController .getSubController( "bookmark.builtin/ball at origin" ) );
button = makeEditButton( enabler, "delete", "Delete selected objects" );
secondToolbar .add( button );
button = makeEditButton( enabler, "hideball", "Hide selected objects" );
secondToolbar .add( button );
secondToolbar .addSeparator();
button = makeEditButton( enabler, "joinballs", "Connect balls in a loop" );
secondToolbar .add( button );
button = makeEditButton( enabler, "chainballs", "Connect balls in a chain" );
secondToolbar .add( button );
button = makeEditButton( enabler, "joinBallsAllToLast", "Connect all balls to last selected" );
secondToolbar .add( button );
button = makeEditButton( enabler, "joinBallsAllPossible", "Connect balls in all possible ways" );
secondToolbar .add( button );
button = makeEditButton( enabler, "panel", "Make a panel polygon" );
secondToolbar .add( button );
button = makeEditButton( enabler, "centroid", "Construct centroid of points" );
secondToolbar .add( button );
secondToolbar .addSeparator();
// we want this presentation order to be controlled here, not in the core
addTool( toolsController .getSubController( "icosahedral.builtin/icosahedral around origin" ) );
addTool( toolsController .getSubController( "octahedral.builtin/octahedral around origin" ) );
addTool( toolsController .getSubController( "tetrahedral.builtin/tetrahedral around origin" ) );
addTool( toolsController .getSubController( "point reflection.builtin/reflection through origin" ) );
addTool( toolsController .getSubController( "mirror.builtin/reflection through XY plane" ) );
addTool( toolsController .getSubController( "axial symmetry.builtin/symmetry around red through origin" ) );
secondToolbar .addSeparator();
addTool( toolsController .getSubController( "scaling.builtin/scale down" ) );
addTool( toolsController .getSubController( "scaling.builtin/scale up" ) );
addTool( toolsController .getSubController( "rotation.builtin/rotate around red through origin" ) );
addTool( toolsController .getSubController( "translation.builtin/b1 move along +X" ) );
secondToolbar .addSeparator();
toolsController .addPropertyListener( new PropertyChangeListener()
{
@Override
public void propertyChange( PropertyChangeEvent evt )
{
switch ( evt .getPropertyName() ) {
case "tool.added":
if ( evt .getOldValue() == null )
{
Controller controller = (Controller) evt .getNewValue();
String kind = controller .getProperty( "kind" );
if ( kind .equals( "bookmark" ) )
addBookmark( controller );
else
addTool( controller );
}
break;
default:
break;
}
}
});
if ( hasOldToolBar ) {
// --------------------------------------- Create the fixed toolbar.
this .oldToolBar = new JToolBar( "vZome Toolbar" );
this .oldToolBar .setOrientation( JToolBar.VERTICAL );
this .add( oldToolBar, BorderLayout .LINE_END );
button = makeLegacyEditButton( enabler, "joinballs", "Join two or more selected balls" );
oldToolBar.add( button );
button.setRolloverEnabled( true );
String fieldName = controller.getProperty( "field.name" );
if ( "golden" .equals( fieldName ) ) {
button = makeLegacyEditButton( enabler, "icosasymm-golden", "Repeat selection with chiral icosahedral symmetry" );
oldToolBar.add( button );
button.setRolloverEnabled( true );
} else if ( "snubDodec" .equals( fieldName ) ) {
button = makeLegacyEditButton( enabler, "icosasymm-snubDodec", "Repeat selection with chiral icosahedral symmetry" );
oldToolBar.add( button );
button.setRolloverEnabled( true );
} else {
button = makeLegacyEditButton( enabler, "octasymm", "Repeat selection with chiral octahedral symmetry" );
oldToolBar.add( button );
button.setRolloverEnabled( true );
}
button = makeLegacyEditButton( enabler, "tetrasymm", "Repeat selection with chiral tetrahedral symmetry" );
oldToolBar.add( button );
button.setRolloverEnabled( true );
button = makeLegacyEditButton( enabler, "axialsymm", "Repeat selection with symmetry around an axis" );
oldToolBar.add( button );
button.setRolloverEnabled( true );
button = makeLegacyEditButton( enabler, "pointsymm", "Reflect selection through origin" );
oldToolBar.add( button );
button.setRolloverEnabled( true );
button = makeLegacyEditButton( enabler, "mirrorsymm", "Reflect selection through mirror" );
oldToolBar.add( button );
button.setRolloverEnabled( true );
button = makeLegacyEditButton( enabler, "translate", "Repeat selection translated along symmetry axis" );
oldToolBar.add( button );
button.setRolloverEnabled( true );
if ( fullPower )
{
button = makeLegacyEditButton( enabler, "centroid", "Construct centroid of points" );
oldToolBar.add( button );
button.setRolloverEnabled( true );
}
button = makeLegacyEditButton( enabler, "hideball", "Hide selected objects" );
oldToolBar.add( button );
button.setRolloverEnabled( true );
button = makeLegacyEditButton( enabler, "panel", "Make a panel polygon" );
oldToolBar.add( button );
button.setRolloverEnabled( true );
}
else
this .oldToolBar = null;
}
else {
this .oldToolBar = null;
this .secondToolbar = null;
this .firstToolbar = null;
this .bookmarkBar = null;
firstScroller = null; secondScroller = null; bookmarkScroller = null;
}
monocularClicks = new ContextualMenuMouseListener( monoController , new PickerContextualMenu( monoController, enabler, "monocular" ) );
leftEyeClicks = new ContextualMenuMouseListener( leftController , new PickerContextualMenu( leftController, enabler, "leftEye" ) );
rightEyeClicks = new ContextualMenuMouseListener( rightController , new PickerContextualMenu( rightController, enabler, "rightEye" ) );
monocularCanvas .addMouseListener( monocularClicks );
leftEyeCanvas .addMouseListener( leftEyeClicks );
rightEyeCanvas .addMouseListener( rightEyeClicks );
}
else {
this .oldToolBar = null;
this .secondToolbar = null;
this .firstToolbar = null;
this .bookmarkBar = null;
firstScroller = null; secondScroller = null; bookmarkScroller = null;
}
}
private void addTool( Controller controller )
{
if ( controller == null )
// the field may not support the tool that was requested
return;
this .addTool( controller, controller .getProperty( "label" ) );
}
private void addTool( Controller controller, String label )
{
String kind = controller .getProperty( "kind" );
String iconPath = "/icons/tools/small/" + kind + ".png";
String tooltip = TOOLTIP_PREFIX + label + TOOLTIP_SUFFIX;
JButton button = makeEditButton2( tooltip, iconPath );
button .setActionCommand( "apply" );
button .addActionListener( controller );
button .addMouseListener( new MouseAdapter()
{
@Override
public void mousePressed( MouseEvent e )
{
maybeShowPopup( e );
}
@Override
public void mouseReleased( MouseEvent e )
{
maybeShowPopup( e );
}
private void maybeShowPopup( MouseEvent e )
{
if ( e.isPopupTrigger() ) {
toolConfigDialog .showTool( button, controller );
}
}
} );
controller .addPropertyListener( new PropertyChangeListener()
{
@Override
public void propertyChange( PropertyChangeEvent evt )
{
if ( "label" .equals( evt .getPropertyName() ) )
{
String label = (String) evt .getNewValue();
String tooltip = TOOLTIP_PREFIX + label + TOOLTIP_SUFFIX;
button .setToolTipText( tooltip );
}
}
});
secondToolbar .add( button );
}
private void addBookmark( Controller controller )
{
String name = controller .getProperty( "label" );
String iconPath = "/icons/tools/small/bookmark.png";
String tooltip = TOOLTIP_PREFIX + name + TOOLTIP_SUFFIX;
JButton button = makeEditButton2( tooltip, iconPath );
button .setActionCommand( "apply" );
button .addActionListener( controller );
tooltip = "<html><b>" + name + "</b></html>";
button .setToolTipText( tooltip );
bookmarkBar .add( button );
}
private AbstractButton newToolButton( Controller toolsController, String group, String title, String helpHtml )
{
String iconPath = "/icons/tools/newTool/" + group + ".png";
String html = "<html><img src=\"" + ModelPanel.class.getResource( iconPath ) + "\"> <b>" + title
+ "</b><br><br>" + helpHtml + "</html>";
final JButton button = makeEditButton2( html, iconPath );
button .setActionCommand( "createTool" );
Controller buttonController = toolsController .getSubController( group );
button .addActionListener( buttonController );
button .setEnabled( buttonController != null && buttonController .propertyIsTrue( "enabled" ) );
if ( buttonController != null )
buttonController .addPropertyListener( new PropertyChangeListener()
{
@Override
public void propertyChange( PropertyChangeEvent evt )
{
switch ( evt .getPropertyName() ) {
case "enabled":
button .setEnabled( (Boolean) evt .getNewValue() );
break;
}
}
});
else
System .out .println( "no controller for tool Factory: " + group );
this .toolCreationButtons .put( group, button );
return button;
}
private AbstractButton newBookmarkButton( Controller toolsController )
{
String iconPath = "/icons/tools/newTool/bookmark.png";
String html = "<html><img src=\"" + ModelPanel.class.getResource( iconPath ) + "\"> <b>" + "Create a selection bookmark"
+ "</b><br><br>A selection bookmark lets you re-create<br>any selection at a later time.</html>";
final JButton button = makeEditButton2( html, iconPath );
button .setActionCommand( "createTool" );
Controller buttonController = toolsController .getSubController( "bookmark" );
button .addActionListener( buttonController );
button .setEnabled( buttonController != null && buttonController .propertyIsTrue( "enabled" ) );
if ( buttonController != null )
buttonController .addPropertyListener( new PropertyChangeListener()
{
@Override
public void propertyChange( PropertyChangeEvent evt )
{
switch ( evt .getPropertyName() ) {
case "enabled":
button .setEnabled( (Boolean) evt .getNewValue() );
break;
}
}
});
else
System .out .println( "no controller for tool Factory: bookmark" );
this .toolCreationButtons .put( "bookmark", button );
return button;
}
private AbstractButton makeEditButton( ControlActions enabler, String command, String tooltip )
{
AbstractButton button = makeEditButton2( tooltip, "/icons/tools/small/" + command + ".png" );
button = enabler .setButtonAction( command, button );
return button;
}
private AbstractButton makeLegacyEditButton( ControlActions enabler, String command, String tooltip )
{
String imageName = command;
if ( imageName .endsWith( "-roottwo" ) )
imageName = command .substring( 0, command.length() - 8 );
else if ( imageName .endsWith( "-golden" ) )
imageName = command .substring( 0, command.length() - 7 );
AbstractButton button = makeEditButton2( tooltip, "/icons/" + imageName + "_on.png" );
button = enabler .setButtonAction( command, button );
Dimension dim = new Dimension( 100, 63 );
button .setPreferredSize( dim );
button .setMaximumSize( dim );
return button;
}
private JButton makeEditButton2( String tooltip, String imgLocation )
{
// Create and initialize the button.
JButton button = new JButton();
URL imageURL = getClass() .getResource( imgLocation );
if ( imageURL != null ) {
Icon icon = new ImageIcon( imageURL, tooltip );
button .setIcon( icon );
Dimension dim = new Dimension( icon .getIconWidth()+1, icon .getIconHeight()+1 );
button .setPreferredSize( dim );
button .setMaximumSize( dim );
// the rest will only work if setRolloverEnabled(true) is called
// after adding to the toolbar!
// imageURL = getClass() .getResource( "/icons/" + imageName + "_on.png" );
// icon = new ImageIcon( imageURL, label );
// button .setRolloverIcon( icon );
} else
System.err.println( "Resource not found: " + imgLocation );
button .setVerticalTextPosition( SwingConstants.TOP );
button .setHorizontalTextPosition( SwingConstants.CENTER );
button .setToolTipText( tooltip );
return button;
}
@Override
public void propertyChange( PropertyChangeEvent e )
{
switch ( e .getPropertyName() ) {
case "editor.mode":
if ( isEditor )
{
if ( "article" .equals( e .getNewValue() ) ) {
if ( this .oldToolBar != null )
this .oldToolBar .setVisible( false );
this .firstScroller .setVisible( false );
this .secondScroller .setVisible( false );
this .bookmarkScroller .setVisible( false );
monocularCanvas .removeMouseListener( monocularClicks );
leftEyeCanvas .removeMouseListener( leftEyeClicks );
rightEyeCanvas .removeMouseListener( rightEyeClicks );
}
else if ( ! "true" .equals( this .controller .getProperty( "no.toolbar" ) ) ) {
if ( this .oldToolBar != null )
this .oldToolBar .setVisible( true );
this .firstScroller .setVisible( true );
this .secondScroller .setVisible( true );
this .bookmarkScroller .setVisible( true );
monocularCanvas .addMouseListener( monocularClicks );
leftEyeCanvas .addMouseListener( leftEyeClicks );
rightEyeCanvas .addMouseListener( rightEyeClicks );
}
}
break;
default:
break;
}
}
public Dimension getRenderedSize()
{
return this .mMonocularPanel .getSize();
}
private static class PickerContextualMenu extends ContextualMenu
{
private final Controller controller;
public PickerContextualMenu( Controller controller, ControlActions enabler, String key )
{
super();
this .controller = controller;
boolean oldTools = controller .propertyIsTrue( "original.tools" );
this .setLightWeightPopupEnabled( false );
this .add( setMenuAction( "copyThisView", new JMenuItem( "Copy This View" ) ) );
this .add( setMenuAction( "useCopiedView", new JMenuItem( "Use Copied View" ) ) );
this .addSeparator();
this .add( setMenuAction( "lookAtBall", new JMenuItem( "Look At This" ) ) );
this .add( setMenuAction( "lookAtOrigin", new JMenuItem( "Look At Origin" ) ) );
if ( oldTools ) {
this .add( setMenuAction( "lookAtSymmetryCenter", new JMenuItem( "Look At Symmetry Center" ) ) );
this .addSeparator();
this .add( setMenuAction( "setSymmetryCenter", new JMenuItem( "Set Symmetry Center" ) ) );
this .add( setMenuAction( "setSymmetryAxis", new JMenuItem( "Set Symmetry Axis" ) ) );
}
this .addSeparator();
this .add( setMenuAction( "setWorkingPlane", new JMenuItem( "Set Working Plane" ) ) );
this .add( setMenuAction( "setWorkingPlaneAxis", new JMenuItem( "Set Working Plane Axis" ) ) );
this .addSeparator();
this .add( setMenuAction( "selectCollinear", new JMenuItem( "Select Collinear" ) ) );
this .add( setMenuAction( "selectParallelStruts", new JMenuItem( "Select Parallel Struts" ) ) );
this .add( setMenuAction( "selectSimilarSize", new JMenuItem( "Select Similar Struts" ) ) );
this .add( setMenuAction( "undoToManifestation", new JMenuItem( "Undo Including This" ) ) );
this .addSeparator();
this .add( enabler .setMenuAction( "setBackgroundColor", new JMenuItem( "Set Background Color..." ) ) );
this .addSeparator();
this .add( setMenuAction( "setBuildOrbitAndLength", new JMenuItem( "Build With This" ) ) );
this .add( enabler .setMenuAction( "showProperties-"+key, new JMenuItem( "Show Properties" ) ) );
}
private JMenuItem setMenuAction( String action, JMenuItem control )
{
control .setEnabled( true );
control .setActionCommand( action );
control .addActionListener( this .controller );
return control;
}
}
}