public Renderable generateShortcut() {
   RenderableComplex shortcut = new RenderableComplex();
   shortcut.hideComponents = hideComponents;
   generateShortcut(shortcut);
   if (textBounds != null) {
     shortcut.setTextPosition(textBounds.x, textBounds.y);
   }
   return shortcut;
 }
 /** Search for a suitable container in this complex for a Node. */
 public RenderableComplex pickUpContainer(Node node) {
   if (componentsInHiearchy != null) {
     for (Renderable r : componentsInHiearchy) {
       if (!(r instanceof RenderableComplex)) continue;
       RenderableComplex complex = (RenderableComplex) r;
       if (complex.isAssignable(node)) return complex;
     }
   }
   return isAssignable(node) ? this : null;
 }
 @Override
 public void hideComponents(boolean hide) {
   this.hideComponents = hide;
   if (componentsInHiearchy == null) return;
   for (Renderable r : componentsInHiearchy) {
     Node node = (Node) r;
     node.setIsVisible(!hide);
     if (r instanceof RenderableComplex) {
       ((RenderableComplex) r).hideComponents = hide;
     }
   }
   // The following statements are related to bounds.
   if (bounds == null) return;
   if (hide) {
     saveOldBounds();
     copyBoundsToComponents();
   } else {
     recoverOldBounds();
     // Make sure a complex container has enough size
     Renderable container = this.container;
     Rectangle childBounds = this.bounds;
     while (container instanceof RenderableComplex) {
       Rectangle cBounds = container.getBounds();
       if (!cBounds.contains(childBounds)) {
         ((RenderableComplex) container).layout();
       }
       childBounds = container.getBounds();
       container = container.getContainer();
     }
   }
 }
 private boolean isOldBoundsRecoverable() {
   if (oldIdToBounds == null || oldIdToBounds.size() == 0) return false;
   // Make sure all nodes have been registered
   if (componentsInHiearchy == null || componentsInHiearchy.size() == 0) return false;
   for (Renderable r : componentsInHiearchy) {
     if (!oldIdToBounds.containsKey(r.getID())) return false;
   }
   // Check if a complex component has the same size as its container.
   // This may occur when a hidecomponent complex forms a complex with
   // another Node.
   for (Renderable r : componentsInHiearchy) {
     if (!(r instanceof RenderableComplex)) continue;
     RenderableComplex complex = (RenderableComplex) r;
     Rectangle complexBounds = oldIdToBounds.get(complex.getID());
     List<Renderable> list = RenderUtility.getComponentsInHierarchy(complex);
     for (Renderable tmp : list) {
       Rectangle tmpBounds = oldIdToBounds.get(tmp.getID());
       if (tmpBounds.width == complexBounds.width && tmpBounds.height == complexBounds.height)
         return false;
     }
   }
   return true;
 }
 private void recoverOldBounds() {
   // Make sure it is recoverable. If not, just do a simple
   // layout
   if (!isOldBoundsRecoverable()) {
     // Copy any known bounds
     if (oldIdToBounds != null) {
       for (Renderable r : componentsInHiearchy) {
         Rectangle bounds = oldIdToBounds.get(r.getID());
         if (bounds == null) continue;
         if (r.bounds != null) {
           r.bounds.width = bounds.width;
           r.bounds.height = bounds.height;
         } else r.bounds = new Rectangle(bounds);
       }
     }
     // Just do an auto layout. Should start from the smalled complexes
     for (Renderable r : componentsInHiearchy) {
       if (r instanceof RenderableComplex) ((RenderableComplex) r).layout();
     }
     layout();
     return;
   }
   Rectangle oldBounds = oldIdToBounds.get(getID());
   int dx = bounds.x - oldBounds.x;
   int dy = bounds.y - oldBounds.y;
   bounds.width = oldBounds.width;
   bounds.height = oldBounds.height;
   invalidateTextBounds();
   for (Renderable r : componentsInHiearchy) {
     oldBounds = oldIdToBounds.get(r.getID());
     oldBounds.translate(dx, dy);
     Rectangle newBounds = r.getBounds();
     if (newBounds == null) {
       newBounds = new Rectangle(oldBounds);
       r.setBounds(newBounds);
     } else {
       newBounds.x = oldBounds.x;
       newBounds.y = oldBounds.y;
       newBounds.width = oldBounds.width;
       newBounds.height = oldBounds.height;
     }
     ((Node) r).invalidateTextBounds();
   }
 }