3 TrakEM2 plugin for ImageJ(C).
4 Copyright (C) 2005, 2006 Albert Cardona and Rodney Douglas.
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation (http://www.gnu.org/licenses/gpl.txt )
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 You may contact Albert Cardona at acardona at ini.phys.ethz.ch
20 Institute of Neuroinformatics, University of Zurich / ETH, Switzerland.
23 package ini
.trakem2
.display
;
27 import ij
.measure
.Calibration
;
28 import ini
.trakem2
.Project
;
29 import ini
.trakem2
.ControlWindow
;
30 import ini
.trakem2
.persistence
.DBObject
;
31 import ini
.trakem2
.persistence
.Loader
;
32 import ini
.trakem2
.utils
.IJError
;
33 import ini
.trakem2
.imaging
.PatchStack
;
34 import ini
.trakem2
.imaging
.Registration
;
35 import ini
.trakem2
.imaging
.StitchingTEM
;
36 import ini
.trakem2
.imaging
.Blending
;
37 import ini
.trakem2
.utils
.ProjectToolbar
;
38 import ini
.trakem2
.utils
.Utils
;
39 import ini
.trakem2
.utils
.DNDInsertImage
;
40 import ini
.trakem2
.utils
.Search
;
41 import ini
.trakem2
.utils
.Bureaucrat
;
42 import ini
.trakem2
.utils
.Worker
;
43 import ini
.trakem2
.utils
.Dispatcher
;
44 import ini
.trakem2
.utils
.Lock
;
45 import ini
.trakem2
.utils
.M
;
46 import ini
.trakem2
.tree
.*;
49 import javax
.swing
.event
.*;
51 import mpicbg
.trakem2
.align
.AlignTask
;
54 import java
.awt
.event
.*;
56 import java
.lang
.reflect
.Method
;
57 import java
.io
.Writer
;
59 import lenscorrection
.DistortionCorrectionTask
;
61 /** A Display is a class to show a Layer and enable mouse and keyboard manipulation of all its components. */
62 public final class Display
extends DBObject
implements ActionListener
, ImageListener
{
64 /** The Layer this Display is showing. */
67 private Displayable active
= null;
68 /** All selected Displayable objects, including the active one. */
69 final private Selection selection
= new Selection(this);
71 private ImagePlus last_temp
= null;
74 private JTabbedPane tabs
;
75 private Hashtable
<Class
,JScrollPane
> ht_tabs
;
76 private JScrollPane scroll_patches
;
77 private JPanel panel_patches
;
78 private JScrollPane scroll_profiles
;
79 private JPanel panel_profiles
;
80 private JScrollPane scroll_zdispl
;
81 private JPanel panel_zdispl
;
82 private JScrollPane scroll_channels
;
83 private JPanel panel_channels
;
84 private JScrollPane scroll_labels
;
85 private JPanel panel_labels
;
87 private JPanel panel_layers
;
88 private JScrollPane scroll_layers
;
89 private Hashtable
<Layer
,LayerPanel
> layer_panels
= new Hashtable
<Layer
,LayerPanel
>();
91 private JSlider transp_slider
;
92 private DisplayNavigator navigator
;
93 private JScrollBar scroller
;
95 private DisplayCanvas canvas
; // WARNING this is an AWT component, since it extends ImageCanvas
96 private JPanel canvas_panel
; // and this is a workaround, to better (perhaps) integrate the awt canvas inside a JSplitPane
97 private JSplitPane split
;
99 private JPopupMenu popup
= null;
101 /** Contains the packed alphas of every channel. */
102 private int c_alphas
= 0xffffffff; // all 100 % visible
103 private Channel
[] channels
;
105 private Hashtable
<Displayable
,DisplayablePanel
> ht_panels
= new Hashtable
<Displayable
,DisplayablePanel
>();
107 /** Handle drop events, to insert image files. */
108 private DNDInsertImage dnd
;
110 private boolean size_adjusted
= false;
112 private int scroll_step
= 1;
114 /** Keep track of all existing Display objects. */
115 static private ArrayList
<Display
> al_displays
= new ArrayList
<Display
>();
116 /** The currently focused Display, if any. */
117 static private Display front
= null;
119 /** Displays to open when all objects have been reloaded from the database. */
120 static private final Hashtable ht_later
= new Hashtable();
122 /** A thread to handle user actions, for example an event sent from a popup menu. */
123 private final Dispatcher dispatcher
= new Dispatcher();
125 static private WindowAdapter window_listener
= new WindowAdapter() {
126 /** Unregister the closed Display. */
127 public void windowClosing(WindowEvent we
) {
128 final Object source
= we
.getSource();
129 for (Iterator it
= al_displays
.iterator(); it
.hasNext(); ) {
130 Display d
= (Display
)it
.next();
131 if (source
== d
.frame
) {
133 if (d
== front
) front
= null;
134 d
.remove(false); //calls destroy
139 /** Set the source Display as front. */
140 public void windowActivated(WindowEvent we
) {
141 // find which was it to make it be the front
142 final Object source
= we
.getSource();
143 for (final Display d
: al_displays
) {
144 if (source
== d
.frame
) {
147 ProjectToolbar
.setProjectToolbar();
148 // now, select the layer in the LayerTree
149 front
.getProject().select(front
.layer
);
150 // finally, set the virtual ImagePlus that ImageJ will see
151 d
.setTempCurrentImage();
152 // copied from ij.gui.ImageWindow, with modifications
153 if (IJ
.isMacintosh() && IJ
.getInstance()!=null) {
154 IJ
.wait(10); // may be needed for Java 1.4 on OS X
155 d
.frame
.setMenuBar(ij
.Menus
.getMenuBar());
160 // else, restore the ImageJ toolbar for non-project images
161 //if (!source.equals(IJ.getInstance())) {
162 // ProjectToolbar.setImageJToolbar();
165 /** Restore the ImageJ toolbar */
166 public void windowDeactivated(WindowEvent we
) {
167 // Can't, the user can never click the ProjectToolbar then. This has to be done in a different way, for example checking who is the WindowManager.getCurrentImage (and maybe setting a dummy image into it) //ProjectToolbar.setImageJToolbar();
169 /** Call a pack() when the window is maximized to fit the canvas correctly. */
170 public void windowStateChanged(WindowEvent we
) {
171 final Object source
= we
.getSource();
172 for (final Display d
: al_displays
) {
173 if (source
!= d
.frame
) continue;
180 static private MouseListener frame_mouse_listener
= new MouseAdapter() {
181 public void mouseReleased(MouseEvent me
) {
182 Object source
= me
.getSource();
183 for (final Display d
: al_displays
) {
184 if (d
.frame
== source
) {
185 if (d
.size_adjusted
) {
187 d
.size_adjusted
= false;
188 Utils
.log2("mouse released on JFrame");
196 private int last_frame_state
= frame
.NORMAL
;
198 // THIS WHOLE SYSTEM OF LISTENERS IS BROKEN:
199 // * when zooming in, the window growths in width a few pixels.
200 // * when enlarging the window quickly, the canvas is not resized as large as it should.
201 // -- the whole problem: swing threading, which I am not handling properly. It's hard.
202 static private ComponentListener component_listener
= new ComponentAdapter() {
203 public void componentResized(ComponentEvent ce
) {
204 final Display d
= getDisplaySource(ce
);
206 d
.size_adjusted
= true; // works in combination with mouseReleased to call pack(), avoiding infinite loops.
208 int frame_state
= d
.frame
.getExtendedState();
209 if (frame_state
!= d
.last_frame_state
) { // this setup avoids infinite loops (for pack() calls componentResized as well
210 d
.last_frame_state
= frame_state
;
211 if (d
.frame
.ICONIFIED
!= frame_state
) d
.pack();
215 public void componentMoved(ComponentEvent ce
) {
216 Display d
= getDisplaySource(ce
);
217 if (null != d
) d
.updateInDatabase("position");
219 private Display
getDisplaySource(ComponentEvent ce
) {
220 final Object source
= ce
.getSource();
221 for (final Display d
: al_displays
) {
222 if (source
== d
.frame
) {
230 static private ChangeListener tabs_listener
= new ChangeListener() {
231 /** Listen to tab changes. */
232 public void stateChanged(final ChangeEvent ce
) {
233 final Object source
= ce
.getSource();
234 for (final Display d
: al_displays
) {
235 if (source
== d
.tabs
) {
236 d
.dispatcher
.exec(new Runnable() { public void run() {
237 // creating tabs fires the event!!!
238 if (null == d
.frame
|| null == d
.canvas
) return;
239 final Container tab
= (Container
)d
.tabs
.getSelectedComponent();
240 if (tab
== d
.scroll_channels
) {
241 // find active channel if any
242 for (int i
=0; i
<d
.channels
.length
; i
++) {
243 if (d
.channels
[i
].isActive()) {
244 d
.transp_slider
.setValue((int)(d
.channels
[i
].getAlpha() * 100));
251 int count = tab.getComponentCount();
252 if (0 == count || (1 == count && tab.getComponent(0).getClass().equals(JLabel.class))) {
253 */ // ALWAYS, because it could be the case that the user changes layer while on one specific tab, and then clicks on the other tab which may not be empty and shows totally the wrong contents (i.e. for another layer)
258 if (tab
== d
.scroll_zdispl
) {
259 label
= "Z-space objects";
260 al
= d
.layer
.getParent().getZDisplayables();
262 } else if (tab
== d
.scroll_patches
) {
264 al
= d
.layer
.getDisplayables(Patch
.class);
266 } else if (tab
== d
.scroll_labels
) {
268 al
= d
.layer
.getDisplayables(DLabel
.class);
270 } else if (tab
== d
.scroll_profiles
) {
272 al
= d
.layer
.getDisplayables(Profile
.class);
273 p
= d
.panel_profiles
;
274 } else if (tab
== d
.scroll_layers
) {
279 d
.updateTab(p
, label
, al
);
280 //Utils.updateComponent(d.tabs.getSelectedComponent());
281 //Utils.log2("updated tab: " + p + " with " + al.size() + " objects.");
284 if (null != d
.active
) {
285 // set the transp slider to the alpha value of the active Displayable if any
286 d
.transp_slider
.setValue((int)(d
.active
.getAlpha() * 100));
287 DisplayablePanel dp
= d
.ht_panels
.get(d
.active
);
288 if (null != dp
) dp
.setActive(true);
298 private final ScrollLayerListener scroller_listener
= new ScrollLayerListener();
300 private class ScrollLayerListener
implements AdjustmentListener
{
302 public void adjustmentValueChanged(final AdjustmentEvent ae
) {
303 final int index
= scroller
.getValue();
304 slt
.set(layer
.getParent().getLayer(index
));
308 private final SetLayerThread slt
= new SetLayerThread();
310 private class SetLayerThread
extends Thread
{
312 private boolean go
= true;
314 private final Lock lock
= new Lock();
315 private final Lock lock2
= new Lock();
318 setPriority(Thread
.NORM_PRIORITY
);
323 public final void set(final Layer layer
) {
324 synchronized (lock
) {
327 synchronized (this) {
332 public final void setAndWait(final Layer layer
) {
339 while (null == this.layer
) {
340 synchronized (this) {
341 try { wait(); } catch (InterruptedException ie
) {}
345 synchronized (lock
) {
350 if (!go
) return; // after nullifying layer
353 Display
.this.setLayer(layer
);
354 Display
.this.updateInDatabase("layer_id");
356 // unlock any calls waiting on setAndWait
357 synchronized (lock2
) {
362 synchronized (lock2
) {
367 public void waitForLayer() {
368 while (null != layer
&& go
) {
369 try { Thread
.sleep(10); } catch (Exception e
) {}
378 /** Creates a new Display with adjusted magnification to fit in the screen. */
379 static public void createDisplay(final Project project
, final Layer layer
) {
380 SwingUtilities
.invokeLater(new Runnable() { public void run() {
381 Display display
= new Display(project
, layer
);
382 Dimension screen
= Toolkit
.getDefaultToolkit().getScreenSize();
383 Rectangle srcRect
= new Rectangle(0, 0, (int)layer
.getLayerWidth(), (int)layer
.getLayerHeight());
384 double mag
= screen
.width
/ layer
.getLayerWidth();
385 if (mag
* layer
.getLayerHeight() > screen
.height
) mag
= screen
.height
/ layer
.getLayerHeight();
386 mag
= display
.canvas
.getLowerZoomLevel2(mag
);
387 if (mag
> 1.0) mag
= 1.0;
388 //display.getCanvas().setup(mag, srcRect); // would call pack() at the wrong time!
389 // ... so instead: manually
390 display
.getCanvas().setMagnification(mag
);
391 display
.getCanvas().setSrcRect(srcRect
.x
, srcRect
.y
, srcRect
.width
, srcRect
.height
);
392 display
.getCanvas().setDrawingSize((int)Math
.ceil(srcRect
.width
* mag
), (int)Math
.ceil(srcRect
.height
* mag
));
394 display
.updateTitle();
395 ij
.gui
.GUI
.center(display
.frame
);
396 display
.frame
.pack();
400 /** A new Display from scratch, to show the given Layer. */
401 public Display(Project project
, final Layer layer
) {
404 makeGUI(layer
, null);
405 ImagePlus
.addImageListener(this);
407 this.layer
= layer
; // after, or it doesn't update properly
408 al_displays
.add(this);
412 /** For reconstruction purposes. The Display will be stored in the ht_later.*/
413 public Display(Project project
, long id
, Layer layer
, Object
[] props
) {
415 synchronized (ht_later
) {
416 Display
.ht_later
.put(this, props
);
421 /** Open a new Display centered around the given Displayable. */
422 public Display(Project project
, Layer layer
, Displayable displ
) {
426 makeGUI(layer
, null);
427 ImagePlus
.addImageListener(this);
429 this.layer
= layer
; // after set layer!
430 al_displays
.add(this);
434 /** Reconstruct a Display from an XML entry, to be opened when everything is ready. */
435 public Display(Project project
, long id
, Layer layer
, HashMap ht_attributes
) {
438 Utils
.log2("Display: need a non-null Layer for id=" + id
);
441 Rectangle srcRect
= new Rectangle(0, 0, (int)layer
.getLayerWidth(), (int)layer
.getLayerHeight());
442 double magnification
= 0.25;
443 Point p
= new Point(0, 0);
444 int c_alphas
= 0xffffffff;
445 int c_alphas_state
= 0xffffffff;
446 for (Iterator it
= ht_attributes
.entrySet().iterator(); it
.hasNext(); ) {
447 Map
.Entry entry
= (Map
.Entry
)it
.next();
448 String key
= (String
)entry
.getKey();
449 String data
= (String
)entry
.getValue();
450 if (key
.equals("srcrect_x")) { // reflection! Reflection!
451 srcRect
.x
= Integer
.parseInt(data
);
452 } else if (key
.equals("srcrect_y")) {
453 srcRect
.y
= Integer
.parseInt(data
);
454 } else if (key
.equals("srcrect_width")) {
455 srcRect
.width
= Integer
.parseInt(data
);
456 } else if (key
.equals("srcrect_height")) {
457 srcRect
.height
= Integer
.parseInt(data
);
458 } else if (key
.equals("magnification")) {
459 magnification
= Double
.parseDouble(data
);
460 } else if (key
.equals("x")) {
461 p
.x
= Integer
.parseInt(data
);
462 } else if (key
.equals("y")) {
463 p
.y
= Integer
.parseInt(data
);
464 } else if (key
.equals("c_alphas")) {
466 c_alphas
= Integer
.parseInt(data
);
467 } catch (Exception ex
) {
468 c_alphas
= 0xffffffff;
470 } else if (key
.equals("c_alphas_state")) {
472 c_alphas_state
= Integer
.parseInt(data
);
473 } catch (Exception ex
) {
475 c_alphas_state
= 0xffffffff;
477 } else if (key
.equals("scroll_step")) {
479 setScrollStep(Integer
.parseInt(data
));
480 } catch (Exception ex
) {
485 // TODO the above is insecure, in that data is not fully checked to be within bounds.
487 Object
[] props
= new Object
[]{p
, new Double(magnification
), srcRect
, new Long(layer
.getId()), new Integer(c_alphas
), new Integer(c_alphas_state
)};
488 synchronized (ht_later
) {
489 Display
.ht_later
.put(this, props
);
494 /** After reloading a project from the database, open the Displays that the project had. */
495 static public Bureaucrat
openLater() {
496 final Hashtable ht_later_local
;
497 synchronized (ht_later
) {
498 if (0 == ht_later
.size()) return null;
499 ht_later_local
= new Hashtable(ht_later
);
500 ht_later
.keySet().removeAll(ht_later_local
.keySet());
502 final Worker worker
= new Worker("Opening displays") {
506 Thread
.sleep(300); // waiting for Swing
508 for (Enumeration e
= ht_later_local
.keys(); e
.hasMoreElements(); ) {
509 final Display d
= (Display
)e
.nextElement();
510 front
= d
; // must be set before repainting any ZDisplayable!
511 Object
[] props
= (Object
[])ht_later_local
.get(d
);
512 if (ControlWindow
.isGUIEnabled()) d
.makeGUI(d
.layer
, props
);
513 d
.setLayerLater(d
.layer
, d
.layer
.get(((Long
)props
[3]).longValue())); //important to do it after makeGUI
514 if (!ControlWindow
.isGUIEnabled()) continue;
515 ImagePlus
.addImageListener(d
);
518 // force a repaint if a prePaint was done TODO this should be properly managed with repaints using always the invokeLater, but then it's DOG SLOW
519 if (d
.canvas
.getMagnification() > 0.499) {
520 SwingUtilities
.invokeLater(new Runnable() { public void run() {
522 d
.project
.getLoader().setChanged(false);
523 Utils
.log2("A set to false");
526 d
.project
.getLoader().setChanged(false);
527 Utils
.log2("B set to false");
529 if (null != front
) front
.getProject().select(front
.layer
);
531 } catch (Throwable t
) {
538 return Bureaucrat
.createAndStart(worker
, ((Display
)ht_later_local
.keySet().iterator().next()).getProject()); // gets the project from the first Display
541 private void makeGUI(final Layer layer
, final Object
[] props
) {
545 Rectangle srcRect
= null;
548 mag
= ((Double
)props
[1]).doubleValue();
549 srcRect
= (Rectangle
)props
[2];
552 // transparency slider
553 this.transp_slider
= new JSlider(javax
.swing
.SwingConstants
.HORIZONTAL
, 0, 100, 100);
554 this.transp_slider
.setBackground(Color
.white
);
555 this.transp_slider
.setMinimumSize(new Dimension(250, 20));
556 this.transp_slider
.setMaximumSize(new Dimension(250, 20));
557 this.transp_slider
.setPreferredSize(new Dimension(250, 20));
558 TransparencySliderListener tsl
= new TransparencySliderListener();
559 this.transp_slider
.addChangeListener(tsl
);
560 this.transp_slider
.addMouseListener(tsl
);
561 for (final KeyListener kl
: this.transp_slider
.getKeyListeners()) {
562 this.transp_slider
.removeKeyListener(kl
);
565 // Tabbed pane on the left
566 this.tabs
= new JTabbedPane();
567 this.tabs
.setMinimumSize(new Dimension(250, 300));
568 this.tabs
.setBackground(Color
.white
);
569 this.tabs
.addChangeListener(tabs_listener
);
572 this.panel_patches
= makeTabPanel();
573 this.panel_patches
.add(new JLabel("No patches."));
574 this.scroll_patches
= makeScrollPane(panel_patches
);
575 this.tabs
.add("Patches", scroll_patches
);
578 this.panel_profiles
= makeTabPanel();
579 this.panel_profiles
.add(new JLabel("No profiles."));
580 this.scroll_profiles
= makeScrollPane(panel_profiles
);
581 this.tabs
.add("Profiles", scroll_profiles
);
584 this.panel_zdispl
= makeTabPanel();
585 this.panel_zdispl
.add(new JLabel("No objects."));
586 this.scroll_zdispl
= makeScrollPane(panel_zdispl
);
587 this.tabs
.add("Z space", scroll_zdispl
);
590 this.panel_channels
= makeTabPanel();
591 this.scroll_channels
= makeScrollPane(panel_channels
);
592 this.channels
= new Channel
[4];
593 this.channels
[0] = new Channel(this, Channel
.MONO
);
594 this.channels
[1] = new Channel(this, Channel
.RED
);
595 this.channels
[2] = new Channel(this, Channel
.GREEN
);
596 this.channels
[3] = new Channel(this, Channel
.BLUE
);
597 //this.panel_channels.add(this.channels[0]);
598 this.panel_channels
.add(this.channels
[1]);
599 this.panel_channels
.add(this.channels
[2]);
600 this.panel_channels
.add(this.channels
[3]);
601 this.tabs
.add("Opacity", scroll_channels
);
604 this.panel_labels
= makeTabPanel();
605 this.panel_labels
.add(new JLabel("No labels."));
606 this.scroll_labels
= makeScrollPane(panel_labels
);
607 this.tabs
.add("Labels", scroll_labels
);
610 this.panel_layers
= makeTabPanel();
611 this.scroll_layers
= makeScrollPane(panel_layers
);
612 recreateLayerPanels(layer
);
613 this.scroll_layers
.addMouseWheelListener(canvas
);
614 this.tabs
.add("Layers", scroll_layers
);
616 this.ht_tabs
= new Hashtable
<Class
,JScrollPane
>();
617 this.ht_tabs
.put(Patch
.class, scroll_patches
);
618 this.ht_tabs
.put(Profile
.class, scroll_profiles
);
619 this.ht_tabs
.put(ZDisplayable
.class, scroll_zdispl
);
620 this.ht_tabs
.put(AreaList
.class, scroll_zdispl
);
621 this.ht_tabs
.put(Pipe
.class, scroll_zdispl
);
622 this.ht_tabs
.put(Polyline
.class, scroll_zdispl
);
623 this.ht_tabs
.put(Ball
.class, scroll_zdispl
);
624 this.ht_tabs
.put(Dissector
.class, scroll_zdispl
);
625 this.ht_tabs
.put(DLabel
.class, scroll_labels
);
626 // channels not included
627 // layers not included
630 this.navigator
= new DisplayNavigator(this, layer
.getLayerWidth(), layer
.getLayerHeight());
631 // Layer scroller (to scroll slices)
632 int extent
= (int)(250.0 / layer
.getParent().size());
633 if (extent
< 10) extent
= 10;
634 this.scroller
= new JScrollBar(JScrollBar
.HORIZONTAL
);
635 updateLayerScroller(layer
);
636 this.scroller
.addAdjustmentListener(scroller_listener
);
639 // Left panel, contains the transp slider, the tabbed pane, the navigation panel and the layer scroller
640 JPanel left
= new JPanel();
641 BoxLayout left_layout
= new BoxLayout(left
, BoxLayout
.Y_AXIS
);
642 left
.setLayout(left_layout
);
643 left
.add(transp_slider
);
649 this.canvas
= new DisplayCanvas(this, (int)Math
.ceil(layer
.getLayerWidth()), (int)Math
.ceil(layer
.getLayerHeight()));
650 this.canvas_panel
= new JPanel();
651 GridBagLayout gb
= new GridBagLayout();
652 this.canvas_panel
.setLayout(gb
);
653 GridBagConstraints c
= new GridBagConstraints();
654 c
.fill
= GridBagConstraints
.BOTH
;
655 c
.anchor
= GridBagConstraints
.NORTHWEST
;
656 gb
.setConstraints(this.canvas_panel
, c
);
657 gb
.setConstraints(this.canvas
, c
);
659 // prevent new Displays from screweing up if input is globally disabled
660 if (!project
.isInputEnabled()) this.canvas
.setReceivesInput(false);
662 this.canvas_panel
.add(canvas
);
664 this.navigator
.addMouseWheelListener(canvas
);
666 this.transp_slider
.addKeyListener(canvas
);
668 // Split pane to contain everything
669 this.split
= new JSplitPane(JSplitPane
.HORIZONTAL_SPLIT
, left
, canvas_panel
);
670 this.split
.setOneTouchExpandable(true); // NOT present in all L&F (?)
673 gb
.setConstraints(split
.getRightComponent(), c
);
675 // JFrame to show the split pane
676 this.frame
= ControlWindow
.createJFrame(layer
.toString());
677 if (IJ
.isMacintosh() && IJ
.getInstance()!=null) {
678 IJ
.wait(10); // may be needed for Java 1.4 on OS X
679 this.frame
.setMenuBar(ij
.Menus
.getMenuBar());
681 this.frame
.addWindowListener(window_listener
);
682 this.frame
.addComponentListener(component_listener
);
683 this.frame
.getContentPane().add(split
);
684 this.frame
.addMouseListener(frame_mouse_listener
);
685 //doesn't exist//this.frame.setMinimumSize(new Dimension(270, 600));
689 canvas
.setup(mag
, srcRect
);
690 // restore visibility of each channel
691 int cs
= ((Integer
)props
[5]).intValue(); // aka c_alphas_state
692 int[] sel
= new int[4];
693 sel
[0] = ((cs
&0xff000000)>>24);
694 sel
[1] = ((cs
&0xff0000)>>16);
695 sel
[2] = ((cs
&0xff00)>>8);
697 // restore channel alphas
698 this.c_alphas
= ((Integer
)props
[4]).intValue();
699 channels
[0].setAlpha( (float)((c_alphas
&0xff000000)>>24) / 255.0f
, 0 != sel
[0]);
700 channels
[1].setAlpha( (float)((c_alphas
&0xff0000)>>16) / 255.0f
, 0 != sel
[1]);
701 channels
[2].setAlpha( (float)((c_alphas
&0xff00)>>8) / 255.0f
, 0 != sel
[2]);
702 channels
[3].setAlpha( (float) (c_alphas
&0xff) / 255.0f
, 0 != sel
[3]);
703 // restore visibility in the working c_alphas
704 this.c_alphas
= ((0 != sel
[0] ?
(int)(255 * channels
[0].getAlpha()) : 0)<<24) + ((0 != sel
[1] ?
(int)(255 * channels
[1].getAlpha()) : 0)<<16) + ((0 != sel
[2] ?
(int)(255 * channels
[2].getAlpha()) : 0)<<8) + (0 != sel
[3] ?
(int)(255 * channels
[3].getAlpha()) : 0);
707 if (null != active
&& null != layer
) {
708 Rectangle r
= active
.getBoundingBox();
712 r
.height
+= r
.height
;
713 if (r
.x
< 0) r
.x
= 0;
714 if (r
.y
< 0) r
.y
= 0;
715 if (r
.width
> layer
.getLayerWidth()) r
.width
= (int)layer
.getLayerWidth();
716 if (r
.height
> layer
.getLayerHeight())r
.height
= (int)layer
.getLayerHeight();
717 double magn
= layer
.getLayerWidth() / (double)r
.width
;
718 canvas
.setup(magn
, r
);
721 // add keyListener to the whole frame
722 this.tabs
.addKeyListener(canvas
);
723 this.canvas_panel
.addKeyListener(canvas
);
724 this.frame
.addKeyListener(canvas
);
727 ij
.gui
.GUI
.center(this.frame
);
728 this.frame
.setVisible(true);
729 ProjectToolbar
.setProjectToolbar(); // doesn't get it through events
731 final Dimension screen
= Toolkit
.getDefaultToolkit().getScreenSize();
734 // fix positioning outside the screen (dual to single monitor)
735 if (p
.x
>= 0 && p
.x
< screen
.width
- 50 && p
.y
>= 0 && p
.y
<= screen
.height
- 50) this.frame
.setLocation(p
);
736 else frame
.setLocation(0, 0);
739 // fix excessive size
740 final Rectangle box
= this.frame
.getBounds();
743 int width
= box
.width
;
744 int height
= box
.height
;
745 if (box
.width
> screen
.width
) { x
= 0; width
= screen
.width
; }
746 if (box
.height
> screen
.height
) { y
= 0; height
= screen
.height
; }
747 if (x
!= box
.x
|| y
!= box
.y
) {
748 this.frame
.setLocation(x
, y
+ (0 == y ?
30 : 0)); // added insets for bad window managers
749 updateInDatabase("position");
751 if (width
!= box
.width
|| height
!= box
.height
) {
752 this.frame
.setSize(new Dimension(width
-10, height
-30)); // added insets for bad window managers
755 // try to optimize canvas dimensions and magn
756 double magn
= layer
.getLayerHeight() / screen
.height
;
757 if (magn
> 1.0) magn
= 1.0;
759 // limit magnification if appropriate
760 for (Iterator it
= layer
.getDisplayables(Patch
.class).iterator(); it
.hasNext(); ) {
761 final Patch pa
= (Patch
)it
.next();
762 final Rectangle ba
= pa
.getBoundingBox();
763 size
+= (long)(ba
.width
* ba
.height
);
765 if (size
> 10000000) canvas
.setInitialMagnification(0.25); // 10 Mb
767 this.frame
.setSize(new Dimension((int)(screen
.width
* 0.66), (int)(screen
.height
* 0.66)));
771 Utils
.updateComponent(tabs
); // otherwise fails in FreeBSD java 1.4.2 when reconstructing
774 // Set the calibration of the FakeImagePlus to that of the LayerSet
775 ((FakeImagePlus
)canvas
.getFakeImagePlus()).setCalibrationSuper(layer
.getParent().getCalibrationCopy());
777 // Set the FakeImagePlus as the current image
778 setTempCurrentImage();
780 // create a drag and drop listener
781 dnd
= new DNDInsertImage(this);
783 // start a repainting thread
785 canvas
.repaint(true); // repaint() is unreliable
788 // Set the minimum size of the tabbed pane on the left, so it can be completely collapsed now that it has been properly displayed. This is a patch to the lack of respect for the setDividerLocation method.
789 SwingUtilities
.invokeLater(new Runnable() {
791 tabs
.setMinimumSize(new Dimension(0, 100));
792 Display
.scrollbar_width
= Display
.this.scroll_patches
.getVerticalScrollBar().getPreferredSize().width
; // using scroll_patches since it's the one selected by default and thus visible and painted
797 private JPanel
makeTabPanel() {
798 JPanel panel
= new JPanel();
799 BoxLayout layout
= new BoxLayout(panel
, BoxLayout
.Y_AXIS
);
800 panel
.setLayout(layout
);
804 private JScrollPane
makeScrollPane(Component c
) {
805 JScrollPane jsp
= new JScrollPane(c
);
806 // adjust scrolling to use one DisplayablePanel as the minimal unit
807 jsp
.getVerticalScrollBar().setBlockIncrement(DisplayablePanel
.HEIGHT
); // clicking within the track
808 jsp
.getVerticalScrollBar().setUnitIncrement(DisplayablePanel
.HEIGHT
); // clicking on an arrow
809 jsp
.setHorizontalScrollBarPolicy(javax
.swing
.ScrollPaneConstants
.HORIZONTAL_SCROLLBAR_NEVER
);
810 jsp
.setPreferredSize(new Dimension(250, 300));
811 jsp
.setMinimumSize(new Dimension(250, 300));
815 static protected int scrollbar_width
= 0;
817 public JPanel
getCanvasPanel() {
821 public DisplayCanvas
getCanvas() {
825 public synchronized void setLayer(final Layer layer
) {
826 if (null == layer
|| layer
== this.layer
) return;
827 translateLayerColors(this.layer
, layer
);
828 if (tabs
.getSelectedComponent() == scroll_layers
) scrollToShow(scroll_layers
, layer_panels
.get(layer
));
829 final boolean set_zdispl
= null == Display
.this.layer
|| layer
.getParent() != Display
.this.layer
.getParent();
830 if (selection
.isTransforming()) {
831 Utils
.log("Can't browse layers while transforming.\nCANCEL the transform first with the ESCAPE key or right-click -> cancel.");
832 scroller
.setValue(Display
.this.layer
.getParent().getLayerIndex(Display
.this.layer
.getId()));
836 scroller
.setValue(layer
.getParent().getLayerIndex(layer
.getId()));
838 // update the current Layer pointer in ZDisplayable objects
839 for (Iterator it
= layer
.getParent().getZDisplayables().iterator(); it
.hasNext(); ) {
840 ((ZDisplayable
)it
.next()).setLayer(layer
); // the active layer
843 updateVisibleTab(set_zdispl
);
845 // see if a lot has to be reloaded, put the relevant ones at the end
846 project
.getLoader().prepare(layer
);
847 updateTitle(); // to show the new 'z'
848 // select the Layer in the LayerTree
849 project
.select(Display
.this.layer
); // does so in a separate thread
850 // update active Displayable:
852 // deselect all except ZDisplayables
853 final ArrayList sel
= selection
.getSelected();
854 final Displayable last_active
= Display
.this.active
;
856 for (Iterator it
= sel
.iterator(); it
.hasNext(); ) {
857 Displayable d
= (Displayable
)it
.next();
858 if (!(d
instanceof ZDisplayable
)) {
861 if (d
== last_active
&& sel
.size() > 0) {
862 // select the last one of the remaining, if any
863 sel_next
= sel
.size()-1;
867 if (-1 != sel_next
&& sel
.size() > 0) select((Displayable
)sel
.get(sel_next
), true);
868 else if (null != last_active
&& last_active
.getClass() == Patch
.class && null != last_temp
&& last_temp
instanceof PatchStack
) {
869 Displayable d
= ((PatchStack
)last_temp
).getPatch(layer
, (Patch
)last_active
);
870 if (null != d
) selection
.add(d
);
872 // TODO last_temp doesn't remain the PatchStack // Utils.log2("last_temp is: " + last_temp.getClass().getName());
874 // Keep Profile chain selected, for best ease of use:
875 if (null != last_active
&& last_active
.getClass() == Profile
.class && last_active
.isLinked(Profile
.class)) {
876 Displayable other
= null;
877 for (final Displayable prof
: last_active
.getLinked(Profile
.class)) {
878 if (prof
.getLayer() == layer
) {
883 if (null != other
) selection
.add(other
);
886 // repaint everything
887 navigator
.repaint(true);
888 canvas
.repaint(true);
890 // repaint tabs (hard as hell)
891 Utils
.updateComponent(tabs
);
892 // @#$%^! The above works half the times, so explicit repaint as well:
893 Component c
= tabs
.getSelectedComponent();
896 tabs
.setSelectedComponent(scroll_patches
);
898 Utils
.updateComponent(c
);
900 project
.getLoader().setMassiveMode(false); // resetting if it was set true
902 // update the coloring in the ProjectTree
903 project
.getProjectTree().updateUILater();
905 setTempCurrentImage();
908 static public void updateVisibleTabs() {
909 for (final Display d
: al_displays
) {
910 d
.updateVisibleTab(true);
914 /** Recreate the tab that is being shown. */
915 public void updateVisibleTab(boolean set_zdispl
) {
916 // update only the visible tab
917 switch (tabs
.getSelectedIndex()) {
920 updateTab(panel_patches
, "Patches", layer
.getDisplayables(Patch
.class));
924 updateTab(panel_profiles
, "Profiles", layer
.getDisplayables(Profile
.class));
929 updateTab(panel_zdispl
, "Z-space objects", layer
.getParent().getZDisplayables());
932 // case 3: channel opacities
935 updateTab(panel_labels
, "Labels", layer
.getDisplayables(DLabel
.class));
941 private void setLayerLater(final Layer layer
, final Displayable active
) {
942 if (null == layer
) return;
944 if (!ControlWindow
.isGUIEnabled()) return;
945 SwingUtilities
.invokeLater(new Runnable() { public void run() {
946 // empty the tabs, except channels and pipes
947 clearTab(panel_profiles
, "Profiles");
948 clearTab(panel_patches
, "Patches");
949 clearTab(panel_labels
, "Labels");
950 // distribute Displayable to the tabs. Ignore LayerSet instances.
951 if (null == ht_panels
) ht_panels
= new Hashtable
<Displayable
,DisplayablePanel
>();
952 else ht_panels
.clear();
953 Iterator it
= layer
.getDisplayables().iterator();
954 while (it
.hasNext()) {
955 add((Displayable
)it
.next(), false, false);
957 it
= layer
.getParent().getZDisplayables().iterator(); // the pipes, that live in the LayerSet
958 while (it
.hasNext()) {
959 add((Displayable
)it
.next(), false, false);
961 navigator
.repaint(true); // was not done when adding
962 Utils
.updateComponent(tabs
.getSelectedComponent());
970 setPriority(Thread.NORM_PRIORITY);
971 try { Thread.sleep(1000); } catch (Exception e) {}
978 /** Remove all components from the tab and add a "No [label]" label to each. */
979 private void clearTab(final Container c
, final String label
) {
981 c
.add(new JLabel("No " + label
+ "."));
983 if (tabs
.getSelectedComponent() == c
) {
984 Utils
.updateComponent(c
);
988 /** A class to listen to the transparency_slider of the DisplayablesSelectorWindow. */
989 private class TransparencySliderListener
extends MouseAdapter
implements ChangeListener
{
991 public void stateChanged(ChangeEvent ce
) {
992 //change the transparency value of the current active displayable
993 float new_value
= (float)((JSlider
)ce
.getSource()).getValue();
994 setTransparency(new_value
/ 100.0f
);
997 public void mousePressed(MouseEvent me
) {
998 JScrollPane scroll
= (JScrollPane
)tabs
.getSelectedComponent();
999 if (scroll
!= scroll_channels
&& !selection
.isEmpty()) selection
.addDataEditStep(new String
[]{"alpha"});
1002 public void mouseReleased(MouseEvent me
) {
1003 // update navigator window
1004 navigator
.repaint(true);
1005 JScrollPane scroll
= (JScrollPane
)tabs
.getSelectedComponent();
1006 if (scroll
!= scroll_channels
&& !selection
.isEmpty()) selection
.addDataEditStep(new String
[]{"alpha"});
1010 /** Context-sensitive: to a Displayable, or to a channel. */
1011 private void setTransparency(final float value
) {
1012 JScrollPane scroll
= (JScrollPane
)tabs
.getSelectedComponent();
1013 if (scroll
== scroll_channels
) {
1014 for (int i
=0; i
<4; i
++) {
1015 if (channels
[i
].getBackground() == Color
.cyan
) {
1016 channels
[i
].setAlpha(value
); // will call back and repaint the Display
1020 } else if (null != active
) {
1021 if (value
!= active
.getAlpha()) { // because there's a callback from setActive that would then affect all other selected Displayable without having dragged the slider, i.e. just by being selected.
1022 canvas
.invalidateVolatile();
1023 selection
.setAlpha(value
);
1028 public void setTransparencySlider(final float transp
) {
1029 if (transp
>= 0.0f
&& transp
<= 1.0f
) {
1031 transp_slider
.setValue((int)(transp
* 100));
1035 /** Mark the canvas for updating the offscreen images if the given Displayable is NOT the active. */ // Used by the Displayable.setVisible for example.
1036 static public void setUpdateGraphics(final Layer layer
, final Displayable displ
) {
1037 for (final Display d
: al_displays
) {
1038 if (layer
== d
.layer
&& null != d
.active
&& d
.active
!= displ
) {
1039 d
.canvas
.setUpdateGraphics(true);
1044 /** Flag the DisplayCanvas of Displays showing the given Layer to update their offscreen images.*/
1045 static public void setUpdateGraphics(final Layer layer
, final boolean update
) {
1046 for (final Display d
: al_displays
) {
1047 if (layer
== d
.layer
) {
1048 d
.canvas
.setUpdateGraphics(update
);
1053 /** Whether to update the offscreen images or not. */
1054 public void setUpdateGraphics(boolean b
) {
1055 canvas
.setUpdateGraphics(b
);
1058 /** Find all Display instances that contain the layer and repaint them, in the Swing GUI thread. */
1059 static public void update(final Layer layer
) {
1060 if (null == layer
) return;
1061 SwingUtilities
.invokeLater(new Runnable() { public void run() {
1062 for (final Display d
: al_displays
) {
1063 if (d
.isShowing(layer
)) {
1070 static public void update(final LayerSet set
) {
1074 /** Find all Display instances showing a Layer of this LayerSet, and update the dimensions of the navigator and canvas and snapshots, and repaint, in the Swing GUI thread. */
1075 static public void update(final LayerSet set
, final boolean update_canvas_dimensions
) {
1076 if (null == set
) return;
1077 SwingUtilities
.invokeLater(new Runnable() { public void run() {
1078 for (final Display d
: al_displays
) {
1079 if (set
.contains(d
.layer
)) {
1080 d
.updateSnapshots();
1081 if (update_canvas_dimensions
) d
.canvas
.setDimensions(set
.getLayerWidth(), set
.getLayerHeight());
1088 /** Release all resources held by this Display and close the frame. */
1089 protected void destroy() {
1091 canvas
.setReceivesInput(false);
1094 // update the coloring in the ProjectTree and LayerTree
1095 if (!project
.isBeingDestroyed()) {
1097 project
.getProjectTree().updateUILater();
1098 project
.getLayerTree().updateUILater();
1099 } catch (Exception e
) {
1100 Utils
.log2("updateUI failed at Display.destroy()");
1104 frame
.removeComponentListener(component_listener
);
1105 frame
.removeWindowListener(window_listener
);
1106 frame
.removeWindowFocusListener(window_listener
);
1107 frame
.removeWindowStateListener(window_listener
);
1108 frame
.removeKeyListener(canvas
);
1109 frame
.removeMouseListener(frame_mouse_listener
);
1110 canvas_panel
.removeKeyListener(canvas
);
1111 canvas
.removeKeyListener(canvas
);
1112 tabs
.removeChangeListener(tabs_listener
);
1113 tabs
.removeKeyListener(canvas
);
1114 ImagePlus
.removeImageListener(this);
1115 bytypelistener
= null;
1117 navigator
.destroy();
1118 scroller
.removeAdjustmentListener(scroller_listener
);
1119 frame
.setVisible(false);
1120 //no need, and throws exception//frame.dispose();
1122 if (null != selection
) selection
.clear();
1123 //Utils.log2("destroying selection");
1125 // below, need for SetLayerThread threads to quit
1127 // set a new front if any
1128 if (null == front
&& al_displays
.size() > 0) {
1129 front
= (Display
)al_displays
.get(al_displays
.size() -1);
1131 // repaint layer tree (to update the label color)
1133 project
.getLayerTree().updateUILater(); // works only after setting the front above
1134 } catch (Exception e
) {} // ignore swing sync bullshit when closing everything too fast
1135 // remove the drag and drop listener
1139 /** Find all Display instances that contain a Layer of the given project and close them without removing the Display entries from the database. */
1140 static synchronized public void close(final Project project
) {
1141 /* // concurrent modifications if more than 1 Display are being removed asynchronously
1142 for (final Display d : al_displays) {
1143 if (d.getLayer().getProject().equals(project)) {
1149 Display
[] d
= new Display
[al_displays
.size()];
1150 al_displays
.toArray(d
);
1151 for (int i
=0; i
<d
.length
; i
++) {
1152 if (d
[i
].getProject() == project
) {
1153 al_displays
.remove(d
[i
]);
1159 /** Find all Display instances that contain the layer and close them and remove the Display from the database. */
1160 static public void close(final Layer layer
) {
1161 for (Iterator it
= al_displays
.iterator(); it
.hasNext(); ) {
1162 Display d
= (Display
)it
.next();
1163 if (d
.isShowing(layer
)) {
1170 public boolean remove(boolean check
) {
1172 if (!Utils
.check("Delete the Display ?")) return false;
1174 // flush the offscreen images and close the frame
1176 removeFromDatabase();
1180 public Layer
getLayer() {
1184 public LayerSet
getLayerSet() {
1185 return layer
.getParent();
1188 public boolean isShowing(final Layer layer
) {
1189 return this.layer
== layer
;
1192 public DisplayNavigator
getNavigator() {
1196 /** Repaint both the canvas and the navigator, updating the graphics, and the title and tabs. */
1197 public void repaintAll() {
1198 if (repaint_disabled
) return;
1199 navigator
.repaint(true);
1200 canvas
.repaint(true);
1201 Utils
.updateComponent(tabs
);
1205 /** Repaint the canvas updating graphics, the navigator without updating graphics, and the title. */
1206 public void repaintAll2() {
1207 if (repaint_disabled
) return;
1208 navigator
.repaint(false);
1209 canvas
.repaint(true);
1213 static public void repaintSnapshots(final LayerSet set
) {
1214 if (repaint_disabled
) return;
1215 for (final Display d
: al_displays
) {
1216 if (d
.getLayer().getParent() == set
) {
1217 d
.navigator
.repaint(true);
1218 Utils
.updateComponent(d
.tabs
);
1222 static public void repaintSnapshots(final Layer layer
) {
1223 if (repaint_disabled
) return;
1224 for (final Display d
: al_displays
) {
1225 if (d
.getLayer() == layer
) {
1226 d
.navigator
.repaint(true);
1227 Utils
.updateComponent(d
.tabs
);
1232 public void pack() {
1233 dispatcher
.exec(new Runnable() { public void run() {
1235 Thread
.currentThread().sleep(100);
1236 SwingUtilities
.invokeAndWait(new Runnable() { public void run() {
1239 } catch (Exception e
) { IJError
.print(e
); }
1243 static public void pack(final LayerSet ls
) {
1244 for (final Display d
: al_displays
) {
1245 if (d
.layer
.getParent() == ls
) d
.pack();
1249 private void adjustCanvas() {
1250 SwingUtilities
.invokeLater(new Runnable() { public void run() {
1251 Rectangle r
= split
.getRightComponent().getBounds();
1252 canvas
.setDrawingSize(r
.width
, r
.height
, true);
1253 // fix not-on-top-left problem
1254 canvas
.setLocation(0, 0);
1255 //frame.pack(); // don't! Would go into an infinite loop
1256 canvas
.repaint(true);
1257 updateInDatabase("srcRect");
1261 /** Grab the last selected display (or create an new one if none) and show in it the layer,centered on the Displayable object. */
1262 static public void setFront(final Layer layer
, final Displayable displ
) {
1263 if (null == front
) {
1264 Display display
= new Display(layer
.getProject(), layer
); // gets set to front
1265 display
.showCentered(displ
);
1266 } else if (layer
== front
.layer
) {
1267 front
.showCentered(displ
);
1270 for (final Display d
: al_displays
) {
1271 if (d
.layer
== layer
) {
1273 d
.showCentered(displ
);
1277 // else, open new one
1278 new Display(layer
.getProject(), layer
).showCentered(displ
);
1282 /** Find the displays that show the given Layer, and add the given Displayable to the GUI and sets it active only in the front Display and only if 'activate' is true. */
1283 static public void add(final Layer layer
, final Displayable displ
, final boolean activate
) {
1284 for (final Display d
: al_displays
) {
1285 if (d
.layer
== layer
) {
1287 d
.add(displ
, activate
, true);
1288 //front.frame.toFront();
1290 d
.add(displ
, false, true);
1296 static public void add(final Layer layer
, final Displayable displ
) {
1297 add(layer
, displ
, true);
1300 /** Add the ZDisplayable to all Displays that show a Layer belonging to the given LayerSet. */
1301 static public void add(final LayerSet set
, final ZDisplayable zdispl
) {
1302 for (final Display d
: al_displays
) {
1303 if (set
.contains(d
.layer
)) {
1305 zdispl
.setLayer(d
.layer
); // the active one
1306 d
.add(zdispl
, true, true); // calling add(Displayable, boolean, boolean)
1307 //front.frame.toFront();
1309 d
.add(zdispl
, false, true);
1315 static public void addAll(final Layer layer
, final Collection
<?
extends Displayable
> coll
) {
1316 for (final Display d
: al_displays
) {
1317 if (d
.layer
== layer
) {
1323 static public void addAll(final LayerSet set
, final Collection
<?
extends ZDisplayable
> coll
) {
1324 for (final Display d
: al_displays
) {
1325 if (set
.contains(d
.layer
)) {
1326 for (final ZDisplayable zd
: coll
) {
1327 if (front
== d
) zd
.setLayer(d
.layer
);
1334 private final void addAll(final Collection
<?
extends Displayable
> coll
) {
1335 for (final Displayable d
: coll
) {
1336 add(d
, false, false);
1339 Utils
.updateComponent(tabs
);
1340 navigator
.repaint(true);
1343 // TODO this very old method could take some improvement:
1344 // - there is no need to create a new DisplayablePanel if its panel is not shown
1345 // - other issues; the method looks overly "if a dog barks and a duck quacks during a lunar eclipse then .."
1346 /** Add it to the proper panel, at the top, and set it active. */
1347 private final void add(final Displayable d
, final boolean activate
, final boolean repaint_snapshot
) {
1348 DisplayablePanel dp
= ht_panels
.get(d
);
1349 if (null != dp
&& activate
) { // for ZDisplayable objects (TODO I think this is not used anymore)
1356 // add to the proper list
1358 if (d
instanceof Profile
) {
1360 } else if (d
instanceof Patch
) {
1362 } else if (d
instanceof DLabel
) {
1364 } else if (d
instanceof ZDisplayable
) { //both pipes and balls and AreaList
1370 dp
= new DisplayablePanel(this, d
); // TODO: instead of destroying/recreating, we could just recycle them by reassigning a different Displayable. See how it goes! It'd need a pool of objects
1371 addToPanel(p
, 0, dp
, activate
);
1372 ht_panels
.put(d
, dp
);
1379 if (repaint_snapshot
) navigator
.repaint(true);
1382 private void addToPanel(JPanel panel
, int index
, DisplayablePanel dp
, boolean repaint
) {
1384 if (1 == panel
.getComponentCount() && panel
.getComponent(0) instanceof JLabel
) {
1387 panel
.add(dp
, index
);
1389 Utils
.updateComponent(tabs
);
1393 /** Find the displays that show the given Layer, and remove the given Displayable from the GUI. */
1394 static public void remove(final Layer layer
, final Displayable displ
) {
1395 for (final Display d
: al_displays
) {
1396 if (layer
== d
.layer
) d
.remove(displ
);
1400 private void remove(final Displayable displ
) {
1401 DisplayablePanel ob
= ht_panels
.remove(displ
);
1403 final JScrollPane jsp
= ht_tabs
.get(displ
.getClass());
1405 JPanel p
= (JPanel
)jsp
.getViewport().getView();
1406 p
.remove((Component
)ob
);
1407 Utils
.revalidateComponent(p
);
1410 if (null == active
|| !selection
.contains(displ
)) {
1411 canvas
.setUpdateGraphics(true);
1413 canvas
.invalidateVolatile(); // removing active, no need to update offscreen but yes the volatile
1414 repaint(displ
, null, 5, true, false);
1415 // from Selection.deleteAll this method is called ... but it's ok: same thread, no locking problems.
1416 selection
.remove(displ
);
1419 static public void remove(final ZDisplayable zdispl
) {
1420 for (final Display d
: al_displays
) {
1421 if (zdispl
.getLayerSet() == d
.layer
.getParent()) {
1422 d
.remove((Displayable
)zdispl
);
1427 static public void repaint(final Layer layer
, final Displayable displ
, final int extra
) {
1428 repaint(layer
, displ
, displ
.getBoundingBox(), extra
);
1431 static public void repaint(final Layer layer
, final Displayable displ
, final Rectangle r
, final int extra
) {
1432 repaint(layer
, displ
, r
, extra
, true);
1435 /** Find the displays that show the given Layer, and repaint the given Displayable. */
1436 static public void repaint(final Layer layer
, final Displayable displ
, final Rectangle r
, final int extra
, final boolean repaint_navigator
) {
1437 if (repaint_disabled
) return;
1438 for (final Display d
: al_displays
) {
1439 if (layer
== d
.layer
) {
1440 d
.repaint(displ
, r
, extra
, repaint_navigator
, false);
1445 static public void repaint(final Displayable d
) {
1446 if (d
instanceof ZDisplayable
) repaint(d
.getLayerSet(), d
, d
.getBoundingBox(null), 5, true);
1447 repaint(d
.getLayer(), d
, d
.getBoundingBox(null), 5, true);
1450 /** Repaint as much as the bounding box around the given Displayable, or the r if not null. */
1451 private void repaint(final Displayable displ
, final Rectangle r
, final int extra
, final boolean repaint_navigator
, final boolean update_graphics
) {
1452 if (repaint_disabled
|| null == displ
) return;
1453 if (update_graphics
|| displ
.getClass() == Patch
.class || displ
!= active
) {
1454 canvas
.setUpdateGraphics(true);
1456 if (null != r
) canvas
.repaint(r
, extra
);
1457 else canvas
.repaint(displ
, extra
);
1458 if (repaint_navigator
) {
1459 DisplayablePanel dp
= ht_panels
.get(displ
);
1460 if (null != dp
) dp
.repaint(); // is null when creating it, or after deleting it
1461 navigator
.repaint(true); // everything
1465 /** Repaint the snapshot for the given Displayable both at the DisplayNavigator and on its panel,and only if it has not been painted before. This method is intended for the loader to know when to paint a snap, to avoid overhead. */
1466 static public void repaintSnapshot(final Displayable displ
) {
1467 for (final Display d
: al_displays
) {
1468 if (d
.layer
.contains(displ
)) {
1469 if (!d
.navigator
.isPainted(displ
)) {
1470 DisplayablePanel dp
= d
.ht_panels
.get(displ
);
1471 if (null != dp
) dp
.repaint(); // is null when creating it, or after deleting it
1472 d
.navigator
.repaint(displ
);
1478 /** Repaint the given Rectangle in all Displays showing the layer, updating the offscreen image if any. */
1479 static public void repaint(final Layer layer
, final Rectangle r
, final int extra
) {
1480 repaint(layer
, extra
, r
, true, true);
1483 static public void repaint(final Layer layer
, final int extra
, final Rectangle r
, final boolean update_navigator
) {
1484 repaint(layer
, extra
, r
, update_navigator
, true);
1487 static public void repaint(final Layer layer
, final int extra
, final Rectangle r
, final boolean update_navigator
, final boolean update_graphics
) {
1488 if (repaint_disabled
) return;
1489 for (final Display d
: al_displays
) {
1490 if (layer
== d
.layer
) {
1491 d
.canvas
.setUpdateGraphics(update_graphics
);
1492 d
.canvas
.repaint(r
, extra
);
1493 if (update_navigator
) {
1494 d
.navigator
.repaint(true);
1495 Utils
.updateComponent(d
.tabs
.getSelectedComponent());
1502 /** Repaint the given Rectangle in all Displays showing the layer, optionally updating the offscreen image (if any). */
1503 static public void repaint(final Layer layer
, final Rectangle r
, final int extra
, final boolean update_graphics
) {
1504 if (repaint_disabled
) return;
1505 for (final Display d
: al_displays
) {
1506 if (layer
== d
.layer
) {
1507 d
.canvas
.setUpdateGraphics(update_graphics
);
1508 d
.canvas
.repaint(r
, extra
);
1509 d
.navigator
.repaint(update_graphics
);
1510 if (update_graphics
) Utils
.updateComponent(d
.tabs
.getSelectedComponent());
1515 /** Repaint the DisplayablePanel (and DisplayNavigator) only for the given Displayable, in all Displays showing the given Layer. */
1516 static public void repaint(final Layer layer
, final Displayable displ
) {
1517 if (repaint_disabled
) return;
1518 for (final Display d
: al_displays
) {
1519 if (layer
== d
.layer
) {
1520 DisplayablePanel dp
= d
.ht_panels
.get(displ
);
1521 if (null != dp
) dp
.repaint();
1522 d
.navigator
.repaint(true);
1527 static public void repaint(LayerSet set
, Displayable displ
, int extra
) {
1528 repaint(set
, displ
, null, extra
);
1531 static public void repaint(LayerSet set
, Displayable displ
, Rectangle r
, int extra
) {
1532 repaint(set
, displ
, r
, extra
, true);
1535 /** Repaint the Displayable in every Display that shows a Layer belonging to the given LayerSet. */
1536 static public void repaint(final LayerSet set
, final Displayable displ
, final Rectangle r
, final int extra
, final boolean repaint_navigator
) {
1537 if (repaint_disabled
) return;
1538 for (final Display d
: al_displays
) {
1539 if (set
.contains(d
.layer
)) {
1540 if (repaint_navigator
) {
1541 if (null != displ
) {
1542 DisplayablePanel dp
= d
.ht_panels
.get(displ
);
1543 if (null != dp
) dp
.repaint();
1545 d
.navigator
.repaint(true);
1547 if (null == displ
|| displ
!= d
.active
) d
.setUpdateGraphics(true); // safeguard
1548 // paint the given box or the actual Displayable's box
1549 if (null != r
) d
.canvas
.repaint(r
, extra
);
1550 else d
.canvas
.repaint(displ
, extra
);
1555 /** Repaint the entire LayerSet, in all Displays showing a Layer of it.*/
1556 static public void repaint(final LayerSet set
) {
1557 if (repaint_disabled
) return;
1558 for (final Display d
: al_displays
) {
1559 if (set
.contains(d
.layer
)) {
1560 d
.navigator
.repaint(true);
1561 d
.canvas
.repaint(true);
1565 /** Repaint the given box in the LayerSet, in all Displays showing a Layer of it.*/
1566 static public void repaint(final LayerSet set
, final Rectangle box
) {
1567 if (repaint_disabled
) return;
1568 for (final Display d
: al_displays
) {
1569 if (set
.contains(d
.layer
)) {
1570 d
.navigator
.repaint(box
);
1571 d
.canvas
.repaint(box
, 0, true);
1575 /** Repaint the entire Layer, in all Displays showing it, including the tabs.*/
1576 static public void repaint(final Layer layer
) { // TODO this method overlaps with update(layer)
1577 if (repaint_disabled
) return;
1578 for (final Display d
: al_displays
) {
1579 if (layer
== d
.layer
) {
1580 d
.navigator
.repaint(true);
1581 d
.canvas
.repaint(true);
1586 /** Call repaint on all open Displays. */
1587 static public void repaint() {
1588 if (repaint_disabled
) {
1589 Utils
.logAll("Can't repaint -- repainting is disabled!");
1592 for (final Display d
: al_displays
) {
1593 d
.navigator
.repaint(true);
1594 d
.canvas
.repaint(true);
1598 static private boolean repaint_disabled
= false;
1600 /** Set a flag to enable/disable repainting of all Display instances. */
1601 static protected void setRepaint(boolean b
) {
1602 repaint_disabled
= !b
;
1605 public Rectangle
getBounds() {
1606 return frame
.getBounds();
1609 public Point
getLocation() {
1610 return frame
.getLocation();
1613 public JFrame
getFrame() {
1617 public void setLocation(Point p
) {
1618 this.frame
.setLocation(p
);
1621 public Displayable
getActive() {
1622 return active
; //TODO this should return selection.active !!
1625 public void select(Displayable d
) {
1629 /** Select/deselect accordingly to the current state and the shift key. */
1630 public void select(final Displayable d
, final boolean shift_down
) {
1631 if (null != active
&& active
!= d
&& active
.getClass() != Patch
.class) {
1632 // active is being deselected, so link underlying patches
1633 active
.linkPatches();
1636 //Utils.log2("Display.select: clearing selection");
1637 canvas
.setUpdateGraphics(true);
1642 //Utils.log2("Display.select: single selection");
1647 } else if (selection
.contains(d
)) {
1649 selection
.remove(d
);
1650 //Utils.log2("Display.select: removing from a selection");
1652 //Utils.log2("Display.select: activing within a selection");
1653 selection
.setActive(d
);
1656 //Utils.log2("Display.select: adding to an existing selection");
1659 // update the image shown to ImageJ
1660 // NO longer necessary, always he same FakeImagePlus // setTempCurrentImage();
1663 protected void choose(int screen_x_p
, int screen_y_p
, int x_p
, int y_p
, final Class c
) {
1664 choose(screen_x_p
, screen_y_p
, x_p
, y_p
, false, c
);
1666 protected void choose(int screen_x_p
, int screen_y_p
, int x_p
, int y_p
) {
1667 choose(screen_x_p
, screen_y_p
, x_p
, y_p
, false, null);
1670 /** Find a Displayable to add to the selection under the given point (which is in offscreen coords); will use a popup menu to give the user a range of Displayable objects to select from. */
1671 protected void choose(int screen_x_p
, int screen_y_p
, int x_p
, int y_p
, boolean shift_down
, Class c
) {
1672 //Utils.log("Display.choose: x,y " + x_p + "," + y_p);
1673 final ArrayList
<Displayable
> al
= new ArrayList
<Displayable
>(layer
.find(x_p
, y_p
, true));
1674 al
.addAll(layer
.getParent().findZDisplayables(layer
, x_p
, y_p
, true)); // only visible ones
1676 Displayable act
= this.active
;
1678 canvas
.setUpdateGraphics(true);
1679 //Utils.log("choose: set active to null");
1680 // fixing lack of repainting for unknown reasons, of the active one TODO this is a temporary solution
1681 if (null != act
) Display
.repaint(layer
, act
, 5);
1682 } else if (1 == al
.size()) {
1683 Displayable d
= (Displayable
)al
.get(0);
1684 if (null != c
&& d
.getClass() != c
) {
1688 select(d
, shift_down
);
1689 //Utils.log("choose 1: set active to " + active);
1691 if (al
.contains(active
) && !shift_down
) {
1695 // check if at least one of them is of class c
1696 // if only one is of class c, set as selected
1698 for (Iterator it
= al
.iterator(); it
.hasNext(); ) {
1699 Object ob
= it
.next();
1700 if (ob
.getClass() != c
) it
.remove();
1702 if (0 == al
.size()) {
1707 if (1 == al
.size()) {
1708 select((Displayable
)al
.get(0), shift_down
);
1711 // else, choose among the many
1713 choose(screen_x_p
, screen_y_p
, al
, shift_down
, x_p
, y_p
);
1715 //Utils.log("choose many: set active to " + active);
1719 private void choose(final int screen_x_p
, final int screen_y_p
, final Collection al
, final boolean shift_down
, final int x_p
, final int y_p
) {
1720 // show a popup on the canvas to choose
1723 final Object lock
= new Object();
1724 final DisplayableChooser d_chooser
= new DisplayableChooser(al
, lock
);
1725 final JPopupMenu pop
= new JPopupMenu("Select:");
1726 final Iterator itu
= al
.iterator();
1727 while (itu
.hasNext()) {
1728 Displayable d
= (Displayable
)itu
.next();
1729 JMenuItem menu_item
= new JMenuItem(d
.toString());
1730 menu_item
.addActionListener(d_chooser
);
1736 pop
.show(canvas
, screen_x_p
, screen_y_p
);
1740 //now wait until selecting something
1741 synchronized(lock
) {
1745 } catch (InterruptedException ie
) {}
1746 } while (d_chooser
.isWaiting() && pop
.isShowing());
1749 //grab the chosen Displayable object
1750 Displayable d
= d_chooser
.getChosen();
1751 //Utils.log("Chosen: " + d.toString());
1752 if (null == d
) { Utils
.log2("Display.choose: returning a null!"); }
1753 select(d
, shift_down
);
1754 pop
.setVisible(false);
1756 // fix selection bug: never receives mouseReleased event when the popup shows
1757 selection
.mouseReleased(null, x_p
, y_p
, x_p
, y_p
, x_p
, y_p
);
1762 /** Used by the Selection exclusively. This method will change a lot in the near future, and may disappear in favor of getSelection().getActive(). All this method does is update GUI components related to the currently active and the newly active Displayable; called through SwingUtilities.invokeLater. */
1763 protected void setActive(final Displayable displ
) {
1764 final Displayable prev_active
= this.active
;
1765 this.active
= displ
;
1766 SwingUtilities
.invokeLater(new Runnable() { public void run() {
1768 // renew current image if necessary
1769 if (null != displ
&& displ
== prev_active
) {
1770 // make sure the proper tab is selected.
1774 // deactivate previously active
1775 if (null != prev_active
) {
1776 final DisplayablePanel ob
= ht_panels
.get(prev_active
);
1777 if (null != ob
) ob
.setActive(false);
1778 // erase "decorations" of the previously active
1779 canvas
.repaint(selection
.getBox(), 4);
1781 // activate the new active
1782 if (null != displ
) {
1783 final DisplayablePanel ob
= ht_panels
.get(displ
);
1784 if (null != ob
) ob
.setActive(true);
1785 updateInDatabase("active_displayable_id");
1786 if (displ
.getClass() != Patch
.class) project
.select(displ
); // select the node in the corresponding tree, if any.
1787 // select the proper tab, and scroll to visible
1789 boolean update_graphics
= null == prev_active
|| paintsBelow(prev_active
, displ
); // or if it's an image, but that's by default in the repaint method
1790 repaint(displ
, null, 5, false, update_graphics
); // to show the border, and to repaint out of the background image
1791 transp_slider
.setValue((int)(displ
.getAlpha() * 100));
1793 //ensure decorations are removed from the panels, for Displayables in a selection besides the active one
1794 Utils
.updateComponent(tabs
.getSelectedComponent());
1799 /** If the other paints under the base. */
1800 public boolean paintsBelow(Displayable base
, Displayable other
) {
1801 boolean zd_base
= base
instanceof ZDisplayable
;
1802 boolean zd_other
= other
instanceof ZDisplayable
;
1804 if (base
instanceof DLabel
) return true; // zd paints under label
1805 if (!zd_base
) return false; // any zd paints over a mere displ if not a label
1807 // both zd, compare indices
1808 ArrayList
<ZDisplayable
> al
= other
.getLayerSet().getZDisplayables();
1809 return al
.indexOf(base
) > al
.indexOf(other
);
1813 // both displ, compare indices
1814 ArrayList
<Displayable
> al
= other
.getLayer().getDisplayables();
1815 return al
.indexOf(base
) > al
.indexOf(other
);
1817 // base is zd, other is d
1818 if (other
instanceof DLabel
) return false;
1824 /** Select the proper tab, and also scroll it to show the given Displayable -unless it's a LayerSet, and unless the proper tab is already showing. */
1825 private void selectTab(final Displayable displ
) {
1826 Method method
= null;
1828 if (!(displ
instanceof LayerSet
)) {
1829 method
= Display
.class.getDeclaredMethod("selectTab", new Class
[]{displ
.getClass()});
1831 } catch (Exception e
) {
1834 if (null != method
) {
1835 final Method me
= method
;
1836 dispatcher
.exec(new Runnable() { public void run() {
1838 me
.setAccessible(true);
1839 me
.invoke(Display
.this, new Object
[]{displ
});
1840 } catch (Exception e
) { IJError
.print(e
); }
1845 private void selectTab(Patch patch
) {
1846 tabs
.setSelectedComponent(scroll_patches
);
1847 scrollToShow(scroll_patches
, ht_panels
.get(patch
));
1850 private void selectTab(Profile profile
) {
1851 tabs
.setSelectedComponent(scroll_profiles
);
1852 scrollToShow(scroll_profiles
, ht_panels
.get(profile
));
1855 private void selectTab(DLabel label
) {
1856 tabs
.setSelectedComponent(scroll_labels
);
1857 scrollToShow(scroll_labels
, ht_panels
.get(label
));
1860 private void selectTab(ZDisplayable zd
) {
1861 tabs
.setSelectedComponent(scroll_zdispl
);
1862 scrollToShow(scroll_zdispl
, ht_panels
.get(zd
));
1865 private void selectTab(Pipe d
) { selectTab((ZDisplayable
)d
); }
1866 private void selectTab(Polyline d
) { selectTab((ZDisplayable
)d
); }
1867 private void selectTab(AreaList d
) { selectTab((ZDisplayable
)d
); }
1868 private void selectTab(Ball d
) { selectTab((ZDisplayable
)d
); }
1869 private void selectTab(Dissector d
) { selectTab((ZDisplayable
)d
); }
1871 /** A method to update the given tab, creating a new DisplayablePanel for each Displayable present in the given ArrayList, and storing it in the ht_panels (which is cleared first). */
1872 private void updateTab(final Container tab
, final String label
, final ArrayList al
) {
1873 final boolean[] recreated
= new boolean[]{false, true, true};
1874 dispatcher
.execSwing(new Runnable() { public void run() {
1876 if (0 == al
.size()) {
1878 tab
.add(new JLabel("No " + label
+ "."));
1880 Component
[] comp
= tab
.getComponents();
1882 if (1 == comp
.length
&& comp
[0].getClass() == JLabel
.class) {
1886 for (Iterator it
= al
.iterator(); it
.hasNext(); ) {
1887 Displayable d
= (Displayable
)it
.next();
1888 DisplayablePanel dp
= null;
1889 if (next
< comp
.length
) {
1890 dp
= (DisplayablePanel
)comp
[next
++]; // recycling panels
1893 dp
= new DisplayablePanel(Display
.this, d
);
1896 ht_panels
.put(d
, dp
);
1898 if (next
< comp
.length
) {
1899 // remove from the end, to avoid potential repaints of other panels
1900 for (int i
=comp
.length
-1; i
>=next
; i
--) {
1904 recreated
[0] = true;
1911 if (null != Display
.this.active
) scrollToShow(Display
.this.active
);
1912 } catch (Throwable e
) { IJError
.print(e
); }
1916 static public void setActive(final Object event
, final Displayable displ
) {
1917 if (!(event
instanceof InputEvent
)) return;
1918 // find which Display
1919 for (final Display d
: al_displays
) {
1920 if (d
.isOrigin((InputEvent
)event
)) {
1927 /** Find out whether this Display is Transforming its active Displayable. */
1928 public boolean isTransforming() {
1929 return canvas
.isTransforming();
1932 /** Find whether any Display is transforming the given Displayable. */
1933 static public boolean isTransforming(final Displayable displ
) {
1934 for (final Display d
: al_displays
) {
1935 if (null != d
.active
&& d
.active
== displ
&& d
.canvas
.isTransforming()) return true;
1940 static public boolean isAligning(final LayerSet set
) {
1941 for (final Display d
: al_displays
) {
1942 if (d
.layer
.getParent() == set
&& set
.isAligning()) {
1949 /** Set the front Display to transform the Displayable only if no other canvas is transforming it. */
1950 static public void setTransforming(final Displayable displ
) {
1951 if (null == front
) return;
1952 if (front
.active
!= displ
) return;
1953 for (final Display d
: al_displays
) {
1954 if (d
.active
== displ
) {
1955 if (d
.canvas
.isTransforming()) {
1956 Utils
.showMessage("Already transforming " + displ
.getTitle());
1961 front
.canvas
.setTransforming(true);
1964 /** Check whether the source of the event is located in this instance.*/
1965 private boolean isOrigin(InputEvent event
) {
1966 Object source
= event
.getSource();
1967 // find it ... check the canvas for now TODO
1968 if (canvas
== source
) {
1974 /** Get the layer of the front Display, or null if none.*/
1975 static public Layer
getFrontLayer() {
1976 if (null == front
) return null;
1980 /** Get the layer of an open Display of the given Project, or null if none.*/
1981 static public Layer
getFrontLayer(final Project project
) {
1982 if (null == front
) return null;
1983 if (front
.project
== project
) return front
.layer
;
1984 // else, find an open Display for the given Project, if any
1985 for (final Display d
: al_displays
) {
1986 if (d
.project
== project
) {
1991 return null; // none found
1994 static public Display
getFront(final Project project
) {
1995 if (null == front
) return null;
1996 if (front
.project
== project
) return front
;
1997 for (final Display d
: al_displays
) {
1998 if (d
.project
== project
) {
2006 public boolean isReadOnly() {
2007 // TEMPORARY: in the future one will be able show displays as read-only to other people, remotely
2011 static public void showPopup(Component c
, int x
, int y
) {
2012 if (null != front
) front
.getPopupMenu().show(c
, x
, y
);
2015 /** Return a context-sensitive popup menu. */
2016 public JPopupMenu
getPopupMenu() { // called from canvas
2017 // get the job canceling dialog
2018 if (!canvas
.isInputEnabled()) {
2019 return project
.getLoader().getJobsPopup(this);
2023 this.popup
= new JPopupMenu();
2024 JMenuItem item
= null;
2027 if (ProjectToolbar
.ALIGN
== Toolbar
.getToolId()) {
2028 boolean aligning
= layer
.getParent().isAligning();
2029 item
= new JMenuItem("Cancel alignment"); item
.addActionListener(this); popup
.add(item
);
2030 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_ESCAPE
, 0, true));
2031 if (!aligning
) item
.setEnabled(false);
2032 item
= new JMenuItem("Align with landmarks"); item
.addActionListener(this); popup
.add(item
);
2033 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_ENTER
, 0, true));
2034 if (!aligning
) item
.setEnabled(false);
2035 item
= new JMenuItem("Align and register"); item
.addActionListener(this); popup
.add(item
);
2036 if (!aligning
) item
.setEnabled(false);
2037 item
= new JMenuItem("Align using profiles"); item
.addActionListener(this); popup
.add(item
);
2038 if (!aligning
|| selection
.isEmpty() || !selection
.contains(Profile
.class)) item
.setEnabled(false);
2039 item
= new JMenuItem("Align stack slices"); item
.addActionListener(this); popup
.add(item
);
2040 if (selection
.isEmpty() || ! (getActive().getClass() == Patch
.class && ((Patch
)getActive()).isStack())) item
.setEnabled(false);
2041 item
= new JMenuItem("Align layers"); item
.addActionListener(this); popup
.add(item
);
2042 if (1 == layer
.getParent().size()) item
.setEnabled(false);
2043 item
= new JMenuItem("Align multi-layer mosaic"); item
.addActionListener(this); popup
.add(item
);
2044 if (1 == layer
.getParent().size()) item
.setEnabled(false);
2049 JMenu adjust_menu
= new JMenu("Adjust");
2051 if (null != active
) {
2052 if (!canvas
.isTransforming()) {
2053 if (active
instanceof Profile
) {
2054 item
= new JMenuItem("Duplicate, link and send to next layer"); item
.addActionListener(this); popup
.add(item
);
2055 Layer nl
= layer
.getParent().next(layer
);
2056 if (nl
== layer
) item
.setEnabled(false);
2057 item
= new JMenuItem("Duplicate, link and send to previous layer"); item
.addActionListener(this); popup
.add(item
);
2058 nl
= layer
.getParent().previous(layer
);
2059 if (nl
== layer
) item
.setEnabled(false);
2061 menu
= new JMenu("Duplicate, link and send to");
2062 ArrayList al
= layer
.getParent().getLayers();
2063 Iterator it
= al
.iterator();
2065 while (it
.hasNext()) {
2066 Layer la
= (Layer
)it
.next();
2067 item
= new JMenuItem(i
+ ": z = " + la
.getZ()); item
.addActionListener(this); menu
.add(item
); // TODO should label which layers contain Profile instances linked to the one being duplicated
2068 if (la
== this.layer
) item
.setEnabled(false);
2072 item
= new JMenuItem("Duplicate, link and send to..."); item
.addActionListener(this); popup
.add(item
);
2074 popup
.addSeparator();
2076 item
= new JMenuItem("Unlink from images"); item
.addActionListener(this); popup
.add(item
);
2077 if (!active
.isLinked()) item
.setEnabled(false); // isLinked() checks if it's linked to a Patch in its own layer
2078 item
= new JMenuItem("Show in 3D"); item
.addActionListener(this); popup
.add(item
);
2079 popup
.addSeparator();
2080 } else if (active
instanceof Patch
) {
2081 item
= new JMenuItem("Unlink from images"); item
.addActionListener(this); popup
.add(item
);
2082 if (!active
.isLinked(Patch
.class)) item
.setEnabled(false);
2083 if (((Patch
)active
).isStack()) {
2084 item
= new JMenuItem("Unlink slices"); item
.addActionListener(this); popup
.add(item
);
2086 int n_sel_patches
= selection
.getSelected(Patch
.class).size();
2087 if (1 == n_sel_patches
) {
2088 item
= new JMenuItem("Snap"); item
.addActionListener(this); popup
.add(item
);
2089 } else if (n_sel_patches
> 1) {
2090 item
= new JMenuItem("Montage"); item
.addActionListener(this); popup
.add(item
);
2091 item
= new JMenuItem("Lens correction"); item
.addActionListener(this); popup
.add(item
);
2092 item
= new JMenuItem("Blend"); item
.addActionListener(this); popup
.add(item
);
2094 item
= new JMenuItem("Link images..."); item
.addActionListener(this); popup
.add(item
);
2095 item
= new JMenuItem("View volume"); item
.addActionListener(this); popup
.add(item
);
2096 HashSet hs
= active
.getLinked(Patch
.class);
2097 if (null == hs
|| 0 == hs
.size()) item
.setEnabled(false);
2098 item
= new JMenuItem("View orthoslices"); item
.addActionListener(this); popup
.add(item
);
2099 if (null == hs
|| 0 == hs
.size()) item
.setEnabled(false); // if no Patch instances among the directly linked, then it's not a stack
2100 popup
.addSeparator();
2102 item
= new JMenuItem("Unlink"); item
.addActionListener(this); popup
.add(item
);
2103 item
= new JMenuItem("Show in 3D"); item
.addActionListener(this); popup
.add(item
);
2104 popup
.addSeparator();
2106 if (active
instanceof AreaList
) {
2107 item
= new JMenuItem("Merge"); item
.addActionListener(this); popup
.add(item
);
2108 ArrayList al
= selection
.getSelected();
2110 for (Iterator it
= al
.iterator(); it
.hasNext(); ) {
2111 if (it
.next().getClass() == AreaList
.class) n
++;
2113 if (n
< 2) item
.setEnabled(false);
2114 } else if (active
instanceof Pipe
) {
2115 item
= new JMenuItem("Identify..."); item
.addActionListener(this); popup
.add(item
);
2116 item
= new JMenuItem("Identify with axes..."); item
.addActionListener(this); popup
.add(item
);
2119 if (canvas
.isTransforming()) {
2120 item
= new JMenuItem("Apply transform"); item
.addActionListener(this); popup
.add(item
);
2121 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_ENTER
, 0, true)); // dummy, for I don't add a MenuKeyListener, but "works" through the normal key listener. It's here to provide a visual cue
2122 item
= new JMenuItem("Apply transform propagating to last layer"); item
.addActionListener(this); popup
.add(item
);
2123 if (layer
.getParent().indexOf(layer
) == layer
.getParent().size() -1) item
.setEnabled(false);
2124 item
= new JMenuItem("Apply transform propagating to first layer"); item
.addActionListener(this); popup
.add(item
);
2125 if (0 == layer
.getParent().indexOf(layer
)) item
.setEnabled(false);
2127 item
= new JMenuItem("Transform"); item
.addActionListener(this); popup
.add(item
);
2128 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_T
, 0, true));
2130 item
= new JMenuItem("Cancel transform"); item
.addActionListener(this); popup
.add(item
);
2131 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_ESCAPE
, 0, true));
2132 if (!canvas
.isTransforming()) item
.setEnabled(false);
2133 if (canvas
.isTransforming()) {
2134 item
= new JMenuItem("Specify transform..."); item
.addActionListener(this); popup
.add(item
);
2137 if (!canvas
.isTransforming()) {
2138 item
= new JMenuItem("Color..."); item
.addActionListener(this); popup
.add(item
);
2139 if (active
instanceof LayerSet
) item
.setEnabled(false);
2140 if (active
.isLocked()) {
2141 item
= new JMenuItem("Unlock"); item
.addActionListener(this); popup
.add(item
);
2143 item
= new JMenuItem("Lock"); item
.addActionListener(this); popup
.add(item
);
2145 menu
= new JMenu("Move");
2146 popup
.addSeparator();
2147 LayerSet ls
= layer
.getParent();
2148 item
= new JMenuItem("Move to top"); item
.addActionListener(this); menu
.add(item
);
2149 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_HOME
, 0, true)); // this is just to draw the key name by the menu; it does not incur on any event being generated (that I know if), and certainly not any event being listened to by TrakEM2.
2150 if (ls
.isTop(active
)) item
.setEnabled(false);
2151 item
= new JMenuItem("Move up"); item
.addActionListener(this); menu
.add(item
);
2152 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_PAGE_UP
, 0, true));
2153 if (ls
.isTop(active
)) item
.setEnabled(false);
2154 item
= new JMenuItem("Move down"); item
.addActionListener(this); menu
.add(item
);
2155 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_PAGE_DOWN
, 0, true));
2156 if (ls
.isBottom(active
)) item
.setEnabled(false);
2157 item
= new JMenuItem("Move to bottom"); item
.addActionListener(this); menu
.add(item
);
2158 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_END
, 0, true));
2159 if (ls
.isBottom(active
)) item
.setEnabled(false);
2162 popup
.addSeparator();
2163 item
= new JMenuItem("Delete..."); item
.addActionListener(this); popup
.add(item
);
2165 if (active
instanceof Patch
) {
2166 if (!active
.isOnlyLinkedTo(Patch
.class)) {
2167 item
.setEnabled(false);
2169 } else if (!(active
instanceof DLabel
)) { // can't delete elements from the trees (Profile, Pipe, LayerSet)
2170 item
.setEnabled(false);
2172 } catch (Exception e
) { IJError
.print(e
); item
.setEnabled(false); }
2174 if (active
instanceof Patch
) {
2175 item
= new JMenuItem("Revert"); item
.addActionListener(this); popup
.add(item
);
2176 popup
.addSeparator();
2178 item
= new JMenuItem("Properties..."); item
.addActionListener(this); popup
.add(item
);
2179 item
= new JMenuItem("Show centered"); item
.addActionListener(this); popup
.add(item
);
2181 popup
.addSeparator();
2183 if (! (active
instanceof ZDisplayable
)) {
2184 ArrayList al_layers
= layer
.getParent().getLayers();
2185 int i_layer
= al_layers
.indexOf(layer
);
2186 int n_layers
= al_layers
.size();
2187 item
= new JMenuItem("Send to previous layer"); item
.addActionListener(this); popup
.add(item
);
2188 if (1 == n_layers
|| 0 == i_layer
|| active
.isLinked()) item
.setEnabled(false);
2189 // check if the active is a profile and contains a link to another profile in the layer it is going to be sent to, or it is linked
2190 else if (active
instanceof Profile
&& !active
.canSendTo(layer
.getParent().previous(layer
))) item
.setEnabled(false);
2191 item
= new JMenuItem("Send to next layer"); item
.addActionListener(this); popup
.add(item
);
2192 if (1 == n_layers
|| n_layers
-1 == i_layer
|| active
.isLinked()) item
.setEnabled(false);
2193 else if (active
instanceof Profile
&& !active
.canSendTo(layer
.getParent().next(layer
))) item
.setEnabled(false);
2196 menu
= new JMenu("Send linked group to...");
2197 if (active
.hasLinkedGroupWithinLayer(this.layer
)) {
2199 for (final Layer la
: ls
.getLayers()) {
2200 String layer_title
= i
+ ": " + la
.getTitle();
2201 if (-1 == layer_title
.indexOf(' ')) layer_title
+= " ";
2202 item
= new JMenuItem(layer_title
); item
.addActionListener(this); menu
.add(item
);
2203 if (la
== this.layer
) item
.setEnabled(false);
2208 menu
.setEnabled(false);
2209 //Utils.log("Active's linked group not within layer.");
2212 popup
.addSeparator();
2217 if (!canvas
.isTransforming()) {
2219 item
= new JMenuItem("Undo");item
.addActionListener(this); popup
.add(item
);
2220 if (!layer
.getParent().canUndo() || canvas
.isTransforming()) item
.setEnabled(false);
2221 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_Z
, Utils
.getControlModifier(), true));
2222 item
= new JMenuItem("Redo");item
.addActionListener(this); popup
.add(item
);
2223 if (!layer
.getParent().canRedo() || canvas
.isTransforming()) item
.setEnabled(false);
2224 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_Z
, Event
.ALT_MASK
, true));
2226 item
= new JMenuItem("Enhance contrast layer-wise..."); item
.addActionListener(this); adjust_menu
.add(item
);
2227 item
= new JMenuItem("Enhance contrast (selected images)..."); item
.addActionListener(this); adjust_menu
.add(item
);
2228 if (selection
.isEmpty()) item
.setEnabled(false);
2229 item
= new JMenuItem("Set Min and Max layer-wise..."); item
.addActionListener(this); adjust_menu
.add(item
);
2230 item
= new JMenuItem("Set Min and Max (selected images)..."); item
.addActionListener(this); adjust_menu
.add(item
);
2231 if (selection
.isEmpty()) item
.setEnabled(false);
2232 popup
.add(adjust_menu
);
2233 popup
.addSeparator();
2235 // Would get so much simpler with a clojure macro ...
2238 menu
= new JMenu("Hide/Unhide");
2239 item
= new JMenuItem("Hide deselected"); item
.addActionListener(this); menu
.add(item
); item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_H
, Event
.SHIFT_MASK
, true));
2240 boolean none
= 0 == selection
.getNSelected();
2241 if (none
) item
.setEnabled(false);
2242 item
= new JMenuItem("Hide deselected except images"); item
.addActionListener(this); menu
.add(item
); item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_H
, Event
.SHIFT_MASK
| Event
.ALT_MASK
, true));
2243 if (none
) item
.setEnabled(false);
2244 item
= new JMenuItem("Hide selected"); item
.addActionListener(this); menu
.add(item
); item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_H
, 0, true));
2245 if (none
) item
.setEnabled(false);
2246 none
= ! layer
.getParent().containsDisplayable(DLabel
.class);
2247 item
= new JMenuItem("Hide all labels"); item
.addActionListener(this); menu
.add(item
);
2248 if (none
) item
.setEnabled(false);
2249 item
= new JMenuItem("Unhide all labels"); item
.addActionListener(this); menu
.add(item
);
2250 if (none
) item
.setEnabled(false);
2251 none
= ! layer
.getParent().contains(AreaList
.class);
2252 item
= new JMenuItem("Hide all arealists"); item
.addActionListener(this); menu
.add(item
);
2253 if (none
) item
.setEnabled(false);
2254 item
= new JMenuItem("Unhide all arealists"); item
.addActionListener(this); menu
.add(item
);
2255 if (none
) item
.setEnabled(false);
2256 none
= ! layer
.contains(Profile
.class);
2257 item
= new JMenuItem("Hide all profiles"); item
.addActionListener(this); menu
.add(item
);
2258 if (none
) item
.setEnabled(false);
2259 item
= new JMenuItem("Unhide all profiles"); item
.addActionListener(this); menu
.add(item
);
2260 if (none
) item
.setEnabled(false);
2261 none
= ! layer
.getParent().contains(Pipe
.class);
2262 item
= new JMenuItem("Hide all pipes"); item
.addActionListener(this); menu
.add(item
);
2263 if (none
) item
.setEnabled(false);
2264 item
= new JMenuItem("Unhide all pipes"); item
.addActionListener(this); menu
.add(item
);
2265 if (none
) item
.setEnabled(false);
2266 none
= ! layer
.getParent().contains(Polyline
.class);
2267 item
= new JMenuItem("Hide all polylines"); item
.addActionListener(this); menu
.add(item
);
2268 if (none
) item
.setEnabled(false);
2269 item
= new JMenuItem("Unhide all polylines"); item
.addActionListener(this); menu
.add(item
);
2270 if (none
) item
.setEnabled(false);
2271 none
= ! layer
.getParent().contains(Ball
.class);
2272 item
= new JMenuItem("Hide all balls"); item
.addActionListener(this); menu
.add(item
);
2273 if (none
) item
.setEnabled(false);
2274 item
= new JMenuItem("Unhide all balls"); item
.addActionListener(this); menu
.add(item
);
2275 if (none
) item
.setEnabled(false);
2276 none
= ! layer
.getParent().containsDisplayable(Patch
.class);
2277 item
= new JMenuItem("Hide all images"); item
.addActionListener(this); menu
.add(item
);
2278 if (none
) item
.setEnabled(false);
2279 item
= new JMenuItem("Unhide all images"); item
.addActionListener(this); menu
.add(item
);
2280 if (none
) item
.setEnabled(false);
2281 item
= new JMenuItem("Hide all but images"); item
.addActionListener(this); menu
.add(item
);
2282 item
= new JMenuItem("Unhide all"); item
.addActionListener(this); menu
.add(item
); item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_H
, Event
.ALT_MASK
, true));
2285 } catch (Exception e
) { IJError
.print(e
); }
2287 menu
= new JMenu("Import");
2288 item
= new JMenuItem("Import image"); item
.addActionListener(this); menu
.add(item
);
2289 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_I
, Event
.ALT_MASK
& Event
.SHIFT_MASK
, true));
2290 item
= new JMenuItem("Import stack..."); item
.addActionListener(this); menu
.add(item
);
2291 item
= new JMenuItem("Import grid..."); item
.addActionListener(this); menu
.add(item
);
2292 item
= new JMenuItem("Import sequence as grid..."); item
.addActionListener(this); menu
.add(item
);
2293 item
= new JMenuItem("Import from text file..."); item
.addActionListener(this); menu
.add(item
);
2294 item
= new JMenuItem("Import labels as arealists..."); item
.addActionListener(this); menu
.add(item
);
2297 menu
= new JMenu("Export");
2298 item
= new JMenuItem("Make flat image..."); item
.addActionListener(this); menu
.add(item
);
2299 item
= new JMenuItem("Arealists as labels (tif)"); item
.addActionListener(this); menu
.add(item
);
2300 if (0 == layer
.getParent().getZDisplayables(AreaList
.class).size()) item
.setEnabled(false);
2301 item
= new JMenuItem("Arealists as labels (amira)"); item
.addActionListener(this); menu
.add(item
);
2302 if (0 == layer
.getParent().getZDisplayables(AreaList
.class).size()) item
.setEnabled(false);
2305 menu
= new JMenu("Display");
2306 item
= new JMenuItem("Resize canvas/LayerSet..."); item
.addActionListener(this); menu
.add(item
);
2307 item
= new JMenuItem("Autoresize canvas/LayerSet"); item
.addActionListener(this); menu
.add(item
);
2308 // OBSOLETE // item = new JMenuItem("Rotate Layer/LayerSet..."); item.addActionListener(this); menu.add(item);
2309 item
= new JMenuItem("Properties ..."); item
.addActionListener(this); menu
.add(item
);
2312 menu
= new JMenu("Project");
2313 this.project
.getLoader().setupMenuItems(menu
, this.getProject());
2314 item
= new JMenuItem("Project properties..."); item
.addActionListener(this); menu
.add(item
);
2315 item
= new JMenuItem("Create subproject"); item
.addActionListener(this); menu
.add(item
);
2316 if (null == canvas
.getFakeImagePlus().getRoi()) item
.setEnabled(false);
2317 item
= new JMenuItem("Release memory..."); item
.addActionListener(this); menu
.add(item
);
2318 item
= new JMenuItem("Flush image cache"); item
.addActionListener(this); menu
.add(item
);
2321 menu
= new JMenu("Selection");
2322 item
= new JMenuItem("Select all"); item
.addActionListener(this); menu
.add(item
);
2323 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_A
, Utils
.getControlModifier(), true));
2324 if (0 == layer
.getDisplayables().size() && 0 == layer
.getParent().getZDisplayables().size()) item
.setEnabled(false);
2325 item
= new JMenuItem("Select none"); item
.addActionListener(this); menu
.add(item
);
2326 if (0 == selection
.getNSelected()) item
.setEnabled(false);
2327 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_ESCAPE
, 0, true));
2329 JMenu bytype
= new JMenu("Select all by type");
2330 item
= new JMenuItem("AreaList"); item
.addActionListener(bytypelistener
); bytype
.add(item
);
2331 item
= new JMenuItem("Ball"); item
.addActionListener(bytypelistener
); bytype
.add(item
);
2332 item
= new JMenuItem("Dissector"); item
.addActionListener(bytypelistener
); bytype
.add(item
);
2333 item
= new JMenuItem("Image"); item
.addActionListener(bytypelistener
); bytype
.add(item
);
2334 item
= new JMenuItem("Text"); item
.addActionListener(bytypelistener
); bytype
.add(item
);
2335 item
= new JMenuItem("Pipe"); item
.addActionListener(bytypelistener
); bytype
.add(item
);
2336 item
= new JMenuItem("Polyline"); item
.addActionListener(bytypelistener
); bytype
.add(item
);
2337 item
= new JMenuItem("Profile"); item
.addActionListener(bytypelistener
); bytype
.add(item
);
2340 item
= new JMenuItem("Restore selection"); item
.addActionListener(this); menu
.add(item
);
2341 item
= new JMenuItem("Select under ROI"); item
.addActionListener(this); menu
.add(item
);
2342 if (canvas
.getFakeImagePlus().getRoi() == null) item
.setEnabled(false);
2344 item
= new JMenuItem("Search..."); item
.addActionListener(this); popup
.add(item
);
2347 //canvas.add(popup);
2351 private ByTypeListener bytypelistener
= new ByTypeListener(this);
2353 static private class ByTypeListener
implements ActionListener
{
2355 ByTypeListener(final Display d
) {
2358 public void actionPerformed(final ActionEvent ae
) {
2359 final String command
= ae
.getActionCommand();
2361 final java
.awt
.geom
.Area aroi
= Utils
.getArea(d
.canvas
.getFakeImagePlus().getRoi());
2363 d
.dispatcher
.exec(new Runnable() { public void run() {
2366 String type
= command
;
2367 if (type
.equals("Image")) type
= "Patch";
2368 Class c
= Class
.forName("ini.trakem2.display." + type
);
2370 java
.util
.List
<Displayable
> a
= new ArrayList
<Displayable
>();
2372 a
.addAll(d
.layer
.getDisplayables(c
, aroi
, true));
2373 a
.addAll(d
.layer
.getParent().getZDisplayables(c
, d
.layer
, aroi
, true));
2375 a
.addAll(d
.layer
.getDisplayables(c
));
2376 a
.addAll(d
.layer
.getParent().getZDisplayables(c
));
2377 // Remove non-visible ones
2378 for (final Iterator
<Displayable
> it
= a
.iterator(); it
.hasNext(); ) {
2379 if (!it
.next().isVisible()) it
.remove();
2383 if (0 == a
.size()) return;
2385 boolean selected
= false;
2387 if (0 == ae
.getModifiers()) {
2388 Utils
.log2("first");
2389 d
.selection
.clear();
2390 d
.selection
.selectAll(a
);
2392 } else if (0 == (ae
.getModifiers() ^ Event
.SHIFT_MASK
)) {
2393 Utils
.log2("with shift");
2394 d
.selection
.selectAll(a
); // just add them to the current selection
2399 d
.selection
.setActive(a
.get(a
.size() -1));
2402 } catch (ClassNotFoundException e
) {
2403 Utils
.log2(e
.toString());
2410 /** Check if a panel for the given Displayable is completely visible in the JScrollPane */
2411 public boolean isWithinViewport(final Displayable d
) {
2412 final JScrollPane scroll
= (JScrollPane
)tabs
.getSelectedComponent();
2413 if (ht_tabs
.get(d
.getClass()) == scroll
) return isWithinViewport(scroll
, ht_panels
.get(d
));
2417 private boolean isWithinViewport(JScrollPane scroll
, DisplayablePanel dp
) {
2418 if(null == dp
) return false;
2419 JViewport view
= scroll
.getViewport();
2420 java
.awt
.Dimension dimensions
= view
.getExtentSize();
2421 java
.awt
.Point p
= view
.getViewPosition();
2423 if ((y
+ DisplayablePanel
.HEIGHT
- p
.y
) <= dimensions
.height
&& y
>= p
.y
) {
2429 /** Check if a panel for the given Displayable is partially visible in the JScrollPane */
2430 public boolean isPartiallyWithinViewport(final Displayable d
) {
2431 final JScrollPane scroll
= ht_tabs
.get(d
.getClass());
2432 if (tabs
.getSelectedComponent() == scroll
) return isPartiallyWithinViewport(scroll
, ht_panels
.get(d
));
2436 /** Check if a panel for the given Displayable is at least partially visible in the JScrollPane */
2437 private boolean isPartiallyWithinViewport(final JScrollPane scroll
, final DisplayablePanel dp
) {
2439 //Utils.log2("Display.isPartiallyWithinViewport: null DisplayablePanel ??");
2440 return false; // to fast for you baby
2442 JViewport view
= scroll
.getViewport();
2443 java
.awt
.Dimension dimensions
= view
.getExtentSize();
2444 java
.awt
.Point p
= view
.getViewPosition();
2446 if ( ((y
+ DisplayablePanel
.HEIGHT
- p
.y
) <= dimensions
.height
&& y
>= p
.y
) // completely visible
2447 || ((y
+ DisplayablePanel
.HEIGHT
- p
.y
) > dimensions
.height
&& y
< p
.y
+ dimensions
.height
) // partially hovering at the bottom
2448 || ((y
+ DisplayablePanel
.HEIGHT
) > p
.y
&& y
< p
.y
) // partially hovering at the top
2455 /** A function to make a Displayable panel be visible in the screen, by scrolling the viewport of the JScrollPane. */
2456 private void scrollToShow(final Displayable d
) {
2457 dispatcher
.execSwing(new Runnable() { public void run() {
2458 final JScrollPane scroll
= (JScrollPane
)tabs
.getSelectedComponent();
2459 if (d
instanceof ZDisplayable
&& scroll
== scroll_zdispl
) {
2460 scrollToShow(scroll_zdispl
, ht_panels
.get(d
));
2463 final Class c
= d
.getClass();
2464 if (Patch
.class == c
&& scroll
== scroll_patches
) {
2465 scrollToShow(scroll_patches
, ht_panels
.get(d
));
2466 } else if (DLabel
.class == c
&& scroll
== scroll_labels
) {
2467 scrollToShow(scroll_labels
, ht_panels
.get(d
));
2468 } else if (Profile
.class == c
&& scroll
== scroll_profiles
) {
2469 scrollToShow(scroll_profiles
, ht_panels
.get(d
));
2474 private void scrollToShow(final JScrollPane scroll
, final JPanel dp
) {
2475 if (null == dp
) return;
2476 JViewport view
= scroll
.getViewport();
2477 Point current
= view
.getViewPosition();
2478 Dimension extent
= view
.getExtentSize();
2479 int panel_y
= dp
.getY();
2480 if ((panel_y
+ DisplayablePanel
.HEIGHT
- current
.y
) <= extent
.height
&& panel_y
>= current
.y
) {
2481 // it's completely visible already
2484 // scroll just enough
2485 // if it's above, show at the top
2486 if (panel_y
- current
.y
< 0) {
2487 view
.setViewPosition(new Point(0, panel_y
));
2489 // if it's below (even if partially), show at the bottom
2490 else if (panel_y
+ 50 > current
.y
+ extent
.height
) {
2491 view
.setViewPosition(new Point(0, panel_y
- extent
.height
+ 50));
2492 //Utils.log("Display.scrollToShow: panel_y: " + panel_y + " current.y: " + current.y + " extent.height: " + extent.height);
2497 /** Update the title of the given Displayable in its DisplayablePanel, if any. */
2498 static public void updateTitle(final Layer layer
, final Displayable displ
) {
2499 for (final Display d
: al_displays
) {
2500 if (layer
== d
.layer
) {
2501 DisplayablePanel dp
= d
.ht_panels
.get(displ
);
2502 if (null != dp
) dp
.updateTitle();
2507 /** Update the Display's title in all Displays showing the given Layer. */
2508 static public void updateTitle(final Layer layer
) {
2509 for (final Display d
: al_displays
) {
2510 if (d
.layer
== layer
) {
2515 /** Update the Display's title in all Displays showing a Layer of the given LayerSet. */
2516 static public void updateTitle(final LayerSet ls
) {
2517 for (final Display d
: al_displays
) {
2518 if (d
.layer
.getParent() == ls
) {
2524 /** Set a new title in the JFrame, showing info on the layer 'z' and the magnification. */
2525 public void updateTitle() {
2526 // From ij.ImagePlus class, the solution:
2528 final double magnification
= canvas
.getMagnification();
2529 if (magnification
!=1.0) {
2530 final double percent
= magnification
*100.0;
2531 scale
= new StringBuffer(" (").append(Utils
.d2s(percent
, percent
==(int)percent ?
0 : 1)).append("%)").toString();
2533 final Calibration cal
= layer
.getParent().getCalibration();
2534 String title
= new StringBuffer().append(layer
.getParent().indexOf(layer
) + 1).append('/').append(layer
.getParent().size()).append(' ').append((null == layer
.getTitle() ?
"" : layer
.getTitle())).append(scale
).append(" -- ").append(getProject().toString()).append(' ').append(' ').append(Utils
.cutNumber(layer
.getParent().getLayerWidth() * cal
.pixelWidth
, 2, true)).append('x').append(Utils
.cutNumber(layer
.getParent().getLayerHeight() * cal
.pixelHeight
, 2, true)).append(' ').append(cal
.getUnit()).toString();
2535 frame
.setTitle(title
);
2536 // fix the title for the FakeImageWindow and thus the WindowManager listing in the menus
2537 canvas
.getFakeImagePlus().setTitle(title
);
2540 /** If shift is down, scroll to the next non-empty layer; otherwise, if scroll_step is larger than 1, then scroll 'scroll_step' layers ahead; else just the next Layer. */
2541 public void nextLayer(final int modifiers
) {
2543 if (0 == (modifiers ^ Event
.SHIFT_MASK
)) {
2544 l
= layer
.getParent().nextNonEmpty(layer
);
2545 } else if (scroll_step
> 1) {
2546 int i
= layer
.getParent().indexOf(this.layer
);
2547 Layer la
= layer
.getParent().getLayer(i
+ scroll_step
);
2548 if (null != la
) l
= la
;
2551 l
= layer
.getParent().next(layer
);
2555 updateInDatabase("layer_id");
2559 private final void translateLayerColors(final Layer current
, final Layer other
) {
2560 if (current
== other
) return;
2561 if (layer_channels
.size() > 0) {
2562 final LayerSet ls
= getLayerSet();
2563 // translate colors by distance from current layer to new Layer l
2564 final int dist
= ls
.indexOf(other
) - ls
.indexOf(current
);
2565 translateLayerColor(Color
.red
, dist
);
2566 translateLayerColor(Color
.blue
, dist
);
2570 private final void translateLayerColor(final Color color
, final int dist
) {
2571 final LayerSet ls
= getLayerSet();
2572 final Layer l
= layer_channels
.get(color
);
2573 if (null == l
) return;
2574 updateColor(Color
.white
, layer_panels
.get(l
));
2575 final Layer l2
= ls
.getLayer(ls
.indexOf(l
) + dist
);
2576 if (null != l2
) updateColor(color
, layer_panels
.get(l2
));
2579 private final void updateColor(final Color color
, final LayerPanel lp
) {
2581 setColorChannel(lp
.layer
, color
);
2584 /** Calls setLayer(la) on the SetLayerThread. */
2585 public void toLayer(final Layer la
) {
2586 if (la
.getParent() != layer
.getParent()) return; // not of the same LayerSet
2587 if (la
== layer
) return; // nothing to do
2589 updateInDatabase("layer_id");
2592 /** If shift is down, scroll to the previous non-empty layer; otherwise, if scroll_step is larger than 1, then scroll 'scroll_step' layers backward; else just the previous Layer. */
2593 public void previousLayer(final int modifiers
) {
2595 if (0 == (modifiers ^ Event
.SHIFT_MASK
)) {
2596 l
= layer
.getParent().previousNonEmpty(layer
);
2597 } else if (scroll_step
> 1) {
2598 int i
= layer
.getParent().indexOf(this.layer
);
2599 Layer la
= layer
.getParent().getLayer(i
- scroll_step
);
2600 if (null != la
) l
= la
;
2603 l
= layer
.getParent().previous(layer
);
2607 updateInDatabase("layer_id");
2611 static public void updateLayerScroller(LayerSet set
) {
2612 for (final Display d
: al_displays
) {
2613 if (d
.layer
.getParent() == set
) {
2614 d
.updateLayerScroller(d
.layer
);
2619 private void updateLayerScroller(Layer layer
) {
2620 int size
= layer
.getParent().size();
2622 scroller
.setValues(0, 1, 0, 0);
2623 scroller
.setEnabled(false);
2625 scroller
.setEnabled(true);
2626 scroller
.setValues(layer
.getParent().getLayerIndex(layer
.getId()), 1, 0, size
);
2628 recreateLayerPanels(layer
);
2631 // Can't use this.layer, may still be null. User argument instead.
2632 private synchronized void recreateLayerPanels(final Layer layer
) {
2633 synchronized (layer_channels
) {
2634 panel_layers
.removeAll();
2636 if (0 == layer_panels
.size()) {
2637 for (final Layer la
: layer
.getParent().getLayers()) {
2638 final LayerPanel lp
= new LayerPanel(this, la
);
2639 layer_panels
.put(la
, lp
);
2640 this.panel_layers
.add(lp
);
2643 // Set theory at work: keep old to reuse
2644 layer_panels
.keySet().retainAll(layer
.getParent().getLayers());
2645 for (final Layer la
: layer
.getParent().getLayers()) {
2646 LayerPanel lp
= layer_panels
.get(la
);
2648 lp
= new LayerPanel(this, la
);
2649 layer_panels
.put(la
, lp
);
2651 this.panel_layers
.add(lp
);
2653 for (final Iterator
<Map
.Entry
<Integer
,LayerPanel
>> it
= layer_alpha
.entrySet().iterator(); it
.hasNext(); ) {
2654 final Map
.Entry
<Integer
,LayerPanel
> e
= it
.next();
2655 if (-1 == getLayerSet().indexOf(e
.getValue().layer
)) it
.remove();
2657 for (final Iterator
<Map
.Entry
<Color
,Layer
>> it
= layer_channels
.entrySet().iterator(); it
.hasNext(); ) {
2658 final Map
.Entry
<Color
,Layer
> e
= it
.next();
2659 if (-1 == getLayerSet().indexOf(e
.getValue())) it
.remove();
2661 scroll_layers
.repaint();
2666 private void updateSnapshots() {
2667 Enumeration
<DisplayablePanel
> e
= ht_panels
.elements();
2668 while (e
.hasMoreElements()) {
2669 e
.nextElement().remake();
2671 Utils
.updateComponent(tabs
.getSelectedComponent());
2674 static public void updatePanel(Layer layer
, final Displayable displ
) {
2675 if (null == layer
&& null != front
) layer
= front
.layer
; // the front layer
2676 for (final Display d
: al_displays
) {
2677 if (d
.layer
== layer
) {
2678 d
.updatePanel(displ
);
2683 private void updatePanel(Displayable d
) {
2685 if (d
instanceof Profile
) {
2687 } else if (d
instanceof Patch
) {
2689 } else if (d
instanceof DLabel
) {
2691 } else if (d
instanceof Pipe
) {
2694 if (null == c
) return;
2695 DisplayablePanel dp
= ht_panels
.get(d
);
2697 Utils
.updateComponent(c
);
2700 static public void updatePanelIndex(final Layer layer
, final Displayable displ
) {
2701 for (final Display d
: al_displays
) {
2702 if (d
.layer
== layer
|| displ
instanceof ZDisplayable
) {
2703 d
.updatePanelIndex(displ
);
2708 private void updatePanelIndex(final Displayable d
) {
2709 // find first of the kind, then remove and insert its panel
2712 if (d
instanceof ZDisplayable
) {
2713 i
= layer
.getParent().indexOf((ZDisplayable
)d
);
2716 i
= layer
.relativeIndexOf(d
);
2717 if (d
instanceof Profile
) {
2719 } else if (d
instanceof Patch
) {
2721 } else if (d
instanceof DLabel
) {
2725 if (null == c
) return;
2726 DisplayablePanel dp
= ht_panels
.get(d
);
2727 if (null == dp
) return; // may be half-baked, wait
2729 c
.add(dp
, i
); // java and its fabulous consistency
2730 // not enough! Utils.updateComponent(c);
2734 Utils
.updateComponent(c
);
2737 /** Repair possibly missing panels and other components by simply resetting the same Layer */
2738 public void repairGUI() {
2739 Layer layer
= this.layer
;
2744 public void actionPerformed(final ActionEvent ae
) {
2745 dispatcher
.exec(new Runnable() { public void run() {
2747 String command
= ae
.getActionCommand();
2748 if (command
.startsWith("Job")) {
2749 if (Utils
.checkYN("Really cancel job?")) {
2750 project
.getLoader().quitJob(command
);
2754 } else if (command
.equals("Move to top")) {
2755 if (null == active
) return;
2756 canvas
.setUpdateGraphics(true);
2757 layer
.getParent().move(LayerSet
.TOP
, active
);
2758 Display
.repaint(layer
.getParent(), active
, 5);
2759 //Display.updatePanelIndex(layer, active);
2760 } else if (command
.equals("Move up")) {
2761 if (null == active
) return;
2762 canvas
.setUpdateGraphics(true);
2763 layer
.getParent().move(LayerSet
.UP
, active
);
2764 Display
.repaint(layer
.getParent(), active
, 5);
2765 //Display.updatePanelIndex(layer, active);
2766 } else if (command
.equals("Move down")) {
2767 if (null == active
) return;
2768 canvas
.setUpdateGraphics(true);
2769 layer
.getParent().move(LayerSet
.DOWN
, active
);
2770 Display
.repaint(layer
.getParent(), active
, 5);
2771 //Display.updatePanelIndex(layer, active);
2772 } else if (command
.equals("Move to bottom")) {
2773 if (null == active
) return;
2774 canvas
.setUpdateGraphics(true);
2775 layer
.getParent().move(LayerSet
.BOTTOM
, active
);
2776 Display
.repaint(layer
.getParent(), active
, 5);
2777 //Display.updatePanelIndex(layer, active);
2778 } else if (command
.equals("Duplicate, link and send to next layer")) {
2779 duplicateLinkAndSendTo(active
, 1, layer
.getParent().next(layer
));
2780 } else if (command
.equals("Duplicate, link and send to previous layer")) {
2781 duplicateLinkAndSendTo(active
, 0, layer
.getParent().previous(layer
));
2782 } else if (command
.equals("Duplicate, link and send to...")) {
2783 // fix non-scrolling popup menu
2784 GenericDialog gd
= new GenericDialog("Send to");
2785 gd
.addMessage("Duplicate, link and send to...");
2786 String
[] sl
= new String
[layer
.getParent().size()];
2788 for (Iterator it
= layer
.getParent().getLayers().iterator(); it
.hasNext(); ) {
2789 sl
[next
++] = project
.findLayerThing(it
.next()).toString();
2791 gd
.addChoice("Layer: ", sl
, sl
[layer
.getParent().indexOf(layer
)]);
2793 if (gd
.wasCanceled()) return;
2794 Layer la
= layer
.getParent().getLayer(gd
.getNextChoiceIndex());
2796 Utils
.showMessage("Can't duplicate, link and send to the same layer.");
2799 duplicateLinkAndSendTo(active
, 0, la
);
2800 } else if (-1 != command
.indexOf("z = ")) {
2801 // this is an item from the "Duplicate, link and send to" menu of layer z's
2802 Layer target_layer
= layer
.getParent().getLayer(Double
.parseDouble(command
.substring(command
.lastIndexOf(' ') +1)));
2803 Utils
.log2("layer: __" +command
.substring(command
.lastIndexOf(' ') +1) + "__");
2804 if (null == target_layer
) return;
2805 duplicateLinkAndSendTo(active
, 0, target_layer
);
2806 } else if (-1 != command
.indexOf("z=")) {
2807 // WARNING the indexOf is very similar to the previous one
2808 // Send the linked group to the selected layer
2809 int iz
= command
.indexOf("z=")+2;
2810 Utils
.log2("iz=" + iz
+ " other: " + command
.indexOf(' ', iz
+2));
2811 int end
= command
.indexOf(' ', iz
);
2812 if (-1 == end
) end
= command
.length();
2813 double lz
= Double
.parseDouble(command
.substring(iz
, end
));
2814 Layer target
= layer
.getParent().getLayer(lz
);
2815 HashSet hs
= active
.getLinkedGroup(new HashSet());
2816 layer
.getParent().move(hs
, active
.getLayer(), target
);
2817 } else if (command
.equals("Unlink")) {
2818 if (null == active
|| active
instanceof Patch
) return;
2820 updateSelection();//selection.update();
2821 } else if (command
.equals("Unlink from images")) {
2822 if (null == active
) return;
2824 for (Displayable displ
: selection
.getSelected()) {
2825 displ
.unlinkAll(Patch
.class);
2827 updateSelection();//selection.update();
2828 } catch (Exception e
) { IJError
.print(e
); }
2829 } else if (command
.equals("Unlink slices")) {
2830 YesNoCancelDialog yn
= new YesNoCancelDialog(frame
, "Attention", "Really unlink all slices from each other?\nThere is no undo.");
2831 if (!yn
.yesPressed()) return;
2832 final ArrayList
<Patch
> pa
= ((Patch
)active
).getStackPatches();
2833 for (int i
=pa
.size()-1; i
>0; i
--) {
2834 pa
.get(i
).unlink(pa
.get(i
-1));
2836 } else if (command
.equals("Send to next layer")) {
2837 Rectangle box
= selection
.getBox();
2839 // unlink Patch instances
2840 for (final Displayable displ
: selection
.getSelected()) {
2841 displ
.unlinkAll(Patch
.class);
2843 updateSelection();//selection.update();
2844 } catch (Exception e
) { IJError
.print(e
); }
2845 //layer.getParent().moveDown(layer, active); // will repaint whatever appropriate layers
2846 selection
.moveDown();
2847 repaint(layer
.getParent(), box
);
2848 } else if (command
.equals("Send to previous layer")) {
2849 Rectangle box
= selection
.getBox();
2851 // unlink Patch instances
2852 for (final Displayable displ
: selection
.getSelected()) {
2853 displ
.unlinkAll(Patch
.class);
2855 updateSelection();//selection.update();
2856 } catch (Exception e
) { IJError
.print(e
); }
2857 //layer.getParent().moveUp(layer, active); // will repaint whatever appropriate layers
2859 repaint(layer
.getParent(), box
);
2860 } else if (command
.equals("Show centered")) {
2861 if (active
== null) return;
2862 showCentered(active
);
2863 } else if (command
.equals("Delete...")) {
2865 if (null != active) {
2866 Displayable d = active;
2867 selection.remove(d);
2868 d.remove(true); // will repaint
2871 // remove all selected objects
2872 selection
.deleteAll();
2873 } else if (command
.equals("Color...")) {
2874 IJ
.doCommand("Color Picker...");
2875 } else if (command
.equals("Revert")) {
2876 if (null == active
|| active
.getClass() != Patch
.class) return;
2877 Patch p
= (Patch
)active
;
2879 if (null == p
.getOriginalPath()) Utils
.log("No editions to save for patch " + p
.getTitle() + " #" + p
.getId());
2880 else Utils
.log("Could not revert Patch " + p
.getTitle() + " #" + p
.getId());
2882 } else if (command
.equals("Undo")) {
2883 Bureaucrat
.createAndStart(new Worker
.Task("Undo") { public void exec() {
2884 layer
.getParent().undoOneStep();
2885 Display
.repaint(layer
.getParent());
2887 } else if (command
.equals("Redo")) {
2888 Bureaucrat
.createAndStart(new Worker
.Task("Redo") { public void exec() {
2889 layer
.getParent().redoOneStep();
2890 Display
.repaint(layer
.getParent());
2892 } else if (command
.equals("Transform")) {
2893 if (null == active
) return;
2894 canvas
.setTransforming(true);
2895 } else if (command
.equals("Apply transform")) {
2896 if (null == active
) return;
2897 canvas
.setTransforming(false);
2898 } else if (command
.equals("Apply transform propagating to last layer")) {
2899 if (selection
.isTransforming()) {
2900 final java
.util
.List
<Layer
> layers
= layer
.getParent().getLayers();
2901 selection
.applyAndPropagate(new HashSet
<Layer
>(layers
.subList(layers
.indexOf(Display
.this.layer
)+1, layers
.size()))); // +1 to exclude current layer
2903 } else if (command
.equals("Apply transform propagating to first layer")) {
2904 if (selection
.isTransforming()) {
2905 final java
.util
.List
<Layer
> layers
= layer
.getParent().getLayers();
2906 selection
.applyAndPropagate(new HashSet
<Layer
>(layers
.subList(0, layers
.indexOf(Display
.this.layer
))));
2908 } else if (command
.equals("Cancel transform")) {
2909 if (null == active
) return;
2910 canvas
.cancelTransform();
2911 } else if (command
.equals("Specify transform...")) {
2912 if (null == active
) return;
2913 selection
.specify();
2914 } else if (command
.equals("Hide all but images")) {
2915 ArrayList
<Class
> type
= new ArrayList
<Class
>();
2916 type
.add(Patch
.class);
2917 selection
.removeAll(layer
.getParent().hideExcept(type
, false));
2918 Display
.update(layer
.getParent(), false);
2919 } else if (command
.equals("Unhide all")) {
2920 layer
.getParent().setAllVisible(false);
2921 Display
.update(layer
.getParent(), false);
2922 } else if (command
.startsWith("Hide all ")) {
2923 String type
= command
.substring(9, command
.length() -1); // skip the ending plural 's'
2924 type
= type
.substring(0, 1).toUpperCase() + type
.substring(1);
2925 selection
.removeAll(layer
.getParent().setVisible(type
, false, true));
2926 } else if (command
.startsWith("Unhide all ")) {
2927 String type
= command
.substring(11, command
.length() -1); // skip the ending plural 's'
2928 type
= type
.substring(0, 1).toUpperCase() + type
.substring(1);
2929 layer
.getParent().setVisible(type
, true, true);
2930 } else if (command
.equals("Hide deselected")) {
2931 hideDeselected(0 != (ActionEvent
.ALT_MASK
& ae
.getModifiers()));
2932 } else if (command
.equals("Hide deselected except images")) {
2933 hideDeselected(true);
2934 } else if (command
.equals("Hide selected")) {
2935 selection
.setVisible(false); // TODO should deselect them too? I don't think so.
2936 } else if (command
.equals("Resize canvas/LayerSet...")) {
2938 } else if (command
.equals("Autoresize canvas/LayerSet")) {
2939 layer
.getParent().setMinimumDimensions();
2940 } else if (command
.equals("Import image")) {
2942 } else if (command
.equals("Import next image")) {
2944 } else if (command
.equals("Import stack...")) {
2945 Display
.this.getLayerSet().addLayerContentStep(layer
);
2946 Rectangle sr
= getCanvas().getSrcRect();
2947 Bureaucrat burro
= project
.getLoader().importStack(layer
, sr
.x
+ sr
.width
/2, sr
.y
+ sr
.height
/2, null, true, null);
2948 burro
.addPostTask(new Runnable() { public void run() {
2949 Display
.this.getLayerSet().addLayerContentStep(layer
);
2951 } else if (command
.equals("Import grid...")) {
2952 Display
.this.getLayerSet().addLayerContentStep(layer
);
2953 Bureaucrat burro
= project
.getLoader().importGrid(layer
);
2954 burro
.addPostTask(new Runnable() { public void run() {
2955 Display
.this.getLayerSet().addLayerContentStep(layer
);
2957 } else if (command
.equals("Import sequence as grid...")) {
2958 Display
.this.getLayerSet().addLayerContentStep(layer
);
2959 Bureaucrat burro
= project
.getLoader().importSequenceAsGrid(layer
);
2960 burro
.addPostTask(new Runnable() { public void run() {
2961 Display
.this.getLayerSet().addLayerContentStep(layer
);
2963 } else if (command
.equals("Import from text file...")) {
2964 Display
.this.getLayerSet().addLayerContentStep(layer
);
2965 Bureaucrat burro
= project
.getLoader().importImages(layer
);
2966 burro
.addPostTask(new Runnable() { public void run() {
2967 Display
.this.getLayerSet().addLayerContentStep(layer
);
2969 } else if (command
.equals("Import labels as arealists...")) {
2970 Display
.this.getLayerSet().addChangeTreesStep();
2971 Bureaucrat burro
= project
.getLoader().importLabelsAsAreaLists(layer
, null, Double
.MAX_VALUE
, 0, 0.4f
, false);
2972 burro
.addPostTask(new Runnable() { public void run() {
2973 Display
.this.getLayerSet().addChangeTreesStep();
2975 } else if (command
.equals("Make flat image...")) {
2976 // if there's a ROI, just use that as cropping rectangle
2977 Rectangle srcRect
= null;
2978 Roi roi
= canvas
.getFakeImagePlus().getRoi();
2980 srcRect
= roi
.getBounds();
2982 // otherwise, whatever is visible
2983 //srcRect = canvas.getSrcRect();
2984 // The above is confusing. That is what ROIs are for. So paint all:
2985 srcRect
= new Rectangle(0, 0, (int)Math
.ceil(layer
.getParent().getLayerWidth()), (int)Math
.ceil(layer
.getParent().getLayerHeight()));
2988 final String
[] types
= new String
[]{"8-bit grayscale", "RGB Color"};
2989 int the_type
= ImagePlus
.GRAY8
;
2990 final GenericDialog gd
= new GenericDialog("Choose", frame
);
2991 gd
.addSlider("Scale: ", 1, 100, 100);
2992 gd
.addChoice("Type: ", types
, types
[0]);
2993 if (layer
.getParent().size() > 1) {
2995 String[] layers = new String[layer.getParent().size()];
2997 for (Iterator it = layer.getParent().getLayers().iterator(); it.hasNext(); ) {
2998 layers[i] = layer.getProject().findLayerThing((Layer)it.next()).toString();
3001 int i_layer = layer.getParent().indexOf(layer);
3002 gd.addChoice("Start: ", layers, layers[i_layer]);
3003 gd.addChoice("End: ", layers, layers[i_layer]);
3005 Utils
.addLayerRangeChoices(Display
.this.layer
, gd
); /// $#%! where are my lisp macros
3006 gd
.addCheckbox("Include non-empty layers only", true);
3008 gd
.addMessage("Background color:");
3009 Utils
.addRGBColorSliders(gd
, Color
.black
);
3010 gd
.addCheckbox("Best quality", false);
3012 gd
.addCheckbox("Save to file", false);
3013 gd
.addCheckbox("Save for web", false);
3015 if (gd
.wasCanceled()) return;
3016 scale
= gd
.getNextNumber() / 100;
3017 the_type
= (0 == gd
.getNextChoiceIndex() ? ImagePlus
.GRAY8
: ImagePlus
.COLOR_RGB
);
3018 if (Double
.isNaN(scale
) || scale
<= 0.0) {
3019 Utils
.showMessage("Invalid scale.");
3022 Layer
[] layer_array
= null;
3023 boolean non_empty_only
= false;
3024 if (layer
.getParent().size() > 1) {
3025 non_empty_only
= gd
.getNextBoolean();
3026 int i_start
= gd
.getNextChoiceIndex();
3027 int i_end
= gd
.getNextChoiceIndex();
3028 ArrayList al
= new ArrayList();
3029 ArrayList al_zd
= layer
.getParent().getZDisplayables();
3030 ZDisplayable
[] zd
= new ZDisplayable
[al_zd
.size()];
3032 for (int i
=i_start
, j
=0; i
<= i_end
; i
++, j
++) {
3033 Layer la
= layer
.getParent().getLayer(i
);
3034 if (!la
.isEmpty() || !non_empty_only
) al
.add(la
); // checks both the Layer and the ZDisplayable objects in the parent LayerSet
3036 if (0 == al
.size()) {
3037 Utils
.showMessage("All layers are empty!");
3040 layer_array
= new Layer
[al
.size()];
3041 al
.toArray(layer_array
);
3043 layer_array
= new Layer
[]{Display
.this.layer
};
3045 final Color background
= new Color((int)gd
.getNextNumber(), (int)gd
.getNextNumber(), (int)gd
.getNextNumber());
3046 final boolean quality
= gd
.getNextBoolean();
3047 final boolean save_to_file
= gd
.getNextBoolean();
3048 final boolean save_for_web
= gd
.getNextBoolean();
3049 // in its own thread
3050 if (save_for_web
) project
.getLoader().makePrescaledTiles(layer_array
, Patch
.class, srcRect
, scale
, c_alphas
, the_type
);
3051 else project
.getLoader().makeFlatImage(layer_array
, srcRect
, scale
, c_alphas
, the_type
, save_to_file
, quality
, background
);
3053 } else if (command
.equals("Lock")) {
3054 selection
.setLocked(true);
3055 } else if (command
.equals("Unlock")) {
3056 selection
.setLocked(false);
3057 } else if (command
.equals("Properties...")) {
3058 active
.adjustProperties();
3060 } else if (command
.equals("Cancel alignment")) {
3061 layer
.getParent().cancelAlign();
3062 } else if (command
.equals("Align with landmarks")) {
3063 layer
.getParent().applyAlign(false);
3064 } else if (command
.equals("Align and register")) {
3065 layer
.getParent().applyAlign(true);
3066 } else if (command
.equals("Align using profiles")) {
3067 if (!selection
.contains(Profile
.class)) {
3068 Utils
.showMessage("No profiles are selected.");
3071 // ask for range of layers
3072 final GenericDialog gd
= new GenericDialog("Choose range");
3073 Utils
.addLayerRangeChoices(Display
.this.layer
, gd
);
3075 if (gd
.wasCanceled()) return;
3076 Layer la_start
= layer
.getParent().getLayer(gd
.getNextChoiceIndex());
3077 Layer la_end
= layer
.getParent().getLayer(gd
.getNextChoiceIndex());
3078 if (la_start
== la_end
) {
3079 Utils
.showMessage("Need at least two layers.");
3082 if (selection
.isLocked()) {
3083 Utils
.showMessage("There are locked objects.");
3086 layer
.getParent().startAlign(Display
.this);
3087 layer
.getParent().applyAlign(la_start
, la_end
, selection
);
3088 } else if (command
.equals("Align stack slices")) {
3089 if (getActive() instanceof Patch
) {
3090 final Patch slice
= (Patch
)getActive();
3091 if (slice
.isStack()) {
3092 // check linked group
3093 final HashSet hs
= slice
.getLinkedGroup(new HashSet());
3094 for (Iterator it
= hs
.iterator(); it
.hasNext(); ) {
3095 if (it
.next().getClass() != Patch
.class) {
3096 Utils
.showMessage("Images are linked to other objects, can't proceed to cross-correlate them."); // labels should be fine, need to check that
3100 final LayerSet ls
= slice
.getLayerSet();
3101 final HashSet
<Displayable
> linked
= slice
.getLinkedGroup(null);
3102 ls
.addTransformStep(linked
);
3103 Bureaucrat burro
= Registration
.registerStackSlices((Patch
)getActive()); // will repaint
3104 burro
.addPostTask(new Runnable() { public void run() {
3105 // The current state when done
3106 ls
.addTransformStep(linked
);
3109 Utils
.log("Align stack slices: selected image is not part of a stack.");
3112 } else if (command
.equals("Align layers")) {
3113 final Layer la
= layer
;; // caching, since scroll wheel may change it
3114 la
.getParent().addTransformStep(la
);
3115 Bureaucrat burro
= AlignTask
.alignLayersLinearlyTask( la
);
3116 burro
.addPostTask(new Runnable() { public void run() {
3117 la
.getParent().addTransformStep(la
);
3119 } else if (command
.equals("Align multi-layer mosaic")) {
3120 final Layer la
= layer
; // caching, since scroll wheel may change it
3121 la
.getParent().addTransformStep();
3122 Bureaucrat burro
= AlignTask
.alignMultiLayerMosaicTask( la
);
3123 burro
.addPostTask(new Runnable() { public void run() {
3124 la
.getParent().addTransformStep();
3126 } else if (command
.equals("Properties ...")) { // NOTE the space before the dots, to distinguish from the "Properties..." command that works on Displayable objects.
3127 GenericDialog gd
= new GenericDialog("Properties", Display
.this.frame
);
3128 //gd.addNumericField("layer_scroll_step: ", this.scroll_step, 0);
3129 gd
.addSlider("layer_scroll_step: ", 1, layer
.getParent().size(), Display
.this.scroll_step
);
3130 gd
.addChoice("snapshots_mode", LayerSet
.snapshot_modes
, LayerSet
.snapshot_modes
[layer
.getParent().getSnapshotsMode()]);
3131 gd
.addCheckbox("prefer_snapshots_quality", layer
.getParent().snapshotsQuality());
3132 Loader lo
= getProject().getLoader();
3133 boolean using_mipmaps
= lo
.isMipMapsEnabled();
3134 gd
.addCheckbox("enable_mipmaps", using_mipmaps
);
3135 String preprocessor
= project
.getLoader().getPreprocessor();
3136 gd
.addStringField("image_preprocessor: ", null == preprocessor ?
"" : preprocessor
);
3137 gd
.addCheckbox("enable_layer_pixels virtualization", layer
.getParent().isPixelsVirtualizationEnabled());
3138 double max
= layer
.getParent().getLayerWidth() < layer
.getParent().getLayerHeight() ? layer
.getParent().getLayerWidth() : layer
.getParent().getLayerHeight();
3139 gd
.addSlider("max_dimension of virtualized layer pixels: ", 0, max
, layer
.getParent().getPixelsMaxDimension());
3142 if (gd
.wasCanceled()) return;
3144 int sc
= (int) gd
.getNextNumber();
3146 Display
.this.scroll_step
= sc
;
3147 updateInDatabase("scroll_step");
3149 layer
.getParent().setSnapshotsMode(gd
.getNextChoiceIndex());
3150 layer
.getParent().setSnapshotsQuality(gd
.getNextBoolean());
3152 boolean generate_mipmaps
= gd
.getNextBoolean();
3153 if (using_mipmaps
&& generate_mipmaps
) {
3156 if (using_mipmaps
) { // and !generate_mipmaps
3157 lo
.flushMipMaps(true);
3159 // not using mipmaps before, and true == generate_mipmaps
3160 lo
.generateMipMaps(layer
.getParent().getDisplayables(Patch
.class));
3164 final String prepro
= gd
.getNextString();
3165 if (!project
.getLoader().setPreprocessor(prepro
.trim())) {
3166 Utils
.showMessage("Could NOT set the preprocessor to " + prepro
);
3169 layer
.getParent().setPixelsVirtualizationEnabled(gd
.getNextBoolean());
3170 layer
.getParent().setPixelsMaxDimension((int)gd
.getNextNumber());
3171 } else if (command
.equals("Search...")) {
3173 } else if (command
.equals("Select all")) {
3174 selection
.selectAll();
3175 repaint(Display
.this.layer
, selection
.getBox(), 0);
3176 } else if (command
.equals("Select none")) {
3177 Rectangle box
= selection
.getBox();
3179 repaint(Display
.this.layer
, box
, 0);
3180 } else if (command
.equals("Restore selection")) {
3181 selection
.restore();
3182 } else if (command
.equals("Select under ROI")) {
3183 Roi roi
= canvas
.getFakeImagePlus().getRoi();
3184 if (null == roi
) return;
3185 selection
.selectAll(roi
, true);
3186 } else if (command
.equals("Merge")) {
3187 ArrayList al_sel
= selection
.getSelected();
3188 // put active at the beginning, to work as the base on which other's will get merged
3189 al_sel
.remove(Display
.this.active
);
3190 al_sel
.add(0, Display
.this.active
);
3191 AreaList ali
= AreaList
.merge(al_sel
);
3193 // remove all but the first from the selection
3194 for (int i
=1; i
<al_sel
.size(); i
++) {
3195 Object ob
= al_sel
.get(i
);
3196 if (ob
.getClass() == AreaList
.class) {
3197 selection
.remove((Displayable
)ob
);
3200 selection
.updateTransform(ali
);
3201 repaint(ali
.getLayerSet(), ali
, 0);
3203 } else if (command
.equals("Identify...")) {
3204 // for pipes only for now
3205 if (!(active
instanceof Pipe
)) return;
3206 ini
.trakem2
.vector
.Compare
.findSimilar((Pipe
)active
);
3207 } else if (command
.equals("Identify with axes...")) {
3208 if (!(active
instanceof Pipe
)) return;
3209 if (Project
.getProjects().size() < 2) {
3210 Utils
.showMessage("You need at least two projects open:\n-A reference project\n-The current project with the pipe to identify");
3213 ini
.trakem2
.vector
.Compare
.findSimilarWithAxes((Pipe
)active
);
3214 } else if (command
.equals("View orthoslices")) {
3215 if (!(active
instanceof Patch
)) return;
3216 Display3D
.showOrthoslices(((Patch
)active
));
3217 } else if (command
.equals("View volume")) {
3218 if (!(active
instanceof Patch
)) return;
3219 Display3D
.showVolume(((Patch
)active
));
3220 } else if (command
.equals("Show in 3D")) {
3221 for (Iterator it
= selection
.getSelected(ZDisplayable
.class).iterator(); it
.hasNext(); ) {
3222 ZDisplayable zd
= (ZDisplayable
)it
.next();
3223 Display3D
.show(zd
.getProject().findProjectThing(zd
));
3225 // handle profile lists ...
3226 HashSet hs
= new HashSet();
3227 for (Iterator it
= selection
.getSelected(Profile
.class).iterator(); it
.hasNext(); ) {
3228 Displayable d
= (Displayable
)it
.next();
3229 ProjectThing profile_list
= (ProjectThing
)d
.getProject().findProjectThing(d
).getParent();
3230 if (!hs
.contains(profile_list
)) {
3231 Display3D
.show(profile_list
);
3232 hs
.add(profile_list
);
3235 } else if (command
.equals("Snap")) {
3236 if (!(active
instanceof Patch
)) return;
3237 StitchingTEM
.snap(getActive(), Display
.this);
3238 } else if (command
.equals("Blend")) {
3239 HashSet
<Patch
> patches
= new HashSet
<Patch
>();
3240 for (final Displayable d
: selection
.getSelected()) {
3241 if (d
.getClass() == Patch
.class) patches
.add((Patch
)d
);
3243 if (patches
.size() > 1) {
3244 GenericDialog gd
= new GenericDialog("Blending");
3245 gd
.addCheckbox("Respect current alpha mask", true);
3247 if (gd
.wasCanceled()) return;
3248 Blending
.blend(patches
, gd
.getNextBoolean());
3250 IJ
.log("Please select more than one overlapping image.");
3252 } else if (command
.equals("Montage")) {
3253 if (!(active
instanceof Patch
)) {
3254 Utils
.showMessage("Please select only images.");
3257 final Set
<Displayable
> affected
= new HashSet
<Displayable
>(selection
.getAffected());
3258 for (final Displayable d
: affected
)
3260 Utils
.showMessage( "You cannot montage linked objects." );
3263 // make an undo step!
3264 final LayerSet ls
= layer
.getParent();
3265 ls
.addTransformStep(affected
);
3266 Bureaucrat burro
= AlignTask
.alignSelectionTask( selection
);
3267 burro
.addPostTask(new Runnable() { public void run() {
3268 ls
.addTransformStep(affected
);
3270 } else if (command
.equals("Lens correction")) {
3271 final Layer la
= layer
;
3272 la
.getParent().addDataEditStep(new HashSet
<Displayable
>(la
.getParent().getDisplayables()));
3273 Bureaucrat burro
= DistortionCorrectionTask
.correctDistortionFromSelection( selection
);
3274 burro
.addPostTask(new Runnable() { public void run() {
3275 // no means to know which where modified and from which layers!
3276 la
.getParent().addDataEditStep(new HashSet
<Displayable
>(la
.getParent().getDisplayables()));
3278 } else if (command
.equals("Link images...")) {
3279 GenericDialog gd
= new GenericDialog("Options");
3280 gd
.addMessage("Linking images to images (within their own layer only):");
3281 String
[] options
= {"all images to all images", "each image with any other overlapping image"};
3282 gd
.addChoice("Link: ", options
, options
[1]);
3283 String
[] options2
= {"selected images only", "all images in this layer", "all images in all layers"};
3284 gd
.addChoice("Apply to: ", options2
, options2
[0]);
3286 if (gd
.wasCanceled()) return;
3288 final HashSet
<Displayable
> ds
= new HashSet
<Displayable
>(lay
.getParent().getDisplayables());
3289 lay
.getParent().addDataEditStep(ds
);
3290 boolean overlapping_only
= 1 == gd
.getNextChoiceIndex();
3291 switch (gd
.getNextChoiceIndex()) {
3293 Patch
.crosslink(selection
.getSelected(Patch
.class), overlapping_only
);
3296 Patch
.crosslink(lay
.getDisplayables(Patch
.class), overlapping_only
);
3299 for (final Layer la
: lay
.getParent().getLayers()) {
3300 Patch
.crosslink(la
.getDisplayables(Patch
.class), overlapping_only
);
3304 lay
.getParent().addDataEditStep(ds
);
3305 } else if (command
.equals("Enhance contrast (selected images)...")) {
3306 final Layer la
= layer
;
3307 final HashSet
<Displayable
> ds
= new HashSet
<Displayable
>(la
.getParent().getDisplayables());
3308 la
.getParent().addDataEditStep(ds
);
3309 ArrayList al
= selection
.getSelected(Patch
.class);
3310 Bureaucrat burro
= getProject().getLoader().homogenizeContrast(al
);
3311 burro
.addPostTask(new Runnable() { public void run() {
3312 la
.getParent().addDataEditStep(ds
);
3314 } else if (command
.equals("Enhance contrast layer-wise...")) {
3315 // ask for range of layers
3316 final GenericDialog gd
= new GenericDialog("Choose range");
3317 Utils
.addLayerRangeChoices(Display
.this.layer
, gd
);
3319 if (gd
.wasCanceled()) return;
3320 java
.util
.List list
= layer
.getParent().getLayers().subList(gd
.getNextChoiceIndex(), gd
.getNextChoiceIndex() +1); // exclusive end
3321 Layer
[] la
= new Layer
[list
.size()];
3323 final HashSet
<Displayable
> ds
= new HashSet
<Displayable
>();
3324 for (final Layer l
: la
) ds
.addAll(l
.getDisplayables(Patch
.class));
3325 getLayerSet().addDataEditStep(ds
);
3326 Bureaucrat burro
= project
.getLoader().homogenizeContrast(la
);
3327 burro
.addPostTask(new Runnable() { public void run() {
3328 getLayerSet().addDataEditStep(ds
);
3330 } else if (command
.equals("Set Min and Max layer-wise...")) {
3331 Displayable active
= getActive();
3334 if (null != active
&& active
.getClass() == Patch
.class) {
3335 min
= ((Patch
)active
).getMin();
3336 max
= ((Patch
)active
).getMax();
3338 final GenericDialog gd
= new GenericDialog("Min and Max");
3339 gd
.addMessage("Set min and max to all images in the layer range");
3340 Utils
.addLayerRangeChoices(Display
.this.layer
, gd
);
3341 gd
.addNumericField("min: ", min
, 2);
3342 gd
.addNumericField("max: ", max
, 2);
3344 if (gd
.wasCanceled()) return;
3346 min
= gd
.getNextNumber();
3347 max
= gd
.getNextNumber();
3348 ArrayList
<Displayable
> al
= new ArrayList
<Displayable
>();
3349 for (final Layer la
: layer
.getParent().getLayers().subList(gd
.getNextChoiceIndex(), gd
.getNextChoiceIndex() +1)) { // exclusive end
3350 al
.addAll(la
.getDisplayables(Patch
.class));
3352 final HashSet
<Displayable
> ds
= new HashSet
<Displayable
>(al
);
3353 getLayerSet().addDataEditStep(ds
);
3354 Bureaucrat burro
= project
.getLoader().setMinAndMax(al
, min
, max
);
3355 burro
.addPostTask(new Runnable() { public void run() {
3356 getLayerSet().addDataEditStep(ds
);
3358 } else if (command
.equals("Set Min and Max (selected images)...")) {
3359 Displayable active
= getActive();
3362 if (null != active
&& active
.getClass() == Patch
.class) {
3363 min
= ((Patch
)active
).getMin();
3364 max
= ((Patch
)active
).getMax();
3366 final GenericDialog gd
= new GenericDialog("Min and Max");
3367 gd
.addMessage("Set min and max to all selected images");
3368 gd
.addNumericField("min: ", min
, 2);
3369 gd
.addNumericField("max: ", max
, 2);
3371 if (gd
.wasCanceled()) return;
3373 min
= gd
.getNextNumber();
3374 max
= gd
.getNextNumber();
3375 final HashSet
<Displayable
> ds
= new HashSet
<Displayable
>(selection
.getSelected(Patch
.class));
3376 getLayerSet().addDataEditStep(ds
);
3377 Bureaucrat burro
= project
.getLoader().setMinAndMax(selection
.getSelected(Patch
.class), min
, max
);
3378 burro
.addPostTask(new Runnable() { public void run() {
3379 getLayerSet().addDataEditStep(ds
);
3381 } else if (command
.equals("Create subproject")) {
3382 Roi roi
= canvas
.getFakeImagePlus().getRoi();
3383 if (null == roi
) return; // the menu item is not active unless there is a ROI
3385 if (1 == layer
.getParent().size()) {
3386 first
= last
= layer
;
3388 GenericDialog gd
= new GenericDialog("Choose layer range");
3389 Utils
.addLayerRangeChoices(layer
, gd
);
3391 if (gd
.wasCanceled()) return;
3392 first
= layer
.getParent().getLayer(gd
.getNextChoiceIndex());
3393 last
= layer
.getParent().getLayer(gd
.getNextChoiceIndex());
3394 Utils
.log2("first, last: " + first
+ ", " + last
);
3396 Project sub
= getProject().createSubproject(roi
.getBounds(), first
, last
);
3397 final LayerSet subls
= sub
.getRootLayerSet();
3398 final Display d
= new Display(sub
, subls
.getLayer(0));
3399 SwingUtilities
.invokeLater(new Runnable() { public void run() {
3400 d
.canvas
.showCentered(new Rectangle(0, 0, (int)subls
.getLayerWidth(), (int)subls
.getLayerHeight()));
3402 } else if (command
.startsWith("Arealists as labels")) {
3403 GenericDialog gd
= new GenericDialog("Export labels");
3404 gd
.addSlider("Scale: ", 1, 100, 100);
3405 final String
[] options
= {"All area list", "Selected area lists"};
3406 gd
.addChoice("Export: ", options
, options
[0]);
3407 Utils
.addLayerRangeChoices(layer
, gd
);
3408 gd
.addCheckbox("Visible only", true);
3410 if (gd
.wasCanceled()) return;
3411 final float scale
= (float)(gd
.getNextNumber() / 100);
3412 java
.util
.List al
= 0 == gd
.getNextChoiceIndex() ? layer
.getParent().getZDisplayables(AreaList
.class) : selection
.getSelected(AreaList
.class);
3414 Utils
.log("No area lists found to export.");
3417 // Generics are ... a pain? I don't understand them? They fail when they shouldn't? And so easy to workaround that they are a shame?
3418 al
= (java
.util
.List
<Displayable
>) al
;
3420 int first
= gd
.getNextChoiceIndex();
3421 int last
= gd
.getNextChoiceIndex();
3422 boolean visible_only
= gd
.getNextBoolean();
3423 if (-1 != command
.indexOf("(amira)")) {
3424 AreaList
.exportAsLabels(al
, canvas
.getFakeImagePlus().getRoi(), scale
, first
, last
, visible_only
, true, true);
3425 } else if (-1 != command
.indexOf("(tif)")) {
3426 AreaList
.exportAsLabels(al
, canvas
.getFakeImagePlus().getRoi(), scale
, first
, last
, visible_only
, false, false);
3428 } else if (command
.equals("Project properties...")) {
3429 project
.adjustProperties();
3430 } else if (command
.equals("Release memory...")) {
3431 Bureaucrat
.createAndStart(new Worker("Releasing memory") {
3435 GenericDialog gd
= new GenericDialog("Release Memory");
3436 int max
= (int)(IJ
.maxMemory() / 1000000);
3437 gd
.addSlider("Megabytes: ", 0, max
, max
/2);
3439 if (!gd
.wasCanceled()) {
3440 int n_mb
= (int)gd
.getNextNumber();
3441 project
.getLoader().releaseToFit((long)n_mb
*1000000);
3443 } catch (Throwable e
) {
3450 } else if (command
.equals("Flush image cache")) {
3451 Loader
.releaseAllCaches();
3453 Utils
.log2("Display: don't know what to do with command " + command
);
3458 /** Update in all displays the Transform for the given Displayable if it's selected. */
3459 static public void updateTransform(final Displayable displ
) {
3460 for (final Display d
: al_displays
) {
3461 if (d
.selection
.contains(displ
)) d
.selection
.updateTransform(displ
);
3465 /** Order the profiles of the parent profile_list by Z order, and fix the ProjectTree.*/
3467 private void fixZOrdering(Profile profile) {
3468 ProjectThing thing = project.findProjectThing(profile);
3469 if (null == thing) {
3470 Utils.log2("Display.fixZOrdering: null thing?");
3473 ((ProjectThing)thing.getParent()).fixZOrdering();
3474 project.getProjectTree().updateList(thing.getParent());
3478 /** The number of layers to scroll through with the wheel; 1 by default.*/
3479 public int getScrollStep() { return this.scroll_step
; }
3481 public void setScrollStep(int scroll_step
) {
3482 if (scroll_step
< 1) scroll_step
= 1;
3483 this.scroll_step
= scroll_step
;
3484 updateInDatabase("scroll_step");
3487 protected Bureaucrat
importImage() {
3488 Worker worker
= new Worker("Import image") { /// all this verbosity is what happens when functions are not first class citizens. I could abstract it away by passing a string name "importImage" and invoking it with reflection, but that is an even bigger PAIN
3494 Rectangle srcRect
= canvas
.getSrcRect();
3495 int x
= srcRect
.x
+ srcRect
.width
/ 2;
3496 int y
= srcRect
.y
+ srcRect
.height
/ 2;
3497 Patch p
= project
.getLoader().importImage(project
, x
, y
);
3500 Utils
.showMessage("Could not open the image.");
3504 Display
.this.getLayerSet().addLayerContentStep(layer
);
3506 layer
.add(p
); // will add it to the proper Displays
3508 Display
.this.getLayerSet().addLayerContentStep(layer
);
3511 } catch (Exception e
) {
3517 return Bureaucrat
.createAndStart(worker
, getProject());
3520 protected Bureaucrat
importNextImage() {
3521 Worker worker
= new Worker("Import image") { /// all this verbosity is what happens when functions are not first class citizens. I could abstract it away by passing a string name "importImage" and invoking it with reflection, but that is an even bigger PAIN
3526 Rectangle srcRect
= canvas
.getSrcRect();
3527 int x
= srcRect
.x
+ srcRect
.width
/ 2;// - imp.getWidth() / 2;
3528 int y
= srcRect
.y
+ srcRect
.height
/ 2;// - imp.getHeight()/ 2;
3529 Patch p
= project
.getLoader().importNextImage(project
, x
, y
);
3531 Utils
.showMessage("Could not open next image.");
3536 Display
.this.getLayerSet().addLayerContentStep(layer
);
3538 layer
.add(p
); // will add it to the proper Displays
3540 Display
.this.getLayerSet().addLayerContentStep(layer
);
3542 } catch (Exception e
) {
3548 return Bureaucrat
.createAndStart(worker
, getProject());
3552 /** Make the given channel have the given alpha (transparency). */
3553 public void setChannel(int c
, float alpha
) {
3554 int a
= (int)(255 * alpha
);
3555 int l
= (c_alphas
&0xff000000)>>24;
3556 int r
= (c_alphas
&0xff0000)>>16;
3557 int g
= (c_alphas
&0xff00)>>8;
3558 int b
= c_alphas
&0xff;
3561 // all to the given alpha
3562 c_alphas
= (l
<<24) + (r
<<16) + (g
<<8) + b
; // parenthesis are NECESSARY
3565 // modify only the red
3566 c_alphas
= (l
<<24) + (a
<<16) + (g
<<8) + b
;
3569 c_alphas
= (l
<<24) + (r
<<16) + (a
<<8) + b
;
3572 c_alphas
= (l
<<24) + (r
<<16) + (g
<<8) + a
;
3575 //Utils.log2("c_alphas: " + c_alphas);
3576 //canvas.setUpdateGraphics(true);
3577 canvas
.repaint(true);
3578 updateInDatabase("c_alphas");
3581 /** Set the channel as active and the others as inactive. */
3582 public void setActiveChannel(Channel channel
) {
3583 for (int i
=0; i
<4; i
++) {
3584 if (channel
!= channels
[i
]) channels
[i
].setActive(false);
3585 else channel
.setActive(true);
3587 Utils
.updateComponent(panel_channels
);
3588 transp_slider
.setValue((int)(channel
.getAlpha() * 100));
3591 public int getDisplayChannelAlphas() { return c_alphas
; }
3593 // rename this method and the getDisplayChannelAlphas ! They sound the same!
3594 public int getChannelAlphas() {
3595 return ((int)(channels
[0].getAlpha() * 255)<<24) + ((int)(channels
[1].getAlpha() * 255)<<16) + ((int)(channels
[2].getAlpha() * 255)<<8) + (int)(channels
[3].getAlpha() * 255);
3598 public int getChannelAlphasState() {
3599 return ((channels
[0].isSelected() ?
255 : 0)<<24)
3600 + ((channels
[1].isSelected() ?
255 : 0)<<16)
3601 + ((channels
[2].isSelected() ?
255 : 0)<<8)
3602 + (channels
[3].isSelected() ?
255 : 0);
3605 /** Show the layer in the front Display, or in a new Display if the front Display is showing a layer from a different LayerSet. */
3606 static public void showFront(final Layer layer
) {
3607 Display display
= front
;
3608 if (null == display
|| display
.layer
.getParent() != layer
.getParent()) {
3609 display
= new Display(layer
.getProject(), layer
, null); // gets set to front
3611 display
.setLayer(layer
);
3615 /** Show the given Displayable centered and selected. If select is false, the selection is cleared. */
3616 static public void showCentered(Layer layer
, Displayable displ
, boolean select
, boolean shift_down
) {
3617 // see if the given layer belongs to the layer set being displayed
3618 Display display
= front
; // to ensure thread consistency to some extent
3619 if (null == display
|| display
.layer
.getParent() != layer
.getParent()) {
3620 display
= new Display(layer
.getProject(), layer
, displ
); // gets set to front
3621 } else if (display
.layer
!= layer
) {
3622 display
.setLayer(layer
);
3625 if (!shift_down
) display
.selection
.clear();
3626 display
.selection
.add(displ
);
3628 display
.selection
.clear();
3630 display
.showCentered(displ
);
3633 private final void showCentered(final Displayable displ
) {
3634 if (null == displ
) return;
3635 SwingUtilities
.invokeLater(new Runnable() { public void run() {
3636 displ
.setVisible(true);
3637 Rectangle box
= displ
.getBoundingBox();
3638 if (0 == box
.width
|| 0 == box
.height
) {
3639 box
.width
= (int)layer
.getLayerWidth();
3640 box
.height
= (int)layer
.getLayerHeight();
3642 canvas
.showCentered(box
);
3643 scrollToShow(displ
);
3644 if (displ
instanceof ZDisplayable
) {
3645 // scroll to first layer that has a point
3646 ZDisplayable zd
= (ZDisplayable
)displ
;
3647 setLayer(zd
.getFirstLayer());
3652 /** Listen to interesting updates, such as the ColorPicker and updates to Patch objects. */
3653 public void imageUpdated(ImagePlus updated
) {
3654 // detect ColorPicker WARNING this will work even if the Display is not the window immediately active under the color picker.
3655 if (this == front
&& updated
instanceof ij
.plugin
.ColorPicker
) {
3656 if (null != active
&& project
.isInputEnabled()) {
3657 selection
.setColor(Toolbar
.getForegroundColor());
3658 Display
.repaint(front
.layer
, selection
.getBox(), 0);
3662 // $%#@!! LUT changes don't set the image as changed
3663 //if (updated instanceof PatchStack) {
3664 // updated.changes = 1
3667 //Utils.log2("imageUpdated: " + updated + " " + updated.getClass());
3669 /* // never gets called (?)
3670 // the above is overkill. Instead:
3671 if (updated instanceof PatchStack) {
3672 Patch p = ((PatchStack)updated).getCurrentPatch();
3673 ImageProcessor ip = updated.getProcessor();
3674 p.setMinAndMax(ip.getMin(), ip.getMax());
3675 Utils.log2("setting min and max: " + ip.getMin() + ", " + ip.getMax());
3676 project.getLoader().decacheAWT(p.getId()); // including level 0, which will be editable
3677 // on repaint, it will be recreated
3678 //((PatchStack)updated).decacheAll(); // so that it will repaint with a newly created image
3682 // detect LUT changes: DONE at PatchStack, which is the active (virtual) image
3683 //Utils.log2("calling decache for " + updated);
3684 //getProject().getLoader().decache(updated);
3687 public void imageClosed(ImagePlus imp
) {}
3688 public void imageOpened(ImagePlus imp
) {}
3690 /** Release memory captured by the offscreen images */
3691 static public void flushAll() {
3692 for (final Display d
: al_displays
) {
3700 static public Display
getFront() {
3704 static public void setCursorToAll(final Cursor c
) {
3705 for (final Display d
: al_displays
) {
3706 d
.frame
.setCursor(c
);
3710 protected void setCursor(Cursor c
) {
3714 /** Used by the Displayable to update the visibility checkbox in other Displays. */
3715 static protected void updateVisibilityCheckbox(final Layer layer
, final Displayable displ
, final Display calling_display
) {
3716 //LOCKS ALL //SwingUtilities.invokeLater(new Runnable() { public void run() {
3717 for (final Display d
: al_displays
) {
3718 if (d
== calling_display
) continue;
3719 if (d
.layer
.contains(displ
) || (displ
instanceof ZDisplayable
&& d
.layer
.getParent().contains((ZDisplayable
)displ
))) {
3720 DisplayablePanel dp
= d
.ht_panels
.get(displ
);
3721 if (null != dp
) dp
.updateVisibilityCheckbox();
3727 protected boolean isActiveWindow() {
3728 return frame
.isActive();
3731 /** Toggle user input; pan and zoom are always enabled though.*/
3732 static public void setReceivesInput(final Project project
, final boolean b
) {
3733 for (final Display d
: al_displays
) {
3734 if (d
.project
== project
) d
.canvas
.setReceivesInput(b
);
3738 /** Export the DTD that defines this object. */
3739 static public void exportDTD(StringBuffer sb_header
, HashSet hs
, String indent
) {
3740 if (hs
.contains("t2_display")) return; // TODO to avoid collisions the type shoud be in a namespace such as tm2:display
3741 hs
.add("t2_display");
3742 sb_header
.append(indent
).append("<!ELEMENT t2_display EMPTY>\n")
3743 .append(indent
).append("<!ATTLIST t2_display id NMTOKEN #REQUIRED>\n")
3744 .append(indent
).append("<!ATTLIST t2_display layer_id NMTOKEN #REQUIRED>\n")
3745 .append(indent
).append("<!ATTLIST t2_display x NMTOKEN #REQUIRED>\n")
3746 .append(indent
).append("<!ATTLIST t2_display y NMTOKEN #REQUIRED>\n")
3747 .append(indent
).append("<!ATTLIST t2_display magnification NMTOKEN #REQUIRED>\n")
3748 .append(indent
).append("<!ATTLIST t2_display srcrect_x NMTOKEN #REQUIRED>\n")
3749 .append(indent
).append("<!ATTLIST t2_display srcrect_y NMTOKEN #REQUIRED>\n")
3750 .append(indent
).append("<!ATTLIST t2_display srcrect_width NMTOKEN #REQUIRED>\n")
3751 .append(indent
).append("<!ATTLIST t2_display srcrect_height NMTOKEN #REQUIRED>\n")
3752 .append(indent
).append("<!ATTLIST t2_display scroll_step NMTOKEN #REQUIRED>\n")
3753 .append(indent
).append("<!ATTLIST t2_display c_alphas NMTOKEN #REQUIRED>\n")
3754 .append(indent
).append("<!ATTLIST t2_display c_alphas_state NMTOKEN #REQUIRED>\n")
3757 /** Export all displays of the given project as XML entries. */
3758 static public void exportXML(final Project project
, final Writer writer
, final String indent
, final Object any
) throws Exception
{
3759 final StringBuffer sb_body
= new StringBuffer();
3760 final String in
= indent
+ "\t";
3761 for (final Display d
: al_displays
) {
3762 if (d
.project
!= project
) continue;
3763 final Rectangle r
= d
.frame
.getBounds();
3764 final Rectangle srcRect
= d
.canvas
.getSrcRect();
3765 final double magnification
= d
.canvas
.getMagnification();
3766 sb_body
.append(indent
).append("<t2_display id=\"").append(d
.id
).append("\"\n")
3767 .append(in
).append("layer_id=\"").append(d
.layer
.getId()).append("\"\n")
3768 .append(in
).append("c_alphas=\"").append(d
.c_alphas
).append("\"\n")
3769 .append(in
).append("c_alphas_state=\"").append(d
.getChannelAlphasState()).append("\"\n")
3770 .append(in
).append("x=\"").append(r
.x
).append("\"\n")
3771 .append(in
).append("y=\"").append(r
.y
).append("\"\n")
3772 .append(in
).append("magnification=\"").append(magnification
).append("\"\n")
3773 .append(in
).append("srcrect_x=\"").append(srcRect
.x
).append("\"\n")
3774 .append(in
).append("srcrect_y=\"").append(srcRect
.y
).append("\"\n")
3775 .append(in
).append("srcrect_width=\"").append(srcRect
.width
).append("\"\n")
3776 .append(in
).append("srcrect_height=\"").append(srcRect
.height
).append("\"\n")
3777 .append(in
).append("scroll_step=\"").append(d
.scroll_step
).append("\"\n")
3779 sb_body
.append(indent
).append("/>\n");
3781 writer
.write(sb_body
.toString());
3784 static public void toolChanged(final String tool_name
) {
3785 Utils
.log2("tool name: " + tool_name
);
3786 if (!tool_name
.equals("ALIGN")) {
3787 for (final Display d
: al_displays
) {
3788 d
.layer
.getParent().cancelAlign();
3793 static public void toolChanged(final int tool
) {
3794 //Utils.log2("int tool is " + tool);
3795 if (ProjectToolbar
.PEN
== tool
) {
3796 // erase bounding boxes
3797 for (final Display d
: al_displays
) {
3798 if (null != d
.active
) d
.repaint(d
.layer
, d
.selection
.getBox(), 2);
3801 if (null != front
) {
3802 WindowManager
.setTempCurrentImage(front
.canvas
.getFakeImagePlus());
3806 public Selection
getSelection() {
3810 public boolean isSelected(Displayable d
) {
3811 return selection
.contains(d
);
3814 static public void updateSelection() {
3815 Display
.updateSelection(null);
3817 static public void updateSelection(final Display calling
) {
3818 final HashSet hs
= new HashSet();
3819 for (final Display d
: al_displays
) {
3820 if (hs
.contains(d
.layer
)) continue;
3822 if (null == d
|| null == d
.selection
) {
3823 Utils
.log2("d is : "+ d
+ " d.selection is " + d
.selection
);
3825 d
.selection
.update(); // recomputes box
3827 if (d
!= calling
) { // TODO this is so dirty!
3828 if (d
.selection
.getNLinked() > 1) d
.canvas
.setUpdateGraphics(true); // this is overkill anyway
3829 d
.canvas
.repaint(d
.selection
.getLinkedBox(), Selection
.PADDING
);
3830 d
.navigator
.repaint(true); // everything
3835 static public void clearSelection(final Layer layer
) {
3836 for (final Display d
: al_displays
) {
3837 if (d
.layer
== layer
) d
.selection
.clear();
3840 static public void clearSelection() {
3841 for (final Display d
: al_displays
) {
3842 d
.selection
.clear();
3846 private void setTempCurrentImage() {
3847 WindowManager
.setTempCurrentImage(canvas
.getFakeImagePlus());
3850 /** Check if any display will paint the given Displayable at the given magnification. */
3851 static public boolean willPaint(final Displayable displ
, final double magnification
) {
3852 Rectangle box
= null; ;
3853 for (final Display d
: al_displays
) {
3854 /* // Can no longer do this check, because 'magnification' is now affected by the Displayable AffineTransform! And thus it would not paint after the prePaint.
3855 if (Math.abs(d.canvas.getMagnification() - magnification) > 0.00000001) {
3859 if (null == box
) box
= displ
.getBoundingBox(null);
3860 if (d
.canvas
.getSrcRect().intersects(box
)) {
3867 public void hideDeselected(final boolean not_images
) {
3869 final ArrayList all
= layer
.getParent().getZDisplayables(); // a copy
3870 all
.addAll(layer
.getDisplayables());
3871 all
.removeAll(selection
.getSelected());
3872 if (not_images
) all
.removeAll(layer
.getDisplayables(Patch
.class));
3873 for (final Displayable d
: (ArrayList
<Displayable
>)all
) {
3874 if (d
.isVisible()) d
.setVisible(false);
3876 Display
.update(layer
);
3879 /** Cleanup internal lists that may contain the given Displayable. */
3880 static public void flush(final Displayable displ
) {
3881 for (final Display d
: al_displays
) {
3882 d
.selection
.removeFromPrev(displ
);
3886 public void resizeCanvas() {
3887 GenericDialog gd
= new GenericDialog("Resize LayerSet");
3888 gd
.addNumericField("new width: ", layer
.getLayerWidth(), 3);
3889 gd
.addNumericField("new height: ",layer
.getLayerHeight(),3);
3890 gd
.addChoice("Anchor: ", LayerSet
.ANCHORS
, LayerSet
.ANCHORS
[7]);
3892 if (gd
.wasCanceled()) return;
3893 double new_width
= gd
.getNextNumber();
3894 double new_height
=gd
.getNextNumber();
3895 layer
.getParent().setDimensions(new_width
, new_height
, gd
.getNextChoiceIndex()); // will complain and prevent cropping existing Displayable objects
3899 // To record layer changes -- but it's annoying, this is visualization not data.
3900 static class DoSetLayer implements DoStep {
3901 final Display display;
3903 DoSetLayer(final Display display) {
3904 this.display = display;
3905 this.layer = display.layer;
3907 public Displayable getD() { return null; }
3908 public boolean isEmpty() { return false; }
3909 public boolean apply(final int action) {
3910 display.setLayer(layer);
3912 public boolean isIdenticalTo(final Object ob) {
3913 if (!ob instanceof DoSetLayer) return false;
3914 final DoSetLayer dsl = (DoSetLayer) ob;
3915 return dsl.display == this.display && dsl.layer == this.layer;
3920 protected void duplicateLinkAndSendTo(final Displayable active
, final int position
, final Layer other_layer
) {
3921 if (null == active
|| !(active
instanceof Profile
)) return;
3922 if (active
.getLayer() == other_layer
) return; // can't do that!
3923 Profile profile
= project
.getProjectTree().duplicateChild((Profile
)active
, position
, other_layer
);
3924 if (null == profile
) return;
3925 active
.link(profile
);
3926 slt
.setAndWait(other_layer
);
3927 other_layer
.add(profile
);
3928 selection
.add(profile
);
3931 private final HashMap
<Color
,Layer
> layer_channels
= new HashMap
<Color
,Layer
>();
3932 private final TreeMap
<Integer
,LayerPanel
> layer_alpha
= new TreeMap
<Integer
,LayerPanel
>();
3934 /** Remove all red/blue coloring of layers, and repaint canvas. */
3935 protected void resetLayerColors() {
3936 synchronized (layer_channels
) {
3937 for (final Layer l
: new ArrayList
<Layer
>(layer_channels
.values())) { // avoid concurrent modification exception
3938 final LayerPanel lp
= layer_panels
.get(l
);
3939 lp
.setColor(Color
.white
);
3940 setColorChannel(lp
.layer
, Color
.white
);
3942 layer_channels
.clear();
3947 /** Set all layer alphas to zero, and repaint canvas. */
3948 protected void resetLayerAlphas() {
3949 synchronized (layer_channels
) {
3950 for (final LayerPanel lp
: new ArrayList
<LayerPanel
>(layer_alpha
.values())) {
3953 layer_alpha
.clear(); // should have already been cleared
3958 /** Add to layer_alpha table, or remove if alpha is zero. */
3959 protected void storeLayerAlpha(final LayerPanel lp
, final float a
) {
3960 synchronized (layer_channels
) {
3961 if (M
.equals(0, a
)) {
3962 layer_alpha
.remove(lp
.layer
.getParent().indexOf(lp
.layer
));
3964 layer_alpha
.put(lp
.layer
.getParent().indexOf(lp
.layer
), lp
);
3969 static protected final int REPAINT_SINGLE_LAYER
= 0;
3970 static protected final int REPAINT_MULTI_LAYER
= 1;
3971 static protected final int REPAINT_RGB_LAYER
= 2;
3973 /** Sets the values atomically, returns the painting mode. */
3974 protected int getPaintMode(final HashMap
<Color
,Layer
> hm
, final ArrayList
<LayerPanel
> list
) {
3975 synchronized (layer_channels
) {
3976 if (layer_channels
.size() > 0) {
3977 hm
.putAll(layer_channels
);
3978 hm
.put(Color
.green
, this.layer
);
3979 return REPAINT_RGB_LAYER
;
3981 list
.addAll(layer_alpha
.values());
3982 final int len
= list
.size();
3983 if (len
> 1) return REPAINT_MULTI_LAYER
;
3985 if (list
.get(0).layer
== this.layer
) return REPAINT_SINGLE_LAYER
; // normal mode
3986 return REPAINT_MULTI_LAYER
;
3988 return REPAINT_SINGLE_LAYER
;
3992 /** Set a layer to be painted as a specific color channel in the canvas.
3993 * Only Color.red and Color.blue are accepted.
3994 * Color.green is reserved for the current layer. */
3995 protected void setColorChannel(final Layer layer
, final Color color
) {
3996 synchronized (layer_channels
) {
3997 if (Color
.white
== color
) {
3999 for (final Iterator
<Layer
> it
= layer_channels
.values().iterator(); it
.hasNext(); ) {
4000 if (it
.next() == layer
) {
4006 } else if (Color
.red
== color
|| Color
.blue
== color
) {
4007 // Reset current of that color, if any, to white
4008 final Layer l
= layer_channels
.remove(color
);
4009 if (null != l
) layer_panels
.get(l
).setColor(Color
.white
);
4010 // Replace or set new
4011 layer_channels
.put(color
, layer
);
4015 Utils
.log2("Trying to set unacceptable color for layer " + layer
+ " : " + color
);
4018 this.canvas
.repaint(true);