4141import org .scijava .command .CommandModule ;
4242import org .scijava .command .CommandService ;
4343import org .scijava .util .Types ;
44+ import sc .fiji .snt .analysis .ColorMapper ;
4445import sc .fiji .snt .analysis .SNTTable ;
4546import sc .fiji .snt .analysis .TreeStatistics ;
4647import sc .fiji .snt .analysis .curation .PlausibilityCheck ;
@@ -1869,7 +1870,7 @@ private JPanel sourcePanel(final ImagePlus imp) {
18691870 });
18701871
18711872 final JCheckBox autoCTcheckbox = new JCheckBox ("Auto-load channel/frame when starting new paths" , plugin .autoCT );
1872- commandFinder . register (autoCTcheckbox , "Toggle Auto-load CT Position of New Paths" ,
1873+ registerInCommandFinder (autoCTcheckbox , "Toggle Auto-load CT Position of New Paths" ,
18731874 "Main Tab" , "Data Source" );
18741875 GuiUtils .addTooltip (autoCTcheckbox , "Automatically loads the active channel and frame of the starting " +
18751876 "node of newly created paths.<br>" +
@@ -2624,7 +2625,7 @@ private JPanel miscPanel() {
26242625 prefsButton .addActionListener (e -> (new CmdRunner (PrefsCmd .class )).execute ());
26252626 gdb .fill = GridBagConstraints .NONE ;
26262627 miscPanel .add (prefsButton , gdb );
2627- commandFinder . register (prefsButton , "Options tab" );
2628+ registerInCommandFinder (prefsButton , "Options tab" );
26282629 return miscPanel ;
26292630 }
26302631
@@ -3340,16 +3341,16 @@ private JPanel secondaryDataPanel() {
33403341 final JMenuItem mi1 = new JMenuItem ("Secondary Layer Creation Wizard..." ,
33413342 IconFactory .menuIcon (IconFactory .GLYPH .WIZARD ));
33423343 ScriptRecorder .setRecordingCall (mi1 , "snt.getUI().runSecondaryLayerWizard()" );
3343- commandFinder . register (mi1 , "Main tab" , "Interactive tracing (II Layer)" );
3344+ registerInCommandFinder (mi1 , "Main tab" , "Interactive tracing (II Layer)" );
33443345 mi1 .setToolTipText ("Create a secondary layer using built-in image processing routines" );
33453346 mi1 .addActionListener (e -> runSecondaryLayerWizard (true ));
33463347 final JMenuItem mi2 = GuiUtils .MenuItems .fromOpenImage ();
33473348 mi2 .addActionListener (e -> loadSecondaryImage (true ));
33483349 mi2 .setEnabled (!plugin .isStreamMode ());
3349- commandFinder . register (mi2 , "Main tab" , "Interactive tracing (II Layer)" );
3350+ registerInCommandFinder (mi2 , "Main tab" , "Interactive tracing (II Layer)" );
33503351 final JMenuItem mi3 = GuiUtils .MenuItems .fromFileImage ();
33513352 mi3 .addActionListener (e -> loadSecondaryImage (false ));
3352- commandFinder . register (mi3 , "Main tab" , "Interactive tracing (II Layer)" );
3353+ registerInCommandFinder (mi3 , "Main tab" , "Interactive tracing (II Layer)" );
33533354 final JMenuItem mi4 = new JMenuItem ("Flush Current Layer..." , IconFactory .menuIcon (IconFactory .GLYPH .TOILET ));
33543355 registerInCommandFinder (mi4 , "Flush Secondary Layer" , "Main tab" , "Interactive tracing" );
33553356 mi4 .addActionListener (e -> {
@@ -3371,7 +3372,7 @@ private JPanel secondaryDataPanel() {
33713372 (new DynamicCmdRunner (WekaModelLoader .class , null )).run ();
33723373 });
33733374 ScriptRecorder .setRecordingCall (mi5 , "snt.getUI().runCommand(\" From Labkit/TWS Model...\" )" );
3374- commandFinder . register (mi5 , "Main tab" , "Interactive tracing (II Layer)" );
3375+ registerInCommandFinder (mi5 , "Main tab" , "Interactive tracing (II Layer)" );
33753376 GuiUtils .addSeparator (secLayerMenu , "Create:" );
33763377 secLayerMenu .add (mi1 );
33773378 GuiUtils .addSeparator (secLayerMenu , "Load Precomputed:" );
@@ -3384,7 +3385,7 @@ private JPanel secondaryDataPanel() {
33843385 secLayerMenu .addSeparator ();
33853386 final JMenuItem mi6 = GuiUtils .MenuItems .openHelpURL ("Help on Secondary Layers" ,
33863387 "https://imagej.net/plugins/snt/manual#tracing-on-secondary-image" );
3387- commandFinder . register (mi6 , "Main tab" , "Interactive tracing (II Layer)" );
3388+ registerInCommandFinder (mi6 , "Main tab" , "Interactive tracing (II Layer)" );
33883389 secLayerMenu .add (mi6 );
33893390
33903391 // Assemble panel
@@ -4467,14 +4468,14 @@ private JPanel renderingPanel() {
44674468 }
44684469
44694470 private JPanel colorOptionsPanel () {
4470- final ColorChooserButton colorChooser1 = new ColorChooserButton (SNTPrefs .selectedPathColor (), " Selected: " );
4471+ final ColorChooserButton colorChooser1 = new ColorChooserButton (SNTPrefs .selectedPathColor (), "Selected: " );
44714472 colorChooser1 .setName ("Color for Selected Paths" );
44724473 colorChooser1 .addColorChangedListener (newColor -> {
44734474 if (SNTPrefs .deselectedPathColor ().equals (newColor )) {
44744475 guiError ("Selected and deselected colors cannot be the same." );
4475- colorChooser1 .setSelectedColor (SNTPrefs .selectedPathColor (), true );
4476+ colorChooser1 .setSelectedColor (SNTPrefs .selectedPathColor (), false ); // revert only, no need to re-notify
44764477 } else {
4477- plugin .setSelectedColor (newColor );
4478+ plugin .setSelectedAndDeselectedColors (newColor , SNTPrefs . deselectedPathColor () );
44784479 }
44794480 });
44804481 registerInCommandFinder (colorChooser1 , "Default color for selected paths" , "Main Tab" );
@@ -4483,13 +4484,13 @@ private JPanel colorOptionsPanel() {
44834484 colorChooser2 .addColorChangedListener (newColor -> {
44844485 if (SNTPrefs .selectedPathColor ().equals (newColor )) {
44854486 guiError ("Selected and deselected colors cannot be the same." );
4486- colorChooser2 .setSelectedColor (SNTPrefs .deselectedPathColor (), true );
4487+ colorChooser2 .setSelectedColor (SNTPrefs .deselectedPathColor (), false ); // revert only, no need to re-notify
44874488 } else {
4488- plugin .setDeselectedColor ( newColor );
4489+ plugin .setSelectedAndDeselectedColors ( SNTPrefs . selectedPathColor (), newColor );
44894490 }
44904491 });
44914492 registerInCommandFinder (colorChooser2 , "Default color for deselected paths" , "Main Tab" );
4492- final JCheckBox jcheckbox = new JCheckBox ("Override color tags with default colors " , !plugin .getPrefs ().getDisplayCustomPathColors ());
4493+ final JCheckBox jcheckbox = new JCheckBox ("Enforce default colors over Path Manager tags " , !plugin .getPrefs ().getDisplayCustomPathColors ());
44934494 //jcheckbox.putClientProperty(FlatClientProperties.STYLE_CLASS, "small");
44944495 GuiUtils .addTooltip (jcheckbox ,
44954496 "Whether default colors above should be used even when color tags have been applied in the Path Manager.<br><br>" +
@@ -4499,35 +4500,113 @@ private JPanel colorOptionsPanel() {
44994500 plugin .getPrefs ().setDisplayCustomPathColors (!jcheckbox .isSelected ());
45004501 plugin .updateTracingViewers (true );
45014502 });
4502- final JButton resetButton1 = resetButton ("default path colors " );
4503+ final JButton resetButton1 = resetButton ("default selected color " );
45034504 resetButton1 .addActionListener ( e -> {
45044505 colorChooser1 .setSelectedColor (SNTPrefs .DEFAULT_SELECTED_COLOR , true );
4505- showStatus ("Default path colors reset" , true );
4506+ showStatus ("Selected color reset" , true );
45064507 });
4507- final JButton resetButton2 = resetButton ("default path colors " );
4508+ final JButton resetButton2 = resetButton ("default deselected color " );
45084509 resetButton2 .addActionListener ( e -> {
45094510 colorChooser2 .setSelectedColor (SNTPrefs .DEFAULT_DESELECTED_COLOR , true );
4510- showStatus ("Default path colors reset" , true );
4511+ showStatus ("Deselected color reset" , true );
45114512 });
4513+
4514+
4515+ // Assemble elements
45124516 final JToolBar toolbar = new JToolBar ();
4517+ toolbar .setFloatable (false );
45134518 toolbar .setBackground (null );
4514- toolbar .add (Box .createHorizontalGlue ());
4519+ toolbar .setBorder (null ); // don't let the toolbar add its own inset
4520+ toolbar .setMargin (null );
4521+ final int gap = jcheckbox .getIconTextGap ();
4522+ for (final ColorChooserButton cb : new ColorChooserButton []{colorChooser1 , colorChooser2 }) {
4523+ final Insets m = cb .getMargin ();
4524+ cb .setMargin (new Insets (m .top , gap , m .bottom , m .right ));
4525+ }
4526+ GuiUtils .Buttons .addToGroup (List .of (colorChooser1 , resetButton1 ));
45154527 toolbar .add (colorChooser1 );
45164528 toolbar .add (resetButton1 );
4517- GuiUtils .Buttons .addToGroup (List .of (colorChooser1 , resetButton1 ));
4518- toolbar .add (Box .createHorizontalStrut (InternalUtils .MARGIN * 2 ));
4529+ toolbar .add (Box .createHorizontalStrut (InternalUtils .MARGIN ));
45194530 toolbar .addSeparator ();
4520- toolbar .add (Box .createHorizontalStrut (InternalUtils .MARGIN * 2 ));
4531+ toolbar .add (Box .createHorizontalStrut (InternalUtils .MARGIN ));
45214532 toolbar .add (colorChooser2 );
45224533 toolbar .add (resetButton2 );
45234534 GuiUtils .Buttons .addToGroup (List .of (colorChooser2 , resetButton2 ));
4524- toolbar .add (Box .createHorizontalGlue ());
4525- final JPanel panel = new JPanel (new BorderLayout ());
4526- panel .add (toolbar , BorderLayout .CENTER );
4527- panel .add (jcheckbox , BorderLayout .SOUTH );
4535+
4536+ final JPanel topPanel = new JPanel (new BorderLayout ());
4537+ topPanel .setBorder (null );
4538+ topPanel .add (toolbar , BorderLayout .WEST );
4539+ topPanel .add (resetPathColorsOptionsButton (colorChooser1 , colorChooser2 , jcheckbox ), BorderLayout .EAST );
4540+
4541+ final JPanel bottomPanel = new JPanel (new FlowLayout (FlowLayout .LEFT , 0 , 0 ));
4542+ bottomPanel .setBorder (null );
4543+ bottomPanel .add (jcheckbox );
4544+
4545+ final JPanel panel = new JPanel ();
4546+ panel .setLayout (new BoxLayout (panel , BoxLayout .Y_AXIS ));
4547+ panel .setBorder (null ); // don't let the panel add its own inset
4548+ panel .add (topPanel );
4549+ panel .add (bottomPanel );
45284550 return panel ;
45294551 }
45304552
4553+ private JButton resetPathColorsOptionsButton (final ColorChooserButton selected , final ColorChooserButton deselected ,
4554+ final JCheckBox enforceDefaultColorsCheckbox ) {
4555+ final JPopupMenu optionsMenu = new JPopupMenu ();
4556+ final JButton optionsButton = GuiUtils .Buttons .OptionsButton (GLYPH .DROPLET , 1f , optionsMenu );
4557+ GuiUtils .addTooltip (optionsButton , "More options for default path colors" );
4558+ JMenuItem jmi = new JMenuItem ("Swap Selected/Deselected Colors" , IconFactory .menuIcon (GLYPH .ARROWS_LR ));
4559+ jmi .addActionListener (e -> {
4560+ final Color currentSelected = selected .getSelectedColor ();
4561+ final Color currentDeselected = deselected .getSelectedColor ();
4562+ // Bypass the ColorChooserButtons' own "colors cannot match" guard. Set both
4563+ // colors in one call so only one BVV/BDV resync/repaint happens, not two racing
4564+ // ones (see SNT#setSelectedAndDeselectedColors)
4565+ selected .setSelectedColor (currentDeselected , false );
4566+ deselected .setSelectedColor (currentSelected , false );
4567+ plugin .setSelectedAndDeselectedColors (currentDeselected , currentSelected );
4568+ showStatus ("Selected/Deselected colors swapped" , true );
4569+ });
4570+ registerInCommandFinder (jmi , null , "Main tab" , "Default Path Colors" );
4571+ optionsMenu .add (jmi );
4572+ jmi = new JMenuItem ("Use Colorblind-Safe Colors" , IconFactory .menuIcon (GLYPH .EYE_LOW_VISION ));
4573+ jmi .setToolTipText ("Applies an Okabe-Ito colorblind-safe yellow/sky-blue pair" );
4574+ jmi .addActionListener (e -> {
4575+ selected .setSelectedColor (SNTPrefs .COLORBLIND_SAFE_SELECTED_COLOR , false );
4576+ deselected .setSelectedColor (SNTPrefs .COLORBLIND_SAFE_DESELECTED_COLOR , false );
4577+ plugin .setSelectedAndDeselectedColors (SNTPrefs .COLORBLIND_SAFE_SELECTED_COLOR ,
4578+ SNTPrefs .COLORBLIND_SAFE_DESELECTED_COLOR );
4579+ showStatus ("Colorblind-safe path colors applied" , true );
4580+ });
4581+ registerInCommandFinder (jmi , null , "Main tab" , "Default Path Colors" );
4582+ optionsMenu .add (jmi );
4583+ optionsMenu .addSeparator ();
4584+ jmi = new JMenuItem ("Discard All Path Manager Color Tags/Mappings..." , IconFactory .menuIcon (GLYPH .BROOM ));
4585+ jmi .addActionListener (e -> {
4586+ if (!noPathsError () && guiUtils .getConfirmation (
4587+ "Remove color mappings/tags from all paths?" , "Reset Path Colors?" )) {
4588+ ColorMapper .unMap (pathAndFillManager .getPaths ());
4589+ plugin .updateAllViewers ();
4590+ }
4591+ });
4592+ registerInCommandFinder (jmi , null , "Main tab" , "Default Path Colors" );
4593+ optionsMenu .add (jmi );
4594+ optionsMenu .addSeparator ();
4595+ jmi = new JMenuItem ("Reset Defaults" , IconFactory .menuIcon (GLYPH .UNDO ));
4596+ jmi .addActionListener (e -> {
4597+ // Bypass the guard/listener on all three settings and drive plugin/prefs directly
4598+ selected .setSelectedColor (SNTPrefs .DEFAULT_SELECTED_COLOR , false );
4599+ deselected .setSelectedColor (SNTPrefs .DEFAULT_DESELECTED_COLOR , false );
4600+ plugin .setSelectedAndDeselectedColors (SNTPrefs .DEFAULT_SELECTED_COLOR , SNTPrefs .DEFAULT_DESELECTED_COLOR );
4601+ enforceDefaultColorsCheckbox .setSelected (SNTPrefs .DEFAULT_ENFORCE_DEFAULT_PATH_COLORS );
4602+ plugin .getPrefs ().setDisplayCustomPathColors (!SNTPrefs .DEFAULT_ENFORCE_DEFAULT_PATH_COLORS );
4603+ showStatus ("Color settings reset" , true );
4604+ });
4605+ registerInCommandFinder (jmi , null , "Main tab" , "Default Path Colors" );
4606+ optionsMenu .add (jmi );
4607+ return optionsButton ;
4608+ }
4609+
45314610 private JPanel snappingPanel () {
45324611
45334612 tracingOptionsPanel = new JPanel (new FlowLayout (FlowLayout .LEADING , 0 , 0 ));
@@ -4672,7 +4751,7 @@ public void itemStateChanged(final ItemEvent e) {
46724751 // the best one visually (SigmaPalette-style)
46734752 final JMenuItem tuneCost = new JMenuItem ("Cost Function Selection Wizard..." , IconFactory .menuIcon (GLYPH .WIZARD ));
46744753 tuneCost .setToolTipText ("Compare cost-function variants on a probe segment to pick the most suitable one" );
4675- commandFinder . register (tuneCost , "Main tab" , "Interactive Tracing" , "Algorithm Settings" );
4754+ registerInCommandFinder (tuneCost , null , "Main tab" , "Interactive Tracing" , "Algorithm Settings" );
46764755 tuneCost .addActionListener (e -> {
46774756 final CommandService cs = plugin .getContext ().getService (CommandService .class );
46784757 if (cs != null ) cs .run (CostFunctionSelectionCmd .class , true );
@@ -4760,7 +4839,7 @@ protected void done() {
47604839 final JRadioButtonMenuItem rubberBandTracingRbmi = new JRadioButtonMenuItem ("Live Preview" , plugin .rubberBandTracing );
47614840 GuiUtils .addTooltip (rubberBandTracingRbmi , "<html>Continuously previews the path to the cursor position as you move the mouse."
47624841 + "(ala NeuronJ).<br>Click to confirm each segment. <b>Only recommended for 2D images." );
4763- commandFinder . register (rubberBandTracingRbmi , "Main tab" , "Interactive Tracing" , "Algorithm Settings" );
4842+ registerInCommandFinder (rubberBandTracingRbmi , "Main tab" , "Interactive Tracing" , "Algorithm Settings" );
47644843 tracingModeButtonGroup .add (standardTracingRbmi );
47654844 tracingModeButtonGroup .add (rubberBandTracingRbmi );
47664845 optionsMenu .add (standardTracingRbmi );
0 commit comments