001/* 002 * Copyright 2010-2015 Institut Pasteur. 003 * 004 * This file is part of Icy. 005 * 006 * Icy is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * Icy is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with Icy. If not, see <http://www.gnu.org/licenses/>. 018 */ 019package icy.gui.menu; 020 021import java.awt.event.ActionEvent; 022import java.awt.event.ActionListener; 023import java.util.ArrayList; 024import java.util.EventListener; 025import java.util.List; 026 027import javax.swing.SwingConstants; 028import javax.swing.event.EventListenerList; 029 030import org.pushingpixels.flamingo.api.common.AbstractCommandButton; 031import org.pushingpixels.flamingo.api.common.CommandToggleButtonGroup; 032import org.pushingpixels.flamingo.api.common.HorizontalAlignment; 033import org.pushingpixels.flamingo.api.common.JCommandButton; 034import org.pushingpixels.flamingo.api.common.JCommandButton.CommandButtonKind; 035import org.pushingpixels.flamingo.api.common.JCommandToggleButton; 036import org.pushingpixels.flamingo.api.common.popup.JCommandPopupMenu; 037import org.pushingpixels.flamingo.api.common.popup.JPopupPanel; 038import org.pushingpixels.flamingo.api.common.popup.PopupPanelCallback; 039import org.pushingpixels.flamingo.api.ribbon.JRibbonBand; 040import org.pushingpixels.flamingo.api.ribbon.JRibbonComponent; 041import org.pushingpixels.flamingo.api.ribbon.RibbonElementPriority; 042import org.pushingpixels.flamingo.api.ribbon.RibbonTask; 043import org.pushingpixels.flamingo.api.ribbon.resize.CoreRibbonResizeSequencingPolicies; 044import org.pushingpixels.flamingo.internal.ui.ribbon.BasicBandControlPanelUI; 045 046import icy.action.RoiActions; 047import icy.gui.component.NumberTextField; 048import icy.gui.component.button.IcyButton; 049import icy.gui.component.button.IcyCommandButton; 050import icy.gui.component.button.IcyCommandMenuButton; 051import icy.gui.component.button.IcyCommandToggleButton; 052import icy.gui.inspector.RoisPanel; 053import icy.gui.plugin.PluginCommandButton; 054import icy.gui.util.RibbonUtil; 055import icy.main.Icy; 056import icy.plugin.PluginDescriptor; 057import icy.plugin.PluginLoader; 058import icy.plugin.PluginLoader.PluginLoaderEvent; 059import icy.plugin.PluginLoader.PluginLoaderListener; 060import icy.resource.ResourceUtil; 061import icy.resource.icon.IcyIcon; 062import icy.roi.ROI; 063import icy.roi.ROI2D; 064import icy.roi.ROI3D; 065import icy.sequence.Sequence; 066import icy.system.thread.ThreadUtil; 067import icy.util.StringUtil; 068import plugins.kernel.roi.roi2d.ROI2DArea; 069import plugins.kernel.roi.roi2d.ROI2DShape; 070import plugins.kernel.roi.roi2d.plugin.ROI2DAreaPlugin; 071import plugins.kernel.roi.roi2d.plugin.ROI2DEllipsePlugin; 072import plugins.kernel.roi.roi2d.plugin.ROI2DLinePlugin; 073import plugins.kernel.roi.roi2d.plugin.ROI2DPointPlugin; 074import plugins.kernel.roi.roi2d.plugin.ROI2DPolyLinePlugin; 075import plugins.kernel.roi.roi2d.plugin.ROI2DPolygonPlugin; 076import plugins.kernel.roi.roi2d.plugin.ROI2DRectanglePlugin; 077import plugins.kernel.roi.roi3d.ROI3DArea; 078import plugins.kernel.roi.roi3d.plugin.ROI3DLinePlugin; 079import plugins.kernel.roi.roi3d.plugin.ROI3DPointPlugin; 080import plugins.kernel.roi.roi3d.plugin.ROI3DPolyLinePlugin; 081import plugins.kernel.roi.roi4d.ROI4DArea; 082import plugins.kernel.roi.roi5d.ROI5DArea; 083import plugins.kernel.roi.tool.plugin.ROILineCutterPlugin; 084 085/** 086 * ROI dedicated task 087 * 088 * @author Stephane 089 */ 090public class ROITask extends RibbonTask implements PluginLoaderListener 091{ 092 public static final String NAME = "Region Of Interest"; 093 094 /** 095 * @deprecated Use {@link #setSelected(String)} with <code>null</code> parameter instead. 096 */ 097 @Deprecated 098 public static final String SELECT = "Selection"; 099 /** 100 * @deprecated Use {@link #setSelected(String)} with <code>null</code> parameter instead. 101 */ 102 @Deprecated 103 public static final String MOVE = "Move"; 104 105 /** 106 * @deprecated Use {@link ROITask#isROITool()} instead. 107 */ 108 @Deprecated 109 public static boolean isROITool(String command) 110 { 111 // assume it's a ROI command when it's not null 112 return (command != null); 113 } 114 115 /** 116 * Listener class 117 */ 118 public interface ROITaskListener extends EventListener 119 { 120 public void toolChanged(String command); 121 } 122 123 static class ROI2DBand extends JRibbonBand 124 { 125 public static final String BAND_NAME = "2D ROI"; 126 127 final List<IcyCommandToggleButton> pluginButtons; 128 129 public ROI2DBand() 130 { 131 super(BAND_NAME, new IcyIcon(ResourceUtil.ICON_ROI_POLYGON)); 132 133 startGroup(); 134 135 pluginButtons = new ArrayList<IcyCommandToggleButton>(); 136 137 // refresh buttons (don't set button group and listener at this point) 138 setHidden(setROIFromPlugins(pluginButtons, this, getROIPlugins(), null, null)); 139 updateButtonsState(); 140 141 RibbonUtil.setRestrictiveResizePolicies(this); 142 setToolTipText("2D Region Of Interest"); 143 } 144 145 public static List<PluginDescriptor> getROIPlugins() 146 { 147 final List<PluginDescriptor> result = new ArrayList<PluginDescriptor>(); 148 149 // 2D ROI 150 result.add(PluginLoader.getPlugin(ROI2DAreaPlugin.class.getName())); 151 result.add(PluginLoader.getPlugin(ROI2DRectanglePlugin.class.getName())); 152 result.add(PluginLoader.getPlugin(ROI2DEllipsePlugin.class.getName())); 153 result.add(PluginLoader.getPlugin(ROI2DPolygonPlugin.class.getName())); 154 result.add(PluginLoader.getPlugin(ROI2DPointPlugin.class.getName())); 155 result.add(PluginLoader.getPlugin(ROI2DLinePlugin.class.getName())); 156 result.add(PluginLoader.getPlugin(ROI2DPolyLinePlugin.class.getName())); 157 158 return result; 159 } 160 161 void updateButtonsState() 162 { 163 final Sequence sequence = Icy.getMainInterface().getActiveSequence(); 164 165 for (IcyCommandToggleButton button : pluginButtons) 166 button.setEnabled(sequence != null); 167 } 168 } 169 170 static class ROI3DBand extends JRibbonBand 171 { 172 public static final String BAND_NAME = "3D ROI"; 173 174 final List<IcyCommandToggleButton> pluginButtons; 175 176 public ROI3DBand() 177 { 178 super(BAND_NAME, new IcyIcon(ResourceUtil.ICON_CUBE_3D)); 179 180 startGroup(); 181 182 pluginButtons = new ArrayList<IcyCommandToggleButton>(); 183 184 // refresh buttons (don't set button group and listener at this point) 185 setHidden(setROIFromPlugins(pluginButtons, this, getROIPlugins(), null, null)); 186 updateButtonsState(); 187 188 RibbonUtil.setRestrictiveResizePolicies(this); 189 setToolTipText("3D Region Of Interest"); 190 } 191 192 public static List<PluginDescriptor> getROIPlugins() 193 { 194 final List<PluginDescriptor> result = new ArrayList<PluginDescriptor>(); 195 196 // 3D ROI 197 result.add(PluginLoader.getPlugin(ROI3DPointPlugin.class.getName())); 198 result.add(PluginLoader.getPlugin(ROI3DLinePlugin.class.getName())); 199 result.add(PluginLoader.getPlugin(ROI3DPolyLinePlugin.class.getName())); 200 201 return result; 202 } 203 204 void updateButtonsState() 205 { 206 final Sequence sequence = Icy.getMainInterface().getActiveSequence(); 207 208 for (IcyCommandToggleButton button : pluginButtons) 209 button.setEnabled(sequence != null); 210 } 211 } 212 213 static class ROIExtBand extends JRibbonBand 214 { 215 public static final String BAND_NAME = "External ROI"; 216 217 final List<IcyCommandToggleButton> pluginButtons; 218 219 public ROIExtBand() 220 { 221 super(BAND_NAME, new IcyIcon(ResourceUtil.ICON_ROI)); 222 223 startGroup(); 224 225 pluginButtons = new ArrayList<IcyCommandToggleButton>(); 226 227 // refresh buttons (don't set button group and listener at this point) 228 setHidden(setROIFromPlugins(pluginButtons, this, getROIPlugins(), null, null)); 229 updateButtonsState(); 230 231 RibbonUtil.setRestrictiveResizePolicies(this); 232 setToolTipText("External Region Of Interest"); 233 updateButtonsState(); 234 } 235 236 public static List<PluginDescriptor> getROIPlugins() 237 { 238 final List<PluginDescriptor> result = new ArrayList<PluginDescriptor>(); 239 240 // remove default 2D & 3D ROI to only keep external ROI 241 result.removeAll(ROI2DBand.getROIPlugins()); 242 result.removeAll(ROI3DBand.getROIPlugins()); 243 // explicitly remove the ROI cutter from the list 244 result.remove(PluginLoader.getPlugin(ROILineCutterPlugin.class.getName())); 245 246 return result; 247 } 248 249 void updateButtonsState() 250 { 251 final Sequence sequence = Icy.getMainInterface().getActiveSequence(); 252 253 for (IcyCommandToggleButton button : pluginButtons) 254 button.setEnabled(sequence != null); 255 } 256 } 257 258 static class ROIConversionBand extends JRibbonBand 259 { 260 public static final String BAND_NAME = "Conversion"; 261 262 final NumberTextField radiusField; 263 final IcyButton convertToEllipseButton; 264 final IcyButton convertToRectangleButton; 265 final IcyButton convertToStackButton; 266 final IcyButton convertToMaskButton; 267 final IcyButton convertToShapeButton; 268 269 public ROIConversionBand() 270 { 271 super(BAND_NAME, new IcyIcon(ResourceUtil.ICON_LAYER_V2)); 272 273 // conversion 274 // convertToStackButton = new IcyCommandButton(RoiActions.convertToStackAction); 275 // convertToMaskButton = new IcyCommandButton(RoiActions.convertToMaskAction); 276 // convertToShapeButton = new IcyCommandButton(RoiActions.convertToShapeAction); 277 // addCommandButton(convertToStackButton, RibbonElementPriority.MEDIUM); 278 // addCommandButton(convertToMaskButton, RibbonElementPriority.MEDIUM); 279 // addCommandButton(convertToShapeButton, RibbonElementPriority.MEDIUM); 280 281 convertToStackButton = new IcyButton(RoiActions.convertToStackAction); 282 convertToStackButton.setHorizontalAlignment(SwingConstants.LEADING); 283 convertToStackButton.setFlat(true); 284 convertToMaskButton = new IcyButton(RoiActions.convertToMaskAction); 285 convertToMaskButton.setHorizontalAlignment(SwingConstants.LEADING); 286 convertToMaskButton.setFlat(true); 287 convertToShapeButton = new IcyButton(RoiActions.convertToShapeAction); 288 convertToShapeButton.setHorizontalAlignment(SwingConstants.LEADING); 289 convertToShapeButton.setFlat(true); 290 convertToEllipseButton = new IcyButton(RoiActions.convertToEllipseAction); 291 convertToEllipseButton.setHorizontalAlignment(SwingConstants.LEADING); 292 convertToEllipseButton.setFlat(true); 293 convertToRectangleButton = new IcyButton(RoiActions.convertToRectangleAction); 294 convertToRectangleButton.setHorizontalAlignment(SwingConstants.LEADING); 295 convertToRectangleButton.setFlat(true); 296 radiusField = new NumberTextField(); 297 radiusField.setHorizontalAlignment(SwingConstants.CENTER); 298 radiusField.setToolTipText("Radius for Circle/Rectangle conversion"); 299 radiusField.setNumericValue(1); 300 301 JRibbonComponent comp; 302 303 comp = new JRibbonComponent(convertToStackButton); 304 comp.setResizingAware(true); 305 comp.setHorizontalAlignment(HorizontalAlignment.FILL); 306 addRibbonComponent(comp); 307 comp = new JRibbonComponent(convertToMaskButton); 308 comp.setResizingAware(true); 309 comp.setHorizontalAlignment(HorizontalAlignment.FILL); 310 addRibbonComponent(comp); 311 comp = new JRibbonComponent(convertToShapeButton); 312 comp.setResizingAware(true); 313 comp.setHorizontalAlignment(HorizontalAlignment.FILL); 314 addRibbonComponent(comp); 315 316 comp = new JRibbonComponent(convertToEllipseButton); 317 comp.setResizingAware(true); 318 comp.setHorizontalAlignment(HorizontalAlignment.FILL); 319 addRibbonComponent(comp); 320 comp = new JRibbonComponent(convertToRectangleButton); 321 comp.setResizingAware(true); 322 comp.setHorizontalAlignment(HorizontalAlignment.FILL); 323 addRibbonComponent(comp); 324 comp = new JRibbonComponent(radiusField); 325 comp.setResizingAware(true); 326 comp.setHorizontalAlignment(HorizontalAlignment.FILL); 327 addRibbonComponent(comp); 328 329 setToolTipText("Conversion tools for ROI"); 330 331 // better to do that to fix the ending gap for band containing wrapped components 332 ((BasicBandControlPanelUI) getControlPanel().getUI()).setLayoutGap(0); 333 RibbonUtil.setRestrictiveResizePolicies(this, 0); 334 updateButtonsState(); 335 } 336 337 public double getRadius() 338 { 339 return Math.max(0, radiusField.getNumericValue()); 340 } 341 342 public void updateButtonsState() 343 { 344 boolean convertEllipseEnable = false; 345 boolean convertRectangleEnable = false; 346 boolean convertStackEnable = false; 347 boolean convertMaskEnable = false; 348 boolean convertShapeEnable = false; 349 final Sequence seq = Icy.getMainInterface().getActiveSequence(); 350 351 if (seq != null) 352 { 353 final List<ROI> selectedRois = seq.getSelectedROIs(); 354 355 for (ROI roi : selectedRois) 356 { 357 if (roi instanceof ROI2D) 358 { 359 convertStackEnable = true; 360 if (!(roi instanceof ROI2DShape)) 361 convertShapeEnable = true; 362 } 363 if (!((roi instanceof ROI2DArea) || (roi instanceof ROI3DArea) || (roi instanceof ROI4DArea) 364 || (roi instanceof ROI5DArea))) 365 convertMaskEnable = true; 366 if (roi instanceof ROI3D) 367 { 368 if (roi instanceof ROI3DArea) 369 convertShapeEnable = true; 370 } 371 convertEllipseEnable = true; 372 convertRectangleEnable = true; 373 } 374 } 375 376 convertToEllipseButton.setEnabled(convertEllipseEnable); 377 convertToRectangleButton.setEnabled(convertRectangleEnable); 378 radiusField.setEnabled(convertEllipseEnable || convertRectangleEnable); 379 convertToStackButton.setEnabled(convertStackEnable); 380 convertToMaskButton.setEnabled(convertMaskEnable); 381 convertToShapeButton.setEnabled(convertShapeEnable); 382 } 383 } 384 385 static class ROIBooleanOpBand extends JRibbonBand 386 { 387 public static final String BAND_NAME = "Boolean Op"; 388 389 final IcyCommandButton booleanUnionButton; 390 final IcyCommandButton booleanIntersectionButton; 391 final IcyCommandButton booleanOthersButton; 392 final IcyCommandMenuButton booleanInversionButton; 393 final IcyCommandMenuButton booleanExclusiveUnionButton; 394 final IcyCommandMenuButton booleanSubtractionButton; 395 396 public ROIBooleanOpBand() 397 { 398 super(BAND_NAME, new IcyIcon(ResourceUtil.ICON_ROI_OR)); 399 400 booleanUnionButton = new IcyCommandButton(RoiActions.boolOrAction); 401 booleanIntersectionButton = new IcyCommandButton(RoiActions.boolAndAction); 402 booleanInversionButton = new IcyCommandMenuButton(RoiActions.boolNotAction); 403 booleanExclusiveUnionButton = new IcyCommandMenuButton(RoiActions.boolXorAction); 404 booleanSubtractionButton = new IcyCommandMenuButton(RoiActions.boolSubtractAction); 405 406 booleanOthersButton = new IcyCommandButton("Other operation"); 407 booleanOthersButton.setCommandButtonKind(CommandButtonKind.POPUP_ONLY); 408 booleanOthersButton.setPopupCallback(new PopupPanelCallback() 409 { 410 @Override 411 public JPopupPanel getPopupPanel(JCommandButton arg0) 412 { 413 final JCommandPopupMenu result = new JCommandPopupMenu(); 414 415 result.addMenuButton(booleanInversionButton); 416 result.addMenuButton(booleanExclusiveUnionButton); 417 result.addMenuButton(booleanSubtractionButton); 418 419 return result; 420 } 421 }); 422 booleanOthersButton.setEnabled(false); 423 424 addCommandButton(booleanUnionButton, RibbonElementPriority.MEDIUM); 425 addCommandButton(booleanIntersectionButton, RibbonElementPriority.MEDIUM); 426 addCommandButton(booleanOthersButton, RibbonElementPriority.MEDIUM); 427 428 setToolTipText("Boolean operation for ROI"); 429 RibbonUtil.setRestrictiveResizePolicies(this); 430 updateButtonsState(); 431 } 432 433 public void updateButtonsState() 434 { 435 boolean singleOp = false; 436 boolean boolOp = false; 437 final Sequence seq = Icy.getMainInterface().getActiveSequence(); 438 439 if (seq != null) 440 { 441 final List<ROI> selectedRois = seq.getSelectedROIs(); 442 443 singleOp = !selectedRois.isEmpty(); 444 boolOp = selectedRois.size() > 1; 445 } 446 447 booleanUnionButton.setEnabled(boolOp); 448 booleanIntersectionButton.setEnabled(boolOp); 449 booleanInversionButton.setEnabled(singleOp); 450 booleanExclusiveUnionButton.setEnabled(boolOp); 451 booleanSubtractionButton.setEnabled(boolOp); 452 booleanOthersButton.setEnabled(singleOp || boolOp); 453 } 454 } 455 456 static class ROISeparationBand extends JRibbonBand 457 { 458 public static final String BAND_NAME = "Separation"; 459 460 final IcyCommandButton separateObjectsButton; 461 final IcyCommandToggleButton cutButton; 462 final IcyCommandButton splitButton; 463 464 public ROISeparationBand() 465 { 466 super(BAND_NAME, new IcyIcon(ResourceUtil.ICON_ROI_COMP)); 467 468 // conversion 469 separateObjectsButton = new IcyCommandButton(RoiActions.separateObjectsAction); 470 cutButton = PluginCommandButton 471 .createToggleButton(PluginLoader.getPlugin(ROILineCutterPlugin.class.getName()), false, true); 472 splitButton = new IcyCommandButton(RoiActions.autoSplitAction); 473 addCommandButton(separateObjectsButton, RibbonElementPriority.MEDIUM); 474 addCommandButton(cutButton, RibbonElementPriority.MEDIUM); 475 // addCommandButton(splitButton, RibbonElementPriority.MEDIUM); 476 477 cutButton.setEnabled(false); 478 479 setToolTipText("Separation tools for ROI"); 480 RibbonUtil.setRestrictiveResizePolicies(this); 481 } 482 483 public void updateButtonsState() 484 { 485 boolean separateObjEnable = false; 486 boolean cutEnable = false; 487 boolean splitEnable = false; 488 final Sequence seq = Icy.getMainInterface().getActiveSequence(); 489 490 if (seq != null) 491 { 492 final List<ROI> selectedRois = seq.getSelectedROIs(); 493 494 cutEnable = seq.hasROI(); 495 separateObjEnable = !selectedRois.isEmpty(); 496 splitEnable = !selectedRois.isEmpty(); 497 } 498 499 separateObjectsButton.setEnabled(separateObjEnable); 500 cutButton.setEnabled(cutEnable); 501 splitButton.setEnabled(splitEnable); 502 } 503 } 504 505 // static class ROIScaleBand extends JRibbonBand 506 // { 507 // public static final String BAND_NAME = "Resize"; 508 // 509 // final IcyCommandButton upscaleButton; 510 // final IcyCommandMenuButton upscale2dButton; 511 // final IcyCommandButton downscaleButton; 512 // final IcyCommandMenuButton downscale2dButton; 513 // 514 // public ROIScaleBand() 515 // { 516 // super(BAND_NAME, new IcyIcon(ResourceUtil.ICON_RESIZE_2)); 517 // 518 // // up scale 519 // upscale2dButton = new IcyCommandMenuButton(RoiActions.upscale2dAction); 520 // upscaleButton = new IcyCommandButton(RoiActions.upscaleAction); 521 // upscaleButton.setCommandButtonKind(CommandButtonKind.ACTION_AND_POPUP_MAIN_ACTION); 522 // upscaleButton.setPopupCallback(new PopupPanelCallback() 523 // { 524 // @Override 525 // public JPopupPanel getPopupPanel(JCommandButton arg0) 526 // { 527 // final JCommandPopupMenu result = new JCommandPopupMenu(); 528 // 529 // result.addMenuButton(upscale2dButton); 530 // 531 // return result; 532 // } 533 // }); 534 // 535 // // down scale 536 // downscale2dButton = new IcyCommandMenuButton(RoiActions.downscale2dAction); 537 // downscaleButton = new IcyCommandButton(RoiActions.downscaleAction); 538 // downscaleButton.setCommandButtonKind(CommandButtonKind.ACTION_AND_POPUP_MAIN_ACTION); 539 // downscaleButton.setPopupCallback(new PopupPanelCallback() 540 // { 541 // @Override 542 // public JPopupPanel getPopupPanel(JCommandButton arg0) 543 // { 544 // final JCommandPopupMenu result = new JCommandPopupMenu(); 545 // 546 // result.addMenuButton(downscale2dButton); 547 // 548 // return result; 549 // } 550 // }); 551 // 552 // addCommandButton(upscaleButton, RibbonElementPriority.MEDIUM); 553 // addCommandButton(downscaleButton, RibbonElementPriority.MEDIUM); 554 // 555 // setToolTipText("Resize tools for ROI"); 556 // RibbonUtil.setRestrictiveResizePolicies(this); 557 // } 558 // 559 // public void updateButtonsState() 560 // { 561 // boolean resizeEnable = false; 562 // final Sequence seq = Icy.getMainInterface().getActiveSequence(); 563 // 564 // if (seq != null) 565 // { 566 // final List<ROI> selectedRois = seq.getSelectedROIs(); 567 // 568 // resizeEnable = !selectedRois.isEmpty(); 569 // } 570 // 571 // upscaleButton.setEnabled(resizeEnable); 572 // upscale2dButton.setEnabled(resizeEnable); 573 // downscaleButton.setEnabled(resizeEnable); 574 // downscale2dButton.setEnabled(resizeEnable); 575 // } 576 // } 577 578 // static class ROIMorphoBand extends JRibbonBand 579 // { 580 // public static final String BAND_NAME = "Morphology"; 581 // 582 // final IcyCommandButton erodeButton; 583 // final IcyCommandButton dilateButton; 584 // final IcyCommandButton fillHoledilateButton; 585 // final IcyCommandButton otherButton; 586 // 587 // public ROIMorphoBand() 588 // { 589 // super(BAND_NAME, new IcyIcon(ResourceUtil.ICON_LAYER_V2)); 590 // 591 // booleanUnionButton = new IcyCommandButton(RoiActions.boolOrAction); 592 // booleanIntersectionButton = new IcyCommandButton(RoiActions.boolAndAction); 593 // booleanInversionButton = new IcyCommandMenuButton(RoiActions.boolNotAction); 594 // booleanExclusiveUnionButton = new IcyCommandMenuButton(RoiActions.boolXorAction); 595 // booleanSubtractionButton = new IcyCommandMenuButton(RoiActions.boolSubtractAction); 596 // 597 // booleanOthersButton = new IcyCommandButton("Other operation"); 598 // booleanOthersButton.setCommandButtonKind(CommandButtonKind.POPUP_ONLY); 599 // booleanOthersButton.setPopupCallback(new PopupPanelCallback() 600 // { 601 // @Override 602 // public JPopupPanel getPopupPanel(JCommandButton arg0) 603 // { 604 // final JCommandPopupMenu result = new JCommandPopupMenu(); 605 // 606 // result.addMenuButton(booleanInversionButton); 607 // result.addMenuButton(booleanExclusiveUnionButton); 608 // result.addMenuButton(booleanSubtractionButton); 609 // 610 // return result; 611 // } 612 // }); 613 // booleanOthersButton.setEnabled(false); 614 // 615 // addCommandButton(booleanUnionButton, RibbonElementPriority.MEDIUM); 616 // addCommandButton(booleanIntersectionButton, RibbonElementPriority.MEDIUM); 617 // addCommandButton(booleanOthersButton, RibbonElementPriority.MEDIUM); 618 // 619 // setToolTipText("Morphological operator for ROI"); 620 // RibbonUtil.setRestrictiveResizePolicies(this); 621 // } 622 // 623 // public void updateButtonsState() 624 // { 625 // boolean separateObjEnable = false; 626 // boolean cutEnable = false; 627 // boolean splitEnable = false; 628 // final Sequence seq = Icy.getMainInterface().getActiveSequence(); 629 // 630 // if (seq != null) 631 // { 632 // final List<ROI> selectedRois = seq.getSelectedROIs(); 633 // 634 // cutEnable = seq.hasROI(); 635 // separateObjEnable = !selectedRois.isEmpty(); 636 // splitEnable = !selectedRois.isEmpty(); 637 // } 638 // 639 // separateObjectsButton.setEnabled(separateObjEnable); 640 // cutButton.setEnabled(cutEnable); 641 // splitButton.setEnabled(splitEnable); 642 // } 643 // } 644 645 static class ROIIOBand extends JRibbonBand 646 { 647 public static final String BAND_NAME = "File"; 648 649 final IcyCommandButton loadFromXMLButton; 650 final IcyCommandButton saveToXMLButton; 651 final IcyCommandButton exportToXLSButton; 652 653 public ROIIOBand() 654 { 655 super(BAND_NAME, new IcyIcon(ResourceUtil.ICON_OPEN)); 656 657 // conversion 658 loadFromXMLButton = new IcyCommandButton(RoiActions.loadAction); 659 saveToXMLButton = new IcyCommandButton(RoiActions.saveAction); 660 exportToXLSButton = new IcyCommandButton(RoiActions.xlsExportAction); 661 addCommandButton(loadFromXMLButton, RibbonElementPriority.MEDIUM); 662 addCommandButton(saveToXMLButton, RibbonElementPriority.MEDIUM); 663 addCommandButton(exportToXLSButton, RibbonElementPriority.MEDIUM); 664 665 setToolTipText("File operations for ROI"); 666 RibbonUtil.setRestrictiveResizePolicies(this); 667 } 668 669 public void updateButtonsState() 670 { 671 final Sequence seq = Icy.getMainInterface().getActiveSequence(); 672 final RoisPanel roisPanel = Icy.getMainInterface().getRoisPanel(); 673 674 loadFromXMLButton.setEnabled(seq != null); 675 saveToXMLButton.setEnabled((seq != null) && seq.hasSelectedROI()); 676 exportToXLSButton.setEnabled((roisPanel != null) && (roisPanel.getVisibleRois().size() > 0)); 677 } 678 } 679 680 static class ROIFillBand extends JRibbonBand 681 { 682 public static final String BAND_NAME = "Fill operation"; 683 684 final NumberTextField fillValueField; 685 // final IcyButton fillImage; 686 final IcyButton fillInterior; 687 final IcyButton fillExterior; 688 689 public ROIFillBand() 690 { 691 super(BAND_NAME, new IcyIcon("document")); 692 693 fillInterior = new IcyButton(RoiActions.fillInteriorAction); 694 fillInterior.setHorizontalAlignment(SwingConstants.LEADING); 695 fillInterior.setFlat(true); 696 fillExterior = new IcyButton(RoiActions.fillExteriorAction); 697 fillExterior.setHorizontalAlignment(SwingConstants.LEADING); 698 fillExterior.setFlat(true); 699 fillValueField = new NumberTextField(); 700 fillValueField.setHorizontalAlignment(SwingConstants.CENTER); 701 fillValueField.setToolTipText("Value used for filling"); 702 fillValueField.setNumericValue(0); 703 704 JRibbonComponent comp; 705 706 comp = new JRibbonComponent(fillInterior); 707 comp.setResizingAware(true); 708 comp.setHorizontalAlignment(HorizontalAlignment.FILL); 709 addRibbonComponent(comp); 710 comp = new JRibbonComponent(fillExterior); 711 comp.setResizingAware(true); 712 comp.setHorizontalAlignment(HorizontalAlignment.FILL); 713 addRibbonComponent(comp); 714 comp = new JRibbonComponent(fillValueField); 715 comp.setResizingAware(true); 716 comp.setHorizontalAlignment(HorizontalAlignment.FILL); 717 addRibbonComponent(comp); 718 719 setToolTipText("Fill operation for ROI"); 720 721 // better to do that to fix the ending gap for band containing wrapped components 722 ((BasicBandControlPanelUI) getControlPanel().getUI()).setLayoutGap(0); 723 RibbonUtil.setRestrictiveResizePolicies(this, 0); 724 updateButtonsState(); 725 } 726 727 public double getFillValue() 728 { 729 double value = fillValueField.getNumericValue(); 730 final Sequence sequence = Icy.getMainInterface().getActiveSequence(); 731 732 if ((sequence != null) && (!sequence.isFloatDataType())) 733 { 734 final double bounds[] = sequence.getDataType_().getDefaultBounds(); 735 736 // limit value to data type bounds 737 if (value < bounds[0]) 738 value = bounds[0]; 739 if (value > bounds[1]) 740 value = bounds[1]; 741 } 742 743 // set value back if incorrect 744 fillValueField.setNumericValue(value); 745 746 return value; 747 } 748 749 void updateButtonsState() 750 { 751 final Sequence seq = Icy.getMainInterface().getActiveSequence(); 752 final boolean enabled = (seq != null) && !seq.isEmpty() && seq.hasSelectedROI(); 753 754 fillValueField.setEnabled(enabled); 755 fillInterior.setEnabled(enabled); 756 fillExterior.setEnabled(enabled); 757 } 758 } 759 760 final ROI2DBand roi2dBand; 761 final ROI3DBand roi3dBand; 762 final ROIExtBand roiExtBand; 763 final ROIConversionBand roiConversionBand; 764 final ROISeparationBand roiSeparationBand; 765 // final ROIScaleBand roiScaleBand; 766 final ROIBooleanOpBand roiBooleanOpBand; 767 final ROIFillBand roiFillBand; 768 final ROIIOBand roiIOBand; 769 770 final CommandToggleButtonGroup buttonGroup; 771 final ActionListener buttonActionListener; 772 773 /** 774 * List of listeners 775 */ 776 private final EventListenerList listeners; 777 778 private Runnable buttonUpdater; 779 780 public ROITask() 781 { 782 super(NAME, new ROI2DBand(), new ROI3DBand(), new ROIExtBand(), new ROIConversionBand(), 783 new ROISeparationBand(), new ROIBooleanOpBand(), new ROIFillBand(), new ROIIOBand()); 784 // super(NAME,, new ROIRibbonBand()); 785 786 setResizeSequencingPolicy(new CoreRibbonResizeSequencingPolicies.CollapseFromLast(this)); 787 788 // get band 789 roi2dBand = (ROI2DBand) RibbonUtil.getBand(this, ROI2DBand.BAND_NAME); 790 roi3dBand = (ROI3DBand) RibbonUtil.getBand(this, ROI3DBand.BAND_NAME); 791 roiExtBand = (ROIExtBand) RibbonUtil.getBand(this, ROIExtBand.BAND_NAME); 792 roiConversionBand = (ROIConversionBand) RibbonUtil.getBand(this, ROIConversionBand.BAND_NAME); 793 roiSeparationBand = (ROISeparationBand) RibbonUtil.getBand(this, ROISeparationBand.BAND_NAME); 794 // roiScaleBand = (ROIScaleBand) RibbonUtil.getBand(this, ROIScaleBand.BAND_NAME); 795 roiBooleanOpBand = (ROIBooleanOpBand) RibbonUtil.getBand(this, ROIBooleanOpBand.BAND_NAME); 796 roiFillBand = (ROIFillBand) RibbonUtil.getBand(this, ROIFillBand.BAND_NAME); 797 roiIOBand = (ROIIOBand) RibbonUtil.getBand(this, ROIIOBand.BAND_NAME); 798 799 // create button state updater 800 buttonUpdater = new Runnable() 801 { 802 @Override 803 public void run() 804 { 805 // sleep a bit 806 ThreadUtil.sleep(1); 807 808 ThreadUtil.invokeNow(new Runnable() 809 { 810 @Override 811 public void run() 812 { 813 roi2dBand.updateButtonsState(); 814 roi3dBand.updateButtonsState(); 815 roiExtBand.updateButtonsState(); 816 roiConversionBand.updateButtonsState(); 817 roiSeparationBand.updateButtonsState(); 818 // roiScaleBand.updateButtonsState(); 819 roiBooleanOpBand.updateButtonsState(); 820 roiFillBand.updateButtonsState(); 821 roiIOBand.updateButtonsState(); 822 } 823 }); 824 } 825 }; 826 827 listeners = new EventListenerList(); 828 829 buttonActionListener = new ActionListener() 830 { 831 @Override 832 public void actionPerformed(ActionEvent e) 833 { 834 setSelectedButton((IcyCommandToggleButton) e.getSource(), true); 835 } 836 }; 837 838 // create button group 839 buttonGroup = new CommandToggleButtonGroup(); 840 buttonGroup.setAllowsClearingSelection(true); 841 842 // add action listener here 843 for (AbstractCommandButton button : RibbonUtil.getButtons(roi2dBand)) 844 button.addActionListener(buttonActionListener); 845 for (AbstractCommandButton button : RibbonUtil.getButtons(roi3dBand)) 846 button.addActionListener(buttonActionListener); 847 for (AbstractCommandButton button : RibbonUtil.getButtons(roiExtBand)) 848 button.addActionListener(buttonActionListener); 849 // cut button act as a selector 850 roiSeparationBand.cutButton.addActionListener(buttonActionListener); 851 852 for (AbstractCommandButton button : RibbonUtil.getButtons(roi2dBand)) 853 buttonGroup.add((IcyCommandToggleButton) button); 854 for (AbstractCommandButton button : RibbonUtil.getButtons(roi3dBand)) 855 buttonGroup.add((IcyCommandToggleButton) button); 856 for (AbstractCommandButton button : RibbonUtil.getButtons(roiExtBand)) 857 buttonGroup.add((IcyCommandToggleButton) button); 858 // cut button which act as a selector 859 buttonGroup.add(roiSeparationBand.cutButton); 860 861 PluginLoader.addListener(this); 862 } 863 864 protected void refreshROIButtons() 865 { 866 // refresh 2D ROI buttons 867 roi2dBand.setHidden(setROIFromPlugins(roi2dBand.pluginButtons, roi2dBand, ROI2DBand.getROIPlugins(), 868 buttonGroup, buttonActionListener)); 869 // refresh 3D ROI buttons 870 roi3dBand.setHidden(setROIFromPlugins(roi3dBand.pluginButtons, roi3dBand, ROI3DBand.getROIPlugins(), 871 buttonGroup, buttonActionListener)); 872 // refresh external ROI buttons 873 roiExtBand.setHidden(setROIFromPlugins(roiExtBand.pluginButtons, roiExtBand, ROIExtBand.getROIPlugins(), 874 buttonGroup, buttonActionListener)); 875 876 roi2dBand.updateButtonsState(); 877 roi3dBand.updateButtonsState(); 878 roiExtBand.updateButtonsState(); 879 } 880 881 protected static boolean setROIFromPlugins(List<IcyCommandToggleButton> pluginButtons, JRibbonBand band, 882 List<PluginDescriptor> roiPlugins, CommandToggleButtonGroup buttonGroup, ActionListener al) 883 { 884 // remove previous plugin buttons 885 for (IcyCommandToggleButton button : pluginButtons) 886 { 887 if (al != null) 888 button.removeActionListener(al); 889 band.removeCommandButton(button); 890 if (buttonGroup != null) 891 buttonGroup.remove(button); 892 } 893 pluginButtons.clear(); 894 895 for (PluginDescriptor plugin : roiPlugins) 896 { 897 final IcyCommandToggleButton button = PluginCommandButton.createToggleButton(plugin, false, 898 plugin.isKernelPlugin()); 899 900 if (plugin.getClassName().equals(ROI2DAreaPlugin.class.getName())) 901 band.addCommandButton(button, RibbonElementPriority.TOP); 902 else 903 band.addCommandButton(button, RibbonElementPriority.MEDIUM); 904 905 if (al != null) 906 button.addActionListener(al); 907 if (buttonGroup != null) 908 buttonGroup.add(button); 909 910 pluginButtons.add(button); 911 } 912 913 return pluginButtons.isEmpty(); 914 } 915 916 protected IcyCommandToggleButton getButtonFromName(String name) 917 { 918 if (StringUtil.isEmpty(name)) 919 return null; 920 921 for (AbstractCommandButton button : RibbonUtil.getButtons(roi2dBand)) 922 if (name.equals(button.getName())) 923 return (IcyCommandToggleButton) button; 924 for (AbstractCommandButton button : RibbonUtil.getButtons(roi3dBand)) 925 if (name.equals(button.getName())) 926 return (IcyCommandToggleButton) button; 927 for (AbstractCommandButton button : RibbonUtil.getButtons(roiExtBand)) 928 if (name.equals(button.getName())) 929 return (IcyCommandToggleButton) button; 930 931 return null; 932 } 933 934 /** 935 * Returns true if current selected tool is ROI type tool. 936 */ 937 public boolean isROITool() 938 { 939 // currently we only have ROI tool 940 // so as soon selected is not null it is ROI tool 941 return getSelected() != null; 942 } 943 944 /** 945 * Returns the current selected button (can be <code>null</code>). 946 */ 947 protected JCommandToggleButton getSelectedButton() 948 { 949 return buttonGroup.getSelected(); 950 } 951 952 /** 953 * Sets the current selected button (can be <code>null</code>). 954 */ 955 protected void setSelectedButton(JCommandToggleButton button, boolean force) 956 { 957 if (force || (getSelectedButton() != button)) 958 { 959 // select the button 960 if (button != null) 961 buttonGroup.setSelected(button, true); 962 else 963 buttonGroup.clearSelection(); 964 965 // notify tool change 966 toolChanged(getSelected()); 967 } 968 } 969 970 /** 971 * Returns the current selected tool.<br> 972 * It can be null if no tool is currently selected. 973 */ 974 public String getSelected() 975 { 976 final JCommandToggleButton button = getSelectedButton(); 977 978 if (button != null) 979 return button.getName(); 980 981 return null; 982 } 983 984 /** 985 * Sets the current selected tool.<br> 986 * If <i>toolName</i> is a invalid tool name or <code>null</code> then no tool is selected. 987 */ 988 public void setSelected(String value) 989 { 990 setSelectedButton(getButtonFromName(value), false); 991 } 992 993 public void toolChanged(String toolName) 994 { 995 fireChangedEvent(toolName); 996 } 997 998 public double getRadius() 999 { 1000 return roiConversionBand.getRadius(); 1001 } 1002 1003 public double getFillValue() 1004 { 1005 return roiFillBand.getFillValue(); 1006 } 1007 1008 /** 1009 * Add a listener 1010 * 1011 * @param listener 1012 */ 1013 public void addListener(ROITaskListener listener) 1014 { 1015 listeners.add(ROITaskListener.class, listener); 1016 } 1017 1018 /** 1019 * Remove a listener 1020 * 1021 * @param listener 1022 */ 1023 public void removeListener(ROITaskListener listener) 1024 { 1025 listeners.remove(ROITaskListener.class, listener); 1026 } 1027 1028 /** 1029 * @param toolName 1030 */ 1031 void fireChangedEvent(String toolName) 1032 { 1033 for (ROITaskListener listener : listeners.getListeners(ROITaskListener.class)) 1034 listener.toolChanged(toolName); 1035 } 1036 1037 /** 1038 * call this method on sequence activation change 1039 */ 1040 public void onSequenceActivationChange() 1041 { 1042 ThreadUtil.runSingle(buttonUpdater); 1043 } 1044 1045 /** 1046 * call this method on sequence ROI change 1047 */ 1048 public void onSequenceChange() 1049 { 1050 ThreadUtil.runSingle(buttonUpdater); 1051 } 1052 1053 @Override 1054 public void pluginLoaderChanged(PluginLoaderEvent e) 1055 { 1056 ThreadUtil.invokeLater(new Runnable() 1057 { 1058 @Override 1059 public void run() 1060 { 1061 refreshROIButtons(); 1062 } 1063 }); 1064 } 1065}