Updated copyright dates.
[trakem2.git] / ini / trakem2 / tree / LayerTree.java
blob248f188ab50d8c76a8e6bae178c07c1eaa1c3592
1 /**
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.
21 **/
23 package ini.trakem2.tree;
25 import ij.gui.GenericDialog;
27 import ini.trakem2.ControlWindow;
28 import ini.trakem2.Project;
29 import ini.trakem2.display.Display;
30 import ini.trakem2.display.DLabel;
31 import ini.trakem2.display.Layer;
32 import ini.trakem2.display.LayerSet;
33 import ini.trakem2.persistence.DBObject;
34 import ini.trakem2.persistence.FSLoader;
35 import ini.trakem2.utils.IJError;
36 import ini.trakem2.utils.Utils;
37 import ini.trakem2.utils.Search;
39 import java.awt.Component;
40 import java.awt.Point;
41 import java.awt.Color;
42 import java.awt.Event;
43 import java.awt.event.ActionEvent;
44 import java.awt.event.ActionListener;
45 import java.awt.event.MouseEvent;
46 import java.awt.event.MouseListener;
47 import java.util.Hashtable;
48 import java.util.Iterator;
49 //import java.util.Enumeration;
50 import java.util.ArrayList;
51 import java.util.HashSet;
53 import javax.swing.JMenuItem;
54 import javax.swing.JPopupMenu;
55 import javax.swing.JScrollPane;
56 import javax.swing.tree.DefaultMutableTreeNode;
57 import javax.swing.tree.DefaultTreeModel;
58 import javax.swing.tree.TreePath;
59 import javax.swing.tree.TreeSelectionModel;
61 public final class LayerTree extends DNDTree implements MouseListener, ActionListener {
63 private DefaultMutableTreeNode selected_node = null;
65 public LayerTree(Project project, LayerThing root) {
66 super(project, DNDTree.makeNode(root), new Color(230, 235, 255)); // Color(200, 200, 255));
67 setEditable(false);
68 addMouseListener(this);
69 // enable multiple discontiguous selection
70 this.getSelectionModel().setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
73 /** Get a custom, context-sensitive popup menu for the selected node. */
74 private JPopupMenu getPopupMenu(DefaultMutableTreeNode node) {
75 Object ob = node.getUserObject();
76 LayerThing thing = null;
77 if (ob instanceof LayerThing) {
78 thing = (LayerThing)ob;
79 } else {
80 return null;
82 // context-sensitive popup
83 JMenuItem[] item = thing.getPopupItems(this);
84 if (0 == item.length) return null;
85 JPopupMenu popup = new JPopupMenu();
86 for (int i=0; i<item.length; i++) {
87 if (null == item[i] || "" == item[i].getText()) popup.addSeparator();
88 else popup.add(item[i]);
90 return popup;
93 public void mousePressed(MouseEvent me) {
94 Object source = me.getSource();
95 if (!source.equals(this) || !Project.getInstance(this).isInputEnabled()) {
96 return;
99 // ignore if doing multiple selection
100 if (!me.isPopupTrigger() && (me.isShiftDown() || (!ij.IJ.isMacOSX() && me.isControlDown()))) {
101 return;
104 int x = me.getX();
105 int y = me.getY();
107 // check if there is a multiple selection
108 TreePath[] paths = this.getSelectionPaths();
109 if (null != paths && paths.length > 1) {
110 if (me.isPopupTrigger() || MouseEvent.BUTTON2 == me.getButton() || 0 != (me.getModifiers() & Event.META_MASK)) {
111 // check that all items are of the same type
112 String type_first = ((LayerThing)((DefaultMutableTreeNode)paths[0].getLastPathComponent()).getUserObject()).getType();
113 for (int i=1; i<paths.length; i++) {
114 String type = ((LayerThing)((DefaultMutableTreeNode)paths[i].getLastPathComponent()).getUserObject()).getType();
115 if (!type.equals(type_first)) {
116 Utils.showMessage("All selected items must be of the same type for operations on multiple items.");
117 return;
120 // prepare popup menu
121 JPopupMenu popup = new JPopupMenu();
122 JMenuItem item = null;
123 if (type_first.equals("layer")) {
124 item = new JMenuItem("Reverse layer Z coords"); item.addActionListener(this); popup.add(item);
125 item = new JMenuItem("Translate layers in Z..."); item.addActionListener(this); popup.add(item);
126 item = new JMenuItem("Scale Z and thickness..."); item.addActionListener(this); popup.add(item);
127 item = new JMenuItem("Delete..."); item.addActionListener(this); popup.add(item);
129 if (popup.getSubElements().length > 0) {
130 popup.show(this, x, y);
133 // disable commands depending upon a single node being selected
134 selected_node = null;
135 return;
138 // find the node and set it selected
139 TreePath path = getPathForLocation(x, y);
140 if (null == path) {
141 return;
143 setSelectionPath(path);
144 selected_node = (DefaultMutableTreeNode)path.getLastPathComponent();
146 if (2 == me.getClickCount() && !me.isPopupTrigger() && MouseEvent.BUTTON1 == me.getButton()) {
147 // create a new Display
148 LayerThing thing = (LayerThing)selected_node.getUserObject();
149 DBObject ob = (DBObject)thing.getObject();
150 if (thing.getType().toLowerCase().replace('_', ' ').equals("layer set") && null == ((LayerSet)ob).getParent()) { // the top level LayerSet
151 return;
153 //new Display(ob.getProject(), thing.getType().toLowerCase().equals("layer") ? (Layer)ob : ((LayerSet)ob).getParent());
154 Display.createDisplay(ob.getProject(), thing.getType().toLowerCase().equals("layer") ? (Layer)ob : ((LayerSet)ob).getParent());
155 return;
156 } else if (me.isPopupTrigger() /*|| me.isControlDown()*/ || MouseEvent.BUTTON2 == me.getButton() || 0 != (me.getModifiers() & Event.META_MASK)) {
157 JPopupMenu popup = getPopupMenu(selected_node);
158 if (null == popup) return;
159 popup.show(this, x, y);
160 return;
164 public void mouseDragged(MouseEvent me) {
166 public void mouseReleased(MouseEvent me) {
168 public void mouseEntered(MouseEvent me) {
170 public void mouseExited(MouseEvent me) {
172 public void mouseClicked(MouseEvent me) {
175 public void actionPerformed(ActionEvent ae) {
176 try {
177 String command = ae.getActionCommand();
179 // commands for multiple selections:
180 TreePath[] paths = this.getSelectionPaths();
181 if (null != paths && paths.length > 1) {
182 if (command.equals("Reverse layer Z coords")) {
183 // check that all layers belong to the same layer set
184 // just do it
185 Layer[] layer = new Layer[paths.length];
186 LayerSet ls = null;
187 for (int i=0; i<paths.length; i++) {
188 layer[i] = (Layer) ((LayerThing)((DefaultMutableTreeNode)paths[i].getLastPathComponent()).getUserObject()).getObject();
189 if (null == ls) ls = layer[i].getParent();
190 else if (!ls.equals(layer[i].getParent())) {
191 Utils.showMessage("To reverse, all layers must belong to the same layer set");
192 return;
195 final ArrayList<Layer> al = new ArrayList<Layer>();
196 for (int i=0; i<layer.length; i++) al.add(layer[i]);
197 ls.addLayerEditedStep(al);
198 // ASSSUMING layers are already Z ordered! CHECK
199 for (int i=0, j=layer.length-1; i<layer.length/2; i++, j--) {
200 double z = layer[i].getZ();
201 layer[i].setZ(layer[j].getZ());
202 layer[j].setZ(z);
204 updateList(ls);
205 ls.addLayerEditedStep(al);
206 Display.updateLayerScroller(ls);
207 } else if (command.equals("Translate layers in Z...")) {
208 GenericDialog gd = ControlWindow.makeGenericDialog("Range");
209 gd.addMessage("Translate selected range in the Z axis:");
210 gd.addNumericField("by: ", 0, 4);
211 gd.showDialog();
212 if (gd.wasCanceled()) return;
213 // else, displace
214 double dz = gd.getNextNumber();
215 if (Double.isNaN(dz)) {
216 Utils.showMessage("Invalid number");
217 return;
219 HashSet hs_parents = new HashSet();
220 for (int i=0; i<paths.length; i++) {
221 Layer layer = (Layer) ((LayerThing)((DefaultMutableTreeNode)paths[i].getLastPathComponent()).getUserObject()).getObject();
222 layer.setZ(layer.getZ() + dz);
223 hs_parents.add(layer.getParent());
225 for (Iterator it = hs_parents.iterator(); it.hasNext(); ) {
226 updateList((LayerSet)it.next());
228 // now update all profile's Z ordering in the ProjectTree
229 final Project project = Project.getInstance(this);
230 ProjectThing root_pt = project.getRootProjectThing();
231 ArrayList al_pl = root_pt.findChildrenOfType("profile_list");
232 for (Iterator it = al_pl.iterator(); it.hasNext(); ) {
233 ProjectThing pt = (ProjectThing)it.next();
234 pt.fixZOrdering();
235 project.getProjectTree().updateList(pt);
237 project.getProjectTree().updateUILater();
238 //Display.updateLayerScroller((LayerSet)((DefaultMutableTreeNode)getModel().getRoot()).getUserObject());
239 } else if (command.equals("Delete...")) {
240 if (!Utils.check("Really remove all selected layers?")) return;
241 for (int i=0; i<paths.length; i++) {
242 DefaultMutableTreeNode lnode = (DefaultMutableTreeNode)paths[i].getLastPathComponent();
243 LayerThing lt = (LayerThing)lnode.getUserObject();
244 Layer layer = (Layer)lt.getObject();
245 if (!layer.remove(false)) {
246 Utils.showMessage("Could not delete layer " + layer);
247 this.updateUILater();
248 return;
250 if (lt.remove(false)) {
251 ((DefaultTreeModel)this.getModel()).removeNodeFromParent(lnode);
254 this.updateUILater();
255 } else if (command.equals("Scale Z and thickness...")) {
256 GenericDialog gd = new GenericDialog("Scale Z");
257 gd.addNumericField("scale: ", 1.0, 2);
258 gd.showDialog();
259 double scale = gd.getNextNumber();
260 if (Double.isNaN(scale) || 0 == scale) {
261 Utils.showMessage("Imvalid scaling factor: " + scale);
262 return;
264 for (int i=0; i<paths.length; i++) {
265 DefaultMutableTreeNode lnode = (DefaultMutableTreeNode)paths[i].getLastPathComponent();
266 LayerThing lt = (LayerThing)lnode.getUserObject();
267 Layer layer = (Layer)lt.getObject();
268 layer.setZ(layer.getZ() * scale);
269 layer.setThickness(layer.getThickness() * scale);
271 this.updateUILater();
272 } else {
273 Utils.showMessage("Don't know what to do with command " + command + " for multiple selected nodes");
275 return;
278 // commands for single selection:
279 if (null == selected_node) return;
280 LayerThing thing = (LayerThing)selected_node.getUserObject();
281 LayerThing new_thing = null;
282 TemplateThing tt = null;
283 Object ob = null;
284 int i_position = -1;
286 if (command.startsWith("new ")) {
287 String name = command.substring(4).toLowerCase();
288 if (name.equals("layer")) {
289 // Create new Layer and add it to the selected node
290 LayerSet set = (LayerSet)thing.getObject();
291 Layer new_layer = Layer.create(thing.getProject(), set);
292 if (null == new_layer) return;
293 tt = thing.getChildTemplate("layer");
294 ob = new_layer;
295 Display.updateTitle(set);
296 } else if (name.equals("layer set")) { // with space in the middle
297 // Create a new LayerSet and add it in the middle
298 Layer layer = (Layer)thing.getObject();
299 LayerSet new_set = layer.getParent().create(layer);
300 if (null == new_set) return;
301 layer.add(new_set);
302 // add it at the end of the list
303 tt = thing.getChildTemplate("layer_set");
304 ob = new_set;
305 i_position = selected_node.getChildCount();
306 Display.update(layer);
307 } else {
308 Utils.log("LayerTree.actionPerformed: don't know what to do with the command: " + command);
309 return;
311 } else if (command.equals("many new layers...")) {
312 LayerSet set = (LayerSet)thing.getObject();
313 Layer[] layer = Layer.createMany(set.getProject(), set);
314 // add them to the tree as LayerThing
315 if (null == layer) return;
316 for (int i=0; i<layer.length; i++) {
317 addLayer(set, layer[i]); // null layers will be skipped
319 Display.updateTitle(set);
320 return;
321 } else if (command.equals("Show")) {
322 // create a new Display
323 DBObject dbo = (DBObject)thing.getObject();
324 if (thing.getType().equals("layer_set") && null == ((LayerSet)dbo).getParent()) return; // the top level LayerSet
325 new Display(dbo.getProject(), thing.getType().equals("layer") ? (Layer)dbo : ((LayerSet)dbo).getParent());
326 return;
327 } else if (command.equals("Show centered in Display")) {
328 LayerSet ls = (LayerSet)thing.getObject();
329 Display.showCentered(ls.getParent(), ls, false, false);
330 } else if (command.equals("Delete...")) {
331 remove(true, thing, selected_node);
332 return;
333 } else if (command.equals("Import stack...")) {
335 DBObject dbo = (DBObject)thing.getObject();
337 if (thing.getObject() instanceof LayerSet) {
338 LayerSet set = (LayerSet)thing.getObject();
339 Layer layer = null;
340 if (0 == set.getLayers().size()) {
341 layer = Layer.create(set.getProject(), set);
342 if (null == layer) return;
343 tt = thing.getChildTemplate("Layer");
344 ob = layer;
345 } else return; // click on a desired, existing layer.
346 if (null == layer) return;
347 layer.getProject().getLoader().importStack(layer, null, true);
348 } else if (thing.getObject() instanceof Layer) {
349 Layer layer = (Layer)thing.getObject();
350 layer.getProject().getLoader().importStack(layer, null, true);
351 return;
353 } else if (command.equals("Import grid...")) {
354 if (thing.getObject() instanceof Layer) {
355 Layer layer = (Layer)thing.getObject();
356 layer.getProject().getLoader().importGrid(layer);
358 } else if (command.equals("Import sequence as grid...")) {
359 if (thing.getObject() instanceof Layer) {
360 Layer layer = (Layer)thing.getObject();
361 layer.getProject().getLoader().importSequenceAsGrid(layer);
363 } else if (command.equals("Import from text file...")) {
364 if (thing.getObject() instanceof Layer) {
365 Layer layer = (Layer)thing.getObject();
366 layer.getProject().getLoader().importImages(layer);
368 } else if (command.equals("Resize LayerSet...")) {
369 if (thing.getObject() instanceof LayerSet) {
370 LayerSet ls = (LayerSet)thing.getObject();
371 ij.gui.GenericDialog gd = ControlWindow.makeGenericDialog("Resize LayerSet");
372 gd.addNumericField("new width: ", ls.getLayerWidth(), 3);
373 gd.addNumericField("new height: ",ls.getLayerHeight(),3);
374 gd.addChoice("Anchor: ", LayerSet.ANCHORS, LayerSet.ANCHORS[0]);
375 gd.showDialog();
376 if (gd.wasCanceled()) return;
377 double new_width = gd.getNextNumber();
378 double new_height =gd.getNextNumber();
379 ls.setDimensions(new_width, new_height, gd.getNextChoiceIndex()); // will complain and prevent cropping existing Displayable objects
381 } else if (command.equals("Autoresize LayerSet")) {
382 if (thing.getObject() instanceof LayerSet) {
383 LayerSet ls = (LayerSet)thing.getObject();
384 ls.setMinimumDimensions();
386 } else if (command.equals("Adjust...")) {
387 if (thing.getObject() instanceof Layer) {
388 Layer layer= (Layer)thing.getObject();
389 ij.gui.GenericDialog gd = ControlWindow.makeGenericDialog("Adjust Layer");
390 gd.addNumericField("new z: ", layer.getZ(), 4);
391 gd.addNumericField("new thickness: ",layer.getThickness(),4);
392 gd.showDialog();
393 if (gd.wasCanceled()) return;
394 double new_z = gd.getNextNumber();
395 layer.setThickness(gd.getNextNumber());
396 if (new_z != layer.getZ()) {
397 layer.setZ(new_z);
398 // move in the tree
400 DefaultMutableTreeNode child = findNode(thing, this);
401 DefaultMutableTreeNode parent = (DefaultMutableTreeNode)child.getParent();
402 parent.remove(child);
403 // reinsert
404 int n = parent.getChildCount();
405 int i = 0;
406 for (; i < n; i++) {
407 DefaultMutableTreeNode child_node = (DefaultMutableTreeNode)parent.getChildAt(i);
408 LayerThing child_thing = (LayerThing)child_node.getUserObject();
409 if (!child_thing.getType().equals("Layer")) continue;
410 double iz = ((Layer)child_thing.getObject()).getZ();
411 if (iz < new_z) continue;
412 // else, add the layer here, after this one
413 break;
415 ((DefaultTreeModel)this.getModel()).insertNodeInto(child, parent, i);
418 // fix tree crappiness (empty slot ?!?)
419 /* // the fix doesn't work. ARGH TODO
420 Enumeration e = parent.children();
421 parent.removeAllChildren();
422 i = 0;
423 while (e.hasMoreElements()) {
424 //parent.add((DefaultMutableTreeNode)e.nextElement());
425 ((DefaultTreeModel)this.getModel()).insertNodeInto(child, parent, i);
426 i++;
429 // easier and correct: overkill
430 updateList(layer.getParent());
432 // set selected
433 DefaultMutableTreeNode child = findNode(thing, this);
434 TreePath treePath = new TreePath(child.getPath());
435 this.scrollPathToVisible(treePath);
436 this.setSelectionPath(treePath);
439 return;
440 } else if (command.equals("Rename...")) {
441 GenericDialog gd = ControlWindow.makeGenericDialog("Rename");
442 gd.addStringField("new name: ", thing.getTitle());
443 gd.showDialog();
444 if (gd.wasCanceled()) return;
445 thing.setTitle(gd.getNextString());
446 } else if (command.equals("Translate layers in Z...")) {
447 /// TODO: this method should use multiple selections directly on the tree
448 if (thing.getObject() instanceof LayerSet) {
449 LayerSet ls = (LayerSet)thing.getObject();
450 ArrayList al_layers = ls.getLayers();
451 String[] layer_names = new String[al_layers.size()];
452 for (int i=0; i<layer_names.length; i++) {
453 layer_names[i] = ls.getProject().findLayerThing(al_layers.get(i)).toString();
455 GenericDialog gd = ControlWindow.makeGenericDialog("Range");
456 gd.addMessage("Translate selected range in the Z axis:");
457 gd.addChoice("from: ", layer_names, layer_names[0]);
458 gd.addChoice("to: ", layer_names, layer_names[layer_names.length-1]);
459 gd.addNumericField("by: ", 0, 4);
460 gd.showDialog();
461 if (gd.wasCanceled()) return;
462 // else, displace
463 double dz = gd.getNextNumber();
464 if (Double.isNaN(dz)) {
465 Utils.showMessage("Invalid number");
466 return;
468 int i_start = gd.getNextChoiceIndex();
469 int i_end = gd.getNextChoiceIndex();
470 for (int i = i_start; i<=i_end; i++) {
471 Layer layer = (Layer)al_layers.get(i);
472 layer.setZ(layer.getZ() + dz);
474 // update node labels and position
475 updateList(ls);
477 } else if (command.equals("Reverse layer Z coords...")) {
478 /// TODO: this method should use multiple selections directly on the tree
479 if (thing.getObject() instanceof LayerSet) {
480 LayerSet ls = (LayerSet)thing.getObject();
481 ArrayList al_layers = ls.getLayers();
482 String[] layer_names = new String[al_layers.size()];
483 for (int i=0; i<layer_names.length; i++) {
484 layer_names[i] = ls.getProject().findLayerThing(al_layers.get(i)).toString();
486 GenericDialog gd = ControlWindow.makeGenericDialog("Range");
487 gd.addMessage("Reverse Z coordinates of selected range:");
488 gd.addChoice("from: ", layer_names, layer_names[0]);
489 gd.addChoice("to: ", layer_names, layer_names[layer_names.length-1]);
490 gd.showDialog();
491 if (gd.wasCanceled()) return;
492 int i_start = gd.getNextChoiceIndex();
493 int i_end = gd.getNextChoiceIndex();
494 for (int i = i_start, j=i_end; i<i_end/2; i++, j--) {
495 Layer layer1 = (Layer)al_layers.get(i);
496 double z1 = layer1.getZ();
497 Layer layer2 = (Layer)al_layers.get(j);
498 layer1.setZ(layer2.getZ());
499 layer2.setZ(z1);
501 // update node labels and position
502 updateList(ls);
504 } else if (command.equals("Search...")) {
505 new Search();
506 } else {
507 Utils.log("LayerTree.actionPerformed: don't know what to do with the command: " + command);
508 return;
511 if (null == tt) return;
513 new_thing = new LayerThing(tt, thing.getProject(), ob);
515 if (-1 == i_position && new_thing.getType().equals("layer")) {
516 // find the node whose 'z' is larger than z, and add the Layer before that.
517 // (just because there could be objects other than LayerThing with a Layer in it in the future, so set.getLayers().indexOf(layer) may not be useful)
518 double z = ((Layer)ob).getZ();
519 int n = selected_node.getChildCount();
520 int i = 0;
521 for (; i < n; i++) {
522 DefaultMutableTreeNode child_node = (DefaultMutableTreeNode)selected_node.getChildAt(i);
523 LayerThing child_thing = (LayerThing)child_node.getUserObject();
524 if (!child_thing.getType().equals("layer")) {
525 continue;
527 double iz = ((Layer)child_thing.getObject()).getZ();
528 if (iz < z) {
529 continue;
531 // else, add the layer here, after this one
532 break;
534 i_position = i;
536 thing.addChild(new_thing);
537 DefaultMutableTreeNode new_node = new DefaultMutableTreeNode(new_thing);
538 ((DefaultTreeModel)this.getModel()).insertNodeInto(new_node, selected_node, i_position);
539 TreePath treePath = new TreePath(new_node.getPath());
540 this.scrollPathToVisible(treePath);
541 this.setSelectionPath(treePath);
542 } catch (Exception e) {
543 IJError.print(e);
547 public boolean remove(Layer layer, boolean check) {
548 DefaultMutableTreeNode root = (DefaultMutableTreeNode)this.getModel().getRoot();
549 LayerThing thing = (LayerThing)(((LayerThing)root.getUserObject()).findChild(layer));
550 if (null == thing) { Utils.log2("LayerTree.remove(Layer): thing not found"); return false; }
551 DefaultMutableTreeNode node = DNDTree.findNode(thing, this);
552 if (null == node) { Utils.log2("LayerTree.remove(Layer): node not found"); return false; }
553 if (thing.remove(check)) {
554 ((DefaultTreeModel)this.getModel()).removeNodeFromParent(node);
555 this.updateUILater();
557 return true;
560 /** Used by the Loader.importStack and the "many new layers" command. */
561 public void addLayer(LayerSet layer_set, Layer layer) {
562 if (null == layer_set || null == layer) return;
563 try {
564 // find the node that contains the LayerSet
565 DefaultMutableTreeNode root_node = (DefaultMutableTreeNode)this.getModel().getRoot();
566 LayerThing root_lt = (LayerThing)root_node.getUserObject();
567 Thing thing = null;
568 if (root_lt.getObject().equals(layer_set)) thing = root_lt;
569 else thing = root_lt.findChild(layer_set);
570 DefaultMutableTreeNode parent_node = DNDTree.findNode(thing, this);
571 if (null == parent_node) { Utils.log("LayerTree: LayerSet not found."); return; }
572 LayerThing parent_thing = (LayerThing)parent_node.getUserObject();
573 double z = layer.getZ();
574 // find the node whose 'z' is larger than z, and add the Layer before that.
575 int n = parent_node.getChildCount();
576 int i = 0;
577 for (; i < n; i++) {
578 DefaultMutableTreeNode child_node = (DefaultMutableTreeNode)parent_node.getChildAt(i);
579 LayerThing child_thing = (LayerThing)child_node.getUserObject();
580 if (!child_thing.getType().equals("layer")) {
581 continue;
583 double iz = ((Layer)child_thing.getObject()).getZ();
584 if (iz < z) {
585 continue;
587 // else, add the layer here, after the 'i' layer which has a larger z
588 break;
590 TemplateThing tt = parent_thing.getChildTemplate("layer");
591 if (null == tt) {
592 Utils.log("LayerTree: Null template Thing!");
593 return;
595 LayerThing new_thing = new LayerThing(tt, layer.getProject(), layer);
596 // Add the new_thing to the tree
597 if (null != new_thing) {
598 parent_thing.addChild(new_thing);
599 DefaultMutableTreeNode new_node = new DefaultMutableTreeNode(new_thing);
600 //TODO when changing the Z of a layer, the insertion is proper but an empty space is left //Utils.log("LayerTree: inserting at: " + i);
601 ((DefaultTreeModel)this.getModel()).insertNodeInto(new_node, parent_node, i);
602 TreePath treePath = new TreePath(new_node.getPath());
603 this.scrollPathToVisible(treePath);
604 this.setSelectionPath(treePath);
606 } catch (Exception e) { IJError.print(e); }
609 public void destroy() {
610 super.destroy();
611 this.selected_node = null;
614 /** Remove all layer nodes from the given layer_set, and add them again according to the layer's Z value. */
615 public void updateList(LayerSet layer_set) {
617 // store scrolling position for restoring purposes
619 Component c = this.getParent();
620 Point point = null;
621 if (c instanceof JScrollPane) {
622 point = ((JScrollPane)c).getViewport().getViewPosition();
626 LayerThing lt = layer_set.getProject().findLayerThing(layer_set);
627 if (null == lt) {
628 Utils.log2("LayerTree.updateList: could not find LayerSet " + layer_set);
629 return;
631 // call super
632 updateList(lt);
634 DefaultMutableTreeNode ls_node = DNDTree.findNode(lt, this);
635 if (null == ls_node) {
636 Utils.log2("LayerTree.updateList: could not find a node for LayerThing " + lt);
637 return;
639 Hashtable ht = new Hashtable();
640 for (Enumeration e = ls_node.children(); e.hasMoreElements(); ) {
641 DefaultMutableTreeNode node = (DefaultMutableTreeNode)e.nextElement();
642 ht.put(node.getUserObject(), node);
644 ls_node.removeAllChildren();
645 for (Iterator it = lt.getChildren().iterator(); it.hasNext(); ) {
646 Object ob = ht.remove(it.next());
647 ls_node.add((DefaultMutableTreeNode)ob);
649 if (0 != ht.size()) {
650 Utils.log2("WARNING LayerTree.updateList: did not end up adding this nodes:");
651 for (Iterator it = ht.keySet().iterator(); it.hasNext(); ) {
652 Utils.log2(it.next().toString());
655 this.updateUILater();
657 // restore viewport position
658 if (null != point) {
659 ((JScrollPane)c).getViewport().setViewPosition(point);
662 // what the hell:
663 this.getSelectionModel().setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
666 /** If the given node is null, it will be searched for. */
667 public boolean remove(boolean check, LayerThing thing, DefaultMutableTreeNode node) {
668 if (null == thing || null == thing.getParent()) return false; // can't remove the root LayerSet
669 return thing.remove(check) && removeNode(null != node ? node : findNode(thing, this));