From cbe4e90105d07d7d3fecabffaa01342403aa2ae3 Mon Sep 17 00:00:00 2001
From: Normand Briere <nbriere@noware.ca>
Date: Thu, 19 Sep 2019 20:54:00 -0400
Subject: [PATCH] Better navigation.

---
 GroupEditor.java | 1452 +++++++++++++++++++++++++++++++++++++++++++++-------------
 1 files changed, 1,125 insertions(+), 327 deletions(-)

diff --git a/GroupEditor.java b/GroupEditor.java
index 502ff05..d328971 100644
--- a/GroupEditor.java
+++ b/GroupEditor.java
@@ -16,6 +16,7 @@
 //import buoy.widget.BFileChooser;
 
 class GroupEditor extends ObjEditor implements //iParse, //iCallBack,
+        Grafreed.iResourceCallBack,
         ObjectUI,
         Runnable,
         ActionListener,
@@ -23,6 +24,350 @@
         DragGestureListener, DragSourceListener, DropTargetListener,
         ItemListener // ListSelectionListener
 {
+
+    public void AddSkyboxButton(String f, String s, cGridBag row)
+    {
+        cButton skyboxButton;
+        final String path = "cubemaps/" + f + "-skyboxes/" + s;
+        row.add(skyboxButton = GetButton(path + "/preview.jpg", !Globals.NIMBUSLAF));
+        //row.add(skyboxButton = GetButton(path + "/negx.jpg", !Grafreed.NIMBUSLAF));
+        skyboxButton.setToolTipText(s.equals("") ? "No background" : s);
+        skyboxButton.addActionListener(new ActionListener()
+        {
+                @Override
+                public void actionPerformed(ActionEvent e)
+                {
+                    ChangeSkybox(path);
+                }
+        });
+    }
+
+    public void AddTextureButton(String f, String c, final String t, int count, cGridBag row)
+    {
+        cButton textureButton;
+        final String path = "textures/" + f + "/" + c + "/"; // + t;
+        row.add(textureButton = GetButton(path + "icons/" + t, !Globals.NIMBUSLAF));
+        textureButton.setToolTipText(c + count);
+        textureButton.addActionListener(new ActionListener()
+        {
+                @Override
+                public void actionPerformed(ActionEvent e)
+                {
+                    ChangeTexture(path + t);
+                }
+        });
+    }
+    
+    public void AddSkyboxTab0(JTabbedPane skyboxpanel)
+    {
+        cGridBag tab0 = new cGridBag().setVertical(true);
+        
+        tab0.setName("Urban");
+        skyboxpanel.add(tab0);
+
+        cGridBag row0 = new cGridBag();
+        cGridBag row1 = new cGridBag();
+        cGridBag row2 = new cGridBag();
+        cGridBag row3 = new cGridBag();
+        cGridBag row4 = new cGridBag();
+        cGridBag row5 = new cGridBag();
+        cGridBag row6 = new cGridBag();
+        
+        AddSkyboxButton("default", "", row0);
+        //AddSkyboxButton("default", "cornell", row0);
+        AddSkyboxButton("penguins", "dust", row0);
+        AddSkyboxButton("penguins", "tropic", row0);
+        AddSkyboxButton("penguins", "yonder", row0);
+
+        AddSkyboxButton("default", "uffizi", row1);
+        AddSkyboxButton("bridge", "Bridge", row1);
+        AddSkyboxButton("bridge", "Bridge2", row1);
+        AddSkyboxButton("urban", "GamlaStan2", row1);
+        
+        AddSkyboxButton("urban", "Parliament", row2);
+        AddSkyboxButton("urban", "Roundabout", row2);
+        AddSkyboxButton("urban", "SaintLazarusChurch", row2);
+        AddSkyboxButton("urban", "SaintLazarusChurch2", row2);
+        
+        AddSkyboxButton("urban", "SaintLazarusChurch3", row3);
+        AddSkyboxButton("urban", "UnionSquare", row3);
+        AddSkyboxButton("urban", "Medborgarplatsen", row3);
+        AddSkyboxButton("park", "BerzeliiPark", row3);
+        
+        AddSkyboxButton("park", "Buddha", row4);
+        AddSkyboxButton("park", "CNTower2", row4);
+        AddSkyboxButton("park", "NiagaraFalls1", row4);
+        AddSkyboxButton("park", "NiagaraFalls3", row4);
+        
+        AddSkyboxButton("park", "Park", row5);
+        AddSkyboxButton("park", "Pond", row5);
+        AddSkyboxButton("park", "Skansen", row5);
+        AddSkyboxButton("park", "Skansen2", row5);
+        
+        AddSkyboxButton("park", "Skansen3", row6);
+        AddSkyboxButton("park", "Skansen4", row6);
+        AddSkyboxButton("park", "Skansen5", row6);
+        AddSkyboxButton("persson", "VancouverConventionCentre", row6);
+        
+        tab0.add(row0);
+        tab0.add(row1);
+        tab0.add(row2);
+        tab0.add(row3);
+        tab0.add(row4);
+        tab0.add(row5);
+        tab0.add(row6);
+        
+        for (int i=5; --i>=0;)
+        {
+            //oe.toolboxPanel.Return();
+            //tab0.add(new cGridBag());
+        }
+    }
+
+    public void AddSkyboxTab1(JTabbedPane skyboxpanel)
+    {
+        cGridBag tab0 = new cGridBag().setVertical(true);
+        
+        tab0.setName("Nature");
+        skyboxpanel.add(tab0);
+
+        cGridBag row0 = new cGridBag();
+        cGridBag row1 = new cGridBag();
+        cGridBag row2 = new cGridBag();
+        cGridBag row3 = new cGridBag();
+        cGridBag row4 = new cGridBag();
+        cGridBag row5 = new cGridBag();
+        cGridBag row6 = new cGridBag();
+        
+        AddSkyboxButton("beach", "HeartInTheSand", row0);
+        AddSkyboxButton("beach", "LarnacaBeach", row0);
+        AddSkyboxButton("beach", "PalmTrees", row0);
+        AddSkyboxButton("beach", "Tenerife", row0);
+        
+        AddSkyboxButton("beach", "Tenerife2", row1);
+        AddSkyboxButton("beach", "Tenerife3", row1);
+        AddSkyboxButton("field", "FishPond", row1);
+        AddSkyboxButton("field", "Footballfield", row1);
+
+        AddSkyboxButton("field", "Meadow", row2);
+        AddSkyboxButton("field", "Sorsele", row2);
+        AddSkyboxButton("field", "Sorsele2", row2);
+        AddSkyboxButton("field", "Sorsele3", row2);
+        
+        AddSkyboxButton("forest", "Brudslojan", row3);
+        AddSkyboxButton("forest", "Langholmen2", row3);
+        AddSkyboxButton("forest", "Plants", row3);
+        AddSkyboxButton("mountain", "Maskonaive", row3);
+        
+        AddSkyboxButton("mountain", "Maskonaive2", row4);
+        AddSkyboxButton("mountain", "Maskonaive3", row4);
+        AddSkyboxButton("mountain", "Teide", row4);
+        AddSkyboxButton("park", "Tantolunden4", row4);
+        
+        AddSkyboxButton("park", "Stairs", row5);
+        AddSkyboxButton("default", "skycube", row6);
+        AddSkyboxButton("rocky", "Langholmen", row5);
+        AddSkyboxButton("rocky", "Skinnarviksberget", row5);
+        
+        AddSkyboxButton("rocky", "Tantolunden6", row5);
+        AddSkyboxButton("default", "CloudyHills", row6);
+        AddSkyboxButton("daz", "Autumn", row6);
+        AddSkyboxButton("daz", "MountainTrail", row6);
+        /*
+Autumn
+Greenlands
+MountainTrail
+Oasis
+TheRock
+TopOfTheWorld
+Winter
+         */
+        
+        tab0.add(row0);
+        tab0.add(row1);
+        tab0.add(row2);
+        tab0.add(row3);
+        tab0.add(row4);
+        tab0.add(row5);
+        tab0.add(row6);
+        
+        for (int i=5; --i>=0;)
+        {
+            //oe.toolboxPanel.Return();
+            //tab0.add(new cGridBag());
+        }
+    }
+    
+    public void AddSkyboxTab2(JTabbedPane skyboxpanel)
+    {
+        cGridBag tab0 = new cGridBag().setVertical(true);
+        
+        tab0.setName("Night");
+        skyboxpanel.add(tab0);
+
+        cGridBag row0 = new cGridBag();
+        cGridBag row1 = new cGridBag();
+        cGridBag row2 = new cGridBag();
+        cGridBag row3 = new cGridBag();
+        cGridBag row4 = new cGridBag();
+        cGridBag row5 = new cGridBag();
+        cGridBag row6 = new cGridBag();
+        
+        AddSkyboxButton("night", "NightPath", row0);
+        AddSkyboxButton("night", "PondNight", row0);
+        AddSkyboxButton("night", "Powerlines", row0);
+        AddSkyboxButton("night", "SwedishRoyalCastle", row0);
+        
+        AddSkyboxButton("urban", "CNTower", row1);
+        AddSkyboxButton("bridge", "ArstaBridge", row1);
+        AddSkyboxButton("rocky", "Riddarfjarden", row1);
+        AddSkyboxButton("penguins", "sleepyhollow", row1);
+        
+        AddSkyboxButton("penguins", "kenon_star", row2);
+        AddSkyboxButton("persson", "corona", row2);
+        AddSkyboxButton("persson", "spaceskybox", row2);
+        AddSkyboxButton("indoors", "Vasa", row2);
+        
+        AddSkyboxButton("winter", "Backyard", row3);
+        AddSkyboxButton("winter", "Creek", row3);
+        AddSkyboxButton("winter", "FootballField3", row3);
+        AddSkyboxButton("winter", "Forest", row3);
+        
+        AddSkyboxButton("winter", "HornstullsStrand2", row4);
+        AddSkyboxButton("winter", "House", row4);
+        AddSkyboxButton("winter", "IceLake", row4);
+        AddSkyboxButton("winter", "IceRiver", row4);
+        
+        AddSkyboxButton("winter", "Park3", row5);
+        AddSkyboxButton("winter", "PondWinter", row5);
+        AddSkyboxButton("winter", "Tantolunden5", row5);
+        AddSkyboxButton("winter", "Vindelalven", row5);
+        
+        AddSkyboxButton("daz", "TheRock", row6);
+        AddSkyboxButton("daz", "TopOfTheWorld", row6);
+        AddSkyboxButton("daz", "Winter", row6);
+        AddSkyboxButton("mountain", "Ryfjallet", row6);
+        
+        tab0.add(row0);
+        tab0.add(row1);
+        tab0.add(row2);
+        tab0.add(row3);
+        tab0.add(row4);
+        tab0.add(row5);
+        tab0.add(row6);
+        
+        for (int i=5; --i>=0;)
+        {
+            //oe.toolboxPanel.Return();
+            //tab0.add(new cGridBag());
+        }
+    }
+
+    public void AddSkyboxTab3(JTabbedPane skyboxpanel)
+    {
+        cGridBag tab0 = new cGridBag().setVertical(true);
+        
+        tab0.setName("Others");
+        skyboxpanel.add(tab0);
+
+        cGridBag row0 = new cGridBag();
+        cGridBag row1 = new cGridBag();
+        cGridBag row2 = new cGridBag();
+        cGridBag row3 = new cGridBag();
+        cGridBag row4 = new cGridBag();
+        cGridBag row5 = new cGridBag();
+        cGridBag row6 = new cGridBag();
+        
+        AddSkyboxButton("mayhem", "afterrain", row0);
+        AddSkyboxButton("mayhem", "aqua4", row0);
+        AddSkyboxButton("mayhem", "aqua9", row0);
+        AddSkyboxButton("mayhem", "flame", row0);
+        
+        AddSkyboxButton("mayhem", "h2s", row1);
+        AddSkyboxButton("mayhem", "prehistoric", row1);
+        AddSkyboxButton("mayhem", "scorched", row1);
+        AddSkyboxButton("penguins", "desertdawn", row1);
+        
+        AddSkyboxButton("persson", "Citadella", row2);
+        AddSkyboxButton("persson", "Citadella2", row2);
+        AddSkyboxButton("persson", "clouds1", row2);
+        AddSkyboxButton("penguins", "wrath", row2);
+        
+        AddSkyboxButton("persson", "FishermansBastion", row3);
+        AddSkyboxButton("persson", "HeroesSquare", row3);
+        AddSkyboxButton("indoors", "DallasW", row3);
+        AddSkyboxButton("indoors", "MarriottMadisonWest", row3);
+        
+        AddSkyboxButton("persson", "LancellottiChapel", row4);
+        AddSkyboxButton("persson", "PereaBeach1", row4);
+        AddSkyboxButton("persson", "PereaBeach2", row4);
+        AddSkyboxButton("persson", "redeclipse", row4);
+        
+        AddSkyboxButton("daz", "Greenlands", row5);
+        AddSkyboxButton("daz", "Oasis", row5);
+        AddSkyboxButton("elyvisions", "arch3", row5);
+        AddSkyboxButton("elyvisions", "calm_sea", row5);
+        
+        AddSkyboxButton("elyvisions", "rainbow", row6);
+        AddSkyboxButton("elyvisions", "distant_sunset", row6);
+        AddSkyboxButton("elyvisions", "heaven", row6);
+        AddSkyboxButton("elyvisions", "hot", row6);
+        
+        tab0.add(row0);
+        tab0.add(row1);
+        tab0.add(row2);
+        tab0.add(row3);
+        tab0.add(row4);
+        tab0.add(row5);
+        tab0.add(row6);
+        
+        for (int i=5; --i>=0;)
+        {
+            //oe.toolboxPanel.Return();
+            //tab0.add(new cGridBag());
+        }
+    }
+        
+    public void ChangeSkybox(String skybox)
+    {
+        if (skybox.endsWith("/"))
+        {
+            group.skyboxname = null;
+            group.skyboxext = null;
+            cameraView.repaint();
+        }
+        else
+        {
+            //cameraView.envyoff = false;
+            group.skyboxname = skybox;
+            group.skyboxext = "jpg";
+            cameraView.repaint();
+        }
+    }
+
+    public void CreateSkyboxPanel(cGridBag skyboxPanel)
+    {
+        JTabbedPane skyboxpane = new JTabbedPane(JTabbedPane.LEFT);
+        
+        AddSkyboxTab0(skyboxpane);
+        AddSkyboxTab1(skyboxpane);
+        AddSkyboxTab2(skyboxpane);
+        AddSkyboxTab3(skyboxpane);
+        
+        skyboxPanel.add(skyboxpane);
+    }
+    
+    public void ChangeTexture(String texture)
+    {
+        for (int i=0; i<group.selection.size(); i++)
+        {
+            Object3D obj = group.selection.get(i);
+            obj.SetPigmentTexture("@" + texture);
+        }
+       
+        refreshContents();
+    }
+
     //ObjEditor objEditor;
 	public void closeUI2()
 	{
@@ -60,6 +405,16 @@
 		this.copy = this.group = group;
 		//selectees = this.group.selectees;
 		
+                assert(false);
+                
+//        if (copy.versionlist == null)
+//        {
+//            copy.versionlist = new Object3D[100];
+//            copy.versionindex = -1;
+//            
+//            //Save(true);
+//        }
+        
 		if(ui)
                     SetupUI(objEditor);
 	}
@@ -80,10 +435,22 @@
 		SetupViews(objEditor);
                 
                 ((cRadio)radioPanel.getComponent(0)).SetCamera(cameraView.renderCamera, true);
+                
+//        if (copy.versionlist == null)
+//        {
+//            copy.versionlist = new Object3D[100];
+//            copy.versionindex = -1;
+//            
+//            //Save(true);
+//        }
 	}
 
     void CloneSelection(boolean supports)
     {
+        if (Globals.REPLACEONMAKE)
+            Save();
+        boolean keep = Globals.REPLACEONMAKE;
+        Globals.REPLACEONMAKE = false;
         //   Object3D keep = GrafreeD.clipboard;
            //Object3D obj;
            for (int i=0; i<group.selection.size(); i++)// Enumeration e = group.selection.elements(); e.hasMoreElements();)
@@ -94,6 +461,7 @@
                
                makeSomething(clone, i==group.selection.size()-1);
            }
