@@ -63,6 +63,7 @@ public class BookmarkManager {
6363
6464 private final SNTUI sntui ;
6565 private final Bvv bvv ;
66+ private JToggleButton highlightToggle ;
6667 /** BVV-injected toolbar components (e.g. slab toggle). Added via {@link #addBvvToolbarButton}. */
6768 private final java .util .List <javax .swing .JComponent > bvvToolbarButtons = new java .util .ArrayList <>();
6869 private final GuiUtils guiUtils ;
@@ -195,10 +196,9 @@ This pane stores image locations that you can quickly (re)visit while \
195196 panel .add (assembleHighlightToolbar (), gbc );
196197 gbc .gridy ++;
197198 gbc .weighty = 0.95 ;
198- // Use the cached scroll pane (created in assembleTable) so the
199- // DetachableTable helper can move it in and out of a floating dialog.
200- // Capture the constraints used here so redockTableScroll() can
201- // restore the pane to the same grid cell after a detach.
199+ // Use the cached scroll pane (created in assembleTable) so the DetachableTable helper can move it in and out
200+ // of a floating dialog. Capture the constraints used here so redockTableScroll() can restore the pane to the
201+ // same grid cell after a detach.
202202 tableScrollGbc = (GridBagConstraints ) gbc .clone ();
203203 panel .add (tableScroll , tableScrollGbc );
204204 gbc .gridy ++;
@@ -226,6 +226,17 @@ private BookmarkTable assembleTable(final BookmarkModel model) {
226226 final BookmarkTable table = new BookmarkTable (model );
227227 final JPopupMenu pMenu = assembleTablePopupMenu (table );
228228 table .setComponentPopupMenu (pMenu );
229+ // Live-sync the overlay highlights with the table selection when the
230+ // toggle is on. valueIsAdjusting is filtered so click-and-drag selections
231+ // only repaint once at the end, not on every intermediate index. Empty
232+ // model is guarded here to avoid showHighlights() popping an error
233+ // dialog during table-clear operations.
234+ table .getSelectionModel ().addListSelectionListener (e -> {
235+ if (e .getValueIsAdjusting ()) return ;
236+ if (highlightToggle == null || !highlightToggle .isSelected ()) return ;
237+ if (model .getRowCount () == 0 ) return ;
238+ showHighlights ();
239+ });
229240 // Cache the scroll pane so the detacher can move it between dock and
230241 // floating dialog. BookmarkTable.getContainer() copies the table's
231242 // popup onto the scroll pane, so we must set the popup BEFORE this.
@@ -622,6 +633,7 @@ private JPopupMenu importMenu() {
622633 load (imp .getOverlay ().toArray ());
623634 sntui .showStatus (model .getDataList ().size () + " listed bookmarks " , true );
624635 recordCmd ("load(snt.getInstance().getImagePlus().getOverlay().toArray())" );
636+ clearImportedOverlay (imp );
625637 });
626638 jmi = new JMenuItem ("From ROI Manager" , IconFactory .menuIcon (IconFactory .GLYPH .LIST_ALT ));
627639 menu .add (jmi );
@@ -651,6 +663,32 @@ private JPopupMenu importMenu() {
651663 return menu ;
652664 }
653665
666+ private void clearImportedOverlay (final ImagePlus imp ) {
667+ final Overlay ov = imp .getOverlay ();
668+ if (ov == null || ov .size () == 0 ) return ;
669+ final boolean nag = sntui .plugin .getPrefs ().getTemp ("clear-imported-overlay-nag" , true );
670+ boolean wipe = sntui .plugin .getPrefs ().getTemp ("clear-imported-overlay" , true );
671+ if (nag ) {
672+ final boolean [] options = guiUtils .getPersistentConfirmation (
673+ "Now that ROIs have been imported as bookmarks, remove them from the image overlay? "
674+ + "(This clears all ROIs from the overlay, including any drawn by other tools.)" ,
675+ "Remove Imported Overlay?" );
676+ sntui .plugin .getPrefs ().setTemp ("clear-imported-overlay" , wipe = options [0 ]);
677+ sntui .plugin .getPrefs ().setTemp ("clear-imported-overlay-nag" , !options [1 ]);
678+ }
679+ if (wipe ) {
680+ // Preserve our own highlight ROIs (named with HIGHLIGHT_PREFIX) so the
681+ // toggle doesn't desync when the user happens to clear during a highlight
682+ final Roi [] all = ov .toArray ();
683+ for (int i = all .length - 1 ; i >= 0 ; i --) {
684+ final String name = all [i ].getName ();
685+ if (name != null && name .startsWith (HIGHLIGHT_PREFIX )) continue ;
686+ ov .remove (i );
687+ }
688+ imp .updateAndDraw ();
689+ }
690+ }
691+
654692 private JPopupMenu exportMenu () {
655693 final JPopupMenu menu = new JPopupMenu ();
656694 GuiUtils .addSeparator (menu , "Export:" );
@@ -713,19 +751,30 @@ private void saveToUserChosenFile(final File file) {
713751 }
714752 }
715753
716- private void highlightBookmarks () {
717- if (noBookmarksError ()) return ;
754+ /**
755+ * Adds (or refreshes) overlay highlights for the currently selected
756+ * bookmarks. Re-invoking re-evaluates the current selection (used by
757+ * the table's ListSelectionListener to keep highlights in sync). Sole
758+ * entry point for showing highlights; syncs {@link #highlightToggle}
759+ * state.
760+ */
761+ private void showHighlights () {
762+ if (noBookmarksError ()) {
763+ syncHighlightToggle (false );
764+ return ;
765+ }
718766 final ImagePlus imp = sntui .plugin .getImagePlus ();
719767 if (imp == null ) {
720768 sntui .guiUtils .error ("No image is currently open." );
769+ syncHighlightToggle (false );
721770 return ;
722771 }
723772 Overlay overlay = imp .getOverlay ();
724773 if (overlay == null ) {
725774 overlay = new Overlay ();
726775 imp .setOverlay (overlay );
727776 }
728- // Remove any existing highlights first
777+ // Remove any existing highlights first (so re-clicking refreshes against the current selection)
729778 removeHighlightROIs (overlay );
730779 final int [] rows = getSelectedRowsAllIfNone ();
731780 int idx = 0 ;
@@ -734,22 +783,38 @@ private void highlightBookmarks() {
734783 final Bookmark b = model .getDataList ().get (modelRow );
735784 final PointRoi roi = b .toRoi ();
736785 roi .setName (HIGHLIGHT_PREFIX + idx ++);
737- roi .setPointType (PointRoi .CIRCLE );
786+ roi .setPointType (PointRoi .DOT );
787+ roi .setSize (3 ); // mid size: 0=tiny, 6=XXXL
738788 if (roi .getStrokeColor () == null )
739789 roi .setStrokeColor (Color .CYAN );
740790 overlay .add (roi );
741791 }
742792 imp .updateAndDraw ();
743793 sntui .showStatus (rows .length + " bookmark(s) highlighted on overlay" , true );
794+ syncHighlightToggle (true );
744795 }
745796
746- private void clearHighlights () {
797+ /**
798+ * Removes highlight ROIs from the overlay. Sole entry point for hiding
799+ * highlights; syncs {@link #highlightToggle} state.
800+ */
801+ private void hideHighlights () {
747802 final ImagePlus imp = (sntui == null ) ? null : sntui .plugin .getImagePlus ();
748- if (imp == null || imp .getOverlay () == null ) return ;
749- if (removeHighlightROIs (imp .getOverlay ())) {
803+ if (imp != null && imp .getOverlay () != null && removeHighlightROIs (imp .getOverlay ())) {
750804 imp .updateAndDraw ();
751- sntui .showStatus ("Highlights cleared" , true );
805+ if ( sntui != null ) sntui .showStatus ("Highlights cleared" , true );
752806 }
807+ syncHighlightToggle (false );
808+ }
809+
810+ /** Updates the toggle without retriggering its action listener. */
811+ private void syncHighlightToggle (final boolean selected ) {
812+ if (highlightToggle == null || highlightToggle .isSelected () == selected ) return ;
813+ // Detach listeners briefly so toggling state programmatically doesn't loop back into show/hide
814+ final java .awt .event .ActionListener [] ls = highlightToggle .getActionListeners ();
815+ for (final java .awt .event .ActionListener l : ls ) highlightToggle .removeActionListener (l );
816+ highlightToggle .setSelected (selected );
817+ for (final java .awt .event .ActionListener l : ls ) highlightToggle .addActionListener (l );
753818 }
754819
755820 /**
@@ -778,14 +843,15 @@ private JToolBar assembleHighlightToolbar() {
778843 tb .add (Box .createHorizontalGlue ());
779844 if (sntui != null ) { // no functionality in BVV Markers table
780845 tb .addSeparator ();
781- final JButton highlightButton = new JButton (IconFactory .buttonIcon ('\uf0eb' , false ));
782- highlightButton .setToolTipText ("Highlight selected bookmarks on the image (all if none selected)" );
783- highlightButton .addActionListener (e -> highlightBookmarks ());
784- final JButton clearHighlightButton = GuiUtils .Buttons .undo ();
785- clearHighlightButton .setToolTipText ("Remove bookmark highlights from the image" );
786- clearHighlightButton .addActionListener (e -> clearHighlights ());
787- tb .add (highlightButton );
788- tb .add (clearHighlightButton );
846+ highlightToggle = new JToggleButton (IconFactory .buttonIcon ('\uf0eb' , false ));
847+ highlightToggle .setToolTipText ("<html>Show bookmark locations on the image. With nothing selected,<br>"
848+ + "all bookmarks are highlighted; otherwise only the selected ones.<br>"
849+ + "Click again to toggle." );
850+ highlightToggle .addActionListener (e -> {
851+ if (highlightToggle .isSelected ()) showHighlights ();
852+ else hideHighlights ();
853+ });
854+ tb .add (highlightToggle );
789855 }
790856 return tb ;
791857 }
0 commit comments