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.sequence.tools; 020 021import icy.gui.component.button.IcyButton; 022import icy.gui.component.sequence.SequenceChooser; 023import icy.gui.component.sequence.SequencePreviewPanel; 024import icy.gui.dialog.MessageDialog; 025import icy.resource.ResourceUtil; 026import icy.resource.icon.IcyIcon; 027import icy.sequence.DimensionId; 028import icy.sequence.Sequence; 029import icy.sequence.SequenceModel; 030 031import java.awt.Dimension; 032import java.awt.Font; 033import java.awt.GridBagConstraints; 034import java.awt.GridBagLayout; 035import java.awt.Insets; 036import java.awt.event.ActionEvent; 037import java.awt.event.ActionListener; 038 039import javax.swing.DefaultListModel; 040import javax.swing.JCheckBox; 041import javax.swing.JLabel; 042import javax.swing.JList; 043import javax.swing.JPanel; 044import javax.swing.JScrollPane; 045import javax.swing.ListSelectionModel; 046import javax.swing.SwingConstants; 047import javax.swing.border.TitledBorder; 048import javax.swing.event.ChangeEvent; 049import javax.swing.event.ChangeListener; 050import javax.swing.event.ListSelectionEvent; 051import javax.swing.event.ListSelectionListener; 052 053/** 054 * Frame for dimension merge operation. 055 * 056 * @author Stephane 057 */ 058public class SequenceDimensionMergePanel extends JPanel 059{ 060 static class SequenceChannelEntry 061 { 062 final Sequence sequence; 063 final int c; 064 065 /** 066 * @param sequence 067 * @param c 068 */ 069 public SequenceChannelEntry(Sequence sequence, int c) 070 { 071 super(); 072 073 this.sequence = sequence; 074 this.c = c; 075 } 076 077 public SequenceChannelEntry(Sequence sequence) 078 { 079 this(sequence, -1); 080 } 081 082 @Override 083 public String toString() 084 { 085 if (c == -1) 086 return sequence.toString(); 087 088 return sequence.toString() + " [channel " + c + "]"; 089 } 090 } 091 092 /** 093 * 094 */ 095 private static final long serialVersionUID = -5908902915282090447L; 096 097 // GUI 098 protected IcyButton addButton; 099 protected IcyButton removeButton; 100 protected IcyButton upButton; 101 protected IcyButton downButton; 102 protected JList sequenceList; 103 protected SequenceChooser sequenceChooser; 104 protected SequencePreviewPanel sequencePreview; 105 protected JCheckBox interlaceCheckBox; 106 protected JCheckBox fillEmptyImageCheckBox; 107 protected JCheckBox fitCheckbox; 108 private JLabel bottomArrowLabel; 109 private JLabel dimLabel; 110 111 // internals 112 protected DefaultListModel listModel; 113 protected ListSelectionModel selectionModel; 114 protected final DimensionId dim; 115 116 /** 117 * Create the panel. 118 */ 119 public SequenceDimensionMergePanel(DimensionId dim) 120 { 121 super(); 122 123 this.dim = dim; 124 125 listModel = new DefaultListModel(); 126 127 initialize(); 128 129 selectionModel = sequenceList.getSelectionModel(); 130 selectionModel.addListSelectionListener(new ListSelectionListener() 131 { 132 @Override 133 public void valueChanged(ListSelectionEvent e) 134 { 135 refreshButtonsState(); 136 } 137 }); 138 139 interlaceCheckBox.addActionListener(new ActionListener() 140 { 141 @Override 142 public void actionPerformed(ActionEvent e) 143 { 144 fireChangedEvent(); 145 previewImageChanged(); 146 } 147 }); 148 fillEmptyImageCheckBox.addActionListener(new ActionListener() 149 { 150 @Override 151 public void actionPerformed(ActionEvent e) 152 { 153 fireChangedEvent(); 154 previewImageChanged(); 155 } 156 }); 157 fitCheckbox.addActionListener(new ActionListener() 158 { 159 @Override 160 public void actionPerformed(ActionEvent e) 161 { 162 fireChangedEvent(); 163 previewImageChanged(); 164 } 165 }); 166 167 addButton.addActionListener(new ActionListener() 168 { 169 @Override 170 public void actionPerformed(ActionEvent e) 171 { 172 final Sequence seq = sequenceChooser.getSelectedSequence(); 173 174 if (seq != null) 175 { 176 if (checkSequenceIsCompatible(seq, true, true)) 177 { 178 if (SequenceDimensionMergePanel.this.dim == DimensionId.C) 179 { 180 // add per channel 181 for (int c = 0; c < seq.getSizeC(); c++) 182 listModel.addElement(new SequenceChannelEntry(seq, c)); 183 } 184 else 185 listModel.addElement(new SequenceChannelEntry(seq)); 186 187 refreshButtonsState(); 188 fireChangedEvent(); 189 previewDimensionChanged(); 190 } 191 } 192 } 193 }); 194 removeButton.addActionListener(new ActionListener() 195 { 196 @Override 197 public void actionPerformed(ActionEvent e) 198 { 199 listModel.remove(selectionModel.getMinSelectionIndex()); 200 201 refreshButtonsState(); 202 fireChangedEvent(); 203 previewDimensionChanged(); 204 } 205 }); 206 upButton.addActionListener(new ActionListener() 207 { 208 @Override 209 public void actionPerformed(ActionEvent e) 210 { 211 final int index = selectionModel.getMinSelectionIndex(); 212 213 // exchange index and (index - 1) 214 final Object obj = listModel.getElementAt(index - 1); 215 listModel.set(index - 1, listModel.getElementAt(index)); 216 listModel.set(index, obj); 217 218 selectionModel.setSelectionInterval(index - 1, index - 1); 219 220 refreshButtonsState(); 221 fireChangedEvent(); 222 previewImageChanged(); 223 } 224 }); 225 downButton.addActionListener(new ActionListener() 226 { 227 @Override 228 public void actionPerformed(ActionEvent e) 229 { 230 final int index = selectionModel.getMinSelectionIndex(); 231 232 // exchange index and (index + 1) 233 final Object obj = listModel.getElementAt(index + 1); 234 listModel.set(index + 1, listModel.getElementAt(index)); 235 listModel.set(index, obj); 236 237 selectionModel.setSelectionInterval(index + 1, index + 1); 238 239 refreshButtonsState(); 240 fireChangedEvent(); 241 previewImageChanged(); 242 } 243 }); 244 245 dimLabel.setText(dim.toString()); 246 IcyIcon icon = new IcyIcon(ResourceUtil.ICON_ARROW_DOWN); 247 icon.setDimension(new Dimension(20, 60)); 248 bottomArrowLabel.setIcon(icon); 249 250 // interlace not available for channel merge operation 251 interlaceCheckBox.setVisible(dim != DimensionId.C); 252 // fillEmptyImageCheckBox.setVisible(false); 253 254 refreshButtonsState(); 255 } 256 257 private void initialize() 258 { 259 GridBagLayout gridBagLayout = new GridBagLayout(); 260 gridBagLayout.columnWidths = new int[] {24, 80, 140, 100, 0, 0}; 261 gridBagLayout.rowHeights = new int[] {0, 26, 0, 0, 0, 0, 0, 174, 0}; 262 gridBagLayout.columnWeights = new double[] {0.0, 1.0, 1.0, 1.0, 0.0, Double.MIN_VALUE}; 263 gridBagLayout.rowWeights = new double[] {0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, Double.MIN_VALUE}; 264 setLayout(gridBagLayout); 265 266 JLabel lblSelectSequenceTo = new JLabel("Add sequence to merge in the list :"); 267 GridBagConstraints gbc_lblSelectSequenceTo = new GridBagConstraints(); 268 gbc_lblSelectSequenceTo.fill = GridBagConstraints.BOTH; 269 gbc_lblSelectSequenceTo.gridwidth = 4; 270 gbc_lblSelectSequenceTo.insets = new Insets(0, 0, 5, 5); 271 gbc_lblSelectSequenceTo.gridx = 0; 272 gbc_lblSelectSequenceTo.gridy = 0; 273 add(lblSelectSequenceTo, gbc_lblSelectSequenceTo); 274 275 sequenceChooser = new SequenceChooser(); 276 GridBagConstraints gbc_sequenceChooser = new GridBagConstraints(); 277 gbc_sequenceChooser.gridwidth = 4; 278 gbc_sequenceChooser.insets = new Insets(0, 0, 5, 5); 279 gbc_sequenceChooser.fill = GridBagConstraints.BOTH; 280 gbc_sequenceChooser.gridx = 0; 281 gbc_sequenceChooser.gridy = 1; 282 add(sequenceChooser, gbc_sequenceChooser); 283 284 addButton = new IcyButton(new IcyIcon(ResourceUtil.ICON_ROUND_PLUS)); 285 addButton.setToolTipText("Add selected sequence to the list."); 286 addButton.setFlat(true); 287 GridBagConstraints gbc_addButton = new GridBagConstraints(); 288 gbc_addButton.fill = GridBagConstraints.BOTH; 289 gbc_addButton.insets = new Insets(0, 0, 5, 0); 290 gbc_addButton.gridx = 4; 291 gbc_addButton.gridy = 1; 292 add(addButton, gbc_addButton); 293 294 dimLabel = new JLabel("Z"); 295 dimLabel.setHorizontalAlignment(SwingConstants.CENTER); 296 dimLabel.setFont(new Font("Tahoma", Font.BOLD, 14)); 297 GridBagConstraints gbc_dimLabel = new GridBagConstraints(); 298 gbc_dimLabel.fill = GridBagConstraints.HORIZONTAL; 299 gbc_dimLabel.anchor = GridBagConstraints.BASELINE; 300 gbc_dimLabel.insets = new Insets(0, 0, 5, 5); 301 gbc_dimLabel.gridx = 0; 302 gbc_dimLabel.gridy = 2; 303 add(dimLabel, gbc_dimLabel); 304 305 JScrollPane scrollPane = new JScrollPane(); 306 GridBagConstraints gbc_scrollPane = new GridBagConstraints(); 307 gbc_scrollPane.gridwidth = 3; 308 gbc_scrollPane.fill = GridBagConstraints.BOTH; 309 gbc_scrollPane.gridheight = 4; 310 gbc_scrollPane.insets = new Insets(0, 0, 5, 5); 311 gbc_scrollPane.gridx = 1; 312 gbc_scrollPane.gridy = 2; 313 add(scrollPane, gbc_scrollPane); 314 315 sequenceList = new JList(listModel); 316 scrollPane.setViewportView(sequenceList); 317 sequenceList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 318 319 removeButton = new IcyButton(new IcyIcon(ResourceUtil.ICON_ROUND_MINUS)); 320 removeButton.setToolTipText("Remove selected sequence from the list."); 321 removeButton.setFlat(true); 322 GridBagConstraints gbc_removeButton = new GridBagConstraints(); 323 gbc_removeButton.fill = GridBagConstraints.BOTH; 324 gbc_removeButton.insets = new Insets(0, 0, 5, 0); 325 gbc_removeButton.gridx = 4; 326 gbc_removeButton.gridy = 2; 327 add(removeButton, gbc_removeButton); 328 329 bottomArrowLabel = new JLabel(""); 330 bottomArrowLabel.setHorizontalAlignment(SwingConstants.CENTER); 331 GridBagConstraints gbc_bottomArrowLabel = new GridBagConstraints(); 332 gbc_bottomArrowLabel.gridheight = 3; 333 gbc_bottomArrowLabel.insets = new Insets(0, 0, 5, 5); 334 gbc_bottomArrowLabel.gridx = 0; 335 gbc_bottomArrowLabel.gridy = 3; 336 add(bottomArrowLabel, gbc_bottomArrowLabel); 337 338 upButton = new IcyButton(new IcyIcon(ResourceUtil.ICON_ROUND_ARROW_UP)); 339 upButton.setToolTipText("Move up selected sequence."); 340 upButton.setFlat(true); 341 GridBagConstraints gbc_upButton = new GridBagConstraints(); 342 gbc_upButton.fill = GridBagConstraints.BOTH; 343 gbc_upButton.insets = new Insets(0, 0, 5, 0); 344 gbc_upButton.gridx = 4; 345 gbc_upButton.gridy = 3; 346 add(upButton, gbc_upButton); 347 348 downButton = new IcyButton(new IcyIcon(ResourceUtil.ICON_ROUND_ARROW_DOWN)); 349 downButton.setToolTipText("Move down selected sequence."); 350 downButton.setFlat(true); 351 GridBagConstraints gbc_downButton = new GridBagConstraints(); 352 gbc_downButton.fill = GridBagConstraints.BOTH; 353 gbc_downButton.insets = new Insets(0, 0, 5, 0); 354 gbc_downButton.gridx = 4; 355 gbc_downButton.gridy = 4; 356 add(downButton, gbc_downButton); 357 358 fitCheckbox = new JCheckBox("Scale image"); 359 fitCheckbox.setToolTipText("Scale all image to the largest one"); 360 GridBagConstraints gbc_fitCheckbox = new GridBagConstraints(); 361 gbc_fitCheckbox.anchor = GridBagConstraints.WEST; 362 gbc_fitCheckbox.gridwidth = 2; 363 gbc_fitCheckbox.insets = new Insets(0, 0, 5, 5); 364 gbc_fitCheckbox.gridx = 0; 365 gbc_fitCheckbox.gridy = 6; 366 add(fitCheckbox, gbc_fitCheckbox); 367 368 fillEmptyImageCheckBox = new JCheckBox("Fill empty image"); 369 fillEmptyImageCheckBox.setToolTipText("Replace empty image by the previous non empty one"); 370 GridBagConstraints gbc_noEmptyImageCheckBox = new GridBagConstraints(); 371 gbc_noEmptyImageCheckBox.fill = GridBagConstraints.VERTICAL; 372 gbc_noEmptyImageCheckBox.insets = new Insets(0, 0, 5, 5); 373 gbc_noEmptyImageCheckBox.gridx = 2; 374 gbc_noEmptyImageCheckBox.gridy = 6; 375 add(fillEmptyImageCheckBox, gbc_noEmptyImageCheckBox); 376 377 interlaceCheckBox = new JCheckBox("Interlace image"); 378 interlaceCheckBox.setToolTipText("Interlace sequence image"); 379 GridBagConstraints gbc_interlaceCheckBox = new GridBagConstraints(); 380 gbc_interlaceCheckBox.anchor = GridBagConstraints.EAST; 381 gbc_interlaceCheckBox.gridwidth = 2; 382 gbc_interlaceCheckBox.fill = GridBagConstraints.VERTICAL; 383 gbc_interlaceCheckBox.insets = new Insets(0, 0, 5, 0); 384 gbc_interlaceCheckBox.gridx = 3; 385 gbc_interlaceCheckBox.gridy = 6; 386 add(interlaceCheckBox, gbc_interlaceCheckBox); 387 388 sequencePreview = new SequencePreviewPanel(); 389 sequencePreview 390 .setBorder(new TitledBorder(null, "Preview", TitledBorder.LEADING, TitledBorder.TOP, null, null)); 391 GridBagConstraints gbc_sequencePreview = new GridBagConstraints(); 392 gbc_sequencePreview.gridwidth = 5; 393 gbc_sequencePreview.fill = GridBagConstraints.BOTH; 394 gbc_sequencePreview.gridx = 0; 395 gbc_sequencePreview.gridy = 7; 396 add(sequencePreview, gbc_sequencePreview); 397 } 398 399 public DimensionId getDimensionId() 400 { 401 return dim; 402 } 403 404 void refreshButtonsState() 405 { 406 final int index = selectionModel.getMinSelectionIndex(); 407 final boolean notEmpty = index != -1; 408 final int size = listModel.getSize(); 409 410 removeButton.setEnabled(notEmpty); 411 upButton.setEnabled(notEmpty && (index != 0)); 412 downButton.setEnabled(notEmpty && (index != (size - 1))); 413 } 414 415 public int[] getSelectedChannels() 416 { 417 final int[] result = new int[listModel.size()]; 418 419 for (int i = 0; i < listModel.getSize(); i++) 420 result[i] = ((SequenceChannelEntry) listModel.get(i)).c; 421 422 return result; 423 } 424 425 public Sequence[] getSequences() 426 { 427 final Sequence result[] = new Sequence[listModel.size()]; 428 429 for (int i = 0; i < listModel.getSize(); i++) 430 result[i] = ((SequenceChannelEntry) listModel.get(i)).sequence; 431 432 return result; 433 } 434 435 boolean checkSequenceIsCompatible(Sequence seq, boolean showMessage, boolean showWarning) 436 { 437 boolean warningXYDone = false; 438 439 for (Sequence sequence : getSequences()) 440 { 441 // first check for data type 442 if (!seq.getDataType_().equals(sequence.getDataType_())) 443 { 444 if (showMessage) 445 MessageDialog.showDialog("You cannot merge sequences with different data type."); 446 447 return false; 448 } 449 450 // We can remove all these verification as the merge algorithm take care of that ! 451 452 // then depending dimension merge check for dimension equality 453 // switch (getDimensionId()) 454 // { 455 // case C: 456 // if (seq.getSizeZ() != sequence.getSizeZ()) 457 // { 458 // if (showMessage) 459 // MessageDialog.showDialog("You cannot merge channels from sequences with different Z size."); 460 // 461 // return false; 462 // } 463 // if (seq.getSizeT() != sequence.getSizeT()) 464 // { 465 // if (showMessage) 466 // MessageDialog.showDialog("You cannot merge channels from sequences with different T size."); 467 // 468 // return false; 469 // } 470 // break; 471 // 472 // case Z: 473 // if (seq.getSizeC() != sequence.getSizeC()) 474 // { 475 // if (showMessage) 476 // MessageDialog 477 // .showDialog("You cannot merge slices from sequences with different number of channel."); 478 // 479 // return false; 480 // } 481 // if (seq.getSizeT() != sequence.getSizeT()) 482 // { 483 // if (showMessage) 484 // MessageDialog.showDialog("You cannot merge slices from sequences with different T size."); 485 // 486 // return false; 487 // } 488 // break; 489 // 490 // case T: 491 // if (seq.getSizeC() != sequence.getSizeC()) 492 // { 493 // if (showMessage) 494 // MessageDialog.showDialog( 495 // "You cannot merge frames from sequences with different number of channel.", 496 // MessageDialog.PLAIN_MESSAGE); 497 // 498 // return false; 499 // } 500 // if (seq.getSizeZ() != sequence.getSizeZ()) 501 // { 502 // if (showMessage) 503 // MessageDialog.showDialog("You cannot merge frames from sequences with different Z size."); 504 // 505 // return false; 506 // } 507 // break; 508 // } 509 510 // also consider the XY size 511 if (!isFitImagesEnabled()) 512 { 513 if ((seq.getSizeX() != sequence.getSizeX()) || (seq.getSizeY() != sequence.getSizeY())) 514 { 515 if (showWarning && !warningXYDone) 516 { 517 MessageDialog 518 .showDialog( 519 "Sequences have different XY size !\nYou can enable the \"Scale image\" option to resize images if needed.", 520 MessageDialog.WARNING_MESSAGE); 521 warningXYDone = true; 522 } 523 } 524 } 525 } 526 527 return true; 528 } 529 530 /** 531 * @return the image provider 532 */ 533 public SequenceModel getModel() 534 { 535 return sequencePreview.getModel(); 536 } 537 538 public void setModel(SequenceModel model) 539 { 540 sequencePreview.setModel(model); 541 } 542 543 public void previewDimensionChanged() 544 { 545 sequencePreview.dimensionChanged(); 546 } 547 548 public void previewImageChanged() 549 { 550 sequencePreview.imageChanged(); 551 } 552 553 public boolean isInterlaceEnabled() 554 { 555 return interlaceCheckBox.isVisible() && interlaceCheckBox.isSelected(); 556 } 557 558 public boolean isFillEmptyImageEnabled() 559 { 560 return fillEmptyImageCheckBox.isVisible() && fillEmptyImageCheckBox.isSelected(); 561 } 562 563 public boolean isFitImagesEnabled() 564 { 565 return fitCheckbox.isVisible() && fitCheckbox.isSelected(); 566 } 567 568 public boolean isInterlaceVisible() 569 { 570 return interlaceCheckBox.isVisible(); 571 } 572 573 public void setInterlaceVisible(boolean value) 574 { 575 interlaceCheckBox.setVisible(value); 576 } 577 578 public boolean isFillEmptyImageVisible() 579 { 580 return fillEmptyImageCheckBox.isVisible(); 581 } 582 583 public void setFillEmptyImageVisible(boolean value) 584 { 585 fillEmptyImageCheckBox.setVisible(value); 586 } 587 588 protected void fireChangedEvent() 589 { 590 final ChangeEvent event = new ChangeEvent(SequenceDimensionMergePanel.this); 591 592 for (ChangeListener listener : getListeners(ChangeListener.class)) 593 listener.stateChanged(event); 594 } 595 596 public void addChangeListener(ChangeListener listener) 597 { 598 listenerList.add(ChangeListener.class, listener); 599 } 600 601 public void removeChangeListener(ChangeListener listener) 602 { 603 listenerList.remove(ChangeListener.class, listener); 604 } 605}