From 2fa8e87711c60b68c2ec82575ae724f13281df0b Mon Sep 17 00:00:00 2001 From: Eric Trautman Date: Tue, 17 Oct 2023 14:52:00 +0200 Subject: [PATCH 01/13] Prototype location toolbar: this first draft is not so pretty, but the basics work so I wanted to commit it before refactoring UI. --- .../java/bdv/ui/appearance/Appearance.java | 12 ++ .../java/bdv/ui/appearance/AppearanceIO.java | 2 + .../bdv/ui/appearance/AppearanceManager.java | 3 +- .../ui/appearance/AppearanceSettingsPage.java | 1 + src/main/java/bdv/util/Prefs.java | 14 ++ src/main/java/bdv/viewer/ViewerPanel.java | 76 +++++++++-- .../location/DimensionCoordinatePanel.java | 127 ++++++++++++++++++ .../bdv/viewer/location/LocationToolBar.java | 83 ++++++++++++ 8 files changed, 306 insertions(+), 12 deletions(-) create mode 100644 src/main/java/bdv/viewer/location/DimensionCoordinatePanel.java create mode 100644 src/main/java/bdv/viewer/location/LocationToolBar.java diff --git a/src/main/java/bdv/ui/appearance/Appearance.java b/src/main/java/bdv/ui/appearance/Appearance.java index 8ddee19b..4d8835ab 100644 --- a/src/main/java/bdv/ui/appearance/Appearance.java +++ b/src/main/java/bdv/ui/appearance/Appearance.java @@ -58,6 +58,7 @@ public class Appearance private int scaleBarColor = 0xffffffff; private int scaleBarBgColor = 0x88000000; private LookAndFeelInfo lookAndFeel = DONT_MODIFY_LOOK_AND_FEEL; + private boolean showLocationBar = false; public interface UpdateListener { @@ -81,6 +82,7 @@ public void set( final Appearance other ) this.scaleBarColor = other.scaleBarColor; this.scaleBarBgColor = other.scaleBarBgColor; this.lookAndFeel = other.lookAndFeel; + this.showLocationBar = other.showLocationBar; notifyListeners(); } @@ -222,6 +224,15 @@ public static LookAndFeelInfo lookAndFeelInfoForName( final String name ) return DONT_MODIFY_LOOK_AND_FEEL; } + public boolean showLocationBar() { + return showLocationBar; + } + + public void setShowLocationBar(final boolean showLocationBar) { + this.showLocationBar = showLocationBar; + notifyListeners(); + } + @Override public String toString() { @@ -234,6 +245,7 @@ public String toString() sb.append( ", scaleBarColor=" ).append( scaleBarColor ); sb.append( ", scaleBarBgColor=" ).append( scaleBarBgColor ); sb.append( ", lookAndFeel=" ).append( lookAndFeel ); + sb.append( ", showLocationBar=" ).append( showLocationBar ); sb.append( '}' ); return sb.toString(); } diff --git a/src/main/java/bdv/ui/appearance/AppearanceIO.java b/src/main/java/bdv/ui/appearance/AppearanceIO.java index db07170e..537ae5f2 100644 --- a/src/main/java/bdv/ui/appearance/AppearanceIO.java +++ b/src/main/java/bdv/ui/appearance/AppearanceIO.java @@ -112,6 +112,7 @@ public Node representData( final Object data ) mapping.put( "scaleBarColor", hex( a.scaleBarColor() ) ); mapping.put( "scaleBarBgColor", hex( a.scaleBarBgColor() ) ); mapping.put( "lookAndFeel", a.lookAndFeel() ); + mapping.put( "showLocationBar", a.showLocationBar() ); return representMapping( APPEARANCE_TAG, mapping, getDefaultFlowStyle() ); } } @@ -178,6 +179,7 @@ public Object construct( final Node node ) a.setScaleBarColor( hexColor( mapping.get( "scaleBarColor" ) ) ); a.setScaleBarBgColor( hexColor( mapping.get( "scaleBarBgColor" ) ) ); a.setLookAndFeel( ( LookAndFeelInfo ) mapping.get( "lookAndFeel" ) ); + a.setShowLocationBar( ( Boolean ) mapping.get( "showLocationBar" ) ); return a; } catch( final Exception e ) diff --git a/src/main/java/bdv/ui/appearance/AppearanceManager.java b/src/main/java/bdv/ui/appearance/AppearanceManager.java index 0c6023df..cb66801b 100644 --- a/src/main/java/bdv/ui/appearance/AppearanceManager.java +++ b/src/main/java/bdv/ui/appearance/AppearanceManager.java @@ -179,7 +179,8 @@ public void toPrefs() Prefs.sourceNameOverlayPosition( appearance.sourceNameOverlayPosition() ); Prefs.scaleBarColor( appearance.scaleBarColor() ); Prefs.scaleBarBgColor( appearance.scaleBarBgColor() ); - } + Prefs.showLocationBar( appearance.showLocationBar() ); + } /** * @deprecated Prefs will be replaced eventually by directly using {@code Appearance} in BDV diff --git a/src/main/java/bdv/ui/appearance/AppearanceSettingsPage.java b/src/main/java/bdv/ui/appearance/AppearanceSettingsPage.java index d86243ab..f7dcb027 100644 --- a/src/main/java/bdv/ui/appearance/AppearanceSettingsPage.java +++ b/src/main/java/bdv/ui/appearance/AppearanceSettingsPage.java @@ -137,6 +137,7 @@ public AppearancePanel( final Appearance appearance ) booleanElement( "show scalebar in movies", appearance::showScaleBarInMovie, appearance::setShowScaleBarInMovie ), colorElement( "scalebar foreground", appearance::scaleBarColor, appearance::setScaleBarColor ), colorElement( "scalebar background", appearance::scaleBarBgColor, appearance::setScaleBarBgColor ), + booleanElement( "show location bar", appearance::showLocationBar, appearance::setShowLocationBar ), separator(), booleanElement( "show minimap", appearance::showMultibox, appearance::setShowMultibox ), booleanElement( "show source info", appearance::showTextOverlay, appearance::setShowTextOverlay ), diff --git a/src/main/java/bdv/util/Prefs.java b/src/main/java/bdv/util/Prefs.java index cdced155..854a0be6 100644 --- a/src/main/java/bdv/util/Prefs.java +++ b/src/main/java/bdv/util/Prefs.java @@ -43,6 +43,11 @@ public static boolean showScaleBar() return getInstance().showScaleBar; } + public static boolean showLocationBar() + { + return getInstance().showLocationBar; + } + public static boolean showMultibox() { return getInstance().showMultibox; @@ -108,6 +113,11 @@ public static void scaleBarBgColor( final int color ) getInstance().scaleBarBgColor = color; } + public static void showLocationBar( final boolean show ) + { + getInstance().showLocationBar = show; + } + private static Prefs instance; public static Prefs getInstance() @@ -132,6 +142,7 @@ public enum OverlayPosition private static final String SHOW_SCALE_BAR_IN_MOVIE = "show-scale-bar-in-movie"; private static final String SCALE_BAR_COLOR = "scale-bar-color"; private static final String SCALE_BAR_BG_COLOR = "scale-bar-bg-color"; + private static final String SHOW_LOCATION_BAR = "show-location-bar"; private boolean showScaleBar; private boolean showMultibox; @@ -140,6 +151,7 @@ public enum OverlayPosition private boolean showScaleBarInMovie; private int scaleBarColor; private int scaleBarBgColor; + private boolean showLocationBar; private Prefs( final Properties p ) { @@ -150,6 +162,7 @@ private Prefs( final Properties p ) showScaleBarInMovie = getBoolean( p, SHOW_SCALE_BAR_IN_MOVIE, false ); scaleBarColor = getInt( p, SCALE_BAR_COLOR, 0xffffffff ); scaleBarBgColor = getInt( p, SCALE_BAR_BG_COLOR, 0x88000000 ); + showLocationBar = getBoolean( p, SHOW_LOCATION_BAR, false ); } private boolean getBoolean( final Properties p, final String key, final boolean defaultValue ) @@ -244,6 +257,7 @@ public static Properties getDefaultProperties() properties.put( SHOW_SCALE_BAR_IN_MOVIE, "" + prefs.showScaleBarInMovie ); properties.put( SCALE_BAR_COLOR, "" + prefs.scaleBarColor ); properties.put( SCALE_BAR_BG_COLOR, "" + prefs.scaleBarBgColor ); + properties.put( SHOW_LOCATION_BAR, "" + prefs.showLocationBar ); return properties; } diff --git a/src/main/java/bdv/viewer/ViewerPanel.java b/src/main/java/bdv/viewer/ViewerPanel.java index 98111dc9..b5a89ec8 100644 --- a/src/main/java/bdv/viewer/ViewerPanel.java +++ b/src/main/java/bdv/viewer/ViewerPanel.java @@ -28,10 +28,6 @@ */ package bdv.viewer; -import static bdv.ui.UIUtils.TextPosition.TOP_RIGHT; -import static bdv.viewer.DisplayMode.SINGLE; -import static bdv.viewer.Interpolation.NEARESTNEIGHBOR; - import java.awt.BorderLayout; import java.awt.Component; import java.awt.Font; @@ -69,6 +65,8 @@ import bdv.viewer.animate.OverlayAnimator; import bdv.viewer.animate.TextOverlayAnimator; import bdv.viewer.animate.TextOverlayAnimator.TextPosition; +import bdv.viewer.location.DimensionCoordinatePanel; +import bdv.viewer.location.LocationToolBar; import bdv.viewer.overlay.MultiBoxOverlayRenderer; import bdv.viewer.overlay.ScaleBarOverlayRenderer; import bdv.viewer.overlay.SourceInfoOverlayRenderer; @@ -83,6 +81,10 @@ import net.imglib2.RealPositionable; import net.imglib2.realtransform.AffineTransform3D; +import static bdv.ui.UIUtils.TextPosition.TOP_RIGHT; +import static bdv.viewer.DisplayMode.SINGLE; +import static bdv.viewer.Interpolation.NEARESTNEIGHBOR; + /** * A JPanel for viewing multiple of {@link Source}s. The panel contains a * {@link InteractiveDisplayCanvas canvas} and a time slider (if there @@ -134,6 +136,8 @@ public class ViewerPanel extends AbstractViewerPanel implements OverlayRenderer, */ private final ScaleBarOverlayRenderer scaleBarOverlayRenderer; + private final LocationToolBar locationToolBar; + private final TransformEventHandler transformEventHandler; /** @@ -260,6 +264,10 @@ public ViewerPanel( final List< SourceAndConverter< ? > > sources, final int num display.addHandler( mouseCoordinates ); + // add location toolbar to viewer - will be visible/hidden depending on Prefs.showLocationBar() + locationToolBar = new LocationToolBar(buildCenterViewListener()); + add(locationToolBar, BorderLayout.NORTH); + sliderTime = new JSlider( SwingConstants.HORIZONTAL, 0, numTimepoints - 1, 0 ); sliderTime.addChangeListener( e -> { if ( !blockSliderTimeEvents ) @@ -458,6 +466,14 @@ public void displayToGlobalCoordinates( final double x, final double y, final Re state.getViewerTransform().applyInverse( gPos, lPos ); } + public double[] getDisplayCenterCoordinates() { + return new double[] { + getDisplay().getWidth() / 2.0, + getDisplay().getHeight() / 2.0, + 0.0 + }; + } + @Override public void paint() { @@ -513,6 +529,18 @@ public void drawOverlays( final Graphics g ) requiresRepaint = multiBoxOverlayRenderer.isHighlightInProgress(); } + final boolean showLocationBar = Prefs.showLocationBar(); + locationToolBar.setVisible(showLocationBar); + + final double[] gMousePos = new double[3]; + getGlobalMouseCoordinates(RealPoint.wrap(gMousePos)); + + if (showLocationBar) { + final double[] gCenterPos = new double[3]; + state().getViewerTransform().applyInverse(gCenterPos, getDisplayCenterCoordinates()); + locationToolBar.setPositions(gCenterPos, gMousePos); + } + if ( Prefs.showTextOverlay() ) { final Font font = UIUtils.getFont( "monospaced.small.font" ); @@ -520,14 +548,15 @@ public void drawOverlays( final Graphics g ) sourceInfoOverlayRenderer.setSourceNameOverlayPosition( Prefs.sourceNameOverlayPosition() ); sourceInfoOverlayRenderer.paint( ( Graphics2D ) g ); - final double[] gPos = new double[ 3 ]; - getGlobalMouseCoordinates( RealPoint.wrap( gPos ) ); - final String mousePosGlobalString = options.is2D() - ? String.format( Locale.ROOT, "%6.1f, %6.1f", gPos[ 0 ], gPos[ 1 ] ) - : String.format( Locale.ROOT, "%6.1f, %6.1f, %6.1f", gPos[ 0 ], gPos[ 1 ], gPos[ 2 ] ); + if (! showLocationBar) { + final String mousePosGlobalString = options.is2D() + ? String.format(Locale.ROOT, "%6.1f, %6.1f", gMousePos[0], gMousePos[1]) + : + String.format(Locale.ROOT, "%6.1f, %6.1f, %6.1f", gMousePos[0], gMousePos[1], gMousePos[2]); - g.setFont( font ); - UIUtils.drawString( g, TOP_RIGHT, 1, mousePosGlobalString ); + g.setFont(font); + UIUtils.drawString(g, TOP_RIGHT, 1, mousePosGlobalString); + } } if ( Prefs.showScaleBar() ) @@ -1033,4 +1062,29 @@ public DebugTilingOverlay showDebugTileOverlay() display.overlays().add( overlay ); return overlay; } + + /** Centers the viewer at the given global position in the specified dimension. */ + public void centerViewAt(final double globalPosition, + final int dimension) { + + // NOTE: getViewerTransform() transforms from global to display (window) coordinates + final double[] displayCenter = getDisplayCenterCoordinates(); + final double[] gCenterPos = new double[3]; + state().getViewerTransform().applyInverse(gCenterPos, displayCenter); + + final double deltaPos = gCenterPos[dimension] - globalPosition; + + final AffineTransform3D invertedViewerTransform = state().getViewerTransform().inverse(); + final double q = invertedViewerTransform.get(dimension, 3) - deltaPos; + invertedViewerTransform.set(q, dimension, 3); + + final AffineTransform3D updatedViewerTransform = invertedViewerTransform.inverse(); + state().setViewerTransform(updatedViewerTransform); + } + + /** @return a listener that centers the viewer at the given global position in the specified dimension. */ + public DimensionCoordinatePanel.ChangeListener buildCenterViewListener() { + final ViewerPanel viewerPanel = this; + return viewerPanel::centerViewAt; + } } diff --git a/src/main/java/bdv/viewer/location/DimensionCoordinatePanel.java b/src/main/java/bdv/viewer/location/DimensionCoordinatePanel.java new file mode 100644 index 00000000..286a2bc7 --- /dev/null +++ b/src/main/java/bdv/viewer/location/DimensionCoordinatePanel.java @@ -0,0 +1,127 @@ +package bdv.viewer.location; + +import java.awt.Cursor; +import java.awt.FlowLayout; +import java.util.Locale; + +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; + +/** + * Panel containing UI components for displaying and editing coordinates for one dimension of a volume. + * + * @author Eric Trautman + */ +public class DimensionCoordinatePanel + extends JPanel { + + public interface ChangeListener { + void changeLocation(final double toGlobalPosition, + final int forDimension); + } + + private String name; + private final int dimension; + private final boolean isReadOnly; + private final ChangeListener changeListener; + private final String valueFormat; + private double position; + private final JButton readOnlyValue; + private final JLabel labelForWritableValue; + private final JTextField valueTextField; + + public DimensionCoordinatePanel(final String name, + final int dimension, + final boolean isReadOnly, + final ChangeListener changeListener) { + + super(new FlowLayout(FlowLayout.LEFT, 0, 0)); + + this.name = name; + this.dimension = dimension; + this.isReadOnly = isReadOnly; + this.changeListener = changeListener; + this.valueFormat = "%1.1f"; // TODO: add support for changing this based upon volume size and display scale/size + + this.readOnlyValue = new JButton(); + this.readOnlyValue.setBorderPainted(false); + this.readOnlyValue.setFocusPainted(false); + this.readOnlyValue.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR)); + this.readOnlyValue.setOpaque(false); + this.readOnlyValue.setHorizontalTextPosition(JButton.LEFT); + this.add(this.readOnlyValue); + + if (isReadOnly) { + + this.readOnlyValue.setEnabled(false); + this.labelForWritableValue = null; + this.valueTextField = null; + + } else { + + this.labelForWritableValue = new JLabel(name + ": "); + this.add(this.labelForWritableValue); + this.labelForWritableValue.setVisible(false); + + this.valueTextField = new JTextField(10); + this.add(this.valueTextField); + this.valueTextField.setVisible(false); + + this.readOnlyValue.addActionListener(e -> { + this.labelForWritableValue.setVisible(true); + this.valueTextField.setText(String.format(Locale.ROOT, valueFormat, position)); + this.valueTextField.setEditable(true); + this.readOnlyValue.setVisible(false); + this.valueTextField.setVisible(true); + this.valueTextField.requestFocus(); + }); + + this.valueTextField.addActionListener(e -> stopEditing()); + + final DimensionCoordinatePanel thisPanel = this; + this.valueTextField.addFocusListener(new java.awt.event.FocusAdapter() { + public void focusLost(java.awt.event.FocusEvent evt) { + thisPanel.stopEditing(); + } + }); + } + } + + private void stopEditing() { + if (valueTextField.isEditable()) { + try { + final double updatedPosition = Double.parseDouble(valueTextField.getText()); + changeListener.changeLocation(updatedPosition, dimension); + } catch (NumberFormatException e) { + // ignore and leave position unchanged + } + closeEditor(); + } + } + + private void closeEditor() { + this.labelForWritableValue.setVisible(false); + this.valueTextField.setEditable(false); + this.valueTextField.setVisible(false); + this.readOnlyValue.setVisible(true); + } + + public void setName(final String name) { + this.name = name; + this.labelForWritableValue.setText(name + ": "); + } + + public void setPosition(final double position) { + this.position = position; + final String formattedValue = String.format(Locale.ROOT, valueFormat, position); + if (isReadOnly) { + this.readOnlyValue.setText(name + ": " + formattedValue); + } else if (this.readOnlyValue.isVisible()) { + this.readOnlyValue.setText(name + ": " + formattedValue); + this.valueTextField.setText(formattedValue); + } + } + +} diff --git a/src/main/java/bdv/viewer/location/LocationToolBar.java b/src/main/java/bdv/viewer/location/LocationToolBar.java new file mode 100644 index 00000000..dcc6c56a --- /dev/null +++ b/src/main/java/bdv/viewer/location/LocationToolBar.java @@ -0,0 +1,83 @@ +package bdv.viewer.location; + +import java.awt.FlowLayout; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.swing.JPanel; +import javax.swing.JToolBar; + +/** + * Toolbar containing location information along with UI components to change it. + * + * @author Eric Trautman + */ +public class LocationToolBar extends JToolBar { + + private final DimensionCoordinatePanel.ChangeListener dimensionChangeListener; + private final JPanel dimensionPanel; + private final List dimensionNames; + private final List centerCoordinatePanelList; + private final List mouseCoordinatePanelList; + + public LocationToolBar(final DimensionCoordinatePanel.ChangeListener dimensionChangeListener) { + super("Location Tools"); + this.setFloatable(false); + + this.dimensionChangeListener = dimensionChangeListener; + this.dimensionPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 1)); + this.add(this.dimensionPanel); + + this.dimensionNames = Arrays.asList("x", "y", "z", "t"); + this.centerCoordinatePanelList = new ArrayList<>(); + this.mouseCoordinatePanelList = new ArrayList<>(); + } + + public void setDimensionNames(final List dimensionNames) { + this.dimensionNames.clear(); + this.dimensionNames.addAll(dimensionNames); + + for (int i = 0; i < this.centerCoordinatePanelList.size(); i++) { + this.centerCoordinatePanelList.get(i).setName(getDimensionName(i)); + this.mouseCoordinatePanelList.get(i).setName(getDimensionName(i)); + } + } + + public void setPositions(final double[] centerPosition, + final double[] mousePosition) { + if (centerPosition.length != this.centerCoordinatePanelList.size()) { + setNumberOfDimensions(centerPosition.length); + } + for (int i = 0; i < centerPosition.length; i++) { + this.centerCoordinatePanelList.get(i).setPosition(centerPosition[i]); + this.mouseCoordinatePanelList.get(i).setPosition(mousePosition[i]); + } + } + + private synchronized void setNumberOfDimensions(final int numberOfDimensions) { + if (numberOfDimensions != centerCoordinatePanelList.size()) { + dimensionPanel.removeAll(); + addDimensions(numberOfDimensions, false, centerCoordinatePanelList); + addDimensions(numberOfDimensions, true, mouseCoordinatePanelList); + dimensionPanel.revalidate(); // need this to make panel components visible + } + } + + private void addDimensions(final int numberOfDimensions, + final boolean isReadOnly, + final List coordinatePanelList) { + for (int i = 0; i < numberOfDimensions; i++) { + final DimensionCoordinatePanel coordinatePanel = new DimensionCoordinatePanel(getDimensionName(i), + i, + isReadOnly, + dimensionChangeListener); + coordinatePanelList.add(coordinatePanel); + dimensionPanel.add(coordinatePanel); + } + } + + private String getDimensionName(final int dimensionIndex) { + return dimensionIndex < this.dimensionNames.size() ? this.dimensionNames.get(dimensionIndex) : "d" + dimensionIndex; + } +} From ba275ac0fc5c07532d5fa6106d27082fc7bda1fe Mon Sep 17 00:00:00 2001 From: Eric Trautman Date: Tue, 24 Oct 2023 12:41:53 -0400 Subject: [PATCH 02/13] move location editing UI to new LocationPanel in CardPanel and make LocationToolbar read-only --- src/main/java/bdv/ui/BdvDefaultCards.java | 23 ++ .../java/bdv/ui/splitpanel/SplitPanel.java | 30 +- src/main/java/bdv/viewer/ViewerPanel.java | 31 +- .../DimensionCoordinateComponents.java | 270 ++++++++++++++++++ .../location/DimensionCoordinatePanel.java | 127 -------- .../bdv/viewer/location/LocationPanel.java | 100 +++++++ .../bdv/viewer/location/LocationToolBar.java | 109 +++---- .../bdv/ui/location/edit_pencil_20.png | Bin 0 -> 1159 bytes .../bdv/ui/location/edit_pencil_40.png | Bin 0 -> 1090 bytes .../bdv/viewer/location/GridViewerTest.java | 108 +++++++ .../viewer/location/LocationPanelTest.java | 62 ++++ 11 files changed, 673 insertions(+), 187 deletions(-) create mode 100644 src/main/java/bdv/viewer/location/DimensionCoordinateComponents.java delete mode 100644 src/main/java/bdv/viewer/location/DimensionCoordinatePanel.java create mode 100644 src/main/java/bdv/viewer/location/LocationPanel.java create mode 100644 src/main/resources/bdv/ui/location/edit_pencil_20.png create mode 100644 src/main/resources/bdv/ui/location/edit_pencil_40.png create mode 100644 src/test/java/bdv/viewer/location/GridViewerTest.java create mode 100644 src/test/java/bdv/viewer/location/LocationPanelTest.java diff --git a/src/main/java/bdv/ui/BdvDefaultCards.java b/src/main/java/bdv/ui/BdvDefaultCards.java index b95a273b..b413f71e 100644 --- a/src/main/java/bdv/ui/BdvDefaultCards.java +++ b/src/main/java/bdv/ui/BdvDefaultCards.java @@ -34,7 +34,12 @@ import bdv.ui.viewermodepanel.DisplaySettingsPanel; import bdv.viewer.AbstractViewerPanel; import bdv.viewer.ConverterSetups; +import bdv.viewer.ViewerPanel; import bdv.viewer.ViewerState; +import bdv.viewer.location.DimensionCoordinateComponents; +import bdv.viewer.location.LocationPanel; +import net.imglib2.Interval; + import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; @@ -63,6 +68,8 @@ public class BdvDefaultCards public static final String DEFAULT_VIEWERMODES_CARD = "default bdv viewer modes card"; + public static final String DEFAULT_LOCATIONS_CARD = "default bdv location card"; + public static void setup( final CardPanel cards, final AbstractViewerPanel viewer, final ConverterSetups converterSetups ) { final ViewerState state = viewer.state(); @@ -101,6 +108,22 @@ public static void setup( final CardPanel cards, final AbstractViewerPanel viewe cards.addCard( DEFAULT_VIEWERMODES_CARD, "Display Modes", new DisplaySettingsPanel( viewer.state() ), true, new Insets( 0, 4, 4, 0 ) ); cards.addCard( DEFAULT_SOURCES_CARD, "Sources", tablePanel, true, new Insets( 0, 0, 0, 0 ) ); cards.addCard( DEFAULT_SOURCEGROUPS_CARD, "Groups", treePanel, true, new Insets( 0, 0, 0, 0 ) ); + + // create card location panel and connect it to location toolbar in viewer + if (viewer instanceof ViewerPanel) { + final ViewerPanel viewerPanel = (ViewerPanel) viewer; + final Interval interval = viewerPanel.state().getCurrentSource().getSpimSource().getSource(0, 0); + final LocationPanel locationPanel = new LocationPanel(interval); + cards.addCard(DEFAULT_LOCATIONS_CARD, "Locations", locationPanel, false, new Insets(0, 4, 0, 0)); + + locationPanel.setDimensionValueChangeListener(e -> { + final DimensionCoordinateComponents coordinateComponents = (DimensionCoordinateComponents) e.getSource(); + viewerPanel.centerViewAt(coordinateComponents.getPosition(), coordinateComponents.getDimension()); + }); + + viewerPanel.setLocationPanel(locationPanel); + } + } static class MyScrollPane extends JScrollPane diff --git a/src/main/java/bdv/ui/splitpanel/SplitPanel.java b/src/main/java/bdv/ui/splitpanel/SplitPanel.java index f0698d76..ca5d4b9e 100644 --- a/src/main/java/bdv/ui/splitpanel/SplitPanel.java +++ b/src/main/java/bdv/ui/splitpanel/SplitPanel.java @@ -28,12 +28,10 @@ */ package bdv.ui.splitpanel; -import bdv.ui.CardPanel; -import bdv.ui.UIUtils; -import bdv.viewer.AbstractViewerPanel; import java.awt.Dimension; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; + import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.JScrollPane; @@ -41,9 +39,19 @@ import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.border.EmptyBorder; + import org.scijava.ui.behaviour.io.InputTriggerConfig; import org.scijava.ui.behaviour.util.Actions; +import bdv.ui.CardPanel; +import bdv.ui.UIUtils; +import bdv.viewer.AbstractViewerPanel; +import bdv.viewer.ViewerPanel; +import bdv.viewer.location.LocationPanel; +import bdv.viewer.location.LocationToolBar; + +import static bdv.ui.BdvDefaultCards.*; + /** * A {@code JSplitPane} with a {@code ViewerPanel} on the left and a * {@code CardPanel} on the right. Animated arrows are added to the @@ -129,6 +137,22 @@ public void componentResized( final ComponentEvent e ) width = w; } } ); + + // add hook to expand card panel and locations card when edit button in locations toolbar is clicked + if (viewerPanel instanceof ViewerPanel) { + final ViewerPanel viewer = (ViewerPanel) viewerPanel; + final LocationPanel locationPanel = viewer.getLocationPanel(); + final LocationToolBar locationToolBar = viewer.getLocationToolBar(); + locationToolBar.setEditActionListener(e -> { + // expand card panel and location card + this.setCollapsed(false); + cardPanel.setCardExpanded(DEFAULT_SOURCES_CARD, false); + cardPanel.setCardExpanded(DEFAULT_SOURCEGROUPS_CARD, false); + cardPanel.setCardExpanded(DEFAULT_LOCATIONS_CARD, true); + locationPanel.requestFocusOnFirstComponent(); + }); + } + } private void configureSplitPane() diff --git a/src/main/java/bdv/viewer/ViewerPanel.java b/src/main/java/bdv/viewer/ViewerPanel.java index b5a89ec8..6a7e6dd8 100644 --- a/src/main/java/bdv/viewer/ViewerPanel.java +++ b/src/main/java/bdv/viewer/ViewerPanel.java @@ -65,7 +65,7 @@ import bdv.viewer.animate.OverlayAnimator; import bdv.viewer.animate.TextOverlayAnimator; import bdv.viewer.animate.TextOverlayAnimator.TextPosition; -import bdv.viewer.location.DimensionCoordinatePanel; +import bdv.viewer.location.LocationPanel; import bdv.viewer.location.LocationToolBar; import bdv.viewer.overlay.MultiBoxOverlayRenderer; import bdv.viewer.overlay.ScaleBarOverlayRenderer; @@ -137,6 +137,7 @@ public class ViewerPanel extends AbstractViewerPanel implements OverlayRenderer, private final ScaleBarOverlayRenderer scaleBarOverlayRenderer; private final LocationToolBar locationToolBar; + private LocationPanel locationPanel; private final TransformEventHandler transformEventHandler; @@ -265,7 +266,7 @@ public ViewerPanel( final List< SourceAndConverter< ? > > sources, final int num display.addHandler( mouseCoordinates ); // add location toolbar to viewer - will be visible/hidden depending on Prefs.showLocationBar() - locationToolBar = new LocationToolBar(buildCenterViewListener()); + locationToolBar = new LocationToolBar(); add(locationToolBar, BorderLayout.NORTH); sliderTime = new JSlider( SwingConstants.HORIZONTAL, 0, numTimepoints - 1, 0 ); @@ -306,6 +307,18 @@ public void componentResized( final ComponentEvent e ) painterThread.start(); } + public LocationToolBar getLocationToolBar() { + return locationToolBar; + } + + public LocationPanel getLocationPanel() { + return locationPanel; + } + + public void setLocationPanel(final LocationPanel locationPanel) { + this.locationPanel = locationPanel; + } + /** * Initialize ViewerState with the given {@code sources} and {@code numTimepoints}. * Set up {@code numGroups} SourceGroups named "group 1", "group 2", etc. Add the @@ -535,10 +548,15 @@ public void drawOverlays( final Graphics g ) final double[] gMousePos = new double[3]; getGlobalMouseCoordinates(RealPoint.wrap(gMousePos)); - if (showLocationBar) { + if (showLocationBar || (locationPanel != null)) { final double[] gCenterPos = new double[3]; state().getViewerTransform().applyInverse(gCenterPos, getDisplayCenterCoordinates()); - locationToolBar.setPositions(gCenterPos, gMousePos); + if (showLocationBar) { + locationToolBar.setPositions(gCenterPos, gMousePos); + } + if (locationPanel != null) { + locationPanel.setCenterPosition(gCenterPos); + } } if ( Prefs.showTextOverlay() ) @@ -1082,9 +1100,4 @@ public void centerViewAt(final double globalPosition, state().setViewerTransform(updatedViewerTransform); } - /** @return a listener that centers the viewer at the given global position in the specified dimension. */ - public DimensionCoordinatePanel.ChangeListener buildCenterViewListener() { - final ViewerPanel viewerPanel = this; - return viewerPanel::centerViewAt; - } } diff --git a/src/main/java/bdv/viewer/location/DimensionCoordinateComponents.java b/src/main/java/bdv/viewer/location/DimensionCoordinateComponents.java new file mode 100644 index 00000000..82f67cbe --- /dev/null +++ b/src/main/java/bdv/viewer/location/DimensionCoordinateComponents.java @@ -0,0 +1,270 @@ +package bdv.viewer.location; + +import java.awt.Component; +import java.awt.Container; +import java.awt.Toolkit; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Hashtable; +import java.util.List; +import java.util.Locale; +import java.util.regex.Pattern; + +import javax.swing.JLabel; +import javax.swing.JSlider; +import javax.swing.JTextField; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +/** + * UI components for displaying and editing coordinates for one dimension of a volume. + * + * @author Eric Trautman + */ +public class DimensionCoordinateComponents { + + public static final String[] DEFAULT_NAMES = { "x", "y", "z", "t" }; + public static final String DEFAULT_VALUE_FORMAT = "%1.0f"; + + private final int dimension; + private final double minPosition; + private final double maxPosition; + private final JLabel valueLabel; + private final JTextField valueTextField; + private final JSlider valueSlider; + private final ChangeListener sliderChangeListener; + + private ChangeListener dimensionValueChangeListener; + private String valueFormat; + private double position; + private DimensionCoordinateComponents nextDimensionComponents; + + public DimensionCoordinateComponents(final int dimension, + final double minPosition, + final double maxPosition) { + this(getDefaultDimensionName(dimension), dimension, minPosition, maxPosition); + } + + public DimensionCoordinateComponents(final String name, + final int dimension, + final double minPosition, + final double maxPosition) { + this(name, dimension, minPosition, maxPosition, DEFAULT_VALUE_FORMAT); + } + + public DimensionCoordinateComponents(final String name, + final int dimension, + final double minPosition, + final double maxPosition, + final String valueFormat) { + + this.dimension = dimension; + this.dimensionValueChangeListener = null; + this.minPosition = minPosition; + this.maxPosition = maxPosition; + this.valueFormat = valueFormat; + this.position = 0.0; + + this.valueLabel = new JLabel(name + ": "); + this.valueTextField = new JTextField(5); + this.valueTextField.setHorizontalAlignment(JTextField.RIGHT); + + // parse text and notify listeners when return is hit in text field + this.valueTextField.addActionListener(e -> stopEditingTextField()); + this.valueTextField.setToolTipText("Enter a value and hit return to change the current position.
" + + "Enter multiple values separated by spaces or commas to change multiple dimensions at once."); + + this.valueSlider = new JSlider(); + this.valueSlider.setMajorTickSpacing(25); + this.valueSlider.setPaintTicks(true); + + final Hashtable labelTable = new Hashtable<>(); + labelTable.put(0, new JLabel(formatSliderValue(minPosition))); + labelTable.put(100, new JLabel(formatSliderValue(maxPosition))); + this.valueSlider.setLabelTable(labelTable); + + this.valueSlider.setPaintLabels(true); + + this.sliderChangeListener = e -> { + setPosition(getSliderPosition()); + if (! valueSlider.getValueIsAdjusting()) { + notifyExternalListener(); + } + }; + + this.valueSlider.addChangeListener(this.sliderChangeListener); + + this.valueSlider.setToolTipText("Drag slider to change the current position."); + + // init valueTextArea to be same as valueSlider + this.valueTextField.setText(formatValueAsDouble(getSliderPosition())); + + this.nextDimensionComponents = null; + } + + public int getDimension() { + return dimension; + } + + public double getPosition() { + return position; + } + + public JLabel getValueLabel() { + return valueLabel; + } + + public JTextField getValueTextField() { + return valueTextField; + } + + public JSlider getValueSlider() { + return valueSlider; + } + + public void setNextDimensionComponents(final DimensionCoordinateComponents nextDimensionComponents) { + this.nextDimensionComponents = nextDimensionComponents; + } + + @SuppressWarnings("unused") + public void setValueFormat(final String valueFormat) { + if (! valueFormat.equals(this.valueFormat)) { + this.valueFormat = valueFormat; + this.valueTextField.setText(formatValueAsDouble(position)); + } + } + + private void notifyExternalListener() { + if (dimensionValueChangeListener != null) { + dimensionValueChangeListener.stateChanged(new ChangeEvent(this)); + } + } + + private double getSliderPosition() { + final double sliderPosition = valueSlider.getValue(); + return (sliderPosition / 100.0) * (maxPosition - minPosition) + minPosition; + } + + private void stopEditingTextField() { + final List textValues = Arrays.asList(MULTI_VALUE_PATTERN.split(valueTextField.getText())); + setTextValues(textValues); + } + + public void setTextValues(final List textValues) { + final int numberOfValues = textValues.size(); + if (numberOfValues > 0) { + try { + final double updatedPosition = Double.parseDouble(textValues.get(0)); + setPosition(updatedPosition); + notifyExternalListener(); + if ((nextDimensionComponents != null) && numberOfValues > 1) { + nextDimensionComponents.setTextValues(textValues.subList(1, numberOfValues)); + } + } catch (NumberFormatException e) { + // ignore and leave position unchanged + Toolkit.getDefaultToolkit().beep(); + } + } + } + + public void setDimensionValueChangeListener(final ChangeListener dimensionValueChangeListener) { + this.dimensionValueChangeListener = dimensionValueChangeListener; + } + + public void setName(final String name) { + this.valueLabel.setText(name + ": "); + } + + public void setPosition(final double position) { + if (position != this.position) { + // prevent external position change from triggering change events + valueSlider.removeChangeListener(sliderChangeListener); + + this.position = position; + + valueTextField.setText(formatValueAsDouble(position)); + + final double sliderPosition; + if (position < minPosition) { + sliderPosition = 0.0; + } else if (position > maxPosition) { + sliderPosition = 100.0; + } else { + sliderPosition = ((position - minPosition) / (maxPosition - minPosition)) * 100.0; + } + this.valueSlider.setValue((int) Math.round(sliderPosition)); + + // restore listener now that we are done + valueSlider.addChangeListener(sliderChangeListener); + } + } + + public String formatValueAsDouble(final double value) { + return String.format(Locale.ROOT, valueFormat, value); + } + + public String formatSliderValue(final double value) { + final String longString = String.valueOf(Double.valueOf(value).longValue()); + return longString.length() < 8 ? longString : String.format(Locale.ROOT, "%1.1e", value); + } + + private static final Pattern MULTI_VALUE_PATTERN = Pattern.compile("[\\s,]++"); + + /** + * @return default name for the specified dimension. + */ + public static String getDefaultDimensionName(final int dimension) { + return (dimension < DEFAULT_NAMES.length) ? DEFAULT_NAMES[dimension] : "d" + dimension; + } + + /** + * Traversal policy that forces text area to get focus before slider. + * Seems like this should not be needed, but I wasn't able to get it to work without it. + * Adapted from + * + * FocusTraversalDemo.java + * + */ + public static class FocusTraversalPolicy + extends java.awt.FocusTraversalPolicy + { + List orderedComponents; + + public FocusTraversalPolicy(List coordinateComponentsList) { + this.orderedComponents = new ArrayList<>(); + for (final DimensionCoordinateComponents coordinateComponents : coordinateComponentsList) { + this.orderedComponents.add(coordinateComponents.valueTextField); + this.orderedComponents.add(coordinateComponents.valueSlider); + } + } + public Component getComponentAfter(Container focusCycleRoot, + Component aComponent) + { + final int idx = (orderedComponents.indexOf(aComponent) + 1) % orderedComponents.size(); + return orderedComponents.get(idx); + } + + public Component getComponentBefore(Container focusCycleRoot, + Component aComponent) + { + int idx = orderedComponents.indexOf(aComponent) - 1; + if (idx < 0) { + idx = orderedComponents.size() - 1; + } + return orderedComponents.get(idx); + } + + public Component getDefaultComponent(Container focusCycleRoot) { + return orderedComponents.get(0); + } + + public Component getLastComponent(Container focusCycleRoot) { + return orderedComponents.get(orderedComponents.size() - 1); + } + + public Component getFirstComponent(Container focusCycleRoot) { + return orderedComponents.get(0); + } + } + +} diff --git a/src/main/java/bdv/viewer/location/DimensionCoordinatePanel.java b/src/main/java/bdv/viewer/location/DimensionCoordinatePanel.java deleted file mode 100644 index 286a2bc7..00000000 --- a/src/main/java/bdv/viewer/location/DimensionCoordinatePanel.java +++ /dev/null @@ -1,127 +0,0 @@ -package bdv.viewer.location; - -import java.awt.Cursor; -import java.awt.FlowLayout; -import java.util.Locale; - -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JTextField; - -/** - * Panel containing UI components for displaying and editing coordinates for one dimension of a volume. - * - * @author Eric Trautman - */ -public class DimensionCoordinatePanel - extends JPanel { - - public interface ChangeListener { - void changeLocation(final double toGlobalPosition, - final int forDimension); - } - - private String name; - private final int dimension; - private final boolean isReadOnly; - private final ChangeListener changeListener; - private final String valueFormat; - private double position; - private final JButton readOnlyValue; - private final JLabel labelForWritableValue; - private final JTextField valueTextField; - - public DimensionCoordinatePanel(final String name, - final int dimension, - final boolean isReadOnly, - final ChangeListener changeListener) { - - super(new FlowLayout(FlowLayout.LEFT, 0, 0)); - - this.name = name; - this.dimension = dimension; - this.isReadOnly = isReadOnly; - this.changeListener = changeListener; - this.valueFormat = "%1.1f"; // TODO: add support for changing this based upon volume size and display scale/size - - this.readOnlyValue = new JButton(); - this.readOnlyValue.setBorderPainted(false); - this.readOnlyValue.setFocusPainted(false); - this.readOnlyValue.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR)); - this.readOnlyValue.setOpaque(false); - this.readOnlyValue.setHorizontalTextPosition(JButton.LEFT); - this.add(this.readOnlyValue); - - if (isReadOnly) { - - this.readOnlyValue.setEnabled(false); - this.labelForWritableValue = null; - this.valueTextField = null; - - } else { - - this.labelForWritableValue = new JLabel(name + ": "); - this.add(this.labelForWritableValue); - this.labelForWritableValue.setVisible(false); - - this.valueTextField = new JTextField(10); - this.add(this.valueTextField); - this.valueTextField.setVisible(false); - - this.readOnlyValue.addActionListener(e -> { - this.labelForWritableValue.setVisible(true); - this.valueTextField.setText(String.format(Locale.ROOT, valueFormat, position)); - this.valueTextField.setEditable(true); - this.readOnlyValue.setVisible(false); - this.valueTextField.setVisible(true); - this.valueTextField.requestFocus(); - }); - - this.valueTextField.addActionListener(e -> stopEditing()); - - final DimensionCoordinatePanel thisPanel = this; - this.valueTextField.addFocusListener(new java.awt.event.FocusAdapter() { - public void focusLost(java.awt.event.FocusEvent evt) { - thisPanel.stopEditing(); - } - }); - } - } - - private void stopEditing() { - if (valueTextField.isEditable()) { - try { - final double updatedPosition = Double.parseDouble(valueTextField.getText()); - changeListener.changeLocation(updatedPosition, dimension); - } catch (NumberFormatException e) { - // ignore and leave position unchanged - } - closeEditor(); - } - } - - private void closeEditor() { - this.labelForWritableValue.setVisible(false); - this.valueTextField.setEditable(false); - this.valueTextField.setVisible(false); - this.readOnlyValue.setVisible(true); - } - - public void setName(final String name) { - this.name = name; - this.labelForWritableValue.setText(name + ": "); - } - - public void setPosition(final double position) { - this.position = position; - final String formattedValue = String.format(Locale.ROOT, valueFormat, position); - if (isReadOnly) { - this.readOnlyValue.setText(name + ": " + formattedValue); - } else if (this.readOnlyValue.isVisible()) { - this.readOnlyValue.setText(name + ": " + formattedValue); - this.valueTextField.setText(formattedValue); - } - } - -} diff --git a/src/main/java/bdv/viewer/location/LocationPanel.java b/src/main/java/bdv/viewer/location/LocationPanel.java new file mode 100644 index 00000000..11508bc3 --- /dev/null +++ b/src/main/java/bdv/viewer/location/LocationPanel.java @@ -0,0 +1,100 @@ +package bdv.viewer.location; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JPanel; +import javax.swing.event.ChangeListener; + +import net.imglib2.Interval; + +/** + * Panel containing UI components for displaying and editing location coordinates. + * + * @author Eric Trautman + */ +public class LocationPanel + extends JPanel { + + private final List dimensionComponentsList; + + public LocationPanel(final Interval sourceInterval) { + + super(new GridBagLayout()); + + this.dimensionComponentsList = new ArrayList<>(); + + for (int dimension = 0; dimension < sourceInterval.numDimensions(); dimension++) { + + final DimensionCoordinateComponents coordinateComponents = + new DimensionCoordinateComponents(dimension, + sourceInterval.min(dimension), + sourceInterval.max(dimension)); + + this.dimensionComponentsList.add(coordinateComponents); + + final GridBagConstraints c = new GridBagConstraints(); + c.gridx = GridBagConstraints.RELATIVE; + c.gridy = dimension; + c.weightx = 0.01; + this.add(coordinateComponents.getValueLabel(), c); + + // text field should grow with card width + c.weightx = 0.99; + c.fill = GridBagConstraints.HORIZONTAL; + this.add(coordinateComponents.getValueTextField(), c); + + c.weightx = 0.01; + c.fill = GridBagConstraints.NONE; + this.add(coordinateComponents.getValueSlider(), c); + } + + // connect dimension components to support multi-value paste + for (int d = 1; d < dimensionComponentsList.size(); d++) { + final DimensionCoordinateComponents prior = dimensionComponentsList.get(d - 1); + prior.setNextDimensionComponents(dimensionComponentsList.get(d)); + } + + // make text area get focus before slider, not sure why I had to go to all of this trouble to make focus work + this.setFocusCycleRoot(true); + this.setFocusTraversalPolicy(new DimensionCoordinateComponents.FocusTraversalPolicy(dimensionComponentsList)); + } + + /** + * Set the single listener to be called whenever a dimension value is changed. + * Typical usage is + *
+     * e -> {
+     *   dcc = (DimensionCoordinateComponents) e.getSource();
+     *   viewerPanel.centerViewAt(dcc.getPosition(), dcc.getDimension());
+     * }
+     * 
+ */ + public void setDimensionValueChangeListener(final ChangeListener changeListener) { + for (final DimensionCoordinateComponents dimensionComponents : dimensionComponentsList) { + dimensionComponents.setDimensionValueChangeListener(changeListener); + } + } + + public void requestFocusOnFirstComponent() { + if (! dimensionComponentsList.isEmpty()) { + this.dimensionComponentsList.get(0).getValueTextField().requestFocus(); + } + } + + @SuppressWarnings("unused") + public void setNames(final String[] dimensionNames) { + for (int i = 0; i < dimensionNames.length && i < this.dimensionComponentsList.size(); i++) { + this.dimensionComponentsList.get(i).setName(dimensionNames[i]); + } + } + + public void setCenterPosition(final double[] centerPosition) { + for (int i = 0; i < centerPosition.length; i++) { + this.dimensionComponentsList.get(i).setPosition(centerPosition[i]); + } + } + +} diff --git a/src/main/java/bdv/viewer/location/LocationToolBar.java b/src/main/java/bdv/viewer/location/LocationToolBar.java index dcc6c56a..51eb9cfc 100644 --- a/src/main/java/bdv/viewer/location/LocationToolBar.java +++ b/src/main/java/bdv/viewer/location/LocationToolBar.java @@ -1,83 +1,96 @@ package bdv.viewer.location; +import java.awt.Color; import java.awt.FlowLayout; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; +import java.awt.Insets; +import java.awt.event.ActionListener; +import java.net.URL; +import java.util.Locale; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JToolBar; /** - * Toolbar containing location information along with UI components to change it. + * Toolbar containing location information. * * @author Eric Trautman */ public class LocationToolBar extends JToolBar { - private final DimensionCoordinatePanel.ChangeListener dimensionChangeListener; - private final JPanel dimensionPanel; - private final List dimensionNames; - private final List centerCoordinatePanelList; - private final List mouseCoordinatePanelList; + private final JLabel centerCoordinatesLabel; + private final JButton editCoordinatesButton; + private final JLabel mouseCoordinatesLabel; - public LocationToolBar(final DimensionCoordinatePanel.ChangeListener dimensionChangeListener) { + private ActionListener editCoordinatesActionListener; + + public LocationToolBar() { super("Location Tools"); this.setFloatable(false); - this.dimensionChangeListener = dimensionChangeListener; - this.dimensionPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 1)); - this.add(this.dimensionPanel); + final JPanel flowPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 1)); + this.add(flowPanel); - this.dimensionNames = Arrays.asList("x", "y", "z", "t"); - this.centerCoordinatePanelList = new ArrayList<>(); - this.mouseCoordinatePanelList = new ArrayList<>(); - } + this.centerCoordinatesLabel = new JLabel(); + + final URL url = this.getClass().getResource("/bdv/ui/location/edit_pencil_20.png"); + if (url == null) { + this.editCoordinatesButton = new JButton("Edit"); + } else { + this.editCoordinatesButton = new JButton(new ImageIcon(url)); + this.editCoordinatesButton.setMargin(new Insets(0, 0, 0, 0)); + this.editCoordinatesButton.setContentAreaFilled(false); + } + this.editCoordinatesButton.setToolTipText("Edit center coordinates"); + this.editCoordinatesButton.setVisible(false); + + this.mouseCoordinatesLabel = new JLabel(); + this.mouseCoordinatesLabel.setForeground(Color.MAGENTA); - public void setDimensionNames(final List dimensionNames) { - this.dimensionNames.clear(); - this.dimensionNames.addAll(dimensionNames); + flowPanel.add(this.centerCoordinatesLabel); + flowPanel.add(this.editCoordinatesButton); + flowPanel.add(this.mouseCoordinatesLabel); + } - for (int i = 0; i < this.centerCoordinatePanelList.size(); i++) { - this.centerCoordinatePanelList.get(i).setName(getDimensionName(i)); - this.mouseCoordinatePanelList.get(i).setName(getDimensionName(i)); + public void setEditActionListener(final ActionListener editCoordinatesActionListener) { + if (this.editCoordinatesActionListener != null) { + this.editCoordinatesButton.removeActionListener(this.editCoordinatesActionListener); + } + this.editCoordinatesActionListener = editCoordinatesActionListener; + if (editCoordinatesActionListener == null) { + this.editCoordinatesButton.setVisible(false); + } else { + this.editCoordinatesButton.addActionListener(editCoordinatesActionListener); + this.editCoordinatesButton.setVisible(true); } } public void setPositions(final double[] centerPosition, final double[] mousePosition) { - if (centerPosition.length != this.centerCoordinatePanelList.size()) { - setNumberOfDimensions(centerPosition.length); - } for (int i = 0; i < centerPosition.length; i++) { - this.centerCoordinatePanelList.get(i).setPosition(centerPosition[i]); - this.mouseCoordinatePanelList.get(i).setPosition(mousePosition[i]); + this.centerCoordinatesLabel.setText(formatPosition(centerPosition)); + this.mouseCoordinatesLabel.setText(formatPosition(mousePosition)); } } - private synchronized void setNumberOfDimensions(final int numberOfDimensions) { - if (numberOfDimensions != centerCoordinatePanelList.size()) { - dimensionPanel.removeAll(); - addDimensions(numberOfDimensions, false, centerCoordinatePanelList); - addDimensions(numberOfDimensions, true, mouseCoordinatePanelList); - dimensionPanel.revalidate(); // need this to make panel components visible + private String formatPosition(final double[] position) { + final StringBuilder sb = new StringBuilder(); + for (int i = 0; i < position.length; i++) { + if (i > 0) { + sb.append(" "); + } + sb.append(formatCoordinate(i, position[i])); } + return sb.toString(); } - private void addDimensions(final int numberOfDimensions, - final boolean isReadOnly, - final List coordinatePanelList) { - for (int i = 0; i < numberOfDimensions; i++) { - final DimensionCoordinatePanel coordinatePanel = new DimensionCoordinatePanel(getDimensionName(i), - i, - isReadOnly, - dimensionChangeListener); - coordinatePanelList.add(coordinatePanel); - dimensionPanel.add(coordinatePanel); - } + private String formatCoordinate(final int dimension, + final double value) { + return String.format(Locale.ROOT, "%s: %1.0f", + DimensionCoordinateComponents.getDefaultDimensionName(dimension), + value); } - private String getDimensionName(final int dimensionIndex) { - return dimensionIndex < this.dimensionNames.size() ? this.dimensionNames.get(dimensionIndex) : "d" + dimensionIndex; - } } diff --git a/src/main/resources/bdv/ui/location/edit_pencil_20.png b/src/main/resources/bdv/ui/location/edit_pencil_20.png new file mode 100644 index 0000000000000000000000000000000000000000..9c163d28dc6e13f066ee1c965cc206fe1ed74836 GIT binary patch literal 1159 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE-VV{wqX6T`Z5GB1Ig38@jDX}-P; zT0k}j0~;e712aeo0~3%Cg0dNbY$*m-us8!l7^5_t9mlAFP~V=(zyej12Bbm21BijT zp)~UXMudqAnBb~K7BC~&AZ?uO3b#O1fk$L90|U1(2s1Lwnj^u$z{H&y5>XQ2>tmIi zpR1RclAn~SSCLx)(#2p?VFhI7rj{fsROII56h?X&sHg;q@=(~U%$M(T(8_%FTW^V-_X+1 zQs2Nx-^fT8s6w~6GOr}DLN~8i8Da>`9GBGMm=w6UEJ42`zWHEI0&iT0oMXANbAoZqD^~hrA>H{+JOY(tXU6h&ZnOBlp1k?<* z4dy`HDxhZB=!1d_Dfl390~Q5FjvbecK0KoBxUO)$(f}s1N>3NZ5DUS#lWhGDIY=Du zZ$Gw8O6j7>hJ-0gxVdXs4jp*%?AZ!Fy*6Jbu32qUdK-Hu{9sf_WMbdnc=4d)og1AA zvnFw=EP48I#pIJ;S6ELs+50EHZm*54(>CoBEE5Do7him=aI;r(%2Q*OA1CmRJB#C>Q|Y$LSt3@yw&2}t=83_wjTPYylB-wCPp`rnnSvq&P3(9 zzyBKavbc`&E1aogHyO_NVD-es0zyHnh)v-vJx{F&#EE}1+Q z5&OE}_2ew67i(<7qjg&5JU;QQV$OmMwOrkcbG=u*UQpPrV=SgzeKqUF*-}v%zV#Y9 zjhkn_Fq10qUVrbu{sxUnH7ZOcFA{$-iugXC^mO%eS?83{1OVMhc~SrX literal 0 HcmV?d00001 diff --git a/src/main/resources/bdv/ui/location/edit_pencil_40.png b/src/main/resources/bdv/ui/location/edit_pencil_40.png new file mode 100644 index 0000000000000000000000000000000000000000..bc740bb0dc8c7370fc55802deeacf8e6702c2c08 GIT binary patch literal 1090 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEjKx9jP7LeL$-D$|0#YM9(|mmy zw18|51|~)!24;{FAY^FIWMBca85q=nGy?BG9+G7*YkjTuC zh>{3jAFJg2T)o7U{G?R9irfN_0tTB3D*7iAWdWaj57fXq!y$}cUk zRZ;?31P2gzmSm&Fu6PDlAfQe^)E&lmi-L@0^!U}4<0Brb-21%?MN%x^i$05lB?JD&-Il%_pzLL zFhN|i^23fTHap_$%fAPmUKO6Zz$`%e1`qR<_vR-zExVEbf8i*>=bl-OWD` zxG8q}iO+HjH&@6^VrxEZI)lOM%bv(oHA_R+i-xR?o74hsuJB1I`(khnr&^S3Xbml_@r;XBJ;%wiGgsusQ0|5e%c9tNRa z>2108!mMiqGI(aLQ=0tu-!@MRAC+|zQ>rCT#QS`Dz|+pI_At(nRa3_KQ?Zzkd}ZfJ z4J#uS^TL2@;<=&%!xA|o-=KjA#Cwer$LXAkJ&_1zOUZ) zfnV2IPMPCdEf4he{!NzjOk#bRdP=LnvUo;balzdEGn!wzC#Slm&NkQ{^6f}S;dw^B z%egC_2RK{z-MypTqv5kW^sv`U#`c$H1?NweKKOl2Zc>m;bcJ_eec}Dqm(SAg7F!&@ z9Q#jIr$FX*?(=Qei-nSTR<<`kys?0ZMP}J;0~zK=e7}>LBRx`sW*^=tIDc&lW8TpQ zgPF}fXLT8`u4M6 b=KaGkIsB{aPp_|1pd9Jx>gTe~DWM4fa0-RE literal 0 HcmV?d00001 diff --git a/src/test/java/bdv/viewer/location/GridViewerTest.java b/src/test/java/bdv/viewer/location/GridViewerTest.java new file mode 100644 index 00000000..ada955b8 --- /dev/null +++ b/src/test/java/bdv/viewer/location/GridViewerTest.java @@ -0,0 +1,108 @@ +/*- + * #%L + * BigDataViewer core classes with minimal dependencies. + * %% + * Copyright (C) 2012 - 2023 BigDataViewer developers. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package bdv.viewer.location; + +import java.io.IOException; +import java.util.ArrayList; + +import mpicbg.spim.data.SpimDataException; + +import bdv.BigDataViewer; +import bdv.cache.CacheControl; +import bdv.export.ProgressWriterConsole; +import bdv.tools.brightness.ConverterSetup; +import bdv.tools.brightness.RealARGBColorConverterSetup; +import bdv.util.RealRandomAccessibleSource; +import bdv.viewer.SourceAndConverter; +import bdv.viewer.ViewerOptions; +import net.imglib2.Interval; +import net.imglib2.display.RealARGBColorConverter; +import net.imglib2.position.FunctionRealRandomAccessible; +import net.imglib2.type.numeric.integer.IntType; +import net.imglib2.util.Intervals; + +public class GridViewerTest { + + public static void main(String[] args) + throws IOException, SpimDataException { + + final ArrayList converterSetups = new ArrayList<>(); + final ArrayList> sources = new ArrayList<>(); + final CacheControl cache = new CacheControl.Dummy(); + final ProgressWriterConsole progressWriter = new ProgressWriterConsole(); + final ViewerOptions viewerOptions = ViewerOptions.options(); + + final RealARGBColorConverter converter = RealARGBColorConverter.create(new IntType(), 0, 127); + final ConverterSetup converterSetup = new RealARGBColorConverterSetup(0, converter); + converterSetups.add(converterSetup); + + final SourceAndConverter soc = new SourceAndConverter<>(GRID_SOURCE, converter); + sources.add(soc); + + BigDataViewer.open(converterSetups, + sources, + 1, + cache, + "Viewer Example", + progressWriter, + viewerOptions); + } + + public static final FunctionRealRandomAccessible GRID = + new FunctionRealRandomAccessible<>( + 3, + (x, y) -> { + int i = 0; + int xPos = (int) Math.round(x.getDoublePosition(0)); + int yPos = (int) Math.round(x.getDoublePosition(1)); + if ((xPos == 0) || (yPos == 0)) { + i = 120; + } else { + if (xPos % 10 == 0) { + i = 60; + } else if (yPos % 10 == 0) { + i = 30; + } + } + y.set(i); + }, + IntType::new); + + public static final RealRandomAccessibleSource GRID_SOURCE = + new RealRandomAccessibleSource(GRID, new IntType(), "Grid" ) { + private final int s = 100; + private final Interval interval = Intervals.createMinMax(-s, -s, -s, s, s, s); + @Override + public Interval getInterval(final int t, + final int level) { + return interval; + } + }; + +} diff --git a/src/test/java/bdv/viewer/location/LocationPanelTest.java b/src/test/java/bdv/viewer/location/LocationPanelTest.java new file mode 100644 index 00000000..4672ef5d --- /dev/null +++ b/src/test/java/bdv/viewer/location/LocationPanelTest.java @@ -0,0 +1,62 @@ +/*- + * #%L + * BigDataViewer core classes with minimal dependencies. + * %% + * Copyright (C) 2012 - 2023 BigDataViewer developers. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package bdv.viewer.location; + +import javax.swing.JFrame; +import javax.swing.event.ChangeListener; + +import net.imglib2.Interval; +import net.imglib2.util.Intervals; + +public class LocationPanelTest { + + public static void main(String[] args) { + + JFrame frame = new JFrame("LocationPanelTest"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + final Interval interval = Intervals.createMinMax(0, -111222333444L, -98765, 100, 66, 4321); + final LocationPanel locationPanel = new LocationPanel(interval); + + final ChangeListener changeListener = e -> { + final DimensionCoordinateComponents source = (DimensionCoordinateComponents) e.getSource(); + System.out.println("coordinateChangeListener: " + source.getPosition() + ", " + source.getDimension()); + }; + locationPanel.setDimensionValueChangeListener(changeListener); + + frame.setContentPane(locationPanel); + + //Display the window. + frame.pack(); + frame.setVisible(true); + + locationPanel.setCenterPosition(new double[] {35.67, -111222333444.5, -222 }); + } + +} From 6d2629839fed5651e101a251accfa3b04081bd94 Mon Sep 17 00:00:00 2001 From: Eric Trautman Date: Tue, 24 Oct 2023 18:27:06 -0400 Subject: [PATCH 03/13] update location toolbar to source info toolbar and include source and time point info --- .../java/bdv/ui/appearance/Appearance.java | 14 +++--- .../java/bdv/ui/appearance/AppearanceIO.java | 4 +- .../bdv/ui/appearance/AppearanceManager.java | 3 +- .../ui/appearance/AppearanceSettingsPage.java | 2 +- .../java/bdv/ui/splitpanel/SplitPanel.java | 8 +-- src/main/java/bdv/util/Prefs.java | 17 +++---- src/main/java/bdv/viewer/ViewerPanel.java | 49 ++++++++++--------- ...ionToolBar.java => SourceInfoToolBar.java} | 47 ++++++++++++++++-- .../overlay/SourceInfoOverlayRenderer.java | 14 +++++- 9 files changed, 106 insertions(+), 52 deletions(-) rename src/main/java/bdv/viewer/location/{LocationToolBar.java => SourceInfoToolBar.java} (64%) diff --git a/src/main/java/bdv/ui/appearance/Appearance.java b/src/main/java/bdv/ui/appearance/Appearance.java index 4d8835ab..1ed96dcc 100644 --- a/src/main/java/bdv/ui/appearance/Appearance.java +++ b/src/main/java/bdv/ui/appearance/Appearance.java @@ -58,7 +58,7 @@ public class Appearance private int scaleBarColor = 0xffffffff; private int scaleBarBgColor = 0x88000000; private LookAndFeelInfo lookAndFeel = DONT_MODIFY_LOOK_AND_FEEL; - private boolean showLocationBar = false; + private boolean showSourceInfoToolBar = false; public interface UpdateListener { @@ -82,7 +82,7 @@ public void set( final Appearance other ) this.scaleBarColor = other.scaleBarColor; this.scaleBarBgColor = other.scaleBarBgColor; this.lookAndFeel = other.lookAndFeel; - this.showLocationBar = other.showLocationBar; + this.showSourceInfoToolBar = other.showSourceInfoToolBar; notifyListeners(); } @@ -224,12 +224,12 @@ public static LookAndFeelInfo lookAndFeelInfoForName( final String name ) return DONT_MODIFY_LOOK_AND_FEEL; } - public boolean showLocationBar() { - return showLocationBar; + public boolean showSourceInfoToolBar() { + return showSourceInfoToolBar; } - public void setShowLocationBar(final boolean showLocationBar) { - this.showLocationBar = showLocationBar; + public void setShowSourceInfoToolBar(final boolean showSourceInfoToolBar) { + this.showSourceInfoToolBar = showSourceInfoToolBar; notifyListeners(); } @@ -245,7 +245,7 @@ public String toString() sb.append( ", scaleBarColor=" ).append( scaleBarColor ); sb.append( ", scaleBarBgColor=" ).append( scaleBarBgColor ); sb.append( ", lookAndFeel=" ).append( lookAndFeel ); - sb.append( ", showLocationBar=" ).append( showLocationBar ); + sb.append( ", showSourceInfoToolBar=" ).append(showSourceInfoToolBar); sb.append( '}' ); return sb.toString(); } diff --git a/src/main/java/bdv/ui/appearance/AppearanceIO.java b/src/main/java/bdv/ui/appearance/AppearanceIO.java index 537ae5f2..609e9e59 100644 --- a/src/main/java/bdv/ui/appearance/AppearanceIO.java +++ b/src/main/java/bdv/ui/appearance/AppearanceIO.java @@ -112,7 +112,7 @@ public Node representData( final Object data ) mapping.put( "scaleBarColor", hex( a.scaleBarColor() ) ); mapping.put( "scaleBarBgColor", hex( a.scaleBarBgColor() ) ); mapping.put( "lookAndFeel", a.lookAndFeel() ); - mapping.put( "showLocationBar", a.showLocationBar() ); + mapping.put( "showSourceInfoToolBar", a.showSourceInfoToolBar() ); return representMapping( APPEARANCE_TAG, mapping, getDefaultFlowStyle() ); } } @@ -179,7 +179,7 @@ public Object construct( final Node node ) a.setScaleBarColor( hexColor( mapping.get( "scaleBarColor" ) ) ); a.setScaleBarBgColor( hexColor( mapping.get( "scaleBarBgColor" ) ) ); a.setLookAndFeel( ( LookAndFeelInfo ) mapping.get( "lookAndFeel" ) ); - a.setShowLocationBar( ( Boolean ) mapping.get( "showLocationBar" ) ); + a.setShowSourceInfoToolBar(( Boolean ) mapping.get("showSourceInfoToolBar" ) ); return a; } catch( final Exception e ) diff --git a/src/main/java/bdv/ui/appearance/AppearanceManager.java b/src/main/java/bdv/ui/appearance/AppearanceManager.java index cb66801b..5a85e296 100644 --- a/src/main/java/bdv/ui/appearance/AppearanceManager.java +++ b/src/main/java/bdv/ui/appearance/AppearanceManager.java @@ -179,7 +179,7 @@ public void toPrefs() Prefs.sourceNameOverlayPosition( appearance.sourceNameOverlayPosition() ); Prefs.scaleBarColor( appearance.scaleBarColor() ); Prefs.scaleBarBgColor( appearance.scaleBarBgColor() ); - Prefs.showLocationBar( appearance.showLocationBar() ); + Prefs.showSourceInfoToolBar( appearance.showSourceInfoToolBar() ); } /** @@ -195,5 +195,6 @@ public void fromPrefs() appearance.setSourceNameOverlayPosition( Prefs.sourceNameOverlayPosition() ); appearance.setScaleBarColor( Prefs.scaleBarColor() ); appearance.setScaleBarBgColor( Prefs.scaleBarBgColor() ); + appearance.setShowSourceInfoToolBar(Prefs.showSourceInfoToolBar() ); } } diff --git a/src/main/java/bdv/ui/appearance/AppearanceSettingsPage.java b/src/main/java/bdv/ui/appearance/AppearanceSettingsPage.java index f7dcb027..abc0f936 100644 --- a/src/main/java/bdv/ui/appearance/AppearanceSettingsPage.java +++ b/src/main/java/bdv/ui/appearance/AppearanceSettingsPage.java @@ -137,7 +137,7 @@ public AppearancePanel( final Appearance appearance ) booleanElement( "show scalebar in movies", appearance::showScaleBarInMovie, appearance::setShowScaleBarInMovie ), colorElement( "scalebar foreground", appearance::scaleBarColor, appearance::setScaleBarColor ), colorElement( "scalebar background", appearance::scaleBarBgColor, appearance::setScaleBarBgColor ), - booleanElement( "show location bar", appearance::showLocationBar, appearance::setShowLocationBar ), + booleanElement("show source info tool bar", appearance::showSourceInfoToolBar, appearance::setShowSourceInfoToolBar), separator(), booleanElement( "show minimap", appearance::showMultibox, appearance::setShowMultibox ), booleanElement( "show source info", appearance::showTextOverlay, appearance::setShowTextOverlay ), diff --git a/src/main/java/bdv/ui/splitpanel/SplitPanel.java b/src/main/java/bdv/ui/splitpanel/SplitPanel.java index ca5d4b9e..412cc28e 100644 --- a/src/main/java/bdv/ui/splitpanel/SplitPanel.java +++ b/src/main/java/bdv/ui/splitpanel/SplitPanel.java @@ -48,7 +48,7 @@ import bdv.viewer.AbstractViewerPanel; import bdv.viewer.ViewerPanel; import bdv.viewer.location.LocationPanel; -import bdv.viewer.location.LocationToolBar; +import bdv.viewer.location.SourceInfoToolBar; import static bdv.ui.BdvDefaultCards.*; @@ -138,12 +138,12 @@ public void componentResized( final ComponentEvent e ) } } ); - // add hook to expand card panel and locations card when edit button in locations toolbar is clicked + // add hook to expand card panel and locations card when edit button in source info toolbar is clicked if (viewerPanel instanceof ViewerPanel) { final ViewerPanel viewer = (ViewerPanel) viewerPanel; final LocationPanel locationPanel = viewer.getLocationPanel(); - final LocationToolBar locationToolBar = viewer.getLocationToolBar(); - locationToolBar.setEditActionListener(e -> { + final SourceInfoToolBar sourceInfoToolBar = viewer.getSourceInfoToolBar(); + sourceInfoToolBar.setEditActionListener(e -> { // expand card panel and location card this.setCollapsed(false); cardPanel.setCardExpanded(DEFAULT_SOURCES_CARD, false); diff --git a/src/main/java/bdv/util/Prefs.java b/src/main/java/bdv/util/Prefs.java index 854a0be6..dee4b76c 100644 --- a/src/main/java/bdv/util/Prefs.java +++ b/src/main/java/bdv/util/Prefs.java @@ -43,9 +43,9 @@ public static boolean showScaleBar() return getInstance().showScaleBar; } - public static boolean showLocationBar() + public static boolean showSourceInfoToolBar() { - return getInstance().showLocationBar; + return getInstance().showSourceInfoToolBar; } public static boolean showMultibox() @@ -113,9 +113,9 @@ public static void scaleBarBgColor( final int color ) getInstance().scaleBarBgColor = color; } - public static void showLocationBar( final boolean show ) + public static void showSourceInfoToolBar(final boolean show ) { - getInstance().showLocationBar = show; + getInstance().showSourceInfoToolBar = show; } private static Prefs instance; @@ -142,8 +142,7 @@ public enum OverlayPosition private static final String SHOW_SCALE_BAR_IN_MOVIE = "show-scale-bar-in-movie"; private static final String SCALE_BAR_COLOR = "scale-bar-color"; private static final String SCALE_BAR_BG_COLOR = "scale-bar-bg-color"; - private static final String SHOW_LOCATION_BAR = "show-location-bar"; - + private static final String SHOW_SOURCE_INFO_TOOL_BAR = "show-source-info-tool-bar"; private boolean showScaleBar; private boolean showMultibox; private boolean showTextOverlay; @@ -151,7 +150,7 @@ public enum OverlayPosition private boolean showScaleBarInMovie; private int scaleBarColor; private int scaleBarBgColor; - private boolean showLocationBar; + private boolean showSourceInfoToolBar; private Prefs( final Properties p ) { @@ -162,7 +161,7 @@ private Prefs( final Properties p ) showScaleBarInMovie = getBoolean( p, SHOW_SCALE_BAR_IN_MOVIE, false ); scaleBarColor = getInt( p, SCALE_BAR_COLOR, 0xffffffff ); scaleBarBgColor = getInt( p, SCALE_BAR_BG_COLOR, 0x88000000 ); - showLocationBar = getBoolean( p, SHOW_LOCATION_BAR, false ); + showSourceInfoToolBar = getBoolean(p, SHOW_SOURCE_INFO_TOOL_BAR, false ); } private boolean getBoolean( final Properties p, final String key, final boolean defaultValue ) @@ -257,7 +256,7 @@ public static Properties getDefaultProperties() properties.put( SHOW_SCALE_BAR_IN_MOVIE, "" + prefs.showScaleBarInMovie ); properties.put( SCALE_BAR_COLOR, "" + prefs.scaleBarColor ); properties.put( SCALE_BAR_BG_COLOR, "" + prefs.scaleBarBgColor ); - properties.put( SHOW_LOCATION_BAR, "" + prefs.showLocationBar ); + properties.put( SHOW_SOURCE_INFO_TOOL_BAR, "" + prefs.showSourceInfoToolBar ); return properties; } diff --git a/src/main/java/bdv/viewer/ViewerPanel.java b/src/main/java/bdv/viewer/ViewerPanel.java index 6a7e6dd8..df3a11d7 100644 --- a/src/main/java/bdv/viewer/ViewerPanel.java +++ b/src/main/java/bdv/viewer/ViewerPanel.java @@ -66,7 +66,7 @@ import bdv.viewer.animate.TextOverlayAnimator; import bdv.viewer.animate.TextOverlayAnimator.TextPosition; import bdv.viewer.location.LocationPanel; -import bdv.viewer.location.LocationToolBar; +import bdv.viewer.location.SourceInfoToolBar; import bdv.viewer.overlay.MultiBoxOverlayRenderer; import bdv.viewer.overlay.ScaleBarOverlayRenderer; import bdv.viewer.overlay.SourceInfoOverlayRenderer; @@ -136,7 +136,7 @@ public class ViewerPanel extends AbstractViewerPanel implements OverlayRenderer, */ private final ScaleBarOverlayRenderer scaleBarOverlayRenderer; - private final LocationToolBar locationToolBar; + private final SourceInfoToolBar sourceInfoToolBar; private LocationPanel locationPanel; private final TransformEventHandler transformEventHandler; @@ -265,9 +265,9 @@ public ViewerPanel( final List< SourceAndConverter< ? > > sources, final int num display.addHandler( mouseCoordinates ); - // add location toolbar to viewer - will be visible/hidden depending on Prefs.showLocationBar() - locationToolBar = new LocationToolBar(); - add(locationToolBar, BorderLayout.NORTH); + // add source info toolbar to viewer - will be visible/hidden depending on Prefs.showSourceInfoToolBar() + sourceInfoToolBar = new SourceInfoToolBar(); + add(sourceInfoToolBar, BorderLayout.NORTH); sliderTime = new JSlider( SwingConstants.HORIZONTAL, 0, numTimepoints - 1, 0 ); sliderTime.addChangeListener( e -> { @@ -307,8 +307,8 @@ public void componentResized( final ComponentEvent e ) painterThread.start(); } - public LocationToolBar getLocationToolBar() { - return locationToolBar; + public SourceInfoToolBar getSourceInfoToolBar() { + return sourceInfoToolBar; } public LocationPanel getLocationPanel() { @@ -525,7 +525,7 @@ public void requestRepaint( final Interval screenInterval ) @Override protected void onMouseMoved() { - if ( Prefs.showTextOverlay() ) + if ( Prefs.showTextOverlay()|| Prefs.showSourceInfoToolBar() ) // trigger repaint for showing updated mouse coordinates getDisplayComponent().repaint(); } @@ -542,39 +542,42 @@ public void drawOverlays( final Graphics g ) requiresRepaint = multiBoxOverlayRenderer.isHighlightInProgress(); } - final boolean showLocationBar = Prefs.showLocationBar(); - locationToolBar.setVisible(showLocationBar); + // always set sourceInfoOverlayRenderer state, even if not visible, in case it is used for source info toolbar + sourceInfoOverlayRenderer.setViewerState(state); + + final boolean showSourceInfoToolBar = Prefs.showSourceInfoToolBar(); + sourceInfoToolBar.setVisible(showSourceInfoToolBar); final double[] gMousePos = new double[3]; getGlobalMouseCoordinates(RealPoint.wrap(gMousePos)); - if (showLocationBar || (locationPanel != null)) { + if (showSourceInfoToolBar || (locationPanel != null)) { final double[] gCenterPos = new double[3]; state().getViewerTransform().applyInverse(gCenterPos, getDisplayCenterCoordinates()); - if (showLocationBar) { - locationToolBar.setPositions(gCenterPos, gMousePos); + if (showSourceInfoToolBar) { + sourceInfoToolBar.setSourceNamesAndTimepoint(sourceInfoOverlayRenderer.getSourceName(), + sourceInfoOverlayRenderer.getGroupName(), + sourceInfoOverlayRenderer.getTimepointString()); + sourceInfoToolBar.setPositions(gCenterPos, gMousePos); } if (locationPanel != null) { locationPanel.setCenterPosition(gCenterPos); } } - if ( Prefs.showTextOverlay() ) + if ( Prefs.showTextOverlay() && (! showSourceInfoToolBar) ) { final Font font = UIUtils.getFont( "monospaced.small.font" ); - sourceInfoOverlayRenderer.setViewerState( state ); sourceInfoOverlayRenderer.setSourceNameOverlayPosition( Prefs.sourceNameOverlayPosition() ); sourceInfoOverlayRenderer.paint( ( Graphics2D ) g ); - if (! showLocationBar) { - final String mousePosGlobalString = options.is2D() - ? String.format(Locale.ROOT, "%6.1f, %6.1f", gMousePos[0], gMousePos[1]) - : - String.format(Locale.ROOT, "%6.1f, %6.1f, %6.1f", gMousePos[0], gMousePos[1], gMousePos[2]); + final String mousePosGlobalString = options.is2D() + ? String.format(Locale.ROOT, "%6.1f, %6.1f", gMousePos[0], gMousePos[1]) + : + String.format(Locale.ROOT, "%6.1f, %6.1f, %6.1f", gMousePos[0], gMousePos[1], gMousePos[2]); - g.setFont(font); - UIUtils.drawString(g, TOP_RIGHT, 1, mousePosGlobalString); - } + g.setFont(font); + UIUtils.drawString(g, TOP_RIGHT, 1, mousePosGlobalString); } if ( Prefs.showScaleBar() ) diff --git a/src/main/java/bdv/viewer/location/LocationToolBar.java b/src/main/java/bdv/viewer/location/SourceInfoToolBar.java similarity index 64% rename from src/main/java/bdv/viewer/location/LocationToolBar.java rename to src/main/java/bdv/viewer/location/SourceInfoToolBar.java index 51eb9cfc..038ba34e 100644 --- a/src/main/java/bdv/viewer/location/LocationToolBar.java +++ b/src/main/java/bdv/viewer/location/SourceInfoToolBar.java @@ -14,26 +14,39 @@ import javax.swing.JToolBar; /** - * Toolbar containing location information. + * Toolbar containing source information. * * @author Eric Trautman */ -public class LocationToolBar extends JToolBar { +public class SourceInfoToolBar extends JToolBar { + private final JLabel sourceNameLabel; + private final JLabel groupNameLabel; private final JLabel centerCoordinatesLabel; + private final JLabel timepointLabel; private final JButton editCoordinatesButton; private final JLabel mouseCoordinatesLabel; private ActionListener editCoordinatesActionListener; - public LocationToolBar() { - super("Location Tools"); + public SourceInfoToolBar() { + super("Source Information"); this.setFloatable(false); final JPanel flowPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 1)); this.add(flowPanel); + this.sourceNameLabel = new JLabel(); + this.sourceNameLabel.setToolTipText("Source name"); + + this.groupNameLabel = new JLabel(); + this.groupNameLabel.setToolTipText("Group name"); + + this.timepointLabel = new JLabel(); + this.timepointLabel.setToolTipText("Timepoint"); + this.centerCoordinatesLabel = new JLabel(); + this.centerCoordinatesLabel.setToolTipText("Center coordinates"); final URL url = this.getClass().getResource("/bdv/ui/location/edit_pencil_20.png"); if (url == null) { @@ -47,13 +60,39 @@ public LocationToolBar() { this.editCoordinatesButton.setVisible(false); this.mouseCoordinatesLabel = new JLabel(); + this.mouseCoordinatesLabel.setToolTipText("Mouse coordinates"); this.mouseCoordinatesLabel.setForeground(Color.MAGENTA); + flowPanel.add(this.sourceNameLabel); + flowPanel.add(this.groupNameLabel); + flowPanel.add(this.timepointLabel); flowPanel.add(this.centerCoordinatesLabel); flowPanel.add(this.editCoordinatesButton); flowPanel.add(this.mouseCoordinatesLabel); } + public void setSourceNamesAndTimepoint(final String sourceName, + final String groupName, + final String timepointString) { + if ((sourceName != null) && (! sourceName.isEmpty())) { + this.sourceNameLabel.setText(sourceName); + if ((groupName != null) && (! groupName.isEmpty())) { + this.groupNameLabel.setText("| " + groupName); + this.groupNameLabel.setVisible(true); + } else { + this.groupNameLabel.setVisible(false); + } + this.sourceNameLabel.setVisible(true); + } else { + this.sourceNameLabel.setVisible(false); + } + if ((timepointString != null) && (! timepointString.isEmpty())) { + this.timepointLabel.setText(timepointString); + } else { + this.timepointLabel.setText(""); + } + } + public void setEditActionListener(final ActionListener editCoordinatesActionListener) { if (this.editCoordinatesActionListener != null) { this.editCoordinatesButton.removeActionListener(this.editCoordinatesActionListener); diff --git a/src/main/java/bdv/viewer/overlay/SourceInfoOverlayRenderer.java b/src/main/java/bdv/viewer/overlay/SourceInfoOverlayRenderer.java index 6d9dc682..c07007fc 100644 --- a/src/main/java/bdv/viewer/overlay/SourceInfoOverlayRenderer.java +++ b/src/main/java/bdv/viewer/overlay/SourceInfoOverlayRenderer.java @@ -83,7 +83,19 @@ public synchronized void paint( final Graphics2D g ) } } - public synchronized void setTimePointsOrdered( final List< TimePoint > timePointsOrdered ) + public String getSourceName() { + return sourceName; + } + + public String getGroupName() { + return groupName; + } + + public String getTimepointString() { + return timepointString; + } + + public synchronized void setTimePointsOrdered(final List< TimePoint > timePointsOrdered ) { this.timePointsOrdered = timePointsOrdered; } From 165c4f841020ce9f9780cbde312d01c42a91516e Mon Sep 17 00:00:00 2001 From: Eric Trautman Date: Wed, 25 Oct 2023 17:09:52 -0400 Subject: [PATCH 04/13] Refactor source info toolbar and location card setup into a single separate manager class. Setup independent mouse listeners instead of hacking onto viewerPanel.drawOverlays method. Make mouse position visible in toolbar only when mouse is inside viewer. --- src/main/java/bdv/ui/BdvDefaultCards.java | 20 -- .../java/bdv/ui/splitpanel/SplitPanel.java | 21 -- src/main/java/bdv/viewer/ViewerFrame.java | 6 + src/main/java/bdv/viewer/ViewerPanel.java | 51 +---- .../viewer/location/SourceInfoToolBar.java | 13 +- ...urceInfoToolbarAndLocationCardManager.java | 184 ++++++++++++++++++ 6 files changed, 206 insertions(+), 89 deletions(-) create mode 100644 src/main/java/bdv/viewer/location/SourceInfoToolbarAndLocationCardManager.java diff --git a/src/main/java/bdv/ui/BdvDefaultCards.java b/src/main/java/bdv/ui/BdvDefaultCards.java index b413f71e..ec643ee5 100644 --- a/src/main/java/bdv/ui/BdvDefaultCards.java +++ b/src/main/java/bdv/ui/BdvDefaultCards.java @@ -34,11 +34,7 @@ import bdv.ui.viewermodepanel.DisplaySettingsPanel; import bdv.viewer.AbstractViewerPanel; import bdv.viewer.ConverterSetups; -import bdv.viewer.ViewerPanel; import bdv.viewer.ViewerState; -import bdv.viewer.location.DimensionCoordinateComponents; -import bdv.viewer.location.LocationPanel; -import net.imglib2.Interval; import java.awt.BorderLayout; import java.awt.Component; @@ -108,22 +104,6 @@ public static void setup( final CardPanel cards, final AbstractViewerPanel viewe cards.addCard( DEFAULT_VIEWERMODES_CARD, "Display Modes", new DisplaySettingsPanel( viewer.state() ), true, new Insets( 0, 4, 4, 0 ) ); cards.addCard( DEFAULT_SOURCES_CARD, "Sources", tablePanel, true, new Insets( 0, 0, 0, 0 ) ); cards.addCard( DEFAULT_SOURCEGROUPS_CARD, "Groups", treePanel, true, new Insets( 0, 0, 0, 0 ) ); - - // create card location panel and connect it to location toolbar in viewer - if (viewer instanceof ViewerPanel) { - final ViewerPanel viewerPanel = (ViewerPanel) viewer; - final Interval interval = viewerPanel.state().getCurrentSource().getSpimSource().getSource(0, 0); - final LocationPanel locationPanel = new LocationPanel(interval); - cards.addCard(DEFAULT_LOCATIONS_CARD, "Locations", locationPanel, false, new Insets(0, 4, 0, 0)); - - locationPanel.setDimensionValueChangeListener(e -> { - final DimensionCoordinateComponents coordinateComponents = (DimensionCoordinateComponents) e.getSource(); - viewerPanel.centerViewAt(coordinateComponents.getPosition(), coordinateComponents.getDimension()); - }); - - viewerPanel.setLocationPanel(locationPanel); - } - } static class MyScrollPane extends JScrollPane diff --git a/src/main/java/bdv/ui/splitpanel/SplitPanel.java b/src/main/java/bdv/ui/splitpanel/SplitPanel.java index 412cc28e..80754e76 100644 --- a/src/main/java/bdv/ui/splitpanel/SplitPanel.java +++ b/src/main/java/bdv/ui/splitpanel/SplitPanel.java @@ -46,11 +46,6 @@ import bdv.ui.CardPanel; import bdv.ui.UIUtils; import bdv.viewer.AbstractViewerPanel; -import bdv.viewer.ViewerPanel; -import bdv.viewer.location.LocationPanel; -import bdv.viewer.location.SourceInfoToolBar; - -import static bdv.ui.BdvDefaultCards.*; /** * A {@code JSplitPane} with a {@code ViewerPanel} on the left and a @@ -137,22 +132,6 @@ public void componentResized( final ComponentEvent e ) width = w; } } ); - - // add hook to expand card panel and locations card when edit button in source info toolbar is clicked - if (viewerPanel instanceof ViewerPanel) { - final ViewerPanel viewer = (ViewerPanel) viewerPanel; - final LocationPanel locationPanel = viewer.getLocationPanel(); - final SourceInfoToolBar sourceInfoToolBar = viewer.getSourceInfoToolBar(); - sourceInfoToolBar.setEditActionListener(e -> { - // expand card panel and location card - this.setCollapsed(false); - cardPanel.setCardExpanded(DEFAULT_SOURCES_CARD, false); - cardPanel.setCardExpanded(DEFAULT_SOURCEGROUPS_CARD, false); - cardPanel.setCardExpanded(DEFAULT_LOCATIONS_CARD, true); - locationPanel.requestFocusOnFirstComponent(); - }); - } - } private void configureSplitPane() diff --git a/src/main/java/bdv/viewer/ViewerFrame.java b/src/main/java/bdv/viewer/ViewerFrame.java index 38b41b71..870c1a1f 100644 --- a/src/main/java/bdv/viewer/ViewerFrame.java +++ b/src/main/java/bdv/viewer/ViewerFrame.java @@ -52,6 +52,7 @@ import bdv.ui.CardPanel; import bdv.ui.splitpanel.SplitPanel; import bdv.util.AWTUtils; +import bdv.viewer.location.SourceInfoToolbarAndLocationCardManager; /** * A {@link JFrame} containing a {@link ViewerPanel} and associated @@ -123,6 +124,11 @@ public ViewerFrame( BdvDefaultCards.setup( cards, viewer, setups ); splitPanel = new SplitPanel( viewer, cards ); + final SourceInfoToolbarAndLocationCardManager manager = + new SourceInfoToolbarAndLocationCardManager(appearanceManager, viewer); + manager.addToolbarToViewerFrame(this); + manager.addLocationCardToSplitPanel(splitPanel, cards); + getRootPane().setDoubleBuffered( true ); // add( viewer, BorderLayout.CENTER ); add( splitPanel, BorderLayout.CENTER ); diff --git a/src/main/java/bdv/viewer/ViewerPanel.java b/src/main/java/bdv/viewer/ViewerPanel.java index df3a11d7..ae352807 100644 --- a/src/main/java/bdv/viewer/ViewerPanel.java +++ b/src/main/java/bdv/viewer/ViewerPanel.java @@ -65,8 +65,6 @@ import bdv.viewer.animate.OverlayAnimator; import bdv.viewer.animate.TextOverlayAnimator; import bdv.viewer.animate.TextOverlayAnimator.TextPosition; -import bdv.viewer.location.LocationPanel; -import bdv.viewer.location.SourceInfoToolBar; import bdv.viewer.overlay.MultiBoxOverlayRenderer; import bdv.viewer.overlay.ScaleBarOverlayRenderer; import bdv.viewer.overlay.SourceInfoOverlayRenderer; @@ -136,9 +134,6 @@ public class ViewerPanel extends AbstractViewerPanel implements OverlayRenderer, */ private final ScaleBarOverlayRenderer scaleBarOverlayRenderer; - private final SourceInfoToolBar sourceInfoToolBar; - private LocationPanel locationPanel; - private final TransformEventHandler transformEventHandler; /** @@ -265,10 +260,6 @@ public ViewerPanel( final List< SourceAndConverter< ? > > sources, final int num display.addHandler( mouseCoordinates ); - // add source info toolbar to viewer - will be visible/hidden depending on Prefs.showSourceInfoToolBar() - sourceInfoToolBar = new SourceInfoToolBar(); - add(sourceInfoToolBar, BorderLayout.NORTH); - sliderTime = new JSlider( SwingConstants.HORIZONTAL, 0, numTimepoints - 1, 0 ); sliderTime.addChangeListener( e -> { if ( !blockSliderTimeEvents ) @@ -307,18 +298,6 @@ public void componentResized( final ComponentEvent e ) painterThread.start(); } - public SourceInfoToolBar getSourceInfoToolBar() { - return sourceInfoToolBar; - } - - public LocationPanel getLocationPanel() { - return locationPanel; - } - - public void setLocationPanel(final LocationPanel locationPanel) { - this.locationPanel = locationPanel; - } - /** * Initialize ViewerState with the given {@code sources} and {@code numTimepoints}. * Set up {@code numGroups} SourceGroups named "group 1", "group 2", etc. Add the @@ -525,7 +504,7 @@ public void requestRepaint( final Interval screenInterval ) @Override protected void onMouseMoved() { - if ( Prefs.showTextOverlay()|| Prefs.showSourceInfoToolBar() ) + if ( Prefs.showTextOverlay() ) // trigger repaint for showing updated mouse coordinates getDisplayComponent().repaint(); } @@ -542,35 +521,15 @@ public void drawOverlays( final Graphics g ) requiresRepaint = multiBoxOverlayRenderer.isHighlightInProgress(); } - // always set sourceInfoOverlayRenderer state, even if not visible, in case it is used for source info toolbar - sourceInfoOverlayRenderer.setViewerState(state); - - final boolean showSourceInfoToolBar = Prefs.showSourceInfoToolBar(); - sourceInfoToolBar.setVisible(showSourceInfoToolBar); - - final double[] gMousePos = new double[3]; - getGlobalMouseCoordinates(RealPoint.wrap(gMousePos)); - - if (showSourceInfoToolBar || (locationPanel != null)) { - final double[] gCenterPos = new double[3]; - state().getViewerTransform().applyInverse(gCenterPos, getDisplayCenterCoordinates()); - if (showSourceInfoToolBar) { - sourceInfoToolBar.setSourceNamesAndTimepoint(sourceInfoOverlayRenderer.getSourceName(), - sourceInfoOverlayRenderer.getGroupName(), - sourceInfoOverlayRenderer.getTimepointString()); - sourceInfoToolBar.setPositions(gCenterPos, gMousePos); - } - if (locationPanel != null) { - locationPanel.setCenterPosition(gCenterPos); - } - } - - if ( Prefs.showTextOverlay() && (! showSourceInfoToolBar) ) + if ( Prefs.showTextOverlay() && (! Prefs.showSourceInfoToolBar()) ) { + sourceInfoOverlayRenderer.setViewerState(state); final Font font = UIUtils.getFont( "monospaced.small.font" ); sourceInfoOverlayRenderer.setSourceNameOverlayPosition( Prefs.sourceNameOverlayPosition() ); sourceInfoOverlayRenderer.paint( ( Graphics2D ) g ); + final double[] gMousePos = new double[3]; + getGlobalMouseCoordinates(RealPoint.wrap(gMousePos)); final String mousePosGlobalString = options.is2D() ? String.format(Locale.ROOT, "%6.1f, %6.1f", gMousePos[0], gMousePos[1]) : diff --git a/src/main/java/bdv/viewer/location/SourceInfoToolBar.java b/src/main/java/bdv/viewer/location/SourceInfoToolBar.java index 038ba34e..7235c97d 100644 --- a/src/main/java/bdv/viewer/location/SourceInfoToolBar.java +++ b/src/main/java/bdv/viewer/location/SourceInfoToolBar.java @@ -62,6 +62,7 @@ public SourceInfoToolBar() { this.mouseCoordinatesLabel = new JLabel(); this.mouseCoordinatesLabel.setToolTipText("Mouse coordinates"); this.mouseCoordinatesLabel.setForeground(Color.MAGENTA); + this.mouseCoordinatesLabel.setVisible(false); flowPanel.add(this.sourceNameLabel); flowPanel.add(this.groupNameLabel); @@ -106,14 +107,22 @@ public void setEditActionListener(final ActionListener editCoordinatesActionList } } - public void setPositions(final double[] centerPosition, - final double[] mousePosition) { + public void setCenterPosition(final double[] centerPosition) { for (int i = 0; i < centerPosition.length; i++) { this.centerCoordinatesLabel.setText(formatPosition(centerPosition)); + } + } + + public void setMousePosition(final double[] mousePosition) { + for (int i = 0; i < mousePosition.length; i++) { this.mouseCoordinatesLabel.setText(formatPosition(mousePosition)); } } + public void setMouseCoordinatesVisible(final boolean visible) { + this.mouseCoordinatesLabel.setVisible(visible); + } + private String formatPosition(final double[] position) { final StringBuilder sb = new StringBuilder(); for (int i = 0; i < position.length; i++) { diff --git a/src/main/java/bdv/viewer/location/SourceInfoToolbarAndLocationCardManager.java b/src/main/java/bdv/viewer/location/SourceInfoToolbarAndLocationCardManager.java new file mode 100644 index 00000000..0f93783a --- /dev/null +++ b/src/main/java/bdv/viewer/location/SourceInfoToolbarAndLocationCardManager.java @@ -0,0 +1,184 @@ +/* + * #%L + * BigDataViewer core classes with minimal dependencies. + * %% + * Copyright (C) 2012 - 2023 BigDataViewer developers. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package bdv.viewer.location; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Insets; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +import org.scijava.listeners.Listeners; + +import bdv.ui.BdvDefaultCards; +import bdv.ui.CardPanel; +import bdv.ui.appearance.Appearance; +import bdv.ui.appearance.AppearanceManager; +import bdv.ui.splitpanel.SplitPanel; +import bdv.viewer.ViewerFrame; +import bdv.viewer.ViewerPanel; +import bdv.viewer.overlay.SourceInfoOverlayRenderer; +import net.imglib2.Interval; +import net.imglib2.RealPoint; + +/** + * Helper class to set up the highly-coupled SourceInfoToolBar and LocationPanel UI components. + * + * @author Eric Trautman + */ +public class SourceInfoToolbarAndLocationCardManager { + private final AppearanceManager appearanceManager; + private final ViewerPanel viewer; + private final SourceInfoToolBar sourceInfoToolBar; + private final LocationPanel locationPanel; + + public SourceInfoToolbarAndLocationCardManager(final AppearanceManager appearanceManager, + final ViewerPanel viewer) { + this.appearanceManager = appearanceManager; + this.viewer = viewer; + + this.sourceInfoToolBar = new SourceInfoToolBar(); + sourceInfoToolBar.setVisible(showSourceInfoToolBar()); + + final Listeners updateListeners = appearanceManager.appearance().updateListeners(); + updateListeners.add(() -> sourceInfoToolBar.setVisible(showSourceInfoToolBar())); + + // create card locationPanel and connect it to sourceInfoToolBar + final Interval interval = viewer.state().getCurrentSource().getSpimSource().getSource(0, 0); + this.locationPanel = new LocationPanel(interval); + locationPanel.setDimensionValueChangeListener(e -> { + final DimensionCoordinateComponents coordinateComponents = (DimensionCoordinateComponents) e.getSource(); + viewer.centerViewAt(coordinateComponents.getPosition(), + coordinateComponents.getDimension()); + final double[] gCenterPos = new double[3]; + viewer.state().getViewerTransform().applyInverse(gCenterPos, viewer.getDisplayCenterCoordinates()); + sourceInfoToolBar.setCenterPosition(gCenterPos); + sourceInfoToolBar.revalidate(); + }); + + addViewerMouseListeners(); + + // populate everything with starting location info + updateSourceInfo(); + updateCenterPosition(); + updateMousePosition(); + } + + public void addToolbarToViewerFrame(final ViewerFrame viewerFrame) { + viewerFrame.add(sourceInfoToolBar, BorderLayout.NORTH); + } + + public void addLocationCardToSplitPanel(final SplitPanel splitPanel, + final CardPanel cards) { + + cards.addCard(BdvDefaultCards.DEFAULT_LOCATIONS_CARD, + "Locations", + locationPanel, + false, + new Insets(0, 4, 0, 0)); + + // add hook to expand card panel and locations card when edit button in source info toolbar is clicked + sourceInfoToolBar.setEditActionListener(e -> { + // expand card panel and location card + splitPanel.setCollapsed(false); + cards.setCardExpanded(BdvDefaultCards.DEFAULT_SOURCES_CARD, false); + cards.setCardExpanded(BdvDefaultCards.DEFAULT_SOURCEGROUPS_CARD, false); + cards.setCardExpanded(BdvDefaultCards.DEFAULT_LOCATIONS_CARD, true); + locationPanel.requestFocusOnFirstComponent(); + }); + } + + private boolean showSourceInfoToolBar() { + return appearanceManager.appearance().showSourceInfoToolBar(); + } + + private void updateSourceInfo() { + if (showSourceInfoToolBar()) { + final SourceInfoOverlayRenderer sourceInfoOverlayRenderer = viewer.getSourceInfoOverlayRenderer(); + sourceInfoOverlayRenderer.setViewerState(viewer.state()); + sourceInfoToolBar.setSourceNamesAndTimepoint(sourceInfoOverlayRenderer.getSourceName(), + sourceInfoOverlayRenderer.getGroupName(), + sourceInfoOverlayRenderer.getTimepointString()); + } + } + + private void updateCenterPosition() { + final double[] gCenterPos = new double[3]; + viewer.state().getViewerTransform().applyInverse(gCenterPos, viewer.getDisplayCenterCoordinates()); + locationPanel.setCenterPosition(gCenterPos); + if (showSourceInfoToolBar()) { + sourceInfoToolBar.setCenterPosition(gCenterPos); + } + } + + private void updateMousePosition() { + if (showSourceInfoToolBar()) { + final double[] gMousePos = new double[3]; + viewer.getGlobalMouseCoordinates(RealPoint.wrap(gMousePos)); + sourceInfoToolBar.setMousePosition(gMousePos); + } + } + + private void addViewerMouseListeners() { + + final Component viewerDisplay = viewer.getDisplayComponent(); + + viewerDisplay.addMouseMotionListener(new MouseAdapter() { + @Override + public void mouseMoved(final MouseEvent e) { + updateMousePosition(); + } + @Override + public void mouseDragged(final MouseEvent e) { + updateMousePosition(); + } + }); + + viewerDisplay.addMouseWheelListener(e -> { + updateCenterPosition(); + updateMousePosition(); + }); + + viewerDisplay.addMouseListener(new MouseAdapter() { + @Override + public void mouseEntered(final MouseEvent e) { + sourceInfoToolBar.setMouseCoordinatesVisible(true); + } + @Override + public void mouseExited(final MouseEvent e) { + sourceInfoToolBar.setMouseCoordinatesVisible(false); + } + @Override + public void mouseReleased(final MouseEvent e) { + updateCenterPosition(); + updateMousePosition(); + } + }); + } +} From bb1f4e25a3da96861dbb009e118ca69b498b502c Mon Sep 17 00:00:00 2001 From: Eric Trautman Date: Wed, 25 Oct 2023 18:08:46 -0400 Subject: [PATCH 05/13] update source info toolbar and location card sliders when source changes --- .../DimensionCoordinateComponents.java | 23 +++++---- .../bdv/viewer/location/LocationPanel.java | 7 +++ ...urceInfoToolbarAndLocationCardManager.java | 47 +++++++++++++++++-- .../bdv/viewer/location/GridViewerTest.java | 37 ++++++++------- 4 files changed, 85 insertions(+), 29 deletions(-) diff --git a/src/main/java/bdv/viewer/location/DimensionCoordinateComponents.java b/src/main/java/bdv/viewer/location/DimensionCoordinateComponents.java index 82f67cbe..91f1bd36 100644 --- a/src/main/java/bdv/viewer/location/DimensionCoordinateComponents.java +++ b/src/main/java/bdv/viewer/location/DimensionCoordinateComponents.java @@ -27,8 +27,8 @@ public class DimensionCoordinateComponents { public static final String DEFAULT_VALUE_FORMAT = "%1.0f"; private final int dimension; - private final double minPosition; - private final double maxPosition; + private double minPosition; + private double maxPosition; private final JLabel valueLabel; private final JTextField valueTextField; private final JSlider valueSlider; @@ -60,8 +60,6 @@ public DimensionCoordinateComponents(final String name, this.dimension = dimension; this.dimensionValueChangeListener = null; - this.minPosition = minPosition; - this.maxPosition = maxPosition; this.valueFormat = valueFormat; this.position = 0.0; @@ -77,13 +75,8 @@ public DimensionCoordinateComponents(final String name, this.valueSlider = new JSlider(); this.valueSlider.setMajorTickSpacing(25); this.valueSlider.setPaintTicks(true); - - final Hashtable labelTable = new Hashtable<>(); - labelTable.put(0, new JLabel(formatSliderValue(minPosition))); - labelTable.put(100, new JLabel(formatSliderValue(maxPosition))); - this.valueSlider.setLabelTable(labelTable); - this.valueSlider.setPaintLabels(true); + this.setMinAndMaxPosition(minPosition, maxPosition); this.sliderChangeListener = e -> { setPosition(getSliderPosition()); @@ -122,6 +115,16 @@ public JSlider getValueSlider() { return valueSlider; } + public void setMinAndMaxPosition(final double minPosition, + final double maxPosition) { + this.minPosition = minPosition; + this.maxPosition = maxPosition; + final Hashtable labelTable = new Hashtable<>(); + labelTable.put(0, new JLabel(formatSliderValue(minPosition))); + labelTable.put(100, new JLabel(formatSliderValue(maxPosition))); + this.valueSlider.setLabelTable(labelTable); + } + public void setNextDimensionComponents(final DimensionCoordinateComponents nextDimensionComponents) { this.nextDimensionComponents = nextDimensionComponents; } diff --git a/src/main/java/bdv/viewer/location/LocationPanel.java b/src/main/java/bdv/viewer/location/LocationPanel.java index 11508bc3..0b070323 100644 --- a/src/main/java/bdv/viewer/location/LocationPanel.java +++ b/src/main/java/bdv/viewer/location/LocationPanel.java @@ -78,6 +78,13 @@ public void setDimensionValueChangeListener(final ChangeListener changeListener) } } + public void setSourceInterval(final Interval sourceInterval) { + for (int dimension = 0; dimension < sourceInterval.numDimensions(); dimension++) { + this.dimensionComponentsList.get(dimension).setMinAndMaxPosition(sourceInterval.min(dimension), + sourceInterval.max(dimension)); + } + } + public void requestFocusOnFirstComponent() { if (! dimensionComponentsList.isEmpty()) { this.dimensionComponentsList.get(0).getValueTextField().requestFocus(); diff --git a/src/main/java/bdv/viewer/location/SourceInfoToolbarAndLocationCardManager.java b/src/main/java/bdv/viewer/location/SourceInfoToolbarAndLocationCardManager.java index 0f93783a..4345be1f 100644 --- a/src/main/java/bdv/viewer/location/SourceInfoToolbarAndLocationCardManager.java +++ b/src/main/java/bdv/viewer/location/SourceInfoToolbarAndLocationCardManager.java @@ -41,18 +41,23 @@ import bdv.ui.appearance.Appearance; import bdv.ui.appearance.AppearanceManager; import bdv.ui.splitpanel.SplitPanel; +import bdv.viewer.Source; +import bdv.viewer.SourceAndConverter; import bdv.viewer.ViewerFrame; import bdv.viewer.ViewerPanel; +import bdv.viewer.ViewerStateChange; +import bdv.viewer.ViewerStateChangeListener; import bdv.viewer.overlay.SourceInfoOverlayRenderer; import net.imglib2.Interval; import net.imglib2.RealPoint; +import net.imglib2.util.Intervals; /** * Helper class to set up the highly-coupled SourceInfoToolBar and LocationPanel UI components. * * @author Eric Trautman */ -public class SourceInfoToolbarAndLocationCardManager { +public class SourceInfoToolbarAndLocationCardManager implements ViewerStateChangeListener { private final AppearanceManager appearanceManager; private final ViewerPanel viewer; private final SourceInfoToolBar sourceInfoToolBar; @@ -70,8 +75,7 @@ public SourceInfoToolbarAndLocationCardManager(final AppearanceManager appearanc updateListeners.add(() -> sourceInfoToolBar.setVisible(showSourceInfoToolBar())); // create card locationPanel and connect it to sourceInfoToolBar - final Interval interval = viewer.state().getCurrentSource().getSpimSource().getSource(0, 0); - this.locationPanel = new LocationPanel(interval); + this.locationPanel = new LocationPanel(getCurrentSourceInterval()); locationPanel.setDimensionValueChangeListener(e -> { final DimensionCoordinateComponents coordinateComponents = (DimensionCoordinateComponents) e.getSource(); viewer.centerViewAt(coordinateComponents.getPosition(), @@ -82,6 +86,10 @@ public SourceInfoToolbarAndLocationCardManager(final AppearanceManager appearanc sourceInfoToolBar.revalidate(); }); + // register for (source) state change updates + viewer.state().changeListeners().add(this); + + // register for mouse events addViewerMouseListeners(); // populate everything with starting location info @@ -114,6 +122,39 @@ public void addLocationCardToSplitPanel(final SplitPanel splitPanel, }); } + @Override + public void viewerStateChanged(final ViewerStateChange change) { + switch (change) { + case CURRENT_SOURCE_CHANGED: + case GROUP_NAME_CHANGED: + case CURRENT_GROUP_CHANGED: + case CURRENT_TIMEPOINT_CHANGED: + case NUM_TIMEPOINTS_CHANGED: + updateSourceInfo(); + locationPanel.setSourceInterval(getCurrentSourceInterval()); + break; + case VIEWER_TRANSFORM_CHANGED: + updateCenterPosition(); + updateMousePosition(); + } + } + + private Interval getCurrentSourceInterval() { + Interval interval = null; + final SourceAndConverter currentSource = viewer.state().getCurrentSource(); + if (currentSource != null) { + final Source spimSource = currentSource.getSpimSource(); + if (spimSource != null) { + final int timePoint = viewer.state().getCurrentTimepoint(); + interval = spimSource.getSource(timePoint, 0); + } + } + if (interval == null) { + interval = Intervals.createMinMax(0, 0, 0, 0, 0, 0); + } + return interval; + } + private boolean showSourceInfoToolBar() { return appearanceManager.appearance().showSourceInfoToolBar(); } diff --git a/src/test/java/bdv/viewer/location/GridViewerTest.java b/src/test/java/bdv/viewer/location/GridViewerTest.java index ada955b8..edfb18a3 100644 --- a/src/test/java/bdv/viewer/location/GridViewerTest.java +++ b/src/test/java/bdv/viewer/location/GridViewerTest.java @@ -58,12 +58,17 @@ public static void main(String[] args) final ProgressWriterConsole progressWriter = new ProgressWriterConsole(); final ViewerOptions viewerOptions = ViewerOptions.options(); - final RealARGBColorConverter converter = RealARGBColorConverter.create(new IntType(), 0, 127); - final ConverterSetup converterSetup = new RealARGBColorConverterSetup(0, converter); - converterSetups.add(converterSetup); + for (int i = 0; i < 3; i++) { + final RealARGBColorConverter converter = RealARGBColorConverter.create(new IntType(), 0, 127); + final ConverterSetup converterSetup = new RealARGBColorConverterSetup(i, converter); + converterSetups.add(converterSetup); - final SourceAndConverter soc = new SourceAndConverter<>(GRID_SOURCE, converter); - sources.add(soc); + final String suffix = String.valueOf((char)('A' + i)); + final int size = 100 * (i + 1); + final RealRandomAccessibleSource source = buildGridSource("Grid " + suffix, size); + final SourceAndConverter soc = new SourceAndConverter<>(source, converter); + sources.add(soc); + } BigDataViewer.open(converterSetups, sources, @@ -94,15 +99,15 @@ public static void main(String[] args) }, IntType::new); - public static final RealRandomAccessibleSource GRID_SOURCE = - new RealRandomAccessibleSource(GRID, new IntType(), "Grid" ) { - private final int s = 100; - private final Interval interval = Intervals.createMinMax(-s, -s, -s, s, s, s); - @Override - public Interval getInterval(final int t, - final int level) { - return interval; - } - }; - + public static RealRandomAccessibleSource buildGridSource(final String name, + final int size) { + return new RealRandomAccessibleSource(GRID, new IntType(), name) { + private final Interval interval = Intervals.createMinMax(-size, -size, -size, size, size, size); + @Override + public Interval getInterval(final int t, + final int level) { + return interval; + } + }; + } } From 994259b29c63729dad6c2b2c612471501b81b584 Mon Sep 17 00:00:00 2001 From: Eric Trautman Date: Wed, 25 Oct 2023 19:42:14 -0400 Subject: [PATCH 06/13] move dimension slider under textfield and remove focus traversal hack --- .../DimensionCoordinateComponents.java | 54 ------------------- .../bdv/viewer/location/LocationPanel.java | 14 +++-- ...urceInfoToolbarAndLocationCardManager.java | 2 +- 3 files changed, 7 insertions(+), 63 deletions(-) diff --git a/src/main/java/bdv/viewer/location/DimensionCoordinateComponents.java b/src/main/java/bdv/viewer/location/DimensionCoordinateComponents.java index 91f1bd36..b1749ec2 100644 --- a/src/main/java/bdv/viewer/location/DimensionCoordinateComponents.java +++ b/src/main/java/bdv/viewer/location/DimensionCoordinateComponents.java @@ -1,9 +1,6 @@ package bdv.viewer.location; -import java.awt.Component; -import java.awt.Container; import java.awt.Toolkit; -import java.util.ArrayList; import java.util.Arrays; import java.util.Hashtable; import java.util.List; @@ -219,55 +216,4 @@ public String formatSliderValue(final double value) { public static String getDefaultDimensionName(final int dimension) { return (dimension < DEFAULT_NAMES.length) ? DEFAULT_NAMES[dimension] : "d" + dimension; } - - /** - * Traversal policy that forces text area to get focus before slider. - * Seems like this should not be needed, but I wasn't able to get it to work without it. - * Adapted from - * - * FocusTraversalDemo.java - * - */ - public static class FocusTraversalPolicy - extends java.awt.FocusTraversalPolicy - { - List orderedComponents; - - public FocusTraversalPolicy(List coordinateComponentsList) { - this.orderedComponents = new ArrayList<>(); - for (final DimensionCoordinateComponents coordinateComponents : coordinateComponentsList) { - this.orderedComponents.add(coordinateComponents.valueTextField); - this.orderedComponents.add(coordinateComponents.valueSlider); - } - } - public Component getComponentAfter(Container focusCycleRoot, - Component aComponent) - { - final int idx = (orderedComponents.indexOf(aComponent) + 1) % orderedComponents.size(); - return orderedComponents.get(idx); - } - - public Component getComponentBefore(Container focusCycleRoot, - Component aComponent) - { - int idx = orderedComponents.indexOf(aComponent) - 1; - if (idx < 0) { - idx = orderedComponents.size() - 1; - } - return orderedComponents.get(idx); - } - - public Component getDefaultComponent(Container focusCycleRoot) { - return orderedComponents.get(0); - } - - public Component getLastComponent(Container focusCycleRoot) { - return orderedComponents.get(orderedComponents.size() - 1); - } - - public Component getFirstComponent(Container focusCycleRoot) { - return orderedComponents.get(0); - } - } - } diff --git a/src/main/java/bdv/viewer/location/LocationPanel.java b/src/main/java/bdv/viewer/location/LocationPanel.java index 0b070323..5019fff3 100644 --- a/src/main/java/bdv/viewer/location/LocationPanel.java +++ b/src/main/java/bdv/viewer/location/LocationPanel.java @@ -36,18 +36,20 @@ public LocationPanel(final Interval sourceInterval) { this.dimensionComponentsList.add(coordinateComponents); final GridBagConstraints c = new GridBagConstraints(); - c.gridx = GridBagConstraints.RELATIVE; - c.gridy = dimension; + c.gridx = 0; + c.gridy = dimension * 2; c.weightx = 0.01; this.add(coordinateComponents.getValueLabel(), c); // text field should grow with card width + c.gridx = 1; c.weightx = 0.99; c.fill = GridBagConstraints.HORIZONTAL; this.add(coordinateComponents.getValueTextField(), c); - c.weightx = 0.01; - c.fill = GridBagConstraints.NONE; + c.gridy++; + c.weightx = 0.99; + c.fill = GridBagConstraints.HORIZONTAL; this.add(coordinateComponents.getValueSlider(), c); } @@ -56,10 +58,6 @@ public LocationPanel(final Interval sourceInterval) { final DimensionCoordinateComponents prior = dimensionComponentsList.get(d - 1); prior.setNextDimensionComponents(dimensionComponentsList.get(d)); } - - // make text area get focus before slider, not sure why I had to go to all of this trouble to make focus work - this.setFocusCycleRoot(true); - this.setFocusTraversalPolicy(new DimensionCoordinateComponents.FocusTraversalPolicy(dimensionComponentsList)); } /** diff --git a/src/main/java/bdv/viewer/location/SourceInfoToolbarAndLocationCardManager.java b/src/main/java/bdv/viewer/location/SourceInfoToolbarAndLocationCardManager.java index 4345be1f..1b77c0b4 100644 --- a/src/main/java/bdv/viewer/location/SourceInfoToolbarAndLocationCardManager.java +++ b/src/main/java/bdv/viewer/location/SourceInfoToolbarAndLocationCardManager.java @@ -109,7 +109,7 @@ public void addLocationCardToSplitPanel(final SplitPanel splitPanel, "Locations", locationPanel, false, - new Insets(0, 4, 0, 0)); + new Insets(10, 4, 0, 10)); // add hook to expand card panel and locations card when edit button in source info toolbar is clicked sourceInfoToolBar.setEditActionListener(e -> { From ddba977129efdb80fb081c46d134c4133b8a2847 Mon Sep 17 00:00:00 2001 From: Eric Trautman Date: Thu, 26 Oct 2023 10:48:05 -0400 Subject: [PATCH 07/13] start improving SourceInfoToolBar coordinate formatting, support sources with different numbers of dimensions (not sure if that is allowed) --- .../bdv/viewer/location/LocationPanel.java | 90 +++++++++++-------- .../viewer/location/SourceInfoToolBar.java | 72 ++++++++------- ...urceInfoToolbarAndLocationCardManager.java | 6 +- .../bdv/viewer/location/GridViewerTest.java | 67 ++++++++------ 4 files changed, 141 insertions(+), 94 deletions(-) diff --git a/src/main/java/bdv/viewer/location/LocationPanel.java b/src/main/java/bdv/viewer/location/LocationPanel.java index 5019fff3..62601ddc 100644 --- a/src/main/java/bdv/viewer/location/LocationPanel.java +++ b/src/main/java/bdv/viewer/location/LocationPanel.java @@ -25,38 +25,10 @@ public LocationPanel(final Interval sourceInterval) { super(new GridBagLayout()); this.dimensionComponentsList = new ArrayList<>(); - for (int dimension = 0; dimension < sourceInterval.numDimensions(); dimension++) { - - final DimensionCoordinateComponents coordinateComponents = - new DimensionCoordinateComponents(dimension, - sourceInterval.min(dimension), - sourceInterval.max(dimension)); - - this.dimensionComponentsList.add(coordinateComponents); - - final GridBagConstraints c = new GridBagConstraints(); - c.gridx = 0; - c.gridy = dimension * 2; - c.weightx = 0.01; - this.add(coordinateComponents.getValueLabel(), c); - - // text field should grow with card width - c.gridx = 1; - c.weightx = 0.99; - c.fill = GridBagConstraints.HORIZONTAL; - this.add(coordinateComponents.getValueTextField(), c); - - c.gridy++; - c.weightx = 0.99; - c.fill = GridBagConstraints.HORIZONTAL; - this.add(coordinateComponents.getValueSlider(), c); - } - - // connect dimension components to support multi-value paste - for (int d = 1; d < dimensionComponentsList.size(); d++) { - final DimensionCoordinateComponents prior = dimensionComponentsList.get(d - 1); - prior.setNextDimensionComponents(dimensionComponentsList.get(d)); + addDimension(dimension, + sourceInterval.min(dimension), + sourceInterval.max(dimension)); } } @@ -77,9 +49,23 @@ public void setDimensionValueChangeListener(final ChangeListener changeListener) } public void setSourceInterval(final Interval sourceInterval) { + for (int dimension = 0; dimension < sourceInterval.numDimensions(); dimension++) { - this.dimensionComponentsList.get(dimension).setMinAndMaxPosition(sourceInterval.min(dimension), - sourceInterval.max(dimension)); + if (dimension < dimensionComponentsList.size()) { + dimensionComponentsList.get(dimension).setMinAndMaxPosition(sourceInterval.min(dimension), + sourceInterval.max(dimension)); + } else { + addDimension(dimension, + sourceInterval.min(dimension), + sourceInterval.max(dimension)); + } + } + + for (int dimension = dimensionComponentsList.size(); dimension > sourceInterval.numDimensions() ; dimension--) { + final DimensionCoordinateComponents removedComponents = dimensionComponentsList.remove(dimension); + this.remove(removedComponents.getValueLabel()); + this.remove(removedComponents.getValueTextField()); + this.remove(removedComponents.getValueSlider()); } } @@ -97,9 +83,43 @@ public void setNames(final String[] dimensionNames) { } public void setCenterPosition(final double[] centerPosition) { - for (int i = 0; i < centerPosition.length; i++) { - this.dimensionComponentsList.get(i).setPosition(centerPosition[i]); + for (int i = 0; i < centerPosition.length && i < dimensionComponentsList.size(); i++) { + dimensionComponentsList.get(i).setPosition(centerPosition[i]); } } + private void addDimension(final int dimension, + final double minPosition, + final double maxPosition) { + + final DimensionCoordinateComponents coordinateComponents = + new DimensionCoordinateComponents(dimension, + minPosition, + maxPosition); + + this.dimensionComponentsList.add(coordinateComponents); + + final GridBagConstraints c = new GridBagConstraints(); + c.gridx = 0; + c.gridy = dimension * 2; + c.weightx = 0.01; + this.add(coordinateComponents.getValueLabel(), c); + + // text field should grow with card width + c.gridx = 1; + c.weightx = 0.99; + c.fill = GridBagConstraints.HORIZONTAL; + this.add(coordinateComponents.getValueTextField(), c); + + c.gridy++; + c.weightx = 0.99; + c.fill = GridBagConstraints.HORIZONTAL; + this.add(coordinateComponents.getValueSlider(), c); + + if (dimension > 0) { + // connect dimension components to support multi-value paste + final DimensionCoordinateComponents prior = dimensionComponentsList.get(dimension - 1); + prior.setNextDimensionComponents(coordinateComponents); + } + } } diff --git a/src/main/java/bdv/viewer/location/SourceInfoToolBar.java b/src/main/java/bdv/viewer/location/SourceInfoToolBar.java index 7235c97d..efceef64 100644 --- a/src/main/java/bdv/viewer/location/SourceInfoToolBar.java +++ b/src/main/java/bdv/viewer/location/SourceInfoToolBar.java @@ -1,12 +1,14 @@ package bdv.viewer.location; import java.awt.Color; -import java.awt.FlowLayout; +import java.awt.Font; import java.awt.Insets; import java.awt.event.ActionListener; import java.net.URL; import java.util.Locale; +import javax.swing.Box; +import javax.swing.BoxLayout; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JLabel; @@ -20,9 +22,8 @@ */ public class SourceInfoToolBar extends JToolBar { - private final JLabel sourceNameLabel; - private final JLabel groupNameLabel; - private final JLabel centerCoordinatesLabel; + private final JLabel sourceAndGroupNameLabel; + private final JLabel centerCoordinatesLabel; private final JLabel timepointLabel; private final JButton editCoordinatesButton; private final JLabel mouseCoordinatesLabel; @@ -33,20 +34,20 @@ public SourceInfoToolBar() { super("Source Information"); this.setFloatable(false); - final JPanel flowPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 1)); - this.add(flowPanel); + final JPanel panel = new JPanel(); + panel.setLayout(new BoxLayout(panel, BoxLayout.LINE_AXIS)); + this.add(panel); - this.sourceNameLabel = new JLabel(); - this.sourceNameLabel.setToolTipText("Source name"); - - this.groupNameLabel = new JLabel(); - this.groupNameLabel.setToolTipText("Group name"); + this.sourceAndGroupNameLabel = new JLabel(); this.timepointLabel = new JLabel(); this.timepointLabel.setToolTipText("Timepoint"); + final Font monospacedFont = new Font(Font.MONOSPACED, Font.PLAIN, 12); this.centerCoordinatesLabel = new JLabel(); this.centerCoordinatesLabel.setToolTipText("Center coordinates"); + this.centerCoordinatesLabel.setHorizontalAlignment(JLabel.LEFT); + this.centerCoordinatesLabel.setFont(monospacedFont); final URL url = this.getClass().getResource("/bdv/ui/location/edit_pencil_20.png"); if (url == null) { @@ -63,34 +64,45 @@ public SourceInfoToolBar() { this.mouseCoordinatesLabel.setToolTipText("Mouse coordinates"); this.mouseCoordinatesLabel.setForeground(Color.MAGENTA); this.mouseCoordinatesLabel.setVisible(false); - - flowPanel.add(this.sourceNameLabel); - flowPanel.add(this.groupNameLabel); - flowPanel.add(this.timepointLabel); - flowPanel.add(this.centerCoordinatesLabel); - flowPanel.add(this.editCoordinatesButton); - flowPanel.add(this.mouseCoordinatesLabel); + this.mouseCoordinatesLabel.setHorizontalAlignment(JLabel.LEFT); + this.mouseCoordinatesLabel.setFont(monospacedFont); + + panel.add(this.sourceAndGroupNameLabel); + panel.add(Box.createHorizontalStrut(10)); + panel.add(this.centerCoordinatesLabel); + panel.add(Box.createHorizontalStrut(10)); + panel.add(this.editCoordinatesButton); + panel.add(Box.createHorizontalStrut(10)); + panel.add(this.mouseCoordinatesLabel); + panel.add(Box.createHorizontalGlue()); + panel.add(this.timepointLabel); } - public void setSourceNamesAndTimepoint(final String sourceName, - final String groupName, - final String timepointString) { + public void updateSource(final String sourceName, + final String groupName, + final String timepointString) { if ((sourceName != null) && (! sourceName.isEmpty())) { - this.sourceNameLabel.setText(sourceName); + String sourceAndGroupName = sourceName; if ((groupName != null) && (! groupName.isEmpty())) { - this.groupNameLabel.setText("| " + groupName); - this.groupNameLabel.setVisible(true); + sourceAndGroupName = sourceAndGroupName + " | " + groupName; + } + if (sourceAndGroupName.length() > 20) { + sourceAndGroupNameLabel.setToolTipText(sourceAndGroupName); + sourceAndGroupName = sourceAndGroupName.substring(0, 17) + "..."; } else { - this.groupNameLabel.setVisible(false); + sourceAndGroupNameLabel.setToolTipText("source and group name"); } - this.sourceNameLabel.setVisible(true); + sourceAndGroupNameLabel.setText(sourceAndGroupName); + sourceAndGroupNameLabel.setVisible(true); } else { - this.sourceNameLabel.setVisible(false); + sourceAndGroupNameLabel.setVisible(false); } + if ((timepointString != null) && (! timepointString.isEmpty())) { - this.timepointLabel.setText(timepointString); + timepointLabel.setText(timepointString); + timepointLabel.setVisible(true); } else { - this.timepointLabel.setText(""); + timepointLabel.setVisible(false); } } @@ -136,7 +148,7 @@ private String formatPosition(final double[] position) { private String formatCoordinate(final int dimension, final double value) { - return String.format(Locale.ROOT, "%s: %1.0f", + return String.format(Locale.ROOT, "%s: % 8.1f", DimensionCoordinateComponents.getDefaultDimensionName(dimension), value); } diff --git a/src/main/java/bdv/viewer/location/SourceInfoToolbarAndLocationCardManager.java b/src/main/java/bdv/viewer/location/SourceInfoToolbarAndLocationCardManager.java index 1b77c0b4..31695ec5 100644 --- a/src/main/java/bdv/viewer/location/SourceInfoToolbarAndLocationCardManager.java +++ b/src/main/java/bdv/viewer/location/SourceInfoToolbarAndLocationCardManager.java @@ -163,9 +163,9 @@ private void updateSourceInfo() { if (showSourceInfoToolBar()) { final SourceInfoOverlayRenderer sourceInfoOverlayRenderer = viewer.getSourceInfoOverlayRenderer(); sourceInfoOverlayRenderer.setViewerState(viewer.state()); - sourceInfoToolBar.setSourceNamesAndTimepoint(sourceInfoOverlayRenderer.getSourceName(), - sourceInfoOverlayRenderer.getGroupName(), - sourceInfoOverlayRenderer.getTimepointString()); + sourceInfoToolBar.updateSource(sourceInfoOverlayRenderer.getSourceName(), + sourceInfoOverlayRenderer.getGroupName(), + sourceInfoOverlayRenderer.getTimepointString()); } } diff --git a/src/test/java/bdv/viewer/location/GridViewerTest.java b/src/test/java/bdv/viewer/location/GridViewerTest.java index edfb18a3..1d46634c 100644 --- a/src/test/java/bdv/viewer/location/GridViewerTest.java +++ b/src/test/java/bdv/viewer/location/GridViewerTest.java @@ -30,6 +30,7 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.function.BiConsumer; import mpicbg.spim.data.SpimDataException; @@ -41,11 +42,12 @@ import bdv.util.RealRandomAccessibleSource; import bdv.viewer.SourceAndConverter; import bdv.viewer.ViewerOptions; +import net.imglib2.FinalInterval; import net.imglib2.Interval; +import net.imglib2.RealLocalizable; import net.imglib2.display.RealARGBColorConverter; import net.imglib2.position.FunctionRealRandomAccessible; import net.imglib2.type.numeric.integer.IntType; -import net.imglib2.util.Intervals; public class GridViewerTest { @@ -58,14 +60,20 @@ public static void main(String[] args) final ProgressWriterConsole progressWriter = new ProgressWriterConsole(); final ViewerOptions viewerOptions = ViewerOptions.options(); - for (int i = 0; i < 3; i++) { + final String[] sourceNames = { + "Grid A", + "Grid B has a name longer than 20 characters", + "Grid C", + }; + + for (int i = 0; i < sourceNames.length; i++) { final RealARGBColorConverter converter = RealARGBColorConverter.create(new IntType(), 0, 127); final ConverterSetup converterSetup = new RealARGBColorConverterSetup(i, converter); converterSetups.add(converterSetup); - final String suffix = String.valueOf((char)('A' + i)); + final int numDimensions = 3; // (i < 3) ? 3 : 2; final int size = 100 * (i + 1); - final RealRandomAccessibleSource source = buildGridSource("Grid " + suffix, size); + final RealRandomAccessibleSource source = buildGridSource(numDimensions, sourceNames[i], size); final SourceAndConverter soc = new SourceAndConverter<>(source, converter); sources.add(soc); } @@ -79,30 +87,37 @@ public static void main(String[] args) viewerOptions); } - public static final FunctionRealRandomAccessible GRID = - new FunctionRealRandomAccessible<>( - 3, - (x, y) -> { - int i = 0; - int xPos = (int) Math.round(x.getDoublePosition(0)); - int yPos = (int) Math.round(x.getDoublePosition(1)); - if ((xPos == 0) || (yPos == 0)) { - i = 120; - } else { - if (xPos % 10 == 0) { - i = 60; - } else if (yPos % 10 == 0) { - i = 30; - } - } - y.set(i); - }, - IntType::new); + public static BiConsumer GRID_FUNCTION = (pos, pixels) -> { + int i = 0; + int xPos = (int) Math.round(pos.getDoublePosition(0)); + int yPos = (int) Math.round(pos.getDoublePosition(1)); + if ((xPos == 0) || (yPos == 0)) { + i = 120; + } else { + if (xPos % 10 == 0) { + i = 60; + } else if (yPos % 10 == 0) { + i = 30; + } + } + pixels.set(i); + }; - public static RealRandomAccessibleSource buildGridSource(final String name, + public static RealRandomAccessibleSource buildGridSource(final int numDimensions, + final String name, final int size) { - return new RealRandomAccessibleSource(GRID, new IntType(), name) { - private final Interval interval = Intervals.createMinMax(-size, -size, -size, size, size, size); + final FunctionRealRandomAccessible grid = + new FunctionRealRandomAccessible<>(numDimensions, GRID_FUNCTION, IntType::new); + + final long[] min = new long[numDimensions]; + final long[] max = new long[numDimensions]; + for (int d = 0; d < numDimensions; d++) { + min[d] = -size; + max[d] = size; + } + + return new RealRandomAccessibleSource(grid, new IntType(), name) { + private final Interval interval = new FinalInterval(min, max); @Override public Interval getInterval(final int t, final int level) { From c62766feed309bd508d7eaf5b5ad2b4dc1d8add2 Mon Sep 17 00:00:00 2001 From: Eric Trautman Date: Thu, 26 Oct 2023 11:05:03 -0400 Subject: [PATCH 08/13] restore original SplitPanel since only import order was changed --- src/main/java/bdv/ui/splitpanel/SplitPanel.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/main/java/bdv/ui/splitpanel/SplitPanel.java b/src/main/java/bdv/ui/splitpanel/SplitPanel.java index 80754e76..f0698d76 100644 --- a/src/main/java/bdv/ui/splitpanel/SplitPanel.java +++ b/src/main/java/bdv/ui/splitpanel/SplitPanel.java @@ -28,10 +28,12 @@ */ package bdv.ui.splitpanel; +import bdv.ui.CardPanel; +import bdv.ui.UIUtils; +import bdv.viewer.AbstractViewerPanel; import java.awt.Dimension; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; - import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.JScrollPane; @@ -39,14 +41,9 @@ import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.border.EmptyBorder; - import org.scijava.ui.behaviour.io.InputTriggerConfig; import org.scijava.ui.behaviour.util.Actions; -import bdv.ui.CardPanel; -import bdv.ui.UIUtils; -import bdv.viewer.AbstractViewerPanel; - /** * A {@code JSplitPane} with a {@code ViewerPanel} on the left and a * {@code CardPanel} on the right. Animated arrows are added to the From 8989c9ea0af0ed06b7e1388334dff398fc9f6d9a Mon Sep 17 00:00:00 2001 From: Eric Trautman Date: Thu, 26 Oct 2023 11:07:21 -0400 Subject: [PATCH 09/13] make card name consistent --- src/main/java/bdv/ui/BdvDefaultCards.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/bdv/ui/BdvDefaultCards.java b/src/main/java/bdv/ui/BdvDefaultCards.java index ec643ee5..3bfc77de 100644 --- a/src/main/java/bdv/ui/BdvDefaultCards.java +++ b/src/main/java/bdv/ui/BdvDefaultCards.java @@ -64,7 +64,7 @@ public class BdvDefaultCards public static final String DEFAULT_VIEWERMODES_CARD = "default bdv viewer modes card"; - public static final String DEFAULT_LOCATIONS_CARD = "default bdv location card"; + public static final String DEFAULT_LOCATIONS_CARD = "default bdv locations card"; public static void setup( final CardPanel cards, final AbstractViewerPanel viewer, final ConverterSetups converterSetups ) { From af5ea154dd245795144c5451ff1c1f75df8cae55 Mon Sep 17 00:00:00 2001 From: Eric Trautman Date: Thu, 26 Oct 2023 11:13:20 -0400 Subject: [PATCH 10/13] restore original ViewerPanel formatting to simplify review --- src/main/java/bdv/viewer/ViewerPanel.java | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/main/java/bdv/viewer/ViewerPanel.java b/src/main/java/bdv/viewer/ViewerPanel.java index ae352807..a7c7badd 100644 --- a/src/main/java/bdv/viewer/ViewerPanel.java +++ b/src/main/java/bdv/viewer/ViewerPanel.java @@ -28,6 +28,10 @@ */ package bdv.viewer; +import static bdv.ui.UIUtils.TextPosition.TOP_RIGHT; +import static bdv.viewer.DisplayMode.SINGLE; +import static bdv.viewer.Interpolation.NEARESTNEIGHBOR; + import java.awt.BorderLayout; import java.awt.Component; import java.awt.Font; @@ -79,10 +83,6 @@ import net.imglib2.RealPositionable; import net.imglib2.realtransform.AffineTransform3D; -import static bdv.ui.UIUtils.TextPosition.TOP_RIGHT; -import static bdv.viewer.DisplayMode.SINGLE; -import static bdv.viewer.Interpolation.NEARESTNEIGHBOR; - /** * A JPanel for viewing multiple of {@link Source}s. The panel contains a * {@link InteractiveDisplayCanvas canvas} and a time slider (if there @@ -523,20 +523,19 @@ public void drawOverlays( final Graphics g ) if ( Prefs.showTextOverlay() && (! Prefs.showSourceInfoToolBar()) ) { - sourceInfoOverlayRenderer.setViewerState(state); + sourceInfoOverlayRenderer.setViewerState( state ); final Font font = UIUtils.getFont( "monospaced.small.font" ); sourceInfoOverlayRenderer.setSourceNameOverlayPosition( Prefs.sourceNameOverlayPosition() ); sourceInfoOverlayRenderer.paint( ( Graphics2D ) g ); - final double[] gMousePos = new double[3]; - getGlobalMouseCoordinates(RealPoint.wrap(gMousePos)); + final double[] gPos = new double[ 3 ]; + getGlobalMouseCoordinates( RealPoint.wrap( gPos ) ); final String mousePosGlobalString = options.is2D() - ? String.format(Locale.ROOT, "%6.1f, %6.1f", gMousePos[0], gMousePos[1]) - : - String.format(Locale.ROOT, "%6.1f, %6.1f, %6.1f", gMousePos[0], gMousePos[1], gMousePos[2]); + ? String.format( Locale.ROOT, "%6.1f, %6.1f", gPos[ 0 ], gPos[ 1 ] ) + : String.format( Locale.ROOT, "%6.1f, %6.1f, %6.1f", gPos[ 0 ], gPos[ 1 ], gPos[ 2 ] ); - g.setFont(font); - UIUtils.drawString(g, TOP_RIGHT, 1, mousePosGlobalString); + g.setFont( font ); + UIUtils.drawString( g, TOP_RIGHT, 1, mousePosGlobalString ); } if ( Prefs.showScaleBar() ) From 8fbdbe04fcd79274e50f307edea50a7b10bbc503 Mon Sep 17 00:00:00 2001 From: Eric Trautman Date: Thu, 26 Oct 2023 11:15:56 -0400 Subject: [PATCH 11/13] restore original ViewerPanel formatting to simplify review --- src/main/java/bdv/viewer/ViewerPanel.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/bdv/viewer/ViewerPanel.java b/src/main/java/bdv/viewer/ViewerPanel.java index a7c7badd..2eee0a1c 100644 --- a/src/main/java/bdv/viewer/ViewerPanel.java +++ b/src/main/java/bdv/viewer/ViewerPanel.java @@ -523,16 +523,16 @@ public void drawOverlays( final Graphics g ) if ( Prefs.showTextOverlay() && (! Prefs.showSourceInfoToolBar()) ) { - sourceInfoOverlayRenderer.setViewerState( state ); final Font font = UIUtils.getFont( "monospaced.small.font" ); + sourceInfoOverlayRenderer.setViewerState( state ); sourceInfoOverlayRenderer.setSourceNameOverlayPosition( Prefs.sourceNameOverlayPosition() ); sourceInfoOverlayRenderer.paint( ( Graphics2D ) g ); final double[] gPos = new double[ 3 ]; getGlobalMouseCoordinates( RealPoint.wrap( gPos ) ); final String mousePosGlobalString = options.is2D() - ? String.format( Locale.ROOT, "%6.1f, %6.1f", gPos[ 0 ], gPos[ 1 ] ) - : String.format( Locale.ROOT, "%6.1f, %6.1f, %6.1f", gPos[ 0 ], gPos[ 1 ], gPos[ 2 ] ); + ? String.format( Locale.ROOT, "%6.1f, %6.1f", gPos[ 0 ], gPos[ 1 ] ) + : String.format( Locale.ROOT, "%6.1f, %6.1f, %6.1f", gPos[ 0 ], gPos[ 1 ], gPos[ 2 ] ); g.setFont( font ); UIUtils.drawString( g, TOP_RIGHT, 1, mousePosGlobalString ); From 80194c7bb4a267c2e11368ea18c9b2647d1207d9 Mon Sep 17 00:00:00 2001 From: Eric Trautman Date: Thu, 26 Oct 2023 11:17:21 -0400 Subject: [PATCH 12/13] restore original ViewerPanel formatting to simplify review --- src/main/java/bdv/viewer/ViewerPanel.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/bdv/viewer/ViewerPanel.java b/src/main/java/bdv/viewer/ViewerPanel.java index 2eee0a1c..8892cc2b 100644 --- a/src/main/java/bdv/viewer/ViewerPanel.java +++ b/src/main/java/bdv/viewer/ViewerPanel.java @@ -531,8 +531,8 @@ public void drawOverlays( final Graphics g ) final double[] gPos = new double[ 3 ]; getGlobalMouseCoordinates( RealPoint.wrap( gPos ) ); final String mousePosGlobalString = options.is2D() - ? String.format( Locale.ROOT, "%6.1f, %6.1f", gPos[ 0 ], gPos[ 1 ] ) - : String.format( Locale.ROOT, "%6.1f, %6.1f, %6.1f", gPos[ 0 ], gPos[ 1 ], gPos[ 2 ] ); + ? String.format( Locale.ROOT, "%6.1f, %6.1f", gPos[ 0 ], gPos[ 1 ] ) + : String.format( Locale.ROOT, "%6.1f, %6.1f, %6.1f", gPos[ 0 ], gPos[ 1 ], gPos[ 2 ] ); g.setFont( font ); UIUtils.drawString( g, TOP_RIGHT, 1, mousePosGlobalString ); From e3a1e8d8661439ee6407f64da01555e33bdf92c1 Mon Sep 17 00:00:00 2001 From: Eric Trautman Date: Thu, 26 Oct 2023 11:23:40 -0400 Subject: [PATCH 13/13] add copyright to new classes --- .../DimensionCoordinateComponents.java | 28 +++++++++++++++++++ .../bdv/viewer/location/LocationPanel.java | 28 +++++++++++++++++++ .../viewer/location/SourceInfoToolBar.java | 28 +++++++++++++++++++ 3 files changed, 84 insertions(+) diff --git a/src/main/java/bdv/viewer/location/DimensionCoordinateComponents.java b/src/main/java/bdv/viewer/location/DimensionCoordinateComponents.java index b1749ec2..38b348f2 100644 --- a/src/main/java/bdv/viewer/location/DimensionCoordinateComponents.java +++ b/src/main/java/bdv/viewer/location/DimensionCoordinateComponents.java @@ -1,3 +1,31 @@ +/* + * #%L + * BigDataViewer core classes with minimal dependencies. + * %% + * Copyright (C) 2012 - 2023 BigDataViewer developers. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ package bdv.viewer.location; import java.awt.Toolkit; diff --git a/src/main/java/bdv/viewer/location/LocationPanel.java b/src/main/java/bdv/viewer/location/LocationPanel.java index 62601ddc..cd8e4bc0 100644 --- a/src/main/java/bdv/viewer/location/LocationPanel.java +++ b/src/main/java/bdv/viewer/location/LocationPanel.java @@ -1,3 +1,31 @@ +/* + * #%L + * BigDataViewer core classes with minimal dependencies. + * %% + * Copyright (C) 2012 - 2023 BigDataViewer developers. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ package bdv.viewer.location; import java.awt.GridBagConstraints; diff --git a/src/main/java/bdv/viewer/location/SourceInfoToolBar.java b/src/main/java/bdv/viewer/location/SourceInfoToolBar.java index efceef64..40336d3d 100644 --- a/src/main/java/bdv/viewer/location/SourceInfoToolBar.java +++ b/src/main/java/bdv/viewer/location/SourceInfoToolBar.java @@ -1,3 +1,31 @@ +/* + * #%L + * BigDataViewer core classes with minimal dependencies. + * %% + * Copyright (C) 2012 - 2023 BigDataViewer developers. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ package bdv.viewer.location; import java.awt.Color;