+        Globals.REPLACEONMAKE = keep;
     }
 
     void CloneClipboard(boolean supports)
@@ -115,11 +483,16 @@
         
         Object3D parent = obj.parent;
         obj.parent = null;
+        
         //               Object3D support = obj.support;
         //               obj.support = null;
         if (!supports)
             obj.SaveSupports();
+        
         Object3D clone = (Object3D)Grafreed.clone(obj);
+        
+        clone.ResetUUIDs();
+        
         obj.parent = parent;
         //               obj.support = support;
         //               clone.support = support; // aout 2013
@@ -164,33 +537,37 @@
 //		menu.add("-");
 		duplicateItem = oe.jTree.popup.add(new JMenuItem("Duplicate"));
 		duplicateItem.addActionListener(this);
-		cloneItem = menu.add(new MenuItem("Clone"));
+                
+		cloneItem = oe.jTree.popup.add(new JMenuItem("Clone"));
 		cloneItem.addActionListener(this);
-        if (Globals.ADVANCED)
+        //if (Globals.ADVANCED)
         {
 		cloneSupportItem = menu.add(new MenuItem("Clone (+supports)"));
 		cloneSupportItem.addActionListener(this);
         }
+                oe.jTree.popup.addSeparator();
 		menu.add("-");
-		cutItem = menu.add(new MenuItem("Cut"));
+		cutItem = oe.jTree.popup.add(new JMenuItem("Cut"));
 		cutItem.addActionListener(this);
-		copyItem = menu.add(new MenuItem("Copy"));
+		copyItem = oe.jTree.popup.add(new JMenuItem("Copy"));
 		copyItem.addActionListener(this);
 		pasteItem = menu.add(new MenuItem("Paste"));
 		pasteItem.addActionListener(this);
                 
-		menu.add("-");
-		pasteIntoItem = menu.add(new MenuItem("Paste into"));
+                oe.jTree.popup.addSeparator();
+		//menu.add("-");
+		pasteIntoItem = oe.jTree.popup.add(new JMenuItem("Paste into"));
 		pasteIntoItem.addActionListener(this);
-		pasteLinkItem = menu.add(new MenuItem("Paste link"));
+		pasteLinkItem = oe.jTree.popup.add(new JMenuItem("Paste link"));
 		pasteLinkItem.addActionListener(this);
-		pasteCloneItem = menu.add(new MenuItem("Paste clone"));
+		pasteCloneItem = oe.jTree.popup.add(new JMenuItem("Paste clone"));
 		pasteCloneItem.addActionListener(this);
-//		pasteExpandItem = menu.add(new MenuItem("Paste expand"));
-//		pasteExpandItem.addActionListener(this);
-		menu.add("-");
-		clearItem = menu.add(new MenuItem("Clear"));
-		clearItem.addActionListener(this);
+// CRASH
+                pasteExpandItem = oe.jTree.popup.add(new JMenuItem("Paste expand"));
+//		
+                pasteExpandItem.addActionListener(this);
+		//menu.add("-");
+                oe.jTree.popup.addSeparator();
                 
         if (Globals.ADVANCED)
         {
@@ -220,7 +597,7 @@
 //        toggleSwitchItem.addItemListener(this);
 //        toggleSwitchItem.setState(CameraPane.SWITCH);
         
-        cameraMenu.add(toggleHandleItem = new CheckboxMenuItem("Handles"));
+        cameraMenu.add(toggleHandleItem = new CheckboxMenuItem("Show Handles"));
         toggleHandleItem.addItemListener(this);
         toggleHandleItem.setState(CameraPane.HANDLES);
         
@@ -276,11 +653,9 @@
 		oe.cameraMenu.add(lookAtItem = new MenuItem("Set Interest"));
 		//cameraMenu.add(lookFromItem = new MenuItem("Look From Selection"));
 		//cameraMenu.add(switchViewItem = new MenuItem("Reverse View"));
-		editLeafItem = oe.cameraMenu.add(new MenuItem("Edit Leaf"));
 		oe.cameraMenu.add("-");
 		openWindowItem =  oe.cameraMenu.add(new MenuItem("Edit copy..."));
 		openWindowItem.addActionListener(this);
-		editLeafItem.addActionListener(this);
 		lookAtItem.addActionListener(this);
 		//lookFromItem.addActinoListener(this);
 		//switchViewItem.addActionListener(this);
@@ -327,9 +702,8 @@
 		setMasterItem.addActionListener(this);
         }
                 
-		oe.menuBar.add(menu = new Menu("Group"));
-//		grabItem = menu.add(new MenuItem("Grab"));
-//		grabItem.addActionListener(this);
+		oe.menuBar.add(menu = new Menu("Order"));
+                
 		backItem = menu.add(new MenuItem("Back"));
 		backItem.addActionListener(this);
 		frontItem = menu.add(new MenuItem("Front"));
@@ -337,13 +711,21 @@
 //		compositeItem = menu.add(new MenuItem("Composite"));
 //		compositeItem.addActionListener(this);
                 
+		grabItem = oe.jTree.popup.add(new JMenuItem("Group"));
+		grabItem.addActionListener(this);
+                
         if (Globals.ADVANCED)
         {
 		hideItem = menu.add(new MenuItem("Hidden Group"));
 		hideItem.addActionListener(this);
         }
-		ungroupItem = menu.add(new MenuItem("Ungroup"));
+		ungroupItem = oe.jTree.popup.add(new JMenuItem("Ungroup"));
 		ungroupItem.addActionListener(this);
+                
+                oe.jTree.popup.addSeparator();
+                
+		deleteItem = oe.jTree.popup.add(new JMenuItem("Delete"));
+		deleteItem.addActionListener(this);
                 
 //		menu.add("-");
 //                
@@ -380,24 +762,32 @@
 		shadowYItem.addActionListener(this);
                 shadowZItem = menu.add(new MenuItem("Shadow Blue"));
 		shadowZItem.addActionListener(this);
+                
+		attributeItem = menu.add(new MenuItem("Attribute"));
+		attributeItem.addActionListener(this);
         if (Globals.ADVANCED)
         {
 		menu.add("-");
 		linkerItem = menu.add(new MenuItem("Linker"));
 		linkerItem.addActionListener(this);
-		attributeItem = menu.add(new MenuItem("Attribute"));
-		attributeItem.addActionListener(this);
 		templateItem = menu.add(new MenuItem("Template"));
 		templateItem.addActionListener(this);
 		pointflowItem = menu.add(new MenuItem("Point Flow"));
 		pointflowItem.addActionListener(this);
         }
 		menu.add("-");
+		textureRatioRItem = menu.add(new MenuItem("Texture Ratio Red"));
+		textureRatioRItem.addActionListener(this);
+		textureRatioGItem = menu.add(new MenuItem("Texture Ratio Green"));
+		textureRatioGItem.addActionListener(this);
+		textureRatioBItem = menu.add(new MenuItem("Texture Ratio Blue"));
+		textureRatioBItem.addActionListener(this);
+		menu.add("-");
 		resetTransformItem = menu.add(new MenuItem("Reset Transform"));
 		resetTransformItem.addActionListener(this);
 		resetCentroidItem = menu.add(new MenuItem("Reset Centroid"));
 		resetCentroidItem.addActionListener(this);
-		resetCentroidXZItem = menu.add(new MenuItem("Reset Centroid XY"));
+		resetCentroidXZItem = menu.add(new MenuItem("Reset Centroid XZ"));
 		resetCentroidXZItem.addActionListener(this);
 		transformGeometryItem = menu.add(new MenuItem("Transform Geometry"));
 		transformGeometryItem.addActionListener(this);
@@ -413,9 +803,9 @@
 		genNormalsCADItem.addActionListener(this);
 		genNormalsMESHItem = menu.add(new MenuItem("Mesh Normals"));
 		genNormalsMESHItem.addActionListener(this);
