3 TrakEM2 plugin for ImageJ(C).
4 Copyright (C) 2005-2009 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
;
58 import java
.util
.concurrent
.Future
;
60 import lenscorrection
.DistortionCorrectionTask
;
62 /** A Display is a class to show a Layer and enable mouse and keyboard manipulation of all its components. */
63 public final class Display
extends DBObject
implements ActionListener
, ImageListener
{
65 /** The Layer this Display is showing. */
68 private Displayable active
= null;
69 /** All selected Displayable objects, including the active one. */
70 final private Selection selection
= new Selection(this);
73 private JTabbedPane tabs
;
74 private Hashtable
<Class
,JScrollPane
> ht_tabs
;
75 private JScrollPane scroll_patches
;
76 private JPanel panel_patches
;
77 private JScrollPane scroll_profiles
;
78 private JPanel panel_profiles
;
79 private JScrollPane scroll_zdispl
;
80 private JPanel panel_zdispl
;
81 private JScrollPane scroll_channels
;
82 private JPanel panel_channels
;
83 private JScrollPane scroll_labels
;
84 private JPanel panel_labels
;
86 private JPanel panel_layers
;
87 private JScrollPane scroll_layers
;
88 private Hashtable
<Layer
,LayerPanel
> layer_panels
= new Hashtable
<Layer
,LayerPanel
>();
90 private JSlider transp_slider
;
91 private DisplayNavigator navigator
;
92 private JScrollBar scroller
;
94 private DisplayCanvas canvas
; // WARNING this is an AWT component, since it extends ImageCanvas
95 private JPanel canvas_panel
; // and this is a workaround, to better (perhaps) integrate the awt canvas inside a JSplitPane
96 private JSplitPane split
;
98 private JPopupMenu popup
= null;
100 /** Contains the packed alphas of every channel. */
101 private int c_alphas
= 0xffffffff; // all 100 % visible
102 private Channel
[] channels
;
104 private Hashtable
<Displayable
,DisplayablePanel
> ht_panels
= new Hashtable
<Displayable
,DisplayablePanel
>();
106 /** Handle drop events, to insert image files. */
107 private DNDInsertImage dnd
;
109 private boolean size_adjusted
= false;
111 private int scroll_step
= 1;
113 /** Keep track of all existing Display objects. */
114 static private ArrayList
<Display
> al_displays
= new ArrayList
<Display
>();
115 /** The currently focused Display, if any. */
116 static private Display front
= null;
118 /** Displays to open when all objects have been reloaded from the database. */
119 static private final Hashtable ht_later
= new Hashtable();
121 /** A thread to handle user actions, for example an event sent from a popup menu. */
122 private final Dispatcher dispatcher
= new Dispatcher();
124 static private WindowAdapter window_listener
= new WindowAdapter() {
125 /** Unregister the closed Display. */
126 public void windowClosing(WindowEvent we
) {
127 final Object source
= we
.getSource();
128 for (Iterator it
= al_displays
.iterator(); it
.hasNext(); ) {
129 Display d
= (Display
)it
.next();
130 if (source
== d
.frame
) {
132 if (d
== front
) front
= null;
133 d
.remove(false); //calls destroy
138 /** Set the source Display as front. */
139 public void windowActivated(WindowEvent we
) {
140 // find which was it to make it be the front
141 final Object source
= we
.getSource();
142 for (final Display d
: al_displays
) {
143 if (source
== d
.frame
) {
146 ProjectToolbar
.setProjectToolbar();
147 // now, select the layer in the LayerTree
148 front
.getProject().select(front
.layer
);
149 // finally, set the virtual ImagePlus that ImageJ will see
150 d
.setTempCurrentImage();
151 // copied from ij.gui.ImageWindow, with modifications
152 if (IJ
.isMacintosh() && IJ
.getInstance()!=null) {
153 IJ
.wait(10); // may be needed for Java 1.4 on OS X
154 d
.frame
.setMenuBar(ij
.Menus
.getMenuBar());
159 // else, restore the ImageJ toolbar for non-project images
160 //if (!source.equals(IJ.getInstance())) {
161 // ProjectToolbar.setImageJToolbar();
164 /** Restore the ImageJ toolbar */
165 public void windowDeactivated(WindowEvent we
) {
166 // 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();
168 /** Call a pack() when the window is maximized to fit the canvas correctly. */
169 public void windowStateChanged(WindowEvent we
) {
170 final Object source
= we
.getSource();
171 for (final Display d
: al_displays
) {
172 if (source
!= d
.frame
) continue;
179 static private MouseListener frame_mouse_listener
= new MouseAdapter() {
180 public void mouseReleased(MouseEvent me
) {
181 Object source
= me
.getSource();
182 for (final Display d
: al_displays
) {
183 if (d
.frame
== source
) {
184 if (d
.size_adjusted
) {
186 d
.size_adjusted
= false;
187 Utils
.log2("mouse released on JFrame");
195 private int last_frame_state
= frame
.NORMAL
;
197 // THIS WHOLE SYSTEM OF LISTENERS IS BROKEN:
198 // * when zooming in, the window growths in width a few pixels.
199 // * when enlarging the window quickly, the canvas is not resized as large as it should.
200 // -- the whole problem: swing threading, which I am not handling properly. It's hard.
201 static private ComponentListener component_listener
= new ComponentAdapter() {
202 public void componentResized(ComponentEvent ce
) {
203 final Display d
= getDisplaySource(ce
);
205 d
.size_adjusted
= true; // works in combination with mouseReleased to call pack(), avoiding infinite loops.
207 int frame_state
= d
.frame
.getExtendedState();
208 if (frame_state
!= d
.last_frame_state
) { // this setup avoids infinite loops (for pack() calls componentResized as well
209 d
.last_frame_state
= frame_state
;
210 if (d
.frame
.ICONIFIED
!= frame_state
) d
.pack();
214 public void componentMoved(ComponentEvent ce
) {
215 Display d
= getDisplaySource(ce
);
216 if (null != d
) d
.updateInDatabase("position");
218 private Display
getDisplaySource(ComponentEvent ce
) {
219 final Object source
= ce
.getSource();
220 for (final Display d
: al_displays
) {
221 if (source
== d
.frame
) {
229 static private ChangeListener tabs_listener
= new ChangeListener() {
230 /** Listen to tab changes. */
231 public void stateChanged(final ChangeEvent ce
) {
232 final Object source
= ce
.getSource();
233 for (final Display d
: al_displays
) {
234 if (source
== d
.tabs
) {
235 d
.dispatcher
.exec(new Runnable() { public void run() {
236 // creating tabs fires the event!!!
237 if (null == d
.frame
|| null == d
.canvas
) return;
238 final Container tab
= (Container
)d
.tabs
.getSelectedComponent();
239 if (tab
== d
.scroll_channels
) {
240 // find active channel if any
241 for (int i
=0; i
<d
.channels
.length
; i
++) {
242 if (d
.channels
[i
].isActive()) {
243 d
.transp_slider
.setValue((int)(d
.channels
[i
].getAlpha() * 100));
250 int count = tab.getComponentCount();
251 if (0 == count || (1 == count && tab.getComponent(0).getClass().equals(JLabel.class))) {
252 */ // 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)
257 if (tab
== d
.scroll_zdispl
) {
258 label
= "Z-space objects";
259 al
= d
.layer
.getParent().getZDisplayables();
261 } else if (tab
== d
.scroll_patches
) {
263 al
= d
.layer
.getDisplayables(Patch
.class);
265 } else if (tab
== d
.scroll_labels
) {
267 al
= d
.layer
.getDisplayables(DLabel
.class);
269 } else if (tab
== d
.scroll_profiles
) {
271 al
= d
.layer
.getDisplayables(Profile
.class);
272 p
= d
.panel_profiles
;
273 } else if (tab
== d
.scroll_layers
) {
278 d
.updateTab(p
, label
, al
);
279 //Utils.updateComponent(d.tabs.getSelectedComponent());
280 //Utils.log2("updated tab: " + p + " with " + al.size() + " objects.");
283 if (null != d
.active
) {
284 // set the transp slider to the alpha value of the active Displayable if any
285 d
.transp_slider
.setValue((int)(d
.active
.getAlpha() * 100));
286 DisplayablePanel dp
= d
.ht_panels
.get(d
.active
);
287 if (null != dp
) dp
.setActive(true);
297 private final ScrollLayerListener scroller_listener
= new ScrollLayerListener();
299 private class ScrollLayerListener
implements AdjustmentListener
{
301 public void adjustmentValueChanged(final AdjustmentEvent ae
) {
302 final int index
= scroller
.getValue();
303 slt
.set(layer
.getParent().getLayer(index
));
307 private final SetLayerThread slt
= new SetLayerThread();
309 private class SetLayerThread
extends Thread
{
311 private boolean go
= true;
313 private final Lock lock
= new Lock();
316 setPriority(Thread
.NORM_PRIORITY
);
321 public final void set(final Layer layer
) {
322 synchronized (lock
) {
325 synchronized (this) {
330 // Does not use the thread, rather just sets it within the context of the calling thread (would be the same as making the caller thread wait.)
331 final void setAndWait(final Layer layer
) {
333 Display
.this.setLayer(layer
);
334 Display
.this.updateInDatabase("layer_id");
340 while (null == this.layer
) {
341 synchronized (this) {
342 try { wait(); } catch (InterruptedException ie
) {}
346 synchronized (lock
) {
351 if (!go
) return; // after nullifying layer
362 /** Creates a new Display with adjusted magnification to fit in the screen. */
363 static public void createDisplay(final Project project
, final Layer layer
) {
364 SwingUtilities
.invokeLater(new Runnable() { public void run() {
365 Display display
= new Display(project
, layer
);
366 Dimension screen
= Toolkit
.getDefaultToolkit().getScreenSize();
367 Rectangle srcRect
= new Rectangle(0, 0, (int)layer
.getLayerWidth(), (int)layer
.getLayerHeight());
368 double mag
= screen
.width
/ layer
.getLayerWidth();
369 if (mag
* layer
.getLayerHeight() > screen
.height
) mag
= screen
.height
/ layer
.getLayerHeight();
370 mag
= display
.canvas
.getLowerZoomLevel2(mag
);
371 if (mag
> 1.0) mag
= 1.0;
372 //display.getCanvas().setup(mag, srcRect); // would call pack() at the wrong time!
373 // ... so instead: manually
374 display
.getCanvas().setMagnification(mag
);
375 display
.getCanvas().setSrcRect(srcRect
.x
, srcRect
.y
, srcRect
.width
, srcRect
.height
);
376 display
.getCanvas().setDrawingSize((int)Math
.ceil(srcRect
.width
* mag
), (int)Math
.ceil(srcRect
.height
* mag
));
378 display
.updateFrameTitle(layer
);
379 ij
.gui
.GUI
.center(display
.frame
);
380 display
.frame
.pack();
384 /** A new Display from scratch, to show the given Layer. */
385 public Display(Project project
, final Layer layer
) {
388 makeGUI(layer
, null);
389 ImagePlus
.addImageListener(this);
391 this.layer
= layer
; // after, or it doesn't update properly
392 al_displays
.add(this);
396 /** For reconstruction purposes. The Display will be stored in the ht_later.*/
397 public Display(Project project
, long id
, Layer layer
, Object
[] props
) {
399 synchronized (ht_later
) {
400 Display
.ht_later
.put(this, props
);
405 /** Open a new Display centered around the given Displayable. */
406 public Display(Project project
, Layer layer
, Displayable displ
) {
410 makeGUI(layer
, null);
411 ImagePlus
.addImageListener(this);
413 this.layer
= layer
; // after set layer!
414 al_displays
.add(this);
418 /** Reconstruct a Display from an XML entry, to be opened when everything is ready. */
419 public Display(Project project
, long id
, Layer layer
, HashMap ht_attributes
) {
422 Utils
.log2("Display: need a non-null Layer for id=" + id
);
425 Rectangle srcRect
= new Rectangle(0, 0, (int)layer
.getLayerWidth(), (int)layer
.getLayerHeight());
426 double magnification
= 0.25;
427 Point p
= new Point(0, 0);
428 int c_alphas
= 0xffffffff;
429 int c_alphas_state
= 0xffffffff;
430 for (Iterator it
= ht_attributes
.entrySet().iterator(); it
.hasNext(); ) {
431 Map
.Entry entry
= (Map
.Entry
)it
.next();
432 String key
= (String
)entry
.getKey();
433 String data
= (String
)entry
.getValue();
434 if (key
.equals("srcrect_x")) { // reflection! Reflection!
435 srcRect
.x
= Integer
.parseInt(data
);
436 } else if (key
.equals("srcrect_y")) {
437 srcRect
.y
= Integer
.parseInt(data
);
438 } else if (key
.equals("srcrect_width")) {
439 srcRect
.width
= Integer
.parseInt(data
);
440 } else if (key
.equals("srcrect_height")) {
441 srcRect
.height
= Integer
.parseInt(data
);
442 } else if (key
.equals("magnification")) {
443 magnification
= Double
.parseDouble(data
);
444 } else if (key
.equals("x")) {
445 p
.x
= Integer
.parseInt(data
);
446 } else if (key
.equals("y")) {
447 p
.y
= Integer
.parseInt(data
);
448 } else if (key
.equals("c_alphas")) {
450 c_alphas
= Integer
.parseInt(data
);
451 } catch (Exception ex
) {
452 c_alphas
= 0xffffffff;
454 } else if (key
.equals("c_alphas_state")) {
456 c_alphas_state
= Integer
.parseInt(data
);
457 } catch (Exception ex
) {
459 c_alphas_state
= 0xffffffff;
461 } else if (key
.equals("scroll_step")) {
463 setScrollStep(Integer
.parseInt(data
));
464 } catch (Exception ex
) {
469 // TODO the above is insecure, in that data is not fully checked to be within bounds.
471 Object
[] props
= new Object
[]{p
, new Double(magnification
), srcRect
, new Long(layer
.getId()), new Integer(c_alphas
), new Integer(c_alphas_state
)};
472 synchronized (ht_later
) {
473 Display
.ht_later
.put(this, props
);
478 /** After reloading a project from the database, open the Displays that the project had. */
479 static public Bureaucrat
openLater() {
480 final Hashtable ht_later_local
;
481 synchronized (ht_later
) {
482 if (0 == ht_later
.size()) return null;
483 ht_later_local
= new Hashtable(ht_later
);
484 ht_later
.keySet().removeAll(ht_later_local
.keySet());
486 final Worker worker
= new Worker("Opening displays") {
490 Thread
.sleep(300); // waiting for Swing
492 for (Enumeration e
= ht_later_local
.keys(); e
.hasMoreElements(); ) {
493 final Display d
= (Display
)e
.nextElement();
494 front
= d
; // must be set before repainting any ZDisplayable!
495 Object
[] props
= (Object
[])ht_later_local
.get(d
);
496 if (ControlWindow
.isGUIEnabled()) d
.makeGUI(d
.layer
, props
);
497 d
.setLayerLater(d
.layer
, d
.layer
.get(((Long
)props
[3]).longValue())); //important to do it after makeGUI
498 if (!ControlWindow
.isGUIEnabled()) continue;
499 ImagePlus
.addImageListener(d
);
501 d
.updateFrameTitle(d
.layer
);
502 // 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
503 if (d
.canvas
.getMagnification() > 0.499) {
504 SwingUtilities
.invokeLater(new Runnable() { public void run() {
506 d
.project
.getLoader().setChanged(false);
507 Utils
.log2("A set to false");
510 d
.project
.getLoader().setChanged(false);
511 Utils
.log2("B set to false");
513 if (null != front
) front
.getProject().select(front
.layer
);
515 } catch (Throwable t
) {
522 return Bureaucrat
.createAndStart(worker
, ((Display
)ht_later_local
.keySet().iterator().next()).getProject()); // gets the project from the first Display
525 private void makeGUI(final Layer layer
, final Object
[] props
) {
529 Rectangle srcRect
= null;
532 mag
= ((Double
)props
[1]).doubleValue();
533 srcRect
= (Rectangle
)props
[2];
536 // transparency slider
537 this.transp_slider
= new JSlider(javax
.swing
.SwingConstants
.HORIZONTAL
, 0, 100, 100);
538 this.transp_slider
.setBackground(Color
.white
);
539 this.transp_slider
.setMinimumSize(new Dimension(250, 20));
540 this.transp_slider
.setMaximumSize(new Dimension(250, 20));
541 this.transp_slider
.setPreferredSize(new Dimension(250, 20));
542 TransparencySliderListener tsl
= new TransparencySliderListener();
543 this.transp_slider
.addChangeListener(tsl
);
544 this.transp_slider
.addMouseListener(tsl
);
545 for (final KeyListener kl
: this.transp_slider
.getKeyListeners()) {
546 this.transp_slider
.removeKeyListener(kl
);
549 // Tabbed pane on the left
550 this.tabs
= new JTabbedPane();
551 this.tabs
.setMinimumSize(new Dimension(250, 300));
552 this.tabs
.setBackground(Color
.white
);
553 this.tabs
.addChangeListener(tabs_listener
);
556 this.panel_patches
= makeTabPanel();
557 this.panel_patches
.add(new JLabel("No patches."));
558 this.scroll_patches
= makeScrollPane(panel_patches
);
559 this.tabs
.add("Patches", scroll_patches
);
562 this.panel_profiles
= makeTabPanel();
563 this.panel_profiles
.add(new JLabel("No profiles."));
564 this.scroll_profiles
= makeScrollPane(panel_profiles
);
565 this.tabs
.add("Profiles", scroll_profiles
);
568 this.panel_zdispl
= makeTabPanel();
569 this.panel_zdispl
.add(new JLabel("No objects."));
570 this.scroll_zdispl
= makeScrollPane(panel_zdispl
);
571 this.tabs
.add("Z space", scroll_zdispl
);
574 this.panel_channels
= makeTabPanel();
575 this.scroll_channels
= makeScrollPane(panel_channels
);
576 this.channels
= new Channel
[4];
577 this.channels
[0] = new Channel(this, Channel
.MONO
);
578 this.channels
[1] = new Channel(this, Channel
.RED
);
579 this.channels
[2] = new Channel(this, Channel
.GREEN
);
580 this.channels
[3] = new Channel(this, Channel
.BLUE
);
581 //this.panel_channels.add(this.channels[0]);
582 this.panel_channels
.add(this.channels
[1]);
583 this.panel_channels
.add(this.channels
[2]);
584 this.panel_channels
.add(this.channels
[3]);
585 this.tabs
.add("Opacity", scroll_channels
);
588 this.panel_labels
= makeTabPanel();
589 this.panel_labels
.add(new JLabel("No labels."));
590 this.scroll_labels
= makeScrollPane(panel_labels
);
591 this.tabs
.add("Labels", scroll_labels
);
594 this.panel_layers
= makeTabPanel();
595 this.scroll_layers
= makeScrollPane(panel_layers
);
596 recreateLayerPanels(layer
);
597 this.scroll_layers
.addMouseWheelListener(canvas
);
598 this.tabs
.add("Layers", scroll_layers
);
600 this.ht_tabs
= new Hashtable
<Class
,JScrollPane
>();
601 this.ht_tabs
.put(Patch
.class, scroll_patches
);
602 this.ht_tabs
.put(Profile
.class, scroll_profiles
);
603 this.ht_tabs
.put(ZDisplayable
.class, scroll_zdispl
);
604 this.ht_tabs
.put(AreaList
.class, scroll_zdispl
);
605 this.ht_tabs
.put(Pipe
.class, scroll_zdispl
);
606 this.ht_tabs
.put(Polyline
.class, scroll_zdispl
);
607 this.ht_tabs
.put(Ball
.class, scroll_zdispl
);
608 this.ht_tabs
.put(Dissector
.class, scroll_zdispl
);
609 this.ht_tabs
.put(DLabel
.class, scroll_labels
);
610 // channels not included
611 // layers not included
614 this.navigator
= new DisplayNavigator(this, layer
.getLayerWidth(), layer
.getLayerHeight());
615 // Layer scroller (to scroll slices)
616 int extent
= (int)(250.0 / layer
.getParent().size());
617 if (extent
< 10) extent
= 10;
618 this.scroller
= new JScrollBar(JScrollBar
.HORIZONTAL
);
619 updateLayerScroller(layer
);
620 this.scroller
.addAdjustmentListener(scroller_listener
);
623 // Left panel, contains the transp slider, the tabbed pane, the navigation panel and the layer scroller
624 JPanel left
= new JPanel();
625 left
.setBackground(Color
.white
);
626 BoxLayout left_layout
= new BoxLayout(left
, BoxLayout
.Y_AXIS
);
627 left
.setLayout(left_layout
);
628 left
.add(transp_slider
);
634 this.canvas
= new DisplayCanvas(this, (int)Math
.ceil(layer
.getLayerWidth()), (int)Math
.ceil(layer
.getLayerHeight()));
635 this.canvas_panel
= new JPanel();
636 GridBagLayout gb
= new GridBagLayout();
637 this.canvas_panel
.setLayout(gb
);
638 GridBagConstraints c
= new GridBagConstraints();
639 c
.fill
= GridBagConstraints
.BOTH
;
640 c
.anchor
= GridBagConstraints
.NORTHWEST
;
641 gb
.setConstraints(this.canvas_panel
, c
);
642 gb
.setConstraints(this.canvas
, c
);
644 // prevent new Displays from screweing up if input is globally disabled
645 if (!project
.isInputEnabled()) this.canvas
.setReceivesInput(false);
647 this.canvas_panel
.add(canvas
);
649 this.navigator
.addMouseWheelListener(canvas
);
651 this.transp_slider
.addKeyListener(canvas
);
653 // Split pane to contain everything
654 this.split
= new JSplitPane(JSplitPane
.HORIZONTAL_SPLIT
, left
, canvas_panel
);
655 this.split
.setOneTouchExpandable(true); // NOT present in all L&F (?)
656 this.split
.setBackground(Color
.white
);
659 gb
.setConstraints(split
.getRightComponent(), c
);
661 // JFrame to show the split pane
662 this.frame
= ControlWindow
.createJFrame(layer
.toString());
663 this.frame
.setBackground(Color
.white
);
664 this.frame
.getContentPane().setBackground(Color
.white
);
665 if (IJ
.isMacintosh() && IJ
.getInstance()!=null) {
666 IJ
.wait(10); // may be needed for Java 1.4 on OS X
667 this.frame
.setMenuBar(ij
.Menus
.getMenuBar());
669 this.frame
.addWindowListener(window_listener
);
670 this.frame
.addComponentListener(component_listener
);
671 this.frame
.getContentPane().add(split
);
672 this.frame
.addMouseListener(frame_mouse_listener
);
673 //doesn't exist//this.frame.setMinimumSize(new Dimension(270, 600));
677 canvas
.setup(mag
, srcRect
);
678 // restore visibility of each channel
679 int cs
= ((Integer
)props
[5]).intValue(); // aka c_alphas_state
680 int[] sel
= new int[4];
681 sel
[0] = ((cs
&0xff000000)>>24);
682 sel
[1] = ((cs
&0xff0000)>>16);
683 sel
[2] = ((cs
&0xff00)>>8);
685 // restore channel alphas
686 this.c_alphas
= ((Integer
)props
[4]).intValue();
687 channels
[0].setAlpha( (float)((c_alphas
&0xff000000)>>24) / 255.0f
, 0 != sel
[0]);
688 channels
[1].setAlpha( (float)((c_alphas
&0xff0000)>>16) / 255.0f
, 0 != sel
[1]);
689 channels
[2].setAlpha( (float)((c_alphas
&0xff00)>>8) / 255.0f
, 0 != sel
[2]);
690 channels
[3].setAlpha( (float) (c_alphas
&0xff) / 255.0f
, 0 != sel
[3]);
691 // restore visibility in the working c_alphas
692 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);
695 if (null != active
&& null != layer
) {
696 Rectangle r
= active
.getBoundingBox();
700 r
.height
+= r
.height
;
701 if (r
.x
< 0) r
.x
= 0;
702 if (r
.y
< 0) r
.y
= 0;
703 if (r
.width
> layer
.getLayerWidth()) r
.width
= (int)layer
.getLayerWidth();
704 if (r
.height
> layer
.getLayerHeight())r
.height
= (int)layer
.getLayerHeight();
705 double magn
= layer
.getLayerWidth() / (double)r
.width
;
706 canvas
.setup(magn
, r
);
709 // add keyListener to the whole frame
710 this.tabs
.addKeyListener(canvas
);
711 this.canvas_panel
.addKeyListener(canvas
);
712 this.frame
.addKeyListener(canvas
);
715 ij
.gui
.GUI
.center(this.frame
);
716 this.frame
.setVisible(true);
717 ProjectToolbar
.setProjectToolbar(); // doesn't get it through events
719 final Dimension screen
= Toolkit
.getDefaultToolkit().getScreenSize();
722 // fix positioning outside the screen (dual to single monitor)
723 if (p
.x
>= 0 && p
.x
< screen
.width
- 50 && p
.y
>= 0 && p
.y
<= screen
.height
- 50) this.frame
.setLocation(p
);
724 else frame
.setLocation(0, 0);
727 // fix excessive size
728 final Rectangle box
= this.frame
.getBounds();
731 int width
= box
.width
;
732 int height
= box
.height
;
733 if (box
.width
> screen
.width
) { x
= 0; width
= screen
.width
; }
734 if (box
.height
> screen
.height
) { y
= 0; height
= screen
.height
; }
735 if (x
!= box
.x
|| y
!= box
.y
) {
736 this.frame
.setLocation(x
, y
+ (0 == y ?
30 : 0)); // added insets for bad window managers
737 updateInDatabase("position");
739 if (width
!= box
.width
|| height
!= box
.height
) {
740 this.frame
.setSize(new Dimension(width
-10, height
-30)); // added insets for bad window managers
743 // try to optimize canvas dimensions and magn
744 double magn
= layer
.getLayerHeight() / screen
.height
;
745 if (magn
> 1.0) magn
= 1.0;
747 // limit magnification if appropriate
748 for (Iterator it
= layer
.getDisplayables(Patch
.class).iterator(); it
.hasNext(); ) {
749 final Patch pa
= (Patch
)it
.next();
750 final Rectangle ba
= pa
.getBoundingBox();
751 size
+= (long)(ba
.width
* ba
.height
);
753 if (size
> 10000000) canvas
.setInitialMagnification(0.25); // 10 Mb
755 this.frame
.setSize(new Dimension((int)(screen
.width
* 0.66), (int)(screen
.height
* 0.66)));
759 updateTab(panel_patches
, "Patches", layer
.getDisplayables(Patch
.class));
760 Utils
.updateComponent(tabs
); // otherwise fails in FreeBSD java 1.4.2 when reconstructing
763 // Set the calibration of the FakeImagePlus to that of the LayerSet
764 ((FakeImagePlus
)canvas
.getFakeImagePlus()).setCalibrationSuper(layer
.getParent().getCalibrationCopy());
766 updateFrameTitle(layer
);
767 // Set the FakeImagePlus as the current image
768 setTempCurrentImage();
770 // create a drag and drop listener
771 dnd
= new DNDInsertImage(this);
773 // start a repainting thread
775 canvas
.repaint(true); // repaint() is unreliable
778 // 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.
779 SwingUtilities
.invokeLater(new Runnable() {
781 tabs
.setMinimumSize(new Dimension(0, 100));
782 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
783 ControlWindow
.setLookAndFeel();
788 private JPanel
makeTabPanel() {
789 JPanel panel
= new JPanel();
790 BoxLayout layout
= new BoxLayout(panel
, BoxLayout
.Y_AXIS
);
791 panel
.setLayout(layout
);
795 private JScrollPane
makeScrollPane(Component c
) {
796 JScrollPane jsp
= new JScrollPane(c
);
797 jsp
.setBackground(Color
.white
); // no effect
798 jsp
.getViewport().setBackground(Color
.white
); // no effect
799 // adjust scrolling to use one DisplayablePanel as the minimal unit
800 jsp
.getVerticalScrollBar().setBlockIncrement(DisplayablePanel
.HEIGHT
); // clicking within the track
801 jsp
.getVerticalScrollBar().setUnitIncrement(DisplayablePanel
.HEIGHT
); // clicking on an arrow
802 jsp
.setHorizontalScrollBarPolicy(javax
.swing
.ScrollPaneConstants
.HORIZONTAL_SCROLLBAR_NEVER
);
803 jsp
.setPreferredSize(new Dimension(250, 300));
804 jsp
.setMinimumSize(new Dimension(250, 300));
808 static protected int scrollbar_width
= 0;
810 public JPanel
getCanvasPanel() {
814 public DisplayCanvas
getCanvas() {
818 public synchronized void setLayer(final Layer layer
) {
819 if (null == layer
|| layer
== this.layer
) return;
820 translateLayerColors(this.layer
, layer
);
821 if (tabs
.getSelectedComponent() == scroll_layers
) {
822 SwingUtilities
.invokeLater(new Runnable() { public void run() {
823 scrollToShow(scroll_layers
, layer_panels
.get(layer
));
826 final boolean set_zdispl
= null == Display
.this.layer
|| layer
.getParent() != Display
.this.layer
.getParent();
827 if (selection
.isTransforming()) {
828 Utils
.log("Can't browse layers while transforming.\nCANCEL the transform first with the ESCAPE key or right-click -> cancel.");
829 scroller
.setValue(Display
.this.layer
.getParent().getLayerIndex(Display
.this.layer
.getId()));
833 scroller
.setValue(layer
.getParent().getLayerIndex(layer
.getId()));
835 // update the current Layer pointer in ZDisplayable objects
836 for (Iterator it
= layer
.getParent().getZDisplayables().iterator(); it
.hasNext(); ) {
837 ((ZDisplayable
)it
.next()).setLayer(layer
); // the active layer
840 updateVisibleTab(set_zdispl
);
842 // see if a lot has to be reloaded, put the relevant ones at the end
843 project
.getLoader().prepare(layer
);
844 updateFrameTitle(layer
); // to show the new 'z'
845 // select the Layer in the LayerTree
846 project
.select(Display
.this.layer
); // does so in a separate thread
847 // update active Displayable:
849 // deselect all except ZDisplayables
850 final ArrayList
<Displayable
> sel
= selection
.getSelected();
851 final Displayable last_active
= Display
.this.active
;
853 for (final Iterator
<Displayable
> it
= sel
.iterator(); it
.hasNext(); ) {
854 final Displayable d
= it
.next();
855 if (!(d
instanceof ZDisplayable
)) {
858 if (d
== last_active
&& sel
.size() > 0) {
859 // select the last one of the remaining, if any
860 sel_next
= sel
.size()-1;
864 if (-1 != sel_next
&& sel
.size() > 0) select(sel
.get(sel_next
), true);
866 // repaint everything
867 navigator
.repaint(true);
868 canvas
.repaint(true);
870 // repaint tabs (hard as hell)
871 Utils
.updateComponent(tabs
);
872 // @#$%^! The above works half the times, so explicit repaint as well:
873 Component c
= tabs
.getSelectedComponent();
876 tabs
.setSelectedComponent(scroll_patches
);
878 Utils
.updateComponent(c
);
880 project
.getLoader().setMassiveMode(false); // resetting if it was set true
882 // update the coloring in the ProjectTree
883 project
.getProjectTree().updateUILater();
885 setTempCurrentImage();
888 static public void updateVisibleTabs() {
889 for (final Display d
: al_displays
) {
890 d
.updateVisibleTab(true);
894 /** Recreate the tab that is being shown. */
895 public void updateVisibleTab(boolean set_zdispl
) {
896 // update only the visible tab
897 switch (tabs
.getSelectedIndex()) {
900 updateTab(panel_patches
, "Patches", layer
.getDisplayables(Patch
.class));
904 updateTab(panel_profiles
, "Profiles", layer
.getDisplayables(Profile
.class));
909 updateTab(panel_zdispl
, "Z-space objects", layer
.getParent().getZDisplayables());
912 // case 3: channel opacities
915 updateTab(panel_labels
, "Labels", layer
.getDisplayables(DLabel
.class));
917 // case 5: layer panels
922 private void setLayerLater(final Layer layer
, final Displayable active
) {
923 if (null == layer
) return;
925 if (!ControlWindow
.isGUIEnabled()) return;
926 SwingUtilities
.invokeLater(new Runnable() { public void run() {
927 // empty the tabs, except channels and pipes
928 clearTab(panel_profiles
, "Profiles");
929 clearTab(panel_patches
, "Patches");
930 clearTab(panel_labels
, "Labels");
931 // distribute Displayable to the tabs. Ignore LayerSet instances.
932 if (null == ht_panels
) ht_panels
= new Hashtable
<Displayable
,DisplayablePanel
>();
933 else ht_panels
.clear();
934 for (final Displayable d
: layer
.getDisplayables()) {
935 add(d
, false, false);
937 for (final Displayable d
: layer
.getParent().getZDisplayables()) {
939 add(d
, false, false);
941 navigator
.repaint(true); // was not done when adding
942 Utils
.updateComponent(tabs
.getSelectedComponent());
950 setPriority(Thread.NORM_PRIORITY);
951 try { Thread.sleep(1000); } catch (Exception e) {}
958 /** Remove all components from the tab and add a "No [label]" label to each. */
959 private void clearTab(final Container c
, final String label
) {
961 c
.add(new JLabel("No " + label
+ "."));
963 if (tabs
.getSelectedComponent() == c
) {
964 Utils
.updateComponent(c
);
968 /** A class to listen to the transparency_slider of the DisplayablesSelectorWindow. */
969 private class TransparencySliderListener
extends MouseAdapter
implements ChangeListener
{
971 public void stateChanged(ChangeEvent ce
) {
972 //change the transparency value of the current active displayable
973 float new_value
= (float)((JSlider
)ce
.getSource()).getValue();
974 setTransparency(new_value
/ 100.0f
);
977 public void mousePressed(MouseEvent me
) {
978 JScrollPane scroll
= (JScrollPane
)tabs
.getSelectedComponent();
979 if (scroll
!= scroll_channels
&& !selection
.isEmpty()) selection
.addDataEditStep(new String
[]{"alpha"});
982 public void mouseReleased(MouseEvent me
) {
983 // update navigator window
984 navigator
.repaint(true);
985 JScrollPane scroll
= (JScrollPane
)tabs
.getSelectedComponent();
986 if (scroll
!= scroll_channels
&& !selection
.isEmpty()) selection
.addDataEditStep(new String
[]{"alpha"});
990 /** Context-sensitive: to a Displayable, or to a channel. */
991 private void setTransparency(final float value
) {
992 JScrollPane scroll
= (JScrollPane
)tabs
.getSelectedComponent();
993 if (scroll
== scroll_channels
) {
994 for (int i
=0; i
<4; i
++) {
995 if (channels
[i
].getBackground() == Color
.cyan
) {
996 channels
[i
].setAlpha(value
); // will call back and repaint the Display
1000 } else if (null != active
) {
1001 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.
1002 canvas
.invalidateVolatile();
1003 selection
.setAlpha(value
);
1008 public void setTransparencySlider(final float transp
) {
1009 if (transp
>= 0.0f
&& transp
<= 1.0f
) {
1011 transp_slider
.setValue((int)(transp
* 100));
1015 /** Mark the canvas for updating the offscreen images if the given Displayable is NOT the active. */ // Used by the Displayable.setVisible for example.
1016 static public void setUpdateGraphics(final Layer layer
, final Displayable displ
) {
1017 for (final Display d
: al_displays
) {
1018 if (layer
== d
.layer
&& null != d
.active
&& d
.active
!= displ
) {
1019 d
.canvas
.setUpdateGraphics(true);
1024 /** Flag the DisplayCanvas of Displays showing the given Layer to update their offscreen images.*/
1025 static public void setUpdateGraphics(final Layer layer
, final boolean update
) {
1026 for (final Display d
: al_displays
) {
1027 if (layer
== d
.layer
) {
1028 d
.canvas
.setUpdateGraphics(update
);
1033 /** Whether to update the offscreen images or not. */
1034 public void setUpdateGraphics(boolean b
) {
1035 canvas
.setUpdateGraphics(b
);
1038 /** Find all Display instances that contain the layer and repaint them, in the Swing GUI thread. */
1039 static public void update(final Layer layer
) {
1040 if (null == layer
) return;
1041 SwingUtilities
.invokeLater(new Runnable() { public void run() {
1042 for (final Display d
: al_displays
) {
1043 if (d
.isShowing(layer
)) {
1050 static public void update(final LayerSet set
) {
1054 /** 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. */
1055 static public void update(final LayerSet set
, final boolean update_canvas_dimensions
) {
1056 if (null == set
) return;
1057 SwingUtilities
.invokeLater(new Runnable() { public void run() {
1058 for (final Display d
: al_displays
) {
1059 if (set
.contains(d
.layer
)) {
1060 d
.updateSnapshots();
1061 if (update_canvas_dimensions
) d
.canvas
.setDimensions(set
.getLayerWidth(), set
.getLayerHeight());
1068 /** Release all resources held by this Display and close the frame. */
1069 protected void destroy() {
1071 canvas
.setReceivesInput(false);
1074 // update the coloring in the ProjectTree and LayerTree
1075 if (!project
.isBeingDestroyed()) {
1077 project
.getProjectTree().updateUILater();
1078 project
.getLayerTree().updateUILater();
1079 } catch (Exception e
) {
1080 Utils
.log2("updateUI failed at Display.destroy()");
1084 frame
.removeComponentListener(component_listener
);
1085 frame
.removeWindowListener(window_listener
);
1086 frame
.removeWindowFocusListener(window_listener
);
1087 frame
.removeWindowStateListener(window_listener
);
1088 frame
.removeKeyListener(canvas
);
1089 frame
.removeMouseListener(frame_mouse_listener
);
1090 canvas_panel
.removeKeyListener(canvas
);
1091 canvas
.removeKeyListener(canvas
);
1092 tabs
.removeChangeListener(tabs_listener
);
1093 tabs
.removeKeyListener(canvas
);
1094 ImagePlus
.removeImageListener(this);
1095 bytypelistener
= null;
1097 navigator
.destroy();
1098 scroller
.removeAdjustmentListener(scroller_listener
);
1099 frame
.setVisible(false);
1100 //no need, and throws exception//frame.dispose();
1102 if (null != selection
) selection
.clear();
1103 //Utils.log2("destroying selection");
1105 // below, need for SetLayerThread threads to quit
1107 // set a new front if any
1108 if (null == front
&& al_displays
.size() > 0) {
1109 front
= (Display
)al_displays
.get(al_displays
.size() -1);
1111 // repaint layer tree (to update the label color)
1113 project
.getLayerTree().updateUILater(); // works only after setting the front above
1114 } catch (Exception e
) {} // ignore swing sync bullshit when closing everything too fast
1115 // remove the drag and drop listener
1119 /** Find all Display instances that contain a Layer of the given project and close them without removing the Display entries from the database. */
1120 static synchronized public void close(final Project project
) {
1121 /* // concurrent modifications if more than 1 Display are being removed asynchronously
1122 for (final Display d : al_displays) {
1123 if (d.getLayer().getProject().equals(project)) {
1129 Display
[] d
= new Display
[al_displays
.size()];
1130 al_displays
.toArray(d
);
1131 for (int i
=0; i
<d
.length
; i
++) {
1132 if (d
[i
].getProject() == project
) {
1133 al_displays
.remove(d
[i
]);
1139 /** Find all Display instances that contain the layer and close them and remove the Display from the database. */
1140 static public void close(final Layer layer
) {
1141 for (Iterator it
= al_displays
.iterator(); it
.hasNext(); ) {
1142 Display d
= (Display
)it
.next();
1143 if (d
.isShowing(layer
)) {
1150 /** Find all Display instances that are showing the layer and either move to the next or previous layer, or close it if none. */
1151 static public void remove(final Layer layer
) {
1152 for (Iterator
<Display
> it
= al_displays
.iterator(); it
.hasNext(); ) {
1153 final Display d
= it
.next();
1154 if (d
.isShowing(layer
)) {
1155 Layer la
= layer
.getParent().next(layer
);
1156 if (layer
== la
|| null == la
) la
= layer
.getParent().previous(layer
);
1157 if (null == la
|| layer
== la
) {
1167 public boolean remove(boolean check
) {
1169 if (!Utils
.check("Delete the Display ?")) return false;
1171 // flush the offscreen images and close the frame
1173 removeFromDatabase();
1177 public Layer
getLayer() {
1181 public LayerSet
getLayerSet() {
1182 return layer
.getParent();
1185 public boolean isShowing(final Layer layer
) {
1186 return this.layer
== layer
;
1189 public DisplayNavigator
getNavigator() {
1193 /** Repaint both the canvas and the navigator, updating the graphics, and the title and tabs. */
1194 public void repaintAll() {
1195 if (repaint_disabled
) return;
1196 navigator
.repaint(true);
1197 canvas
.repaint(true);
1198 Utils
.updateComponent(tabs
);
1202 /** Repaint the canvas updating graphics, the navigator without updating graphics, and the title. */
1203 public void repaintAll2() {
1204 if (repaint_disabled
) return;
1205 navigator
.repaint(false);
1206 canvas
.repaint(true);
1210 static public void repaintSnapshots(final LayerSet set
) {
1211 if (repaint_disabled
) return;
1212 for (final Display d
: al_displays
) {
1213 if (d
.getLayer().getParent() == set
) {
1214 d
.navigator
.repaint(true);
1215 Utils
.updateComponent(d
.tabs
);
1219 static public void repaintSnapshots(final Layer layer
) {
1220 if (repaint_disabled
) return;
1221 for (final Display d
: al_displays
) {
1222 if (d
.getLayer() == layer
) {
1223 d
.navigator
.repaint(true);
1224 Utils
.updateComponent(d
.tabs
);
1229 public void pack() {
1230 dispatcher
.exec(new Runnable() { public void run() {
1232 Thread
.currentThread().sleep(100);
1233 SwingUtilities
.invokeAndWait(new Runnable() { public void run() {
1236 } catch (Exception e
) { IJError
.print(e
); }
1240 static public void pack(final LayerSet ls
) {
1241 for (final Display d
: al_displays
) {
1242 if (d
.layer
.getParent() == ls
) d
.pack();
1246 private void adjustCanvas() {
1247 SwingUtilities
.invokeLater(new Runnable() { public void run() {
1248 Rectangle r
= split
.getRightComponent().getBounds();
1249 canvas
.setDrawingSize(r
.width
, r
.height
, true);
1250 // fix not-on-top-left problem
1251 canvas
.setLocation(0, 0);
1252 //frame.pack(); // don't! Would go into an infinite loop
1253 canvas
.repaint(true);
1254 updateInDatabase("srcRect");
1258 /** Grab the last selected display (or create an new one if none) and show in it the layer,centered on the Displayable object. */
1259 static public void setFront(final Layer layer
, final Displayable displ
) {
1260 if (null == front
) {
1261 Display display
= new Display(layer
.getProject(), layer
); // gets set to front
1262 display
.showCentered(displ
);
1263 } else if (layer
== front
.layer
) {
1264 front
.showCentered(displ
);
1267 for (final Display d
: al_displays
) {
1268 if (d
.layer
== layer
) {
1270 d
.showCentered(displ
);
1274 // else, open new one
1275 new Display(layer
.getProject(), layer
).showCentered(displ
);
1279 /** 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. */
1280 static public void add(final Layer layer
, final Displayable displ
, final boolean activate
) {
1281 for (final Display d
: al_displays
) {
1282 if (d
.layer
== layer
) {
1284 d
.add(displ
, activate
, true);
1285 //front.frame.toFront();
1287 d
.add(displ
, false, true);
1293 static public void add(final Layer layer
, final Displayable displ
) {
1294 add(layer
, displ
, true);
1297 /** Add the ZDisplayable to all Displays that show a Layer belonging to the given LayerSet. */
1298 static public void add(final LayerSet set
, final ZDisplayable zdispl
) {
1299 for (final Display d
: al_displays
) {
1300 if (set
.contains(d
.layer
)) {
1302 zdispl
.setLayer(d
.layer
); // the active one
1303 d
.add(zdispl
, true, true); // calling add(Displayable, boolean, boolean)
1304 //front.frame.toFront();
1306 d
.add(zdispl
, false, true);
1312 static public void addAll(final Layer layer
, final Collection
<?
extends Displayable
> coll
) {
1313 for (final Display d
: al_displays
) {
1314 if (d
.layer
== layer
) {
1320 static public void addAll(final LayerSet set
, final Collection
<?
extends ZDisplayable
> coll
) {
1321 for (final Display d
: al_displays
) {
1322 if (set
.contains(d
.layer
)) {
1323 for (final ZDisplayable zd
: coll
) {
1324 if (front
== d
) zd
.setLayer(d
.layer
);
1331 private final void addAll(final Collection
<?
extends Displayable
> coll
) {
1332 // if any of the elements in the collection matches the type of the current tab, update that tab
1333 // ... it's easier to just update the front tab
1334 JScrollPane selected_tab
= (JScrollPane
) tabs
.getSelectedComponent();
1335 ArrayList al
= null;
1336 for (Map
.Entry
<Class
,JScrollPane
> e
: ht_tabs
.entrySet()) {
1337 if (e
.getValue() == selected_tab
) {
1338 final Class c
= e
.getKey();
1339 for (final Displayable d
: coll
) {
1340 if (d
.getClass() == c
) {
1342 if (ZDisplayable
.class.isAssignableFrom(c
)) al
= layer
.getParent().getZDisplayables();
1343 else al
= layer
.getDisplayables(c
);
1344 if (al
.size() > 0) { // could be empty if the class is LayerSet.class or an unknown class
1345 updateTab( (JPanel
) selected_tab
.getViewport().getView(), "", al
);
1355 navigator
.repaint(true);
1359 /** Add it to the proper panel, at the top, and set it active. */
1360 private final void add(final Displayable d
, final boolean activate
, final boolean repaint_snapshot
) {
1362 DisplayablePanel dp
= ht_panels
.get(d
);
1363 if (null != dp
) dp
.setActive(true);
1367 if (repaint_snapshot
) navigator
.repaint(true);
1370 private void addToPanel(JPanel panel
, int index
, DisplayablePanel dp
, boolean repaint
) {
1372 if (1 == panel
.getComponentCount() && panel
.getComponent(0) instanceof JLabel
) {
1375 panel
.add(dp
, index
);
1377 Utils
.updateComponent(tabs
);
1381 /** Find the displays that show the given Layer, and remove the given Displayable from the GUI. */
1382 static public void remove(final Layer layer
, final Displayable displ
) {
1383 for (final Display d
: al_displays
) {
1384 if (layer
== d
.layer
) d
.remove(displ
);
1388 private void remove(final Displayable displ
) {
1389 DisplayablePanel ob
= ht_panels
.remove(displ
);
1391 final JScrollPane jsp
= ht_tabs
.get(displ
.getClass());
1393 JPanel p
= (JPanel
)jsp
.getViewport().getView();
1394 p
.remove((Component
)ob
);
1395 Utils
.revalidateComponent(p
);
1398 if (null == active
|| !selection
.contains(displ
)) {
1399 canvas
.setUpdateGraphics(true);
1401 canvas
.invalidateVolatile(); // removing active, no need to update offscreen but yes the volatile
1402 repaint(displ
, null, 5, true, false);
1403 // from Selection.deleteAll this method is called ... but it's ok: same thread, no locking problems.
1404 selection
.remove(displ
);
1407 static public void remove(final ZDisplayable zdispl
) {
1408 for (final Display d
: al_displays
) {
1409 if (zdispl
.getLayerSet() == d
.layer
.getParent()) {
1410 d
.remove((Displayable
)zdispl
);
1415 static public void repaint(final Layer layer
, final Displayable displ
, final int extra
) {
1416 repaint(layer
, displ
, displ
.getBoundingBox(), extra
);
1419 static public void repaint(final Layer layer
, final Displayable displ
, final Rectangle r
, final int extra
) {
1420 repaint(layer
, displ
, r
, extra
, true);
1423 /** Find the displays that show the given Layer, and repaint the given Displayable. */
1424 static public void repaint(final Layer layer
, final Displayable displ
, final Rectangle r
, final int extra
, final boolean repaint_navigator
) {
1425 if (repaint_disabled
) return;
1426 for (final Display d
: al_displays
) {
1427 if (layer
== d
.layer
) {
1428 d
.repaint(displ
, r
, extra
, repaint_navigator
, false);
1433 static public void repaint(final Displayable d
) {
1434 if (d
instanceof ZDisplayable
) repaint(d
.getLayerSet(), d
, d
.getBoundingBox(null), 5, true);
1435 repaint(d
.getLayer(), d
, d
.getBoundingBox(null), 5, true);
1438 /** Repaint as much as the bounding box around the given Displayable, or the r if not null. */
1439 private void repaint(final Displayable displ
, final Rectangle r
, final int extra
, final boolean repaint_navigator
, final boolean update_graphics
) {
1440 if (repaint_disabled
|| null == displ
) return;
1441 if (update_graphics
|| displ
.getClass() == Patch
.class || displ
!= active
) {
1442 canvas
.setUpdateGraphics(true);
1444 if (null != r
) canvas
.repaint(r
, extra
);
1445 else canvas
.repaint(displ
, extra
);
1446 if (repaint_navigator
) {
1447 DisplayablePanel dp
= ht_panels
.get(displ
);
1448 if (null != dp
) dp
.repaint(); // is null when creating it, or after deleting it
1449 navigator
.repaint(true); // everything
1453 /** 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. */
1454 static public void repaintSnapshot(final Displayable displ
) {
1455 for (final Display d
: al_displays
) {
1456 if (d
.layer
.contains(displ
)) {
1457 if (!d
.navigator
.isPainted(displ
)) {
1458 DisplayablePanel dp
= d
.ht_panels
.get(displ
);
1459 if (null != dp
) dp
.repaint(); // is null when creating it, or after deleting it
1460 d
.navigator
.repaint(displ
);
1466 /** Repaint the given Rectangle in all Displays showing the layer, updating the offscreen image if any. */
1467 static public void repaint(final Layer layer
, final Rectangle r
, final int extra
) {
1468 repaint(layer
, extra
, r
, true, true);
1471 static public void repaint(final Layer layer
, final int extra
, final Rectangle r
, final boolean update_navigator
) {
1472 repaint(layer
, extra
, r
, update_navigator
, true);
1475 static public void repaint(final Layer layer
, final int extra
, final Rectangle r
, final boolean update_navigator
, final boolean update_graphics
) {
1476 if (repaint_disabled
) return;
1477 for (final Display d
: al_displays
) {
1478 if (layer
== d
.layer
) {
1479 d
.canvas
.setUpdateGraphics(update_graphics
);
1480 d
.canvas
.repaint(r
, extra
);
1481 if (update_navigator
) {
1482 d
.navigator
.repaint(true);
1483 Utils
.updateComponent(d
.tabs
.getSelectedComponent());
1490 /** Repaint the given Rectangle in all Displays showing the layer, optionally updating the offscreen image (if any). */
1491 static public void repaint(final Layer layer
, final Rectangle r
, final int extra
, final boolean update_graphics
) {
1492 if (repaint_disabled
) return;
1493 for (final Display d
: al_displays
) {
1494 if (layer
== d
.layer
) {
1495 d
.canvas
.setUpdateGraphics(update_graphics
);
1496 d
.canvas
.repaint(r
, extra
);
1497 d
.navigator
.repaint(update_graphics
);
1498 if (update_graphics
) Utils
.updateComponent(d
.tabs
.getSelectedComponent());
1503 /** Repaint the DisplayablePanel (and DisplayNavigator) only for the given Displayable, in all Displays showing the given Layer. */
1504 static public void repaint(final Layer layer
, final Displayable displ
) {
1505 if (repaint_disabled
) return;
1506 for (final Display d
: al_displays
) {
1507 if (layer
== d
.layer
) {
1508 DisplayablePanel dp
= d
.ht_panels
.get(displ
);
1509 if (null != dp
) dp
.repaint();
1510 d
.navigator
.repaint(true);
1515 static public void repaint(LayerSet set
, Displayable displ
, int extra
) {
1516 repaint(set
, displ
, null, extra
);
1519 static public void repaint(LayerSet set
, Displayable displ
, Rectangle r
, int extra
) {
1520 repaint(set
, displ
, r
, extra
, true);
1523 /** Repaint the Displayable in every Display that shows a Layer belonging to the given LayerSet. */
1524 static public void repaint(final LayerSet set
, final Displayable displ
, final Rectangle r
, final int extra
, final boolean repaint_navigator
) {
1525 if (repaint_disabled
) return;
1526 for (final Display d
: al_displays
) {
1527 if (set
.contains(d
.layer
)) {
1528 if (repaint_navigator
) {
1529 if (null != displ
) {
1530 DisplayablePanel dp
= d
.ht_panels
.get(displ
);
1531 if (null != dp
) dp
.repaint();
1533 d
.navigator
.repaint(true);
1535 if (null == displ
|| displ
!= d
.active
) d
.setUpdateGraphics(true); // safeguard
1536 // paint the given box or the actual Displayable's box
1537 if (null != r
) d
.canvas
.repaint(r
, extra
);
1538 else d
.canvas
.repaint(displ
, extra
);
1543 /** Repaint the entire LayerSet, in all Displays showing a Layer of it.*/
1544 static public void repaint(final LayerSet set
) {
1545 if (repaint_disabled
) return;
1546 for (final Display d
: al_displays
) {
1547 if (set
.contains(d
.layer
)) {
1548 d
.navigator
.repaint(true);
1549 d
.canvas
.repaint(true);
1553 /** Repaint the given box in the LayerSet, in all Displays showing a Layer of it.*/
1554 static public void repaint(final LayerSet set
, final Rectangle box
) {
1555 if (repaint_disabled
) return;
1556 for (final Display d
: al_displays
) {
1557 if (set
.contains(d
.layer
)) {
1558 d
.navigator
.repaint(box
);
1559 d
.canvas
.repaint(box
, 0, true);
1563 /** Repaint the entire Layer, in all Displays showing it, including the tabs.*/
1564 static public void repaint(final Layer layer
) { // TODO this method overlaps with update(layer)
1565 if (repaint_disabled
) return;
1566 for (final Display d
: al_displays
) {
1567 if (layer
== d
.layer
) {
1568 d
.navigator
.repaint(true);
1569 d
.canvas
.repaint(true);
1574 /** Call repaint on all open Displays. */
1575 static public void repaint() {
1576 if (repaint_disabled
) {
1577 Utils
.logAll("Can't repaint -- repainting is disabled!");
1580 for (final Display d
: al_displays
) {
1581 d
.navigator
.repaint(true);
1582 d
.canvas
.repaint(true);
1586 static private boolean repaint_disabled
= false;
1588 /** Set a flag to enable/disable repainting of all Display instances. */
1589 static protected void setRepaint(boolean b
) {
1590 repaint_disabled
= !b
;
1593 public Rectangle
getBounds() {
1594 return frame
.getBounds();
1597 public Point
getLocation() {
1598 return frame
.getLocation();
1601 public JFrame
getFrame() {
1605 public void setLocation(Point p
) {
1606 this.frame
.setLocation(p
);
1609 public Displayable
getActive() {
1610 return active
; //TODO this should return selection.active !!
1613 public void select(Displayable d
) {
1617 /** Select/deselect accordingly to the current state and the shift key. */
1618 public void select(final Displayable d
, final boolean shift_down
) {
1619 if (null != active
&& active
!= d
&& active
.getClass() != Patch
.class) {
1620 // active is being deselected, so link underlying patches
1621 active
.linkPatches();
1624 //Utils.log2("Display.select: clearing selection");
1625 canvas
.setUpdateGraphics(true);
1630 //Utils.log2("Display.select: single selection");
1635 } else if (selection
.contains(d
)) {
1637 selection
.remove(d
);
1638 //Utils.log2("Display.select: removing from a selection");
1640 //Utils.log2("Display.select: activing within a selection");
1641 selection
.setActive(d
);
1644 //Utils.log2("Display.select: adding to an existing selection");
1647 // update the image shown to ImageJ
1648 // NO longer necessary, always he same FakeImagePlus // setTempCurrentImage();
1651 protected void choose(int screen_x_p
, int screen_y_p
, int x_p
, int y_p
, final Class c
) {
1652 choose(screen_x_p
, screen_y_p
, x_p
, y_p
, false, c
);
1654 protected void choose(int screen_x_p
, int screen_y_p
, int x_p
, int y_p
) {
1655 choose(screen_x_p
, screen_y_p
, x_p
, y_p
, false, null);
1658 /** 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. */
1659 protected void choose(int screen_x_p
, int screen_y_p
, int x_p
, int y_p
, boolean shift_down
, Class c
) {
1660 //Utils.log("Display.choose: x,y " + x_p + "," + y_p);
1661 final ArrayList
<Displayable
> al
= new ArrayList
<Displayable
>(layer
.find(x_p
, y_p
, true));
1662 al
.addAll(layer
.getParent().findZDisplayables(layer
, x_p
, y_p
, true)); // only visible ones
1664 Displayable act
= this.active
;
1666 canvas
.setUpdateGraphics(true);
1667 //Utils.log("choose: set active to null");
1668 // fixing lack of repainting for unknown reasons, of the active one TODO this is a temporary solution
1669 if (null != act
) Display
.repaint(layer
, act
, 5);
1670 } else if (1 == al
.size()) {
1671 Displayable d
= (Displayable
)al
.get(0);
1672 if (null != c
&& d
.getClass() != c
) {
1676 select(d
, shift_down
);
1677 //Utils.log("choose 1: set active to " + active);
1679 if (al
.contains(active
) && !shift_down
) {
1683 // check if at least one of them is of class c
1684 // if only one is of class c, set as selected
1686 for (Iterator it
= al
.iterator(); it
.hasNext(); ) {
1687 Object ob
= it
.next();
1688 if (ob
.getClass() != c
) it
.remove();
1690 if (0 == al
.size()) {
1695 if (1 == al
.size()) {
1696 select((Displayable
)al
.get(0), shift_down
);
1699 // else, choose among the many
1701 choose(screen_x_p
, screen_y_p
, al
, shift_down
, x_p
, y_p
);
1703 //Utils.log("choose many: set active to " + active);
1707 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
) {
1708 // show a popup on the canvas to choose
1711 final Object lock
= new Object();
1712 final DisplayableChooser d_chooser
= new DisplayableChooser(al
, lock
);
1713 final JPopupMenu pop
= new JPopupMenu("Select:");
1714 final Iterator itu
= al
.iterator();
1715 while (itu
.hasNext()) {
1716 Displayable d
= (Displayable
)itu
.next();
1717 JMenuItem menu_item
= new JMenuItem(d
.toString());
1718 menu_item
.addActionListener(d_chooser
);
1724 pop
.show(canvas
, screen_x_p
, screen_y_p
);
1728 //now wait until selecting something
1729 synchronized(lock
) {
1733 } catch (InterruptedException ie
) {}
1734 } while (d_chooser
.isWaiting() && pop
.isShowing());
1737 //grab the chosen Displayable object
1738 Displayable d
= d_chooser
.getChosen();
1739 //Utils.log("Chosen: " + d.toString());
1740 if (null == d
) { Utils
.log2("Display.choose: returning a null!"); }
1741 select(d
, shift_down
);
1742 pop
.setVisible(false);
1744 // fix selection bug: never receives mouseReleased event when the popup shows
1745 selection
.mouseReleased(null, x_p
, y_p
, x_p
, y_p
, x_p
, y_p
);
1750 /** 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. */
1751 protected void setActive(final Displayable displ
) {
1752 final Displayable prev_active
= this.active
;
1753 this.active
= displ
;
1754 SwingUtilities
.invokeLater(new Runnable() { public void run() {
1756 // renew current image if necessary
1757 if (null != displ
&& displ
== prev_active
) {
1758 // make sure the proper tab is selected.
1762 // deactivate previously active
1763 if (null != prev_active
) {
1764 final DisplayablePanel ob
= ht_panels
.get(prev_active
);
1765 if (null != ob
) ob
.setActive(false);
1766 // erase "decorations" of the previously active
1767 canvas
.repaint(selection
.getBox(), 4);
1769 // activate the new active
1770 if (null != displ
) {
1771 final DisplayablePanel ob
= ht_panels
.get(displ
);
1772 if (null != ob
) ob
.setActive(true);
1773 updateInDatabase("active_displayable_id");
1774 if (displ
.getClass() != Patch
.class) project
.select(displ
); // select the node in the corresponding tree, if any.
1775 // select the proper tab, and scroll to visible
1777 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
1778 repaint(displ
, null, 5, false, update_graphics
); // to show the border, and to repaint out of the background image
1779 transp_slider
.setValue((int)(displ
.getAlpha() * 100));
1781 //ensure decorations are removed from the panels, for Displayables in a selection besides the active one
1782 Utils
.updateComponent(tabs
.getSelectedComponent());
1787 /** If the other paints under the base. */
1788 public boolean paintsBelow(Displayable base
, Displayable other
) {
1789 boolean zd_base
= base
instanceof ZDisplayable
;
1790 boolean zd_other
= other
instanceof ZDisplayable
;
1792 if (base
instanceof DLabel
) return true; // zd paints under label
1793 if (!zd_base
) return false; // any zd paints over a mere displ if not a label
1795 // both zd, compare indices
1796 ArrayList
<ZDisplayable
> al
= other
.getLayerSet().getZDisplayables();
1797 return al
.indexOf(base
) > al
.indexOf(other
);
1801 // both displ, compare indices
1802 ArrayList
<Displayable
> al
= other
.getLayer().getDisplayables();
1803 return al
.indexOf(base
) > al
.indexOf(other
);
1805 // base is zd, other is d
1806 if (other
instanceof DLabel
) return false;
1812 /** 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. */
1813 private void selectTab(final Displayable displ
) {
1814 Method method
= null;
1816 if (!(displ
instanceof LayerSet
)) {
1817 method
= Display
.class.getDeclaredMethod("selectTab", new Class
[]{displ
.getClass()});
1819 } catch (Exception e
) {
1822 if (null != method
) {
1823 final Method me
= method
;
1824 dispatcher
.exec(new Runnable() { public void run() {
1826 me
.setAccessible(true);
1827 me
.invoke(Display
.this, new Object
[]{displ
});
1828 } catch (Exception e
) { IJError
.print(e
); }
1833 private void selectTab(Patch patch
) {
1834 tabs
.setSelectedComponent(scroll_patches
);
1835 scrollToShow(scroll_patches
, ht_panels
.get(patch
));
1838 private void selectTab(Profile profile
) {
1839 tabs
.setSelectedComponent(scroll_profiles
);
1840 scrollToShow(scroll_profiles
, ht_panels
.get(profile
));
1843 private void selectTab(DLabel label
) {
1844 tabs
.setSelectedComponent(scroll_labels
);
1845 scrollToShow(scroll_labels
, ht_panels
.get(label
));
1848 private void selectTab(ZDisplayable zd
) {
1849 tabs
.setSelectedComponent(scroll_zdispl
);
1850 scrollToShow(scroll_zdispl
, ht_panels
.get(zd
));
1853 private void selectTab(Pipe d
) { selectTab((ZDisplayable
)d
); }
1854 private void selectTab(Polyline d
) { selectTab((ZDisplayable
)d
); }
1855 private void selectTab(AreaList d
) { selectTab((ZDisplayable
)d
); }
1856 private void selectTab(Ball d
) { selectTab((ZDisplayable
)d
); }
1857 private void selectTab(Dissector d
) { selectTab((ZDisplayable
)d
); }
1859 /** 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). */
1860 private void updateTab(final JPanel tab
, final String label
, final ArrayList al
) {
1861 dispatcher
.exec(new Runnable() { public void run() {
1863 if (0 == al
.size()) {
1865 tab
.add(new JLabel("No " + label
+ "."));
1867 Component
[] comp
= tab
.getComponents();
1869 if (1 == comp
.length
&& comp
[0].getClass() == JLabel
.class) {
1873 // In reverse order:
1874 for (ListIterator it
= al
.listIterator(al
.size()); it
.hasPrevious(); ) {
1875 Displayable d
= (Displayable
)it
.previous();
1876 DisplayablePanel dp
= null;
1877 if (next
< comp
.length
) {
1878 dp
= (DisplayablePanel
)comp
[next
++]; // recycling panels
1881 dp
= new DisplayablePanel(Display
.this, d
);
1884 ht_panels
.put(d
, dp
);
1886 if (next
< comp
.length
) {
1887 // remove from the end, to avoid potential repaints of other panels
1888 for (int i
=comp
.length
-1; i
>=next
; i
--) {
1893 if (null != Display
.this.active
) scrollToShow(Display
.this.active
);
1894 } catch (Throwable e
) { IJError
.print(e
); }
1896 dispatcher
.execSwing(new Runnable() { public void run() {
1897 Component c
= tabs
.getSelectedComponent();
1904 static public void setActive(final Object event
, final Displayable displ
) {
1905 if (!(event
instanceof InputEvent
)) return;
1906 // find which Display
1907 for (final Display d
: al_displays
) {
1908 if (d
.isOrigin((InputEvent
)event
)) {
1915 /** Find out whether this Display is Transforming its active Displayable. */
1916 public boolean isTransforming() {
1917 return canvas
.isTransforming();
1920 /** Find whether any Display is transforming the given Displayable. */
1921 static public boolean isTransforming(final Displayable displ
) {
1922 for (final Display d
: al_displays
) {
1923 if (null != d
.active
&& d
.active
== displ
&& d
.canvas
.isTransforming()) return true;
1928 static public boolean isAligning(final LayerSet set
) {
1929 for (final Display d
: al_displays
) {
1930 if (d
.layer
.getParent() == set
&& set
.isAligning()) {
1937 /** Set the front Display to transform the Displayable only if no other canvas is transforming it. */
1938 static public void setTransforming(final Displayable displ
) {
1939 if (null == front
) return;
1940 if (front
.active
!= displ
) return;
1941 for (final Display d
: al_displays
) {
1942 if (d
.active
== displ
) {
1943 if (d
.canvas
.isTransforming()) {
1944 Utils
.showMessage("Already transforming " + displ
.getTitle());
1949 front
.canvas
.setTransforming(true);
1952 /** Check whether the source of the event is located in this instance.*/
1953 private boolean isOrigin(InputEvent event
) {
1954 Object source
= event
.getSource();
1955 // find it ... check the canvas for now TODO
1956 if (canvas
== source
) {
1962 /** Get the layer of the front Display, or null if none.*/
1963 static public Layer
getFrontLayer() {
1964 if (null == front
) return null;
1968 /** Get the layer of an open Display of the given Project, or null if none.*/
1969 static public Layer
getFrontLayer(final Project project
) {
1970 if (null == front
) return null;
1971 if (front
.project
== project
) return front
.layer
;
1972 // else, find an open Display for the given Project, if any
1973 for (final Display d
: al_displays
) {
1974 if (d
.project
== project
) {
1979 return null; // none found
1982 static public Display
getFront(final Project project
) {
1983 if (null == front
) return null;
1984 if (front
.project
== project
) return front
;
1985 for (final Display d
: al_displays
) {
1986 if (d
.project
== project
) {
1994 public boolean isReadOnly() {
1995 // TEMPORARY: in the future one will be able show displays as read-only to other people, remotely
1999 static public void showPopup(Component c
, int x
, int y
) {
2000 if (null != front
) front
.getPopupMenu().show(c
, x
, y
);
2003 /** Return a context-sensitive popup menu. */
2004 public JPopupMenu
getPopupMenu() { // called from canvas
2005 // get the job canceling dialog
2006 if (!canvas
.isInputEnabled()) {
2007 return project
.getLoader().getJobsPopup(this);
2011 this.popup
= new JPopupMenu();
2012 JMenuItem item
= null;
2015 if (ProjectToolbar
.ALIGN
== Toolbar
.getToolId()) {
2016 boolean aligning
= layer
.getParent().isAligning();
2017 item
= new JMenuItem("Cancel alignment"); item
.addActionListener(this); popup
.add(item
);
2018 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_ESCAPE
, 0, true));
2019 if (!aligning
) item
.setEnabled(false);
2020 item
= new JMenuItem("Align with landmarks"); item
.addActionListener(this); popup
.add(item
);
2021 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_ENTER
, 0, true));
2022 if (!aligning
) item
.setEnabled(false);
2023 item
= new JMenuItem("Align and register"); item
.addActionListener(this); popup
.add(item
);
2024 if (!aligning
) item
.setEnabled(false);
2025 item
= new JMenuItem("Align using profiles"); item
.addActionListener(this); popup
.add(item
);
2026 if (!aligning
|| selection
.isEmpty() || !selection
.contains(Profile
.class)) item
.setEnabled(false);
2027 item
= new JMenuItem("Align stack slices"); item
.addActionListener(this); popup
.add(item
);
2028 if (selection
.isEmpty() || ! (getActive().getClass() == Patch
.class && ((Patch
)getActive()).isStack())) item
.setEnabled(false);
2029 item
= new JMenuItem("Align layers"); item
.addActionListener(this); popup
.add(item
);
2030 if (1 == layer
.getParent().size()) item
.setEnabled(false);
2031 item
= new JMenuItem("Align multi-layer mosaic"); item
.addActionListener(this); popup
.add(item
);
2032 if (1 == layer
.getParent().size()) item
.setEnabled(false);
2037 if (null != active
) {
2038 if (!canvas
.isTransforming()) {
2039 if (active
instanceof Profile
) {
2040 item
= new JMenuItem("Duplicate, link and send to next layer"); item
.addActionListener(this); popup
.add(item
);
2041 Layer nl
= layer
.getParent().next(layer
);
2042 if (nl
== layer
) item
.setEnabled(false);
2043 item
= new JMenuItem("Duplicate, link and send to previous layer"); item
.addActionListener(this); popup
.add(item
);
2044 nl
= layer
.getParent().previous(layer
);
2045 if (nl
== layer
) item
.setEnabled(false);
2047 menu
= new JMenu("Duplicate, link and send to");
2048 ArrayList al
= layer
.getParent().getLayers();
2049 final Iterator it
= al
.iterator();
2051 while (it
.hasNext()) {
2052 Layer la
= (Layer
)it
.next();
2053 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
2054 if (la
== this.layer
) item
.setEnabled(false);
2058 item
= new JMenuItem("Duplicate, link and send to..."); item
.addActionListener(this); popup
.add(item
);
2060 popup
.addSeparator();
2062 item
= new JMenuItem("Unlink from images"); item
.addActionListener(this); popup
.add(item
);
2063 if (!active
.isLinked()) item
.setEnabled(false); // isLinked() checks if it's linked to a Patch in its own layer
2064 item
= new JMenuItem("Show in 3D"); item
.addActionListener(this); popup
.add(item
);
2065 popup
.addSeparator();
2066 } else if (active
instanceof Patch
) {
2067 item
= new JMenuItem("Unlink from images"); item
.addActionListener(this); popup
.add(item
);
2068 if (!active
.isLinked(Patch
.class)) item
.setEnabled(false);
2069 if (((Patch
)active
).isStack()) {
2070 item
= new JMenuItem("Unlink slices"); item
.addActionListener(this); popup
.add(item
);
2072 int n_sel_patches
= selection
.getSelected(Patch
.class).size();
2073 if (1 == n_sel_patches
) {
2074 item
= new JMenuItem("Snap"); item
.addActionListener(this); popup
.add(item
);
2075 } else if (n_sel_patches
> 1) {
2076 item
= new JMenuItem("Montage"); item
.addActionListener(this); popup
.add(item
);
2077 item
= new JMenuItem("Lens correction"); item
.addActionListener(this); popup
.add(item
);
2078 item
= new JMenuItem("Blend"); item
.addActionListener(this); popup
.add(item
);
2080 item
= new JMenuItem("Remove alpha mask"); item
.addActionListener(this); popup
.add(item
);
2081 if ( ! ((Patch
)active
).hasAlphaMask()) item
.setEnabled(false);
2082 item
= new JMenuItem("Link images..."); item
.addActionListener(this); popup
.add(item
);
2083 item
= new JMenuItem("View volume"); item
.addActionListener(this); popup
.add(item
);
2084 HashSet hs
= active
.getLinked(Patch
.class);
2085 if (null == hs
|| 0 == hs
.size()) item
.setEnabled(false);
2086 item
= new JMenuItem("View orthoslices"); item
.addActionListener(this); popup
.add(item
);
2087 if (null == hs
|| 0 == hs
.size()) item
.setEnabled(false); // if no Patch instances among the directly linked, then it's not a stack
2088 popup
.addSeparator();
2090 item
= new JMenuItem("Unlink"); item
.addActionListener(this); popup
.add(item
);
2091 item
= new JMenuItem("Show in 3D"); item
.addActionListener(this); popup
.add(item
);
2092 popup
.addSeparator();
2094 if (active
instanceof AreaList
) {
2095 item
= new JMenuItem("Merge"); item
.addActionListener(this); popup
.add(item
);
2096 ArrayList al
= selection
.getSelected();
2098 for (Iterator it
= al
.iterator(); it
.hasNext(); ) {
2099 if (it
.next().getClass() == AreaList
.class) n
++;
2101 if (n
< 2) item
.setEnabled(false);
2102 } else if (active
instanceof Pipe
) {
2103 item
= new JMenuItem("Identify..."); item
.addActionListener(this); popup
.add(item
);
2104 item
= new JMenuItem("Identify with axes..."); item
.addActionListener(this); popup
.add(item
);
2107 if (canvas
.isTransforming()) {
2108 item
= new JMenuItem("Apply transform"); item
.addActionListener(this); popup
.add(item
);
2109 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
2110 item
= new JMenuItem("Apply transform propagating to last layer"); item
.addActionListener(this); popup
.add(item
);
2111 if (layer
.getParent().indexOf(layer
) == layer
.getParent().size() -1) item
.setEnabled(false);
2112 item
= new JMenuItem("Apply transform propagating to first layer"); item
.addActionListener(this); popup
.add(item
);
2113 if (0 == layer
.getParent().indexOf(layer
)) item
.setEnabled(false);
2115 item
= new JMenuItem("Transform"); item
.addActionListener(this); popup
.add(item
);
2116 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_T
, 0, true));
2118 item
= new JMenuItem("Cancel transform"); item
.addActionListener(this); popup
.add(item
);
2119 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_ESCAPE
, 0, true));
2120 if (!canvas
.isTransforming()) item
.setEnabled(false);
2121 if (canvas
.isTransforming()) {
2122 item
= new JMenuItem("Specify transform..."); item
.addActionListener(this); popup
.add(item
);
2125 if (!canvas
.isTransforming()) {
2126 item
= new JMenuItem("Duplicate"); item
.addActionListener(this); popup
.add(item
);
2127 item
= new JMenuItem("Color..."); item
.addActionListener(this); popup
.add(item
);
2128 if (active
instanceof LayerSet
) item
.setEnabled(false);
2129 if (active
.isLocked()) {
2130 item
= new JMenuItem("Unlock"); item
.addActionListener(this); popup
.add(item
);
2132 item
= new JMenuItem("Lock"); item
.addActionListener(this); popup
.add(item
);
2134 menu
= new JMenu("Move");
2135 popup
.addSeparator();
2136 LayerSet ls
= layer
.getParent();
2137 item
= new JMenuItem("Move to top"); item
.addActionListener(this); menu
.add(item
);
2138 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.
2139 if (ls
.isTop(active
)) item
.setEnabled(false);
2140 item
= new JMenuItem("Move up"); item
.addActionListener(this); menu
.add(item
);
2141 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_PAGE_UP
, 0, true));
2142 if (ls
.isTop(active
)) item
.setEnabled(false);
2143 item
= new JMenuItem("Move down"); item
.addActionListener(this); menu
.add(item
);
2144 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_PAGE_DOWN
, 0, true));
2145 if (ls
.isBottom(active
)) item
.setEnabled(false);
2146 item
= new JMenuItem("Move to bottom"); item
.addActionListener(this); menu
.add(item
);
2147 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_END
, 0, true));
2148 if (ls
.isBottom(active
)) item
.setEnabled(false);
2151 popup
.addSeparator();
2152 item
= new JMenuItem("Delete..."); item
.addActionListener(this); popup
.add(item
);
2154 if (active
instanceof Patch
) {
2155 if (!active
.isOnlyLinkedTo(Patch
.class)) {
2156 item
.setEnabled(false);
2158 } else if (!(active
instanceof DLabel
)) { // can't delete elements from the trees (Profile, Pipe, LayerSet)
2159 item
.setEnabled(false);
2161 } catch (Exception e
) { IJError
.print(e
); item
.setEnabled(false); }
2163 if (active
instanceof Patch
) {
2164 item
= new JMenuItem("Revert"); item
.addActionListener(this); popup
.add(item
);
2165 popup
.addSeparator();
2167 item
= new JMenuItem("Properties..."); item
.addActionListener(this); popup
.add(item
);
2168 item
= new JMenuItem("Show centered"); item
.addActionListener(this); popup
.add(item
);
2170 popup
.addSeparator();
2172 if (! (active
instanceof ZDisplayable
)) {
2173 ArrayList al_layers
= layer
.getParent().getLayers();
2174 int i_layer
= al_layers
.indexOf(layer
);
2175 int n_layers
= al_layers
.size();
2176 item
= new JMenuItem("Send to previous layer"); item
.addActionListener(this); popup
.add(item
);
2177 if (1 == n_layers
|| 0 == i_layer
|| active
.isLinked()) item
.setEnabled(false);
2178 // 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
2179 else if (active
instanceof Profile
&& !active
.canSendTo(layer
.getParent().previous(layer
))) item
.setEnabled(false);
2180 item
= new JMenuItem("Send to next layer"); item
.addActionListener(this); popup
.add(item
);
2181 if (1 == n_layers
|| n_layers
-1 == i_layer
|| active
.isLinked()) item
.setEnabled(false);
2182 else if (active
instanceof Profile
&& !active
.canSendTo(layer
.getParent().next(layer
))) item
.setEnabled(false);
2185 menu
= new JMenu("Send linked group to...");
2186 if (active
.hasLinkedGroupWithinLayer(this.layer
)) {
2188 for (final Layer la
: ls
.getLayers()) {
2189 String layer_title
= i
+ ": " + la
.getTitle();
2190 if (-1 == layer_title
.indexOf(' ')) layer_title
+= " ";
2191 item
= new JMenuItem(layer_title
); item
.addActionListener(this); menu
.add(item
);
2192 if (la
== this.layer
) item
.setEnabled(false);
2197 menu
.setEnabled(false);
2198 //Utils.log("Active's linked group not within layer.");
2201 popup
.addSeparator();
2206 if (!canvas
.isTransforming()) {
2208 item
= new JMenuItem("Undo");item
.addActionListener(this); popup
.add(item
);
2209 if (!layer
.getParent().canUndo() || canvas
.isTransforming()) item
.setEnabled(false);
2210 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_Z
, Utils
.getControlModifier(), true));
2211 item
= new JMenuItem("Redo");item
.addActionListener(this); popup
.add(item
);
2212 if (!layer
.getParent().canRedo() || canvas
.isTransforming()) item
.setEnabled(false);
2213 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_Z
, Event
.SHIFT_MASK
| Event
.CTRL_MASK
, true));
2214 popup
.addSeparator();
2216 // Would get so much simpler with a clojure macro ...
2219 menu
= new JMenu("Hide/Unhide");
2220 item
= new JMenuItem("Hide deselected"); item
.addActionListener(this); menu
.add(item
); item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_H
, Event
.SHIFT_MASK
, true));
2221 boolean none
= 0 == selection
.getNSelected();
2222 if (none
) item
.setEnabled(false);
2223 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));
2224 if (none
) item
.setEnabled(false);
2225 item
= new JMenuItem("Hide selected"); item
.addActionListener(this); menu
.add(item
); item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_H
, 0, true));
2226 if (none
) item
.setEnabled(false);
2227 none
= ! layer
.getParent().containsDisplayable(DLabel
.class);
2228 item
= new JMenuItem("Hide all labels"); item
.addActionListener(this); menu
.add(item
);
2229 if (none
) item
.setEnabled(false);
2230 item
= new JMenuItem("Unhide all labels"); item
.addActionListener(this); menu
.add(item
);
2231 if (none
) item
.setEnabled(false);
2232 none
= ! layer
.getParent().contains(AreaList
.class);
2233 item
= new JMenuItem("Hide all arealists"); item
.addActionListener(this); menu
.add(item
);
2234 if (none
) item
.setEnabled(false);
2235 item
= new JMenuItem("Unhide all arealists"); item
.addActionListener(this); menu
.add(item
);
2236 if (none
) item
.setEnabled(false);
2237 none
= ! layer
.contains(Profile
.class);
2238 item
= new JMenuItem("Hide all profiles"); item
.addActionListener(this); menu
.add(item
);
2239 if (none
) item
.setEnabled(false);
2240 item
= new JMenuItem("Unhide all profiles"); item
.addActionListener(this); menu
.add(item
);
2241 if (none
) item
.setEnabled(false);
2242 none
= ! layer
.getParent().contains(Pipe
.class);
2243 item
= new JMenuItem("Hide all pipes"); item
.addActionListener(this); menu
.add(item
);
2244 if (none
) item
.setEnabled(false);
2245 item
= new JMenuItem("Unhide all pipes"); item
.addActionListener(this); menu
.add(item
);
2246 if (none
) item
.setEnabled(false);
2247 none
= ! layer
.getParent().contains(Polyline
.class);
2248 item
= new JMenuItem("Hide all polylines"); item
.addActionListener(this); menu
.add(item
);
2249 if (none
) item
.setEnabled(false);
2250 item
= new JMenuItem("Unhide all polylines"); item
.addActionListener(this); menu
.add(item
);
2251 if (none
) item
.setEnabled(false);
2252 none
= ! layer
.getParent().contains(Ball
.class);
2253 item
= new JMenuItem("Hide all balls"); item
.addActionListener(this); menu
.add(item
);
2254 if (none
) item
.setEnabled(false);
2255 item
= new JMenuItem("Unhide all balls"); item
.addActionListener(this); menu
.add(item
);
2256 if (none
) item
.setEnabled(false);
2257 none
= ! layer
.getParent().containsDisplayable(Patch
.class);
2258 item
= new JMenuItem("Hide all images"); item
.addActionListener(this); menu
.add(item
);
2259 if (none
) item
.setEnabled(false);
2260 item
= new JMenuItem("Unhide all images"); item
.addActionListener(this); menu
.add(item
);
2261 if (none
) item
.setEnabled(false);
2262 item
= new JMenuItem("Hide all but images"); item
.addActionListener(this); menu
.add(item
);
2263 item
= new JMenuItem("Unhide all"); item
.addActionListener(this); menu
.add(item
); item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_H
, Event
.ALT_MASK
, true));
2266 } catch (Exception e
) { IJError
.print(e
); }
2268 JMenu adjust_menu
= new JMenu("Adjust");
2269 item
= new JMenuItem("Calibration..."); item
.addActionListener(this); adjust_menu
.add(item
);
2270 item
= new JMenuItem("Enhance contrast layer-wise..."); item
.addActionListener(this); adjust_menu
.add(item
);
2271 item
= new JMenuItem("Enhance contrast (selected images)..."); item
.addActionListener(this); adjust_menu
.add(item
);
2272 if (selection
.isEmpty()) item
.setEnabled(false);
2273 item
= new JMenuItem("Set Min and Max layer-wise..."); item
.addActionListener(this); adjust_menu
.add(item
);
2274 item
= new JMenuItem("Set Min and Max (selected images)..."); item
.addActionListener(this); adjust_menu
.add(item
);
2275 if (selection
.isEmpty()) item
.setEnabled(false);
2276 popup
.add(adjust_menu
);
2278 menu
= new JMenu("Import");
2279 item
= new JMenuItem("Import image"); item
.addActionListener(this); menu
.add(item
);
2280 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_I
, Event
.ALT_MASK
& Event
.SHIFT_MASK
, true));
2281 item
= new JMenuItem("Import stack..."); item
.addActionListener(this); menu
.add(item
);
2282 item
= new JMenuItem("Import grid..."); item
.addActionListener(this); menu
.add(item
);
2283 item
= new JMenuItem("Import sequence as grid..."); item
.addActionListener(this); menu
.add(item
);
2284 item
= new JMenuItem("Import from text file..."); item
.addActionListener(this); menu
.add(item
);
2285 item
= new JMenuItem("Import labels as arealists..."); item
.addActionListener(this); menu
.add(item
);
2288 menu
= new JMenu("Export");
2289 item
= new JMenuItem("Make flat image..."); item
.addActionListener(this); menu
.add(item
);
2290 item
= new JMenuItem("Arealists as labels (tif)"); item
.addActionListener(this); menu
.add(item
);
2291 if (0 == layer
.getParent().getZDisplayables(AreaList
.class).size()) item
.setEnabled(false);
2292 item
= new JMenuItem("Arealists as labels (amira)"); item
.addActionListener(this); menu
.add(item
);
2293 if (0 == layer
.getParent().getZDisplayables(AreaList
.class).size()) item
.setEnabled(false);
2296 menu
= new JMenu("Display");
2297 item
= new JMenuItem("Resize canvas/LayerSet..."); item
.addActionListener(this); menu
.add(item
);
2298 item
= new JMenuItem("Autoresize canvas/LayerSet"); item
.addActionListener(this); menu
.add(item
);
2299 // OBSOLETE // item = new JMenuItem("Rotate Layer/LayerSet..."); item.addActionListener(this); menu.add(item);
2300 item
= new JMenuItem("Properties ..."); item
.addActionListener(this); menu
.add(item
);
2303 menu
= new JMenu("Project");
2304 this.project
.getLoader().setupMenuItems(menu
, this.getProject());
2305 item
= new JMenuItem("Project properties..."); item
.addActionListener(this); menu
.add(item
);
2306 item
= new JMenuItem("Create subproject"); item
.addActionListener(this); menu
.add(item
);
2307 if (null == canvas
.getFakeImagePlus().getRoi()) item
.setEnabled(false);
2308 item
= new JMenuItem("Release memory..."); item
.addActionListener(this); menu
.add(item
);
2309 item
= new JMenuItem("Flush image cache"); item
.addActionListener(this); menu
.add(item
);
2310 item
= new JMenuItem("Regenerate all mipmaps"); item
.addActionListener(this); menu
.add(item
);
2313 menu
= new JMenu("Selection");
2314 item
= new JMenuItem("Select all"); item
.addActionListener(this); menu
.add(item
);
2315 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_A
, Utils
.getControlModifier(), true));
2316 if (0 == layer
.getDisplayables().size() && 0 == layer
.getParent().getZDisplayables().size()) item
.setEnabled(false);
2317 item
= new JMenuItem("Select none"); item
.addActionListener(this); menu
.add(item
);
2318 if (0 == selection
.getNSelected()) item
.setEnabled(false);
2319 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_ESCAPE
, 0, true));
2321 JMenu bytype
= new JMenu("Select all by type");
2322 item
= new JMenuItem("AreaList"); item
.addActionListener(bytypelistener
); bytype
.add(item
);
2323 item
= new JMenuItem("Ball"); item
.addActionListener(bytypelistener
); bytype
.add(item
);
2324 item
= new JMenuItem("Dissector"); item
.addActionListener(bytypelistener
); bytype
.add(item
);
2325 item
= new JMenuItem("Image"); item
.addActionListener(bytypelistener
); bytype
.add(item
);
2326 item
= new JMenuItem("Text"); item
.addActionListener(bytypelistener
); bytype
.add(item
);
2327 item
= new JMenuItem("Pipe"); item
.addActionListener(bytypelistener
); bytype
.add(item
);
2328 item
= new JMenuItem("Polyline"); item
.addActionListener(bytypelistener
); bytype
.add(item
);
2329 item
= new JMenuItem("Profile"); item
.addActionListener(bytypelistener
); bytype
.add(item
);
2332 item
= new JMenuItem("Restore selection"); item
.addActionListener(this); menu
.add(item
);
2333 item
= new JMenuItem("Select under ROI"); item
.addActionListener(this); menu
.add(item
);
2334 if (canvas
.getFakeImagePlus().getRoi() == null) item
.setEnabled(false);
2336 item
= new JMenuItem("Search..."); item
.addActionListener(this); popup
.add(item
);
2339 //canvas.add(popup);
2343 private ByTypeListener bytypelistener
= new ByTypeListener(this);
2345 static private class ByTypeListener
implements ActionListener
{
2347 ByTypeListener(final Display d
) {
2350 public void actionPerformed(final ActionEvent ae
) {
2351 final String command
= ae
.getActionCommand();
2353 final java
.awt
.geom
.Area aroi
= M
.getArea(d
.canvas
.getFakeImagePlus().getRoi());
2355 d
.dispatcher
.exec(new Runnable() { public void run() {
2358 String type
= command
;
2359 if (type
.equals("Image")) type
= "Patch";
2360 Class c
= Class
.forName("ini.trakem2.display." + type
);
2362 java
.util
.List
<Displayable
> a
= new ArrayList
<Displayable
>();
2364 a
.addAll(d
.layer
.getDisplayables(c
, aroi
, true));
2365 a
.addAll(d
.layer
.getParent().getZDisplayables(c
, d
.layer
, aroi
, true));
2367 a
.addAll(d
.layer
.getDisplayables(c
));
2368 a
.addAll(d
.layer
.getParent().getZDisplayables(c
));
2369 // Remove non-visible ones
2370 for (final Iterator
<Displayable
> it
= a
.iterator(); it
.hasNext(); ) {
2371 if (!it
.next().isVisible()) it
.remove();
2375 if (0 == a
.size()) return;
2377 boolean selected
= false;
2379 if (0 == ae
.getModifiers()) {
2380 Utils
.log2("first");
2381 d
.selection
.clear();
2382 d
.selection
.selectAll(a
);
2384 } else if (0 == (ae
.getModifiers() ^ Event
.SHIFT_MASK
)) {
2385 Utils
.log2("with shift");
2386 d
.selection
.selectAll(a
); // just add them to the current selection
2391 d
.selection
.setActive(a
.get(a
.size() -1));
2394 } catch (ClassNotFoundException e
) {
2395 Utils
.log2(e
.toString());
2402 /** Check if a panel for the given Displayable is completely visible in the JScrollPane */
2403 public boolean isWithinViewport(final Displayable d
) {
2404 final JScrollPane scroll
= (JScrollPane
)tabs
.getSelectedComponent();
2405 if (ht_tabs
.get(d
.getClass()) == scroll
) return isWithinViewport(scroll
, ht_panels
.get(d
));
2409 private boolean isWithinViewport(JScrollPane scroll
, DisplayablePanel dp
) {
2410 if(null == dp
) return false;
2411 JViewport view
= scroll
.getViewport();
2412 java
.awt
.Dimension dimensions
= view
.getExtentSize();
2413 java
.awt
.Point p
= view
.getViewPosition();
2415 if ((y
+ DisplayablePanel
.HEIGHT
- p
.y
) <= dimensions
.height
&& y
>= p
.y
) {
2421 /** Check if a panel for the given Displayable is partially visible in the JScrollPane */
2422 public boolean isPartiallyWithinViewport(final Displayable d
) {
2423 final JScrollPane scroll
= ht_tabs
.get(d
.getClass());
2424 if (tabs
.getSelectedComponent() == scroll
) return isPartiallyWithinViewport(scroll
, ht_panels
.get(d
));
2428 /** Check if a panel for the given Displayable is at least partially visible in the JScrollPane */
2429 private boolean isPartiallyWithinViewport(final JScrollPane scroll
, final DisplayablePanel dp
) {
2431 //Utils.log2("Display.isPartiallyWithinViewport: null DisplayablePanel ??");
2432 return false; // to fast for you baby
2434 JViewport view
= scroll
.getViewport();
2435 java
.awt
.Dimension dimensions
= view
.getExtentSize();
2436 java
.awt
.Point p
= view
.getViewPosition();
2438 if ( ((y
+ DisplayablePanel
.HEIGHT
- p
.y
) <= dimensions
.height
&& y
>= p
.y
) // completely visible
2439 || ((y
+ DisplayablePanel
.HEIGHT
- p
.y
) > dimensions
.height
&& y
< p
.y
+ dimensions
.height
) // partially hovering at the bottom
2440 || ((y
+ DisplayablePanel
.HEIGHT
) > p
.y
&& y
< p
.y
) // partially hovering at the top
2447 /** A function to make a Displayable panel be visible in the screen, by scrolling the viewport of the JScrollPane. */
2448 private void scrollToShow(final Displayable d
) {
2449 dispatcher
.execSwing(new Runnable() { public void run() {
2450 final JScrollPane scroll
= (JScrollPane
)tabs
.getSelectedComponent();
2451 if (d
instanceof ZDisplayable
&& scroll
== scroll_zdispl
) {
2452 scrollToShow(scroll_zdispl
, ht_panels
.get(d
));
2455 final Class c
= d
.getClass();
2456 if (Patch
.class == c
&& scroll
== scroll_patches
) {
2457 scrollToShow(scroll_patches
, ht_panels
.get(d
));
2458 } else if (DLabel
.class == c
&& scroll
== scroll_labels
) {
2459 scrollToShow(scroll_labels
, ht_panels
.get(d
));
2460 } else if (Profile
.class == c
&& scroll
== scroll_profiles
) {
2461 scrollToShow(scroll_profiles
, ht_panels
.get(d
));
2466 private void scrollToShow(final JScrollPane scroll
, final JPanel dp
) {
2467 if (null == dp
) return;
2468 JViewport view
= scroll
.getViewport();
2469 Point current
= view
.getViewPosition();
2470 Dimension extent
= view
.getExtentSize();
2471 int panel_y
= dp
.getY();
2472 if ((panel_y
+ DisplayablePanel
.HEIGHT
- current
.y
) <= extent
.height
&& panel_y
>= current
.y
) {
2473 // it's completely visible already
2476 // scroll just enough
2477 // if it's above, show at the top
2478 if (panel_y
- current
.y
< 0) {
2479 view
.setViewPosition(new Point(0, panel_y
));
2481 // if it's below (even if partially), show at the bottom
2482 else if (panel_y
+ 50 > current
.y
+ extent
.height
) {
2483 view
.setViewPosition(new Point(0, panel_y
- extent
.height
+ 50));
2484 //Utils.log("Display.scrollToShow: panel_y: " + panel_y + " current.y: " + current.y + " extent.height: " + extent.height);
2489 /** Update the title of the given Displayable in its DisplayablePanel, if any. */
2490 static public void updateTitle(final Layer layer
, final Displayable displ
) {
2491 for (final Display d
: al_displays
) {
2492 if (layer
== d
.layer
) {
2493 DisplayablePanel dp
= d
.ht_panels
.get(displ
);
2494 if (null != dp
) dp
.updateTitle();
2499 /** Update the Display's title in all Displays showing the given Layer. */
2500 static public void updateTitle(final Layer layer
) {
2501 for (final Display d
: al_displays
) {
2502 if (d
.layer
== layer
) {
2503 d
.updateFrameTitle();
2507 /** Update the Display's title in all Displays showing a Layer of the given LayerSet. */
2508 static public void updateTitle(final LayerSet ls
) {
2509 for (final Display d
: al_displays
) {
2510 if (d
.layer
.getParent() == ls
) {
2511 d
.updateFrameTitle();
2516 /** Set a new title in the JFrame, showing info on the layer 'z' and the magnification. */
2517 public void updateFrameTitle() {
2518 updateFrameTitle(layer
);
2520 private void updateFrameTitle(Layer layer
) {
2521 // From ij.ImagePlus class, the solution:
2523 final double magnification
= canvas
.getMagnification();
2524 if (magnification
!=1.0) {
2525 final double percent
= magnification
*100.0;
2526 scale
= new StringBuilder(" (").append(Utils
.d2s(percent
, percent
==(int)percent ?
0 : 1)).append("%)").toString();
2528 final Calibration cal
= layer
.getParent().getCalibration();
2529 String title
= new StringBuilder().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();
2530 frame
.setTitle(title
);
2531 // fix the title for the FakeImageWindow and thus the WindowManager listing in the menus
2532 canvas
.getFakeImagePlus().setTitle(title
);
2535 /** 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. */
2536 public void nextLayer(final int modifiers
) {
2538 if (0 == (modifiers ^ Event
.SHIFT_MASK
)) {
2539 l
= layer
.getParent().nextNonEmpty(layer
);
2540 } else if (scroll_step
> 1) {
2541 int i
= layer
.getParent().indexOf(this.layer
);
2542 Layer la
= layer
.getParent().getLayer(i
+ scroll_step
);
2543 if (null != la
) l
= la
;
2546 l
= layer
.getParent().next(layer
);
2550 updateInDatabase("layer_id");
2554 private final void translateLayerColors(final Layer current
, final Layer other
) {
2555 if (current
== other
) return;
2556 if (layer_channels
.size() > 0) {
2557 final LayerSet ls
= getLayerSet();
2558 // translate colors by distance from current layer to new Layer l
2559 final int dist
= ls
.indexOf(other
) - ls
.indexOf(current
);
2560 translateLayerColor(Color
.red
, dist
);
2561 translateLayerColor(Color
.blue
, dist
);
2565 private final void translateLayerColor(final Color color
, final int dist
) {
2566 final LayerSet ls
= getLayerSet();
2567 final Layer l
= layer_channels
.get(color
);
2568 if (null == l
) return;
2569 updateColor(Color
.white
, layer_panels
.get(l
));
2570 final Layer l2
= ls
.getLayer(ls
.indexOf(l
) + dist
);
2571 if (null != l2
) updateColor(color
, layer_panels
.get(l2
));
2574 private final void updateColor(final Color color
, final LayerPanel lp
) {
2576 setColorChannel(lp
.layer
, color
);
2579 /** Calls setLayer(la) on the SetLayerThread. */
2580 public void toLayer(final Layer la
) {
2581 if (la
.getParent() != layer
.getParent()) return; // not of the same LayerSet
2582 if (la
== layer
) return; // nothing to do
2584 updateInDatabase("layer_id");
2587 /** 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. */
2588 public void previousLayer(final int modifiers
) {
2590 if (0 == (modifiers ^ Event
.SHIFT_MASK
)) {
2591 l
= layer
.getParent().previousNonEmpty(layer
);
2592 } else if (scroll_step
> 1) {
2593 int i
= layer
.getParent().indexOf(this.layer
);
2594 Layer la
= layer
.getParent().getLayer(i
- scroll_step
);
2595 if (null != la
) l
= la
;
2598 l
= layer
.getParent().previous(layer
);
2602 updateInDatabase("layer_id");
2606 static public void updateLayerScroller(LayerSet set
) {
2607 for (final Display d
: al_displays
) {
2608 if (d
.layer
.getParent() == set
) {
2609 d
.updateLayerScroller(d
.layer
);
2614 private void updateLayerScroller(Layer layer
) {
2615 int size
= layer
.getParent().size();
2617 scroller
.setValues(0, 1, 0, 0);
2618 scroller
.setEnabled(false);
2620 scroller
.setEnabled(true);
2621 scroller
.setValues(layer
.getParent().getLayerIndex(layer
.getId()), 1, 0, size
);
2623 recreateLayerPanels(layer
);
2626 // Can't use this.layer, may still be null. User argument instead.
2627 private synchronized void recreateLayerPanels(final Layer layer
) {
2628 synchronized (layer_channels
) {
2629 panel_layers
.removeAll();
2631 if (0 == layer_panels
.size()) {
2632 for (final Layer la
: layer
.getParent().getLayers()) {
2633 final LayerPanel lp
= new LayerPanel(this, la
);
2634 layer_panels
.put(la
, lp
);
2635 this.panel_layers
.add(lp
);
2638 // Set theory at work: keep old to reuse
2639 layer_panels
.keySet().retainAll(layer
.getParent().getLayers());
2640 for (final Layer la
: layer
.getParent().getLayers()) {
2641 LayerPanel lp
= layer_panels
.get(la
);
2643 lp
= new LayerPanel(this, la
);
2644 layer_panels
.put(la
, lp
);
2646 this.panel_layers
.add(lp
);
2648 for (final Iterator
<Map
.Entry
<Integer
,LayerPanel
>> it
= layer_alpha
.entrySet().iterator(); it
.hasNext(); ) {
2649 final Map
.Entry
<Integer
,LayerPanel
> e
= it
.next();
2650 if (-1 == getLayerSet().indexOf(e
.getValue().layer
)) it
.remove();
2652 for (final Iterator
<Map
.Entry
<Color
,Layer
>> it
= layer_channels
.entrySet().iterator(); it
.hasNext(); ) {
2653 final Map
.Entry
<Color
,Layer
> e
= it
.next();
2654 if (-1 == getLayerSet().indexOf(e
.getValue())) it
.remove();
2656 scroll_layers
.repaint();
2661 private void updateSnapshots() {
2662 Enumeration
<DisplayablePanel
> e
= ht_panels
.elements();
2663 while (e
.hasMoreElements()) {
2664 e
.nextElement().remake();
2666 Utils
.updateComponent(tabs
.getSelectedComponent());
2669 static public void updatePanel(Layer layer
, final Displayable displ
) {
2670 if (null == layer
&& null != front
) layer
= front
.layer
; // the front layer
2671 for (final Display d
: al_displays
) {
2672 if (d
.layer
== layer
) {
2673 d
.updatePanel(displ
);
2678 private void updatePanel(Displayable d
) {
2680 if (d
instanceof Profile
) {
2682 } else if (d
instanceof Patch
) {
2684 } else if (d
instanceof DLabel
) {
2686 } else if (d
instanceof Pipe
) {
2689 if (null == c
) return;
2690 DisplayablePanel dp
= ht_panels
.get(d
);
2692 Utils
.updateComponent(c
);
2695 static public void updatePanelIndex(final Layer layer
, final Displayable displ
) {
2696 for (final Display d
: al_displays
) {
2697 if (d
.layer
== layer
|| displ
instanceof ZDisplayable
) {
2698 d
.updatePanelIndex(displ
);
2703 private void updatePanelIndex(final Displayable d
) {
2704 updateTab( (JPanel
) ht_tabs
.get(d
.getClass()).getViewport().getView(), "",
2705 ZDisplayable
.class.isAssignableFrom(d
.getClass()) ?
2706 layer
.getParent().getZDisplayables()
2707 : layer
.getDisplayables(d
.getClass()));
2710 /** Repair possibly missing panels and other components by simply resetting the same Layer */
2711 public void repairGUI() {
2712 Layer layer
= this.layer
;
2717 public void actionPerformed(final ActionEvent ae
) {
2718 dispatcher
.exec(new Runnable() { public void run() {
2720 String command
= ae
.getActionCommand();
2721 if (command
.startsWith("Job")) {
2722 if (Utils
.checkYN("Really cancel job?")) {
2723 project
.getLoader().quitJob(command
);
2727 } else if (command
.equals("Move to top")) {
2728 if (null == active
) return;
2729 canvas
.setUpdateGraphics(true);
2730 layer
.getParent().move(LayerSet
.TOP
, active
);
2731 Display
.repaint(layer
.getParent(), active
, 5);
2732 //Display.updatePanelIndex(layer, active);
2733 } else if (command
.equals("Move up")) {
2734 if (null == active
) return;
2735 canvas
.setUpdateGraphics(true);
2736 layer
.getParent().move(LayerSet
.UP
, active
);
2737 Display
.repaint(layer
.getParent(), active
, 5);
2738 //Display.updatePanelIndex(layer, active);
2739 } else if (command
.equals("Move down")) {
2740 if (null == active
) return;
2741 canvas
.setUpdateGraphics(true);
2742 layer
.getParent().move(LayerSet
.DOWN
, active
);
2743 Display
.repaint(layer
.getParent(), active
, 5);
2744 //Display.updatePanelIndex(layer, active);
2745 } else if (command
.equals("Move to bottom")) {
2746 if (null == active
) return;
2747 canvas
.setUpdateGraphics(true);
2748 layer
.getParent().move(LayerSet
.BOTTOM
, active
);
2749 Display
.repaint(layer
.getParent(), active
, 5);
2750 //Display.updatePanelIndex(layer, active);
2751 } else if (command
.equals("Duplicate, link and send to next layer")) {
2752 duplicateLinkAndSendTo(active
, 1, layer
.getParent().next(layer
));
2753 } else if (command
.equals("Duplicate, link and send to previous layer")) {
2754 duplicateLinkAndSendTo(active
, 0, layer
.getParent().previous(layer
));
2755 } else if (command
.equals("Duplicate, link and send to...")) {
2756 // fix non-scrolling popup menu
2757 GenericDialog gd
= new GenericDialog("Send to");
2758 gd
.addMessage("Duplicate, link and send to...");
2759 String
[] sl
= new String
[layer
.getParent().size()];
2761 for (Iterator it
= layer
.getParent().getLayers().iterator(); it
.hasNext(); ) {
2762 sl
[next
++] = project
.findLayerThing(it
.next()).toString();
2764 gd
.addChoice("Layer: ", sl
, sl
[layer
.getParent().indexOf(layer
)]);
2766 if (gd
.wasCanceled()) return;
2767 Layer la
= layer
.getParent().getLayer(gd
.getNextChoiceIndex());
2769 Utils
.showMessage("Can't duplicate, link and send to the same layer.");
2772 duplicateLinkAndSendTo(active
, 0, la
);
2773 } else if (-1 != command
.indexOf("z = ")) {
2774 // this is an item from the "Duplicate, link and send to" menu of layer z's
2775 Layer target_layer
= layer
.getParent().getLayer(Double
.parseDouble(command
.substring(command
.lastIndexOf(' ') +1)));
2776 Utils
.log2("layer: __" +command
.substring(command
.lastIndexOf(' ') +1) + "__");
2777 if (null == target_layer
) return;
2778 duplicateLinkAndSendTo(active
, 0, target_layer
);
2779 } else if (-1 != command
.indexOf("z=")) {
2780 // WARNING the indexOf is very similar to the previous one
2781 // Send the linked group to the selected layer
2782 int iz
= command
.indexOf("z=")+2;
2783 Utils
.log2("iz=" + iz
+ " other: " + command
.indexOf(' ', iz
+2));
2784 int end
= command
.indexOf(' ', iz
);
2785 if (-1 == end
) end
= command
.length();
2786 double lz
= Double
.parseDouble(command
.substring(iz
, end
));
2787 Layer target
= layer
.getParent().getLayer(lz
);
2788 HashSet hs
= active
.getLinkedGroup(new HashSet());
2789 layer
.getParent().move(hs
, active
.getLayer(), target
);
2790 } else if (command
.equals("Unlink")) {
2791 if (null == active
|| active
instanceof Patch
) return;
2793 updateSelection();//selection.update();
2794 } else if (command
.equals("Unlink from images")) {
2795 if (null == active
) return;
2797 for (Displayable displ
: selection
.getSelected()) {
2798 displ
.unlinkAll(Patch
.class);
2800 updateSelection();//selection.update();
2801 } catch (Exception e
) { IJError
.print(e
); }
2802 } else if (command
.equals("Unlink slices")) {
2803 YesNoCancelDialog yn
= new YesNoCancelDialog(frame
, "Attention", "Really unlink all slices from each other?\nThere is no undo.");
2804 if (!yn
.yesPressed()) return;
2805 final ArrayList
<Patch
> pa
= ((Patch
)active
).getStackPatches();
2806 for (int i
=pa
.size()-1; i
>0; i
--) {
2807 pa
.get(i
).unlink(pa
.get(i
-1));
2809 } else if (command
.equals("Send to next layer")) {
2810 Rectangle box
= selection
.getBox();
2812 // unlink Patch instances
2813 for (final Displayable displ
: selection
.getSelected()) {
2814 displ
.unlinkAll(Patch
.class);
2816 updateSelection();//selection.update();
2817 } catch (Exception e
) { IJError
.print(e
); }
2818 //layer.getParent().moveDown(layer, active); // will repaint whatever appropriate layers
2819 selection
.moveDown();
2820 repaint(layer
.getParent(), box
);
2821 } else if (command
.equals("Send to previous layer")) {
2822 Rectangle box
= selection
.getBox();
2824 // unlink Patch instances
2825 for (final Displayable displ
: selection
.getSelected()) {
2826 displ
.unlinkAll(Patch
.class);
2828 updateSelection();//selection.update();
2829 } catch (Exception e
) { IJError
.print(e
); }
2830 //layer.getParent().moveUp(layer, active); // will repaint whatever appropriate layers
2832 repaint(layer
.getParent(), box
);
2833 } else if (command
.equals("Show centered")) {
2834 if (active
== null) return;
2835 showCentered(active
);
2836 } else if (command
.equals("Delete...")) {
2838 if (null != active) {
2839 Displayable d = active;
2840 selection.remove(d);
2841 d.remove(true); // will repaint
2844 // remove all selected objects
2845 selection
.deleteAll();
2846 } else if (command
.equals("Color...")) {
2847 IJ
.doCommand("Color Picker...");
2848 } else if (command
.equals("Revert")) {
2849 if (null == active
|| active
.getClass() != Patch
.class) return;
2850 Patch p
= (Patch
)active
;
2852 if (null == p
.getOriginalPath()) Utils
.log("No editions to save for patch " + p
.getTitle() + " #" + p
.getId());
2853 else Utils
.log("Could not revert Patch " + p
.getTitle() + " #" + p
.getId());
2855 } else if (command
.equals("Remove alpha mask")) {
2856 final ArrayList
<Displayable
> patches
= selection
.getSelected(Patch
.class);
2857 if (patches
.size() > 0) {
2858 Bureaucrat
.createAndStart(new Worker
.Task("Removing alpha mask" + (patches
.size() > 1 ?
"s" : "")) { public void exec() {
2859 final ArrayList
<Future
> jobs
= new ArrayList
<Future
>();
2860 for (final Displayable d
: patches
) {
2861 final Patch p
= (Patch
) d
;
2862 p
.setAlphaMask(null);
2863 Future job
= p
.getProject().getLoader().regenerateMipMaps(p
); // submit to queue
2864 if (null != job
) jobs
.add(job
);
2867 for (final Future job
: jobs
) try {
2869 } catch (Exception ie
) {}
2870 }}, patches
.get(0).getProject());
2872 } else if (command
.equals("Undo")) {
2873 Bureaucrat
.createAndStart(new Worker
.Task("Undo") { public void exec() {
2874 layer
.getParent().undoOneStep();
2875 Display
.repaint(layer
.getParent());
2877 } else if (command
.equals("Redo")) {
2878 Bureaucrat
.createAndStart(new Worker
.Task("Redo") { public void exec() {
2879 layer
.getParent().redoOneStep();
2880 Display
.repaint(layer
.getParent());
2882 } else if (command
.equals("Transform")) {
2883 if (null == active
) return;
2884 canvas
.setTransforming(true);
2885 } else if (command
.equals("Apply transform")) {
2886 if (null == active
) return;
2887 canvas
.setTransforming(false);
2888 } else if (command
.equals("Apply transform propagating to last layer")) {
2889 if (selection
.isTransforming()) {
2890 final java
.util
.List
<Layer
> layers
= layer
.getParent().getLayers();
2891 selection
.applyAndPropagate(new HashSet
<Layer
>(layers
.subList(layers
.indexOf(Display
.this.layer
)+1, layers
.size()))); // +1 to exclude current layer
2893 } else if (command
.equals("Apply transform propagating to first layer")) {
2894 if (selection
.isTransforming()) {
2895 final java
.util
.List
<Layer
> layers
= layer
.getParent().getLayers();
2896 selection
.applyAndPropagate(new HashSet
<Layer
>(layers
.subList(0, layers
.indexOf(Display
.this.layer
))));
2898 } else if (command
.equals("Cancel transform")) {
2899 if (null == active
) return;
2900 canvas
.cancelTransform();
2901 } else if (command
.equals("Specify transform...")) {
2902 if (null == active
) return;
2903 selection
.specify();
2904 } else if (command
.equals("Hide all but images")) {
2905 ArrayList
<Class
> type
= new ArrayList
<Class
>();
2906 type
.add(Patch
.class);
2907 selection
.removeAll(layer
.getParent().hideExcept(type
, false));
2908 Display
.update(layer
.getParent(), false);
2909 } else if (command
.equals("Unhide all")) {
2910 layer
.getParent().setAllVisible(false);
2911 Display
.update(layer
.getParent(), false);
2912 } else if (command
.startsWith("Hide all ")) {
2913 String type
= command
.substring(9, command
.length() -1); // skip the ending plural 's'
2914 type
= type
.substring(0, 1).toUpperCase() + type
.substring(1);
2915 selection
.removeAll(layer
.getParent().setVisible(type
, false, true));
2916 } else if (command
.startsWith("Unhide all ")) {
2917 String type
= command
.substring(11, command
.length() -1); // skip the ending plural 's'
2918 type
= type
.substring(0, 1).toUpperCase() + type
.substring(1);
2919 layer
.getParent().setVisible(type
, true, true);
2920 } else if (command
.equals("Hide deselected")) {
2921 hideDeselected(0 != (ActionEvent
.ALT_MASK
& ae
.getModifiers()));
2922 } else if (command
.equals("Hide deselected except images")) {
2923 hideDeselected(true);
2924 } else if (command
.equals("Hide selected")) {
2925 selection
.setVisible(false); // TODO should deselect them too? I don't think so.
2926 } else if (command
.equals("Resize canvas/LayerSet...")) {
2928 } else if (command
.equals("Autoresize canvas/LayerSet")) {
2929 layer
.getParent().setMinimumDimensions();
2930 } else if (command
.equals("Import image")) {
2932 } else if (command
.equals("Import next image")) {
2934 } else if (command
.equals("Import stack...")) {
2935 Display
.this.getLayerSet().addLayerContentStep(layer
);
2936 Rectangle sr
= getCanvas().getSrcRect();
2937 Bureaucrat burro
= project
.getLoader().importStack(layer
, sr
.x
+ sr
.width
/2, sr
.y
+ sr
.height
/2, null, true, null);
2938 burro
.addPostTask(new Runnable() { public void run() {
2939 Display
.this.getLayerSet().addLayerContentStep(layer
);
2941 } else if (command
.equals("Import grid...")) {
2942 Display
.this.getLayerSet().addLayerContentStep(layer
);
2943 Bureaucrat burro
= project
.getLoader().importGrid(layer
);
2944 burro
.addPostTask(new Runnable() { public void run() {
2945 Display
.this.getLayerSet().addLayerContentStep(layer
);
2947 } else if (command
.equals("Import sequence as grid...")) {
2948 Display
.this.getLayerSet().addLayerContentStep(layer
);
2949 Bureaucrat burro
= project
.getLoader().importSequenceAsGrid(layer
);
2950 burro
.addPostTask(new Runnable() { public void run() {
2951 Display
.this.getLayerSet().addLayerContentStep(layer
);
2953 } else if (command
.equals("Import from text file...")) {
2954 Display
.this.getLayerSet().addLayerContentStep(layer
);
2955 Bureaucrat burro
= project
.getLoader().importImages(layer
);
2956 burro
.addPostTask(new Runnable() { public void run() {
2957 Display
.this.getLayerSet().addLayerContentStep(layer
);
2959 } else if (command
.equals("Import labels as arealists...")) {
2960 Display
.this.getLayerSet().addChangeTreesStep();
2961 Bureaucrat burro
= project
.getLoader().importLabelsAsAreaLists(layer
, null, Double
.MAX_VALUE
, 0, 0.4f
, false);
2962 burro
.addPostTask(new Runnable() { public void run() {
2963 Display
.this.getLayerSet().addChangeTreesStep();
2965 } else if (command
.equals("Make flat image...")) {
2966 // if there's a ROI, just use that as cropping rectangle
2967 Rectangle srcRect
= null;
2968 Roi roi
= canvas
.getFakeImagePlus().getRoi();
2970 srcRect
= roi
.getBounds();
2972 // otherwise, whatever is visible
2973 //srcRect = canvas.getSrcRect();
2974 // The above is confusing. That is what ROIs are for. So paint all:
2975 srcRect
= new Rectangle(0, 0, (int)Math
.ceil(layer
.getParent().getLayerWidth()), (int)Math
.ceil(layer
.getParent().getLayerHeight()));
2978 final String
[] types
= new String
[]{"8-bit grayscale", "RGB Color"};
2979 int the_type
= ImagePlus
.GRAY8
;
2980 final GenericDialog gd
= new GenericDialog("Choose", frame
);
2981 gd
.addSlider("Scale: ", 1, 100, 100);
2982 gd
.addChoice("Type: ", types
, types
[0]);
2983 if (layer
.getParent().size() > 1) {
2985 String[] layers = new String[layer.getParent().size()];
2987 for (Iterator it = layer.getParent().getLayers().iterator(); it.hasNext(); ) {
2988 layers[i] = layer.getProject().findLayerThing((Layer)it.next()).toString();
2991 int i_layer = layer.getParent().indexOf(layer);
2992 gd.addChoice("Start: ", layers, layers[i_layer]);
2993 gd.addChoice("End: ", layers, layers[i_layer]);
2995 Utils
.addLayerRangeChoices(Display
.this.layer
, gd
); /// $#%! where are my lisp macros
2996 gd
.addCheckbox("Include non-empty layers only", true);
2998 gd
.addMessage("Background color:");
2999 Utils
.addRGBColorSliders(gd
, Color
.black
);
3000 gd
.addCheckbox("Best quality", false);
3002 gd
.addCheckbox("Save to file", false);
3003 gd
.addCheckbox("Save for web", false);
3005 if (gd
.wasCanceled()) return;
3006 scale
= gd
.getNextNumber() / 100;
3007 the_type
= (0 == gd
.getNextChoiceIndex() ? ImagePlus
.GRAY8
: ImagePlus
.COLOR_RGB
);
3008 if (Double
.isNaN(scale
) || scale
<= 0.0) {
3009 Utils
.showMessage("Invalid scale.");
3012 Layer
[] layer_array
= null;
3013 boolean non_empty_only
= false;
3014 if (layer
.getParent().size() > 1) {
3015 non_empty_only
= gd
.getNextBoolean();
3016 int i_start
= gd
.getNextChoiceIndex();
3017 int i_end
= gd
.getNextChoiceIndex();
3018 ArrayList al
= new ArrayList();
3019 ArrayList al_zd
= layer
.getParent().getZDisplayables();
3020 ZDisplayable
[] zd
= new ZDisplayable
[al_zd
.size()];
3022 for (int i
=i_start
, j
=0; i
<= i_end
; i
++, j
++) {
3023 Layer la
= layer
.getParent().getLayer(i
);
3024 if (!la
.isEmpty() || !non_empty_only
) al
.add(la
); // checks both the Layer and the ZDisplayable objects in the parent LayerSet
3026 if (0 == al
.size()) {
3027 Utils
.showMessage("All layers are empty!");
3030 layer_array
= new Layer
[al
.size()];
3031 al
.toArray(layer_array
);
3033 layer_array
= new Layer
[]{Display
.this.layer
};
3035 final Color background
= new Color((int)gd
.getNextNumber(), (int)gd
.getNextNumber(), (int)gd
.getNextNumber());
3036 final boolean quality
= gd
.getNextBoolean();
3037 final boolean save_to_file
= gd
.getNextBoolean();
3038 final boolean save_for_web
= gd
.getNextBoolean();
3039 // in its own thread
3040 if (save_for_web
) project
.getLoader().makePrescaledTiles(layer_array
, Patch
.class, srcRect
, scale
, c_alphas
, the_type
);
3041 else project
.getLoader().makeFlatImage(layer_array
, srcRect
, scale
, c_alphas
, the_type
, save_to_file
, quality
, background
);
3043 } else if (command
.equals("Lock")) {
3044 selection
.setLocked(true);
3045 } else if (command
.equals("Unlock")) {
3046 selection
.setLocked(false);
3047 } else if (command
.equals("Properties...")) {
3048 active
.adjustProperties();
3050 } else if (command
.equals("Cancel alignment")) {
3051 layer
.getParent().cancelAlign();
3052 } else if (command
.equals("Align with landmarks")) {
3053 layer
.getParent().applyAlign(false);
3054 } else if (command
.equals("Align and register")) {
3055 layer
.getParent().applyAlign(true);
3056 } else if (command
.equals("Align using profiles")) {
3057 if (!selection
.contains(Profile
.class)) {
3058 Utils
.showMessage("No profiles are selected.");
3061 // ask for range of layers
3062 final GenericDialog gd
= new GenericDialog("Choose range");
3063 Utils
.addLayerRangeChoices(Display
.this.layer
, gd
);
3065 if (gd
.wasCanceled()) return;
3066 Layer la_start
= layer
.getParent().getLayer(gd
.getNextChoiceIndex());
3067 Layer la_end
= layer
.getParent().getLayer(gd
.getNextChoiceIndex());
3068 if (la_start
== la_end
) {
3069 Utils
.showMessage("Need at least two layers.");
3072 if (selection
.isLocked()) {
3073 Utils
.showMessage("There are locked objects.");
3076 layer
.getParent().startAlign(Display
.this);
3077 layer
.getParent().applyAlign(la_start
, la_end
, selection
);
3078 } else if (command
.equals("Align stack slices")) {
3079 if (getActive() instanceof Patch
) {
3080 final Patch slice
= (Patch
)getActive();
3081 if (slice
.isStack()) {
3082 // check linked group
3083 final HashSet hs
= slice
.getLinkedGroup(new HashSet());
3084 for (Iterator it
= hs
.iterator(); it
.hasNext(); ) {
3085 if (it
.next().getClass() != Patch
.class) {
3086 Utils
.showMessage("Images are linked to other objects, can't proceed to cross-correlate them."); // labels should be fine, need to check that
3090 final LayerSet ls
= slice
.getLayerSet();
3091 final HashSet
<Displayable
> linked
= slice
.getLinkedGroup(null);
3092 ls
.addTransformStep(linked
);
3093 Bureaucrat burro
= Registration
.registerStackSlices((Patch
)getActive()); // will repaint
3094 burro
.addPostTask(new Runnable() { public void run() {
3095 // The current state when done
3096 ls
.addTransformStep(linked
);
3099 Utils
.log("Align stack slices: selected image is not part of a stack.");
3102 } else if (command
.equals("Align layers")) {
3103 final Layer la
= layer
;; // caching, since scroll wheel may change it
3104 la
.getParent().addTransformStep(la
);
3105 Bureaucrat burro
= AlignTask
.alignLayersLinearlyTask( la
);
3106 burro
.addPostTask(new Runnable() { public void run() {
3107 la
.getParent().addTransformStep(la
);
3109 } else if (command
.equals("Align multi-layer mosaic")) {
3110 final Layer la
= layer
; // caching, since scroll wheel may change it
3111 la
.getParent().addTransformStep();
3112 Bureaucrat burro
= AlignTask
.alignMultiLayerMosaicTask( la
);
3113 burro
.addPostTask(new Runnable() { public void run() {
3114 la
.getParent().addTransformStep();
3116 } else if (command
.equals("Properties ...")) { // NOTE the space before the dots, to distinguish from the "Properties..." command that works on Displayable objects.
3117 GenericDialog gd
= new GenericDialog("Properties", Display
.this.frame
);
3118 //gd.addNumericField("layer_scroll_step: ", this.scroll_step, 0);
3119 gd
.addSlider("layer_scroll_step: ", 1, layer
.getParent().size(), Display
.this.scroll_step
);
3120 gd
.addChoice("snapshots_mode", LayerSet
.snapshot_modes
, LayerSet
.snapshot_modes
[layer
.getParent().getSnapshotsMode()]);
3121 gd
.addCheckbox("prefer_snapshots_quality", layer
.getParent().snapshotsQuality());
3122 Loader lo
= getProject().getLoader();
3123 boolean using_mipmaps
= lo
.isMipMapsEnabled();
3124 gd
.addCheckbox("enable_mipmaps", using_mipmaps
);
3125 String preprocessor
= project
.getLoader().getPreprocessor();
3126 gd
.addStringField("image_preprocessor: ", null == preprocessor ?
"" : preprocessor
);
3127 gd
.addCheckbox("enable_layer_pixels virtualization", layer
.getParent().isPixelsVirtualizationEnabled());
3128 double max
= layer
.getParent().getLayerWidth() < layer
.getParent().getLayerHeight() ? layer
.getParent().getLayerWidth() : layer
.getParent().getLayerHeight();
3129 gd
.addSlider("max_dimension of virtualized layer pixels: ", 0, max
, layer
.getParent().getPixelsMaxDimension());
3132 if (gd
.wasCanceled()) return;
3134 int sc
= (int) gd
.getNextNumber();
3136 Display
.this.scroll_step
= sc
;
3137 updateInDatabase("scroll_step");
3139 layer
.getParent().setSnapshotsMode(gd
.getNextChoiceIndex());
3140 layer
.getParent().setSnapshotsQuality(gd
.getNextBoolean());
3142 boolean generate_mipmaps
= gd
.getNextBoolean();
3143 if (using_mipmaps
&& generate_mipmaps
) {
3146 if (using_mipmaps
) { // and !generate_mipmaps
3147 lo
.flushMipMaps(true);
3149 // not using mipmaps before, and true == generate_mipmaps
3150 lo
.generateMipMaps(layer
.getParent().getDisplayables(Patch
.class));
3154 final String prepro
= gd
.getNextString();
3155 if (!project
.getLoader().setPreprocessor(prepro
.trim())) {
3156 Utils
.showMessage("Could NOT set the preprocessor to " + prepro
);
3159 layer
.getParent().setPixelsVirtualizationEnabled(gd
.getNextBoolean());
3160 layer
.getParent().setPixelsMaxDimension((int)gd
.getNextNumber());
3161 } else if (command
.equals("Search...")) {
3163 } else if (command
.equals("Select all")) {
3164 selection
.selectAll();
3165 repaint(Display
.this.layer
, selection
.getBox(), 0);
3166 } else if (command
.equals("Select none")) {
3167 Rectangle box
= selection
.getBox();
3169 repaint(Display
.this.layer
, box
, 0);
3170 } else if (command
.equals("Restore selection")) {
3171 selection
.restore();
3172 } else if (command
.equals("Select under ROI")) {
3173 Roi roi
= canvas
.getFakeImagePlus().getRoi();
3174 if (null == roi
) return;
3175 selection
.selectAll(roi
, true);
3176 } else if (command
.equals("Merge")) {
3177 ArrayList al_sel
= selection
.getSelected();
3178 // put active at the beginning, to work as the base on which other's will get merged
3179 al_sel
.remove(Display
.this.active
);
3180 al_sel
.add(0, Display
.this.active
);
3181 AreaList ali
= AreaList
.merge(al_sel
);
3183 // remove all but the first from the selection
3184 for (int i
=1; i
<al_sel
.size(); i
++) {
3185 Object ob
= al_sel
.get(i
);
3186 if (ob
.getClass() == AreaList
.class) {
3187 selection
.remove((Displayable
)ob
);
3190 selection
.updateTransform(ali
);
3191 repaint(ali
.getLayerSet(), ali
, 0);
3193 } else if (command
.equals("Identify...")) {
3194 // for pipes only for now
3195 if (!(active
instanceof Pipe
)) return;
3196 ini
.trakem2
.vector
.Compare
.findSimilar((Pipe
)active
);
3197 } else if (command
.equals("Identify with axes...")) {
3198 if (!(active
instanceof Pipe
)) return;
3199 if (Project
.getProjects().size() < 2) {
3200 Utils
.showMessage("You need at least two projects open:\n-A reference project\n-The current project with the pipe to identify");
3203 ini
.trakem2
.vector
.Compare
.findSimilarWithAxes((Pipe
)active
);
3204 } else if (command
.equals("View orthoslices")) {
3205 if (!(active
instanceof Patch
)) return;
3206 Display3D
.showOrthoslices(((Patch
)active
));
3207 } else if (command
.equals("View volume")) {
3208 if (!(active
instanceof Patch
)) return;
3209 Display3D
.showVolume(((Patch
)active
));
3210 } else if (command
.equals("Show in 3D")) {
3211 for (Iterator it
= selection
.getSelected(ZDisplayable
.class).iterator(); it
.hasNext(); ) {
3212 ZDisplayable zd
= (ZDisplayable
)it
.next();
3213 Display3D
.show(zd
.getProject().findProjectThing(zd
));
3215 // handle profile lists ...
3216 HashSet hs
= new HashSet();
3217 for (Iterator it
= selection
.getSelected(Profile
.class).iterator(); it
.hasNext(); ) {
3218 Displayable d
= (Displayable
)it
.next();
3219 ProjectThing profile_list
= (ProjectThing
)d
.getProject().findProjectThing(d
).getParent();
3220 if (!hs
.contains(profile_list
)) {
3221 Display3D
.show(profile_list
);
3222 hs
.add(profile_list
);
3225 } else if (command
.equals("Snap")) {
3226 if (!(active
instanceof Patch
)) return;
3227 StitchingTEM
.snap(getActive(), Display
.this);
3228 } else if (command
.equals("Blend")) {
3229 HashSet
<Patch
> patches
= new HashSet
<Patch
>();
3230 for (final Displayable d
: selection
.getSelected()) {
3231 if (d
.getClass() == Patch
.class) patches
.add((Patch
)d
);
3233 if (patches
.size() > 1) {
3234 GenericDialog gd
= new GenericDialog("Blending");
3235 gd
.addCheckbox("Respect current alpha mask", true);
3237 if (gd
.wasCanceled()) return;
3238 Blending
.blend(patches
, gd
.getNextBoolean());
3240 IJ
.log("Please select more than one overlapping image.");
3242 } else if (command
.equals("Montage")) {
3243 if (!(active
instanceof Patch
)) {
3244 Utils
.showMessage("Please select only images.");
3247 final Set
<Displayable
> affected
= new HashSet
<Displayable
>(selection
.getAffected());
3248 for (final Displayable d
: affected
)
3250 Utils
.showMessage( "You cannot montage linked objects." );
3253 // make an undo step!
3254 final LayerSet ls
= layer
.getParent();
3255 ls
.addTransformStep(affected
);
3256 Bureaucrat burro
= AlignTask
.alignSelectionTask( selection
);
3257 burro
.addPostTask(new Runnable() { public void run() {
3258 ls
.addTransformStep(affected
);
3260 } else if (command
.equals("Lens correction")) {
3261 final Layer la
= layer
;
3262 la
.getParent().addDataEditStep(new HashSet
<Displayable
>(la
.getParent().getDisplayables()));
3263 Bureaucrat burro
= DistortionCorrectionTask
.correctDistortionFromSelection( selection
);
3264 burro
.addPostTask(new Runnable() { public void run() {
3265 // no means to know which where modified and from which layers!
3266 la
.getParent().addDataEditStep(new HashSet
<Displayable
>(la
.getParent().getDisplayables()));
3268 } else if (command
.equals("Link images...")) {
3269 GenericDialog gd
= new GenericDialog("Options");
3270 gd
.addMessage("Linking images to images (within their own layer only):");
3271 String
[] options
= {"all images to all images", "each image with any other overlapping image"};
3272 gd
.addChoice("Link: ", options
, options
[1]);
3273 String
[] options2
= {"selected images only", "all images in this layer", "all images in all layers"};
3274 gd
.addChoice("Apply to: ", options2
, options2
[0]);
3276 if (gd
.wasCanceled()) return;
3278 final HashSet
<Displayable
> ds
= new HashSet
<Displayable
>(lay
.getParent().getDisplayables());
3279 lay
.getParent().addDataEditStep(ds
);
3280 boolean overlapping_only
= 1 == gd
.getNextChoiceIndex();
3281 switch (gd
.getNextChoiceIndex()) {
3283 Patch
.crosslink(selection
.getSelected(Patch
.class), overlapping_only
);
3286 Patch
.crosslink(lay
.getDisplayables(Patch
.class), overlapping_only
);
3289 for (final Layer la
: lay
.getParent().getLayers()) {
3290 Patch
.crosslink(la
.getDisplayables(Patch
.class), overlapping_only
);
3294 lay
.getParent().addDataEditStep(ds
);
3295 } else if (command
.equals("Calibration...")) {
3297 IJ
.run(canvas
.getFakeImagePlus(), "Properties...", "");
3298 } catch (RuntimeException re
) {
3299 Utils
.log2("Calibration dialog canceled.");
3301 } else if (command
.equals("Enhance contrast (selected images)...")) {
3302 final Layer la
= layer
;
3303 final HashSet
<Displayable
> ds
= new HashSet
<Displayable
>(la
.getParent().getDisplayables());
3304 la
.getParent().addDataEditStep(ds
);
3305 ArrayList al
= selection
.getSelected(Patch
.class);
3306 Bureaucrat burro
= getProject().getLoader().homogenizeContrast(al
);
3307 burro
.addPostTask(new Runnable() { public void run() {
3308 la
.getParent().addDataEditStep(ds
);
3310 } else if (command
.equals("Enhance contrast layer-wise...")) {
3311 // ask for range of layers
3312 final GenericDialog gd
= new GenericDialog("Choose range");
3313 Utils
.addLayerRangeChoices(Display
.this.layer
, gd
);
3315 if (gd
.wasCanceled()) return;
3316 java
.util
.List list
= layer
.getParent().getLayers().subList(gd
.getNextChoiceIndex(), gd
.getNextChoiceIndex() +1); // exclusive end
3317 Layer
[] la
= new Layer
[list
.size()];
3319 final HashSet
<Displayable
> ds
= new HashSet
<Displayable
>();
3320 for (final Layer l
: la
) ds
.addAll(l
.getDisplayables(Patch
.class));
3321 getLayerSet().addDataEditStep(ds
);
3322 Bureaucrat burro
= project
.getLoader().homogenizeContrast(la
);
3323 burro
.addPostTask(new Runnable() { public void run() {
3324 getLayerSet().addDataEditStep(ds
);
3326 } else if (command
.equals("Set Min and Max layer-wise...")) {
3327 Displayable active
= getActive();
3330 if (null != active
&& active
.getClass() == Patch
.class) {
3331 min
= ((Patch
)active
).getMin();
3332 max
= ((Patch
)active
).getMax();
3334 final GenericDialog gd
= new GenericDialog("Min and Max");
3335 gd
.addMessage("Set min and max to all images in the layer range");
3336 Utils
.addLayerRangeChoices(Display
.this.layer
, gd
);
3337 gd
.addNumericField("min: ", min
, 2);
3338 gd
.addNumericField("max: ", max
, 2);
3340 if (gd
.wasCanceled()) return;
3342 min
= gd
.getNextNumber();
3343 max
= gd
.getNextNumber();
3344 ArrayList
<Displayable
> al
= new ArrayList
<Displayable
>();
3345 for (final Layer la
: layer
.getParent().getLayers().subList(gd
.getNextChoiceIndex(), gd
.getNextChoiceIndex() +1)) { // exclusive end
3346 al
.addAll(la
.getDisplayables(Patch
.class));
3348 final HashSet
<Displayable
> ds
= new HashSet
<Displayable
>(al
);
3349 getLayerSet().addDataEditStep(ds
);
3350 Bureaucrat burro
= project
.getLoader().setMinAndMax(al
, min
, max
);
3351 burro
.addPostTask(new Runnable() { public void run() {
3352 getLayerSet().addDataEditStep(ds
);
3354 } else if (command
.equals("Set Min and Max (selected images)...")) {
3355 Displayable active
= getActive();
3358 if (null != active
&& active
.getClass() == Patch
.class) {
3359 min
= ((Patch
)active
).getMin();
3360 max
= ((Patch
)active
).getMax();
3362 final GenericDialog gd
= new GenericDialog("Min and Max");
3363 gd
.addMessage("Set min and max to all selected images");
3364 gd
.addNumericField("min: ", min
, 2);
3365 gd
.addNumericField("max: ", max
, 2);
3367 if (gd
.wasCanceled()) return;
3369 min
= gd
.getNextNumber();
3370 max
= gd
.getNextNumber();
3371 final HashSet
<Displayable
> ds
= new HashSet
<Displayable
>(selection
.getSelected(Patch
.class));
3372 getLayerSet().addDataEditStep(ds
);
3373 Bureaucrat burro
= project
.getLoader().setMinAndMax(selection
.getSelected(Patch
.class), min
, max
);
3374 burro
.addPostTask(new Runnable() { public void run() {
3375 getLayerSet().addDataEditStep(ds
);
3377 } else if (command
.equals("Duplicate")) {
3378 // only Patch and DLabel, i.e. Layer-only resident objects that don't exist in the Project Tree
3379 final HashSet
<Class
> accepted
= new HashSet
<Class
>();
3380 accepted
.add(Patch
.class);
3381 accepted
.add(DLabel
.class);
3382 final ArrayList
<Displayable
> originals
= new ArrayList
<Displayable
>();
3383 final ArrayList
<Displayable
> selected
= selection
.getSelected();
3384 for (final Displayable d
: selected
) {
3385 if (accepted
.contains(d
.getClass())) {
3389 if (originals
.size() > 0) {
3390 getLayerSet().addChangeTreesStep();
3391 for (final Displayable d
: originals
) {
3392 d
.getLayer().add(d
.clone());
3394 getLayerSet().addChangeTreesStep();
3395 } else if (selected
.size() > 0) {
3396 Utils
.log("Can only duplicate images and text labels.\nDuplicate *other* objects in the Project Tree.\n");
3398 } else if (command
.equals("Create subproject")) {
3399 Roi roi
= canvas
.getFakeImagePlus().getRoi();
3400 if (null == roi
) return; // the menu item is not active unless there is a ROI
3402 if (1 == layer
.getParent().size()) {
3403 first
= last
= layer
;
3405 GenericDialog gd
= new GenericDialog("Choose layer range");
3406 Utils
.addLayerRangeChoices(layer
, gd
);
3408 if (gd
.wasCanceled()) return;
3409 first
= layer
.getParent().getLayer(gd
.getNextChoiceIndex());
3410 last
= layer
.getParent().getLayer(gd
.getNextChoiceIndex());
3411 Utils
.log2("first, last: " + first
+ ", " + last
);
3413 Project sub
= getProject().createSubproject(roi
.getBounds(), first
, last
);
3414 final LayerSet subls
= sub
.getRootLayerSet();
3415 final Display d
= new Display(sub
, subls
.getLayer(0));
3416 SwingUtilities
.invokeLater(new Runnable() { public void run() {
3417 d
.canvas
.showCentered(new Rectangle(0, 0, (int)subls
.getLayerWidth(), (int)subls
.getLayerHeight()));
3419 } else if (command
.startsWith("Arealists as labels")) {
3420 GenericDialog gd
= new GenericDialog("Export labels");
3421 gd
.addSlider("Scale: ", 1, 100, 100);
3422 final String
[] options
= {"All area list", "Selected area lists"};
3423 gd
.addChoice("Export: ", options
, options
[0]);
3424 Utils
.addLayerRangeChoices(layer
, gd
);
3425 gd
.addCheckbox("Visible only", true);
3427 if (gd
.wasCanceled()) return;
3428 final float scale
= (float)(gd
.getNextNumber() / 100);
3429 java
.util
.List al
= 0 == gd
.getNextChoiceIndex() ? layer
.getParent().getZDisplayables(AreaList
.class) : selection
.getSelected(AreaList
.class);
3431 Utils
.log("No area lists found to export.");
3434 // 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?
3435 al
= (java
.util
.List
<Displayable
>) al
;
3437 int first
= gd
.getNextChoiceIndex();
3438 int last
= gd
.getNextChoiceIndex();
3439 boolean visible_only
= gd
.getNextBoolean();
3440 if (-1 != command
.indexOf("(amira)")) {
3441 AreaList
.exportAsLabels(al
, canvas
.getFakeImagePlus().getRoi(), scale
, first
, last
, visible_only
, true, true);
3442 } else if (-1 != command
.indexOf("(tif)")) {
3443 AreaList
.exportAsLabels(al
, canvas
.getFakeImagePlus().getRoi(), scale
, first
, last
, visible_only
, false, false);
3445 } else if (command
.equals("Project properties...")) {
3446 project
.adjustProperties();
3447 } else if (command
.equals("Release memory...")) {
3448 Bureaucrat
.createAndStart(new Worker("Releasing memory") {
3452 GenericDialog gd
= new GenericDialog("Release Memory");
3453 int max
= (int)(IJ
.maxMemory() / 1000000);
3454 gd
.addSlider("Megabytes: ", 0, max
, max
/2);
3456 if (!gd
.wasCanceled()) {
3457 int n_mb
= (int)gd
.getNextNumber();
3458 project
.getLoader().releaseToFit((long)n_mb
*1000000);
3460 } catch (Throwable e
) {
3467 } else if (command
.equals("Flush image cache")) {
3468 Loader
.releaseAllCaches();
3469 } else if (command
.equals("Regenerate all mipmaps")) {
3470 for (final Displayable d
: getLayerSet().getDisplayables(Patch
.class)) {
3471 d
.getProject().getLoader().regenerateMipMaps((Patch
) d
);
3474 Utils
.log2("Display: don't know what to do with command " + command
);
3479 /** Update in all displays the Transform for the given Displayable if it's selected. */
3480 static public void updateTransform(final Displayable displ
) {
3481 for (final Display d
: al_displays
) {
3482 if (d
.selection
.contains(displ
)) d
.selection
.updateTransform(displ
);
3486 /** Order the profiles of the parent profile_list by Z order, and fix the ProjectTree.*/
3488 private void fixZOrdering(Profile profile) {
3489 ProjectThing thing = project.findProjectThing(profile);
3490 if (null == thing) {
3491 Utils.log2("Display.fixZOrdering: null thing?");
3494 ((ProjectThing)thing.getParent()).fixZOrdering();
3495 project.getProjectTree().updateList(thing.getParent());
3499 /** The number of layers to scroll through with the wheel; 1 by default.*/
3500 public int getScrollStep() { return this.scroll_step
; }
3502 public void setScrollStep(int scroll_step
) {
3503 if (scroll_step
< 1) scroll_step
= 1;
3504 this.scroll_step
= scroll_step
;
3505 updateInDatabase("scroll_step");
3508 protected Bureaucrat
importImage() {
3509 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
3515 Rectangle srcRect
= canvas
.getSrcRect();
3516 int x
= srcRect
.x
+ srcRect
.width
/ 2;
3517 int y
= srcRect
.y
+ srcRect
.height
/ 2;
3518 Patch p
= project
.getLoader().importImage(project
, x
, y
);
3521 Utils
.showMessage("Could not open the image.");
3525 Display
.this.getLayerSet().addLayerContentStep(layer
);
3527 layer
.add(p
); // will add it to the proper Displays
3529 Display
.this.getLayerSet().addLayerContentStep(layer
);
3532 } catch (Exception e
) {
3538 return Bureaucrat
.createAndStart(worker
, getProject());
3541 protected Bureaucrat
importNextImage() {
3542 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
3547 Rectangle srcRect
= canvas
.getSrcRect();
3548 int x
= srcRect
.x
+ srcRect
.width
/ 2;// - imp.getWidth() / 2;
3549 int y
= srcRect
.y
+ srcRect
.height
/ 2;// - imp.getHeight()/ 2;
3550 Patch p
= project
.getLoader().importNextImage(project
, x
, y
);
3552 Utils
.showMessage("Could not open next image.");
3557 Display
.this.getLayerSet().addLayerContentStep(layer
);
3559 layer
.add(p
); // will add it to the proper Displays
3561 Display
.this.getLayerSet().addLayerContentStep(layer
);
3563 } catch (Exception e
) {
3569 return Bureaucrat
.createAndStart(worker
, getProject());
3573 /** Make the given channel have the given alpha (transparency). */
3574 public void setChannel(int c
, float alpha
) {
3575 int a
= (int)(255 * alpha
);
3576 int l
= (c_alphas
&0xff000000)>>24;
3577 int r
= (c_alphas
&0xff0000)>>16;
3578 int g
= (c_alphas
&0xff00)>>8;
3579 int b
= c_alphas
&0xff;
3582 // all to the given alpha
3583 c_alphas
= (l
<<24) + (r
<<16) + (g
<<8) + b
; // parenthesis are NECESSARY
3586 // modify only the red
3587 c_alphas
= (l
<<24) + (a
<<16) + (g
<<8) + b
;
3590 c_alphas
= (l
<<24) + (r
<<16) + (a
<<8) + b
;
3593 c_alphas
= (l
<<24) + (r
<<16) + (g
<<8) + a
;
3596 //Utils.log2("c_alphas: " + c_alphas);
3597 //canvas.setUpdateGraphics(true);
3598 canvas
.repaint(true);
3599 updateInDatabase("c_alphas");
3602 /** Set the channel as active and the others as inactive. */
3603 public void setActiveChannel(Channel channel
) {
3604 for (int i
=0; i
<4; i
++) {
3605 if (channel
!= channels
[i
]) channels
[i
].setActive(false);
3606 else channel
.setActive(true);
3608 Utils
.updateComponent(panel_channels
);
3609 transp_slider
.setValue((int)(channel
.getAlpha() * 100));
3612 public int getDisplayChannelAlphas() { return c_alphas
; }
3614 // rename this method and the getDisplayChannelAlphas ! They sound the same!
3615 public int getChannelAlphas() {
3616 return ((int)(channels
[0].getAlpha() * 255)<<24) + ((int)(channels
[1].getAlpha() * 255)<<16) + ((int)(channels
[2].getAlpha() * 255)<<8) + (int)(channels
[3].getAlpha() * 255);
3619 public int getChannelAlphasState() {
3620 return ((channels
[0].isSelected() ?
255 : 0)<<24)
3621 + ((channels
[1].isSelected() ?
255 : 0)<<16)
3622 + ((channels
[2].isSelected() ?
255 : 0)<<8)
3623 + (channels
[3].isSelected() ?
255 : 0);
3626 /** Show the layer in the front Display, or in a new Display if the front Display is showing a layer from a different LayerSet. */
3627 static public void showFront(final Layer layer
) {
3628 Display display
= front
;
3629 if (null == display
|| display
.layer
.getParent() != layer
.getParent()) {
3630 display
= new Display(layer
.getProject(), layer
, null); // gets set to front
3632 display
.setLayer(layer
);
3636 /** Show the given Displayable centered and selected. If select is false, the selection is cleared. */
3637 static public void showCentered(Layer layer
, Displayable displ
, boolean select
, boolean shift_down
) {
3638 // see if the given layer belongs to the layer set being displayed
3639 Display display
= front
; // to ensure thread consistency to some extent
3640 if (null == display
|| display
.layer
.getParent() != layer
.getParent()) {
3641 display
= new Display(layer
.getProject(), layer
, displ
); // gets set to front
3642 } else if (display
.layer
!= layer
) {
3643 display
.setLayer(layer
);
3646 if (!shift_down
) display
.selection
.clear();
3647 display
.selection
.add(displ
);
3649 display
.selection
.clear();
3651 display
.showCentered(displ
);
3654 private final void showCentered(final Displayable displ
) {
3655 if (null == displ
) return;
3656 SwingUtilities
.invokeLater(new Runnable() { public void run() {
3657 displ
.setVisible(true);
3658 Rectangle box
= displ
.getBoundingBox();
3659 if (0 == box
.width
|| 0 == box
.height
) {
3660 box
.width
= (int)layer
.getLayerWidth();
3661 box
.height
= (int)layer
.getLayerHeight();
3663 canvas
.showCentered(box
);
3664 scrollToShow(displ
);
3665 if (displ
instanceof ZDisplayable
) {
3666 // scroll to first layer that has a point
3667 ZDisplayable zd
= (ZDisplayable
)displ
;
3668 setLayer(zd
.getFirstLayer());
3673 /** Listen to interesting updates, such as the ColorPicker and updates to Patch objects. */
3674 public void imageUpdated(ImagePlus updated
) {
3675 // detect ColorPicker WARNING this will work even if the Display is not the window immediately active under the color picker.
3676 if (this == front
&& updated
instanceof ij
.plugin
.ColorPicker
) {
3677 if (null != active
&& project
.isInputEnabled()) {
3678 selection
.setColor(Toolbar
.getForegroundColor());
3679 Display
.repaint(front
.layer
, selection
.getBox(), 0);
3683 // $%#@!! LUT changes don't set the image as changed
3684 //if (updated instanceof PatchStack) {
3685 // updated.changes = 1
3688 //Utils.log2("imageUpdated: " + updated + " " + updated.getClass());
3690 /* // never gets called (?)
3691 // the above is overkill. Instead:
3692 if (updated instanceof PatchStack) {
3693 Patch p = ((PatchStack)updated).getCurrentPatch();
3694 ImageProcessor ip = updated.getProcessor();
3695 p.setMinAndMax(ip.getMin(), ip.getMax());
3696 Utils.log2("setting min and max: " + ip.getMin() + ", " + ip.getMax());
3697 project.getLoader().decacheAWT(p.getId()); // including level 0, which will be editable
3698 // on repaint, it will be recreated
3699 //((PatchStack)updated).decacheAll(); // so that it will repaint with a newly created image
3703 // detect LUT changes: DONE at PatchStack, which is the active (virtual) image
3704 //Utils.log2("calling decache for " + updated);
3705 //getProject().getLoader().decache(updated);
3708 public void imageClosed(ImagePlus imp
) {}
3709 public void imageOpened(ImagePlus imp
) {}
3711 /** Release memory captured by the offscreen images */
3712 static public void flushAll() {
3713 for (final Display d
: al_displays
) {
3721 static public Display
getFront() {
3725 static public void setCursorToAll(final Cursor c
) {
3726 for (final Display d
: al_displays
) {
3727 d
.frame
.setCursor(c
);
3731 protected void setCursor(Cursor c
) {
3735 /** Used by the Displayable to update the visibility checkbox in other Displays. */
3736 static protected void updateVisibilityCheckbox(final Layer layer
, final Displayable displ
, final Display calling_display
) {
3737 //LOCKS ALL //SwingUtilities.invokeLater(new Runnable() { public void run() {
3738 for (final Display d
: al_displays
) {
3739 if (d
== calling_display
) continue;
3740 if (d
.layer
.contains(displ
) || (displ
instanceof ZDisplayable
&& d
.layer
.getParent().contains((ZDisplayable
)displ
))) {
3741 DisplayablePanel dp
= d
.ht_panels
.get(displ
);
3742 if (null != dp
) dp
.updateVisibilityCheckbox();
3748 protected boolean isActiveWindow() {
3749 return frame
.isActive();
3752 /** Toggle user input; pan and zoom are always enabled though.*/
3753 static public void setReceivesInput(final Project project
, final boolean b
) {
3754 for (final Display d
: al_displays
) {
3755 if (d
.project
== project
) d
.canvas
.setReceivesInput(b
);
3759 /** Export the DTD that defines this object. */
3760 static public void exportDTD(StringBuffer sb_header
, HashSet hs
, String indent
) {
3761 if (hs
.contains("t2_display")) return; // TODO to avoid collisions the type shoud be in a namespace such as tm2:display
3762 hs
.add("t2_display");
3763 sb_header
.append(indent
).append("<!ELEMENT t2_display EMPTY>\n")
3764 .append(indent
).append("<!ATTLIST t2_display id NMTOKEN #REQUIRED>\n")
3765 .append(indent
).append("<!ATTLIST t2_display layer_id NMTOKEN #REQUIRED>\n")
3766 .append(indent
).append("<!ATTLIST t2_display x NMTOKEN #REQUIRED>\n")
3767 .append(indent
).append("<!ATTLIST t2_display y NMTOKEN #REQUIRED>\n")
3768 .append(indent
).append("<!ATTLIST t2_display magnification NMTOKEN #REQUIRED>\n")
3769 .append(indent
).append("<!ATTLIST t2_display srcrect_x NMTOKEN #REQUIRED>\n")
3770 .append(indent
).append("<!ATTLIST t2_display srcrect_y NMTOKEN #REQUIRED>\n")
3771 .append(indent
).append("<!ATTLIST t2_display srcrect_width NMTOKEN #REQUIRED>\n")
3772 .append(indent
).append("<!ATTLIST t2_display srcrect_height NMTOKEN #REQUIRED>\n")
3773 .append(indent
).append("<!ATTLIST t2_display scroll_step NMTOKEN #REQUIRED>\n")
3774 .append(indent
).append("<!ATTLIST t2_display c_alphas NMTOKEN #REQUIRED>\n")
3775 .append(indent
).append("<!ATTLIST t2_display c_alphas_state NMTOKEN #REQUIRED>\n")
3778 /** Export all displays of the given project as XML entries. */
3779 static public void exportXML(final Project project
, final Writer writer
, final String indent
, final Object any
) throws Exception
{
3780 final StringBuffer sb_body
= new StringBuffer();
3781 final String in
= indent
+ "\t";
3782 for (final Display d
: al_displays
) {
3783 if (d
.project
!= project
) continue;
3784 final Rectangle r
= d
.frame
.getBounds();
3785 final Rectangle srcRect
= d
.canvas
.getSrcRect();
3786 final double magnification
= d
.canvas
.getMagnification();
3787 sb_body
.append(indent
).append("<t2_display id=\"").append(d
.id
).append("\"\n")
3788 .append(in
).append("layer_id=\"").append(d
.layer
.getId()).append("\"\n")
3789 .append(in
).append("c_alphas=\"").append(d
.c_alphas
).append("\"\n")
3790 .append(in
).append("c_alphas_state=\"").append(d
.getChannelAlphasState()).append("\"\n")
3791 .append(in
).append("x=\"").append(r
.x
).append("\"\n")
3792 .append(in
).append("y=\"").append(r
.y
).append("\"\n")
3793 .append(in
).append("magnification=\"").append(magnification
).append("\"\n")
3794 .append(in
).append("srcrect_x=\"").append(srcRect
.x
).append("\"\n")
3795 .append(in
).append("srcrect_y=\"").append(srcRect
.y
).append("\"\n")
3796 .append(in
).append("srcrect_width=\"").append(srcRect
.width
).append("\"\n")
3797 .append(in
).append("srcrect_height=\"").append(srcRect
.height
).append("\"\n")
3798 .append(in
).append("scroll_step=\"").append(d
.scroll_step
).append("\"\n")
3800 sb_body
.append(indent
).append("/>\n");
3802 writer
.write(sb_body
.toString());
3805 static public void toolChanged(final String tool_name
) {
3806 Utils
.log2("tool name: " + tool_name
);
3807 if (!tool_name
.equals("ALIGN")) {
3808 for (final Display d
: al_displays
) {
3809 d
.layer
.getParent().cancelAlign();
3814 static public void toolChanged(final int tool
) {
3815 //Utils.log2("int tool is " + tool);
3816 if (ProjectToolbar
.PEN
== tool
) {
3817 // erase bounding boxes
3818 for (final Display d
: al_displays
) {
3819 if (null != d
.active
) d
.repaint(d
.layer
, d
.selection
.getBox(), 2);
3822 if (null != front
) {
3823 WindowManager
.setTempCurrentImage(front
.canvas
.getFakeImagePlus());
3827 public Selection
getSelection() {
3831 public boolean isSelected(Displayable d
) {
3832 return selection
.contains(d
);
3835 static public void updateSelection() {
3836 Display
.updateSelection(null);
3838 static public void updateSelection(final Display calling
) {
3839 final HashSet hs
= new HashSet();
3840 for (final Display d
: al_displays
) {
3841 if (hs
.contains(d
.layer
)) continue;
3843 if (null == d
|| null == d
.selection
) {
3844 Utils
.log2("d is : "+ d
+ " d.selection is " + d
.selection
);
3846 d
.selection
.update(); // recomputes box
3848 if (d
!= calling
) { // TODO this is so dirty!
3849 if (d
.selection
.getNLinked() > 1) d
.canvas
.setUpdateGraphics(true); // this is overkill anyway
3850 d
.canvas
.repaint(d
.selection
.getLinkedBox(), Selection
.PADDING
);
3851 d
.navigator
.repaint(true); // everything
3856 static public void clearSelection(final Layer layer
) {
3857 for (final Display d
: al_displays
) {
3858 if (d
.layer
== layer
) d
.selection
.clear();
3861 static public void clearSelection() {
3862 for (final Display d
: al_displays
) {
3863 d
.selection
.clear();
3867 private void setTempCurrentImage() {
3868 WindowManager
.setCurrentWindow(canvas
.getFakeImagePlus().getWindow(), true);
3869 WindowManager
.setTempCurrentImage(canvas
.getFakeImagePlus());
3872 /** Check if any display will paint the given Displayable at the given magnification. */
3873 static public boolean willPaint(final Displayable displ
, final double magnification
) {
3874 Rectangle box
= null; ;
3875 for (final Display d
: al_displays
) {
3876 /* // Can no longer do this check, because 'magnification' is now affected by the Displayable AffineTransform! And thus it would not paint after the prePaint.
3877 if (Math.abs(d.canvas.getMagnification() - magnification) > 0.00000001) {
3881 if (null == box
) box
= displ
.getBoundingBox(null);
3882 if (d
.canvas
.getSrcRect().intersects(box
)) {
3889 public void hideDeselected(final boolean not_images
) {
3891 final ArrayList all
= layer
.getParent().getZDisplayables(); // a copy
3892 all
.addAll(layer
.getDisplayables());
3893 all
.removeAll(selection
.getSelected());
3894 if (not_images
) all
.removeAll(layer
.getDisplayables(Patch
.class));
3895 for (final Displayable d
: (ArrayList
<Displayable
>)all
) {
3896 if (d
.isVisible()) d
.setVisible(false);
3898 Display
.update(layer
);
3901 /** Cleanup internal lists that may contain the given Displayable. */
3902 static public void flush(final Displayable displ
) {
3903 for (final Display d
: al_displays
) {
3904 d
.selection
.removeFromPrev(displ
);
3908 public void resizeCanvas() {
3909 GenericDialog gd
= new GenericDialog("Resize LayerSet");
3910 gd
.addNumericField("new width: ", layer
.getLayerWidth(), 3);
3911 gd
.addNumericField("new height: ",layer
.getLayerHeight(),3);
3912 gd
.addChoice("Anchor: ", LayerSet
.ANCHORS
, LayerSet
.ANCHORS
[7]);
3914 if (gd
.wasCanceled()) return;
3915 double new_width
= gd
.getNextNumber();
3916 double new_height
=gd
.getNextNumber();
3917 layer
.getParent().setDimensions(new_width
, new_height
, gd
.getNextChoiceIndex()); // will complain and prevent cropping existing Displayable objects
3921 // To record layer changes -- but it's annoying, this is visualization not data.
3922 static class DoSetLayer implements DoStep {
3923 final Display display;
3925 DoSetLayer(final Display display) {
3926 this.display = display;
3927 this.layer = display.layer;
3929 public Displayable getD() { return null; }
3930 public boolean isEmpty() { return false; }
3931 public boolean apply(final int action) {
3932 display.setLayer(layer);
3934 public boolean isIdenticalTo(final Object ob) {
3935 if (!ob instanceof DoSetLayer) return false;
3936 final DoSetLayer dsl = (DoSetLayer) ob;
3937 return dsl.display == this.display && dsl.layer == this.layer;
3942 protected void duplicateLinkAndSendTo(final Displayable active
, final int position
, final Layer other_layer
) {
3943 if (null == active
|| !(active
instanceof Profile
)) return;
3944 if (active
.getLayer() == other_layer
) return; // can't do that!
3945 Profile profile
= project
.getProjectTree().duplicateChild((Profile
)active
, position
, other_layer
);
3946 if (null == profile
) return;
3947 active
.link(profile
);
3948 other_layer
.add(profile
);
3949 slt
.setAndWait(other_layer
);
3950 selection
.add(profile
);
3953 private final HashMap
<Color
,Layer
> layer_channels
= new HashMap
<Color
,Layer
>();
3954 private final TreeMap
<Integer
,LayerPanel
> layer_alpha
= new TreeMap
<Integer
,LayerPanel
>();
3956 /** Remove all red/blue coloring of layers, and repaint canvas. */
3957 protected void resetLayerColors() {
3958 synchronized (layer_channels
) {
3959 for (final Layer l
: new ArrayList
<Layer
>(layer_channels
.values())) { // avoid concurrent modification exception
3960 final LayerPanel lp
= layer_panels
.get(l
);
3961 lp
.setColor(Color
.white
);
3962 setColorChannel(lp
.layer
, Color
.white
);
3963 lp
.slider
.setEnabled(true);
3965 layer_channels
.clear();
3970 /** Set all layer alphas to zero, and repaint canvas. */
3971 protected void resetLayerAlphas() {
3972 synchronized (layer_channels
) {
3973 for (final LayerPanel lp
: new ArrayList
<LayerPanel
>(layer_alpha
.values())) {
3976 layer_alpha
.clear(); // should have already been cleared
3981 /** Add to layer_alpha table, or remove if alpha is zero. */
3982 protected void storeLayerAlpha(final LayerPanel lp
, final float a
) {
3983 synchronized (layer_channels
) {
3984 if (M
.equals(0, a
)) {
3985 layer_alpha
.remove(lp
.layer
.getParent().indexOf(lp
.layer
));
3987 layer_alpha
.put(lp
.layer
.getParent().indexOf(lp
.layer
), lp
);
3992 static protected final int REPAINT_SINGLE_LAYER
= 0;
3993 static protected final int REPAINT_MULTI_LAYER
= 1;
3994 static protected final int REPAINT_RGB_LAYER
= 2;
3996 /** Sets the values atomically, returns the painting mode. */
3997 protected int getPaintMode(final HashMap
<Color
,Layer
> hm
, final ArrayList
<LayerPanel
> list
) {
3998 synchronized (layer_channels
) {
3999 if (layer_channels
.size() > 0) {
4000 hm
.putAll(layer_channels
);
4001 hm
.put(Color
.green
, this.layer
);
4002 return REPAINT_RGB_LAYER
;
4004 list
.addAll(layer_alpha
.values());
4005 final int len
= list
.size();
4006 if (len
> 1) return REPAINT_MULTI_LAYER
;
4008 if (list
.get(0).layer
== this.layer
) return REPAINT_SINGLE_LAYER
; // normal mode
4009 return REPAINT_MULTI_LAYER
;
4011 return REPAINT_SINGLE_LAYER
;
4015 /** Set a layer to be painted as a specific color channel in the canvas.
4016 * Only Color.red and Color.blue are accepted.
4017 * Color.green is reserved for the current layer. */
4018 protected void setColorChannel(final Layer layer
, final Color color
) {
4019 synchronized (layer_channels
) {
4020 if (Color
.white
== color
) {
4022 for (final Iterator
<Layer
> it
= layer_channels
.values().iterator(); it
.hasNext(); ) {
4023 if (it
.next() == layer
) {
4029 } else if (Color
.red
== color
|| Color
.blue
== color
) {
4030 // Reset current of that color, if any, to white
4031 final Layer l
= layer_channels
.remove(color
);
4032 if (null != l
) layer_panels
.get(l
).setColor(Color
.white
);
4033 // Replace or set new
4034 layer_channels
.put(color
, layer
);
4038 Utils
.log2("Trying to set unacceptable color for layer " + layer
+ " : " + color
);
4040 // enable/disable sliders
4041 final boolean b
= 0 == layer_channels
.size();
4042 for (final LayerPanel lp
: layer_panels
.values()) lp
.slider
.setEnabled(b
);
4044 this.canvas
.repaint(true);
4047 static public final void updateComponentTreeUI() {
4049 for (final Display d
: al_displays
) SwingUtilities
.updateComponentTreeUI(d
.frame
);
4050 } catch (Exception e
) {