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.inspector; 020 021import java.awt.Color; 022import java.awt.Dimension; 023import java.awt.GridBagConstraints; 024import java.awt.GridBagLayout; 025import java.awt.Insets; 026import java.awt.event.ActionEvent; 027import java.awt.event.ActionListener; 028import java.lang.ref.Reference; 029import java.lang.ref.WeakReference; 030import java.util.ArrayList; 031import java.util.List; 032import java.util.concurrent.Semaphore; 033 034import javax.swing.BoxLayout; 035import javax.swing.JButton; 036import javax.swing.JCheckBox; 037import javax.swing.JComponent; 038import javax.swing.JLabel; 039import javax.swing.JPanel; 040import javax.swing.JSlider; 041import javax.swing.JSpinner; 042import javax.swing.SpinnerNumberModel; 043import javax.swing.SwingConstants; 044import javax.swing.UIManager; 045import javax.swing.border.TitledBorder; 046import javax.swing.event.ChangeEvent; 047import javax.swing.event.ChangeListener; 048 049import icy.clipboard.Clipboard; 050import icy.clipboard.Clipboard.ClipboardListener; 051import icy.gui.component.AbstractRoisPanel; 052import icy.gui.component.IcyTextField; 053import icy.gui.component.IcyTextField.TextChangeListener; 054import icy.gui.component.SpecialValueSpinner; 055import icy.gui.component.button.ColorChooserButton; 056import icy.gui.component.button.ColorChooserButton.ColorChangeListener; 057import icy.gui.component.model.SpecialValueSpinnerModel; 058import icy.main.Icy; 059import icy.math.MathUtil; 060import icy.roi.ROI; 061import icy.roi.ROIEvent; 062import icy.roi.edit.BoundsROIEdit; 063import icy.roi.edit.BoundsROIsEdit; 064import icy.roi.edit.PositionROIEdit; 065import icy.roi.edit.PositionROIsEdit; 066import icy.roi.edit.PropertyROIsEdit; 067import icy.sequence.Sequence; 068import icy.system.thread.ThreadUtil; 069import icy.type.point.Point5D; 070import icy.type.rectangle.Rectangle5D; 071import icy.util.StringUtil; 072 073/** 074 * @author Stephane 075 */ 076public class RoiControlPanel extends JPanel 077 implements ColorChangeListener, TextChangeListener, ClipboardListener, ChangeListener, ActionListener 078{ 079 /** 080 * 081 */ 082 private static final long serialVersionUID = 7403770406075917063L; 083 084 // GUI 085 JLabel posCFieldLabel; 086 JLabel posTFieldLabel; 087 JLabel posZFieldLabel; 088 JLabel posZSpinnerLabel; 089 JLabel posTSpinnerLabel; 090 JLabel posCSpinnerLabel; 091 IcyTextField posXField; 092 IcyTextField posYField; 093 IcyTextField posTField; 094 IcyTextField posZField; 095 IcyTextField sizeXField; 096 IcyTextField sizeZField; 097 IcyTextField sizeYField; 098 IcyTextField sizeTField; 099 IcyTextField posCField; 100 IcyTextField sizeCField; 101 SpecialValueSpinner posZSpinner; 102 SpecialValueSpinner posTSpinner; 103 SpecialValueSpinner posCSpinner; 104 ColorChooserButton colorButton; 105 JSlider alphaSlider; 106 JSpinner strokeSpinner; 107 JCheckBox displayNameCheckBox; 108 JButton setAsDefaultBtn; 109 110 // internals 111 private final AbstractRoisPanel roisPanel; 112 final Semaphore modifyingRoi; 113 private final List<Reference<ROI>> modifiedRois; 114 final Runnable roiActionsRefresher; 115 final Runnable roiPropertiesRefresher; 116 117 public RoiControlPanel(AbstractRoisPanel roisPanel) 118 { 119 super(); 120 121 this.roisPanel = roisPanel; 122 123 modifyingRoi = new Semaphore(1); 124 modifiedRois = new ArrayList<Reference<ROI>>(); 125 126 roiActionsRefresher = new Runnable() 127 { 128 @Override 129 public void run() 130 { 131 refreshROIActionsInternal(); 132 } 133 }; 134 roiPropertiesRefresher = new Runnable() 135 { 136 @Override 137 public void run() 138 { 139 refreshROIPropertiesInternal(); 140 } 141 }; 142 143 initialize(); 144 145 colorButton.addColorChangeListener(this); 146 strokeSpinner.addChangeListener(this); 147 alphaSlider.addChangeListener(this); 148 displayNameCheckBox.addActionListener(this); 149 150 posXField.addTextChangeListener(this); 151 posYField.addTextChangeListener(this); 152 posZField.addTextChangeListener(this); 153 posZSpinner.addChangeListener(this); 154 posTField.addTextChangeListener(this); 155 posTSpinner.addChangeListener(this); 156 posCField.addTextChangeListener(this); 157 posCSpinner.addChangeListener(this); 158 sizeXField.addTextChangeListener(this); 159 sizeYField.addTextChangeListener(this); 160 sizeZField.addTextChangeListener(this); 161 sizeTField.addTextChangeListener(this); 162 sizeCField.addTextChangeListener(this); 163 164 setAsDefaultBtn.addActionListener(this); 165 166 Clipboard.addListener(this); 167 168 refreshROIActionsInternal(); 169 refreshROIPropertiesInternal(); 170 } 171 172 private void initialize() 173 { 174 setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS)); 175 176 JPanel actionPanel = new JPanel(); 177 actionPanel.setBorder(new TitledBorder(UIManager.getBorder("TitledBorder.border"), "Properties", 178 TitledBorder.LEADING, TitledBorder.TOP, null, new Color(0, 0, 0))); 179 add(actionPanel); 180 GridBagLayout gbl_actionPanel = new GridBagLayout(); 181 gbl_actionPanel.columnWidths = new int[] {0, 0, 0, 60, 0, 0}; 182 gbl_actionPanel.rowHeights = new int[] {0, 0, 0}; 183 gbl_actionPanel.columnWeights = new double[] {0.0, 1.0, 0.0, 1.0, 0.0, Double.MIN_VALUE}; 184 gbl_actionPanel.rowWeights = new double[] {0.0, 0.0, Double.MIN_VALUE}; 185 actionPanel.setLayout(gbl_actionPanel); 186 187 final JLabel lblColor = new JLabel("Color"); 188 GridBagConstraints gbc_lblColor = new GridBagConstraints(); 189 gbc_lblColor.anchor = GridBagConstraints.WEST; 190 gbc_lblColor.insets = new Insets(0, 0, 5, 5); 191 gbc_lblColor.gridx = 0; 192 gbc_lblColor.gridy = 0; 193 actionPanel.add(lblColor, gbc_lblColor); 194 195 colorButton = new ColorChooserButton(); 196 GridBagConstraints gbc_colorButton = new GridBagConstraints(); 197 gbc_colorButton.fill = GridBagConstraints.HORIZONTAL; 198 gbc_colorButton.insets = new Insets(0, 0, 5, 5); 199 gbc_colorButton.gridx = 1; 200 gbc_colorButton.gridy = 0; 201 actionPanel.add(colorButton, gbc_colorButton); 202 colorButton.setToolTipText("ROI color"); 203 204 JLabel lblContentOpacity = new JLabel("Opacity"); 205 GridBagConstraints gbc_lblContentOpacity = new GridBagConstraints(); 206 gbc_lblContentOpacity.anchor = GridBagConstraints.WEST; 207 gbc_lblContentOpacity.insets = new Insets(0, 0, 5, 5); 208 gbc_lblContentOpacity.gridx = 2; 209 gbc_lblContentOpacity.gridy = 0; 210 actionPanel.add(lblContentOpacity, gbc_lblContentOpacity); 211 212 alphaSlider = new JSlider(); 213 alphaSlider.setFocusable(false); 214 GridBagConstraints gbc_alphaSlider = new GridBagConstraints(); 215 gbc_alphaSlider.gridwidth = 2; 216 gbc_alphaSlider.fill = GridBagConstraints.HORIZONTAL; 217 gbc_alphaSlider.insets = new Insets(0, 0, 5, 0); 218 gbc_alphaSlider.gridx = 3; 219 gbc_alphaSlider.gridy = 0; 220 actionPanel.add(alphaSlider, gbc_alphaSlider); 221 alphaSlider.setPreferredSize(new Dimension(80, 20)); 222 alphaSlider.setMaximumSize(new Dimension(32767, 20)); 223 alphaSlider.setMinimumSize(new Dimension(36, 20)); 224 alphaSlider.setToolTipText("ROI content opacity"); 225 226 JLabel lblNewLabel = new JLabel("Stroke"); 227 GridBagConstraints gbc_lblNewLabel = new GridBagConstraints(); 228 gbc_lblNewLabel.anchor = GridBagConstraints.WEST; 229 gbc_lblNewLabel.insets = new Insets(0, 0, 0, 5); 230 gbc_lblNewLabel.gridx = 0; 231 gbc_lblNewLabel.gridy = 1; 232 actionPanel.add(lblNewLabel, gbc_lblNewLabel); 233 234 strokeSpinner = new JSpinner(); 235 strokeSpinner.setToolTipText("ROI stroke size (for visualization only)"); 236 strokeSpinner.setModel(new SpinnerNumberModel(1.0, 1.0, 9.0, 1.0)); 237 GridBagConstraints gbc_strokeSpinner = new GridBagConstraints(); 238 gbc_strokeSpinner.fill = GridBagConstraints.HORIZONTAL; 239 gbc_strokeSpinner.insets = new Insets(0, 0, 0, 5); 240 gbc_strokeSpinner.gridx = 1; 241 gbc_strokeSpinner.gridy = 1; 242 actionPanel.add(strokeSpinner, gbc_strokeSpinner); 243 244 displayNameCheckBox = new JCheckBox("Show name"); 245 displayNameCheckBox.setToolTipText("Show the ROI name"); 246 displayNameCheckBox.setMargin(new Insets(2, 0, 2, 2)); 247 displayNameCheckBox.setIconTextGap(10); 248 displayNameCheckBox.setHorizontalTextPosition(SwingConstants.LEADING); 249 GridBagConstraints gbc_displayNameCheckBox = new GridBagConstraints(); 250 gbc_displayNameCheckBox.anchor = GridBagConstraints.WEST; 251 gbc_displayNameCheckBox.insets = new Insets(0, 0, 0, 5); 252 gbc_displayNameCheckBox.gridwidth = 2; 253 gbc_displayNameCheckBox.gridx = 2; 254 gbc_displayNameCheckBox.gridy = 1; 255 actionPanel.add(displayNameCheckBox, gbc_displayNameCheckBox); 256 257 setAsDefaultBtn = new JButton("As default"); 258 setAsDefaultBtn.setEnabled(false); 259 setAsDefaultBtn.setMargin(new Insets(2, 2, 2, 2)); 260 setAsDefaultBtn.setIconTextGap(0); 261 setAsDefaultBtn 262 .setToolTipText("Set the current color, opacity, stroke and show name values as the default settings"); 263 GridBagConstraints gbc_setAsDefaultBtn = new GridBagConstraints(); 264 gbc_setAsDefaultBtn.anchor = GridBagConstraints.EAST; 265 gbc_setAsDefaultBtn.gridx = 4; 266 gbc_setAsDefaultBtn.gridy = 1; 267 actionPanel.add(setAsDefaultBtn, gbc_setAsDefaultBtn); 268 269 JPanel positionAndSizePanel = new JPanel(); 270 add(positionAndSizePanel); 271 GridBagLayout gbl_positionAndSizePanel = new GridBagLayout(); 272 gbl_positionAndSizePanel.columnWidths = new int[] {0, 0, 0}; 273 gbl_positionAndSizePanel.rowHeights = new int[] {0, 0}; 274 gbl_positionAndSizePanel.columnWeights = new double[] {1.0, 1.0, Double.MIN_VALUE}; 275 gbl_positionAndSizePanel.rowWeights = new double[] {0.0, Double.MIN_VALUE}; 276 positionAndSizePanel.setLayout(gbl_positionAndSizePanel); 277 278 JPanel positionPanel = new JPanel(); 279 positionPanel.setBorder(new TitledBorder(UIManager.getBorder("TitledBorder.border"), "Position", 280 TitledBorder.LEFT, TitledBorder.TOP, null, new Color(0, 0, 0))); 281 GridBagConstraints gbc_positionPanel = new GridBagConstraints(); 282 gbc_positionPanel.anchor = GridBagConstraints.NORTH; 283 gbc_positionPanel.insets = new Insets(0, 0, 0, 5); 284 gbc_positionPanel.fill = GridBagConstraints.HORIZONTAL; 285 gbc_positionPanel.gridx = 0; 286 gbc_positionPanel.gridy = 0; 287 positionAndSizePanel.add(positionPanel, gbc_positionPanel); 288 GridBagLayout gbl_positionPanel = new GridBagLayout(); 289 gbl_positionPanel.columnWidths = new int[] {20, 0, 0}; 290 gbl_positionPanel.rowHeights = new int[] {0, 0, 0, 0, 0, 0, 0, 0, 0}; 291 gbl_positionPanel.columnWeights = new double[] {0.0, 1.0, Double.MIN_VALUE}; 292 gbl_positionPanel.rowWeights = new double[] {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE}; 293 positionPanel.setLayout(gbl_positionPanel); 294 295 final JLabel lblX = new JLabel("X"); 296 GridBagConstraints gbc_lblX = new GridBagConstraints(); 297 gbc_lblX.insets = new Insets(0, 0, 5, 5); 298 gbc_lblX.gridx = 0; 299 gbc_lblX.gridy = 0; 300 positionPanel.add(lblX, gbc_lblX); 301 302 posXField = new IcyTextField(); 303 GridBagConstraints gbc_posXField = new GridBagConstraints(); 304 gbc_posXField.insets = new Insets(0, 0, 5, 0); 305 gbc_posXField.fill = GridBagConstraints.HORIZONTAL; 306 gbc_posXField.gridx = 1; 307 gbc_posXField.gridy = 0; 308 positionPanel.add(posXField, gbc_posXField); 309 posXField.setToolTipText("X position of the ROI"); 310 posXField.setColumns(8); 311 312 final JLabel lblY = new JLabel("Y"); 313 GridBagConstraints gbc_lblY = new GridBagConstraints(); 314 gbc_lblY.insets = new Insets(0, 0, 5, 5); 315 gbc_lblY.gridx = 0; 316 gbc_lblY.gridy = 1; 317 positionPanel.add(lblY, gbc_lblY); 318 319 posYField = new IcyTextField(); 320 GridBagConstraints gbc_posYField = new GridBagConstraints(); 321 gbc_posYField.fill = GridBagConstraints.HORIZONTAL; 322 gbc_posYField.insets = new Insets(0, 0, 5, 0); 323 gbc_posYField.gridx = 1; 324 gbc_posYField.gridy = 1; 325 positionPanel.add(posYField, gbc_posYField); 326 posYField.setToolTipText("Y position of the ROI"); 327 posYField.setColumns(8); 328 329 posZFieldLabel = new JLabel("Z"); 330 GridBagConstraints gbc_posZFieldLabel = new GridBagConstraints(); 331 gbc_posZFieldLabel.insets = new Insets(0, 0, 5, 5); 332 gbc_posZFieldLabel.gridx = 0; 333 gbc_posZFieldLabel.gridy = 2; 334 positionPanel.add(posZFieldLabel, gbc_posZFieldLabel); 335 336 posZField = new IcyTextField(); 337 GridBagConstraints gbc_posZField = new GridBagConstraints(); 338 gbc_posZField.fill = GridBagConstraints.HORIZONTAL; 339 gbc_posZField.insets = new Insets(0, 0, 5, 0); 340 gbc_posZField.gridx = 1; 341 gbc_posZField.gridy = 2; 342 positionPanel.add(posZField, gbc_posZField); 343 posZField.setVisible(false); 344 posZField.setToolTipText("Z position of the ROI"); 345 posZField.setColumns(8); 346 347 posZSpinnerLabel = new JLabel("Z"); 348 GridBagConstraints gbc_posZSpinnerLabel = new GridBagConstraints(); 349 gbc_posZSpinnerLabel.insets = new Insets(0, 0, 5, 5); 350 gbc_posZSpinnerLabel.gridx = 0; 351 gbc_posZSpinnerLabel.gridy = 3; 352 positionPanel.add(posZSpinnerLabel, gbc_posZSpinnerLabel); 353 354 posZSpinner = new SpecialValueSpinner(new SpecialValueSpinnerModel(-1, -1, 0, 1, -1, "ALL")); 355 GridBagConstraints gbc_posZSpinner = new GridBagConstraints(); 356 gbc_posZSpinner.fill = GridBagConstraints.HORIZONTAL; 357 gbc_posZSpinner.insets = new Insets(0, 0, 5, 0); 358 gbc_posZSpinner.gridx = 1; 359 gbc_posZSpinner.gridy = 3; 360 positionPanel.add(posZSpinner, gbc_posZSpinner); 361 posZSpinner.setToolTipText("Attach the ROI to a specific Z slice (set to -1 for ALL)"); 362 363 posTFieldLabel = new JLabel("T"); 364 GridBagConstraints gbc_posTFieldLabel = new GridBagConstraints(); 365 gbc_posTFieldLabel.insets = new Insets(0, 0, 5, 5); 366 gbc_posTFieldLabel.gridx = 0; 367 gbc_posTFieldLabel.gridy = 4; 368 positionPanel.add(posTFieldLabel, gbc_posTFieldLabel); 369 370 posTField = new IcyTextField(); 371 GridBagConstraints gbc_posTField = new GridBagConstraints(); 372 gbc_posTField.fill = GridBagConstraints.HORIZONTAL; 373 gbc_posTField.insets = new Insets(0, 0, 5, 0); 374 gbc_posTField.gridx = 1; 375 gbc_posTField.gridy = 4; 376 positionPanel.add(posTField, gbc_posTField); 377 posTField.setVisible(false); 378 posTField.setToolTipText("T position of the ROI"); 379 posTField.setColumns(8); 380 381 posTSpinnerLabel = new JLabel("T"); 382 GridBagConstraints gbc_posTSpinnerLabel = new GridBagConstraints(); 383 gbc_posTSpinnerLabel.insets = new Insets(0, 0, 5, 5); 384 gbc_posTSpinnerLabel.gridx = 0; 385 gbc_posTSpinnerLabel.gridy = 5; 386 positionPanel.add(posTSpinnerLabel, gbc_posTSpinnerLabel); 387 388 posTSpinner = new SpecialValueSpinner(new SpecialValueSpinnerModel(-1, -1, 0, 1, -1, "ALL")); 389 GridBagConstraints gbc_posTSpinner = new GridBagConstraints(); 390 gbc_posTSpinner.fill = GridBagConstraints.HORIZONTAL; 391 gbc_posTSpinner.insets = new Insets(0, 0, 5, 0); 392 gbc_posTSpinner.gridx = 1; 393 gbc_posTSpinner.gridy = 5; 394 positionPanel.add(posTSpinner, gbc_posTSpinner); 395 posTSpinner.setToolTipText("Attach the ROI to a specific T frame (set to -1 for ALL)"); 396 397 posCFieldLabel = new JLabel("C"); 398 GridBagConstraints gbc_posCFieldLabel = new GridBagConstraints(); 399 gbc_posCFieldLabel.insets = new Insets(0, 0, 5, 5); 400 gbc_posCFieldLabel.gridx = 0; 401 gbc_posCFieldLabel.gridy = 6; 402 positionPanel.add(posCFieldLabel, gbc_posCFieldLabel); 403 404 posCField = new IcyTextField(); 405 GridBagConstraints gbc_posCField = new GridBagConstraints(); 406 gbc_posCField.fill = GridBagConstraints.HORIZONTAL; 407 gbc_posCField.insets = new Insets(0, 0, 5, 0); 408 gbc_posCField.gridx = 1; 409 gbc_posCField.gridy = 6; 410 positionPanel.add(posCField, gbc_posCField); 411 posCField.setVisible(false); 412 posCField.setToolTipText("C position of the ROI"); 413 posCField.setColumns(8); 414 415 posCSpinnerLabel = new JLabel("C"); 416 GridBagConstraints gbc_posCSpinnerLabel = new GridBagConstraints(); 417 gbc_posCSpinnerLabel.insets = new Insets(0, 0, 0, 5); 418 gbc_posCSpinnerLabel.gridx = 0; 419 gbc_posCSpinnerLabel.gridy = 7; 420 positionPanel.add(posCSpinnerLabel, gbc_posCSpinnerLabel); 421 422 posCSpinner = new SpecialValueSpinner(new SpecialValueSpinnerModel(-1, -1, 0, 1, -1, "ALL")); 423 GridBagConstraints gbc_posCSpinner = new GridBagConstraints(); 424 gbc_posCSpinner.fill = GridBagConstraints.HORIZONTAL; 425 gbc_posCSpinner.gridx = 1; 426 gbc_posCSpinner.gridy = 7; 427 positionPanel.add(posCSpinner, gbc_posCSpinner); 428 posCSpinner.setToolTipText("Attach the ROI to a specific C channel (set to -1 for ALL)"); 429 430 JPanel sizePanel = new JPanel(); 431 sizePanel.setBorder(new TitledBorder(null, "Dimension", TitledBorder.LEADING, TitledBorder.TOP, null, null)); 432 GridBagConstraints gbc_sizePanel = new GridBagConstraints(); 433 gbc_sizePanel.anchor = GridBagConstraints.NORTH; 434 gbc_sizePanel.fill = GridBagConstraints.HORIZONTAL; 435 gbc_sizePanel.gridx = 1; 436 gbc_sizePanel.gridy = 0; 437 positionAndSizePanel.add(sizePanel, gbc_sizePanel); 438 439 GridBagLayout gbl_sizePanel = new GridBagLayout(); 440 gbl_sizePanel.columnWidths = new int[] {20, 0, 0}; 441 gbl_sizePanel.rowHeights = new int[] {0, 0, 0, 0, 0, 0}; 442 gbl_sizePanel.columnWeights = new double[] {0.0, 1.0, Double.MIN_VALUE}; 443 gbl_sizePanel.rowWeights = new double[] {0.0, 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE}; 444 sizePanel.setLayout(gbl_sizePanel); 445 446 final JLabel lblNewLabel_2 = new JLabel("X"); 447 GridBagConstraints gbc_lblNewLabel_2 = new GridBagConstraints(); 448 gbc_lblNewLabel_2.insets = new Insets(0, 0, 5, 5); 449 gbc_lblNewLabel_2.gridx = 0; 450 gbc_lblNewLabel_2.gridy = 0; 451 sizePanel.add(lblNewLabel_2, gbc_lblNewLabel_2); 452 453 sizeXField = new IcyTextField(); 454 GridBagConstraints gbc_sizeXField = new GridBagConstraints(); 455 gbc_sizeXField.insets = new Insets(0, 0, 5, 0); 456 gbc_sizeXField.fill = GridBagConstraints.HORIZONTAL; 457 gbc_sizeXField.gridx = 1; 458 gbc_sizeXField.gridy = 0; 459 sizePanel.add(sizeXField, gbc_sizeXField); 460 sizeXField.setToolTipText("Size of dimension X for the ROI"); 461 sizeXField.setColumns(8); 462 463 final JLabel lblY_1 = new JLabel("Y"); 464 GridBagConstraints gbc_lblY_1 = new GridBagConstraints(); 465 gbc_lblY_1.insets = new Insets(0, 0, 5, 5); 466 gbc_lblY_1.gridx = 0; 467 gbc_lblY_1.gridy = 1; 468 sizePanel.add(lblY_1, gbc_lblY_1); 469 470 sizeYField = new IcyTextField(); 471 GridBagConstraints gbc_sizeYField = new GridBagConstraints(); 472 gbc_sizeYField.fill = GridBagConstraints.HORIZONTAL; 473 gbc_sizeYField.insets = new Insets(0, 0, 5, 0); 474 gbc_sizeYField.gridx = 1; 475 gbc_sizeYField.gridy = 1; 476 sizePanel.add(sizeYField, gbc_sizeYField); 477 sizeYField.setToolTipText("Size of dimension Y for the ROI"); 478 sizeYField.setColumns(8); 479 480 JLabel sizeZFieldLabel = new JLabel("Z"); 481 GridBagConstraints gbc_sizeZFieldLabel = new GridBagConstraints(); 482 gbc_sizeZFieldLabel.insets = new Insets(0, 0, 5, 5); 483 gbc_sizeZFieldLabel.gridx = 0; 484 gbc_sizeZFieldLabel.gridy = 2; 485 sizePanel.add(sizeZFieldLabel, gbc_sizeZFieldLabel); 486 487 sizeZField = new IcyTextField(); 488 GridBagConstraints gbc_sizeZField = new GridBagConstraints(); 489 gbc_sizeZField.fill = GridBagConstraints.HORIZONTAL; 490 gbc_sizeZField.insets = new Insets(0, 0, 5, 0); 491 gbc_sizeZField.gridx = 1; 492 gbc_sizeZField.gridy = 2; 493 sizePanel.add(sizeZField, gbc_sizeZField); 494 sizeZField.setToolTipText("Size of dimension Z for the ROI"); 495 sizeZField.setColumns(8); 496 497 JLabel sizeTFieldLabel = new JLabel("T"); 498 GridBagConstraints gbc_sizeTFieldLabel = new GridBagConstraints(); 499 gbc_sizeTFieldLabel.insets = new Insets(0, 0, 5, 5); 500 gbc_sizeTFieldLabel.gridx = 0; 501 gbc_sizeTFieldLabel.gridy = 3; 502 sizePanel.add(sizeTFieldLabel, gbc_sizeTFieldLabel); 503 504 sizeTField = new IcyTextField(); 505 GridBagConstraints gbc_sizeTField = new GridBagConstraints(); 506 gbc_sizeTField.fill = GridBagConstraints.HORIZONTAL; 507 gbc_sizeTField.insets = new Insets(0, 0, 5, 0); 508 gbc_sizeTField.gridx = 1; 509 gbc_sizeTField.gridy = 3; 510 sizePanel.add(sizeTField, gbc_sizeTField); 511 sizeTField.setToolTipText("Size of dimension T for the ROI"); 512 sizeTField.setColumns(8); 513 514 JLabel sizeCFieldLabel = new JLabel("C"); 515 GridBagConstraints gbc_sizeCFieldLabel = new GridBagConstraints(); 516 gbc_sizeCFieldLabel.insets = new Insets(0, 0, 0, 5); 517 gbc_sizeCFieldLabel.gridx = 0; 518 gbc_sizeCFieldLabel.gridy = 4; 519 sizePanel.add(sizeCFieldLabel, gbc_sizeCFieldLabel); 520 521 sizeCField = new IcyTextField(); 522 GridBagConstraints gbc_sizeCField = new GridBagConstraints(); 523 gbc_sizeCField.fill = GridBagConstraints.HORIZONTAL; 524 gbc_sizeCField.gridx = 1; 525 gbc_sizeCField.gridy = 4; 526 sizePanel.add(sizeCField, gbc_sizeCField); 527 sizeCField.setToolTipText("Size of dimension C for the ROI"); 528 sizeCField.setColumns(8); 529 } 530 531 public void selectionChanged() 532 { 533 refreshROIActionsAndProperties(); 534 } 535 536 /** 537 * Get the visible ROI in the ROI control panel 538 */ 539 List<ROI> getVisibleRois() 540 { 541 return roisPanel.getVisibleRois(); 542 } 543 544 /** 545 * Get all selected ROIs 546 */ 547 List<ROI> getSelectedRois() 548 { 549 return getSelectedRois(true); 550 } 551 552 /** 553 * Get the selected ROI in the ROI control panel. 554 * 555 * @param wantReadOnly 556 * If <code>true</code> the returned list will also contains ROI in Read-Only state 557 */ 558 List<ROI> getSelectedRois(boolean wantReadOnly) 559 { 560 final List<ROI> selected = roisPanel.getSelectedRois(); 561 562 if (wantReadOnly) 563 return selected; 564 565 final List<ROI> result = new ArrayList<ROI>(selected.size()); 566 567 for (ROI roi : selected) 568 if (!roi.isReadOnly()) 569 result.add(roi); 570 571 return result; 572 } 573 574 static double formatPosition(double pos, double size) 575 { 576 // special case of infinite dimension 577 if (size == Double.POSITIVE_INFINITY) 578 return -1d; 579 580 return MathUtil.roundSignificant(pos, 5, true); 581 } 582 583 static String getPositionAsString(double pos, double size) 584 { 585 // special case of infinite dimension 586 if (size == Double.POSITIVE_INFINITY) 587 return "all"; 588 589 return StringUtil.toString(MathUtil.roundSignificant(pos, 5, true)); 590 } 591 592 static double formatSize(double value) 593 { 594 return MathUtil.roundSignificant(value, 5, true); 595 } 596 597 static String getSizeAsString(double value) 598 { 599 // special case of infinite dimension 600 if (value == Double.POSITIVE_INFINITY) 601 return MathUtil.INFINITE_STRING; 602 603 return StringUtil.toString(formatSize(value)); 604 } 605 606 /** 607 * Refresh the ROI actions state. 608 */ 609 public void refreshROIActions() 610 { 611 ThreadUtil.runSingle(roiActionsRefresher); 612 } 613 614 /** 615 * Refresh the ROI actions state (internal) 616 */ 617 void refreshROIActionsInternal() 618 { 619 final Sequence sequence = Icy.getMainInterface().getActiveSequence(); 620 final List<ROI> selectedRois = getSelectedRois(); 621 final ROI roi = (selectedRois.size() > 0) ? selectedRois.get(0) : null; 622 623 boolean canSetPos = false; 624 boolean canSetBnd = false; 625 boolean readOnly = true; 626 // set read only flag 627 for (ROI r : selectedRois) 628 { 629 readOnly &= r.isReadOnly(); 630 canSetPos |= r.canSetPosition(); 631 canSetBnd |= r.canSetBounds(); 632 } 633 634 final boolean hasSelected = (roi != null); 635 final boolean canSetPosition = canSetPos; 636 final boolean canSetBounds = canSetBnd; 637 final boolean editable = !readOnly; 638 final int dim = (roi != null) ? roi.getDimension() : 0; 639 640 // wait a bit to avoid eating too much time with refresh 641 ThreadUtil.sleep(1); 642 643 ThreadUtil.invokeNow(new Runnable() 644 { 645 @Override 646 public void run() 647 { 648 // modifyingRoi.acquireUninterruptibly(); 649 if (modifyingRoi.tryAcquire()) 650 { 651 try 652 { 653 if (sequence != null) 654 { 655 ((SpecialValueSpinnerModel) posZSpinner.getModel()) 656 .setMaximum(Integer.valueOf(sequence.getSizeZ() - 1)); 657 ((SpecialValueSpinnerModel) posTSpinner.getModel()) 658 .setMaximum(Integer.valueOf(sequence.getSizeT() - 1)); 659 ((SpecialValueSpinnerModel) posCSpinner.getModel()) 660 .setMaximum(Integer.valueOf(sequence.getSizeC() - 1)); 661 } 662 else 663 { 664 ((SpecialValueSpinnerModel) posZSpinner.getModel()).setMaximum(Integer.valueOf(0)); 665 ((SpecialValueSpinnerModel) posTSpinner.getModel()).setMaximum(Integer.valueOf(0)); 666 ((SpecialValueSpinnerModel) posCSpinner.getModel()).setMaximum(Integer.valueOf(0)); 667 } 668 } 669 finally 670 { 671 modifyingRoi.release(); 672 } 673 } 674 675 posXField.setEnabled(canSetPosition && editable); 676 posYField.setEnabled(canSetPosition && editable); 677 posZField.setEnabled(canSetPosition && editable); 678 posTField.setEnabled(canSetPosition && editable); 679 posCField.setEnabled(canSetPosition && editable); 680 posZSpinner.setEnabled(canSetPosition && editable); 681 posTSpinner.setEnabled(canSetPosition && editable); 682 posCSpinner.setEnabled(canSetPosition && editable); 683 sizeXField.setEnabled(canSetBounds && editable); 684 sizeYField.setEnabled(canSetBounds && editable && (dim > 1)); 685 sizeZField.setEnabled(canSetBounds && editable && (dim > 2)); 686 sizeTField.setEnabled(canSetBounds && editable && (dim > 3)); 687 sizeCField.setEnabled(canSetBounds && editable && (dim > 4)); 688 689 if (dim > 2) 690 { 691 posZField.setVisible(true); 692 posZFieldLabel.setVisible(true); 693 posZSpinner.setVisible(false); 694 posZSpinnerLabel.setVisible(false); 695 } 696 else 697 { 698 posZField.setVisible(false); 699 posZFieldLabel.setVisible(false); 700 posZSpinner.setVisible(true); 701 posZSpinnerLabel.setVisible(true); 702 } 703 704 if (dim > 3) 705 { 706 posTField.setVisible(true); 707 posTFieldLabel.setVisible(true); 708 posTSpinner.setVisible(false); 709 posTSpinnerLabel.setVisible(false); 710 } 711 else 712 { 713 posTField.setVisible(false); 714 posTFieldLabel.setVisible(false); 715 posTSpinner.setVisible(true); 716 posTSpinnerLabel.setVisible(true); 717 } 718 719 if (dim > 4) 720 { 721 posCField.setVisible(true); 722 posCFieldLabel.setVisible(true); 723 posCSpinner.setVisible(false); 724 posCSpinnerLabel.setVisible(false); 725 } 726 else 727 { 728 posCField.setVisible(false); 729 posCFieldLabel.setVisible(false); 730 posCSpinner.setVisible(true); 731 posCSpinnerLabel.setVisible(true); 732 } 733 734 colorButton.setEnabled(hasSelected && editable); 735 strokeSpinner.setEnabled(hasSelected && editable); 736 alphaSlider.setEnabled(hasSelected); 737 displayNameCheckBox.setEnabled(hasSelected); 738 739 setAsDefaultBtn.setEnabled(hasSelected); 740 } 741 }); 742 } 743 744 /** 745 * Refresh ROI properties 746 */ 747 public void refreshROIProperties() 748 { 749 ThreadUtil.runSingle(roiPropertiesRefresher); 750 } 751 752 /** 753 * Refresh ROI properties (internal) 754 */ 755 void refreshROIPropertiesInternal() 756 { 757 final List<ROI> rois = getSelectedRois(); 758 final ROI roi = (rois.size() > 0) ? rois.get(0) : null; 759 final Rectangle5D bounds = (roi != null) ? roi.getBounds5D() : null; 760 761 // wait a bit to avoid eating too much time with refresh 762 ThreadUtil.sleep(1); 763 764 ThreadUtil.invokeNow(new Runnable() 765 { 766 @Override 767 public void run() 768 { 769 // modifyingRoi.acquireUninterruptibly(); 770 if (!modifyingRoi.tryAcquire()) 771 return; 772 773 try 774 { 775 if (roi != null) 776 { 777 colorButton.setColor(roi.getColor()); 778 strokeSpinner.setValue(Double.valueOf(roi.getStroke())); 779 alphaSlider.setValue((int) (roi.getOpacity() * 100)); 780 displayNameCheckBox.setSelected(roi.getShowName()); 781 } 782 else 783 { 784 // no ROI selected 785 colorButton.setColor(Color.gray); 786 strokeSpinner.setValue(Double.valueOf(1d)); 787 alphaSlider.setValue(0); 788 displayNameCheckBox.setSelected(false); 789 } 790 791 if (bounds != null) 792 { 793 posXField.setText(getPositionAsString(bounds.getX(), bounds.getSizeX())); 794 posYField.setText(getPositionAsString(bounds.getY(), bounds.getSizeY())); 795 posZSpinner.setValue(Integer.valueOf((int) formatPosition(bounds.getZ(), bounds.getSizeZ()))); 796 posZField.setText(getPositionAsString(bounds.getZ(), bounds.getSizeZ())); 797 posTSpinner.setValue(Integer.valueOf((int) formatPosition(bounds.getT(), bounds.getSizeT()))); 798 posTField.setText(getPositionAsString(bounds.getT(), bounds.getSizeT())); 799 posCSpinner.setValue(Integer.valueOf((int) formatPosition(bounds.getC(), bounds.getSizeC()))); 800 posCField.setText(getPositionAsString(bounds.getC(), bounds.getSizeC())); 801 802 sizeXField.setText(getSizeAsString(bounds.getSizeX())); 803 sizeYField.setText(getSizeAsString(bounds.getSizeY())); 804 sizeZField.setText(getSizeAsString(bounds.getSizeZ())); 805 sizeTField.setText(getSizeAsString(bounds.getSizeT())); 806 sizeCField.setText(getSizeAsString(bounds.getSizeC())); 807 } 808 else 809 { 810 posXField.setText(""); 811 posYField.setText(""); 812 posZField.setText(""); 813 posTField.setText(""); 814 posCField.setText(""); 815 posZSpinner.setValue(Integer.valueOf(0)); 816 posTSpinner.setValue(Integer.valueOf(0)); 817 posCSpinner.setValue(Integer.valueOf(0)); 818 819 sizeXField.setText(""); 820 sizeYField.setText(""); 821 sizeZField.setText(""); 822 sizeTField.setText(""); 823 sizeCField.setText(""); 824 } 825 } 826 finally 827 { 828 modifyingRoi.release(); 829 } 830 } 831 }); 832 } 833 834 /** 835 * Refresh ROI actions and properties 836 */ 837 public void refreshROIActionsAndProperties() 838 { 839 ThreadUtil.runSingle(roiActionsRefresher); 840 ThreadUtil.runSingle(roiPropertiesRefresher); 841 } 842 843 @Override 844 public void textChanged(IcyTextField source, boolean validate) 845 { 846 // source not anymore enable --> cancel validation 847 if (!source.isEnabled()) 848 return; 849 850 // keep trace of modified ROI and wait for validation 851 if (!validate) 852 { 853 modifiedRois.clear(); 854 // better to use weak reference here to not retain ROI 855 for (ROI roi : getSelectedRois()) 856 modifiedRois.add(new WeakReference<ROI>(roi)); 857 858 return; 859 } 860 861 // at this point the text is validated so we can recover modified ROIs... 862 final List<ROI> rois = new ArrayList<ROI>(); 863 for (Reference<ROI> ref : modifiedRois) 864 { 865 final ROI roi = ref.get(); 866 if (roi != null) 867 rois.add(roi); 868 } 869 870 // nothing to edit ? 871 if (rois.isEmpty()) 872 return; 873 874 // can get semaphore ? 875 if (!modifyingRoi.tryAcquire()) 876 return; 877 878 try 879 { 880 // position fields ? 881 if ((source == posXField) || (source == posYField) || (source == posZField) || (source == posTField) 882 || (source == posCField)) 883 { 884 final double value = StringUtil.parseDouble(source.getText(), Double.NaN); 885 886 // correct value ? 887 if (!Double.isNaN(value)) 888 { 889 final List<ROI> roiToModify = new ArrayList<ROI>(); 890 891 // get the ROI we were modifying 892 for (ROI roi : rois) 893 { 894 // can't edit read only ROI (should not arrive) 895 if (roi.isReadOnly()) 896 continue; 897 898 // roi support position change ? 899 if (roi.canSetPosition()) 900 roiToModify.add(roi); 901 } 902 903 // we have effectively some ROI to modify ? 904 if (!roiToModify.isEmpty()) 905 { 906 Point5D positionSet = null; 907 908 // single change ? 909 if (roiToModify.size() == 1) 910 { 911 final ROI roi = roiToModify.get(0); 912 913 // get current ROI position 914 final Point5D savePosition = roi.getPosition5D(); 915 final Point5D position = (Point5D) savePosition.clone(); 916 917 if (source == posXField) 918 position.setX(value); 919 else if (source == posYField) 920 position.setY(value); 921 else if (source == posZField) 922 position.setZ(value); 923 else if (source == posTField) 924 position.setT(value); 925 else 926 position.setC(value); 927 928 // set new position 929 roi.setPosition5D(position); 930 // keep trace of accepted ROI position 931 positionSet = roi.getPosition5D(); 932 933 // add position change to undo manager 934 Icy.getMainInterface().getUndoManager() 935 .addEdit(new PositionROIEdit(roi, savePosition, false)); 936 } 937 else 938 { 939 final List<Point5D> savePositions = new ArrayList<Point5D>(); 940 final List<Point5D> newPositions = new ArrayList<Point5D>(); 941 942 // save previous positions 943 for (ROI roi : roiToModify) 944 savePositions.add(roi.getPosition5D()); 945 946 for (ROI roi : roiToModify) 947 { 948 // get current ROI position 949 final Point5D position = roi.getPosition5D(); 950 951 if (source == posXField) 952 position.setX(value); 953 else if (source == posYField) 954 position.setY(value); 955 else if (source == posZField) 956 position.setZ(value); 957 else if (source == posTField) 958 position.setT(value); 959 else 960 position.setC(value); 961 962 // set new position 963 roi.setPosition5D(position); 964 // keep trace of first accepted ROI position 965 if (positionSet == null) 966 positionSet = roi.getPosition5D(); 967 968 // save new position 969 newPositions.add(position); 970 } 971 972 // add position change to undo manager 973 Icy.getMainInterface().getUndoManager() 974 .addEdit(new PositionROIsEdit(roiToModify, savePositions, newPositions, false)); 975 } 976 977 if (positionSet != null) 978 { 979 double p; 980 981 // fix field value if needed 982 if (source == posXField) 983 p = positionSet.getX(); 984 else if (source == posYField) 985 p = positionSet.getY(); 986 else if (source == posZField) 987 p = positionSet.getZ(); 988 else if (source == posTField) 989 p = positionSet.getT(); 990 else 991 p = positionSet.getC(); 992 993 // change infinite by -1 994 if (p == Double.NEGATIVE_INFINITY) 995 p = -1d; 996 997 source.setText(Double.toString(p)); 998 } 999 } 1000 } 1001 } 1002 // size fields ? 1003 else if ((source == sizeXField) || (source == sizeYField) || (source == sizeZField) 1004 || (source == sizeTField) || (source == sizeCField)) 1005 { 1006 final double value = StringUtil.parseDouble(source.getText(), Double.NaN); 1007 1008 // correct value ? 1009 if (!Double.isNaN(value)) 1010 { 1011 final List<ROI> roiToModify = new ArrayList<ROI>(); 1012 1013 // get the ROI we were modifying 1014 for (ROI roi : rois) 1015 { 1016 // can't edit read only ROI (should not arrive) 1017 if (roi.isReadOnly()) 1018 continue; 1019 1020 // roi support position change ? 1021 if (roi.canSetPosition()) 1022 roiToModify.add(roi); 1023 } 1024 1025 // we have effectively some ROI to modify ? 1026 if (!roiToModify.isEmpty()) 1027 { 1028 Rectangle5D boundsSet = null; 1029 1030 if (roiToModify.size() == 1) 1031 { 1032 final ROI roi = roiToModify.get(0); 1033 1034 // get current ROI size 1035 final Rectangle5D saveBounds = roi.getBounds5D(); 1036 final Rectangle5D bounds = (Rectangle5D) saveBounds.clone(); 1037 1038 if (source == sizeXField) 1039 bounds.setSizeX(value); 1040 else if (source == sizeYField) 1041 bounds.setSizeY(value); 1042 else if (source == sizeZField) 1043 bounds.setSizeZ(value); 1044 else if (source == sizeTField) 1045 bounds.setSizeT(value); 1046 else 1047 bounds.setSizeC(value); 1048 1049 roi.setBounds5D(bounds); 1050 // keep trace of first accepted ROI bounds 1051 boundsSet = roi.getBounds5D(); 1052 1053 // add position change to undo manager 1054 Icy.getMainInterface().getUndoManager().addEdit(new BoundsROIEdit(roi, saveBounds, false)); 1055 } 1056 else 1057 { 1058 final List<Rectangle5D> saveBoundsList = new ArrayList<Rectangle5D>(); 1059 final List<Rectangle5D> newBoundsList = new ArrayList<Rectangle5D>(); 1060 1061 // save previous size 1062 for (ROI roi : roiToModify) 1063 saveBoundsList.add(roi.getBounds5D()); 1064 1065 for (ROI roi : roiToModify) 1066 { 1067 // get current ROI size 1068 Rectangle5D bounds = roi.getBounds5D(); 1069 1070 if (source == sizeXField) 1071 bounds.setSizeX(value); 1072 else if (source == sizeYField) 1073 bounds.setSizeY(value); 1074 else if (source == sizeZField) 1075 bounds.setSizeZ(value); 1076 else if (source == sizeTField) 1077 bounds.setSizeT(value); 1078 else 1079 bounds.setSizeC(value); 1080 1081 roi.setBounds5D(bounds); 1082 // keep trace of first accepted ROI bounds 1083 if (boundsSet == null) 1084 boundsSet = roi.getBounds5D(); 1085 1086 // save new size 1087 newBoundsList.add(bounds); 1088 } 1089 1090 // add position change to undo manager 1091 Icy.getMainInterface().getUndoManager() 1092 .addEdit(new BoundsROIsEdit(roiToModify, saveBoundsList, newBoundsList, false)); 1093 } 1094 1095 if (boundsSet != null) 1096 { 1097 final double p; 1098 1099 // fix field value if needed 1100 if (source == sizeXField) 1101 p = boundsSet.getSizeX(); 1102 else if (source == sizeYField) 1103 p = boundsSet.getSizeY(); 1104 else if (source == sizeZField) 1105 p = boundsSet.getSizeZ(); 1106 else if (source == sizeTField) 1107 p = boundsSet.getSizeT(); 1108 else 1109 p = boundsSet.getSizeC(); 1110 1111 source.setText(Double.toString(p)); 1112 } 1113 } 1114 } 1115 } 1116 } 1117 finally 1118 { 1119 modifyingRoi.release(); 1120 } 1121 } 1122 1123 @Override 1124 public void colorChanged(ColorChooserButton source) 1125 { 1126 // source not anymore enable --> cancel change 1127 if (!source.isEnabled()) 1128 return; 1129 1130 final Sequence sequence = Icy.getMainInterface().getActiveSequence(); 1131 if (sequence == null) 1132 return; 1133 1134 if (!modifyingRoi.tryAcquire()) 1135 return; 1136 1137 final List<ROI> rois = getSelectedRois(false); 1138 final List<Object> oldValues = new ArrayList<Object>(); 1139 final Color color = source.getColor(); 1140 1141 sequence.beginUpdate(); 1142 try 1143 { 1144 // set new color 1145 for (ROI roi : rois) 1146 { 1147 // save previous color 1148 oldValues.add(roi.getColor()); 1149 roi.setColor(color); 1150 } 1151 } 1152 finally 1153 { 1154 sequence.endUpdate(); 1155 modifyingRoi.release(); 1156 } 1157 1158 // add color change to undo manager 1159 sequence.addUndoableEdit(new PropertyROIsEdit(rois, ROI.PROPERTY_COLOR, oldValues, color)); 1160 } 1161 1162 @Override 1163 public void stateChanged(ChangeEvent e) 1164 { 1165 if (!(e.getSource() instanceof JComponent)) 1166 return; 1167 1168 final JComponent source = (JComponent) e.getSource(); 1169 1170 // source not anymore enable --> cancel change 1171 if (!source.isEnabled()) 1172 return; 1173 1174 final Sequence sequence = Icy.getMainInterface().getActiveSequence(); 1175 if (sequence == null) 1176 return; 1177 1178 if (!modifyingRoi.tryAcquire()) 1179 return; 1180 1181 try 1182 { 1183 if (source == strokeSpinner) 1184 { 1185 final List<ROI> rois = getSelectedRois(false); 1186 final List<Object> oldValues = new ArrayList<Object>(); 1187 final double stroke = ((Double) strokeSpinner.getValue()).doubleValue(); 1188 1189 sequence.beginUpdate(); 1190 try 1191 { 1192 for (ROI roi : rois) 1193 { 1194 // save previous stroke 1195 oldValues.add(Double.valueOf(roi.getStroke())); 1196 roi.setStroke(stroke); 1197 } 1198 } 1199 finally 1200 { 1201 sequence.endUpdate(); 1202 } 1203 1204 // add stroke change to undo manager 1205 sequence.addUndoableEdit( 1206 new PropertyROIsEdit(rois, ROI.PROPERTY_STROKE, oldValues, Double.valueOf(stroke))); 1207 } 1208 else if (source == alphaSlider) 1209 { 1210 final List<ROI> rois = getSelectedRois(true); 1211 final List<Object> oldValues = new ArrayList<Object>(); 1212 final float opacity = alphaSlider.getValue() / 100f; 1213 1214 sequence.beginUpdate(); 1215 try 1216 { 1217 for (ROI roi : rois) 1218 { 1219 // save previous opacity 1220 oldValues.add(Float.valueOf(roi.getOpacity())); 1221 roi.setOpacity(opacity); 1222 } 1223 } 1224 finally 1225 { 1226 sequence.endUpdate(); 1227 } 1228 1229 // add opacity change to undo manager 1230 sequence.addUndoableEdit( 1231 new PropertyROIsEdit(rois, ROI.PROPERTY_OPACITY, oldValues, Float.valueOf(opacity))); 1232 } 1233 else if ((source == posZSpinner) || (source == posTSpinner) || (source == posCSpinner)) 1234 { 1235 final List<ROI> rois = getSelectedRois(); 1236 1237 // nothing to edit 1238 if ((rois == null) || rois.isEmpty()) 1239 return; 1240 1241 final SpecialValueSpinner spinner = (SpecialValueSpinner) source; 1242 final double value = ((Integer) spinner.getValue()).intValue(); 1243 1244 // correct value ? 1245 if (!Double.isNaN(value)) 1246 { 1247 final List<ROI> roiToModify = new ArrayList<ROI>(); 1248 1249 // get the ROI we were modifying 1250 for (ROI roi : rois) 1251 { 1252 // can't edit read only ROI (should not arrive) 1253 if (roi.isReadOnly()) 1254 continue; 1255 1256 // roi support position change ? 1257 if (roi.canSetPosition()) 1258 roiToModify.add(roi); 1259 } 1260 1261 // we have effectively some ROI to modify ? 1262 if (!roiToModify.isEmpty()) 1263 { 1264 Point5D positionSet = null; 1265 1266 // single change ? 1267 if (roiToModify.size() == 1) 1268 { 1269 final ROI roi = roiToModify.get(0); 1270 1271 // get current ROI position 1272 final Point5D savePosition = roi.getPosition5D(); 1273 final Point5D position = (Point5D) savePosition.clone(); 1274 1275 if (source == posZSpinner) 1276 position.setZ(value); 1277 else if (source == posTSpinner) 1278 position.setT(value); 1279 else 1280 position.setC(value); 1281 1282 // set new position 1283 roi.setPosition5D(position); 1284 // keep trace of accepted ROI position 1285 positionSet = roi.getPosition5D(); 1286 1287 // add position change to undo manager 1288 Icy.getMainInterface().getUndoManager() 1289 .addEdit(new PositionROIEdit(roi, savePosition, false)); 1290 } 1291 else 1292 { 1293 final List<Point5D> savePositions = new ArrayList<Point5D>(); 1294 final List<Point5D> newPositions = new ArrayList<Point5D>(); 1295 1296 // save previous positions 1297 for (ROI roi : roiToModify) 1298 savePositions.add(roi.getPosition5D()); 1299 1300 for (ROI roi : roiToModify) 1301 { 1302 // get current ROI position 1303 final Point5D position = roi.getPosition5D(); 1304 1305 if (source == posZSpinner) 1306 position.setZ(value); 1307 else if (source == posTSpinner) 1308 position.setT(value); 1309 else 1310 position.setC(value); 1311 1312 // set new position 1313 roi.setPosition5D(position); 1314 // keep trace of first accepted ROI position 1315 if (positionSet == null) 1316 positionSet = roi.getPosition5D(); 1317 1318 // save new position 1319 newPositions.add(position); 1320 } 1321 1322 // add position change to undo manager 1323 Icy.getMainInterface().getUndoManager() 1324 .addEdit(new PositionROIsEdit(roiToModify, savePositions, newPositions, false)); 1325 } 1326 1327 if (positionSet != null) 1328 { 1329 double p; 1330 1331 // fix field value if needed 1332 if (source == posZSpinner) 1333 p = positionSet.getZ(); 1334 else if (source == posTSpinner) 1335 p = positionSet.getT(); 1336 else 1337 p = positionSet.getC(); 1338 1339 // change infinite by -1 1340 if (p == Double.NEGATIVE_INFINITY) 1341 p = -1d; 1342 1343 spinner.setValue(Integer.valueOf((int) p)); 1344 } 1345 } 1346 } 1347 } 1348 } 1349 finally 1350 { 1351 modifyingRoi.release(); 1352 } 1353 } 1354 1355 @Override 1356 public void actionPerformed(ActionEvent e) 1357 { 1358 if (!(e.getSource() instanceof JComponent)) 1359 return; 1360 1361 final JComponent source = (JComponent) e.getSource(); 1362 1363 // source not anymore enable --> cancel change 1364 if (!source.isEnabled()) 1365 return; 1366 1367 final Sequence sequence = Icy.getMainInterface().getActiveSequence(); 1368 if (sequence == null) 1369 return; 1370 1371 if (!modifyingRoi.tryAcquire()) 1372 return; 1373 1374 try 1375 { 1376 if (source == displayNameCheckBox) 1377 { 1378 sequence.beginUpdate(); 1379 try 1380 { 1381 final boolean display = displayNameCheckBox.isSelected(); 1382 1383 for (ROI roi : getSelectedRois(false)) 1384 roi.setShowName(display); 1385 } 1386 finally 1387 { 1388 sequence.endUpdate(); 1389 } 1390 } 1391 else if (source == setAsDefaultBtn) 1392 { 1393 final boolean display = displayNameCheckBox.isSelected(); 1394 final Color color = colorButton.getColor(); 1395 final double stroke = ((Double) strokeSpinner.getValue()).doubleValue(); 1396 final float opacity = alphaSlider.getValue() / 100f; 1397 1398 // set default ROI Overlay properties 1399 ROI.setDefaultColor(color); 1400 ROI.setDefaultShowName(display); 1401 ROI.setDefaultOpacity(opacity); 1402 ROI.setDefaultStroke(stroke); 1403 } 1404 } 1405 finally 1406 { 1407 modifyingRoi.release(); 1408 } 1409 } 1410 1411 @Override 1412 public void clipboardChanged() 1413 { 1414 refreshROIActions(); 1415 } 1416 1417 // one of the selected ROI changed 1418 public void roiChanged(ROIEvent event) 1419 { 1420 switch (event.getType()) 1421 { 1422 case ROI_CHANGED: 1423 // refresh the properties 1424 refreshROIProperties(); 1425 break; 1426 1427 case FOCUS_CHANGED: 1428 // nothing to do here 1429 break; 1430 1431 case PROPERTY_CHANGED: 1432 final String propertyName = event.getPropertyName(); 1433 1434 if (ROI.PROPERTY_READONLY.equals(propertyName)) 1435 refreshROIActions(); 1436 break; 1437 1438 case SELECTION_CHANGED: 1439 // handle externally with the setSelectedROI() method 1440 break; 1441 } 1442 } 1443 1444}