-        if (Globals.ADVANCED)
+        //if (Globals.ADVANCED)
         {
-		genNormalsMINEItem = menu.add(new MenuItem("My Normals"));
+		genNormalsMINEItem = menu.add(new MenuItem("Stitch Normals"));
 		genNormalsMINEItem.addActionListener(this);
         }
 		stripifyItem = menu.add(new MenuItem("Stripify"));
@@ -449,6 +839,8 @@
         }
 
                 oe.menuBar.add(menu = new Menu("Attributes"));
+		clearVersionsItem = menu.add(new MenuItem("Clear Versions"));
+		clearVersionsItem.addActionListener(this);
 		clearMaterialsItem = menu.add(new MenuItem("Clear Materials"));
 		clearMaterialsItem.addActionListener(this);
                 resetAllItem = menu.add(new MenuItem("Reset All"));
@@ -471,9 +863,9 @@
 		hideleavesItem.addActionListener(this);
 		showleavesItem = menu.add(new MenuItem("Show Leaves"));
 		showleavesItem.addActionListener(this);
-		markleavesItem = menu.add(new MenuItem("Mark Leaves"));
+		markleavesItem = menu.add(new MenuItem("Anim Leaves"));
 		markleavesItem.addActionListener(this);
-		unmarkleavesItem = menu.add(new MenuItem("Unmark Leaves"));
+		unmarkleavesItem = menu.add(new MenuItem("Unanim Leaves"));
 		unmarkleavesItem.addActionListener(this);
 		rewindleavesItem = menu.add(new MenuItem("Rewind Leaves"));
 		rewindleavesItem.addActionListener(this);
@@ -508,10 +900,15 @@
 		attachBumpItem.addActionListener(this);
 		pigmentBumpItem = menu.add(new MenuItem("Pigment -> Bump"));
 		pigmentBumpItem.addActionListener(this);
+                //embedTexturesItem
 		detachPigmentItem = menu.add(new MenuItem("Detach Pigment"));
 		detachPigmentItem.addActionListener(this);
 		detachBumpItem = menu.add(new MenuItem("Detach Bump"));
 		detachBumpItem.addActionListener(this);
+		embedTexturesItem = menu.add(new MenuItem("Embed Textures"));
+		embedTexturesItem.addActionListener(this);
+		deEmbedTexturesItem = menu.add(new MenuItem("De-embed Textures"));
+		deEmbedTexturesItem.addActionListener(this);
 		menu.add("-");
 		sortbysizeItem = menu.add(new MenuItem("Sort by size"));
 		sortbysizeItem.addActionListener(this);
@@ -522,6 +919,9 @@
 		shareGeometriesItem.addActionListener(this);
 		mergeGeometriesItem = menu.add(new MenuItem("Merge Geometries"));
 		mergeGeometriesItem.addActionListener(this);
+		menu.add("-");
+		editLeafItem = menu.add(new MenuItem("Edit leaf..."));
+		editLeafItem.addActionListener(this);
         if (Globals.ADVANCED)
         {
             // Pretty much the same as duplicate and clone.
@@ -538,6 +938,85 @@
 		buildToolsMenu(menu);
 	}
 	
