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
.io
.OpenDialog
;
28 import ij
.measure
.Calibration
;
29 import ini
.trakem2
.Project
;
30 import ini
.trakem2
.ControlWindow
;
31 import ini
.trakem2
.persistence
.DBObject
;
32 import ini
.trakem2
.persistence
.Loader
;
33 import ini
.trakem2
.utils
.IJError
;
34 import ini
.trakem2
.imaging
.PatchStack
;
35 import ini
.trakem2
.imaging
.Blending
;
36 import ini
.trakem2
.imaging
.Segmentation
;
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
.*;
47 import ini
.trakem2
.display
.graphics
.*;
50 import javax
.swing
.event
.*;
52 import mpicbg
.trakem2
.align
.AlignTask
;
55 import java
.awt
.geom
.AffineTransform
;
56 import java
.awt
.geom
.Area
;
57 import java
.awt
.geom
.Line2D
;
58 import java
.awt
.event
.*;
60 import java
.util
.List
;
61 import java
.lang
.reflect
.Field
;
62 import java
.lang
.reflect
.Method
;
63 import java
.io
.Writer
;
65 import java
.util
.concurrent
.Future
;
67 import lenscorrection
.DistortionCorrectionTask
;
68 import mpicbg
.models
.PointMatch
;
69 import mpicbg
.trakem2
.transform
.AffineModel3D
;
71 /** A Display is a class to show a Layer and enable mouse and keyboard manipulation of all its components. */
72 public final class Display
extends DBObject
implements ActionListener
, ImageListener
{
74 /** The Layer this Display is showing. */
77 private Displayable active
= null;
78 /** All selected Displayable objects, including the active one. */
79 final private Selection selection
= new Selection(this);
82 private JTabbedPane tabs
;
83 private Hashtable
<Class
,JScrollPane
> ht_tabs
;
84 private JScrollPane scroll_patches
;
85 private JPanel panel_patches
;
86 private JScrollPane scroll_profiles
;
87 private JPanel panel_profiles
;
88 private JScrollPane scroll_zdispl
;
89 private JPanel panel_zdispl
;
90 private JScrollPane scroll_channels
;
91 private JPanel panel_channels
;
92 private JScrollPane scroll_labels
;
93 private JPanel panel_labels
;
95 private JPanel panel_layers
;
96 private JScrollPane scroll_layers
;
97 private Hashtable
<Layer
,LayerPanel
> layer_panels
= new Hashtable
<Layer
,LayerPanel
>();
99 private JSlider transp_slider
;
100 private DisplayNavigator navigator
;
101 private JScrollBar scroller
;
103 private DisplayCanvas canvas
; // WARNING this is an AWT component, since it extends ImageCanvas
104 private JPanel canvas_panel
; // and this is a workaround, to better (perhaps) integrate the awt canvas inside a JSplitPane
105 private JSplitPane split
;
107 private JPopupMenu popup
= null;
109 private ToolbarPanel toolbar_panel
= null;
111 private AreaList
.PaintParametersGUI tool_options_panel
= null;
113 /** Contains the packed alphas of every channel. */
114 private int c_alphas
= 0xffffffff; // all 100 % visible
115 private Channel
[] channels
;
117 private Hashtable
<Displayable
,DisplayablePanel
> ht_panels
= new Hashtable
<Displayable
,DisplayablePanel
>();
119 /** Handle drop events, to insert image files. */
120 private DNDInsertImage dnd
;
122 private boolean size_adjusted
= false;
124 private int scroll_step
= 1;
126 /** Keep track of all existing Display objects. */
127 static private Vector
<Display
> al_displays
= new Vector
<Display
>();
128 /** The currently focused Display, if any. */
129 static private Display front
= null;
131 /** Displays to open when all objects have been reloaded from the database. */
132 static private final Hashtable ht_later
= new Hashtable();
134 /** A thread to handle user actions, for example an event sent from a popup menu. */
135 protected final Dispatcher dispatcher
= new Dispatcher("Display GUI Updater");
137 static private WindowAdapter window_listener
= new WindowAdapter() {
138 /** Unregister the closed Display. */
139 public void windowClosing(WindowEvent we
) {
140 final Object source
= we
.getSource();
141 for (Iterator it
= al_displays
.iterator(); it
.hasNext(); ) {
142 Display d
= (Display
)it
.next();
143 if (source
== d
.frame
) {
145 if (d
== front
) front
= null;
146 d
.remove(false); //calls destroy
151 /** Set the source Display as front. */
152 public void windowActivated(WindowEvent we
) {
153 // find which was it to make it be the front
154 ImageJ ij
= IJ
.getInstance();
155 if (null != ij
&& ij
.quitting()) return;
156 final Object source
= we
.getSource();
157 for (final Display d
: al_displays
) {
158 if (source
== d
.frame
) {
161 ProjectToolbar
.setProjectToolbar();
162 // now, select the layer in the LayerTree
163 front
.getProject().select(front
.layer
);
164 // finally, set the virtual ImagePlus that ImageJ will see
165 d
.setTempCurrentImage();
166 // copied from ij.gui.ImageWindow, with modifications
167 if (IJ
.isMacintosh() && IJ
.getInstance()!=null) {
168 IJ
.wait(10); // may be needed for Java 1.4 on OS X
169 d
.frame
.setMenuBar(Menus
.getMenuBar());
174 // else, restore the ImageJ toolbar for non-project images
175 //if (!source.equals(IJ.getInstance())) {
176 // ProjectToolbar.setImageJToolbar();
179 /** Restore the ImageJ toolbar */
180 public void windowDeactivated(WindowEvent we
) {
181 // 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();
183 /** Call a pack() when the window is maximized to fit the canvas correctly. */
184 public void windowStateChanged(WindowEvent we
) {
185 final Object source
= we
.getSource();
186 for (final Display d
: al_displays
) {
187 if (source
!= d
.frame
) continue;
194 static private MouseListener frame_mouse_listener
= new MouseAdapter() {
195 public void mouseReleased(MouseEvent me
) {
196 Object source
= me
.getSource();
197 for (final Display d
: al_displays
) {
198 if (d
.frame
== source
) {
199 if (d
.size_adjusted
) {
201 d
.size_adjusted
= false;
202 Utils
.log2("mouse released on JFrame");
210 private int last_frame_state
= frame
.NORMAL
;
212 // THIS WHOLE SYSTEM OF LISTENERS IS BROKEN:
213 // * when zooming in, the window growths in width a few pixels.
214 // * when enlarging the window quickly, the canvas is not resized as large as it should.
215 // -- the whole problem: swing threading, which I am not handling properly. It's hard.
216 static private ComponentListener component_listener
= new ComponentAdapter() {
217 public void componentResized(ComponentEvent ce
) {
218 final Display d
= getDisplaySource(ce
);
220 d
.size_adjusted
= true; // works in combination with mouseReleased to call pack(), avoiding infinite loops.
222 d
.navigator
.repaint(false); // upate srcRect red frame position/size
223 int frame_state
= d
.frame
.getExtendedState();
224 if (frame_state
!= d
.last_frame_state
) { // this setup avoids infinite loops (for pack() calls componentResized as well
225 d
.last_frame_state
= frame_state
;
226 if (d
.frame
.ICONIFIED
!= frame_state
) d
.pack();
230 public void componentMoved(ComponentEvent ce
) {
231 Display d
= getDisplaySource(ce
);
232 if (null != d
) d
.updateInDatabase("position");
234 private Display
getDisplaySource(ComponentEvent ce
) {
235 final Object source
= ce
.getSource();
236 for (final Display d
: al_displays
) {
237 if (source
== d
.frame
) {
245 static private ChangeListener tabs_listener
= new ChangeListener() {
246 /** Listen to tab changes. */
247 public void stateChanged(final ChangeEvent ce
) {
248 final Object source
= ce
.getSource();
249 for (final Display d
: al_displays
) {
250 if (source
== d
.tabs
) {
251 d
.dispatcher
.exec(new Runnable() { public void run() {
252 // creating tabs fires the event!!!
253 if (null == d
.frame
|| null == d
.canvas
) return;
254 final Container tab
= (Container
)d
.tabs
.getSelectedComponent();
255 if (tab
== d
.scroll_channels
) {
256 // find active channel if any
257 for (int i
=0; i
<d
.channels
.length
; i
++) {
258 if (d
.channels
[i
].isActive()) {
259 d
.transp_slider
.setValue((int)(d
.channels
[i
].getAlpha() * 100));
266 int count = tab.getComponentCount();
267 if (0 == count || (1 == count && tab.getComponent(0).getClass().equals(JLabel.class))) {
268 */ // 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)
273 if (tab
== d
.scroll_zdispl
) {
274 label
= "Z-space objects";
275 al
= d
.layer
.getParent().getZDisplayables();
277 } else if (tab
== d
.scroll_patches
) {
279 al
= d
.layer
.getDisplayables(Patch
.class);
281 } else if (tab
== d
.scroll_labels
) {
283 al
= d
.layer
.getDisplayables(DLabel
.class);
285 } else if (tab
== d
.scroll_profiles
) {
287 al
= d
.layer
.getDisplayables(Profile
.class);
288 p
= d
.panel_profiles
;
289 } else if (tab
== d
.scroll_layers
) {
294 d
.updateTab(p
, label
, al
);
295 //Utils.updateComponent(d.tabs.getSelectedComponent());
296 //Utils.log2("updated tab: " + p + " with " + al.size() + " objects.");
299 if (null != d
.active
) {
300 // set the transp slider to the alpha value of the active Displayable if any
301 d
.transp_slider
.setValue((int)(d
.active
.getAlpha() * 100));
302 DisplayablePanel dp
= d
.ht_panels
.get(d
.active
);
303 if (null != dp
) dp
.setActive(true);
313 private final ScrollLayerListener scroller_listener
= new ScrollLayerListener();
315 private class ScrollLayerListener
implements AdjustmentListener
{
317 public void adjustmentValueChanged(final AdjustmentEvent ae
) {
318 final int index
= scroller
.getValue();
319 slt
.set(layer
.getParent().getLayer(index
));
323 private final SetLayerThread slt
= new SetLayerThread();
325 private class SetLayerThread
extends Thread
{
327 private boolean go
= true;
329 private final Lock lock
= new Lock();
332 setPriority(Thread
.NORM_PRIORITY
);
337 public final void set(final Layer layer
) {
338 synchronized (lock
) {
341 synchronized (this) {
346 // 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.)
347 final void setAndWait(final Layer layer
) {
349 Display
.this.setLayer(layer
);
350 Display
.this.updateInDatabase("layer_id");
356 while (null == this.layer
) {
357 synchronized (this) {
358 try { wait(); } catch (InterruptedException ie
) {}
362 synchronized (lock
) {
367 if (!go
) return; // after nullifying layer
378 /** Creates a new Display with adjusted magnification to fit in the screen. */
379 static public void createDisplay(final Project project
, final Layer layer
) {
380 SwingUtilities
.invokeLater(new Runnable() { public void run() {
381 Display display
= new Display(project
, layer
);
382 Dimension screen
= Toolkit
.getDefaultToolkit().getScreenSize();
383 Rectangle srcRect
= new Rectangle(0, 0, (int)layer
.getLayerWidth(), (int)layer
.getLayerHeight());
384 double mag
= screen
.width
/ layer
.getLayerWidth();
385 if (mag
* layer
.getLayerHeight() > screen
.height
) mag
= screen
.height
/ layer
.getLayerHeight();
386 mag
= display
.canvas
.getLowerZoomLevel2(mag
);
387 if (mag
> 1.0) mag
= 1.0;
388 //display.getCanvas().setup(mag, srcRect); // would call pack() at the wrong time!
389 // ... so instead: manually
390 display
.getCanvas().setMagnification(mag
);
391 display
.getCanvas().setSrcRect(srcRect
.x
, srcRect
.y
, srcRect
.width
, srcRect
.height
);
392 display
.getCanvas().setDrawingSize((int)Math
.ceil(srcRect
.width
* mag
), (int)Math
.ceil(srcRect
.height
* mag
));
394 display
.updateFrameTitle(layer
);
395 ij
.gui
.GUI
.center(display
.frame
);
396 display
.frame
.pack();
400 /** A new Display from scratch, to show the given Layer. */
401 public Display(Project project
, final Layer layer
) {
404 makeGUI(layer
, null);
405 ImagePlus
.addImageListener(this);
407 this.layer
= layer
; // after, or it doesn't update properly
408 al_displays
.add(this);
412 /** For reconstruction purposes. The Display will be stored in the ht_later.*/
413 public Display(Project project
, long id
, Layer layer
, Object
[] props
) {
415 synchronized (ht_later
) {
416 Display
.ht_later
.put(this, props
);
421 /** Open a new Display centered around the given Displayable. */
422 public Display(Project project
, Layer layer
, Displayable displ
) {
426 makeGUI(layer
, null);
427 ImagePlus
.addImageListener(this);
429 this.layer
= layer
; // after set layer!
430 al_displays
.add(this);
434 /** Reconstruct a Display from an XML entry, to be opened when everything is ready. */
435 public Display(Project project
, long id
, Layer layer
, HashMap ht_attributes
) {
438 Utils
.log2("Display: need a non-null Layer for id=" + id
);
441 Rectangle srcRect
= new Rectangle(0, 0, (int)layer
.getLayerWidth(), (int)layer
.getLayerHeight());
442 double magnification
= 0.25;
443 Point p
= new Point(0, 0);
444 int c_alphas
= 0xffffffff;
445 int c_alphas_state
= 0xffffffff;
446 for (Iterator it
= ht_attributes
.entrySet().iterator(); it
.hasNext(); ) {
447 Map
.Entry entry
= (Map
.Entry
)it
.next();
448 String key
= (String
)entry
.getKey();
449 String data
= (String
)entry
.getValue();
450 if (key
.equals("srcrect_x")) { // reflection! Reflection!
451 srcRect
.x
= Integer
.parseInt(data
);
452 } else if (key
.equals("srcrect_y")) {
453 srcRect
.y
= Integer
.parseInt(data
);
454 } else if (key
.equals("srcrect_width")) {
455 srcRect
.width
= Integer
.parseInt(data
);
456 } else if (key
.equals("srcrect_height")) {
457 srcRect
.height
= Integer
.parseInt(data
);
458 } else if (key
.equals("magnification")) {
459 magnification
= Double
.parseDouble(data
);
460 } else if (key
.equals("x")) {
461 p
.x
= Integer
.parseInt(data
);
462 } else if (key
.equals("y")) {
463 p
.y
= Integer
.parseInt(data
);
464 } else if (key
.equals("c_alphas")) {
466 c_alphas
= Integer
.parseInt(data
);
467 } catch (Exception ex
) {
468 c_alphas
= 0xffffffff;
470 } else if (key
.equals("c_alphas_state")) {
472 c_alphas_state
= Integer
.parseInt(data
);
473 } catch (Exception ex
) {
475 c_alphas_state
= 0xffffffff;
477 } else if (key
.equals("scroll_step")) {
479 setScrollStep(Integer
.parseInt(data
));
480 } catch (Exception ex
) {
485 // TODO the above is insecure, in that data is not fully checked to be within bounds.
487 Object
[] props
= new Object
[]{p
, new Double(magnification
), srcRect
, new Long(layer
.getId()), new Integer(c_alphas
), new Integer(c_alphas_state
)};
488 synchronized (ht_later
) {
489 Display
.ht_later
.put(this, props
);
494 /** After reloading a project from the database, open the Displays that the project had. */
495 static public Bureaucrat
openLater() {
496 final Hashtable ht_later_local
;
497 synchronized (ht_later
) {
498 if (0 == ht_later
.size()) return null;
499 ht_later_local
= new Hashtable(ht_later
);
500 ht_later
.keySet().removeAll(ht_later_local
.keySet());
502 final Worker worker
= new Worker
.Task("Opening displays") {
505 Thread
.sleep(300); // waiting for Swing
507 for (Enumeration e
= ht_later_local
.keys(); e
.hasMoreElements(); ) {
508 final Display d
= (Display
)e
.nextElement();
509 front
= d
; // must be set before repainting any ZDisplayable!
510 Object
[] props
= (Object
[])ht_later_local
.get(d
);
511 if (ControlWindow
.isGUIEnabled()) d
.makeGUI(d
.layer
, props
);
512 d
.setLayerLater(d
.layer
, d
.layer
.get(((Long
)props
[3]).longValue())); //important to do it after makeGUI
513 if (!ControlWindow
.isGUIEnabled()) continue;
514 ImagePlus
.addImageListener(d
);
516 d
.updateFrameTitle(d
.layer
);
517 // 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
518 if (d
.canvas
.getMagnification() > 0.499) {
519 SwingUtilities
.invokeLater(new Runnable() { public void run() {
521 d
.project
.getLoader().setChanged(false);
522 Utils
.log2("A set to false");
525 d
.project
.getLoader().setChanged(false);
526 Utils
.log2("B set to false");
528 if (null != front
) front
.getProject().select(front
.layer
);
530 } catch (Throwable t
) {
535 return Bureaucrat
.createAndStart(worker
, ((Display
)ht_later_local
.keySet().iterator().next()).getProject()); // gets the project from the first Display
538 private void makeGUI(final Layer layer
, final Object
[] props
) {
542 Rectangle srcRect
= null;
545 mag
= ((Double
)props
[1]).doubleValue();
546 srcRect
= (Rectangle
)props
[2];
549 // transparency slider
550 this.transp_slider
= new JSlider(javax
.swing
.SwingConstants
.HORIZONTAL
, 0, 100, 100);
551 this.transp_slider
.setBackground(Color
.white
);
552 this.transp_slider
.setMinimumSize(new Dimension(250, 20));
553 this.transp_slider
.setMaximumSize(new Dimension(250, 20));
554 this.transp_slider
.setPreferredSize(new Dimension(250, 20));
555 TransparencySliderListener tsl
= new TransparencySliderListener();
556 this.transp_slider
.addChangeListener(tsl
);
557 this.transp_slider
.addMouseListener(tsl
);
558 for (final KeyListener kl
: this.transp_slider
.getKeyListeners()) {
559 this.transp_slider
.removeKeyListener(kl
);
562 // Tabbed pane on the left
563 this.tabs
= new JTabbedPane();
564 this.tabs
.setMinimumSize(new Dimension(250, 300));
565 this.tabs
.setBackground(Color
.white
);
566 this.tabs
.addChangeListener(tabs_listener
);
569 this.panel_patches
= makeTabPanel();
570 this.scroll_patches
= makeScrollPane(panel_patches
);
571 this.tabs
.add("Patches", scroll_patches
);
574 this.panel_profiles
= makeTabPanel();
575 this.scroll_profiles
= makeScrollPane(panel_profiles
);
576 this.tabs
.add("Profiles", scroll_profiles
);
579 this.panel_zdispl
= makeTabPanel();
580 this.scroll_zdispl
= makeScrollPane(panel_zdispl
);
581 this.tabs
.add("Z space", scroll_zdispl
);
584 this.panel_channels
= makeTabPanel();
585 this.scroll_channels
= makeScrollPane(panel_channels
);
586 this.channels
= new Channel
[4];
587 this.channels
[0] = new Channel(this, Channel
.MONO
);
588 this.channels
[1] = new Channel(this, Channel
.RED
);
589 this.channels
[2] = new Channel(this, Channel
.GREEN
);
590 this.channels
[3] = new Channel(this, Channel
.BLUE
);
591 //this.panel_channels.add(this.channels[0]);
592 this.panel_channels
.add(this.channels
[1]);
593 this.panel_channels
.add(this.channels
[2]);
594 this.panel_channels
.add(this.channels
[3]);
595 this.tabs
.add("Opacity", scroll_channels
);
598 this.panel_labels
= makeTabPanel();
599 this.scroll_labels
= makeScrollPane(panel_labels
);
600 this.tabs
.add("Labels", scroll_labels
);
603 this.panel_layers
= makeTabPanel();
604 this.scroll_layers
= makeScrollPane(panel_layers
);
605 recreateLayerPanels(layer
);
606 this.scroll_layers
.addMouseWheelListener(canvas
);
607 this.tabs
.add("Layers", scroll_layers
);
609 this.ht_tabs
= new Hashtable
<Class
,JScrollPane
>();
610 this.ht_tabs
.put(Patch
.class, scroll_patches
);
611 this.ht_tabs
.put(Profile
.class, scroll_profiles
);
612 this.ht_tabs
.put(ZDisplayable
.class, scroll_zdispl
);
613 this.ht_tabs
.put(AreaList
.class, scroll_zdispl
);
614 this.ht_tabs
.put(Pipe
.class, scroll_zdispl
);
615 this.ht_tabs
.put(Polyline
.class, scroll_zdispl
);
616 this.ht_tabs
.put(Ball
.class, scroll_zdispl
);
617 this.ht_tabs
.put(Dissector
.class, scroll_zdispl
);
618 this.ht_tabs
.put(DLabel
.class, scroll_labels
);
619 this.ht_tabs
.put(Stack
.class, scroll_zdispl
);
620 // channels not included
621 // layers not included
624 this.navigator
= new DisplayNavigator(this, layer
.getLayerWidth(), layer
.getLayerHeight());
625 // Layer scroller (to scroll slices)
626 int extent
= (int)(250.0 / layer
.getParent().size());
627 if (extent
< 10) extent
= 10;
628 this.scroller
= new JScrollBar(JScrollBar
.HORIZONTAL
);
629 updateLayerScroller(layer
);
630 this.scroller
.addAdjustmentListener(scroller_listener
);
633 // Left panel, contains the transp slider, the tabbed pane, the navigation panel and the layer scroller
634 JPanel left
= new JPanel();
635 left
.setBackground(Color
.white
);
636 BoxLayout left_layout
= new BoxLayout(left
, BoxLayout
.Y_AXIS
);
637 left
.setLayout(left_layout
);
638 toolbar_panel
= new ToolbarPanel();
639 tool_options_panel
= new AreaList
.PaintParametersGUI();
640 tool_options_panel
.setBackground(Color
.white
);
641 left
.add(toolbar_panel
);
642 left
.add(tool_options_panel
); // empty
643 left
.add(transp_slider
);
649 this.canvas
= new DisplayCanvas(this, (int)Math
.ceil(layer
.getLayerWidth()), (int)Math
.ceil(layer
.getLayerHeight()));
650 this.canvas_panel
= new JPanel();
651 GridBagLayout gb
= new GridBagLayout();
652 this.canvas_panel
.setLayout(gb
);
653 GridBagConstraints c
= new GridBagConstraints();
654 c
.fill
= GridBagConstraints
.BOTH
;
655 c
.anchor
= GridBagConstraints
.NORTHWEST
;
656 gb
.setConstraints(this.canvas_panel
, c
);
657 gb
.setConstraints(this.canvas
, c
);
659 // prevent new Displays from screweing up if input is globally disabled
660 if (!project
.isInputEnabled()) this.canvas
.setReceivesInput(false);
662 this.canvas_panel
.add(canvas
);
664 this.navigator
.addMouseWheelListener(canvas
);
666 this.transp_slider
.addKeyListener(canvas
);
668 // Split pane to contain everything
669 this.split
= new JSplitPane(JSplitPane
.HORIZONTAL_SPLIT
, left
, canvas_panel
);
670 this.split
.setOneTouchExpandable(true); // NOT present in all L&F (?)
671 this.split
.setBackground(Color
.white
);
674 gb
.setConstraints(split
.getRightComponent(), c
);
676 // JFrame to show the split pane
677 this.frame
= ControlWindow
.createJFrame(layer
.toString());
678 this.frame
.setBackground(Color
.white
);
679 this.frame
.getContentPane().setBackground(Color
.white
);
680 if (IJ
.isMacintosh() && IJ
.getInstance()!=null) {
681 IJ
.wait(10); // may be needed for Java 1.4 on OS X
682 this.frame
.setMenuBar(ij
.Menus
.getMenuBar());
684 this.frame
.addWindowListener(window_listener
);
685 this.frame
.addComponentListener(component_listener
);
686 this.frame
.getContentPane().add(split
);
687 this.frame
.addMouseListener(frame_mouse_listener
);
688 //doesn't exist//this.frame.setMinimumSize(new Dimension(270, 600));
692 canvas
.setup(mag
, srcRect
);
693 // restore visibility of each channel
694 int cs
= ((Integer
)props
[5]).intValue(); // aka c_alphas_state
695 int[] sel
= new int[4];
696 sel
[0] = ((cs
&0xff000000)>>24);
697 sel
[1] = ((cs
&0xff0000)>>16);
698 sel
[2] = ((cs
&0xff00)>>8);
700 // restore channel alphas
701 this.c_alphas
= ((Integer
)props
[4]).intValue();
702 channels
[0].setAlpha( (float)((c_alphas
&0xff000000)>>24) / 255.0f
, 0 != sel
[0]);
703 channels
[1].setAlpha( (float)((c_alphas
&0xff0000)>>16) / 255.0f
, 0 != sel
[1]);
704 channels
[2].setAlpha( (float)((c_alphas
&0xff00)>>8) / 255.0f
, 0 != sel
[2]);
705 channels
[3].setAlpha( (float) (c_alphas
&0xff) / 255.0f
, 0 != sel
[3]);
706 // restore visibility in the working c_alphas
707 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);
710 if (null != active
&& null != layer
) {
711 Rectangle r
= active
.getBoundingBox();
715 r
.height
+= r
.height
;
716 if (r
.x
< 0) r
.x
= 0;
717 if (r
.y
< 0) r
.y
= 0;
718 if (r
.width
> layer
.getLayerWidth()) r
.width
= (int)layer
.getLayerWidth();
719 if (r
.height
> layer
.getLayerHeight())r
.height
= (int)layer
.getLayerHeight();
720 double magn
= layer
.getLayerWidth() / (double)r
.width
;
721 canvas
.setup(magn
, r
);
724 // add keyListener to the whole frame
725 this.tabs
.addKeyListener(canvas
);
726 this.canvas_panel
.addKeyListener(canvas
);
727 this.frame
.addKeyListener(canvas
);
730 ij
.gui
.GUI
.center(this.frame
);
731 this.frame
.setVisible(true);
732 ProjectToolbar
.setProjectToolbar(); // doesn't get it through events
734 final Dimension screen
= Toolkit
.getDefaultToolkit().getScreenSize();
737 // fix positioning outside the screen (dual to single monitor)
738 if (p
.x
>= 0 && p
.x
< screen
.width
- 50 && p
.y
>= 0 && p
.y
<= screen
.height
- 50) this.frame
.setLocation(p
);
739 else frame
.setLocation(0, 0);
742 // fix excessive size
743 final Rectangle box
= this.frame
.getBounds();
746 int width
= box
.width
;
747 int height
= box
.height
;
748 if (box
.width
> screen
.width
) { x
= 0; width
= screen
.width
; }
749 if (box
.height
> screen
.height
) { y
= 0; height
= screen
.height
; }
750 if (x
!= box
.x
|| y
!= box
.y
) {
751 this.frame
.setLocation(x
, y
+ (0 == y ?
30 : 0)); // added insets for bad window managers
752 updateInDatabase("position");
754 if (width
!= box
.width
|| height
!= box
.height
) {
755 this.frame
.setSize(new Dimension(width
-10, height
-30)); // added insets for bad window managers
758 // try to optimize canvas dimensions and magn
759 double magn
= layer
.getLayerHeight() / screen
.height
;
760 if (magn
> 1.0) magn
= 1.0;
762 // limit magnification if appropriate
763 for (Iterator it
= layer
.getDisplayables(Patch
.class).iterator(); it
.hasNext(); ) {
764 final Patch pa
= (Patch
)it
.next();
765 final Rectangle ba
= pa
.getBoundingBox();
766 size
+= (long)(ba
.width
* ba
.height
);
768 if (size
> 10000000) canvas
.setInitialMagnification(0.25); // 10 Mb
770 this.frame
.setSize(new Dimension((int)(screen
.width
* 0.66), (int)(screen
.height
* 0.66)));
774 updateTab(panel_patches
, "Patches", layer
.getDisplayables(Patch
.class));
775 Utils
.updateComponent(tabs
); // otherwise fails in FreeBSD java 1.4.2 when reconstructing
778 // Set the calibration of the FakeImagePlus to that of the LayerSet
779 ((FakeImagePlus
)canvas
.getFakeImagePlus()).setCalibrationSuper(layer
.getParent().getCalibrationCopy());
781 updateFrameTitle(layer
);
782 // Set the FakeImagePlus as the current image
783 setTempCurrentImage();
785 // create a drag and drop listener
786 dnd
= new DNDInsertImage(this);
788 // start a repainting thread
790 canvas
.repaint(true); // repaint() is unreliable
793 // 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.
794 SwingUtilities
.invokeLater(new Runnable() {
796 tabs
.setMinimumSize(new Dimension(0, 100));
797 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
798 ControlWindow
.setLookAndFeel();
803 static public void repaintToolbar() {
804 for (final Display d
: al_displays
) {
805 d
.toolbar_panel
.repaint();
809 private class ToolbarPanel
extends JPanel
implements MouseListener
{
814 Toolbar toolbar
= Toolbar
.getInstance();
818 setBackground(Color
.white
);
819 addMouseListener(this);
821 drawButton
= Toolbar
.class.getDeclaredMethod("drawButton", Graphics
.class, Integer
.TYPE
);
822 drawButton
.setAccessible(true);
823 lineType
= Toolbar
.class.getDeclaredField("lineType");
824 lineType
.setAccessible(true);
825 SIZE
= Toolbar
.class.getDeclaredField("SIZE");
826 SIZE
.setAccessible(true);
827 OFFSET
= Toolbar
.class.getDeclaredField("OFFSET");
828 OFFSET
.setAccessible(true);
829 size
= ((Integer
)SIZE
.get(null)).intValue();
830 offset
= ((Integer
)OFFSET
.get(null)).intValue();
831 } catch (Exception e
) {
835 Dimension dim
= new Dimension(250, size
+size
);
836 setPreferredSize(dim
);
840 public void update(Graphics g
) { paint(g
); }
841 public void paint(Graphics g
) {
844 for (; i
<Toolbar
.LINE
; i
++) {
845 drawButton
.invoke(toolbar
, g
, i
);
847 drawButton
.invoke(toolbar
, g
, lineType
.get(toolbar
));
848 for (; i
<=Toolbar
.TEXT
; i
++) {
849 drawButton
.invoke(toolbar
, g
, i
);
851 drawButton
.invoke(toolbar
, g
, Toolbar
.ANGLE
);
853 AffineTransform aff
= new AffineTransform();
854 aff
.translate(-size
*Toolbar
.TEXT
, size
-1);
855 ((Graphics2D
)g
).setTransform(aff
);
857 drawButton
.invoke(toolbar
, g
, i
);
859 } catch (Exception e
) {
863 public void mousePressed(MouseEvent me
) {
867 if (x
> size
* 7) return; // off limits
871 if (x
> size
* 9) return; // off limits
873 Toolbar
.getInstance().mousePressed(new MouseEvent(toolbar
, me
.getID(), System
.currentTimeMillis(), me
.getModifiers(), x
, y
, me
.getClickCount(), me
.isPopupTrigger()));
875 Display
.this.toolChanged(ProjectToolbar
.getToolId()); // should fire on its own but it does not (?) TODO
877 public void mouseReleased(MouseEvent me
) {}
878 public void mouseClicked(MouseEvent me
) {}
879 public void mouseEntered(MouseEvent me
) {}
880 public void mouseExited(MouseEvent me
) {}
883 private JPanel
makeTabPanel() {
884 JPanel panel
= new JPanel();
885 BoxLayout layout
= new BoxLayout(panel
, BoxLayout
.Y_AXIS
);
886 panel
.setLayout(layout
);
890 private JScrollPane
makeScrollPane(Component c
) {
891 JScrollPane jsp
= new JScrollPane(c
);
892 jsp
.setBackground(Color
.white
); // no effect
893 jsp
.getViewport().setBackground(Color
.white
); // no effect
894 // adjust scrolling to use one DisplayablePanel as the minimal unit
895 jsp
.getVerticalScrollBar().setBlockIncrement(DisplayablePanel
.HEIGHT
); // clicking within the track
896 jsp
.getVerticalScrollBar().setUnitIncrement(DisplayablePanel
.HEIGHT
); // clicking on an arrow
897 jsp
.setHorizontalScrollBarPolicy(javax
.swing
.ScrollPaneConstants
.HORIZONTAL_SCROLLBAR_NEVER
);
898 jsp
.setPreferredSize(new Dimension(250, 300));
899 jsp
.setMinimumSize(new Dimension(250, 300));
903 static protected int scrollbar_width
= 0;
905 public JPanel
getCanvasPanel() {
909 public DisplayCanvas
getCanvas() {
913 public synchronized void setLayer(final Layer layer
) {
914 if (!mode
.canChangeLayer()) return;
915 if (null == layer
|| layer
== this.layer
) return;
916 translateLayerColors(this.layer
, layer
);
917 if (tabs
.getSelectedComponent() == scroll_layers
) {
918 SwingUtilities
.invokeLater(new Runnable() { public void run() {
919 scrollToShow(scroll_layers
, layer_panels
.get(layer
));
922 final boolean set_zdispl
= null == Display
.this.layer
|| layer
.getParent() != Display
.this.layer
.getParent();
923 if (canvas
.isTransforming()) {
924 Utils
.log("Can't browse layers while transforming.\nCANCEL the transform first with the ESCAPE key or right-click -> cancel.");
925 scroller
.setValue(Display
.this.layer
.getParent().getLayerIndex(Display
.this.layer
.getId()));
929 scroller
.setValue(layer
.getParent().getLayerIndex(layer
.getId()));
931 // update the current Layer pointer in ZDisplayable objects
932 for (Iterator it
= layer
.getParent().getZDisplayables().iterator(); it
.hasNext(); ) {
933 ((ZDisplayable
)it
.next()).setLayer(layer
); // the active layer
936 updateVisibleTab(set_zdispl
);
938 // see if a lot has to be reloaded, put the relevant ones at the end
939 project
.getLoader().prepare(layer
);
940 updateFrameTitle(layer
); // to show the new 'z'
941 // select the Layer in the LayerTree
942 project
.select(Display
.this.layer
); // does so in a separate thread
943 // update active Displayable:
945 // deselect all except ZDisplayables
946 final ArrayList
<Displayable
> sel
= selection
.getSelected();
947 final Displayable last_active
= Display
.this.active
;
949 for (final Iterator
<Displayable
> it
= sel
.iterator(); it
.hasNext(); ) {
950 final Displayable d
= it
.next();
951 if (!(d
instanceof ZDisplayable
)) {
954 if (d
== last_active
&& sel
.size() > 0) {
955 // select the last one of the remaining, if any
956 sel_next
= sel
.size()-1;
960 if (-1 != sel_next
&& sel
.size() > 0) select(sel
.get(sel_next
), true);
962 // repaint everything
963 navigator
.repaint(true);
964 canvas
.repaint(true);
966 // repaint tabs (hard as hell)
967 Utils
.updateComponent(tabs
);
968 // @#$%^! The above works half the times, so explicit repaint as well:
969 Component c
= tabs
.getSelectedComponent();
972 tabs
.setSelectedComponent(scroll_patches
);
974 Utils
.updateComponent(c
);
976 project
.getLoader().setMassiveMode(false); // resetting if it was set true
978 // update the coloring in the ProjectTree
979 project
.getProjectTree().updateUILater();
981 setTempCurrentImage();
984 static public void updateVisibleTabs() {
985 for (final Display d
: al_displays
) {
986 d
.updateVisibleTab(true);
990 /** Recreate the tab that is being shown. */
991 public void updateVisibleTab(boolean set_zdispl
) {
992 // update only the visible tab
993 switch (tabs
.getSelectedIndex()) {
996 updateTab(panel_patches
, "Patches", layer
.getDisplayables(Patch
.class));
1000 updateTab(panel_profiles
, "Profiles", layer
.getDisplayables(Profile
.class));
1005 updateTab(panel_zdispl
, "Z-space objects", layer
.getParent().getZDisplayables());
1008 // case 3: channel opacities
1011 updateTab(panel_labels
, "Labels", layer
.getDisplayables(DLabel
.class));
1013 // case 5: layer panels
1018 private void setLayerLater(final Layer layer
, final Displayable active
) {
1019 if (null == layer
) return;
1021 if (!ControlWindow
.isGUIEnabled()) return;
1022 SwingUtilities
.invokeLater(new Runnable() { public void run() {
1023 // empty the tabs, except channels and pipes
1024 clearTab(panel_profiles
);
1025 clearTab(panel_patches
);
1026 clearTab(panel_labels
);
1027 // distribute Displayable to the tabs. Ignore LayerSet instances.
1028 if (null == ht_panels
) ht_panels
= new Hashtable
<Displayable
,DisplayablePanel
>();
1029 else ht_panels
.clear();
1030 for (final Displayable d
: layer
.getParent().getZDisplayables()) {
1033 updateTab(panel_patches
, "Patches", layer
.getDisplayables(Patch
.class));
1034 navigator
.repaint(true); // was not done when adding
1035 Utils
.updateComponent(tabs
.getSelectedComponent());
1043 setPriority(Thread.NORM_PRIORITY);
1044 try { Thread.sleep(1000); } catch (Exception e) {}
1051 /** Remove all components from the tab. */
1052 private void clearTab(final Container c
) {
1055 if (tabs
.getSelectedComponent() == c
) {
1056 Utils
.updateComponent(c
);
1060 /** A class to listen to the transparency_slider of the DisplayablesSelectorWindow. */
1061 private class TransparencySliderListener
extends MouseAdapter
implements ChangeListener
{
1063 public void stateChanged(ChangeEvent ce
) {
1064 //change the transparency value of the current active displayable
1065 float new_value
= (float)((JSlider
)ce
.getSource()).getValue();
1066 setTransparency(new_value
/ 100.0f
);
1069 public void mousePressed(MouseEvent me
) {
1070 JScrollPane scroll
= (JScrollPane
)tabs
.getSelectedComponent();
1071 if (scroll
!= scroll_channels
&& !selection
.isEmpty()) selection
.addDataEditStep(new String
[]{"alpha"});
1074 public void mouseReleased(MouseEvent me
) {
1075 // update navigator window
1076 navigator
.repaint(true);
1077 JScrollPane scroll
= (JScrollPane
)tabs
.getSelectedComponent();
1078 if (scroll
!= scroll_channels
&& !selection
.isEmpty()) selection
.addDataEditStep(new String
[]{"alpha"});
1082 /** Context-sensitive: to a Displayable, or to a channel. */
1083 private void setTransparency(final float value
) {
1084 JScrollPane scroll
= (JScrollPane
)tabs
.getSelectedComponent();
1085 if (scroll
== scroll_channels
) {
1086 for (int i
=0; i
<4; i
++) {
1087 if (channels
[i
].getBackground() == Color
.cyan
) {
1088 channels
[i
].setAlpha(value
); // will call back and repaint the Display
1092 } else if (null != active
) {
1093 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.
1094 canvas
.invalidateVolatile();
1095 selection
.setAlpha(value
);
1100 public void setTransparencySlider(final float transp
) {
1101 if (transp
>= 0.0f
&& transp
<= 1.0f
) {
1103 transp_slider
.setValue((int)(transp
* 100));
1107 /** Mark the canvas for updating the offscreen images if the given Displayable is NOT the active. */ // Used by the Displayable.setVisible for example.
1108 static public void setUpdateGraphics(final Layer layer
, final Displayable displ
) {
1109 for (final Display d
: al_displays
) {
1110 if (layer
== d
.layer
&& null != d
.active
&& d
.active
!= displ
) {
1111 d
.canvas
.setUpdateGraphics(true);
1116 /** Flag the DisplayCanvas of Displays showing the given Layer to update their offscreen images.*/
1117 static public void setUpdateGraphics(final Layer layer
, final boolean update
) {
1118 for (final Display d
: al_displays
) {
1119 if (layer
== d
.layer
) {
1120 d
.canvas
.setUpdateGraphics(update
);
1125 /** Whether to update the offscreen images or not. */
1126 public void setUpdateGraphics(boolean b
) {
1127 canvas
.setUpdateGraphics(b
);
1130 /** Update the entire GUI:
1131 * 1 - The layer scroller
1132 * 2 - The visible tab panels
1137 static public void update() {
1138 for (final Display d
: al_displays
) {
1139 d
.updateLayerScroller(d
.layer
);
1140 d
.updateVisibleTab(true);
1141 d
.toolbar_panel
.repaint();
1142 d
.navigator
.repaint(true);
1143 d
.canvas
.repaint(true);
1147 /** Find all Display instances that contain the layer and repaint them, in the Swing GUI thread. */
1148 static public void update(final Layer layer
) {
1149 if (null == layer
) return;
1150 SwingUtilities
.invokeLater(new Runnable() { public void run() {
1151 for (final Display d
: al_displays
) {
1152 if (d
.isShowing(layer
)) {
1159 static public void update(final LayerSet set
) {
1163 /** 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. */
1164 static public void update(final LayerSet set
, final boolean update_canvas_dimensions
) {
1165 if (null == set
) return;
1166 SwingUtilities
.invokeLater(new Runnable() { public void run() {
1167 for (final Display d
: al_displays
) {
1168 if (set
.contains(d
.layer
)) {
1169 d
.updateSnapshots();
1170 if (update_canvas_dimensions
) d
.canvas
.setDimensions(set
.getLayerWidth(), set
.getLayerHeight());
1177 /** Release all resources held by this Display and close the frame. */
1178 protected void destroy() {
1180 canvas
.setReceivesInput(false);
1183 // update the coloring in the ProjectTree and LayerTree
1184 if (!project
.isBeingDestroyed()) {
1186 project
.getProjectTree().updateUILater();
1187 project
.getLayerTree().updateUILater();
1188 } catch (Exception e
) {
1189 Utils
.log2("updateUI failed at Display.destroy()");
1193 frame
.removeComponentListener(component_listener
);
1194 frame
.removeWindowListener(window_listener
);
1195 frame
.removeWindowFocusListener(window_listener
);
1196 frame
.removeWindowStateListener(window_listener
);
1197 frame
.removeKeyListener(canvas
);
1198 frame
.removeMouseListener(frame_mouse_listener
);
1199 canvas_panel
.removeKeyListener(canvas
);
1200 canvas
.removeKeyListener(canvas
);
1201 tabs
.removeChangeListener(tabs_listener
);
1202 tabs
.removeKeyListener(canvas
);
1203 ImagePlus
.removeImageListener(this);
1204 bytypelistener
= null;
1206 navigator
.destroy();
1207 scroller
.removeAdjustmentListener(scroller_listener
);
1208 frame
.setVisible(false);
1209 //no need, and throws exception//frame.dispose();
1211 if (null != selection
) selection
.clear();
1212 //Utils.log2("destroying selection");
1214 // below, need for SetLayerThread threads to quit
1216 // set a new front if any
1217 if (null == front
&& al_displays
.size() > 0) {
1218 front
= (Display
)al_displays
.get(al_displays
.size() -1);
1220 // repaint layer tree (to update the label color)
1222 project
.getLayerTree().updateUILater(); // works only after setting the front above
1223 } catch (Exception e
) {} // ignore swing sync bullshit when closing everything too fast
1224 // remove the drag and drop listener
1228 /** Find all Display instances that contain a Layer of the given project and close them without removing the Display entries from the database. */
1229 static synchronized public void close(final Project project
) {
1230 /* // concurrent modifications if more than 1 Display are being removed asynchronously
1231 for (final Display d : al_displays) {
1232 if (d.getLayer().getProject().equals(project)) {
1238 Display
[] d
= new Display
[al_displays
.size()];
1239 al_displays
.toArray(d
);
1240 for (int i
=0; i
<d
.length
; i
++) {
1241 if (d
[i
].getProject() == project
) {
1242 al_displays
.remove(d
[i
]);
1248 /** Find all Display instances that contain the layer and close them and remove the Display from the database. */
1249 static public void close(final Layer layer
) {
1250 for (Iterator it
= al_displays
.iterator(); it
.hasNext(); ) {
1251 Display d
= (Display
)it
.next();
1252 if (d
.isShowing(layer
)) {
1259 /** Find all Display instances that are showing the layer and either move to the next or previous layer, or close it if none. */
1260 static public void remove(final Layer layer
) {
1261 for (Iterator
<Display
> it
= al_displays
.iterator(); it
.hasNext(); ) {
1262 final Display d
= it
.next();
1263 if (d
.isShowing(layer
)) {
1264 Layer la
= layer
.getParent().next(layer
);
1265 if (layer
== la
|| null == la
) la
= layer
.getParent().previous(layer
);
1266 if (null == la
|| layer
== la
) {
1276 public boolean remove(boolean check
) {
1278 if (!Utils
.check("Delete the Display ?")) return false;
1280 // flush the offscreen images and close the frame
1282 removeFromDatabase();
1286 public Layer
getLayer() {
1290 public LayerSet
getLayerSet() {
1291 return layer
.getParent();
1294 public boolean isShowing(final Layer layer
) {
1295 return this.layer
== layer
;
1298 public DisplayNavigator
getNavigator() {
1302 /** Repaint both the canvas and the navigator, updating the graphics, and the title and tabs. */
1303 public void repaintAll() {
1304 if (repaint_disabled
) return;
1305 navigator
.repaint(true);
1306 canvas
.repaint(true);
1307 Utils
.updateComponent(tabs
);
1311 /** Repaint the canvas updating graphics, the navigator without updating graphics, and the title. */
1312 public void repaintAll2() {
1313 if (repaint_disabled
) return;
1314 navigator
.repaint(false);
1315 canvas
.repaint(true);
1319 static public void repaintSnapshots(final LayerSet set
) {
1320 if (repaint_disabled
) return;
1321 for (final Display d
: al_displays
) {
1322 if (d
.getLayer().getParent() == set
) {
1323 d
.navigator
.repaint(true);
1324 Utils
.updateComponent(d
.tabs
);
1328 static public void repaintSnapshots(final Layer layer
) {
1329 if (repaint_disabled
) return;
1330 for (final Display d
: al_displays
) {
1331 if (d
.getLayer() == layer
) {
1332 d
.navigator
.repaint(true);
1333 Utils
.updateComponent(d
.tabs
);
1338 public void pack() {
1339 dispatcher
.exec(new Runnable() { public void run() {
1341 Thread
.currentThread().sleep(100);
1342 SwingUtilities
.invokeAndWait(new Runnable() { public void run() {
1344 navigator
.repaint(false); // upate srcRect red frame position/size
1346 } catch (Exception e
) { IJError
.print(e
); }
1350 static public void pack(final LayerSet ls
) {
1351 for (final Display d
: al_displays
) {
1352 if (d
.layer
.getParent() == ls
) d
.pack();
1356 private void adjustCanvas() {
1357 SwingUtilities
.invokeLater(new Runnable() { public void run() {
1358 Rectangle r
= split
.getRightComponent().getBounds();
1359 canvas
.setDrawingSize(r
.width
, r
.height
, true);
1360 // fix not-on-top-left problem
1361 canvas
.setLocation(0, 0);
1362 //frame.pack(); // don't! Would go into an infinite loop
1363 canvas
.repaint(true);
1364 updateInDatabase("srcRect");
1368 /** Grab the last selected display (or create an new one if none) and show in it the layer,centered on the Displayable object. */
1369 static public void setFront(final Layer layer
, final Displayable displ
) {
1370 if (null == front
) {
1371 Display display
= new Display(layer
.getProject(), layer
); // gets set to front
1372 display
.showCentered(displ
);
1373 } else if (layer
== front
.layer
) {
1374 front
.showCentered(displ
);
1377 for (final Display d
: al_displays
) {
1378 if (d
.layer
== layer
) {
1380 d
.showCentered(displ
);
1384 // else, open new one
1385 new Display(layer
.getProject(), layer
).showCentered(displ
);
1389 /** 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. */
1390 static public void add(final Layer layer
, final Displayable displ
, final boolean activate
) {
1391 for (final Display d
: al_displays
) {
1392 if (d
.layer
== layer
) {
1394 d
.add(displ
, activate
, true);
1395 //front.frame.toFront();
1397 d
.add(displ
, false, true);
1403 static public void add(final Layer layer
, final Displayable displ
) {
1404 add(layer
, displ
, true);
1407 /** Add the ZDisplayable to all Displays that show a Layer belonging to the given LayerSet. */
1408 static public void add(final LayerSet set
, final ZDisplayable zdispl
) {
1409 for (final Display d
: al_displays
) {
1410 if (set
.contains(d
.layer
)) {
1412 zdispl
.setLayer(d
.layer
); // the active one
1413 d
.add(zdispl
, true, true); // calling add(Displayable, boolean, boolean)
1414 //front.frame.toFront();
1416 d
.add(zdispl
, false, true);
1422 static public void addAll(final Layer layer
, final Collection
<?
extends Displayable
> coll
) {
1423 for (final Display d
: al_displays
) {
1424 if (d
.layer
== layer
) {
1430 static public void addAll(final LayerSet set
, final Collection
<?
extends ZDisplayable
> coll
) {
1431 for (final Display d
: al_displays
) {
1432 if (set
.contains(d
.layer
)) {
1433 for (final ZDisplayable zd
: coll
) {
1434 if (front
== d
) zd
.setLayer(d
.layer
);
1441 private final void addAll(final Collection
<?
extends Displayable
> coll
) {
1442 // if any of the elements in the collection matches the type of the current tab, update that tab
1443 // ... it's easier to just update the front tab
1444 updateVisibleTab(true);
1446 navigator
.repaint(true);
1449 /** Add it to the proper panel, at the top, and set it active. */
1450 private final void add(final Displayable d
, final boolean activate
, final boolean repaint_snapshot
) {
1452 DisplayablePanel dp
= ht_panels
.get(d
);
1453 if (null != dp
) dp
.setActive(true);
1454 else updateVisibleTab(d
instanceof ZDisplayable
);
1457 Display
.repaint(d
.getLayerSet()); // update the al_top list to contain the active one, or background image for a new Patch.
1458 Utils
.log2("Added " + d
);
1460 if (repaint_snapshot
) navigator
.repaint(true);
1463 private void addToPanel(JPanel panel
, int index
, DisplayablePanel dp
, boolean repaint
) {
1465 if (1 == panel
.getComponentCount() && panel
.getComponent(0) instanceof JLabel
) {
1468 panel
.add(dp
, index
);
1470 Utils
.updateComponent(tabs
);
1474 /** Find the displays that show the given Layer, and remove the given Displayable from the GUI. */
1475 static public void remove(final Layer layer
, final Displayable displ
) {
1476 for (final Display d
: al_displays
) {
1477 if (layer
== d
.layer
) d
.remove(displ
);
1481 private void remove(final Displayable displ
) {
1482 DisplayablePanel ob
= ht_panels
.remove(displ
);
1484 final JScrollPane jsp
= ht_tabs
.get(displ
.getClass());
1486 JPanel p
= (JPanel
)jsp
.getViewport().getView();
1487 p
.remove((Component
)ob
);
1488 Utils
.revalidateComponent(p
);
1491 if (null == active
|| !selection
.contains(displ
)) {
1492 canvas
.setUpdateGraphics(true);
1494 canvas
.invalidateVolatile(); // removing active, no need to update offscreen but yes the volatile
1495 repaint(displ
, null, 5, true, false);
1496 // from Selection.deleteAll this method is called ... but it's ok: same thread, no locking problems.
1497 selection
.remove(displ
);
1500 static public void remove(final ZDisplayable zdispl
) {
1501 for (final Display d
: al_displays
) {
1502 if (zdispl
.getLayerSet() == d
.layer
.getParent()) {
1503 d
.remove((Displayable
)zdispl
);
1508 static public void repaint(final Layer layer
, final Displayable displ
, final int extra
) {
1509 repaint(layer
, displ
, displ
.getBoundingBox(), extra
);
1512 static public void repaint(final Layer layer
, final Displayable displ
, final Rectangle r
, final int extra
) {
1513 repaint(layer
, displ
, r
, extra
, true);
1516 /** Find the displays that show the given Layer, and repaint the given Displayable. */
1517 static public void repaint(final Layer layer
, final Displayable displ
, final Rectangle r
, final int extra
, final boolean repaint_navigator
) {
1518 if (repaint_disabled
) return;
1519 for (final Display d
: al_displays
) {
1520 if (layer
== d
.layer
) {
1521 d
.repaint(displ
, r
, extra
, repaint_navigator
, false);
1526 static public void repaint(final Displayable d
) {
1527 if (d
instanceof ZDisplayable
) repaint(d
.getLayerSet(), d
, d
.getBoundingBox(null), 5, true);
1528 repaint(d
.getLayer(), d
, d
.getBoundingBox(null), 5, true);
1531 /** Repaint as much as the bounding box around the given Displayable, or the r if not null. */
1532 private void repaint(final Displayable displ
, final Rectangle r
, final int extra
, final boolean repaint_navigator
, final boolean update_graphics
) {
1533 if (repaint_disabled
|| null == displ
) return;
1534 canvas
.invalidateVolatile();
1535 if (update_graphics
|| displ
.getClass() == Patch
.class || displ
!= active
) {
1536 canvas
.setUpdateGraphics(true);
1538 if (null != r
) canvas
.repaint(r
, extra
);
1539 else canvas
.repaint(displ
, extra
);
1540 if (repaint_navigator
) {
1541 DisplayablePanel dp
= ht_panels
.get(displ
);
1542 if (null != dp
) dp
.repaint(); // is null when creating it, or after deleting it
1543 navigator
.repaint(true); // everything
1547 /** 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. */
1548 static public void repaintSnapshot(final Displayable displ
) {
1549 for (final Display d
: al_displays
) {
1550 if (d
.layer
.contains(displ
)) {
1551 if (!d
.navigator
.isPainted(displ
)) {
1552 DisplayablePanel dp
= d
.ht_panels
.get(displ
);
1553 if (null != dp
) dp
.repaint(); // is null when creating it, or after deleting it
1554 d
.navigator
.repaint(displ
);
1560 /** Repaint the given Rectangle in all Displays showing the layer, updating the offscreen image if any. */
1561 static public void repaint(final Layer layer
, final Rectangle r
, final int extra
) {
1562 repaint(layer
, extra
, r
, true, true);
1565 static public void repaint(final Layer layer
, final int extra
, final Rectangle r
, final boolean update_navigator
) {
1566 repaint(layer
, extra
, r
, update_navigator
, true);
1569 static public void repaint(final Layer layer
, final int extra
, final Rectangle r
, final boolean update_navigator
, final boolean update_graphics
) {
1570 if (repaint_disabled
) return;
1571 for (final Display d
: al_displays
) {
1572 if (layer
== d
.layer
) {
1573 d
.canvas
.setUpdateGraphics(update_graphics
);
1574 d
.canvas
.repaint(r
, extra
);
1575 if (update_navigator
) {
1576 d
.navigator
.repaint(true);
1577 Utils
.updateComponent(d
.tabs
.getSelectedComponent());
1584 /** Repaint the given Rectangle in all Displays showing the layer, optionally updating the offscreen image (if any). */
1585 static public void repaint(final Layer layer
, final Rectangle r
, final int extra
, final boolean update_graphics
) {
1586 if (repaint_disabled
) return;
1587 for (final Display d
: al_displays
) {
1588 if (layer
== d
.layer
) {
1589 d
.canvas
.setUpdateGraphics(update_graphics
);
1590 d
.canvas
.repaint(r
, extra
);
1591 d
.navigator
.repaint(update_graphics
);
1592 if (update_graphics
) Utils
.updateComponent(d
.tabs
.getSelectedComponent());
1597 /** Repaint the DisplayablePanel (and DisplayNavigator) only for the given Displayable, in all Displays showing the given Layer. */
1598 static public void repaint(final Layer layer
, final Displayable displ
) {
1599 if (repaint_disabled
) return;
1600 for (final Display d
: al_displays
) {
1601 if (layer
== d
.layer
) {
1602 DisplayablePanel dp
= d
.ht_panels
.get(displ
);
1603 if (null != dp
) dp
.repaint();
1604 d
.navigator
.repaint(true);
1609 static public void repaint(LayerSet set
, Displayable displ
, int extra
) {
1610 repaint(set
, displ
, null, extra
);
1613 static public void repaint(LayerSet set
, Displayable displ
, Rectangle r
, int extra
) {
1614 repaint(set
, displ
, r
, extra
, true);
1617 /** Repaint the Displayable in every Display that shows a Layer belonging to the given LayerSet. */
1618 static public void repaint(final LayerSet set
, final Displayable displ
, final Rectangle r
, final int extra
, final boolean repaint_navigator
) {
1619 if (repaint_disabled
) return;
1620 for (final Display d
: al_displays
) {
1621 if (set
.contains(d
.layer
)) {
1622 if (repaint_navigator
) {
1623 if (null != displ
) {
1624 DisplayablePanel dp
= d
.ht_panels
.get(displ
);
1625 if (null != dp
) dp
.repaint();
1627 d
.navigator
.repaint(true);
1629 if (null == displ
|| displ
!= d
.active
) d
.setUpdateGraphics(true); // safeguard
1630 // paint the given box or the actual Displayable's box
1631 if (null != r
) d
.canvas
.repaint(r
, extra
);
1632 else d
.canvas
.repaint(displ
, extra
);
1637 /** Repaint the entire LayerSet, in all Displays showing a Layer of it.*/
1638 static public void repaint(final LayerSet set
) {
1639 if (repaint_disabled
) return;
1640 for (final Display d
: al_displays
) {
1641 if (set
.contains(d
.layer
)) {
1642 d
.navigator
.repaint(true);
1643 d
.canvas
.repaint(true);
1647 /** Repaint the given box in the LayerSet, in all Displays showing a Layer of it.*/
1648 static public void repaint(final LayerSet set
, final Rectangle box
) {
1649 if (repaint_disabled
) return;
1650 for (final Display d
: al_displays
) {
1651 if (set
.contains(d
.layer
)) {
1652 d
.navigator
.repaint(box
);
1653 d
.canvas
.repaint(box
, 0, true);
1657 /** Repaint the entire Layer, in all Displays showing it, including the tabs.*/
1658 static public void repaint(final Layer layer
) { // TODO this method overlaps with update(layer)
1659 if (repaint_disabled
) return;
1660 for (final Display d
: al_displays
) {
1661 if (layer
== d
.layer
) {
1662 d
.navigator
.repaint(true);
1663 d
.canvas
.repaint(true);
1668 /** Call repaint on all open Displays. */
1669 static public void repaint() {
1670 if (repaint_disabled
) {
1671 Utils
.logAll("Can't repaint -- repainting is disabled!");
1674 for (final Display d
: al_displays
) {
1675 d
.navigator
.repaint(true);
1676 d
.canvas
.repaint(true);
1680 static private boolean repaint_disabled
= false;
1682 /** Set a flag to enable/disable repainting of all Display instances. */
1683 static protected void setRepaint(boolean b
) {
1684 repaint_disabled
= !b
;
1687 public Rectangle
getBounds() {
1688 return frame
.getBounds();
1691 public Point
getLocation() {
1692 return frame
.getLocation();
1695 public JFrame
getFrame() {
1699 public void setLocation(Point p
) {
1700 this.frame
.setLocation(p
);
1703 public Displayable
getActive() {
1704 return active
; //TODO this should return selection.active !!
1707 public void select(Displayable d
) {
1711 /** Select/deselect accordingly to the current state and the shift key. */
1712 public void select(final Displayable d
, final boolean shift_down
) {
1713 if (null != active
&& active
!= d
&& active
.getClass() != Patch
.class) {
1714 // active is being deselected, so link underlying patches
1715 final String prop
= project
.getProperty(Project
.getName(active
.getClass()).toLowerCase() + "_nolinks");
1716 HashSet
<Displayable
> glinked
= null;
1717 if (null != prop
&& prop
.equals("true")) {
1718 // do nothing: linking disabled for active's type
1719 } else if (active
.linkPatches()) {
1720 // Locking state changed:
1721 glinked
= active
.getLinkedGroup(null);
1722 updateCheckboxes(glinked
, DisplayablePanel
.LOCK_STATE
, true);
1724 // Update link icons:
1725 Display
.updateCheckboxes(null == glinked ? active
.getLinkedGroup(null) : glinked
, DisplayablePanel
.LINK_STATE
);
1728 //Utils.log2("Display.select: clearing selection");
1729 canvas
.setUpdateGraphics(true);
1734 //Utils.log2("Display.select: single selection");
1739 } else if (selection
.contains(d
)) {
1741 selection
.remove(d
);
1742 //Utils.log2("Display.select: removing from a selection");
1744 //Utils.log2("Display.select: activing within a selection");
1745 selection
.setActive(d
);
1748 //Utils.log2("Display.select: adding to an existing selection");
1751 // update the image shown to ImageJ
1752 // NO longer necessary, always he same FakeImagePlus // setTempCurrentImage();
1755 protected void choose(int screen_x_p
, int screen_y_p
, int x_p
, int y_p
, final Class c
) {
1756 choose(screen_x_p
, screen_y_p
, x_p
, y_p
, false, c
);
1758 protected void choose(int screen_x_p
, int screen_y_p
, int x_p
, int y_p
) {
1759 choose(screen_x_p
, screen_y_p
, x_p
, y_p
, false, null);
1762 /** 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. */
1763 protected void choose(int screen_x_p
, int screen_y_p
, int x_p
, int y_p
, boolean shift_down
, Class c
) {
1764 //Utils.log("Display.choose: x,y " + x_p + "," + y_p);
1765 final ArrayList
<Displayable
> al
= new ArrayList
<Displayable
>(layer
.find(x_p
, y_p
, true));
1766 al
.addAll(layer
.getParent().findZDisplayables(layer
, x_p
, y_p
, true)); // only visible ones
1768 Displayable act
= this.active
;
1770 canvas
.setUpdateGraphics(true);
1771 //Utils.log("choose: set active to null");
1772 // fixing lack of repainting for unknown reasons, of the active one TODO this is a temporary solution
1773 if (null != act
) Display
.repaint(layer
, act
, 5);
1774 } else if (1 == al
.size()) {
1775 Displayable d
= (Displayable
)al
.get(0);
1776 if (null != c
&& d
.getClass() != c
) {
1780 select(d
, shift_down
);
1781 //Utils.log("choose 1: set active to " + active);
1783 if (al
.contains(active
) && !shift_down
) {
1787 // check if at least one of them is of class c
1788 // if only one is of class c, set as selected
1790 for (Iterator it
= al
.iterator(); it
.hasNext(); ) {
1791 Object ob
= it
.next();
1792 if (ob
.getClass() != c
) it
.remove();
1794 if (0 == al
.size()) {
1799 if (1 == al
.size()) {
1800 select((Displayable
)al
.get(0), shift_down
);
1803 // else, choose among the many
1805 choose(screen_x_p
, screen_y_p
, al
, shift_down
, x_p
, y_p
);
1807 //Utils.log("choose many: set active to " + active);
1811 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
) {
1812 // show a popup on the canvas to choose
1815 final Object lock
= new Object();
1816 final DisplayableChooser d_chooser
= new DisplayableChooser(al
, lock
);
1817 final JPopupMenu pop
= new JPopupMenu("Select:");
1818 final Iterator itu
= al
.iterator();
1819 while (itu
.hasNext()) {
1820 Displayable d
= (Displayable
)itu
.next();
1821 JMenuItem menu_item
= new JMenuItem(d
.toString());
1822 menu_item
.addActionListener(d_chooser
);
1828 pop
.show(canvas
, screen_x_p
, screen_y_p
);
1832 //now wait until selecting something
1833 synchronized(lock
) {
1837 } catch (InterruptedException ie
) {}
1838 } while (d_chooser
.isWaiting() && pop
.isShowing());
1841 //grab the chosen Displayable object
1842 Displayable d
= d_chooser
.getChosen();
1843 //Utils.log("Chosen: " + d.toString());
1844 if (null == d
) { Utils
.log2("Display.choose: returning a null!"); }
1845 select(d
, shift_down
);
1846 pop
.setVisible(false);
1848 // fix selection bug: never receives mouseReleased event when the popup shows
1849 getMode().mouseReleased(null, x_p
, y_p
, x_p
, y_p
, x_p
, y_p
);
1854 /** 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. */
1855 protected void setActive(final Displayable displ
) {
1856 final Displayable prev_active
= this.active
;
1857 this.active
= displ
;
1858 SwingUtilities
.invokeLater(new Runnable() { public void run() {
1860 if (null != displ
&& displ
== prev_active
) {
1861 // make sure the proper tab is selected.
1865 // deactivate previously active
1866 if (null != prev_active
) {
1867 final DisplayablePanel ob
= ht_panels
.get(prev_active
);
1868 if (null != ob
) ob
.setActive(false);
1869 // erase "decorations" of the previously active
1870 canvas
.repaint(selection
.getBox(), 4);
1872 // activate the new active
1873 if (null != displ
) {
1874 final DisplayablePanel ob
= ht_panels
.get(displ
);
1875 if (null != ob
) ob
.setActive(true);
1876 updateInDatabase("active_displayable_id");
1877 if (displ
.getClass() != Patch
.class) project
.select(displ
); // select the node in the corresponding tree, if any.
1878 // select the proper tab, and scroll to visible
1880 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
1881 repaint(displ
, null, 5, false, update_graphics
); // to show the border, and to repaint out of the background image
1882 transp_slider
.setValue((int)(displ
.getAlpha() * 100));
1884 //ensure decorations are removed from the panels, for Displayables in a selection besides the active one
1885 Utils
.updateComponent(tabs
.getSelectedComponent());
1890 /** If the other paints under the base. */
1891 public boolean paintsBelow(Displayable base
, Displayable other
) {
1892 boolean zd_base
= base
instanceof ZDisplayable
;
1893 boolean zd_other
= other
instanceof ZDisplayable
;
1895 if (base
instanceof DLabel
) return true; // zd paints under label
1896 if (!zd_base
) return false; // any zd paints over a mere displ if not a label
1898 // both zd, compare indices
1899 ArrayList
<ZDisplayable
> al
= other
.getLayerSet().getZDisplayables();
1900 return al
.indexOf(base
) > al
.indexOf(other
);
1904 // both displ, compare indices
1905 ArrayList
<Displayable
> al
= other
.getLayer().getDisplayables();
1906 return al
.indexOf(base
) > al
.indexOf(other
);
1908 // base is zd, other is d
1909 if (other
instanceof DLabel
) return false;
1915 /** 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. */
1916 private void selectTab(final Displayable displ
) {
1917 Method method
= null;
1919 if (!(displ
instanceof LayerSet
)) {
1920 method
= Display
.class.getDeclaredMethod("selectTab", new Class
[]{displ
.getClass()});
1922 } catch (Exception e
) {
1925 if (null != method
) {
1926 final Method me
= method
;
1927 dispatcher
.exec(new Runnable() { public void run() {
1929 me
.setAccessible(true);
1930 me
.invoke(Display
.this, new Object
[]{displ
});
1931 } catch (Exception e
) { IJError
.print(e
); }
1936 private void selectTab(Patch patch
) {
1937 tabs
.setSelectedComponent(scroll_patches
);
1938 scrollToShow(scroll_patches
, ht_panels
.get(patch
));
1941 private void selectTab(Profile profile
) {
1942 tabs
.setSelectedComponent(scroll_profiles
);
1943 scrollToShow(scroll_profiles
, ht_panels
.get(profile
));
1946 private void selectTab(DLabel label
) {
1947 tabs
.setSelectedComponent(scroll_labels
);
1948 scrollToShow(scroll_labels
, ht_panels
.get(label
));
1951 private void selectTab(ZDisplayable zd
) {
1952 tabs
.setSelectedComponent(scroll_zdispl
);
1953 scrollToShow(scroll_zdispl
, ht_panels
.get(zd
));
1956 private void selectTab(Pipe d
) { selectTab((ZDisplayable
)d
); }
1957 private void selectTab(Polyline d
) { selectTab((ZDisplayable
)d
); }
1958 private void selectTab(AreaList d
) { selectTab((ZDisplayable
)d
); }
1959 private void selectTab(Ball d
) { selectTab((ZDisplayable
)d
); }
1960 private void selectTab(Dissector d
) { selectTab((ZDisplayable
)d
); }
1961 private void selectTab(Stack d
) { selectTab((ZDisplayable
)d
); }
1963 /** 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). */
1964 private void updateTab(final JPanel tab
, final String label
, final ArrayList al
) {
1965 dispatcher
.exec(new Runnable() { public void run() {
1967 if (0 == al
.size()) {
1970 Component
[] comp
= tab
.getComponents();
1972 if (1 == comp
.length
&& comp
[0].getClass() == JLabel
.class) {
1976 // In reverse order:
1977 for (ListIterator it
= al
.listIterator(al
.size()); it
.hasPrevious(); ) {
1978 Displayable d
= (Displayable
)it
.previous();
1979 DisplayablePanel dp
= null;
1980 if (next
< comp
.length
) {
1981 dp
= (DisplayablePanel
)comp
[next
++]; // recycling panels
1984 dp
= new DisplayablePanel(Display
.this, d
);
1987 ht_panels
.put(d
, dp
);
1989 if (next
< comp
.length
) {
1990 // remove from the end, to avoid potential repaints of other panels
1991 for (int i
=comp
.length
-1; i
>=next
; i
--) {
1996 if (null != Display
.this.active
) scrollToShow(Display
.this.active
);
1997 } catch (Throwable e
) { IJError
.print(e
); }
2000 Utils
.updateComponent(tabs
);
2004 static public void setActive(final Object event
, final Displayable displ
) {
2005 if (!(event
instanceof InputEvent
)) return;
2006 // find which Display
2007 for (final Display d
: al_displays
) {
2008 if (d
.isOrigin((InputEvent
)event
)) {
2015 /** Find out whether this Display is Transforming its active Displayable. */
2016 public boolean isTransforming() {
2017 return canvas
.isTransforming();
2020 /** Find whether any Display is transforming the given Displayable. */
2021 static public boolean isTransforming(final Displayable displ
) {
2022 for (final Display d
: al_displays
) {
2023 if (null != d
.active
&& d
.active
== displ
&& d
.canvas
.isTransforming()) return true;
2028 static public boolean isAligning(final LayerSet set
) {
2029 for (final Display d
: al_displays
) {
2030 if (d
.layer
.getParent() == set
&& set
.isAligning()) {
2037 /** Check whether the source of the event is located in this instance.*/
2038 private boolean isOrigin(InputEvent event
) {
2039 Object source
= event
.getSource();
2040 // find it ... check the canvas for now TODO
2041 if (canvas
== source
) {
2047 /** Get the layer of the front Display, or null if none.*/
2048 static public Layer
getFrontLayer() {
2049 if (null == front
) return null;
2053 /** Get the layer of an open Display of the given Project, or null if none.*/
2054 static public Layer
getFrontLayer(final Project project
) {
2055 if (null == front
) return null;
2056 if (front
.project
== project
) return front
.layer
;
2057 // else, find an open Display for the given Project, if any
2058 for (final Display d
: al_displays
) {
2059 if (d
.project
== project
) {
2064 return null; // none found
2067 /** Get a pointer to a Display for @param project, or null if none. */
2068 static public Display
getFront(final Project project
) {
2069 if (null == front
) return null;
2070 if (front
.project
== project
) return front
;
2071 for (final Display d
: al_displays
) {
2072 if (d
.project
== project
) {
2080 /** Return the list of selected Displayable objects of the front Display, or an emtpy list if no Display or none selected. */
2081 static public List
<Displayable
> getSelected() {
2082 if (null == front
) return new ArrayList
<Displayable
>();
2083 return front
.selection
.getSelected();
2085 /** Return the list of selected Displayable objects of class @param c of the front Display, or an emtpy list if no Display or none selected. */
2086 static public List
<Displayable
> getSelected(final Class c
) {
2087 if (null == front
) return new ArrayList
<Displayable
>();
2088 return front
.selection
.getSelected(c
);
2091 public boolean isReadOnly() {
2092 // TEMPORARY: in the future one will be able show displays as read-only to other people, remotely
2096 static public void showPopup(Component c
, int x
, int y
) {
2097 if (null != front
) front
.getPopupMenu().show(c
, x
, y
);
2100 /** Return a context-sensitive popup menu. */
2101 public JPopupMenu
getPopupMenu() { // called from canvas
2102 // get the job canceling dialog
2103 if (!canvas
.isInputEnabled()) {
2104 return project
.getLoader().getJobsPopup(this);
2108 this.popup
= new JPopupMenu();
2109 JMenuItem item
= null;
2112 if (ProjectToolbar
.ALIGN
== Toolbar
.getToolId()) {
2113 boolean aligning
= layer
.getParent().isAligning();
2114 item
= new JMenuItem("Cancel alignment"); item
.addActionListener(this); popup
.add(item
);
2115 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_ESCAPE
, 0, true));
2116 if (!aligning
) item
.setEnabled(false);
2117 item
= new JMenuItem("Align with landmarks"); item
.addActionListener(this); popup
.add(item
);
2118 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_ENTER
, 0, true));
2119 if (!aligning
) item
.setEnabled(false);
2120 item
= new JMenuItem("Align and register"); item
.addActionListener(this); popup
.add(item
);
2121 if (!aligning
) item
.setEnabled(false);
2122 item
= new JMenuItem("Align using profiles"); item
.addActionListener(this); popup
.add(item
);
2123 if (!aligning
|| selection
.isEmpty() || !selection
.contains(Profile
.class)) item
.setEnabled(false);
2124 item
= new JMenuItem("Align stack slices"); item
.addActionListener(this); popup
.add(item
);
2125 if (selection
.isEmpty() || ! (getActive().getClass() == Patch
.class && ((Patch
)getActive()).isStack())) item
.setEnabled(false);
2126 item
= new JMenuItem("Align layers"); item
.addActionListener(this); popup
.add(item
);
2127 if (1 == layer
.getParent().size()) item
.setEnabled(false);
2128 item
= new JMenuItem("Align multi-layer mosaic"); item
.addActionListener(this); popup
.add(item
);
2129 if (1 == layer
.getParent().size()) item
.setEnabled(false);
2133 if (canvas
.isTransforming()) {
2134 item
= new JMenuItem("Apply transform"); item
.addActionListener(this); popup
.add(item
);
2135 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
2136 item
= new JMenuItem("Apply transform propagating to last layer"); item
.addActionListener(this); popup
.add(item
);
2137 if (layer
.getParent().indexOf(layer
) == layer
.getParent().size() -1) item
.setEnabled(false);
2138 if (getMode().getClass() != AffineTransformMode
.class) item
.setEnabled(false);
2139 item
= new JMenuItem("Apply transform propagating to first layer"); item
.addActionListener(this); popup
.add(item
);
2140 if (0 == layer
.getParent().indexOf(layer
)) item
.setEnabled(false);
2141 if (getMode().getClass() != AffineTransformMode
.class) item
.setEnabled(false);
2142 item
= new JMenuItem("Cancel transform"); item
.addActionListener(this); popup
.add(item
);
2143 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_ESCAPE
, 0, true));
2144 item
= new JMenuItem("Specify transform..."); item
.addActionListener(this); popup
.add(item
);
2145 if (getMode().getClass() != AffineTransformMode
.class) item
.setEnabled(false);
2149 final Class aclass
= null == active ?
null : active
.getClass();
2151 if (null != active
) {
2152 if (Profile
.class == aclass
) {
2153 item
= new JMenuItem("Duplicate, link and send to next layer"); item
.addActionListener(this); popup
.add(item
);
2154 Layer nl
= layer
.getParent().next(layer
);
2155 if (nl
== layer
) item
.setEnabled(false);
2156 item
= new JMenuItem("Duplicate, link and send to previous layer"); item
.addActionListener(this); popup
.add(item
);
2157 nl
= layer
.getParent().previous(layer
);
2158 if (nl
== layer
) item
.setEnabled(false);
2160 menu
= new JMenu("Duplicate, link and send to");
2161 ArrayList al
= layer
.getParent().getLayers();
2162 final Iterator it
= al
.iterator();
2164 while (it
.hasNext()) {
2165 Layer la
= (Layer
)it
.next();
2166 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
2167 if (la
== this.layer
) item
.setEnabled(false);
2171 item
= new JMenuItem("Duplicate, link and send to..."); item
.addActionListener(this); popup
.add(item
);
2173 popup
.addSeparator();
2175 item
= new JMenuItem("Unlink from images"); item
.addActionListener(this); popup
.add(item
);
2176 if (!active
.isLinked()) item
.setEnabled(false); // isLinked() checks if it's linked to a Patch in its own layer
2177 item
= new JMenuItem("Show in 3D"); item
.addActionListener(this); popup
.add(item
);
2178 popup
.addSeparator();
2179 } else if (Patch
.class == aclass
) {
2180 item
= new JMenuItem("Unlink from images"); item
.addActionListener(this); popup
.add(item
);
2181 if (!active
.isLinked(Patch
.class)) item
.setEnabled(false);
2182 if (((Patch
)active
).isStack()) {
2183 item
= new JMenuItem("Unlink slices"); item
.addActionListener(this); popup
.add(item
);
2185 int n_sel_patches
= selection
.getSelected(Patch
.class).size();
2186 if (1 == n_sel_patches
) {
2187 item
= new JMenuItem("Snap"); item
.addActionListener(this); popup
.add(item
);
2188 } else if (n_sel_patches
> 1) {
2189 item
= new JMenuItem("Montage"); item
.addActionListener(this); popup
.add(item
);
2190 item
= new JMenuItem("Lens correction"); item
.addActionListener(this); popup
.add(item
);
2191 item
= new JMenuItem("Blend"); item
.addActionListener(this); popup
.add(item
);
2193 item
= new JMenuItem("Remove alpha mask"); item
.addActionListener(this); popup
.add(item
);
2194 if ( ! ((Patch
)active
).hasAlphaMask()) item
.setEnabled(false);
2195 item
= new JMenuItem("View volume"); item
.addActionListener(this); popup
.add(item
);
2196 HashSet hs
= active
.getLinked(Patch
.class);
2197 if (null == hs
|| 0 == hs
.size()) item
.setEnabled(false);
2198 item
= new JMenuItem("View orthoslices"); item
.addActionListener(this); popup
.add(item
);
2199 if (null == hs
|| 0 == hs
.size()) item
.setEnabled(false); // if no Patch instances among the directly linked, then it's not a stack
2200 popup
.addSeparator();
2202 item
= new JMenuItem("Unlink"); item
.addActionListener(this); popup
.add(item
);
2203 item
= new JMenuItem("Show in 3D"); item
.addActionListener(this); popup
.add(item
);
2204 popup
.addSeparator();
2206 if (AreaList
.class == aclass
) {
2207 item
= new JMenuItem("Merge"); item
.addActionListener(this); popup
.add(item
);
2208 ArrayList al
= selection
.getSelected();
2210 for (Iterator it
= al
.iterator(); it
.hasNext(); ) {
2211 if (it
.next().getClass() == AreaList
.class) n
++;
2213 if (n
< 2) item
.setEnabled(false);
2214 popup
.addSeparator();
2215 } else if (Pipe
.class == aclass
) {
2216 item
= new JMenuItem("Identify..."); item
.addActionListener(this); popup
.add(item
);
2217 item
= new JMenuItem("Identify with axes..."); item
.addActionListener(this); popup
.add(item
);
2218 item
= new JMenuItem("Identify with fiducials..."); item
.addActionListener(this); popup
.add(item
);
2219 item
= new JMenuItem("Reverse point order"); item
.addActionListener(this); popup
.add(item
);
2220 popup
.addSeparator();
2223 JMenuItem st
= new JMenu("Transform");
2224 StartTransformMenuListener tml
= new StartTransformMenuListener();
2225 item
= new JMenuItem("Transform (affine)"); item
.addActionListener(tml
); st
.add(item
);
2226 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_T
, 0, true));
2227 item
= new JMenuItem("Transform (non-linear)"); item
.addActionListener(tml
); st
.add(item
);
2228 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_T
, Event
.SHIFT_MASK
, true));
2229 item
= new JMenuItem("Cancel transform"); st
.add(item
);
2230 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_ESCAPE
, 0, true));
2231 item
.setEnabled(false); // just added as a self-documenting cue; no listener
2232 item
= new JMenuItem("Remove coordinate transforms (selected images)"); item
.addActionListener(tml
); st
.add(item
);
2233 // TODO the next one should really be somewhere where it appears even if no images are selected!
2234 item
= new JMenuItem("Remove coordinate transforms layer-wise"); item
.addActionListener(tml
); st
.add(item
);
2238 item
= new JMenuItem("Duplicate"); item
.addActionListener(this); popup
.add(item
);
2239 item
= new JMenuItem("Color..."); item
.addActionListener(this); popup
.add(item
);
2240 if (active
instanceof LayerSet
) item
.setEnabled(false);
2241 if (active
.isLocked()) {
2242 item
= new JMenuItem("Unlock"); item
.addActionListener(this); popup
.add(item
);
2244 item
= new JMenuItem("Lock"); item
.addActionListener(this); popup
.add(item
);
2246 menu
= new JMenu("Move");
2247 popup
.addSeparator();
2248 LayerSet ls
= layer
.getParent();
2249 item
= new JMenuItem("Move to top"); item
.addActionListener(this); menu
.add(item
);
2250 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.
2251 if (ls
.isTop(active
)) item
.setEnabled(false);
2252 item
= new JMenuItem("Move up"); item
.addActionListener(this); menu
.add(item
);
2253 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_PAGE_UP
, 0, true));
2254 if (ls
.isTop(active
)) item
.setEnabled(false);
2255 item
= new JMenuItem("Move down"); item
.addActionListener(this); menu
.add(item
);
2256 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_PAGE_DOWN
, 0, true));
2257 if (ls
.isBottom(active
)) item
.setEnabled(false);
2258 item
= new JMenuItem("Move to bottom"); item
.addActionListener(this); menu
.add(item
);
2259 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_END
, 0, true));
2260 if (ls
.isBottom(active
)) item
.setEnabled(false);
2263 popup
.addSeparator();
2264 item
= new JMenuItem("Delete..."); item
.addActionListener(this); popup
.add(item
);
2266 if (Patch
.class == aclass
) {
2267 if (!active
.isOnlyLinkedTo(Patch
.class)) {
2268 item
.setEnabled(false);
2270 } else if (!(DLabel
.class == aclass
|| Stack
.class == aclass
)) { // can't delete elements from the trees (Profile, Pipe, LayerSet, Ball, Dissector, AreaList, Polyline)
2271 item
.setEnabled(false);
2273 } catch (Exception e
) { IJError
.print(e
); item
.setEnabled(false); }
2275 if (Patch
.class == aclass
) {
2276 item
= new JMenuItem("Revert"); item
.addActionListener(this); popup
.add(item
);
2277 if ( null == ((Patch
)active
).getOriginalPath()) item
.setEnabled(false);
2278 popup
.addSeparator();
2280 item
= new JMenuItem("Properties..."); item
.addActionListener(this); popup
.add(item
);
2281 item
= new JMenuItem("Show centered"); item
.addActionListener(this); popup
.add(item
);
2283 popup
.addSeparator();
2285 if (! (active
instanceof ZDisplayable
)) {
2286 ArrayList al_layers
= layer
.getParent().getLayers();
2287 int i_layer
= al_layers
.indexOf(layer
);
2288 int n_layers
= al_layers
.size();
2289 item
= new JMenuItem("Send to previous layer"); item
.addActionListener(this); popup
.add(item
);
2290 if (1 == n_layers
|| 0 == i_layer
|| active
.isLinked()) item
.setEnabled(false);
2291 // 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
2292 else if (active
instanceof Profile
&& !active
.canSendTo(layer
.getParent().previous(layer
))) item
.setEnabled(false);
2293 item
= new JMenuItem("Send to next layer"); item
.addActionListener(this); popup
.add(item
);
2294 if (1 == n_layers
|| n_layers
-1 == i_layer
|| active
.isLinked()) item
.setEnabled(false);
2295 else if (active
instanceof Profile
&& !active
.canSendTo(layer
.getParent().next(layer
))) item
.setEnabled(false);
2298 menu
= new JMenu("Send linked group to...");
2299 if (active
.hasLinkedGroupWithinLayer(this.layer
)) {
2301 for (final Layer la
: ls
.getLayers()) {
2302 String layer_title
= i
+ ": " + la
.getTitle();
2303 if (-1 == layer_title
.indexOf(' ')) layer_title
+= " ";
2304 item
= new JMenuItem(layer_title
); item
.addActionListener(this); menu
.add(item
);
2305 if (la
== this.layer
) item
.setEnabled(false);
2310 menu
.setEnabled(false);
2311 //Utils.log("Active's linked group not within layer.");
2314 popup
.addSeparator();
2318 item
= new JMenuItem("Undo");item
.addActionListener(this); popup
.add(item
);
2319 if (!layer
.getParent().canUndo() || canvas
.isTransforming()) item
.setEnabled(false);
2320 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_Z
, Utils
.getControlModifier(), true));
2321 item
= new JMenuItem("Redo");item
.addActionListener(this); popup
.add(item
);
2322 if (!layer
.getParent().canRedo() || canvas
.isTransforming()) item
.setEnabled(false);
2323 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_Z
, Event
.SHIFT_MASK
| Event
.CTRL_MASK
, true));
2324 popup
.addSeparator();
2326 // Would get so much simpler with a clojure macro ...
2329 menu
= new JMenu("Hide/Unhide");
2330 item
= new JMenuItem("Hide deselected"); item
.addActionListener(this); menu
.add(item
); item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_H
, Event
.SHIFT_MASK
, true));
2331 boolean none
= 0 == selection
.getNSelected();
2332 if (none
) item
.setEnabled(false);
2333 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));
2334 if (none
) item
.setEnabled(false);
2335 item
= new JMenuItem("Hide selected"); item
.addActionListener(this); menu
.add(item
); item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_H
, 0, true));
2336 if (none
) item
.setEnabled(false);
2337 none
= ! layer
.getParent().containsDisplayable(DLabel
.class);
2338 item
= new JMenuItem("Hide all labels"); item
.addActionListener(this); menu
.add(item
);
2339 if (none
) item
.setEnabled(false);
2340 item
= new JMenuItem("Unhide all labels"); item
.addActionListener(this); menu
.add(item
);
2341 if (none
) item
.setEnabled(false);
2342 none
= ! layer
.getParent().contains(AreaList
.class);
2343 item
= new JMenuItem("Hide all arealists"); item
.addActionListener(this); menu
.add(item
);
2344 if (none
) item
.setEnabled(false);
2345 item
= new JMenuItem("Unhide all arealists"); item
.addActionListener(this); menu
.add(item
);
2346 if (none
) item
.setEnabled(false);
2347 none
= ! layer
.contains(Profile
.class);
2348 item
= new JMenuItem("Hide all profiles"); item
.addActionListener(this); menu
.add(item
);
2349 if (none
) item
.setEnabled(false);
2350 item
= new JMenuItem("Unhide all profiles"); item
.addActionListener(this); menu
.add(item
);
2351 if (none
) item
.setEnabled(false);
2352 none
= ! layer
.getParent().contains(Pipe
.class);
2353 item
= new JMenuItem("Hide all pipes"); item
.addActionListener(this); menu
.add(item
);
2354 if (none
) item
.setEnabled(false);
2355 item
= new JMenuItem("Unhide all pipes"); item
.addActionListener(this); menu
.add(item
);
2356 if (none
) item
.setEnabled(false);
2357 none
= ! layer
.getParent().contains(Polyline
.class);
2358 item
= new JMenuItem("Hide all polylines"); item
.addActionListener(this); menu
.add(item
);
2359 if (none
) item
.setEnabled(false);
2360 item
= new JMenuItem("Unhide all polylines"); item
.addActionListener(this); menu
.add(item
);
2361 if (none
) item
.setEnabled(false);
2362 none
= ! layer
.getParent().contains(Ball
.class);
2363 item
= new JMenuItem("Hide all balls"); item
.addActionListener(this); menu
.add(item
);
2364 if (none
) item
.setEnabled(false);
2365 item
= new JMenuItem("Unhide all balls"); item
.addActionListener(this); menu
.add(item
);
2366 if (none
) item
.setEnabled(false);
2367 none
= ! layer
.getParent().containsDisplayable(Patch
.class);
2368 item
= new JMenuItem("Hide all images"); item
.addActionListener(this); menu
.add(item
);
2369 if (none
) item
.setEnabled(false);
2370 item
= new JMenuItem("Unhide all images"); item
.addActionListener(this); menu
.add(item
);
2371 if (none
) item
.setEnabled(false);
2372 item
= new JMenuItem("Hide all but images"); item
.addActionListener(this); menu
.add(item
);
2373 item
= new JMenuItem("Unhide all"); item
.addActionListener(this); menu
.add(item
); item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_H
, Event
.ALT_MASK
, true));
2376 } catch (Exception e
) { IJError
.print(e
); }
2378 JMenu align_menu
= new JMenu("Align");
2379 item
= new JMenuItem("Align stack slices"); item
.addActionListener(this); align_menu
.add(item
);
2380 if (selection
.isEmpty() || ! (getActive().getClass() == Patch
.class && ((Patch
)getActive()).isStack())) item
.setEnabled(false);
2381 item
= new JMenuItem("Align layers"); item
.addActionListener(this); align_menu
.add(item
);
2382 if (1 == layer
.getParent().size()) item
.setEnabled(false);
2383 item
= new JMenuItem("Align multi-layer mosaic"); item
.addActionListener(this); align_menu
.add(item
);
2384 if (1 == layer
.getParent().size()) item
.setEnabled(false);
2385 popup
.add(align_menu
);
2387 JMenu link_menu
= new JMenu("Link");
2388 item
= new JMenuItem("Link images..."); item
.addActionListener(this); link_menu
.add(item
);
2389 item
= new JMenuItem("Unlink all selected images"); item
.addActionListener(this); link_menu
.add(item
);
2390 item
.setEnabled(selection
.getSelected(Patch
.class).size() > 0);
2391 item
= new JMenuItem("Unlink all"); item
.addActionListener(this); link_menu
.add(item
);
2392 popup
.add(link_menu
);
2394 JMenu adjust_menu
= new JMenu("Adjust");
2395 item
= new JMenuItem("Enhance contrast layer-wise..."); item
.addActionListener(this); adjust_menu
.add(item
);
2396 item
= new JMenuItem("Enhance contrast (selected images)..."); item
.addActionListener(this); adjust_menu
.add(item
);
2397 if (selection
.isEmpty()) item
.setEnabled(false);
2398 item
= new JMenuItem("Set Min and Max layer-wise..."); item
.addActionListener(this); adjust_menu
.add(item
);
2399 item
= new JMenuItem("Set Min and Max (selected images)..."); item
.addActionListener(this); adjust_menu
.add(item
);
2400 if (selection
.isEmpty()) item
.setEnabled(false);
2401 popup
.add(adjust_menu
);
2403 JMenu script
= new JMenu("Script");
2404 MenuScriptListener msl
= new MenuScriptListener();
2405 item
= new JMenuItem("Set preprocessor script layer-wise..."); item
.addActionListener(msl
); script
.add(item
);
2406 item
= new JMenuItem("Set preprocessor script (selected images)..."); item
.addActionListener(msl
); script
.add(item
);
2407 if (selection
.isEmpty()) item
.setEnabled(false);
2408 item
= new JMenuItem("Remove preprocessor script layer-wise..."); item
.addActionListener(msl
); script
.add(item
);
2409 item
= new JMenuItem("Remove preprocessor script (selected images)..."); item
.addActionListener(msl
); script
.add(item
);
2410 if (selection
.isEmpty()) item
.setEnabled(false);
2413 menu
= new JMenu("Import");
2414 item
= new JMenuItem("Import image"); item
.addActionListener(this); menu
.add(item
);
2415 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_I
, Event
.ALT_MASK
& Event
.SHIFT_MASK
, true));
2416 item
= new JMenuItem("Import stack..."); item
.addActionListener(this); menu
.add(item
);
2417 item
= new JMenuItem("Import stack with landmarks..."); item
.addActionListener(this); menu
.add(item
);
2418 item
= new JMenuItem("Import grid..."); item
.addActionListener(this); menu
.add(item
);
2419 item
= new JMenuItem("Import sequence as grid..."); item
.addActionListener(this); menu
.add(item
);
2420 item
= new JMenuItem("Import from text file..."); item
.addActionListener(this); menu
.add(item
);
2421 item
= new JMenuItem("Import labels as arealists..."); item
.addActionListener(this); menu
.add(item
);
2424 menu
= new JMenu("Export");
2425 item
= new JMenuItem("Make flat image..."); item
.addActionListener(this); menu
.add(item
);
2426 item
= new JMenuItem("Arealists as labels (tif)"); item
.addActionListener(this); menu
.add(item
);
2427 if (0 == layer
.getParent().getZDisplayables(AreaList
.class).size()) item
.setEnabled(false);
2428 item
= new JMenuItem("Arealists as labels (amira)"); item
.addActionListener(this); menu
.add(item
);
2429 if (0 == layer
.getParent().getZDisplayables(AreaList
.class).size()) item
.setEnabled(false);
2432 menu
= new JMenu("Display");
2433 item
= new JMenuItem("Resize canvas/LayerSet..."); item
.addActionListener(this); menu
.add(item
);
2434 item
= new JMenuItem("Autoresize canvas/LayerSet"); item
.addActionListener(this); menu
.add(item
);
2435 item
= new JMenuItem("Properties ..."); item
.addActionListener(this); menu
.add(item
);
2436 item
= new JMenuItem("Calibration..."); item
.addActionListener(this); menu
.add(item
);
2437 item
= new JMenuItem("Grid overlay..."); item
.addActionListener(this); menu
.add(item
);
2438 item
= new JMenuItem("Adjust snapping parameters..."); item
.addActionListener(this); menu
.add(item
);
2439 item
= new JMenuItem("Adjust fast-marching parameters..."); item
.addActionListener(this); menu
.add(item
);
2440 item
= new JMenuItem("Adjust arealist paint parameters..."); item
.addActionListener(this); menu
.add(item
);
2443 menu
= new JMenu("Project");
2444 this.project
.getLoader().setupMenuItems(menu
, this.getProject());
2445 item
= new JMenuItem("Project properties..."); item
.addActionListener(this); menu
.add(item
);
2446 item
= new JMenuItem("Create subproject"); item
.addActionListener(this); menu
.add(item
);
2447 if (null == canvas
.getFakeImagePlus().getRoi()) item
.setEnabled(false);
2448 item
= new JMenuItem("Release memory..."); item
.addActionListener(this); menu
.add(item
);
2449 item
= new JMenuItem("Flush image cache"); item
.addActionListener(this); menu
.add(item
);
2450 item
= new JMenuItem("Regenerate all mipmaps"); item
.addActionListener(this); menu
.add(item
);
2451 item
= new JMenuItem("Regenerate mipmaps (selected images)"); item
.addActionListener(this); menu
.add(item
);
2454 menu
= new JMenu("Selection");
2455 item
= new JMenuItem("Select all"); item
.addActionListener(this); menu
.add(item
);
2456 item
= new JMenuItem("Select all visible"); item
.addActionListener(this); menu
.add(item
);
2457 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_A
, Utils
.getControlModifier(), true));
2458 if (0 == layer
.getDisplayables().size() && 0 == layer
.getParent().getZDisplayables().size()) item
.setEnabled(false);
2459 item
= new JMenuItem("Select none"); item
.addActionListener(this); menu
.add(item
);
2460 if (0 == selection
.getNSelected()) item
.setEnabled(false);
2461 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_ESCAPE
, 0, true));
2463 JMenu bytype
= new JMenu("Select all by type");
2464 item
= new JMenuItem("AreaList"); item
.addActionListener(bytypelistener
); bytype
.add(item
);
2465 item
= new JMenuItem("Ball"); item
.addActionListener(bytypelistener
); bytype
.add(item
);
2466 item
= new JMenuItem("Dissector"); item
.addActionListener(bytypelistener
); bytype
.add(item
);
2467 item
= new JMenuItem("Image"); item
.addActionListener(bytypelistener
); bytype
.add(item
);
2468 item
= new JMenuItem("Text"); item
.addActionListener(bytypelistener
); bytype
.add(item
);
2469 item
= new JMenuItem("Pipe"); item
.addActionListener(bytypelistener
); bytype
.add(item
);
2470 item
= new JMenuItem("Polyline"); item
.addActionListener(bytypelistener
); bytype
.add(item
);
2471 item
= new JMenuItem("Profile"); item
.addActionListener(bytypelistener
); bytype
.add(item
);
2474 item
= new JMenuItem("Restore selection"); item
.addActionListener(this); menu
.add(item
);
2475 item
= new JMenuItem("Select under ROI"); item
.addActionListener(this); menu
.add(item
);
2476 if (canvas
.getFakeImagePlus().getRoi() == null) item
.setEnabled(false);
2479 menu
= new JMenu("Tool");
2480 item
= new JMenuItem("Rectangular ROI"); item
.addActionListener(new SetToolListener(Toolbar
.RECTANGLE
)); menu
.add(item
);
2481 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_F1
, 0, true));
2482 item
= new JMenuItem("Polygon ROI"); item
.addActionListener(new SetToolListener(Toolbar
.POLYGON
)); menu
.add(item
);
2483 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_F2
, 0, true));
2484 item
= new JMenuItem("Freehand ROI"); item
.addActionListener(new SetToolListener(Toolbar
.FREEROI
)); menu
.add(item
);
2485 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_F3
, 0, true));
2486 item
= new JMenuItem("Text"); item
.addActionListener(new SetToolListener(Toolbar
.TEXT
)); menu
.add(item
);
2487 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_F4
, 0, true));
2488 item
= new JMenuItem("Magnifier glass"); item
.addActionListener(new SetToolListener(Toolbar
.MAGNIFIER
)); menu
.add(item
);
2489 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_F5
, 0, true));
2490 item
= new JMenuItem("Hand"); item
.addActionListener(new SetToolListener(Toolbar
.HAND
)); menu
.add(item
);
2491 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_F6
, 0, true));
2492 item
= new JMenuItem("Select"); item
.addActionListener(new SetToolListener(ProjectToolbar
.SELECT
)); menu
.add(item
);
2493 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_F9
, 0, true));
2494 item
= new JMenuItem("Pencil"); item
.addActionListener(new SetToolListener(ProjectToolbar
.PENCIL
)); menu
.add(item
);
2495 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_F10
, 0, true));
2496 item
= new JMenuItem("Pen"); item
.addActionListener(new SetToolListener(ProjectToolbar
.PEN
)); menu
.add(item
);
2497 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_F11
, 0, true));
2498 item
= new JMenuItem("Align"); item
.addActionListener(new SetToolListener(ProjectToolbar
.ALIGN
)); menu
.add(item
);
2499 item
.setAccelerator(KeyStroke
.getKeyStroke(KeyEvent
.VK_F12
, 0, true));
2503 item
= new JMenuItem("Search..."); item
.addActionListener(this); popup
.add(item
);
2505 //canvas.add(popup);
2509 protected class GridOverlay
{
2510 ArrayList
<Line2D
> lines
= new ArrayList
<Line2D
>();
2512 width
=(int)layer
.getLayerWidth(),
2513 height
=(int)layer
.getLayerHeight(),
2514 xoffset
=0, yoffset
=0,
2515 tilewidth
=100, tileheight
=100,
2517 boolean visible
= true;
2518 Color color
= new Color(255,255,0,255); // yellow with full alpha
2520 /** Expects values in pixels. */
2525 lines
.add(new Line2D
.Float(ox
, oy
, ox
, oy
+height
));
2527 lines
.add(new Line2D
.Float(ox
+width
, oy
, ox
+width
, oy
+height
));
2528 for (int x
= ox
+ xoffset
; x
<= ox
+ width
; x
+= tilewidth
) {
2529 lines
.add(new Line2D
.Float(x
, oy
, x
, oy
+ height
));
2531 // Horizontal lines:
2533 lines
.add(new Line2D
.Float(ox
, oy
, ox
+width
, oy
));
2535 lines
.add(new Line2D
.Float(ox
, oy
+height
, ox
+width
, oy
+height
));
2536 for (int y
= oy
+ yoffset
; y
<= oy
+ height
; y
+= tileheight
) {
2537 lines
.add(new Line2D
.Float(ox
, y
, ox
+ width
, y
));
2540 protected void paint(final Graphics2D g
) {
2541 if (!visible
) return;
2542 g
.setStroke(new BasicStroke((float)(linewidth
/canvas
.getMagnification())));
2544 for (final Line2D line
: lines
) {
2548 void setup(Roi roi
) {
2549 GenericDialog gd
= new GenericDialog("Grid overlay");
2550 Calibration cal
= getLayerSet().getCalibration();
2551 gd
.addNumericField("Top-left corner X:", ox
*cal
.pixelWidth
, 1, 10, cal
.getUnits());
2552 gd
.addNumericField("Top-left corner Y:", oy
*cal
.pixelHeight
, 1, 10, cal
.getUnits());
2553 gd
.addNumericField("Grid total width:", width
*cal
.pixelWidth
, 1, 10, cal
.getUnits());
2554 gd
.addNumericField("Grid total height:", height
*cal
.pixelHeight
, 1, 10, cal
.getUnits());
2555 gd
.addCheckbox("Read bounds from ROI", null != roi
);
2556 ((Component
)gd
.getCheckboxes().get(0)).setEnabled(null != roi
);
2558 gd
.addNumericField("Tile width:", tilewidth
*cal
.pixelWidth
, 1, 10, cal
.getUnits());
2559 gd
.addNumericField("Tile height:", tileheight
*cal
.pixelHeight
, 1, 10, cal
.getUnits());
2560 gd
.addNumericField("Tile offset X:", xoffset
*cal
.pixelWidth
, 1, 10, cal
.getUnits());
2561 gd
.addNumericField("Tile offset Y:", yoffset
*cal
.pixelHeight
, 1, 10, cal
.getUnits());
2563 gd
.addNumericField("Line width:", linewidth
, 1, 10, "pixels");
2564 gd
.addSlider("Red: ", 0, 255, color
.getRed());
2565 gd
.addSlider("Green: ", 0, 255, color
.getGreen());
2566 gd
.addSlider("Blue: ", 0, 255, color
.getBlue());
2567 gd
.addSlider("Alpha: ", 0, 255, color
.getAlpha());
2569 gd
.addCheckbox("Visible", visible
);
2571 if (gd
.wasCanceled()) return;
2572 this.ox
= (int)(gd
.getNextNumber() / cal
.pixelWidth
);
2573 this.oy
= (int)(gd
.getNextNumber() / cal
.pixelHeight
);
2574 this.width
= (int)(gd
.getNextNumber() / cal
.pixelWidth
);
2575 this.height
= (int)(gd
.getNextNumber() / cal
.pixelHeight
);
2576 if (gd
.getNextBoolean() && null != roi
) {
2577 Rectangle r
= roi
.getBounds();
2580 this.width
= r
.width
;
2581 this.height
= r
.height
;
2583 this.tilewidth
= (int)(gd
.getNextNumber() / cal
.pixelWidth
);
2584 this.tileheight
= (int)(gd
.getNextNumber() / cal
.pixelHeight
);
2585 this.xoffset
= (int)(gd
.getNextNumber() / cal
.pixelWidth
) % tilewidth
;
2586 this.yoffset
= (int)(gd
.getNextNumber() / cal
.pixelHeight
) % tileheight
;
2587 this.linewidth
= (int)gd
.getNextNumber();
2588 this.color
= new Color((int)gd
.getNextNumber(), (int)gd
.getNextNumber(), (int)gd
.getNextNumber(), (int)gd
.getNextNumber());
2589 this.visible
= gd
.getNextBoolean();
2594 protected GridOverlay gridoverlay
= null;
2597 private class StartTransformMenuListener
implements ActionListener
{
2598 public void actionPerformed(ActionEvent ae
) {
2599 if (null == active
) return;
2600 String command
= ae
.getActionCommand();
2601 if (command
.equals("Transform (affine)")) {
2602 setMode(new AffineTransformMode(Display
.this));
2603 } else if (command
.equals("Transform (non-linear)")) {
2604 List
<Displayable
> col
= selection
.getSelected(Patch
.class);
2605 for (final Displayable d
: col
) {
2607 Utils
.showMessage("Can't enter manual non-linear transformation mode:\nat least one image is linked.");
2611 setMode(new NonLinearTransformMode(Display
.this, col
));
2612 } else if (command
.equals("Remove coordinate transforms (selected images)")) {
2613 final List
<Displayable
> col
= selection
.getSelected(Patch
.class);
2614 if (col
.isEmpty()) return;
2615 removeCoordinateTransforms( (List
<Patch
>) (List
) col
);
2616 } else if (command
.equals("Remove coordinate transforms layer-wise")) {
2617 GenericDialog gd
= new GenericDialog("Remove Coordinate Transforms");
2618 gd
.addMessage("Remove coordinate transforms");
2619 gd
.addMessage("for all images in:");
2620 Utils
.addLayerRangeChoices(Display
.this.layer
, gd
);
2622 if (gd
.wasCanceled()) return;
2623 final ArrayList
<Displayable
> patches
= new ArrayList
<Displayable
>();
2624 for (final Layer layer
: getLayerSet().getLayers().subList(gd
.getNextChoiceIndex(), gd
.getNextChoiceIndex()+1)) {
2625 patches
.addAll(layer
.getDisplayables(Patch
.class));
2627 removeCoordinateTransforms( (List
<Patch
>) (List
) patches
);
2633 public Bureaucrat
removeCoordinateTransforms(final List
<Patch
> patches
) {
2634 return Bureaucrat
.createAndStart(new Worker
.Task("Removing coordinate transforms") { public void exec() {
2635 // Check if any are linked: cannot remove, would break image-to-segmentation relationship
2636 for (final Patch p
: patches
) {
2638 Utils
.logAll("Cannot remove coordinate transform: some images are linked to segmentations!");
2643 // Collect Patch instances to modify:
2644 final HashSet
<Patch
> ds
= new HashSet
<Patch
>(patches
);
2645 for (final Patch p
: patches
) {
2646 if (null != p
.getCoordinateTransform()) {
2652 getLayerSet().addDataEditStep(ds
);
2654 // Remove coordinate transforms:
2655 final ArrayList
<Future
> fus
= new ArrayList
<Future
>();
2656 for (final Patch p
: ds
) {
2657 p
.setCoordinateTransform(null);
2658 fus
.add(p
.getProject().getLoader().regenerateMipMaps(p
)); // queue
2660 // wait until all done
2661 for (Future fu
: fus
) try { fu
.get(); } catch (Exception e
) { IJError
.print(e
); }
2663 // Set current state
2664 getLayerSet().addDataEditStep(ds
);
2668 private class MenuScriptListener
implements ActionListener
{
2669 public void actionPerformed(ActionEvent ae
) {
2670 String command
= ae
.getActionCommand();
2671 if (command
.equals("Set preprocessor script layer-wise...")) {
2672 Collection
<Layer
> ls
= getLayerList("Set preprocessor script");
2673 if (null == ls
) return;
2674 String path
= getScriptPath();
2675 if (null == path
) return;
2676 for (final Layer la
: ls
) setScriptPath(la
.getDisplayables(Patch
.class), path
);
2677 } else if (command
.equals("Set preprocessor script (selected images)...")) {
2678 if (selection
.isEmpty()) return;
2679 String path
= getScriptPath();
2680 if (null == path
) return;
2681 setScriptPath(selection
.getSelected(Patch
.class), path
);
2682 } else if (command
.equals("Remove preprocessor script layer-wise...")) {
2683 Collection
<Layer
> ls
= getLayerList("Remove preprocessor script");
2684 if (null == ls
) return;
2685 for (final Layer la
: ls
) setScriptPath(la
.getDisplayables(Patch
.class), null);
2686 } else if (command
.equals("Remove preprocessor script (selected images)...")) {
2687 if (selection
.isEmpty()) return;
2688 setScriptPath(selection
.getSelected(Patch
.class), null);
2691 /** Accepts null script, to remove it if there. */
2692 private void setScriptPath(final Collection
<Displayable
> list
, final String script
) {
2693 for (final Displayable d
: list
) {
2694 Patch p
= (Patch
) d
;
2695 p
.setPreprocessorScriptPath(script
);
2698 private Collection
<Layer
> getLayerList(String title
) {
2699 final GenericDialog gd
= new GenericDialog(title
);
2700 Utils
.addLayerRangeChoices(Display
.this.layer
, gd
);
2702 if (gd
.wasCanceled()) return null;
2703 return layer
.getParent().getLayers().subList(gd
.getNextChoiceIndex(), gd
.getNextChoiceIndex() +1); // exclusive end
2705 private String
getScriptPath() {
2706 OpenDialog od
= new OpenDialog("Select script", OpenDialog
.getLastDirectory(), null);
2707 String dir
= od
.getDirectory();
2708 if (null == dir
) return null;
2709 if (IJ
.isWindows()) dir
= dir
.replace('\\','/');
2710 if (!dir
.endsWith("/")) dir
+= "/";
2711 return dir
+ od
.getFileName();
2715 private class SetToolListener
implements ActionListener
{
2717 SetToolListener(int tool
) {
2720 public void actionPerformed(ActionEvent ae
) {
2721 ProjectToolbar
.setTool(tool
);
2722 toolbar_panel
.repaint();
2726 private ByTypeListener bytypelistener
= new ByTypeListener(this);
2728 static private class ByTypeListener
implements ActionListener
{
2730 ByTypeListener(final Display d
) {
2733 public void actionPerformed(final ActionEvent ae
) {
2734 final String command
= ae
.getActionCommand();
2736 final Area aroi
= M
.getArea(d
.canvas
.getFakeImagePlus().getRoi());
2738 d
.dispatcher
.exec(new Runnable() { public void run() {
2741 String type
= command
;
2742 if (type
.equals("Image")) type
= "Patch";
2743 Class c
= Class
.forName("ini.trakem2.display." + type
);
2745 java
.util
.List
<Displayable
> a
= new ArrayList
<Displayable
>();
2747 a
.addAll(d
.layer
.getDisplayables(c
, aroi
, true));
2748 a
.addAll(d
.layer
.getParent().getZDisplayables(c
, d
.layer
, aroi
, true));
2750 a
.addAll(d
.layer
.getDisplayables(c
));
2751 a
.addAll(d
.layer
.getParent().getZDisplayables(c
));
2752 // Remove non-visible ones
2753 for (final Iterator
<Displayable
> it
= a
.iterator(); it
.hasNext(); ) {
2754 if (!it
.next().isVisible()) it
.remove();
2758 if (0 == a
.size()) return;
2760 boolean selected
= false;
2762 if (0 == ae
.getModifiers()) {
2763 Utils
.log2("first");
2764 d
.selection
.clear();
2765 d
.selection
.selectAll(a
);
2767 } else if (0 == (ae
.getModifiers() ^ Event
.SHIFT_MASK
)) {
2768 Utils
.log2("with shift");
2769 d
.selection
.selectAll(a
); // just add them to the current selection
2774 d
.selection
.setActive(a
.get(a
.size() -1));
2777 } catch (ClassNotFoundException e
) {
2778 Utils
.log2(e
.toString());
2785 /** Check if a panel for the given Displayable is completely visible in the JScrollPane */
2786 public boolean isWithinViewport(final Displayable d
) {
2787 final JScrollPane scroll
= (JScrollPane
)tabs
.getSelectedComponent();
2788 if (ht_tabs
.get(d
.getClass()) == scroll
) return isWithinViewport(scroll
, ht_panels
.get(d
));
2792 private boolean isWithinViewport(JScrollPane scroll
, DisplayablePanel dp
) {
2793 if(null == dp
) return false;
2794 JViewport view
= scroll
.getViewport();
2795 java
.awt
.Dimension dimensions
= view
.getExtentSize();
2796 java
.awt
.Point p
= view
.getViewPosition();
2798 if ((y
+ DisplayablePanel
.HEIGHT
- p
.y
) <= dimensions
.height
&& y
>= p
.y
) {
2804 /** Check if a panel for the given Displayable is partially visible in the JScrollPane */
2805 public boolean isPartiallyWithinViewport(final Displayable d
) {
2806 final JScrollPane scroll
= ht_tabs
.get(d
.getClass());
2807 if (tabs
.getSelectedComponent() == scroll
) return isPartiallyWithinViewport(scroll
, ht_panels
.get(d
));
2811 /** Check if a panel for the given Displayable is at least partially visible in the JScrollPane */
2812 private boolean isPartiallyWithinViewport(final JScrollPane scroll
, final DisplayablePanel dp
) {
2814 //Utils.log2("Display.isPartiallyWithinViewport: null DisplayablePanel ??");
2815 return false; // to fast for you baby
2817 JViewport view
= scroll
.getViewport();
2818 java
.awt
.Dimension dimensions
= view
.getExtentSize();
2819 java
.awt
.Point p
= view
.getViewPosition();
2821 if ( ((y
+ DisplayablePanel
.HEIGHT
- p
.y
) <= dimensions
.height
&& y
>= p
.y
) // completely visible
2822 || ((y
+ DisplayablePanel
.HEIGHT
- p
.y
) > dimensions
.height
&& y
< p
.y
+ dimensions
.height
) // partially hovering at the bottom
2823 || ((y
+ DisplayablePanel
.HEIGHT
) > p
.y
&& y
< p
.y
) // partially hovering at the top
2830 /** A function to make a Displayable panel be visible in the screen, by scrolling the viewport of the JScrollPane. */
2831 private void scrollToShow(final Displayable d
) {
2832 dispatcher
.execSwing(new Runnable() { public void run() {
2833 final JScrollPane scroll
= (JScrollPane
)tabs
.getSelectedComponent();
2834 if (d
instanceof ZDisplayable
&& scroll
== scroll_zdispl
) {
2835 scrollToShow(scroll_zdispl
, ht_panels
.get(d
));
2838 final Class c
= d
.getClass();
2839 if (Patch
.class == c
&& scroll
== scroll_patches
) {
2840 scrollToShow(scroll_patches
, ht_panels
.get(d
));
2841 } else if (DLabel
.class == c
&& scroll
== scroll_labels
) {
2842 scrollToShow(scroll_labels
, ht_panels
.get(d
));
2843 } else if (Profile
.class == c
&& scroll
== scroll_profiles
) {
2844 scrollToShow(scroll_profiles
, ht_panels
.get(d
));
2849 private void scrollToShow(final JScrollPane scroll
, final JPanel dp
) {
2850 if (null == dp
) return;
2851 JViewport view
= scroll
.getViewport();
2852 Point current
= view
.getViewPosition();
2853 Dimension extent
= view
.getExtentSize();
2854 int panel_y
= dp
.getY();
2855 if ((panel_y
+ DisplayablePanel
.HEIGHT
- current
.y
) <= extent
.height
&& panel_y
>= current
.y
) {
2856 // it's completely visible already
2859 // scroll just enough
2860 // if it's above, show at the top
2861 if (panel_y
- current
.y
< 0) {
2862 view
.setViewPosition(new Point(0, panel_y
));
2864 // if it's below (even if partially), show at the bottom
2865 else if (panel_y
+ 50 > current
.y
+ extent
.height
) {
2866 view
.setViewPosition(new Point(0, panel_y
- extent
.height
+ 50));
2867 //Utils.log("Display.scrollToShow: panel_y: " + panel_y + " current.y: " + current.y + " extent.height: " + extent.height);
2872 /** Update the title of the given Displayable in its DisplayablePanel, if any. */
2873 static public void updateTitle(final Layer layer
, final Displayable displ
) {
2874 for (final Display d
: al_displays
) {
2875 if (layer
== d
.layer
) {
2876 DisplayablePanel dp
= d
.ht_panels
.get(displ
);
2877 if (null != dp
) dp
.updateTitle();
2882 /** Update the Display's title in all Displays showing the given Layer. */
2883 static public void updateTitle(final Layer layer
) {
2884 for (final Display d
: al_displays
) {
2885 if (d
.layer
== layer
) {
2886 d
.updateFrameTitle();
2890 /** Update the Display's title in all Displays showing a Layer of the given LayerSet. */
2891 static public void updateTitle(final LayerSet ls
) {
2892 for (final Display d
: al_displays
) {
2893 if (d
.layer
.getParent() == ls
) {
2894 d
.updateFrameTitle();
2899 /** Set a new title in the JFrame, showing info on the layer 'z' and the magnification. */
2900 public void updateFrameTitle() {
2901 updateFrameTitle(layer
);
2903 private void updateFrameTitle(Layer layer
) {
2904 // From ij.ImagePlus class, the solution:
2906 final double magnification
= canvas
.getMagnification();
2907 if (magnification
!=1.0) {
2908 final double percent
= magnification
*100.0;
2909 scale
= new StringBuilder(" (").append(Utils
.d2s(percent
, percent
==(int)percent ?
0 : 1)).append("%)").toString();
2911 final Calibration cal
= layer
.getParent().getCalibration();
2912 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();
2913 frame
.setTitle(title
);
2914 // fix the title for the FakeImageWindow and thus the WindowManager listing in the menus
2915 canvas
.getFakeImagePlus().setTitle(title
);
2918 /** 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. */
2919 public void nextLayer(final int modifiers
) {
2921 if (0 == (modifiers ^ Event
.SHIFT_MASK
)) {
2922 l
= layer
.getParent().nextNonEmpty(layer
);
2923 } else if (scroll_step
> 1) {
2924 int i
= layer
.getParent().indexOf(this.layer
);
2925 Layer la
= layer
.getParent().getLayer(i
+ scroll_step
);
2926 if (null != la
) l
= la
;
2929 l
= layer
.getParent().next(layer
);
2933 updateInDatabase("layer_id");
2937 private final void translateLayerColors(final Layer current
, final Layer other
) {
2938 if (current
== other
) return;
2939 if (layer_channels
.size() > 0) {
2940 final LayerSet ls
= getLayerSet();
2941 // translate colors by distance from current layer to new Layer l
2942 final int dist
= ls
.indexOf(other
) - ls
.indexOf(current
);
2943 translateLayerColor(Color
.red
, dist
);
2944 translateLayerColor(Color
.blue
, dist
);
2948 private final void translateLayerColor(final Color color
, final int dist
) {
2949 final LayerSet ls
= getLayerSet();
2950 final Layer l
= layer_channels
.get(color
);
2951 if (null == l
) return;
2952 updateColor(Color
.white
, layer_panels
.get(l
));
2953 final Layer l2
= ls
.getLayer(ls
.indexOf(l
) + dist
);
2954 if (null != l2
) updateColor(color
, layer_panels
.get(l2
));
2957 private final void updateColor(final Color color
, final LayerPanel lp
) {
2959 setColorChannel(lp
.layer
, color
);
2962 /** Calls setLayer(la) on the SetLayerThread. */
2963 public void toLayer(final Layer la
) {
2964 if (la
.getParent() != layer
.getParent()) return; // not of the same LayerSet
2965 if (la
== layer
) return; // nothing to do
2967 updateInDatabase("layer_id");
2970 /** 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. */
2971 public void previousLayer(final int modifiers
) {
2973 if (0 == (modifiers ^ Event
.SHIFT_MASK
)) {
2974 l
= layer
.getParent().previousNonEmpty(layer
);
2975 } else if (scroll_step
> 1) {
2976 int i
= layer
.getParent().indexOf(this.layer
);
2977 Layer la
= layer
.getParent().getLayer(i
- scroll_step
);
2978 if (null != la
) l
= la
;
2981 l
= layer
.getParent().previous(layer
);
2985 updateInDatabase("layer_id");
2989 static public void updateLayerScroller(LayerSet set
) {
2990 for (final Display d
: al_displays
) {
2991 if (d
.layer
.getParent() == set
) {
2992 d
.updateLayerScroller(d
.layer
);
2997 private void updateLayerScroller(Layer layer
) {
2998 int size
= layer
.getParent().size();
3000 scroller
.setValues(0, 1, 0, 0);
3001 scroller
.setEnabled(false);
3003 scroller
.setEnabled(true);
3004 scroller
.setValues(layer
.getParent().getLayerIndex(layer
.getId()), 1, 0, size
);
3006 recreateLayerPanels(layer
);
3009 // Can't use this.layer, may still be null. User argument instead.
3010 private synchronized void recreateLayerPanels(final Layer layer
) {
3011 synchronized (layer_channels
) {
3012 panel_layers
.removeAll();
3014 if (0 == layer_panels
.size()) {
3015 for (final Layer la
: layer
.getParent().getLayers()) {
3016 final LayerPanel lp
= new LayerPanel(this, la
);
3017 layer_panels
.put(la
, lp
);
3018 this.panel_layers
.add(lp
);
3021 // Set theory at work: keep old to reuse
3022 layer_panels
.keySet().retainAll(layer
.getParent().getLayers());
3023 for (final Layer la
: layer
.getParent().getLayers()) {
3024 LayerPanel lp
= layer_panels
.get(la
);
3026 lp
= new LayerPanel(this, la
);
3027 layer_panels
.put(la
, lp
);
3029 this.panel_layers
.add(lp
);
3031 for (final Iterator
<Map
.Entry
<Integer
,LayerPanel
>> it
= layer_alpha
.entrySet().iterator(); it
.hasNext(); ) {
3032 final Map
.Entry
<Integer
,LayerPanel
> e
= it
.next();
3033 if (-1 == getLayerSet().indexOf(e
.getValue().layer
)) it
.remove();
3035 for (final Iterator
<Map
.Entry
<Color
,Layer
>> it
= layer_channels
.entrySet().iterator(); it
.hasNext(); ) {
3036 final Map
.Entry
<Color
,Layer
> e
= it
.next();
3037 if (-1 == getLayerSet().indexOf(e
.getValue())) it
.remove();
3039 scroll_layers
.repaint();
3044 private void updateSnapshots() {
3045 Enumeration
<DisplayablePanel
> e
= ht_panels
.elements();
3046 while (e
.hasMoreElements()) {
3047 e
.nextElement().repaint();
3049 Utils
.updateComponent(tabs
.getSelectedComponent());
3052 static public void updatePanel(Layer layer
, final Displayable displ
) {
3053 if (null == layer
&& null != front
) layer
= front
.layer
; // the front layer
3054 for (final Display d
: al_displays
) {
3055 if (d
.layer
== layer
) {
3056 d
.updatePanel(displ
);
3061 private void updatePanel(Displayable d
) {
3063 if (d
instanceof Profile
) {
3065 } else if (d
instanceof Patch
) {
3067 } else if (d
instanceof DLabel
) {
3069 } else if (d
instanceof Pipe
) {
3072 if (null == c
) return;
3073 DisplayablePanel dp
= ht_panels
.get(d
);
3076 Utils
.updateComponent(c
);
3080 static public void updatePanelIndex(final Layer layer
, final Displayable displ
) {
3081 for (final Display d
: al_displays
) {
3082 if (d
.layer
== layer
|| displ
instanceof ZDisplayable
) {
3083 d
.updatePanelIndex(displ
);
3088 private void updatePanelIndex(final Displayable d
) {
3089 updateTab( (JPanel
) ht_tabs
.get(d
.getClass()).getViewport().getView(), "",
3090 ZDisplayable
.class.isAssignableFrom(d
.getClass()) ?
3091 layer
.getParent().getZDisplayables()
3092 : layer
.getDisplayables(d
.getClass()));
3095 /** Repair possibly missing panels and other components by simply resetting the same Layer */
3096 public void repairGUI() {
3097 Layer layer
= this.layer
;
3102 public void actionPerformed(final ActionEvent ae
) {
3103 dispatcher
.exec(new Runnable() { public void run() {
3105 String command
= ae
.getActionCommand();
3106 if (command
.startsWith("Job")) {
3107 if (Utils
.checkYN("Really cancel job?")) {
3108 project
.getLoader().quitJob(command
);
3112 } else if (command
.equals("Move to top")) {
3113 if (null == active
) return;
3114 canvas
.setUpdateGraphics(true);
3115 layer
.getParent().move(LayerSet
.TOP
, active
);
3116 Display
.repaint(layer
.getParent(), active
, 5);
3117 //Display.updatePanelIndex(layer, active);
3118 } else if (command
.equals("Move up")) {
3119 if (null == active
) return;
3120 canvas
.setUpdateGraphics(true);
3121 layer
.getParent().move(LayerSet
.UP
, active
);
3122 Display
.repaint(layer
.getParent(), active
, 5);
3123 //Display.updatePanelIndex(layer, active);
3124 } else if (command
.equals("Move down")) {
3125 if (null == active
) return;
3126 canvas
.setUpdateGraphics(true);
3127 layer
.getParent().move(LayerSet
.DOWN
, active
);
3128 Display
.repaint(layer
.getParent(), active
, 5);
3129 //Display.updatePanelIndex(layer, active);
3130 } else if (command
.equals("Move to bottom")) {
3131 if (null == active
) return;
3132 canvas
.setUpdateGraphics(true);
3133 layer
.getParent().move(LayerSet
.BOTTOM
, active
);
3134 Display
.repaint(layer
.getParent(), active
, 5);
3135 //Display.updatePanelIndex(layer, active);
3136 } else if (command
.equals("Duplicate, link and send to next layer")) {
3137 duplicateLinkAndSendTo(active
, 1, layer
.getParent().next(layer
));
3138 } else if (command
.equals("Duplicate, link and send to previous layer")) {
3139 duplicateLinkAndSendTo(active
, 0, layer
.getParent().previous(layer
));
3140 } else if (command
.equals("Duplicate, link and send to...")) {
3141 // fix non-scrolling popup menu
3142 GenericDialog gd
= new GenericDialog("Send to");
3143 gd
.addMessage("Duplicate, link and send to...");
3144 String
[] sl
= new String
[layer
.getParent().size()];
3146 for (Iterator it
= layer
.getParent().getLayers().iterator(); it
.hasNext(); ) {
3147 sl
[next
++] = project
.findLayerThing(it
.next()).toString();
3149 gd
.addChoice("Layer: ", sl
, sl
[layer
.getParent().indexOf(layer
)]);
3151 if (gd
.wasCanceled()) return;
3152 Layer la
= layer
.getParent().getLayer(gd
.getNextChoiceIndex());
3154 Utils
.showMessage("Can't duplicate, link and send to the same layer.");
3157 duplicateLinkAndSendTo(active
, 0, la
);
3158 } else if (-1 != command
.indexOf("z = ")) {
3159 // this is an item from the "Duplicate, link and send to" menu of layer z's
3160 Layer target_layer
= layer
.getParent().getLayer(Double
.parseDouble(command
.substring(command
.lastIndexOf(' ') +1)));
3161 Utils
.log2("layer: __" +command
.substring(command
.lastIndexOf(' ') +1) + "__");
3162 if (null == target_layer
) return;
3163 duplicateLinkAndSendTo(active
, 0, target_layer
);
3164 } else if (-1 != command
.indexOf("z=")) {
3165 // WARNING the indexOf is very similar to the previous one
3166 // Send the linked group to the selected layer
3167 int iz
= command
.indexOf("z=")+2;
3168 Utils
.log2("iz=" + iz
+ " other: " + command
.indexOf(' ', iz
+2));
3169 int end
= command
.indexOf(' ', iz
);
3170 if (-1 == end
) end
= command
.length();
3171 double lz
= Double
.parseDouble(command
.substring(iz
, end
));
3172 Layer target
= layer
.getParent().getLayer(lz
);
3173 HashSet hs
= active
.getLinkedGroup(new HashSet());
3174 layer
.getParent().move(hs
, active
.getLayer(), target
);
3175 } else if (command
.equals("Unlink")) {
3176 if (null == active
|| active
instanceof Patch
) return;
3178 updateSelection();//selection.update();
3179 } else if (command
.equals("Unlink from images")) {
3180 if (null == active
) return;
3182 for (Displayable displ
: selection
.getSelected()) {
3183 displ
.unlinkAll(Patch
.class);
3185 updateSelection();//selection.update();
3186 } catch (Exception e
) { IJError
.print(e
); }
3187 } else if (command
.equals("Unlink slices")) {
3188 YesNoCancelDialog yn
= new YesNoCancelDialog(frame
, "Attention", "Really unlink all slices from each other?\nThere is no undo.");
3189 if (!yn
.yesPressed()) return;
3190 final ArrayList
<Patch
> pa
= ((Patch
)active
).getStackPatches();
3191 for (int i
=pa
.size()-1; i
>0; i
--) {
3192 pa
.get(i
).unlink(pa
.get(i
-1));
3194 } else if (command
.equals("Send to next layer")) {
3195 Rectangle box
= selection
.getBox();
3197 // unlink Patch instances
3198 for (final Displayable displ
: selection
.getSelected()) {
3199 displ
.unlinkAll(Patch
.class);
3201 updateSelection();//selection.update();
3202 } catch (Exception e
) { IJError
.print(e
); }
3203 //layer.getParent().moveDown(layer, active); // will repaint whatever appropriate layers
3204 selection
.moveDown();
3205 repaint(layer
.getParent(), box
);
3206 } else if (command
.equals("Send to previous layer")) {
3207 Rectangle box
= selection
.getBox();
3209 // unlink Patch instances
3210 for (final Displayable displ
: selection
.getSelected()) {
3211 displ
.unlinkAll(Patch
.class);
3213 updateSelection();//selection.update();
3214 } catch (Exception e
) { IJError
.print(e
); }
3215 //layer.getParent().moveUp(layer, active); // will repaint whatever appropriate layers
3217 repaint(layer
.getParent(), box
);
3218 } else if (command
.equals("Show centered")) {
3219 if (active
== null) return;
3220 showCentered(active
);
3221 } else if (command
.equals("Delete...")) {
3223 if (null != active) {
3224 Displayable d = active;
3225 selection.remove(d);
3226 d.remove(true); // will repaint
3229 // remove all selected objects
3230 selection
.deleteAll();
3231 } else if (command
.equals("Color...")) {
3232 IJ
.doCommand("Color Picker...");
3233 } else if (command
.equals("Revert")) {
3234 if (null == active
|| active
.getClass() != Patch
.class) return;
3235 Patch p
= (Patch
)active
;
3237 if (null == p
.getOriginalPath()) Utils
.log("No editions to save for patch " + p
.getTitle() + " #" + p
.getId());
3238 else Utils
.log("Could not revert Patch " + p
.getTitle() + " #" + p
.getId());
3240 } else if (command
.equals("Remove alpha mask")) {
3241 final ArrayList
<Displayable
> patches
= selection
.getSelected(Patch
.class);
3242 if (patches
.size() > 0) {
3243 Bureaucrat
.createAndStart(new Worker
.Task("Removing alpha mask" + (patches
.size() > 1 ?
"s" : "")) { public void exec() {
3244 final ArrayList
<Future
> jobs
= new ArrayList
<Future
>();
3245 for (final Displayable d
: patches
) {
3246 final Patch p
= (Patch
) d
;
3247 p
.setAlphaMask(null);
3248 Future job
= p
.getProject().getLoader().regenerateMipMaps(p
); // submit to queue
3249 if (null != job
) jobs
.add(job
);
3252 for (final Future job
: jobs
) try {
3254 } catch (Exception ie
) {}
3255 }}, patches
.get(0).getProject());
3257 } else if (command
.equals("Undo")) {
3258 Bureaucrat
.createAndStart(new Worker
.Task("Undo") { public void exec() {
3259 layer
.getParent().undoOneStep();
3260 Display
.repaint(layer
.getParent());
3262 } else if (command
.equals("Redo")) {
3263 Bureaucrat
.createAndStart(new Worker
.Task("Redo") { public void exec() {
3264 layer
.getParent().redoOneStep();
3265 Display
.repaint(layer
.getParent());
3267 } else if (command
.equals("Apply transform")) {
3268 if (null == active
) return;
3269 canvas
.applyTransform();
3270 } else if (command
.equals("Apply transform propagating to last layer")) {
3271 if (mode
.getClass() == AffineTransformMode
.class) {
3272 final java
.util
.List
<Layer
> layers
= layer
.getParent().getLayers();
3273 ((AffineTransformMode
)mode
).applyAndPropagate(new HashSet
<Layer
>(layers
.subList(layers
.indexOf(Display
.this.layer
)+1, layers
.size()))); // +1 to exclude current layer
3274 setMode(new DefaultMode(Display
.this));
3276 } else if (command
.equals("Apply transform propagating to first layer")) {
3277 if (mode
.getClass() == AffineTransformMode
.class) {
3278 final java
.util
.List
<Layer
> layers
= layer
.getParent().getLayers();
3279 ((AffineTransformMode
)mode
).applyAndPropagate(new HashSet
<Layer
>(layers
.subList(0, layers
.indexOf(Display
.this.layer
))));
3280 setMode(new DefaultMode(Display
.this));
3282 } else if (command
.equals("Cancel transform")) {
3283 if (null == active
) return;
3284 canvas
.cancelTransform(); // calls getMode().cancel()
3285 } else if (command
.equals("Specify transform...")) {
3286 if (null == active
) return;
3287 selection
.specify();
3288 } else if (command
.equals("Hide all but images")) {
3289 ArrayList
<Class
> type
= new ArrayList
<Class
>();
3290 type
.add(Patch
.class);
3291 type
.add(Stack
.class);
3292 Collection
<Displayable
> col
= layer
.getParent().hideExcept(type
, false);
3293 selection
.removeAll(col
);
3294 Display
.updateCheckboxes(col
, DisplayablePanel
.VISIBILITY_STATE
);
3295 Display
.update(layer
.getParent(), false);
3296 } else if (command
.equals("Unhide all")) {
3297 Display
.updateCheckboxes(layer
.getParent().setAllVisible(false), DisplayablePanel
.VISIBILITY_STATE
);
3298 Display
.update(layer
.getParent(), false);
3299 } else if (command
.startsWith("Hide all ")) {
3300 String type
= command
.substring(9, command
.length() -1); // skip the ending plural 's'
3301 type
= type
.substring(0, 1).toUpperCase() + type
.substring(1);
3302 Collection
<Displayable
> col
= layer
.getParent().setVisible(type
, false, true);
3303 selection
.removeAll(col
);
3304 Display
.updateCheckboxes(col
, DisplayablePanel
.VISIBILITY_STATE
);
3305 } else if (command
.startsWith("Unhide all ")) {
3306 String type
= command
.substring(11, command
.length() -1); // skip the ending plural 's'
3307 type
= type
.substring(0, 1).toUpperCase() + type
.substring(1);
3308 updateCheckboxes(layer
.getParent().setVisible(type
, true, true), DisplayablePanel
.VISIBILITY_STATE
);
3309 } else if (command
.equals("Hide deselected")) {
3310 hideDeselected(0 != (ActionEvent
.ALT_MASK
& ae
.getModifiers()));
3311 } else if (command
.equals("Hide deselected except images")) {
3312 hideDeselected(true);
3313 } else if (command
.equals("Hide selected")) {
3314 selection
.setVisible(false); // TODO should deselect them too? I don't think so.
3315 Display
.updateCheckboxes(selection
.getSelected(), DisplayablePanel
.VISIBILITY_STATE
);
3316 } else if (command
.equals("Resize canvas/LayerSet...")) {
3318 } else if (command
.equals("Autoresize canvas/LayerSet")) {
3319 layer
.getParent().setMinimumDimensions();
3320 } else if (command
.equals("Import image")) {
3322 } else if (command
.equals("Import next image")) {
3324 } else if (command
.equals("Import stack...")) {
3325 Display
.this.getLayerSet().addChangeTreesStep();
3326 Rectangle sr
= getCanvas().getSrcRect();
3327 Bureaucrat burro
= project
.getLoader().importStack(layer
, sr
.x
+ sr
.width
/2, sr
.y
+ sr
.height
/2, null, true, null, false);
3328 burro
.addPostTask(new Runnable() { public void run() {
3329 Display
.this.getLayerSet().addChangeTreesStep();
3331 } else if (command
.equals("Import stack with landmarks...")) {
3332 // 1 - Find out if there's any other project open
3333 List
<Project
> pr
= Project
.getProjects();
3334 if (1 == pr
.size()) {
3335 Utils
.logAll("Need another project open!");
3338 // 2 - Ask for a "landmarks" type
3339 GenericDialog gd
= new GenericDialog("Landmarks");
3340 gd
.addStringField("landmarks type:", "landmarks");
3341 final String
[] none
= {"-- None --"};
3342 final Hashtable
<String
,Project
> mpr
= new Hashtable
<String
,Project
>();
3343 for (Project p
: pr
) {
3344 if (p
== project
) continue;
3345 mpr
.put(p
.toString(), p
);
3347 final String
[] project_titles
= mpr
.keySet().toArray(new String
[0]);
3349 final Hashtable
<String
,ProjectThing
> map_target
= findLandmarkNodes(project
, "landmarks");
3350 String
[] target_landmark_titles
= map_target
.isEmpty() ? none
: map_target
.keySet().toArray(new String
[0]);
3351 gd
.addChoice("Landmarks node in this project:", target_landmark_titles
, target_landmark_titles
[0]);
3354 gd
.addChoice("Source project:", project_titles
, project_titles
[0]);
3356 final Hashtable
<String
,ProjectThing
> map_source
= findLandmarkNodes(mpr
.get(project_titles
[0]), "landmarks");
3357 String
[] source_landmark_titles
= map_source
.isEmpty() ? none
: map_source
.keySet().toArray(new String
[0]);
3358 gd
.addChoice("Landmarks node in source project:", source_landmark_titles
, source_landmark_titles
[0]);
3360 final List
<Patch
> stacks
= Display
.getPatchStacks(mpr
.get(project_titles
[0]).getRootLayerSet());
3362 String
[] stack_titles
;
3363 if (stacks
.isEmpty()) {
3364 if (1 == mpr
.size()) {
3365 IJ
.showMessage("Project " + project_titles
[0] + " does not contain any Stack.");
3368 stack_titles
= none
;
3370 stack_titles
= new String
[stacks
.size()];
3372 for (Patch pa
: stacks
) stack_titles
[next
++] = pa
.toString();
3374 gd
.addChoice("Stacks:", stack_titles
, stack_titles
[0]);
3376 Vector vc
= gd
.getChoices();
3377 final Choice choice_target_landmarks
= (Choice
) vc
.get(0);
3378 final Choice choice_source_projects
= (Choice
) vc
.get(1);
3379 final Choice choice_source_landmarks
= (Choice
) vc
.get(2);
3380 final Choice choice_stacks
= (Choice
) vc
.get(3);
3382 final TextField input
= (TextField
) gd
.getStringFields().get(0);
3383 input
.addTextListener(new TextListener() {
3384 public void textValueChanged(TextEvent te
) {
3385 final String text
= input
.getText();
3386 update(choice_target_landmarks
, Display
.this.project
, text
, map_target
);
3387 update(choice_source_landmarks
, mpr
.get(choice_source_projects
.getSelectedItem()), text
, map_source
);
3389 private void update(Choice c
, Project p
, String type
, Hashtable
<String
,ProjectThing
> table
) {
3391 table
.putAll(findLandmarkNodes(p
, type
));
3393 if (table
.isEmpty()) c
.add(none
[0]);
3394 else for (String t
: table
.keySet()) c
.add(t
);
3398 choice_source_projects
.addItemListener(new ItemListener() {
3399 public void itemStateChanged(ItemEvent e
) {
3400 String item
= (String
) e
.getItem();
3401 Project p
= mpr
.get(choice_source_projects
.getSelectedItem());
3402 // 1 - Update choice of landmark items
3404 map_source
.putAll(findLandmarkNodes(p
, input
.getText()));
3405 choice_target_landmarks
.removeAll();
3406 if (map_source
.isEmpty()) choice_target_landmarks
.add(none
[0]);
3407 else for (String t
: map_source
.keySet()) choice_target_landmarks
.add(t
);
3408 // 2 - Update choice of Stack items
3410 choice_stacks
.removeAll();
3411 stacks
.addAll(Display
.getPatchStacks(mpr
.get(project_titles
[0]).getRootLayerSet()));
3412 if (stacks
.isEmpty()) choice_stacks
.add(none
[0]);
3413 else for (Patch pa
: stacks
) choice_stacks
.add(pa
.toString());
3418 if (gd
.wasCanceled()) return;
3420 String type
= gd
.getNextString();
3421 if (null == type
|| 0 == type
.trim().length()) {
3422 Utils
.log("Invalid landmarks node type!");
3425 ProjectThing target_landmarks_node
= map_target
.get(gd
.getNextChoice());
3426 Project source
= mpr
.get(gd
.getNextChoice());
3427 ProjectThing source_landmarks_node
= map_source
.get(gd
.getNextChoice());
3428 Patch stack_patch
= stacks
.get(gd
.getNextChoiceIndex());
3430 // Store current state
3431 Display
.this.getLayerSet().addLayerContentStep(layer
);
3434 insertStack(target_landmarks_node
, source
, source_landmarks_node
, stack_patch
);
3437 Display
.this.getLayerSet().addChangeTreesStep();
3438 } else if (command
.equals("Import grid...")) {
3439 Display
.this.getLayerSet().addLayerContentStep(layer
);
3440 Bureaucrat burro
= project
.getLoader().importGrid(layer
);
3442 burro
.addPostTask(new Runnable() { public void run() {
3443 Display
.this.getLayerSet().addLayerContentStep(layer
);
3445 } else if (command
.equals("Import sequence as grid...")) {
3446 Display
.this.getLayerSet().addLayerContentStep(layer
);
3447 Bureaucrat burro
= project
.getLoader().importSequenceAsGrid(layer
);
3449 burro
.addPostTask(new Runnable() { public void run() {
3450 Display
.this.getLayerSet().addLayerContentStep(layer
);
3452 } else if (command
.equals("Import from text file...")) {
3453 Display
.this.getLayerSet().addLayerContentStep(layer
);
3454 Bureaucrat burro
= project
.getLoader().importImages(layer
);
3456 burro
.addPostTask(new Runnable() { public void run() {
3457 Display
.this.getLayerSet().addLayerContentStep(layer
);
3459 } else if (command
.equals("Import labels as arealists...")) {
3460 Display
.this.getLayerSet().addChangeTreesStep();
3461 Bureaucrat burro
= project
.getLoader().importLabelsAsAreaLists(layer
, null, Double
.MAX_VALUE
, 0, 0.4f
, false);
3462 burro
.addPostTask(new Runnable() { public void run() {
3463 Display
.this.getLayerSet().addChangeTreesStep();
3465 } else if (command
.equals("Make flat image...")) {
3466 // if there's a ROI, just use that as cropping rectangle
3467 Rectangle srcRect
= null;
3468 Roi roi
= canvas
.getFakeImagePlus().getRoi();
3470 srcRect
= roi
.getBounds();
3472 // otherwise, whatever is visible
3473 //srcRect = canvas.getSrcRect();
3474 // The above is confusing. That is what ROIs are for. So paint all:
3475 srcRect
= new Rectangle(0, 0, (int)Math
.ceil(layer
.getParent().getLayerWidth()), (int)Math
.ceil(layer
.getParent().getLayerHeight()));
3478 final String
[] types
= new String
[]{"8-bit grayscale", "RGB Color"};
3479 int the_type
= ImagePlus
.GRAY8
;
3480 final GenericDialog gd
= new GenericDialog("Choose", frame
);
3481 gd
.addSlider("Scale: ", 1, 100, 100);
3482 gd
.addNumericField("Width: ", srcRect
.width
, 0);
3483 gd
.addNumericField("height: ", srcRect
.height
, 0);
3484 // connect the above 3 fields:
3485 Vector numfields
= gd
.getNumericFields();
3486 UpdateDimensionField udf
= new UpdateDimensionField(srcRect
.width
, srcRect
.height
, (TextField
) numfields
.get(1), (TextField
) numfields
.get(2), (TextField
) numfields
.get(0), (Scrollbar
) gd
.getSliders().get(0));
3487 for (Object ob
: numfields
) ((TextField
)ob
).addTextListener(udf
);
3489 gd
.addChoice("Type: ", types
, types
[0]);
3490 if (layer
.getParent().size() > 1) {
3491 Utils
.addLayerRangeChoices(Display
.this.layer
, gd
); /// $#%! where are my lisp macros
3492 gd
.addCheckbox("Include non-empty layers only", true);
3494 gd
.addMessage("Background color:");
3495 Utils
.addRGBColorSliders(gd
, Color
.black
);
3496 gd
.addCheckbox("Best quality", false);
3498 gd
.addCheckbox("Save to file", false);
3499 gd
.addCheckbox("Save for web", false);
3501 if (gd
.wasCanceled()) return;
3502 scale
= gd
.getNextNumber() / 100;
3503 the_type
= (0 == gd
.getNextChoiceIndex() ? ImagePlus
.GRAY8
: ImagePlus
.COLOR_RGB
);
3504 if (Double
.isNaN(scale
) || scale
<= 0.0) {
3505 Utils
.showMessage("Invalid scale.");
3509 // consuming and ignoring width and height:
3513 Layer
[] layer_array
= null;
3514 boolean non_empty_only
= false;
3515 if (layer
.getParent().size() > 1) {
3516 non_empty_only
= gd
.getNextBoolean();
3517 int i_start
= gd
.getNextChoiceIndex();
3518 int i_end
= gd
.getNextChoiceIndex();
3519 ArrayList al
= new ArrayList();
3520 ArrayList al_zd
= layer
.getParent().getZDisplayables();
3521 ZDisplayable
[] zd
= new ZDisplayable
[al_zd
.size()];
3523 for (int i
=i_start
, j
=0; i
<= i_end
; i
++, j
++) {
3524 Layer la
= layer
.getParent().getLayer(i
);
3525 if (!la
.isEmpty() || !non_empty_only
) al
.add(la
); // checks both the Layer and the ZDisplayable objects in the parent LayerSet
3527 if (0 == al
.size()) {
3528 Utils
.showMessage("All layers are empty!");
3531 layer_array
= new Layer
[al
.size()];
3532 al
.toArray(layer_array
);
3534 layer_array
= new Layer
[]{Display
.this.layer
};
3536 final Color background
= new Color((int)gd
.getNextNumber(), (int)gd
.getNextNumber(), (int)gd
.getNextNumber());
3537 final boolean quality
= gd
.getNextBoolean();
3538 final boolean save_to_file
= gd
.getNextBoolean();
3539 final boolean save_for_web
= gd
.getNextBoolean();
3540 // in its own thread
3541 if (save_for_web
) project
.getLoader().makePrescaledTiles(layer_array
, Patch
.class, srcRect
, scale
, c_alphas
, the_type
);
3542 else project
.getLoader().makeFlatImage(layer_array
, srcRect
, scale
, c_alphas
, the_type
, save_to_file
, quality
, background
);
3544 } else if (command
.equals("Lock")) {
3545 selection
.setLocked(true);
3546 Utils
.revalidateComponent(tabs
.getSelectedComponent());
3547 } else if (command
.equals("Unlock")) {
3548 selection
.setLocked(false);
3549 Utils
.revalidateComponent(tabs
.getSelectedComponent());
3550 } else if (command
.equals("Properties...")) {
3551 active
.adjustProperties();
3553 } else if (command
.equals("Cancel alignment")) {
3554 layer
.getParent().cancelAlign();
3555 } else if (command
.equals("Align with landmarks")) {
3556 layer
.getParent().applyAlign(false);
3557 } else if (command
.equals("Align and register")) {
3558 layer
.getParent().applyAlign(true);
3559 } else if (command
.equals("Align using profiles")) {
3560 if (!selection
.contains(Profile
.class)) {
3561 Utils
.showMessage("No profiles are selected.");
3564 // ask for range of layers
3565 final GenericDialog gd
= new GenericDialog("Choose range");
3566 Utils
.addLayerRangeChoices(Display
.this.layer
, gd
);
3568 if (gd
.wasCanceled()) return;
3569 Layer la_start
= layer
.getParent().getLayer(gd
.getNextChoiceIndex());
3570 Layer la_end
= layer
.getParent().getLayer(gd
.getNextChoiceIndex());
3571 if (la_start
== la_end
) {
3572 Utils
.showMessage("Need at least two layers.");
3575 if (selection
.isLocked()) {
3576 Utils
.showMessage("There are locked objects.");
3579 layer
.getParent().startAlign(Display
.this);
3580 layer
.getParent().applyAlign(la_start
, la_end
, selection
);
3581 } else if (command
.equals("Align stack slices")) {
3582 if (getActive() instanceof Patch
) {
3583 final Patch slice
= (Patch
)getActive();
3584 if (slice
.isStack()) {
3585 // check linked group
3586 final HashSet hs
= slice
.getLinkedGroup(new HashSet());
3587 for (Iterator it
= hs
.iterator(); it
.hasNext(); ) {
3588 if (it
.next().getClass() != Patch
.class) {
3589 Utils
.showMessage("Images are linked to other objects, can't proceed to cross-correlate them."); // labels should be fine, need to check that
3593 final LayerSet ls
= slice
.getLayerSet();
3594 final HashSet
<Displayable
> linked
= slice
.getLinkedGroup(null);
3595 ls
.addTransformStep(linked
);
3596 Bureaucrat burro
= AlignTask
.registerStackSlices((Patch
)getActive()); // will repaint
3597 burro
.addPostTask(new Runnable() { public void run() {
3598 // The current state when done
3599 ls
.addTransformStep(linked
);
3602 Utils
.log("Align stack slices: selected image is not part of a stack.");
3605 } else if (command
.equals("Align layers")) {
3606 final Layer la
= layer
;; // caching, since scroll wheel may change it
3607 la
.getParent().addTransformStep(la
);
3608 Bureaucrat burro
= AlignTask
.alignLayersLinearlyTask( la
);
3609 burro
.addPostTask(new Runnable() { public void run() {
3610 la
.getParent().addTransformStep(la
);
3612 } else if (command
.equals("Align multi-layer mosaic")) {
3613 final Layer la
= layer
; // caching, since scroll wheel may change it
3614 la
.getParent().addTransformStep();
3615 Bureaucrat burro
= AlignTask
.alignMultiLayerMosaicTask( la
);
3616 burro
.addPostTask(new Runnable() { public void run() {
3617 la
.getParent().addTransformStep();
3619 } else if (command
.equals("Properties ...")) { // NOTE the space before the dots, to distinguish from the "Properties..." command that works on Displayable objects.
3620 GenericDialog gd
= new GenericDialog("Properties", Display
.this.frame
);
3621 //gd.addNumericField("layer_scroll_step: ", this.scroll_step, 0);
3622 gd
.addSlider("layer_scroll_step: ", 1, layer
.getParent().size(), Display
.this.scroll_step
);
3623 gd
.addChoice("snapshots_mode", LayerSet
.snapshot_modes
, LayerSet
.snapshot_modes
[layer
.getParent().getSnapshotsMode()]);
3624 gd
.addCheckbox("prefer_snapshots_quality", layer
.getParent().snapshotsQuality());
3625 Loader lo
= getProject().getLoader();
3626 boolean using_mipmaps
= lo
.isMipMapsEnabled();
3627 gd
.addCheckbox("enable_mipmaps", using_mipmaps
);
3628 gd
.addCheckbox("enable_layer_pixels virtualization", layer
.getParent().isPixelsVirtualizationEnabled());
3629 double max
= layer
.getParent().getLayerWidth() < layer
.getParent().getLayerHeight() ? layer
.getParent().getLayerWidth() : layer
.getParent().getLayerHeight();
3630 gd
.addSlider("max_dimension of virtualized layer pixels: ", 0, max
, layer
.getParent().getPixelsMaxDimension());
3633 if (gd
.wasCanceled()) return;
3635 int sc
= (int) gd
.getNextNumber();
3637 Display
.this.scroll_step
= sc
;
3638 updateInDatabase("scroll_step");
3640 layer
.getParent().setSnapshotsMode(gd
.getNextChoiceIndex());
3641 layer
.getParent().setSnapshotsQuality(gd
.getNextBoolean());
3643 boolean generate_mipmaps
= gd
.getNextBoolean();
3644 if (using_mipmaps
&& generate_mipmaps
) {
3647 if (using_mipmaps
) { // and !generate_mipmaps
3648 lo
.flushMipMaps(true);
3650 // not using mipmaps before, and true == generate_mipmaps
3651 lo
.generateMipMaps(layer
.getParent().getDisplayables(Patch
.class));
3655 layer
.getParent().setPixelsVirtualizationEnabled(gd
.getNextBoolean());
3656 layer
.getParent().setPixelsMaxDimension((int)gd
.getNextNumber());
3657 } else if (command
.equals("Adjust snapping parameters...")) {
3658 AlignTask
.p_snap
.setup("Snap");
3659 } else if (command
.equals("Adjust fast-marching parameters...")) {
3660 Segmentation
.fmp
.setup();
3661 } else if (command
.equals("Adjust arealist paint parameters...")) {
3662 AreaList
.PP
.setup();
3663 } else if (command
.equals("Search...")) {
3665 } else if (command
.equals("Select all")) {
3666 selection
.selectAll();
3667 repaint(Display
.this.layer
, selection
.getBox(), 0);
3668 } else if (command
.equals("Select all visible")) {
3669 selection
.selectAllVisible();
3670 repaint(Display
.this.layer
, selection
.getBox(), 0);
3671 } else if (command
.equals("Select none")) {
3672 Rectangle box
= selection
.getBox();
3674 repaint(Display
.this.layer
, box
, 0);
3675 } else if (command
.equals("Restore selection")) {
3676 selection
.restore();
3677 } else if (command
.equals("Select under ROI")) {
3678 Roi roi
= canvas
.getFakeImagePlus().getRoi();
3679 if (null == roi
) return;
3680 selection
.selectAll(roi
, true);
3681 } else if (command
.equals("Merge")) {
3682 Bureaucrat burro
= Bureaucrat
.create(new Worker
.Task("Merging AreaLists") {
3683 public void exec() {
3684 ArrayList al_sel
= selection
.getSelected(AreaList
.class);
3685 // put active at the beginning, to work as the base on which other's will get merged
3686 al_sel
.remove(Display
.this.active
);
3687 al_sel
.add(0, Display
.this.active
);
3688 Set
<DoStep
> dataedits
= new HashSet
<DoStep
>();
3689 dataedits
.add(new Displayable
.DoEdit(Display
.this.active
).init(Display
.this.active
, new String
[]{"data"}));
3690 getLayerSet().addChangeTreesStep(dataedits
);
3691 AreaList ali
= AreaList
.merge(al_sel
);
3693 // remove all but the first from the selection
3694 for (int i
=1; i
<al_sel
.size(); i
++) {
3695 Object ob
= al_sel
.get(i
);
3696 if (ob
.getClass() == AreaList
.class) {
3697 selection
.remove((Displayable
)ob
);
3700 selection
.updateTransform(ali
);
3701 repaint(ali
.getLayerSet(), ali
, 0);
3704 }, Display
.this.project
);
3705 burro
.addPostTask(new Runnable() { public void run() {
3706 Set
<DoStep
> dataedits
= new HashSet
<DoStep
>();
3707 dataedits
.add(new Displayable
.DoEdit(Display
.this.active
).init(Display
.this.active
, new String
[]{"data"}));
3708 getLayerSet().addChangeTreesStep(dataedits
);
3710 burro
.goHaveBreakfast();
3711 } else if (command
.equals("Reverse point order")) {
3712 if (!(active
instanceof Pipe
)) return;
3713 getLayerSet().addDataEditStep(active
);
3714 ((Pipe
)active
).reverse();
3715 Display
.repaint(Display
.this.layer
);
3716 getLayerSet().addDataEditStep(active
);
3717 } else if (command
.equals("Identify...")) {
3718 // for pipes only for now
3719 if (!(active
instanceof Line3D
)) return;
3720 ini
.trakem2
.vector
.Compare
.findSimilar((Line3D
)active
);
3721 } else if (command
.equals("Identify with axes...")) {
3722 if (!(active
instanceof Pipe
)) return;
3723 if (Project
.getProjects().size() < 2) {
3724 Utils
.showMessage("You need at least two projects open:\n-A reference project\n-The current project with the pipe to identify");
3727 ini
.trakem2
.vector
.Compare
.findSimilarWithAxes((Line3D
)active
);
3728 } else if (command
.equals("Identify with fiducials...")) {
3729 if (!(active
instanceof Line3D
)) return;
3730 ini
.trakem2
.vector
.Compare
.findSimilarWithFiducials((Line3D
)active
);
3731 } else if (command
.equals("View orthoslices")) {
3732 if (!(active
instanceof Patch
)) return;
3733 Display3D
.showOrthoslices(((Patch
)active
));
3734 } else if (command
.equals("View volume")) {
3735 if (!(active
instanceof Patch
)) return;
3736 Display3D
.showVolume(((Patch
)active
));
3737 } else if (command
.equals("Show in 3D")) {
3738 for (Iterator it
= selection
.getSelected(ZDisplayable
.class).iterator(); it
.hasNext(); ) {
3739 ZDisplayable zd
= (ZDisplayable
)it
.next();
3740 Display3D
.show(zd
.getProject().findProjectThing(zd
));
3742 // handle profile lists ...
3743 HashSet hs
= new HashSet();
3744 for (Iterator it
= selection
.getSelected(Profile
.class).iterator(); it
.hasNext(); ) {
3745 Displayable d
= (Displayable
)it
.next();
3746 ProjectThing profile_list
= (ProjectThing
)d
.getProject().findProjectThing(d
).getParent();
3747 if (!hs
.contains(profile_list
)) {
3748 Display3D
.show(profile_list
);
3749 hs
.add(profile_list
);
3752 } else if (command
.equals("Snap")) {
3753 // Take the active if it's a Patch
3754 if (!(active
instanceof Patch
)) return;
3755 Display
.snap((Patch
)active
);
3756 } else if (command
.equals("Blend")) {
3757 HashSet
<Patch
> patches
= new HashSet
<Patch
>();
3758 for (final Displayable d
: selection
.getSelected()) {
3759 if (d
.getClass() == Patch
.class) patches
.add((Patch
)d
);
3761 if (patches
.size() > 1) {
3762 GenericDialog gd
= new GenericDialog("Blending");
3763 gd
.addCheckbox("Respect current alpha mask", true);
3765 if (gd
.wasCanceled()) return;
3766 Blending
.blend(patches
, gd
.getNextBoolean());
3768 IJ
.log("Please select more than one overlapping image.");
3770 } else if (command
.equals("Montage")) {
3771 if (!(active
instanceof Patch
)) {
3772 Utils
.showMessage("Please select only images.");
3775 final Set
<Displayable
> affected
= new HashSet
<Displayable
>(selection
.getAffected());
3776 for (final Displayable d
: affected
)
3778 Utils
.showMessage( "You cannot montage linked objects." );
3781 // make an undo step!
3782 final LayerSet ls
= layer
.getParent();
3783 ls
.addTransformStep(affected
);
3784 Bureaucrat burro
= AlignTask
.alignSelectionTask( selection
);
3785 burro
.addPostTask(new Runnable() { public void run() {
3786 ls
.addTransformStep(affected
);
3788 } else if (command
.equals("Lens correction")) {
3789 final Layer la
= layer
;
3790 la
.getParent().addDataEditStep(new HashSet
<Displayable
>(la
.getParent().getDisplayables()));
3791 Bureaucrat burro
= DistortionCorrectionTask
.correctDistortionFromSelection( selection
);
3792 burro
.addPostTask(new Runnable() { public void run() {
3793 // no means to know which where modified and from which layers!
3794 la
.getParent().addDataEditStep(new HashSet
<Displayable
>(la
.getParent().getDisplayables()));
3796 } else if (command
.equals("Link images...")) {
3797 GenericDialog gd
= new GenericDialog("Options");
3798 gd
.addMessage("Linking images to images (within their own layer only):");
3799 String
[] options
= {"all images to all images", "each image with any other overlapping image"};
3800 gd
.addChoice("Link: ", options
, options
[1]);
3801 String
[] options2
= {"selected images only", "all images in this layer", "all images in all layers, within the layer only", "all images in all layers, within and across consecutive layers"};
3802 gd
.addChoice("Apply to: ", options2
, options2
[0]);
3804 if (gd
.wasCanceled()) return;
3806 final HashSet
<Displayable
> ds
= new HashSet
<Displayable
>(lay
.getParent().getDisplayables());
3807 lay
.getParent().addDataEditStep(ds
, new String
[]{"data"});
3808 boolean overlapping_only
= 1 == gd
.getNextChoiceIndex();
3809 Collection
<Displayable
> coll
= null;
3810 switch (gd
.getNextChoiceIndex()) {
3812 coll
= selection
.getSelected(Patch
.class);
3813 Patch
.crosslink(coll
, overlapping_only
);
3816 coll
= lay
.getDisplayables(Patch
.class);
3817 Patch
.crosslink(coll
, overlapping_only
);
3820 coll
= new ArrayList
<Displayable
>();
3821 for (final Layer la
: lay
.getParent().getLayers()) {
3822 Collection
<Displayable
> acoll
= la
.getDisplayables(Patch
.class);
3823 Patch
.crosslink(acoll
, overlapping_only
);
3828 ArrayList
<Layer
> layers
= lay
.getParent().getLayers();
3829 Collection
<Displayable
> lc1
= layers
.get(0).getDisplayables(Patch
.class);
3830 if (lay
== layers
.get(0)) coll
= lc1
;
3831 for (int i
=1; i
<layers
.size(); i
++) {
3832 Collection
<Displayable
> lc2
= layers
.get(i
).getDisplayables(Patch
.class);
3833 if (null == coll
&& Display
.this.layer
== layers
.get(i
)) coll
= lc2
;
3834 Collection
<Displayable
> both
= new ArrayList
<Displayable
>();
3837 Patch
.crosslink(both
, overlapping_only
);
3842 if (null != coll
) Display
.updateCheckboxes(coll
, DisplayablePanel
.LINK_STATE
, true);
3843 lay
.getParent().addDataEditStep(ds
);
3844 } else if (command
.equals("Unlink all selected images")) {
3845 if (Utils
.check("Really unlink selected images?")) {
3846 for (final Displayable d
: selection
.getSelected(Patch
.class)) {
3850 } else if (command
.equals("Unlink all")) {
3851 if (Utils
.check("Really unlink all objects from all layers?")) {
3852 for (final Displayable d
: layer
.getParent().getDisplayables()) {
3856 } else if (command
.equals("Calibration...")) {
3858 IJ
.run(canvas
.getFakeImagePlus(), "Properties...", "");
3859 } catch (RuntimeException re
) {
3860 Utils
.log2("Calibration dialog canceled.");
3862 } else if (command
.equals("Grid overlay...")) {
3863 if (null == gridoverlay
) gridoverlay
= new GridOverlay();
3864 gridoverlay
.setup(canvas
.getFakeImagePlus().getRoi());
3865 canvas
.invalidateVolatile();
3866 canvas
.repaint(false);
3867 } else if (command
.equals("Enhance contrast (selected images)...")) {
3868 final Layer la
= layer
;
3869 final HashSet
<Displayable
> ds
= new HashSet
<Displayable
>(la
.getParent().getDisplayables());
3870 la
.getParent().addDataEditStep(ds
);
3871 ArrayList al
= selection
.getSelected(Patch
.class);
3872 Bureaucrat burro
= getProject().getLoader().homogenizeContrast(al
);
3873 burro
.addPostTask(new Runnable() { public void run() {
3874 la
.getParent().addDataEditStep(ds
);
3876 } else if (command
.equals("Enhance contrast layer-wise...")) {
3877 // ask for range of layers
3878 final GenericDialog gd
= new GenericDialog("Choose range");
3879 Utils
.addLayerRangeChoices(Display
.this.layer
, gd
);
3881 if (gd
.wasCanceled()) return;
3882 java
.util
.List list
= layer
.getParent().getLayers().subList(gd
.getNextChoiceIndex(), gd
.getNextChoiceIndex() +1); // exclusive end
3883 Layer
[] la
= new Layer
[list
.size()];
3885 final HashSet
<Displayable
> ds
= new HashSet
<Displayable
>();
3886 for (final Layer l
: la
) ds
.addAll(l
.getDisplayables(Patch
.class));
3887 getLayerSet().addDataEditStep(ds
);
3888 Bureaucrat burro
= project
.getLoader().homogenizeContrast(la
);
3889 burro
.addPostTask(new Runnable() { public void run() {
3890 getLayerSet().addDataEditStep(ds
);
3892 } else if (command
.equals("Set Min and Max layer-wise...")) {
3893 Displayable active
= getActive();
3896 if (null != active
&& active
.getClass() == Patch
.class) {
3897 min
= ((Patch
)active
).getMin();
3898 max
= ((Patch
)active
).getMax();
3900 final GenericDialog gd
= new GenericDialog("Min and Max");
3901 gd
.addMessage("Set min and max to all images in the layer range");
3902 Utils
.addLayerRangeChoices(Display
.this.layer
, gd
);
3903 gd
.addNumericField("min: ", min
, 2);
3904 gd
.addNumericField("max: ", max
, 2);
3906 if (gd
.wasCanceled()) return;
3908 min
= gd
.getNextNumber();
3909 max
= gd
.getNextNumber();
3910 ArrayList
<Displayable
> al
= new ArrayList
<Displayable
>();
3911 for (final Layer la
: layer
.getParent().getLayers().subList(gd
.getNextChoiceIndex(), gd
.getNextChoiceIndex() +1)) { // exclusive end
3912 al
.addAll(la
.getDisplayables(Patch
.class));
3914 final HashSet
<Displayable
> ds
= new HashSet
<Displayable
>(al
);
3915 getLayerSet().addDataEditStep(ds
);
3916 Bureaucrat burro
= project
.getLoader().setMinAndMax(al
, min
, max
);
3917 burro
.addPostTask(new Runnable() { public void run() {
3918 getLayerSet().addDataEditStep(ds
);
3920 } else if (command
.equals("Set Min and Max (selected images)...")) {
3921 Displayable active
= getActive();
3924 if (null != active
&& active
.getClass() == Patch
.class) {
3925 min
= ((Patch
)active
).getMin();
3926 max
= ((Patch
)active
).getMax();
3928 final GenericDialog gd
= new GenericDialog("Min and Max");
3929 gd
.addMessage("Set min and max to all selected images");
3930 gd
.addNumericField("min: ", min
, 2);
3931 gd
.addNumericField("max: ", max
, 2);
3933 if (gd
.wasCanceled()) return;
3935 min
= gd
.getNextNumber();
3936 max
= gd
.getNextNumber();
3937 final HashSet
<Displayable
> ds
= new HashSet
<Displayable
>(selection
.getSelected(Patch
.class));
3938 getLayerSet().addDataEditStep(ds
);
3939 Bureaucrat burro
= project
.getLoader().setMinAndMax(selection
.getSelected(Patch
.class), min
, max
);
3940 burro
.addPostTask(new Runnable() { public void run() {
3941 getLayerSet().addDataEditStep(ds
);
3943 } else if (command
.equals("Duplicate")) {
3944 // only Patch and DLabel, i.e. Layer-only resident objects that don't exist in the Project Tree
3945 final HashSet
<Class
> accepted
= new HashSet
<Class
>();
3946 accepted
.add(Patch
.class);
3947 accepted
.add(DLabel
.class);
3948 accepted
.add(Stack
.class);
3949 final ArrayList
<Displayable
> originals
= new ArrayList
<Displayable
>();
3950 final ArrayList
<Displayable
> selected
= selection
.getSelected();
3951 for (final Displayable d
: selected
) {
3952 if (accepted
.contains(d
.getClass())) {
3956 if (originals
.size() > 0) {
3957 getLayerSet().addChangeTreesStep();
3958 for (final Displayable d
: originals
) {
3959 if (d
instanceof ZDisplayable
) {
3960 d
.getLayerSet().add((ZDisplayable
)d
.clone());
3962 d
.getLayer().add(d
.clone());
3965 getLayerSet().addChangeTreesStep();
3966 } else if (selected
.size() > 0) {
3967 Utils
.log("Can only duplicate images and text labels.\nDuplicate *other* objects in the Project Tree.\n");
3969 } else if (command
.equals("Create subproject")) {
3970 Roi roi
= canvas
.getFakeImagePlus().getRoi();
3971 if (null == roi
) return; // the menu item is not active unless there is a ROI
3973 if (1 == layer
.getParent().size()) {
3974 first
= last
= layer
;
3976 GenericDialog gd
= new GenericDialog("Choose layer range");
3977 Utils
.addLayerRangeChoices(layer
, gd
);
3979 if (gd
.wasCanceled()) return;
3980 first
= layer
.getParent().getLayer(gd
.getNextChoiceIndex());
3981 last
= layer
.getParent().getLayer(gd
.getNextChoiceIndex());
3982 Utils
.log2("first, last: " + first
+ ", " + last
);
3984 Project sub
= getProject().createSubproject(roi
.getBounds(), first
, last
);
3985 final LayerSet subls
= sub
.getRootLayerSet();
3986 Display
.createDisplay(sub
, subls
.getLayer(0));
3987 } else if (command
.startsWith("Arealists as labels")) {
3988 GenericDialog gd
= new GenericDialog("Export labels");
3989 gd
.addSlider("Scale: ", 1, 100, 100);
3990 final String
[] options
= {"All area list", "Selected area lists"};
3991 gd
.addChoice("Export: ", options
, options
[0]);
3992 Utils
.addLayerRangeChoices(layer
, gd
);
3993 gd
.addCheckbox("Visible only", true);
3995 if (gd
.wasCanceled()) return;
3996 final float scale
= (float)(gd
.getNextNumber() / 100);
3997 java
.util
.List al
= 0 == gd
.getNextChoiceIndex() ? layer
.getParent().getZDisplayables(AreaList
.class) : selection
.getSelected(AreaList
.class);
3999 Utils
.log("No area lists found to export.");
4002 // 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?
4003 al
= (java
.util
.List
<Displayable
>) al
;
4005 int first
= gd
.getNextChoiceIndex();
4006 int last
= gd
.getNextChoiceIndex();
4007 boolean visible_only
= gd
.getNextBoolean();
4008 if (-1 != command
.indexOf("(amira)")) {
4009 AreaList
.exportAsLabels(al
, canvas
.getFakeImagePlus().getRoi(), scale
, first
, last
, visible_only
, true, true);
4010 } else if (-1 != command
.indexOf("(tif)")) {
4011 AreaList
.exportAsLabels(al
, canvas
.getFakeImagePlus().getRoi(), scale
, first
, last
, visible_only
, false, false);
4013 } else if (command
.equals("Project properties...")) {
4014 project
.adjustProperties();
4015 } else if (command
.equals("Release memory...")) {
4016 Bureaucrat
.createAndStart(new Worker("Releasing memory") {
4020 GenericDialog gd
= new GenericDialog("Release Memory");
4021 int max
= (int)(IJ
.maxMemory() / 1000000);
4022 gd
.addSlider("Megabytes: ", 0, max
, max
/2);
4024 if (!gd
.wasCanceled()) {
4025 int n_mb
= (int)gd
.getNextNumber();
4026 project
.getLoader().releaseToFit((long)n_mb
*1000000);
4028 } catch (Throwable e
) {
4035 } else if (command
.equals("Flush image cache")) {
4036 Loader
.releaseAllCaches();
4037 } else if (command
.equals("Regenerate all mipmaps")) {
4038 project
.getLoader().regenerateMipMaps(getLayerSet().getDisplayables(Patch
.class));
4039 } else if (command
.equals("Regenerate mipmaps (selected images)")) {
4040 project
.getLoader().regenerateMipMaps(selection
.getSelected(Patch
.class));
4042 Utils
.log2("Display: don't know what to do with command " + command
);
4047 private static class UpdateDimensionField
implements TextListener
{
4048 final TextField width
, height
, scale
;
4049 final Scrollbar bar
;
4050 final int initial_width
, initial_height
;
4051 UpdateDimensionField(int initial_width
, int initial_height
, TextField width
, TextField height
, TextField scale
, Scrollbar bar
) {
4052 this.initial_width
= initial_width
;
4053 this.initial_height
= initial_height
;
4055 this.height
= height
;
4059 public void textValueChanged(TextEvent e
) {
4061 final TextField source
= (TextField
) e
.getSource();
4062 if (scale
== source
&& (scale
.isFocusOwner() || bar
.isFocusOwner())) {
4063 final double sc
= Double
.parseDouble(scale
.getText()) / 100;
4065 width
.setText(Integer
.toString((int) (sc
* initial_width
+ 0.5)));
4066 height
.setText(Integer
.toString((int) (sc
* initial_height
+ 0.5)));
4067 } else if (width
== source
&& width
.isFocusOwner()) {
4069 final int width = Integer.toString((int) (width.getText() + 0.5));
4070 final double sc = width / (double)initial_width;
4071 scale.setText(Integer.toString((int)(sc * 100 + 0.5)));
4072 height.setText(Integer.toString((int)(sc * initial_height + 0.5)));
4074 set(width
, height
, initial_width
, initial_height
);
4075 } else if (height
== source
&& height
.isFocusOwner()) {
4076 set(height
, width
, initial_height
, initial_width
);
4078 } catch (NumberFormatException nfe
) {
4079 Utils
.logAll("Unparsable number: " + nfe
.getMessage());
4080 } catch (Exception ee
) {
4084 private void set(TextField source
, TextField target
, int initial_source
, int initial_target
) {
4085 final int dim
= (int) ((Double
.parseDouble(source
.getText()) + 0.5));
4086 final double sc
= dim
/ (double)initial_source
;
4087 scale
.setText(Utils
.cutNumber(sc
* 100, 3));
4088 target
.setText(Integer
.toString((int)(sc
* initial_target
+ 0.5)));
4093 /** Update in all displays the Transform for the given Displayable if it's selected. */
4094 static public void updateTransform(final Displayable displ
) {
4095 for (final Display d
: al_displays
) {
4096 if (d
.selection
.contains(displ
)) d
.selection
.updateTransform(displ
);
4100 /** Order the profiles of the parent profile_list by Z order, and fix the ProjectTree.*/
4102 private void fixZOrdering(Profile profile) {
4103 ProjectThing thing = project.findProjectThing(profile);
4104 if (null == thing) {
4105 Utils.log2("Display.fixZOrdering: null thing?");
4108 ((ProjectThing)thing.getParent()).fixZOrdering();
4109 project.getProjectTree().updateList(thing.getParent());
4113 /** The number of layers to scroll through with the wheel; 1 by default.*/
4114 public int getScrollStep() { return this.scroll_step
; }
4116 public void setScrollStep(int scroll_step
) {
4117 if (scroll_step
< 1) scroll_step
= 1;
4118 this.scroll_step
= scroll_step
;
4119 updateInDatabase("scroll_step");
4122 protected Bureaucrat
importImage() {
4123 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
4129 Rectangle srcRect
= canvas
.getSrcRect();
4130 int x
= srcRect
.x
+ srcRect
.width
/ 2;
4131 int y
= srcRect
.y
+ srcRect
.height
/ 2;
4132 Patch p
= project
.getLoader().importImage(project
, x
, y
);
4135 Utils
.showMessage("Could not open the image.");
4139 Display
.this.getLayerSet().addLayerContentStep(layer
);
4141 layer
.add(p
); // will add it to the proper Displays
4143 Display
.this.getLayerSet().addLayerContentStep(layer
);
4146 } catch (Exception e
) {
4152 return Bureaucrat
.createAndStart(worker
, getProject());
4155 protected Bureaucrat
importNextImage() {
4156 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
4161 Rectangle srcRect
= canvas
.getSrcRect();
4162 int x
= srcRect
.x
+ srcRect
.width
/ 2;// - imp.getWidth() / 2;
4163 int y
= srcRect
.y
+ srcRect
.height
/ 2;// - imp.getHeight()/ 2;
4164 Patch p
= project
.getLoader().importNextImage(project
, x
, y
);
4166 Utils
.showMessage("Could not open next image.");
4171 Display
.this.getLayerSet().addLayerContentStep(layer
);
4173 layer
.add(p
); // will add it to the proper Displays
4175 Display
.this.getLayerSet().addLayerContentStep(layer
);
4177 } catch (Exception e
) {
4183 return Bureaucrat
.createAndStart(worker
, getProject());
4187 /** Make the given channel have the given alpha (transparency). */
4188 public void setChannel(int c
, float alpha
) {
4189 int a
= (int)(255 * alpha
);
4190 int l
= (c_alphas
&0xff000000)>>24;
4191 int r
= (c_alphas
&0xff0000)>>16;
4192 int g
= (c_alphas
&0xff00)>>8;
4193 int b
= c_alphas
&0xff;
4196 // all to the given alpha
4197 c_alphas
= (l
<<24) + (r
<<16) + (g
<<8) + b
; // parenthesis are NECESSARY
4200 // modify only the red
4201 c_alphas
= (l
<<24) + (a
<<16) + (g
<<8) + b
;
4204 c_alphas
= (l
<<24) + (r
<<16) + (a
<<8) + b
;
4207 c_alphas
= (l
<<24) + (r
<<16) + (g
<<8) + a
;
4210 //Utils.log2("c_alphas: " + c_alphas);
4211 //canvas.setUpdateGraphics(true);
4212 canvas
.repaint(true);
4213 updateInDatabase("c_alphas");
4216 /** Set the channel as active and the others as inactive. */
4217 public void setActiveChannel(Channel channel
) {
4218 for (int i
=0; i
<4; i
++) {
4219 if (channel
!= channels
[i
]) channels
[i
].setActive(false);
4220 else channel
.setActive(true);
4222 Utils
.updateComponent(panel_channels
);
4223 transp_slider
.setValue((int)(channel
.getAlpha() * 100));
4226 public int getDisplayChannelAlphas() { return c_alphas
; }
4228 // rename this method and the getDisplayChannelAlphas ! They sound the same!
4229 public int getChannelAlphas() {
4230 return ((int)(channels
[0].getAlpha() * 255)<<24) + ((int)(channels
[1].getAlpha() * 255)<<16) + ((int)(channels
[2].getAlpha() * 255)<<8) + (int)(channels
[3].getAlpha() * 255);
4233 public int getChannelAlphasState() {
4234 return ((channels
[0].isSelected() ?
255 : 0)<<24)
4235 + ((channels
[1].isSelected() ?
255 : 0)<<16)
4236 + ((channels
[2].isSelected() ?
255 : 0)<<8)
4237 + (channels
[3].isSelected() ?
255 : 0);
4240 /** Show the layer in the front Display, or in a new Display if the front Display is showing a layer from a different LayerSet. */
4241 static public void showFront(final Layer layer
) {
4242 Display display
= front
;
4243 if (null == display
|| display
.layer
.getParent() != layer
.getParent()) {
4244 display
= new Display(layer
.getProject(), layer
, null); // gets set to front
4246 display
.setLayer(layer
);
4250 /** Show the given Displayable centered and selected. If select is false, the selection is cleared. */
4251 static public void showCentered(Layer layer
, Displayable displ
, boolean select
, boolean shift_down
) {
4252 // see if the given layer belongs to the layer set being displayed
4253 Display display
= front
; // to ensure thread consistency to some extent
4254 if (null == display
|| display
.layer
.getParent() != layer
.getParent()) {
4255 display
= new Display(layer
.getProject(), layer
, displ
); // gets set to front
4256 } else if (display
.layer
!= layer
) {
4257 display
.setLayer(layer
);
4260 if (!shift_down
) display
.selection
.clear();
4261 display
.selection
.add(displ
);
4263 display
.selection
.clear();
4265 display
.showCentered(displ
);
4268 private final void showCentered(final Displayable displ
) {
4269 if (null == displ
) return;
4270 SwingUtilities
.invokeLater(new Runnable() { public void run() {
4271 displ
.setVisible(true);
4272 Rectangle box
= displ
.getBoundingBox();
4273 if (0 == box
.width
|| 0 == box
.height
) {
4274 box
.width
= (int)layer
.getLayerWidth();
4275 box
.height
= (int)layer
.getLayerHeight();
4277 canvas
.showCentered(box
);
4278 scrollToShow(displ
);
4279 if (displ
instanceof ZDisplayable
) {
4280 // scroll to first layer that has a point
4281 ZDisplayable zd
= (ZDisplayable
)displ
;
4282 setLayer(zd
.getFirstLayer());
4287 /** Listen to interesting updates, such as the ColorPicker and updates to Patch objects. */
4288 public void imageUpdated(ImagePlus updated
) {
4289 // detect ColorPicker WARNING this will work even if the Display is not the window immediately active under the color picker.
4290 if (this == front
&& updated
instanceof ij
.plugin
.ColorPicker
) {
4291 if (null != active
&& project
.isInputEnabled()) {
4292 selection
.setColor(Toolbar
.getForegroundColor());
4293 Display
.repaint(front
.layer
, selection
.getBox(), 0);
4297 // $%#@!! LUT changes don't set the image as changed
4298 //if (updated instanceof PatchStack) {
4299 // updated.changes = 1
4302 //Utils.log2("imageUpdated: " + updated + " " + updated.getClass());
4304 /* // never gets called (?)
4305 // the above is overkill. Instead:
4306 if (updated instanceof PatchStack) {
4307 Patch p = ((PatchStack)updated).getCurrentPatch();
4308 ImageProcessor ip = updated.getProcessor();
4309 p.setMinAndMax(ip.getMin(), ip.getMax());
4310 Utils.log2("setting min and max: " + ip.getMin() + ", " + ip.getMax());
4311 project.getLoader().decacheAWT(p.getId()); // including level 0, which will be editable
4312 // on repaint, it will be recreated
4313 //((PatchStack)updated).decacheAll(); // so that it will repaint with a newly created image
4317 // detect LUT changes: DONE at PatchStack, which is the active (virtual) image
4318 //Utils.log2("calling decache for " + updated);
4319 //getProject().getLoader().decache(updated);
4322 public void imageClosed(ImagePlus imp
) {}
4323 public void imageOpened(ImagePlus imp
) {}
4325 /** Release memory captured by the offscreen images */
4326 static public void flushAll() {
4327 for (final Display d
: al_displays
) {
4335 static public Display
getFront() {
4339 static public void setCursorToAll(final Cursor c
) {
4340 for (final Display d
: al_displays
) {
4341 d
.frame
.setCursor(c
);
4345 protected void setCursor(Cursor c
) {
4349 /** Used by the Displayable to update the visibility and locking state checkboxes in other Displays. */
4350 static public void updateCheckboxes(final Displayable displ
, final int cb
, final boolean state
) {
4351 for (final Display d
: al_displays
) {
4352 DisplayablePanel dp
= d
.ht_panels
.get(displ
);
4353 if (null != dp
) dp
.updateCheckbox(cb
, state
);
4356 /** Set the checkbox @param cb state to @param state value, for each Displayable. Assumes all Displayable objects belong to one specific project. */
4357 static public void updateCheckboxes(final Collection
<Displayable
> displs
, final int cb
, final boolean state
) {
4358 if (null == displs
|| 0 == displs
.size()) return;
4359 final Project p
= displs
.iterator().next().getProject();
4360 for (final Display d
: al_displays
) {
4361 if (d
.getProject() != p
) continue;
4362 for (final Displayable displ
: displs
) {
4363 DisplayablePanel dp
= d
.ht_panels
.get(displ
);
4364 if (null != dp
) dp
.updateCheckbox(cb
, state
);
4368 /** Update the checkbox @param cb state to an appropriate value for each Displayable. Assumes all Displayable objects belong to one specific project. */
4369 static public void updateCheckboxes(final Collection
<Displayable
> displs
, final int cb
) {
4370 if (null == displs
|| 0 == displs
.size()) return;
4371 final Project p
= displs
.iterator().next().getProject();
4372 for (final Display d
: al_displays
) {
4373 if (d
.getProject() != p
) continue;
4374 for (final Displayable displ
: displs
) {
4375 DisplayablePanel dp
= d
.ht_panels
.get(displ
);
4376 if (null != dp
) dp
.updateCheckbox(cb
);
4381 protected boolean isActiveWindow() {
4382 return frame
.isActive();
4385 /** Toggle user input; pan and zoom are always enabled though.*/
4386 static public void setReceivesInput(final Project project
, final boolean b
) {
4387 for (final Display d
: al_displays
) {
4388 if (d
.project
== project
) d
.canvas
.setReceivesInput(b
);
4392 /** Export the DTD that defines this object. */
4393 static public void exportDTD(StringBuffer sb_header
, HashSet hs
, String indent
) {
4394 if (hs
.contains("t2_display")) return; // TODO to avoid collisions the type shoud be in a namespace such as tm2:display
4395 hs
.add("t2_display");
4396 sb_header
.append(indent
).append("<!ELEMENT t2_display EMPTY>\n")
4397 .append(indent
).append("<!ATTLIST t2_display id NMTOKEN #REQUIRED>\n")
4398 .append(indent
).append("<!ATTLIST t2_display layer_id NMTOKEN #REQUIRED>\n")
4399 .append(indent
).append("<!ATTLIST t2_display x NMTOKEN #REQUIRED>\n")
4400 .append(indent
).append("<!ATTLIST t2_display y NMTOKEN #REQUIRED>\n")
4401 .append(indent
).append("<!ATTLIST t2_display magnification NMTOKEN #REQUIRED>\n")
4402 .append(indent
).append("<!ATTLIST t2_display srcrect_x NMTOKEN #REQUIRED>\n")
4403 .append(indent
).append("<!ATTLIST t2_display srcrect_y NMTOKEN #REQUIRED>\n")
4404 .append(indent
).append("<!ATTLIST t2_display srcrect_width NMTOKEN #REQUIRED>\n")
4405 .append(indent
).append("<!ATTLIST t2_display srcrect_height NMTOKEN #REQUIRED>\n")
4406 .append(indent
).append("<!ATTLIST t2_display scroll_step NMTOKEN #REQUIRED>\n")
4407 .append(indent
).append("<!ATTLIST t2_display c_alphas NMTOKEN #REQUIRED>\n")
4408 .append(indent
).append("<!ATTLIST t2_display c_alphas_state NMTOKEN #REQUIRED>\n")
4411 /** Export all displays of the given project as XML entries. */
4412 static public void exportXML(final Project project
, final Writer writer
, final String indent
, final Object any
) throws Exception
{
4413 final StringBuffer sb_body
= new StringBuffer();
4414 final String in
= indent
+ "\t";
4415 for (final Display d
: al_displays
) {
4416 if (d
.project
!= project
) continue;
4417 final Rectangle r
= d
.frame
.getBounds();
4418 final Rectangle srcRect
= d
.canvas
.getSrcRect();
4419 final double magnification
= d
.canvas
.getMagnification();
4420 sb_body
.append(indent
).append("<t2_display id=\"").append(d
.id
).append("\"\n")
4421 .append(in
).append("layer_id=\"").append(d
.layer
.getId()).append("\"\n")
4422 .append(in
).append("c_alphas=\"").append(d
.c_alphas
).append("\"\n")
4423 .append(in
).append("c_alphas_state=\"").append(d
.getChannelAlphasState()).append("\"\n")
4424 .append(in
).append("x=\"").append(r
.x
).append("\"\n")
4425 .append(in
).append("y=\"").append(r
.y
).append("\"\n")
4426 .append(in
).append("magnification=\"").append(magnification
).append("\"\n")
4427 .append(in
).append("srcrect_x=\"").append(srcRect
.x
).append("\"\n")
4428 .append(in
).append("srcrect_y=\"").append(srcRect
.y
).append("\"\n")
4429 .append(in
).append("srcrect_width=\"").append(srcRect
.width
).append("\"\n")
4430 .append(in
).append("srcrect_height=\"").append(srcRect
.height
).append("\"\n")
4431 .append(in
).append("scroll_step=\"").append(d
.scroll_step
).append("\"\n")
4433 sb_body
.append(indent
).append("/>\n");
4435 writer
.write(sb_body
.toString());
4438 // Never called; ProjectToolbar.toolChanged is also never called, which should forward here.
4439 static public void toolChanged(final String tool_name
) {
4440 Utils
.log2("tool name: " + tool_name
);
4441 if (!tool_name
.equals("ALIGN")) {
4442 for (final Display d
: al_displays
) {
4443 d
.layer
.getParent().cancelAlign();
4446 for (final Display d
: al_displays
) {
4447 Utils
.updateComponent(d
.toolbar_panel
);
4448 if (tool_name
.equals("PEN")) {
4449 AreaList
.PP
.updateGUI(d
.tool_options_panel
);
4451 d
.tool_options_panel
.removeAll();
4454 Utils
.updateComponent(d
.tool_options_panel
);
4455 Utils
.log2("updating toolbar_panel");
4459 static public void toolChanged(final int tool
) {
4460 //Utils.log2("int tool is " + tool);
4461 if (ProjectToolbar
.PEN
== tool
) {
4462 // erase bounding boxes
4463 for (final Display d
: al_displays
) {
4464 if (null != d
.active
) d
.repaint(d
.layer
, d
.selection
.getBox(), 2);
4467 if (null != front
) {
4468 WindowManager
.setTempCurrentImage(front
.canvas
.getFakeImagePlus());
4470 for (final Display d
: al_displays
) {
4472 d
.toolbar_panel
.invalidate();
4473 d
.toolbar_panel
.validate();
4474 d
.toolbar_panel
.repaint();
4476 if (ProjectToolbar
.PEN
== tool
) {
4477 AreaList
.PP
.updateGUI(d
.tool_options_panel
);
4479 d
.tool_options_panel
.removeAll();
4481 Utils
.updateComponent(d
.tool_options_panel
);
4482 Utils
.log2("updating toolbar_panel");
4486 public Selection
getSelection() {
4490 public boolean isSelected(Displayable d
) {
4491 return selection
.contains(d
);
4494 static public void updateSelection() {
4495 Display
.updateSelection(null);
4497 static public void updateSelection(final Display calling
) {
4498 final HashSet hs
= new HashSet();
4499 for (final Display d
: al_displays
) {
4500 if (hs
.contains(d
.layer
)) continue;
4502 if (null == d
|| null == d
.selection
) {
4503 Utils
.log2("d is : "+ d
+ " d.selection is " + d
.selection
);
4505 d
.selection
.update(); // recomputes box
4507 if (d
!= calling
) { // TODO this is so dirty!
4508 if (d
.selection
.getNLinked() > 1) d
.canvas
.setUpdateGraphics(true); // this is overkill anyway
4509 d
.canvas
.repaint(d
.selection
.getLinkedBox(), Selection
.PADDING
);
4510 d
.navigator
.repaint(true); // everything
4515 static public void clearSelection(final Layer layer
) {
4516 for (final Display d
: al_displays
) {
4517 if (d
.layer
== layer
) d
.selection
.clear();
4520 static public void clearSelection() {
4521 for (final Display d
: al_displays
) {
4522 d
.selection
.clear();
4526 private void setTempCurrentImage() {
4527 WindowManager
.setCurrentWindow(canvas
.getFakeImagePlus().getWindow(), true);
4528 WindowManager
.setTempCurrentImage(canvas
.getFakeImagePlus());
4531 /** Check if any display will paint the given Displayable at the given magnification. */
4532 static public boolean willPaint(final Displayable displ
, final double magnification
) {
4533 Rectangle box
= null; ;
4534 for (final Display d
: al_displays
) {
4535 /* // Can no longer do this check, because 'magnification' is now affected by the Displayable AffineTransform! And thus it would not paint after the prePaint.
4536 if (Math.abs(d.canvas.getMagnification() - magnification) > 0.00000001) {
4540 if (null == box
) box
= displ
.getBoundingBox(null);
4541 if (d
.canvas
.getSrcRect().intersects(box
)) {
4548 public void hideDeselected(final boolean not_images
) {
4550 final ArrayList all
= layer
.getParent().getZDisplayables(); // a copy
4551 all
.addAll(layer
.getDisplayables());
4552 all
.removeAll(selection
.getSelected());
4553 if (not_images
) all
.removeAll(layer
.getDisplayables(Patch
.class));
4554 for (final Displayable d
: (ArrayList
<Displayable
>)all
) {
4555 if (d
.isVisible()) {
4556 d
.setVisible(false);
4557 Display
.updateCheckboxes(d
, DisplayablePanel
.VISIBILITY_STATE
, false);
4560 Display
.update(layer
);
4563 /** Cleanup internal lists that may contain the given Displayable. */
4564 static public void flush(final Displayable displ
) {
4565 for (final Display d
: al_displays
) {
4566 d
.selection
.removeFromPrev(displ
);
4570 public void resizeCanvas() {
4571 GenericDialog gd
= new GenericDialog("Resize LayerSet");
4572 gd
.addNumericField("new width: ", layer
.getLayerWidth(), 1, 8, "pixels");
4573 gd
.addNumericField("new height: ", layer
.getLayerHeight(), 1, 8, "pixels");
4574 gd
.addChoice("Anchor: ", LayerSet
.ANCHORS
, LayerSet
.ANCHORS
[7]);
4576 if (gd
.wasCanceled()) return;
4577 double new_width
= gd
.getNextNumber();
4578 double new_height
=gd
.getNextNumber();
4579 layer
.getParent().setDimensions(new_width
, new_height
, gd
.getNextChoiceIndex()); // will complain and prevent cropping existing Displayable objects
4583 // To record layer changes -- but it's annoying, this is visualization not data.
4584 static class DoSetLayer implements DoStep {
4585 final Display display;
4587 DoSetLayer(final Display display) {
4588 this.display = display;
4589 this.layer = display.layer;
4591 public Displayable getD() { return null; }
4592 public boolean isEmpty() { return false; }
4593 public boolean apply(final int action) {
4594 display.setLayer(layer);
4596 public boolean isIdenticalTo(final Object ob) {
4597 if (!ob instanceof DoSetLayer) return false;
4598 final DoSetLayer dsl = (DoSetLayer) ob;
4599 return dsl.display == this.display && dsl.layer == this.layer;
4604 protected void duplicateLinkAndSendTo(final Displayable active
, final int position
, final Layer other_layer
) {
4605 if (null == active
|| !(active
instanceof Profile
)) return;
4606 if (active
.getLayer() == other_layer
) return; // can't do that!
4607 Profile profile
= project
.getProjectTree().duplicateChild((Profile
)active
, position
, other_layer
);
4608 if (null == profile
) return;
4609 active
.link(profile
);
4610 other_layer
.add(profile
);
4611 slt
.setAndWait(other_layer
);
4612 selection
.add(profile
);
4615 private final HashMap
<Color
,Layer
> layer_channels
= new HashMap
<Color
,Layer
>();
4616 private final TreeMap
<Integer
,LayerPanel
> layer_alpha
= new TreeMap
<Integer
,LayerPanel
>();
4617 boolean invert_colors
= false;
4619 /** Remove all red/blue coloring of layers, and repaint canvas. */
4620 protected void resetLayerColors() {
4621 synchronized (layer_channels
) {
4622 for (final Layer l
: new ArrayList
<Layer
>(layer_channels
.values())) { // avoid concurrent modification exception
4623 final LayerPanel lp
= layer_panels
.get(l
);
4624 lp
.setColor(Color
.white
);
4625 setColorChannel(lp
.layer
, Color
.white
);
4626 lp
.slider
.setEnabled(true);
4628 layer_channels
.clear();
4633 /** Set all layer alphas to zero, and repaint canvas. */
4634 protected void resetLayerAlphas() {
4635 synchronized (layer_channels
) {
4636 for (final LayerPanel lp
: new ArrayList
<LayerPanel
>(layer_alpha
.values())) {
4639 layer_alpha
.clear(); // should have already been cleared
4644 /** Add to layer_alpha table, or remove if alpha is zero. */
4645 protected void storeLayerAlpha(final LayerPanel lp
, final float a
) {
4646 synchronized (layer_channels
) {
4647 if (M
.equals(0, a
)) {
4648 layer_alpha
.remove(lp
.layer
.getParent().indexOf(lp
.layer
));
4650 layer_alpha
.put(lp
.layer
.getParent().indexOf(lp
.layer
), lp
);
4655 static protected final int REPAINT_SINGLE_LAYER
= 0;
4656 static protected final int REPAINT_MULTI_LAYER
= 1;
4657 static protected final int REPAINT_RGB_LAYER
= 2;
4659 /** Sets the values atomically, returns the painting mode. */
4660 protected int getPaintMode(final HashMap
<Color
,Layer
> hm
, final ArrayList
<LayerPanel
> list
) {
4661 synchronized (layer_channels
) {
4662 if (layer_channels
.size() > 0) {
4663 hm
.putAll(layer_channels
);
4664 hm
.put(Color
.green
, this.layer
);
4665 return REPAINT_RGB_LAYER
;
4667 list
.addAll(layer_alpha
.values());
4668 final int len
= list
.size();
4669 if (len
> 1) return REPAINT_MULTI_LAYER
;
4671 if (list
.get(0).layer
== this.layer
) return REPAINT_SINGLE_LAYER
; // normal mode
4672 return REPAINT_MULTI_LAYER
;
4674 return REPAINT_SINGLE_LAYER
;
4678 /** Set a layer to be painted as a specific color channel in the canvas.
4679 * Only Color.red and Color.blue are accepted.
4680 * Color.green is reserved for the current layer. */
4681 protected void setColorChannel(final Layer layer
, final Color color
) {
4682 synchronized (layer_channels
) {
4683 if (Color
.white
== color
) {
4685 for (final Iterator
<Layer
> it
= layer_channels
.values().iterator(); it
.hasNext(); ) {
4686 if (it
.next() == layer
) {
4692 } else if (Color
.red
== color
|| Color
.blue
== color
) {
4693 // Reset current of that color, if any, to white
4694 final Layer l
= layer_channels
.remove(color
);
4695 if (null != l
) layer_panels
.get(l
).setColor(Color
.white
);
4696 // Replace or set new
4697 layer_channels
.put(color
, layer
);
4701 Utils
.log2("Trying to set unacceptable color for layer " + layer
+ " : " + color
);
4703 // enable/disable sliders
4704 final boolean b
= 0 == layer_channels
.size();
4705 for (final LayerPanel lp
: layer_panels
.values()) lp
.slider
.setEnabled(b
);
4707 this.canvas
.repaint(true);
4710 static public final void updateComponentTreeUI() {
4712 for (final Display d
: al_displays
) SwingUtilities
.updateComponentTreeUI(d
.frame
);
4713 } catch (Exception e
) {
4718 /** Snap a Patch to the most overlapping Patch, if any.
4719 * This method is a shallow wrap around AlignTask.snap, setting proper undo steps. */
4720 static public final Bureaucrat
snap(final Patch patch
) {
4721 final Set
<Displayable
> linked
= patch
.getLinkedGroup(null);
4722 patch
.getLayerSet().addTransformStep(linked
);
4723 Bureaucrat burro
= AlignTask
.snap(patch
, null, false);
4724 burro
.addPostTask(new Runnable() { public void run() {
4725 patch
.getLayerSet().addTransformStep(linked
);
4730 private Mode mode
= new DefaultMode(this);
4732 public void setMode(final Mode mode
) {
4734 canvas
.repaint(true);
4735 scroller
.setEnabled(mode
.getClass() == DefaultMode
.class);
4738 public Mode
getMode() {
4743 static private final Hashtable
<String
,ProjectThing
> findLandmarkNodes(Project p
, String landmarks_type
) {
4744 Set
<ProjectThing
> landmark_nodes
= p
.getRootProjectThing().findChildrenOfTypeR(landmarks_type
);
4745 Hashtable
<String
,ProjectThing
> map
= new Hashtable
<String
,ProjectThing
>();
4746 for (ProjectThing pt
: landmark_nodes
) {
4747 map
.put(pt
.toString() + "# " + pt
.getId(), pt
);
4752 /** @param stack_patch is just a Patch of a series of Patch that make a stack of Patches. */
4753 private boolean insertStack(ProjectThing target_landmarks
, Project source
, ProjectThing source_landmarks
, Patch stack_patch
) {
4754 List
<Ball
> l1
= new ArrayList
<Ball
>();
4755 List
<Ball
> l2
= new ArrayList
<Ball
>();
4756 Collection
<ProjectThing
> b1s
= source_landmarks
.findChildrenOfType("ball"); // source is the one that has the stack_patch
4757 Collection
<ProjectThing
> b2s
= target_landmarks
.findChildrenOfType("ball"); // target is this
4758 HashSet
<String
> seen
= new HashSet
<String
>();
4759 for (ProjectThing b1
: b1s
) {
4760 Ball ball1
= (Ball
) b1
.getObject();
4761 if (null == ball1
) {
4762 Utils
.log("ERROR: there's an empty 'ball' node in target project" + project
.toString());
4765 String title1
= ball1
.getTitle();
4766 for (ProjectThing b2
: b2s
) {
4767 Ball ball2
= (Ball
) b2
.getObject();
4768 if (null == ball2
) {
4769 Utils
.log("ERROR: there's an empty 'ball' node in source project" + source
.toString());
4772 if (title1
.equals(ball2
.getTitle())) {
4773 if (seen
.contains(title1
)) continue;
4780 if (l1
.size() < 4) {
4781 Utils
.log("ERROR: found only " + l1
.size() + " common landmarks: needs at least 4!");
4784 // Extract coordinates of source project landmarks, in patch stack coordinate space
4785 List
<float[]> c1
= new ArrayList
<float[]>();
4786 for (Ball ball1
: l1
) {
4787 Map
<Layer
,double[]> m
= ball1
.getRawBalls();
4788 if (1 != m
.size()) {
4789 Utils
.log("ERROR: ball object " + ball1
+ " from target project " + project
+ " has " + m
.size() + " balls instead of just 1.");
4792 Map
.Entry
<Layer
,double[]> e
= m
.entrySet().iterator().next();
4793 Layer layer
= e
.getKey();
4794 double[] xyr
= e
.getValue();
4795 float[] fin
= new float[]{(float)xyr
[0], (float)xyr
[1]};
4796 AffineTransform affine
= ball1
.getAffineTransformCopy();
4798 affine
.preConcatenate(stack_patch
.getAffineTransform().createInverse());
4799 } catch (Exception nite
) {
4800 IJError
.print(nite
);
4803 float[] fout
= new float[2];
4804 affine
.transform(fin
, 0, fout
, 0, 1);
4805 c1
.add(new float[]{fout
[0], fout
[1], layer
.getParent().indexOf(layer
)});
4808 // Extract coordinates of target (this) project landmarks, in calibrated world space
4809 List
<float[]> c2
= new ArrayList
<float[]>();
4810 for (Ball ball2
: l2
) {
4811 double[][] b
= ball2
.getBalls();
4812 if (1 != b
.length
) {
4813 Utils
.log("ERROR: ball object " + ball2
+ " from source project " + source
+ " has " + b
.length
+ " balls instead of just 1.");
4816 float[] fin
= new float[]{(float)b
[0][0], (float)b
[0][1]};
4817 AffineTransform affine
= ball2
.getAffineTransformCopy();
4818 float[] fout
= new float[2];
4819 affine
.transform(fin
, 0, fout
, 0, 1);
4820 c2
.add(new float[]{fout
[0], fout
[1], (float)b
[0][2]});
4824 Utils
.log("Landmarks:");
4825 for (Iterator
<float[]> it1
= c1
.iterator(), it2
= c2
.iterator(); it1
.hasNext(); ) {
4826 Utils
.log(Utils
.toString(it1
.next()) + " <--> " + Utils
.toString(it2
.next()));
4829 // Create point matches
4830 List
<PointMatch
> pm
= new ArrayList
<PointMatch
>();
4831 for (Iterator
<float[]> it1
= c1
.iterator(), it2
= c2
.iterator(); it1
.hasNext(); ) {
4832 pm
.add(new mpicbg
.models
.PointMatch(new mpicbg
.models
.Point(it1
.next()), new mpicbg
.models
.Point(it2
.next())));
4835 // Estimate AffineModel3D
4836 AffineModel3D aff3d
= new AffineModel3D();
4839 } catch (Exception e
) {
4844 // Create and add the Stack
4845 String path
= stack_patch
.getImageFilePath();
4846 Stack st
= new Stack(project
, new File(path
).getName(), 0, 0, getLayerSet().getLayers().get(0), path
);
4847 st
.setInvertibleCoordinateTransform(aff3d
);
4848 getLayerSet().add(st
);
4852 static private List
<Patch
> getPatchStacks(final LayerSet ls
) {
4853 HashSet
<Patch
> stacks
= new HashSet
<Patch
>();
4854 for (Patch pa
: (Collection
<Patch
>) (Collection
) ls
.getDisplayables(Patch
.class)) {
4855 PatchStack ps
= pa
.makePatchStack();
4856 if (1 == ps
.getNSlices()) continue;
4857 stacks
.add(ps
.getPatch(0));
4859 return new ArrayList
<Patch
>(stacks
);