+        JTabbedPane resourcecontainer;
+        cGridBag currenttab;
+        //boolean added; // patch for jar
+                
+        int totalcount = 0;
+        
+        int tabcount = 0;
+        int colcount = 0;
+        int rowcount = 0;
+        int texturecount = 0;
+        
+        int columns = 5;
+        int rows = 7;
+        
+    public void ResourceCallBack(String[] path)
+    {
+//        for (int i = 0; i < path.length; i++)
+//            System.out.print(path[i] + "/");
+//        System.out.println();
+        
+        if (//rowcount == 0 ||
+                path.length == 1 && !path[0].equals("") && !path[0].equals(".DS_Store"))
+        {
+            currenttab = new cGridBag();
+            String tabname = path[0]; // String.valueOf((char)('A'+tabcount));
+            currenttab.setName(tabname);
+            //added = false;
+            resourcecontainer.add(currenttab);
+            resourcecontainer.setToolTipTextAt(tabcount++, "Texture " + tabname);
+            rowcount = 1;
+            colcount = 0;
+            texturecount = 0;
+        }
+        
+        if (path.length > 2 && (path[2].toLowerCase().endsWith(".jpg") || path[2].toLowerCase().endsWith(".png")))
+        {
+            //if (!added)
+            {
+                //added = true;
+                String tabname = path[0]; // String.valueOf((char)('A'+tabcount));
+                currenttab = (cGridBag)resourcecontainer.getComponentAt(resourcecontainer.indexOfTab(tabname));
+            }
+            
+        columns = 5;
+        
+        if (path[0].contains("D&R") || path[0].contains("Paint"))
+            columns = 4;
+        
+            AddTextureButton(path[0], path[1], path[2], ++texturecount, currenttab);
+            totalcount++;
+            
+            if (++colcount >= columns)
+            {
+                colcount = 0;
+                currenttab.Return();
+                
+                if (rowcount++ >= rows)
+                {
+                    rowcount = 0;
+                }
+            }
+        }
+        else
+        {
+//            if (!path[path.length-1].equals("icons"))
+//                resourcecontainer.Return();
+        }
+    }
+    
+        void CreateTexturePanel(cGridBag container)
+        {
+            resourcecontainer = new JTabbedPane(JTabbedPane.LEFT);
+            container.add(resourcecontainer);
+            
+            Grafreed.ParseResources("textures", this);
+            
+            // 935. System.out.println("Total = " + totalcount);
+        }
+        
 	void SetupUI2(ObjEditor oe)
 	{
             // June 2019
@@ -555,11 +1034,6 @@
             //new Exception().printStackTrace();
                 
         oe.radioPanel = new JPanel(new GridBagLayout());
-	oe.aConstraints.weightx = 1;
-	oe.aConstraints.weighty = 0;
-		oe.aConstraints.fill = GridBagConstraints.HORIZONTAL;
-	oe.aConstraints.gridwidth = 100;
-	oe.aConstraints.gridheight = 1;
 //        oe.toolbarPanel.add(radioPanel); //, oe.aConstraints);
         
         oe.buttonGroup = new ButtonGroup();
@@ -580,9 +1054,9 @@
         oe.radioPanel.add(dummyButton);
         oe.buttonGroup.add(dummyButton);
          */
-                cGridBag copyOptionsPanel = new cGridBag();
+                cGridBag versionManagerPanel = new cGridBag();
                                         
-                copyOptionsPanel.preferredHeight = 2;
+                versionManagerPanel.preferredHeight = 4;
                 
                 //this.AddOptions(oe.toolbarPanel, oe.aConstraints);
                 
@@ -590,50 +1064,110 @@
                 //minButton.setToolTipText("Minimize window");
 		//minButton.addActionListener(this);
                 
-		oe.toolbarPanel.add(maxButton = GetButton("icons/add-128.png", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
-                maxButton.setToolTipText("Maximize window");
-		maxButton.addActionListener(this);
+                if (Globals.ADVANCED)
+                {
+//                    oe.toolbarPanel.add(maxButton = GetButton("icons/add-128.png", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
+//                    maxButton.setToolTipText("Maximize window");
+//                    maxButton.addActionListener(this);
+                }
                 
-		oe.toolbarPanel.add(fullButton = GetButton("icons/fullscreen.png", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
-                fullButton.setToolTipText("Full-screen window");
-		fullButton.addActionListener(this);
+                cButton gcButton;
                 
-		oe.toolbarPanel.add(restoreCameraButton = GetButton("icons/eye.png", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
+//		oe.toolbarPanel.add(gcButton = GetButton("icons/trash.png", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
+//                gcButton.setToolTipText("Garbage collect");
+//                gcButton.addActionListener(new ActionListener()
+//                {
+//                        public void actionPerformed(ActionEvent e)
+//                        {
+//                            System.gc();
+//                        }
+//                });
+                
+		oe.toolbarPanel.add(fullScreenButton = GetButton("icons/fullscreen.png", !Globals.NIMBUSLAF)); //, oe.aConstraints);
+                fullScreenButton.setToolTipText("Full-screen window");
+		fullScreenButton.addActionListener(this);
+                
+		oe.toolbarPanel.add(collapseButton = GetButton("icons/collapse.png", !Globals.NIMBUSLAF)); //, oe.aConstraints);
+                collapseButton.setToolTipText("Collapse toolbar");
+		collapseButton.addActionListener(this);
+                
+//		oe.toolbarPanel.add(maximize3DButton = GetButton("icons/square.png", !Globals.NIMBUSLAF)); //, oe.aConstraints);
+//                maximize3DButton.setToolTipText("Maximize 3D view");
+//		maximize3DButton.addActionListener(this);
+                
+		oe.toolbarPanel.add(twoButton = GetButton("icons/cube.png", !Globals.NIMBUSLAF)); //, oe.aConstraints);
+                twoButton.setToolTipText("Show 3D view only");
+		twoButton.addActionListener(this);
+                this.fullscreenLayout = twoButton;
+                
+		oe.toolbarPanel.add(threeButton = GetButton("icons/controlsview.png", !Globals.NIMBUSLAF)); //, oe.aConstraints);
+                threeButton.setToolTipText("Show controls and 3D view");
+		threeButton.addActionListener(this);
+                if (Globals.ADVANCED)
+                {
+		oe.toolbarPanel.add(sixButton = GetButton("icons/viewcontrols.png", !Globals.NIMBUSLAF)); //, oe.aConstraints);
+                sixButton.setToolTipText("Show 3D view and controls");
+		sixButton.addActionListener(this);
+                }
+//		oe.toolbarPanel.add(sevenButton = new cButton("-|-|-", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
+//                sevenButton.setToolTipText("3-column layout");
+//		sevenButton.addActionListener(this);
+                //
+                
+		oe.toolbarPanel.add(screenfitButton = GetButton("icons/fit.png", !Globals.NIMBUSLAF)); //, oe.aConstraints);
+                screenfitButton.setToolTipText("Screen fit");
+		screenfitButton.addActionListener(this);
+
+		oe.toolbarPanel.add(restoreCameraButton = GetButton("icons/eye.png", !Globals.NIMBUSLAF)); //, oe.aConstraints);
                 restoreCameraButton.setToolTipText("Restore viewpoint");
 		restoreCameraButton.addActionListener(this);
                 
-		copyOptionsPanel.add(undoButton = GetButton("icons/undo.png", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
-                undoButton.setToolTipText("Undo changes");
-		undoButton.addActionListener(this);
-                undoButton.setEnabled(false);
+		versionManagerPanel.add(saveVersionButton = GetButton("icons/down_arrow.png", !Globals.NIMBUSLAF)); //, oe.aConstraints);
+                saveVersionButton.setToolTipText("Duplicate current version");
+		saveVersionButton.addActionListener(this);
+                                                
+		versionManagerPanel.add(deleteVersionButton = GetButton("icons/trash.png", !Globals.NIMBUSLAF)); //, oe.aConstraints);
+                deleteVersionButton.setToolTipText("Delete current version");
+		deleteVersionButton.addActionListener(this);
+                deleteVersionButton.setEnabled(false);
+                                                
+		versionManagerPanel.add(previousVersionButton = GetButton("icons/undo.png", !Globals.NIMBUSLAF)); //, oe.aConstraints);
+                previousVersionButton.setToolTipText("Previous version");
+		previousVersionButton.addActionListener(this);
+                previousVersionButton.setEnabled(false);
                 
-		copyOptionsPanel.add(redoButton = GetButton("icons/redo.png", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
-                redoButton.setToolTipText("Redo changes");
-		redoButton.addActionListener(this);
-                redoButton.setEnabled(false);
+                cGridBag updown = new cGridBag().setVertical(true);
+		updown.add(restoreButton = GetButton("icons/restore.png", !Globals.NIMBUSLAF)); //, oe.aConstraints);
+                restoreButton.setToolTipText("Undo (restore current version)");
+		restoreButton.addActionListener(this);
+                restoreButton.setEnabled(false);
                 
-		copyOptionsPanel.add(saveButton = GetButton("icons/down_arrow.png", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
-                saveButton.setToolTipText("Save changes");
-		saveButton.addActionListener(this);
+		updown.add(replaceButton = GetButton("icons/replace.png", !Globals.NIMBUSLAF)); //, oe.aConstraints);
+                replaceButton.setToolTipText("Save (replace current version)");
+		replaceButton.addActionListener(this);
+                replaceButton.setEnabled(false);
                 
-                copyOptionsPanel.add(liveCB = GetToggleButton("icons/run.png", Globals.isLIVE())); //, oe.aConstraints);
-                liveCB.setToolTipText("Enable animation");
-                                liveCB.addItemListener(this);
-
-		copyOptionsPanel.add(oneStepButton = GetButton("icons/step.png", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
+                versionManagerPanel.add(updown);
+                
+		versionManagerPanel.add(nextVersionButton = GetButton("icons/redo.png", !Globals.NIMBUSLAF)); //, oe.aConstraints);
+                nextVersionButton.setToolTipText("Next version");
+		nextVersionButton.addActionListener(this);
+                nextVersionButton.setEnabled(false);
+                
+		oe.toolbarPanel.add(oneStepButton = GetButton("icons/step.png", !Globals.NIMBUSLAF)); //, oe.aConstraints);
                 oneStepButton.setToolTipText("Animate one step forward");
 		oneStepButton.addActionListener(this);
                 
-                copyOptionsPanel.add(fastCB = GetToggleButton("icons/runfast.png", CameraPane.FAST)); //, constraints);
+                oe.toolbarPanel.add(liveCB = GetToggleButton("icons/run.png", Globals.isLIVE())); //, oe.aConstraints);
+                liveCB.setToolTipText("Enable animation");
+                                liveCB.addItemListener(this);
+
+                oe.toolbarPanel.add(fastCB = GetToggleButton("icons/runfast.png", CameraPane.FAST)); //, constraints);
                 fastCB.setToolTipText("Fast mode");
                                 fastCB.addItemListener(this);
                                 
                 //oe.toolboxPanel.Return();
                 
-		copyOptionsPanel.add(screenfitButton = GetButton("icons/fit.png", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
-                screenfitButton.setToolTipText("Screen fit");
-		screenfitButton.addActionListener(this);
-
 //                copyOptionsPanel.add(trackCB = GetToggleButton("icons/track.png", CameraPane.TRACK)); //, oe.aConstraints);
 //                trackCB.setToolTipText("Enable tracking");
 //                                trackCB.addItemListener(this);
@@ -643,147 +1177,145 @@
 
                 if (Globals.ADVANCED)
                 {
-                        oe.toolbarPanel.add(snapobjectButton = new cButton("O+", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
+                        oe.toolbarPanel.add(snapobjectButton = new cButton("O+", !Globals.NIMBUSLAF)); //, oe.aConstraints);
                         snapobjectButton.addActionListener(this);
                         snapobjectButton.setToolTipText("Snap Object");
+                        
+		oe.toolbarPanel.add(fourButton = GetButton("icons/controls-horizontal.png", !Globals.NIMBUSLAF)); //, oe.aConstraints);
+		fourButton.addActionListener(this);
+                fourButton.setToolTipText("Show control panel only");
                 }
 
 		//oe.toolbarPanel.add(new JSeparator(SwingConstants.VERTICAL));
                 
-		oe.toolbarPanel.add(twoButton = new cButton("|-|", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
-                twoButton.setToolTipText("Show center view only");
-		twoButton.addActionListener(this);
-                this.fullscreenLayout = twoButton;
-                
-		oe.toolbarPanel.add(fourButton = new cButton("-||", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
-		fourButton.addActionListener(this);
-                fourButton.setToolTipText("Show left panel only");
-		oe.toolbarPanel.add(sixButton = new cButton("-|-|", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
-                sixButton.setToolTipText("2-column layout left");
-		sixButton.addActionListener(this);
-		oe.toolbarPanel.add(threeButton = new cButton("|-|-", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
-                threeButton.setToolTipText("2-column layout right");
-		threeButton.addActionListener(this);
-		oe.toolbarPanel.add(sevenButton = new cButton("-|-|-", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
-                sevenButton.setToolTipText("3-column layout");
-		sevenButton.addActionListener(this);
-                //
 
-		oe.toolbarPanel.add(rootButton = GetButton("icons/write.png", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
-                rootButton.setToolTipText("Edit selection in new tab");
+		oe.toolbarPanel.add(rootButton = GetButton("icons/openwindow.png", !Globals.NIMBUSLAF)); //, oe.aConstraints);
+                rootButton.setToolTipText("Open selection in new tab");
 		rootButton.addActionListener(this);
 
-                oe.toolbarPanel.add(closeButton = GetButton("icons/close-icon.png", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
+                oe.toolbarPanel.add(closeButton = GetButton("icons/close-icon.png", !Globals.NIMBUSLAF)); //, oe.aConstraints);
                 closeButton.setToolTipText("Close tab");
 		closeButton.addActionListener(this);
 		//oe.treePanel.add(clearButton = new cButton("X"), oe.aConstraints);
 		//clearButton.addActionListener(this);
 
+                cGridBag row1 = new cGridBag();
+                row1.preferredHeight = 8;
+                
                 // INSERT
-                oe.toolboxPanel.add(gridButton = GetButton("icons/grid.png", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
-                gridButton.setToolTipText("Create grid");
+                row1.add(gridButton = GetButton("icons/grid.png", !Globals.NIMBUSLAF)); //, oe.aConstraints);
+                gridButton.setToolTipText("Create ground");
                                 gridButton.addActionListener(this);
 
-                oe.toolboxPanel.add(boxButton = GetButton("icons/box.png", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
+                row1.add(boxButton = GetButton("icons/box.png", !Globals.NIMBUSLAF)); //, oe.aConstraints);
                 boxButton.setToolTipText("Create box");
                                 boxButton.addActionListener(this);
 
-                oe.toolboxPanel.add(sphereButton = GetButton("icons/sphere.png", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
-                sphereButton.setToolTipText("Create sphere");
-                                sphereButton.addActionListener(this);
-
-                oe.toolboxPanel.add(coneButton = GetButton("icons/cone.png", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
-                coneButton.setToolTipText("Create cone");
-                                coneButton.addActionListener(this);
-
-                oe.toolboxPanel.add(torusButton = GetButton("icons/torus.png", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
-                torusButton.setToolTipText("Create torus");
-                                torusButton.addActionListener(this);
-
-                oe.toolboxPanel.add(superButton = GetButton("icons/super.png", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
+                row1.add(superButton = GetButton("icons/super.png", !Globals.NIMBUSLAF)); //, oe.aConstraints);
                 superButton.setToolTipText("Create superellipsoid");
                                 superButton.addActionListener(this);
 
-                if (Globals.ADVANCED)
+                row1.add(sphereButton = GetButton("icons/sphere.png", !Globals.NIMBUSLAF)); //, oe.aConstraints);
+                sphereButton.setToolTipText("Create sphere");
+                                sphereButton.addActionListener(this);
+
+                row1.add(coneButton = GetButton("icons/cone.png", !Globals.NIMBUSLAF)); //, oe.aConstraints);
+                coneButton.setToolTipText("Create cone");
+                                coneButton.addActionListener(this);
+
+                row1.add(torusButton = GetButton("icons/torus.png", !Globals.NIMBUSLAF)); //, oe.aConstraints);
+                torusButton.setToolTipText("Create torus");
+                                torusButton.addActionListener(this);
+
+                if (false) //Globals.ADVANCED)
                 {
-                    oe.toolboxPanel.add(kleinButton = GetButton("icons/klein.png", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
+                    oe.toolboxPanel.add(kleinButton = GetButton("icons/klein.png", !Globals.NIMBUSLAF)); //, oe.aConstraints);
                     kleinButton.setToolTipText("Create Klein bottle");
                                     kleinButton.addActionListener(this);
                 }
                 
-                oe.toolboxPanel.add(particlesButton = GetButton("icons/particles.png", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
+                row1.add(particlesButton = GetButton("icons/particles.png", !Globals.NIMBUSLAF)); //, oe.aConstraints);
                 particlesButton.setToolTipText("Create particle system");
                                particlesButton.addActionListener(this);
 
-                oe.toolboxPanel.Return();
+                oe.toolboxPanel.add(row1);
                 
-                oe.toolboxPanel.add(groupButton = GetButton("icons/group.png", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
+                cGridBag row2 = new cGridBag();
+                row2.preferredHeight = 8;
+                
+                row2.add(groupButton = GetButton("icons/group.png", !Globals.NIMBUSLAF)); //, oe.aConstraints);
                 groupButton.setToolTipText("Create group");
                                groupButton.addActionListener(this);
 
-                oe.toolboxPanel.add(compositeButton = GetButton("icons/composite.png", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
+                row2.add(compositeButton = GetButton("icons/composite.png", !Globals.NIMBUSLAF)); //, oe.aConstraints);
                 compositeButton.setToolTipText("Create composite");
                                compositeButton.addActionListener(this);
 
-                oe.toolboxPanel.add(switchButton = GetButton("icons/switch.png", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
-                switchButton.setToolTipText("Create switch");
+                row2.add(switchButton = GetButton("icons/switch.png", !Globals.NIMBUSLAF)); //, oe.aConstraints);
+                switchButton.setToolTipText("Create item switcher");
                                switchButton.addActionListener(this);
 
-                oe.toolboxPanel.add(loopButton = GetButton("icons/loop.png", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
+                row2.add(loopButton = GetButton("icons/loop.png", !Globals.NIMBUSLAF)); //, oe.aConstraints);
                 loopButton.setToolTipText("Create loop");
                                loopButton.addActionListener(this);
 
-                oe.toolboxPanel.add(textureButton = GetButton("icons/texture.png", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
+                row2.add(textureButton = GetButton("icons/texture.png", !Globals.NIMBUSLAF)); //, oe.aConstraints);
                 textureButton.setToolTipText("Create texture");
                                textureButton.addActionListener(this);
 
-                oe.toolboxPanel.add(overlayButton = GetButton("icons/overlay.png", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
+                row2.add(overlayButton = GetButton("icons/overlay.png", !Globals.NIMBUSLAF)); //, oe.aConstraints);
                 overlayButton.setToolTipText("Create overlay");
                                overlayButton.addActionListener(this);
 
-                oe.toolboxPanel.add(lightButton = GetButton("icons/light-bulb.png", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
+                row2.add(lightButton = GetButton("icons/light-bulb.png", !Globals.NIMBUSLAF)); //, oe.aConstraints);
                 lightButton.setToolTipText("Create light");
                                lightButton.addActionListener(this);
 
-                for (int i=6; --i>=0;)
-                {
-                oe.toolboxPanel.Return();
-                oe.toolboxPanel.add(new cGridBag());
-                oe.toolboxPanel.add(new cGridBag());
-                oe.toolboxPanel.add(new cGridBag());
-                oe.toolboxPanel.add(new cGridBag());
-                oe.toolboxPanel.add(new cGridBag());
-                oe.toolboxPanel.add(new cGridBag());
-                oe.toolboxPanel.add(new cGridBag());
-                }
+                oe.toolboxPanel.add(row2);
+                
+                cGridBag textures = new cGridBag();
+                
+                CreateTexturePanel(textures);
+                
+                int tabCount = resourcecontainer.getTabCount();
+                
+                if (tabCount > 0)
+                    resourcecontainer.setSelectedIndex((int)(Math.random() * tabCount));
+                
+                oe.toolboxPanel.add(textures);
+                
+                textures.preferredHeight = 100;
+                
+                CreateSkyboxPanel(oe.skyboxPanel);
                 
                 // EDIT panel
-		editCommandsPanel.add(editButton = new cButton("+", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
-                editButton.setToolTipText("Edit selection");
+		editCommandsPanel.add(editButton = GetButton("icons/controls.png", !Globals.NIMBUSLAF)); //, oe.aConstraints);
+                editButton.setToolTipText("Pin selection controls");
 		editButton.addActionListener(this);
 
-		editCommandsPanel.add(uneditButton = new cButton("-", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
-                uneditButton.setToolTipText("Unedit selection");
+		editCommandsPanel.add(uneditButton = GetButton("icons/remove.png", !Globals.NIMBUSLAF)); //, oe.aConstraints);
+                uneditButton.setToolTipText("Unpin and remove selection controls");
 		uneditButton.addActionListener(this);
 
 		editCommandsPanel.add(allParamsButton = new JCheckBox("All", allparams)); //, oe.aConstraints);
-                allParamsButton.setToolTipText("Edit all params");
+                allParamsButton.setToolTipText("Show all controls");
 		allParamsButton.addActionListener(this);
 
-		editCommandsPanel.add(clearPanelButton = new cButton("C", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
-                clearPanelButton.setToolTipText("Clear edit panel");
+		editCommandsPanel.add(clearPanelButton = GetButton("icons/clear.png", !Globals.NIMBUSLAF)); //, oe.aConstraints);
+                clearPanelButton.setToolTipText("Clear all controls");
 		clearPanelButton.addActionListener(this);
 
-		editCommandsPanel.add(unselectButton = new cButton("U", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
-                unselectButton.setToolTipText("Unselect");
-		unselectButton.addActionListener(this);
+		//editCommandsPanel.add(unselectButton = new cButton("U", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
+                //unselectButton.setToolTipText("Unselect");
+		//unselectButton.addActionListener(this);
 
-		editCommandsPanel.add(flashSelectionButton = GetButton("icons/flash-light.png", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
+		editCommandsPanel.add(flashSelectionButton = GetButton("icons/flash-light.png", !Globals.NIMBUSLAF)); //, oe.aConstraints);
                 flashSelectionButton.setToolTipText("Highlight selection");
 		flashSelectionButton.addActionListener(this);
                 
                 editCommandsPanel.preferredHeight = 1;
                 
+                SetPinStates(false);
 //                oe.treePanel.add(commandsPanel);
 //                oe.treePanel.Return();
                 
@@ -800,6 +1332,8 @@
                 
                 cGridBag jSPPanel = new cGridBag();
 
+            jSPPanel.preferredHeight = 20;
+            
                 JScrollPane jSP;
 		//ctrlPanel.add(new JScrollPane(jTree = new cTree(new cTreeModel(this))), aConstraints);
 		jSPPanel.add(jSP = new JScrollPane(oe.jTree)); //, oe.aConstraints);
@@ -808,11 +1342,15 @@
                 oe.treePanel.add(jSPPanel);
                 oe.treePanel.Return();
 
-                oe.treePanel.add(copyOptionsPanel);
+                oe.treePanel.add(versionManagerPanel);
                 oe.treePanel.Return();
                 
-//        mainPanel.setDividerLocation(0.5); //1.0);
-//        mainPanel.setResizeWeight(0.5);
+        versionSliderPane = AddSlider(oe.treePanel, "Version", 0, 0, 0);
+            versionSlider = (cNumberSlider)versionSliderPane.getComponent(1);
+            versionSliderPane.preferredHeight = 3;
+                
+//        mainPanel.setDividerLocation(0.1); //1.0);
+        mainPanel.setResizeWeight(0.4);
         
 		//jList.addListSelectionListener(this);
 		oe.jTree.addTreeSelectionListener(this);
@@ -820,7 +1358,7 @@
 		//jTree.setEditable(true);
 		oe.jTree.setDragEnabled(true);
 		//jTree.setPreferredSize(new Dimension(10,10));
-		jSP.setPreferredSize(new Dimension(100,200));
+		//jSP.setPreferredSize(new Dimension(100,200));
 		
                 oe.jTree.setCellRenderer(new cTreeModel.Renderer());
                 
@@ -832,7 +1370,7 @@
 		dgr.addDragGestureListener(this);
 		}catch(Exception e) {}
 		*/
-        radio.layout = sevenButton;
+        radio.layout = threeButton; // sixButton;
         oe.toolbarPanel.add(radioPanel); //, oe.aConstraints);
 	}
         
@@ -857,56 +1395,62 @@
                                 boxCB.addItemListener(this);
                                 
                 panel.add(zoomBoxCB = new cCheckBox("Zoom", CameraPane.ZOOMBOXMODE)); //, constraints);
-                zoomBoxCB.setToolTipText("Display bounding boxes when moving the wheel");
+                zoomBoxCB.setToolTipText("Display only for wheel");
                                 zoomBoxCB.addItemListener(this);
 
         if (true) // Globals.ADVANCED)
         {
-                panel.add(supportCB = new cCheckBox("Support", CameraPane.SUPPORT)); //, constraints);
-                supportCB.setToolTipText("Enable rigging");
-                                supportCB.addItemListener(this);
+//                panel.add(supportCB = new cCheckBox("Support", CameraPane.SUPPORT)); //, constraints);
+//                supportCB.setToolTipText("Enable rigging");
+//                                supportCB.addItemListener(this);
+
+                panel.add(freezeCB = new cCheckBox("Fast cam", Globals.FREEZEONMOVE)); //, constraints);
+                freezeCB.setToolTipText("Fast moving camera");
+                                freezeCB.addItemListener(this);
 
         //        panel.add(localCB = new cCheckBox("Local", CameraPane.LOCALTRANSFORM), constraints);
         //                        localCB.addItemListener(this);
 
                 panel.Return();
                 
+            if (Globals.ADVANCED)
+            {
                 panel.add(crowdCB = new cCheckBox("Crowd", Globals.CROWD)); //, constraints);
                 crowdCB.setToolTipText("Used for crowds");
                                 crowdCB.addItemListener(this);
+            }
 
                 panel.add(smoothCB = new cCheckBox("Inertia", CameraPane.INERTIA)); //, constraints);
                 smoothCB.setToolTipText("Snapping delay");
                                 smoothCB.addItemListener(this);
 
-                panel.add(slowCB = new cCheckBox("Slow", CameraPane.SLOWPOSE)); //, constraints);
-                slowCB.setToolTipText("Smooth interpolation");
-                                slowCB.addItemListener(this);
+//                panel.add(slowCB = new cCheckBox("Slow", CameraPane.SLOWPOSE)); //, constraints);
+//                slowCB.setToolTipText("Smooth interpolation");
+//                                slowCB.addItemListener(this);
+                panel.add(minshaderCB = new cCheckBox("Min shader", Globals.MINSHADER)); //, constraints);
+                minshaderCB.setToolTipText("Minimal fast shader");
+                                minshaderCB.addItemListener(this);
                                 
-//        	constraints.gridy += 1;
 //                panel.add(speakerMocapCB = new cCheckBox("Mocap", CameraPane.SPEAKERMOCAP), constraints);
 //                                speakerMocapCB.addItemListener(this);
 
-                panel.Return();
-                
             if (false)
             {
                 // handled in scripts
-        	//constraints.gridy += 1;
                 panel.add(speakerCameraCB = new cCheckBox("Cam", CameraPane.SPEAKERCAMERA)); //, constraints);
                                 speakerCameraCB.addItemListener(this);
 
-        	//constraints.gridy += 1;
                 panel.add(speakerFocusCB = new cCheckBox("Focus", CameraPane.SPEAKERFOCUS)); //, constraints);
                                 speakerFocusCB.addItemListener(this);
 
-        	//constraints.gridy += 1;
-                panel.add(smoothfocusCB = new cCheckBox("Smooth", CameraPane.SMOOTHFOCUS)); //, constraints);
-                                smoothfocusCB.addItemListener(this);
                 panel.Return();
             }
 
-//constraints.gridx += 1;
+                panel.add(smoothfocusCB = new cCheckBox("Smooth", CameraPane.SMOOTHFOCUS)); //, constraints);
+                                smoothfocusCB.addItemListener(this);
+                                
+                panel.Return();
+                
 //panel.add(debugCB = new cCheckBox("Debug", CameraPane.DEBUG), constraints);
 //                debugCB.addItemListener(this);
 
@@ -919,7 +1463,7 @@
                                 oeilCB.addItemListener(this);
 
                 panel.add(shadowCB = new cCheckBox("Shadow", Globals.COMPUTESHADOWWHENLIVE)); //, constraints);
-                shadowCB.setToolTipText("Compute shadows when live");
+                shadowCB.setToolTipText("When live compute shadows");
                                 shadowCB.addItemListener(this);
 
                 panel.Return();
@@ -928,12 +1472,12 @@
                                 toggleTextureCB.addItemListener(this);
                                 
                 panel.add(toggleSwitchCB = new cCheckBox("Switch", CameraPane.SWITCH)); //, constraints);
-                toggleSwitchCB.setToolTipText("Use switch");
+                toggleSwitchCB.setToolTipText("Choose a single item");
                                 toggleSwitchCB.addItemListener(this);
                                 
-                panel.add(autosaveCB = new cCheckBox("Auto-save", Globals.SAVEONMAKE)); //, constraints);
-                autosaveCB.setToolTipText("Auto-save on structure change");
-                                autosaveCB.addItemListener(this);
+                panel.add(autokeepCB = new cCheckBox("Auto-replace", Globals.REPLACEONMAKE)); //, constraints);
+                autokeepCB.setToolTipText("On structure change");
+                                autokeepCB.addItemListener(this);
 
                 panel.Return();
             if (Globals.ADVANCED)
@@ -960,9 +1504,17 @@
 	
         void EditObject(Object3D obj)
         {
+            //assert(obj instanceof Composite);
+            
+//            if (obj.versionlist == null)
+//            {
+//                obj.versionlist = new Object3D[100];
+//                obj.versionindex = -1;
+//            }
+            
             cRadio radioButton = new cRadio(obj.name);
             
-            // Patch to avoid bug with transparency.
+            // June 2019. Patch to avoid bug with transparency.
             radioButton.hadMaterial = obj.material != null;
             if (!radioButton.hadMaterial)
             {
@@ -970,7 +1522,7 @@
             }
             
             radioButton.SetObject(obj);
-            radioButton.layout = sevenButton;
+            radioButton.layout = threeButton; // sixButton;
             radioButton.SetCamera(cameraView.renderCamera, false);
             radioButton.addActionListener(this);
             radioPanel.add(radioButton);
@@ -984,6 +1536,7 @@
                     
             oe.SetupViews();
             
+        if (Globals.DEBUG)
 		System.out.println("SetupViews");
             DragSource.getDefaultDragSource().createDefaultDragGestureRecognizer(
                   oe.cameraView, DnDConstants.ACTION_COPY_OR_MOVE, this); // ACTION_LINK ??
@@ -995,10 +1548,13 @@
         cCheckBox localCB;
         cCheckBox crowdCB;
         cCheckBox smoothCB;
+        cCheckBox minshaderCB;
+        
         cToggleButton fastCB;
         cCheckBox slowCB;
         cCheckBox boxCB;
         cCheckBox zoomBoxCB;
+        cCheckBox freezeCB;
         //cToggleButton trackCB;
         cCheckBox trackCB;
         cCheckBox smoothfocusCB;
@@ -1009,7 +1565,7 @@
         
         cCheckBox oeilCB;
         cCheckBox shadowCB;
-        cCheckBox autosaveCB;
+        cCheckBox autokeepCB;
         cCheckBox lookAtCB;
         
 //        static int COLOR = 1;
@@ -1065,6 +1621,12 @@
                 cameraView.ToggleInertia();
         cameraView.repaint();
             }
+            else if(e.getSource() == minshaderCB)
+            {
+                Globals.MINSHADER ^= true;
+                cameraView.programInitialized = false;
+                cameraView.repaint();
+            }
             else if(e.getSource() == localCB)
             {
                 cameraView.ToggleLocal();
@@ -1117,9 +1679,13 @@
             {
                 Globals.COMPUTESHADOWWHENLIVE ^= true;
             }
-            else if(e.getSource() == autosaveCB)
+            else if(e.getSource() == freezeCB)
             {
-                Globals.SAVEONMAKE ^= true;
+                Globals.FREEZEONMOVE ^= true;
+            }
+            else if(e.getSource() == autokeepCB)
+            {
+                Globals.REPLACEONMAKE ^= true;
             }
             else if(e.getSource() == lookAtCB)
             {
@@ -1201,8 +1767,6 @@
                         }
                     }
                     
-                    String string = (String) object;
-                    
                     System.out.println("Transfer = " + object + "; drop : " + target);
 //                    if( object instanceof java.io.File[])
 //                    {
@@ -1210,6 +1774,8 @@
 //                        objEditor.DropFile((java.io.File[]) object, true);
 //                        return;
 //                    }
+                    
+                    String string = object.toString();
                     
                     // File path for Mac and Windows
                     if (string.charAt(0) == '/' || string.charAt(1) == ':')
@@ -1337,7 +1903,12 @@
   TreePath path;
 
   public TransferableTreePath(TreePath tp) {
-    path = tp;
+      Object[] objs = new Object[tp.getPathCount()];
+      for (int i=0; i<objs.length; i++)
+      {
+          objs[i] = ((Object3D)tp.getPathComponent(i)).GetUUID();
+      }
+    path = new TreePath(objs);
   }
 
   public synchronized DataFlavor[] getTransferDataFlavors() {
@@ -1459,6 +2030,9 @@
 		animationItem.addItemListener(this);
                 animationItem.setState(Globals.ANIMATION);
                 
+		menu.add(archiveItem = new MenuItem("Archive3D..."));
+		archiveItem.addActionListener(this);
+                
 		menu.add("-");
 		parseverticesItem = menu.add(new MenuItem("Multiplicity"));
 		parseverticesItem.addActionListener(this);
@@ -1471,6 +2045,8 @@
 		reduce34MorphItem = menu.add(new MenuItem("Reduce Morphs (34)"));
 		reduce34MorphItem.addActionListener(this);
 		menu.add("-");
+		menu.add(memoryItem = new MenuItem("Memory Usage"));
+		memoryItem.addActionListener(this);
 		menu.add(computeAOItem = new MenuItem("Compute AO"));
 		computeAOItem.addActionListener(this);
                 
@@ -1479,8 +2055,6 @@
 		mirrorItem = menu.add(new MenuItem("Mirror Poses"));
 		mirrorItem.addActionListener(this);
 		menu.add("-");
-		menu.add(memoryItem = new MenuItem("Memory Usage"));
-		memoryItem.addActionListener(this);
 		menu.add(analyzeItem = new MenuItem("Analyze"));
 		analyzeItem.addActionListener(this);
 		menu.add(dumpItem = new MenuItem("Print"));
@@ -1515,7 +2089,7 @@
             Object3D obj = (Object3D)group.selection.elementAt(0);
             objEditor.ScreenFit(obj, false);
             
-            cameraView.pingthread.StepToTarget(true);
+            cameraView.pingthread.StepToTarget(); //true);
             refreshContents();
         }
 
@@ -1552,7 +2126,7 @@
                 obj.parent.TransformToWorld(maxima); //, maxima);
             }
             
-            Object3D shadow = new Object3D("Shadow " + obj.name);
+            Object3D shadow = new Object3D("Shadow" + obj.name);
             
             shadow.toParent = LA.newMatrix();
             shadow.fromParent = LA.newMatrix();
@@ -1566,24 +2140,24 @@
             switch(axis)
             {
                 case 0 :
-                    vert1.x = minima.x; vert1.y = minima.y; vert1.z = minima.z;
-                    vert2.x = minima.x; vert2.y = maxima.y; vert2.z = minima.z;
-                    vert3.x = minima.x; vert3.y = minima.y; vert3.z = maxima.z;
-                    vert4.x = minima.x; vert4.y = maxima.y; vert4.z = maxima.z;
+                    vert1.x = minima.x + 0.001f; vert1.y = minima.y; vert1.z = minima.z;
+                    vert2.x = minima.x + 0.001f; vert2.y = maxima.y; vert2.z = minima.z;
+                    vert3.x = minima.x + 0.001f; vert3.y = minima.y; vert3.z = maxima.z;
+                    vert4.x = minima.x + 0.001f; vert4.y = maxima.y; vert4.z = maxima.z;
                     norm = cVector.X;
                     break;
                 case 1 :
-                    vert1.x = minima.x; vert1.y = minima.y; vert1.z = minima.z;
-                    vert2.x = maxima.x; vert2.y = minima.y; vert2.z = minima.z;
-                    vert3.x = minima.x; vert3.y = minima.y; vert3.z = maxima.z;
-                    vert4.x = maxima.x; vert4.y = minima.y; vert4.z = maxima.z;
+                    vert1.x = minima.x; vert1.y = minima.y + 0.001f; vert1.z = minima.z;
+                    vert2.x = maxima.x; vert2.y = minima.y + 0.001f; vert2.z = minima.z;
+                    vert3.x = minima.x; vert3.y = minima.y + 0.001f; vert3.z = maxima.z;
+                    vert4.x = maxima.x; vert4.y = minima.y + 0.001f; vert4.z = maxima.z;
                     norm = cVector.Y;
                     break;
                 case 2 :
-                    vert1.x = minima.x; vert1.y = minima.y; vert1.z = minima.z;
-                    vert2.x = maxima.x; vert2.y = minima.y; vert2.z = minima.z;
-                    vert3.x = minima.x; vert3.y = maxima.y; vert3.z = minima.z;
-                    vert4.x = maxima.x; vert4.y = maxima.y; vert4.z = minima.z;
+                    vert1.x = minima.x; vert1.y = minima.y; vert1.z = minima.z + 0.001f;
+                    vert2.x = maxima.x; vert2.y = minima.y; vert2.z = minima.z + 0.001f;
+                    vert3.x = minima.x; vert3.y = maxima.y; vert3.z = minima.z + 0.001f;
+                    vert4.x = maxima.x; vert4.y = maxima.y; vert4.z = minima.z + 0.001f;
                     norm = cVector.Z;
                     break;
             }
@@ -1624,10 +2198,38 @@
             shadow.material = new cMaterial(obj.material);
             shadow.material.diffuse = 0.0001f;
             shadow.material.specular = 0.0001f;
-            //shadow.projectedVertices[1].x = 300;
+            shadow.material.opacity = 0.75f;
+            
+            AllocProjectedVertices(shadow);
+        
+            shadow.projectedVertices[1].x = 300;
             
             makeSomething(shadow);
         }
+
+    private void ClearUnpinned()
+    {
+        //for (Object3D obj : listUI)
+        for (int i=listUI.size(); --i>=0;)
+        {
+            Object3D obj = listUI.elementAt(i);
+            if (!obj.pinned)
+            {
+                obj.CloseUI();
+                listUI.remove(i);
+            }
+        }
+    }
+
+    private void EditElement(Object3D elem, boolean newWindow)
+    {
+        //    if (!(elem instanceof Composite))
+        //        newWindow = false;
+            listUI.add(elem);
+            elem.openEditWindow(this, newWindow); //, false);
+            System.out.println("edit : " + elem);
+            elem.editWindow.refreshContents(true); // ? new
+    }
         
     /**
      * applyExample
@@ -2056,6 +2658,23 @@
 		{
 			makeSomething(new Light());
 		} else
+//		if (source == skybox1Button ||
+//                    source == skybox2Button ||
+//                    source == skybox3Button ||
+//                    source == skybox4Button ||
+//                    source == skybox5Button ||
+//                    source == skybox6Button ||
+//                    source == skybox7Button ||
+//                    source == skybox11Button ||
+//                    source == skybox12Button ||
+//                    source == skybox13Button ||
+//                    source == skybox14Button ||
+//                    source == skybox15Button ||
+//                    source == skybox16Button ||
+//                    source == skybox17Button)
+//		{
+//                    ChangeSkybox(source);
+//		} else
 		if (source == csgItem)
 		{
 			group(new CSG());
@@ -2104,28 +2723,36 @@
 		} else
 		if (source == loopItem || source == loopButton)
 		{
+                    if (!group.selection.isEmpty())
+                    {
                         Composite csg = new GroupLeaf();
-                        csg.count = 5;
 			group(csg);
-                        Composite child = new cGroup();
+                        csg.count = 5;
+                        Composite child = new cGroup("Branch");
                         csg.addChild(child);
                         child.addChild(csg);
+                    }
 		} else
 		if (source == doubleItem)
 		{
-                        Composite csg = new GroupLeaf();
-                        csg.count = 5;
+                    if (!group.selection.isEmpty())
+                    {
+                        Composite csg = new GroupLeaf("Fork");
 			group(csg);
-                        Composite child = new cGroup();
+                        csg.count = 5;
+                        Composite child = new cGroup("Branch A");
                         csg.addChild(child);
                         child.addChild(csg);
-                        child = new cGroup();
+                        child = new cGroup("Branch B");
                         csg.addChild(child);
                         child.addChild(csg);
+                    }
 		} else
 		if (source == tripleItem)
 		{
-                        Composite csg = new GroupLeaf();
+                    if (!group.selection.isEmpty())
+                    {
+                        Composite csg = new GroupLeaf("Trident");
                         csg.count = 4;
 			group(csg);
                         Composite child = new cGroup();
@@ -2137,11 +2764,12 @@
                         child = new cGroup();
                         csg.addChild(child);
                         child.addChild(csg);
+                    }
 		} else
 		if (source == computeAOItem)
 		{
                     Globals.drawMode = CameraPane.OCCLUSION;
-                    Globals.theRenderer.repaint();
+                    cameraView.repaint();
 		} else
 		if (source == recompileItem)
 		{
@@ -2156,7 +2784,7 @@
 		if (source == invariantsItem)
 		{
                     System.out.println("Invariants:");
-                    Grafreed.grafreeD.universe.invariants();
+                    Grafreed.grafreed.universe.invariants();
 		} else
 		if (source == memoryItem)
 		{
@@ -2183,21 +2811,56 @@
 		{
 			Maximize();
 		} else
-		if (source == fullButton)
+		if (source == fullScreenButton)
 		{
 			ToggleFullScreen();
 		} else
-		if (source == undoButton)
+		if (source == collapseButton)
 		{
-			Undo();
+                        this.expandedLayout = radio.layout;
+			CollapseToolbar();
 		} else
-		if (source == redoButton)
+//		if (source == maximize3DButton)
+//		{
+//                        this.expandedLayout = radio.layout;
+//                        radio.layout = twoButton;
+//			CollapseToolbar();
+//                        Show3DView();
+//		} else
+		if (source == previousVersionButton)
 		{
-			Redo();
+                        // Go to previous version
+			//if (!Undo())
+                            //java.awt.Toolkit.getDefaultToolkit().beep();
+			PreviousVersion();
 		} else
-		if (source == saveButton)
+		if (source == restoreButton)
 		{
-			Save();
+                        // Restore current version
+			Restore();
+        //restoreButton.setEnabled(false);
+		} else
+		if (source == replaceButton)
+		{
+                        // Overwrite current version
+			Replace();
+        //replaceButton.setEnabled(false);
+		} else
+		if (source == nextVersionButton)
+		{
+                        // Go to next version
+			NextVersion();
+		} else
+		if (source == saveVersionButton)
+		{
+                        // Save a new version
+			if (!DuplicateVersion()) //true))
+                            java.awt.Toolkit.getDefaultToolkit().beep();
+		} else
+		if (source == deleteVersionButton)
+		{
+                        // Delete a new version
+                        DeleteVersion();
 		} else
 		if (source == oneStepButton)
 		{
@@ -2206,17 +2869,14 @@
 		} else
 		if (source == screenfitButton)
 		{
-			//Reload(lastConverter, lastFilename, true);
                     ScreenFit();
 		} else
 		if (source == screenfitpointButton)
 		{
-			//Reload(lastConverter, lastFilename, true);
                     ScreenFitPoint();
 		} else
 		if (source == snapobjectButton)
 		{
-			//Reload(lastConverter, lastFilename, true);
                     SnapObject();
 		} else
 //		if (event.getSource() == recompileButton)
@@ -2255,11 +2915,11 @@
 		} else
 		if (source == undoItem)
 		{
-			Undo();
+			PreviousVersion();
 		} else
 		if (source == redoItem)
 		{
-			Redo();
+			NextVersion();
 		} else
 		if (source == duplicateItem)
 		{
@@ -2580,7 +3240,7 @@
 		{
 			StepAll();
 		} else
-		if (source == clearItem) // || event.getSource() == clearButton)
+		if (source == deleteItem) // || event.getSource() == clearButton)
 		{
 			//int indices[] = jList.getSelectedIndices();
 			//for (int i = indices.length - 1; i >= 0; i--)
@@ -2740,15 +3400,40 @@
 		} else
 		if (source == ungroupItem || source == ungroupButton)
 		{
-			//ungroup();
+                    boolean canUngroup = true;
+                    
                     for (int i=0; i<group.selection.size(); i++)
                     {
-                        Ungroup(group.selection.get(i));
+                        Object3D selectedItem = group.selection.get(i);
+                        
+                        if (selectedItem.Size() == 0)
+                        {
+                            // Cannot ungroup leaves
+                            canUngroup = false;
+                            break;
+                        }
+                                
+                        if (selectedItem == group)
+                        {
+                            // Cannot ungroup root
+                            canUngroup = false;
+                            break;
+                        }
                     }
 
-                    ClearSelection(false);
-                    
-                    refreshContents();
+                    if (canUngroup)
+                    {
+                        for (int i=0; i<group.selection.size(); i++)
+                        {
+                            Object3D selectedItem = group.selection.get(i);
+                            
+                            Ungroup(selectedItem);
+                        }
+
+                        ClearSelection(false);
+
+                        refreshContents();
+                    }
 		} else
 		if (source == genUVItem)
                 {
@@ -2793,6 +3478,10 @@
 		if (source == clearMaterialsItem)
                 {
 			ClearMaterials();
+		} else
+		if (source == clearVersionsItem)
+                {
+			ClearVersions();
 		} else
 		if (source == liveleavesItem)
                 {
@@ -2934,6 +3623,18 @@
 		{
                         TransformChildren();
 		} else
+		if (source == textureRatioRItem)
+		{
+                        TextureRatio(0);
+		} else
+		if (source == textureRatioGItem)
+		{
+                        TextureRatio(1);
+		} else
+		if (source == textureRatioBItem)
+		{
+                        TextureRatio(2);
+		} else
 		if (source == resetTransformItem)
 		{
                         ResetTransform();
@@ -3049,7 +3750,7 @@
 			for (Enumeration e = group.selection.elements(); e.hasMoreElements();)
 			{
 				obj = (Object3D)e.nextElement();
-                                obj.SetBumpTexture(null);
+                                obj.ResetBumpTexture();
 			}
 			
 			refreshContents();
@@ -3063,6 +3764,31 @@
                                 obj.SetBumpTexture(obj.GetPigmentTexture());
 			}
 			
+			refreshContents();
+		} else
+		if (source == embedTexturesItem)
+		{
+			Object3D obj;
+			for (Enumeration e = group.selection.elements(); e.hasMoreElements();)
+			{
+				obj = (Object3D)e.nextElement();
+                                obj.EmbedTextures(true);
+			}
+			
+			refreshContents();
+		} else
+		if (source == deEmbedTexturesItem)
+		{
+			Object3D obj;
+			for (Enumeration e = group.selection.elements(); e.hasMoreElements();)
+			{
+				obj = (Object3D)e.nextElement();
+                                obj.EmbedTextures(false);
+			}
+			
+                CameraPane.texturepigment.clear();
+                CameraPane.texturebump.clear();
+                
 			refreshContents();
 		} else
 		if (source == flashSelectionButton)
@@ -3080,38 +3806,7 @@
                     if (CameraPane.FULLSCREEN)
                         fullscreenLayout = radio.layout;
                     
-                    // bug
-                    //gridPanel.setDividerLocation(1.0);
-                    //bigPanel.setDividerLocation(0.0);
-//                    bigThree.remove(scenePanel);
-//                    bigThree.remove(centralPanel);
-//                    bigThree.remove(XYZPanel);
-//                    aWindowConstraints.gridx = 0;
-//                    aWindowConstraints.gridy = 0;
-//                    aWindowConstraints.gridwidth = 1;
-//            //        aConstraints.gridheight = 3;
-//                    aWindowConstraints.fill = GridBagConstraints.VERTICAL;
-//                    aWindowConstraints.weightx = 0;
-//                    aWindowConstraints.weighty = 1;
-//                    //bigThree.add(jtp, aWindowConstraints);
-//                    aWindowConstraints.weightx = 1;
-//                    aWindowConstraints.gridwidth = 3;
-//            //        aConstraints.gridheight = 3;
-//                    aWindowConstraints.gridx = 1;
-//                    aWindowConstraints.fill = GridBagConstraints.BOTH;
-//                    bigThree.add(centralPanel, aWindowConstraints);
-//                    aWindowConstraints.weightx = 0;
-//                    aWindowConstraints.gridx = 4;
-//                    aWindowConstraints.gridwidth = 1;
-//            //        aConstraints.gridheight = 3;
-//                    aWindowConstraints.fill = GridBagConstraints.VERTICAL;
-//                    //bigThree.add(XYZPanel, aWindowConstraints);
-//                    scenePanel.setVisible(false);
-//                    centralPanel.setVisible(true);
-//                    XYZPanel.setVisible(false);
-                    bigThree.ClearUI();
-                    bigThree.add(centralPanel);
-                    bigThree.FlushUI();
+                    Show3DView();
                     
                     cameraView.requestFocusInWindow();
                     
@@ -3166,8 +3861,8 @@
 //                    centralPanel.setVisible(true);
 //                    XYZPanel.setVisible(true);
                     bigThree.ClearUI();
+                    bigThree.add(scenePanel);
                     bigThree.add(centralPanel);
-                    bigThree.add(XYZPanel);
                     bigThree.FlushUI();
                     
                     cameraView.requestFocusInWindow();
@@ -3247,8 +3942,8 @@
 //                    centralPanel.setVisible(true);
 //                    XYZPanel.setVisible(false);
                     bigThree.ClearUI();
-                    bigThree.add(scenePanel);
                     bigThree.add(centralPanel);
+                    bigThree.add(scenePanel);
                     bigThree.FlushUI();
                     
                     cameraView.requestFocusInWindow();
@@ -3297,6 +3992,7 @@
                 } else
 		if (source == rootButton)
 		{
+                    Replace();
                     Object3D obj;
                     for (Enumeration e = group.selection.elements(); e.hasMoreElements();)
                     {
@@ -3311,6 +4007,9 @@
 		if (source == closeButton)
 		{
 //System.out.println("CLOSE: " + buttonGroup.getSelection());
+                    if (copy.versionlist != null)
+                        Replace();
+                    
                     cRadio ab;
                     for (Enumeration e = buttonGroup.getElements(); e.hasMoreElements();)
                     {
@@ -3354,10 +4053,10 @@
 			{
 				Object3D child = (Object3D)e.nextElement();
                                 if(child.editWindow != null)
-                                    objectPanel.setSelectedIndex(objectPanel.indexOfTab("Edit"));
                                 child.pinned = false;
 				child.CloseUI();
                                 listUI.remove(child);
+//                                    objectPanel.setSelectedIndex(objectPanel.indexOfTab("Edit"));
                                 
                                 //child.editWindow = null; // ???????????
 			}
@@ -3372,10 +4071,11 @@
                     //copy.ClearUI();
                     for (Object3D obj : listUI)
                     {
-                        obj.pinned = true;
+                        obj.pinned = false;
                         obj.CloseUI();
                     }
                     listUI.clear();
+                    SetPinStates(group.selection.size() > 0);
                     refreshContents(true);
 		} else
 		if (source == allParamsButton)
@@ -3408,6 +4108,9 @@
 		} else
                 if(source instanceof cRadio)
 		{
+                    if (copy.versionlist != null)
+                        Replace();
+                    
                     group.parent = keepparent;
                         group.attributes = 0;
                     //group.editWindow = null;
@@ -3431,6 +4134,7 @@
                     }
                     
                     copy = group;
+                    
                     //Globals.theRenderer.object = group;
                     if(!useclient)
                     {
@@ -3457,6 +4161,40 @@
                         currentLayout = sevenButton;
                      */
                     radio.layout.doClick();
+                    
+                    //assert(copy instanceof Composite);
+                    
+        if (copy.versionlist == null)
+        {
+            copy.versionindex = -1;
+            
+            // Cannot work with loops
+            // To fix this issue, we first mark all nodes above the root,
+            // and check if any of these nodes are reachable below the root.
+            Grafreed.grafreed.universe.TagObjects(copy, true);
+            
+            if (copy instanceof Composite && !copy.HasTags())
+            {
+                if (copy.versionlist == null)
+                    copy.versionlist = new Object3D[100];
+                
+                //Save(true);
+            }
+            else
+                copy.versionindex = -2;
+            
+            Grafreed.grafreed.universe.TagObjects(copy, false);
+        }
+        
+                    SetVersionStates();
+
+                    ClearUnpinned();
+                    
+                    //Grafreed.Assert(group != null);
+                    //Grafreed.Assert(group.selection != null);
+                    SetPinStates(group.selection == null || group.selection.size() > 0);
+                    if (group.selection == null || group.selection.size() == 1)
+                        EditSelection(false);
                     keepparent = group.parent;
                     // PARENT = NULL or not???
                     //group.parent = null; // ROOT
@@ -3468,11 +4206,13 @@
                 } else if (event.getSource() == editCameraItem)
                 {
                     cameraView.ProtectCamera();
+                    cameraView.requestFocusInWindow();
                     cameraView.repaint();
                     return;
                 } else if (event.getSource() == restoreCameraItem || event.getSource() == restoreCameraButton)
                 {
                     cameraView.RevertCamera();
+                    cameraView.requestFocusInWindow();
                     cameraView.repaint();
                     return;
         //        } else if (event.getSource() == textureButton)
@@ -3561,9 +4301,46 @@
             refreshContents();
         }
         
+        void TextureRatio(int axis)
+        {
+            Object3D obj;
+            for (Enumeration e = group.selection.elements(); e.hasMoreElements();)
+            {
+                    obj = (Object3D)e.nextElement();
+                    obj.TextureRatio(axis);
+            }
+
+            refreshContents();
+        }
+        
         void ResetTransform()
         {
             ResetTransform(-1);
+        }
+        
+        void ScaleSelection(int scale)
+        {
+            Object3D obj;
+            for (Enumeration e = group.selection.elements(); e.hasMoreElements();)
+            {
+                    obj = (Object3D)e.nextElement();
+                    
+                    if (obj.toParent == null)
+                        continue;
+                    
+                    obj.Scale(scale);
+                    
+                    if (obj.parent == null)
+                    {
+                        System.out.println("NULL PARENT!");
+                    //    new Exception().printStackTrace();
+                    }
+                    else
+                        TouchTransform(obj);
+                        //obj.parent.Touch();
+            }
+
+            refreshContents();
         }
         
         void ResetTransform(int mask)
@@ -3576,42 +4353,8 @@
                     if (obj.toParent == null)
                         continue;
                     
-                    if (mask == -1)
-                    {
-                        if (obj instanceof Camera) // jan 2014
-                        {
-                            LA.matIdentity(obj.toParent);
-                            LA.matIdentity(obj.fromParent);
-                        }
-                        else
-                        {
-                            obj.toParent = null; // jan 2014 LA.matIdentity(obj.toParent);
-                            obj.fromParent = null; // LA.matIdentity(obj.fromParent);
-                        }
-                        TouchTransform(obj);
-                        continue;
-                    }
-                    if ((mask&2) != 0) // Scale
-                    {
-                        obj.toParent[0][0] = obj.toParent[1][1] = obj.toParent[2][2] = 1;
-                        obj.toParent[0][1] = obj.toParent[1][0] = obj.toParent[2][0] = 0;
-                        obj.toParent[0][2] = obj.toParent[1][2] = obj.toParent[2][1] = 0;
-                        obj.fromParent[0][0] = obj.fromParent[1][1] = obj.fromParent[2][2] = 1;
-                        obj.fromParent[0][1] = obj.fromParent[1][0] = obj.fromParent[2][0] = 0;
-                        obj.fromParent[0][2] = obj.fromParent[1][2] = obj.fromParent[2][1] = 0;
-                    }
-                    if ((mask&4) != 0) // Rotation
-                    {
-                        // ?
-                    }
-                    if ((mask&1) != 0) // Translation
-                    {
-                        if (obj.toParent != null)
-                        {
-                            obj.toParent[3][0] = obj.toParent[3][1] = obj.toParent[3][2] = 0;
-                            obj.fromParent[3][0] = obj.fromParent[3][1] = obj.fromParent[3][2] = 0;
-                        }
-                    }
+                    obj.ResetTransform(mask);
+                    
                     if (obj.parent == null)
                     {
                         System.out.println("NULL PARENT!");
@@ -4049,7 +4792,7 @@
                             
                             try
                             {
-                                texturedata = Globals.theRenderer.GetTextureData(pigment, false, node.texres);
+                                texturedata = Globals.theRenderer.GetTextureData(tex, false, node.texres);
                             }
                             catch (Exception e)
                             {
@@ -4120,10 +4863,17 @@
             {
 		Object3D obj = group.selection.get(i);
                 
+                if (obj.toParent == null)
+                {
+                    obj.toParent = LA.newMatrix();
+                    obj.fromParent = LA.newMatrix();
+                }
+                
                 LA.matTranslate(obj.toParent, i * scale, 0, 0);
                 LA.matTranslateInv(obj.fromParent, -i * scale, 0, 0);
             }
             
+            Globals.lighttouched = true;
             refreshContents();
 	}
 	
@@ -4411,6 +5161,12 @@
 		refreshContents();
 	}
 	
+	void ClearVersions()
+	{
+		group.selection.ClearVersions();
+		refreshContents();
+	}
+	
 	void FlipV(boolean flip)
 	{
 		group.selection.FlipV(flip);
@@ -4526,6 +5282,12 @@
 	
 	void EditSelection(boolean newWindow)
 	{
+                if (group.selection == null)
+                {
+                     EditElement(group, newWindow); // ? new
+                     return;
+                }
+                
         //    aConstraints.gridy = 0;
 		for (int i=0; i<group.selection.size(); i++)
 		{
@@ -4536,12 +5298,7 @@
 			Object3D elem = (Object3D)group.selection.elementAt(i);
                         if(elem != group || !newWindow)
                         {
-                        //    if (!(elem instanceof Composite))
-                        //        newWindow = false;
-                            listUI.add(elem);
-                            elem.openEditWindow(this, newWindow); //, false);
-                            System.out.println("edit : " + elem);
-                            elem.editWindow.refreshContents(true); // ? new
+                            EditElement(elem, newWindow); // ? new
                         }
 		}
 	}
@@ -4604,9 +5361,7 @@
 		
 		freezemodel = false;
 	}
-	
-        boolean flashIt = true;
-        
+	        
 	public void valueChanged(TreeSelectionEvent e)
 	//public boolean handleEvent(Event event)
 	{
@@ -4616,17 +5371,7 @@
 		//new Exception().printStackTrace();
 
 		freezemodel = true;
-		
-                //for (Object3D obj : listUI)
-                for (int i=listUI.size(); --i>=0;)
-                {
-                    Object3D obj = listUI.elementAt(i);
-                    if (!obj.pinned)
-                    {
-                        obj.CloseUI();
-                        listUI.remove(i);
-                    }
-                }
+        ClearUnpinned();
                     
 		/**/
 		//switch (event.id)
@@ -4660,10 +5405,10 @@
 			if (tps != null && tps.length > 0 && tps[0].getLastPathComponent() instanceof Camera)
                             // a camera
                         {
-                            if (tps[0].getLastPathComponent() != Globals.theRenderer.LightCamera()) // Crash the camera because of invalid lightspace
+                            if (tps[0].getLastPathComponent() != Globals.theRenderer.LightCamera()) // Crashes the camera because of invalid lightspace
                             {
                                 CameraPane.camerachangeframe = 0; // don't refuse it
-                                Globals.theRenderer.SetCamera((Camera) tps[0].getLastPathComponent());
+                                Globals.theRenderer.SetCamera((Camera) tps[0].getLastPathComponent(), true);
                             }
                          //   Globals.theRenderer.renderCamera = Globals.theRenderer.manipCamera;
                          //   Globals.theRenderer.eyeCamera = Globals.theRenderer.manipCamera;
@@ -4689,12 +5434,32 @@
         {
             editButton.setEnabled(enabled);
             uneditButton.setEnabled(enabled);
+            //unselectButton.setEnabled(enabled);
+            flashSelectionButton.setEnabled(enabled);
+            
+            clearPanelButton.setEnabled(!listUI.isEmpty());
+            
+            boolean allComposites = true;
+            
+            if (group.selection != null)
+            for (Enumeration e = group.selection.elements(); e.hasMoreElements();)
+            {
+                    Object next = e.nextElement();
+                    if (!(next instanceof Composite)) // || (next instanceof GroupLeaf))
+                    {
+                        allComposites = false;
+                        break;
+                    }
+            }
+            
+            rootButton.setEnabled(true); // allComposites);
         }
 
         void refreshContents(boolean cp)
         {
-            if (objectPanel.getSelectedIndex() == objectPanel.indexOfTab("Info"))
-            if (!Globals.MOUSEDRAGGED) // && !Globals.TIMERRUNNING)
+            if (Globals.SHOWINFO)
+            //if (objectPanel.getSelectedIndex() == 2) // objectPanel.indexOfTab("Info"))
+            if (!Globals.MOUSEDRAGGED && group.selection != null) // && !Globals.TIMERRUNNING)
             {
                 objEditor.ClearInfo(); // .GetMaterial());
 
@@ -4703,7 +5468,7 @@
                     Object3D child = (Object3D) group.selection.get(i);
 
                     objEditor.AddInfo(child, this, true);
-    System.err.println("info : " + child.GetPath());
+//    System.err.println("info : " + child.GetPath());
                 }
 
                 objEditor.SetText(); // jan 2014
@@ -4793,8 +5558,8 @@
                 
 		if (cut)
 		{
-        if (Globals.SAVEONMAKE)
-                    Save();
+//        if (Globals.SAVEONMAKE) // Moved to the clipboard, no need to save.
+//                    Save();
 			//int indices[] = jList.getSelectedIndices();
 			//for (int i = indices.length - 1; i >= 0; i--)
 			//jList.remove(indices[i]);
@@ -4897,6 +5662,10 @@
 	
 	void paste(boolean expand)
 	{
+        if (Globals.REPLACEONMAKE)
+            Save();
+        boolean keep = Globals.REPLACEONMAKE;
+        Globals.REPLACEONMAKE = false;
 	//	if (GrafreeD.clipboard == null)
 	//		return;
 		boolean first = true;
@@ -4956,6 +5725,7 @@
                         Grafreed.clipboard.get(0).parent = keepparent;
 		}
 		
+        Globals.REPLACEONMAKE = keep;
 		ResetModel();
 		refreshContents();
 	}
@@ -5091,6 +5861,10 @@
         
 	void group(Object3D csg, boolean grab)
 	{
+        if (Globals.REPLACEONMAKE)
+            Save();
+        boolean keep = Globals.REPLACEONMAKE;
+        Globals.REPLACEONMAKE = false;
 		if (//false) // why??
                     !group.selection.isEmpty())
                 {
@@ -5204,10 +5978,15 @@
                 //node.add(csg);
 		//makeSomething(node);
 		makeSomething(csg);
+        Globals.REPLACEONMAKE = keep;
 	}
 	
         void Ungroup(Object3D g)
         {
+        if (Globals.REPLACEONMAKE)
+            Save();
+        boolean keep = Globals.REPLACEONMAKE;
+        Globals.REPLACEONMAKE = false;
             if (g instanceof HiddenObject)
             {
                 HiddenObject h = (HiddenObject) g;
@@ -5224,6 +6003,7 @@
                     objEditor.makeSomething(g.get(i), false);
                 }
             }
+        Globals.REPLACEONMAKE = keep;
         }
         
 	void ungroup()
@@ -5514,10 +6294,6 @@
 	
 	cButton restoreCameraButton;
         
-	cButton minButton;
-	cButton maxButton;
-	cButton fullButton;
-	cButton saveButton;
 	cButton oneStepButton;
         
         cButton groupButton;
@@ -5526,6 +6302,22 @@
         cButton switchButton;
         cButton loopButton;
         cButton textureButton;
+        
+        cButton skybox1Button;
+        cButton skybox2Button;
+        cButton skybox3Button;
+        cButton skybox4Button;
+        cButton skybox5Button;
+        cButton skybox6Button;
+        cButton skybox7Button;
+        
+        cButton skybox11Button;
+        cButton skybox12Button;
+        cButton skybox13Button;
+        cButton skybox14Button;
+        cButton skybox15Button;
+        cButton skybox16Button;
+        cButton skybox17Button;
         
         cButton gridButton;
         cButton boxButton;
@@ -5559,11 +6351,11 @@
 	private MenuItem lookAtItem;
 	private MenuItem lookFromItem;
 	private MenuItem switchViewItem;
-	private MenuItem cutItem;
+	private JMenuItem cutItem;
 	private MenuItem undoItem;
 	private MenuItem redoItem;
 	private JMenuItem duplicateItem;
-	private MenuItem cloneItem;
+	private JMenuItem cloneItem;
 	private MenuItem cloneSupportItem;
 	private MenuItem overwriteGeoItem;
 	private MenuItem overwriteMatItem;
@@ -5584,13 +6376,13 @@
 	private MenuItem cloneGeometriesItem;
 	private MenuItem shareGeometriesItem;
 	private MenuItem mergeGeometriesItem;
-	private MenuItem copyItem;
+	private JMenuItem copyItem;
 	private MenuItem pasteItem;
-	private MenuItem pasteIntoItem;
-	private MenuItem pasteLinkItem;
-	private MenuItem pasteCloneItem;
-	private MenuItem pasteExpandItem;
-	private MenuItem clearItem;
+	private JMenuItem pasteIntoItem;
+	private JMenuItem pasteLinkItem;
+	private JMenuItem pasteCloneItem;
+	private JMenuItem pasteExpandItem;
+	private JMenuItem deleteItem;
 	private MenuItem clearAllItem;
 	private MenuItem genUVItem;
 	private MenuItem genNormalsMESHItem;
@@ -5616,6 +6408,7 @@
 	private MenuItem clipMeshItem;
 	private MenuItem smoothMeshItem;
 	private MenuItem clearMaterialsItem;
+	private MenuItem clearVersionsItem;
         
 	private MenuItem liveleavesItem;
 	private MenuItem unliveleavesItem;
@@ -5639,13 +6432,16 @@
 	private MenuItem maxTexturesItem;
 	private MenuItem panoTexturesItem;
         
+	private MenuItem textureRatioRItem;
+	private MenuItem textureRatioGItem;
+	private MenuItem textureRatioBItem;
 	private MenuItem resetCentroidItem;
 	private MenuItem resetCentroidXZItem;
 	private MenuItem resetTransformItem;
 	private MenuItem transformGeometryItem;
 	private MenuItem transformChildrenItem;
 	private MenuItem hideItem;
-	private MenuItem grabItem;
+	private JMenuItem grabItem;
 	private MenuItem backItem;
 	private MenuItem frontItem;
 	private MenuItem cameraItem;
@@ -5658,7 +6454,7 @@
 	private MenuItem switchTransfoItem;
 	private MenuItem morphItem;
 	private MenuItem linkerItem;
-	private MenuItem ungroupItem;
+	private JMenuItem ungroupItem;
 	private MenuItem editItem;
 	private MenuItem openWindowItem;
 	private MenuItem editLeafItem;
@@ -5674,6 +6470,8 @@
 	private MenuItem attachBumpItem;
 	private MenuItem detachBumpItem;
 	private MenuItem pigmentBumpItem;
+	private MenuItem embedTexturesItem;
+	private MenuItem deEmbedTexturesItem;
 
 	private MenuItem particleItem;
 	private MenuItem ragdollItem;

--
Gitblit v1.6.2