001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: /*
042: * CompositorPanel.java
043: *
044: * Created on May 24, 2006, 5:28 PM
045: *
046: * To change this template, choose Tools | Template Manager
047: * and open the template in the editor.
048: */
049:
050: package org.netbeans.modules.xml.schema.abe;
051:
052: import java.awt.BorderLayout;
053: import java.awt.Component;
054: import java.awt.Dimension;
055: import java.awt.Graphics;
056: import java.awt.Graphics2D;
057: import java.awt.event.ComponentEvent;
058: import java.awt.event.ComponentListener;
059: import java.util.ArrayList;
060: import java.util.LinkedList;
061: import java.util.List;
062: import javax.swing.JPanel;
063: import javax.swing.SpringLayout;
064: import javax.swing.SwingConstants;
065: import javax.swing.SwingUtilities;
066: import org.netbeans.api.progress.ProgressHandle;
067: import org.netbeans.api.progress.ProgressHandleFactory;
068: import org.netbeans.modules.xml.axi.AXIComponent;
069: import org.netbeans.modules.xml.axi.AXIDocument;
070: import org.netbeans.modules.xml.axi.AnyAttribute;
071: import org.netbeans.modules.xml.axi.AnyElement;
072: import org.netbeans.modules.xml.axi.Attribute;
073: import org.netbeans.modules.xml.axi.Compositor;
074: import org.netbeans.modules.xml.axi.ContentModel;
075: import org.netbeans.modules.xml.axi.Element;
076: import org.netbeans.modules.xml.axi.datatype.Datatype;
077: import org.netbeans.modules.xml.axi.visitor.AXIVisitor;
078: import org.netbeans.modules.xml.schema.abe.palette.DnDHelper;
079: import org.openide.util.NbBundle;
080: import org.openide.util.RequestProcessor;
081:
082: /**
083: *
084: * @author girix
085: */
086: public abstract class ContainerPanel extends AnnotatedBorderPanel
087: implements AXIVisitor {
088: private static final long serialVersionUID = 7526472295622776147L;
089: private AXIComponent axiParent;
090: Component parentPanel;
091: private static final int INTER_PANEL_VERTICAL_SPACE = 0;
092: private int panelIndendation = InstanceDesignConstants.TAG_INDENT;
093: private LinkedList<Component> childrenList = new LinkedList<Component>();
094: protected Component visitorResult = null;
095: boolean openByDefault = true;
096:
097: public ContainerPanel(InstanceUIContext context,
098: AXIComponent axiParent, Component parentPanel,
099: boolean openByDefault) {
100: super (context);
101: this .setAXIParent(axiParent);
102: this .parentPanel = parentPanel;
103: this .openByDefault = openByDefault;
104: initialize();
105: }
106:
107: protected abstract void setupAXIComponentListener();
108:
109: public abstract List<? extends AXIComponent> getAXIChildren();
110:
111: private void initialize() {
112: setOpaque(false);
113: setLayout(new BorderLayout());
114: addHeaderPanel();
115: initChildrenPanel();
116: if (openByDefault)
117: addAllChildren();
118: setupAXIComponentListener();
119: }
120:
121: public void addHeaderPanel() {
122: }
123:
124: JPanel childrenPanel;
125: SpringLayout childrenPanelLayout;
126: Component childrenPanelLastComponent;
127:
128: public void initChildrenPanel() {
129: //create a child panel that could expand and collapse
130: childrenPanelLayout = new SpringLayout();
131: childrenPanel = new JPanel(childrenPanelLayout);
132: childrenPanel.setOpaque(false);
133:
134: add(childrenPanel, BorderLayout.CENTER);
135:
136: //add always a tweener panel to save drop logic in the compositorTypePanel
137: TweenerPanel tweener = new TweenerPanel(
138: SwingConstants.HORIZONTAL, context);
139: //appendChild(tweener);
140: childrenPanel.add(tweener);
141: getChildrenList().add(tweener);
142: addTweenerListener(tweener);
143:
144: //always compensate the expand button of the compositor panel
145: childrenPanelLayout.putConstraint(SpringLayout.WEST, tweener,
146: getChildrenIndent(), SpringLayout.WEST, childrenPanel);
147: childrenPanelLayout.putConstraint(SpringLayout.NORTH, tweener,
148: getInterComponentVerticalSpacing(), SpringLayout.NORTH,
149: childrenPanel);
150: adjustChildrenPanelSize();
151: addComponentEventListener(tweener);
152: childrenPanelLastComponent = tweener;
153:
154: }
155:
156: public void verifyChildrenWithModel() {
157: List<Component> noTweenerList = new ArrayList<Component>();
158: for (Component comp : getChildrenList()) {
159: if (comp instanceof TweenerPanel)
160: continue;
161: noTweenerList.add(comp);
162: }
163:
164: if (noTweenerList.size() != getAXIChildren().size()) {
165: //this will solve the count problem
166: removeAndAddAllChildren();
167: return;
168: }
169:
170: for (AXIComponent axiComponent : getAXIChildren()) {
171: //ensure that all the children are in order and
172: if (isAlreadyAdded(axiComponent) == null) {
173: removeAndAddAllChildren();
174: return;
175: }
176: }
177: }
178:
179: public void removeAndAddAllChildren() {
180: this .remove(childrenPanel);
181: childrenPanel = null;
182: initChildrenPanel();
183: //childrenAdded = false;
184: addAllChildren();
185: }
186:
187: boolean childrenAdded = false;
188:
189: public void addAllChildren() {
190: if (childrenAdded)
191: return;
192: childrenAdded = true;
193: final List<? extends AXIComponent> children = getAXIChildren();
194: if (children.size() < InstanceDesignerPanel.EXPAND_BY_DEFAULT_LIMIT) {
195: addAllChildren(children);
196: } else {
197: RequestProcessor.getDefault().post(new Runnable() {
198: public void run() {
199: addAllChildren(children);
200: }
201: });
202: }
203: }
204:
205: private void addAllChildren(List<? extends AXIComponent> children) {
206: initProgress(children);
207: for (final AXIComponent axiComp : children) {
208: showProgress();
209: visitorResult = null;
210: axiComp.accept(ContainerPanel.this );
211: final Component compToAdd = visitorResult;
212: if (visitorResult != null) {
213: appendChild(compToAdd, false);
214: TweenerPanel tweener = new TweenerPanel(
215: SwingConstants.HORIZONTAL, context);
216: addTweenerListener(tweener);
217: appendChild(tweener, false);
218: }
219: }
220: adjustChildrenPanelSize();
221: revalidate();
222: finishProgress();
223: }
224:
225: ProgressHandle progressHandle = null;
226: int stepCount = 0;
227:
228: private void initProgress(List<? extends AXIComponent> children) {
229: final int size = children.size();
230: if (size < InstanceDesignerPanel.EXPAND_BY_DEFAULT_LIMIT) {
231: return;
232: }
233: UIUtilities.showBulbMessage(NbBundle.getMessage(
234: ContainerPanel.class, "MSG_RENDERING_CHILDREN")
235: + "...", this .context);
236: progressHandle = ProgressHandleFactory.createHandle(NbBundle
237: .getMessage(ContainerPanel.class,
238: "MSG_RENDERING_CHILDREN")
239: + ": ");
240: progressHandle.setInitialDelay(1);
241: progressHandle.start(size + 10);
242: UIUtilities.setBusyCursor(this .context);
243: stepCount = 0;
244: }
245:
246: private void showProgress() {
247: if (progressHandle != null) {
248: progressHandle.progress(stepCount++);
249: }
250: }
251:
252: private void finishProgress() {
253: if (progressHandle != null) {
254: progressHandle.finish();
255: progressHandle = null;
256: UIUtilities.setDefaultCursor(this .context);
257: UIUtilities.hideGlassMessage();
258: stepCount = 0;
259: }
260: }
261:
262: public int getChildrenIndent() {
263: return getPanelIndendation()
264: + InstanceDesignConstants.COMPOSITOR_CHILDREN_INDENT;//+ExpandCollapseButton.WIDTH;
265: }
266:
267: public void appendChild(Component component, boolean resize) {
268: appendChild(component, 0, resize);
269: }
270:
271: public void appendChild(Component component, int vPadding,
272: boolean resize) {
273: childrenPanel.add(component);
274: getChildrenList().add(component);
275: //always compensate the expand button of the compositor panel
276: childrenPanelLayout.putConstraint(SpringLayout.WEST, component,
277: getChildrenIndent(), SpringLayout.WEST, childrenPanel);
278: childrenPanelLayout.putConstraint(SpringLayout.NORTH,
279: component, getInterComponentVerticalSpacing()
280: + vPadding, SpringLayout.SOUTH,
281: childrenPanelLastComponent);
282:
283: childrenPanelLastComponent = component;
284: if (resize) {
285: adjustChildrenPanelSize();
286: revalidate();
287: }
288: addComponentEventListener(component);
289: }
290:
291: public boolean addChildAt(Component component, int index) {
292: childrenPanel.add(component);
293: Component cAbove = null;
294: Component cBelow = null;
295: try {
296: cAbove = getAboveAdjacentComponent(index);
297: cBelow = getBelowAdjacentComponent(index);
298: } catch (Exception e) {
299: removeAndAddAllChildren();
300: return false;
301: }
302: if (cAbove == null) {
303: //this usecase is invalid
304: return false;
305: }
306: //standard left spring
307: childrenPanelLayout.putConstraint(SpringLayout.WEST, component,
308: getChildrenIndent(), SpringLayout.WEST, childrenPanel);
309:
310: childrenPanelLayout.putConstraint(SpringLayout.NORTH,
311: component, getInterComponentVerticalSpacing(),
312: SpringLayout.SOUTH, cAbove);
313: //add a tweener panel...always
314: TweenerPanel tweener = new TweenerPanel(
315: SwingConstants.HORIZONTAL, context);
316: addTweenerListener(tweener);
317: childrenPanel.add(tweener);
318: childrenPanelLayout.putConstraint(SpringLayout.WEST, tweener,
319: getChildrenIndent(), SpringLayout.WEST, childrenPanel);
320:
321: childrenPanelLayout.putConstraint(SpringLayout.NORTH, tweener,
322: getInterComponentVerticalSpacing(), SpringLayout.SOUTH,
323: component);
324:
325: if (cBelow != null) {
326: //adjust the spring between tweener and below
327: childrenPanelLayout.putConstraint(SpringLayout.NORTH,
328: cBelow, getInterComponentVerticalSpacing(),
329: SpringLayout.SOUTH, tweener);
330: }
331:
332: //int cAboveIndex = getChildrenList().indexOf(cAbove);
333: if (getChildrenList().getLast() == cAbove) {
334: //already last element so just append
335: getChildrenList().add(component);
336: getChildrenList().add(tweener);
337: } else {
338: getChildrenList().add(
339: getChildrenList().indexOf(cAbove) + 1, component);
340: getChildrenList().add(
341: getChildrenList().indexOf(component) + 1, tweener);
342: }
343:
344: adjustChildrenPanelSize();
345: revalidate();
346: //repaint();
347: addComponentEventListener(component);
348: addComponentEventListener(tweener);
349: return true;
350: }
351:
352: public void removeComponent(Component component) {
353: int cIndex = getChildrenList().indexOf(component);
354: //tweener panel index
355: int belowTIndex = cIndex + 1;
356: int aboveTIndex = cIndex - 1;
357: Component belowTweener = getChildrenList().get(belowTIndex);
358: Component aboveTweener = getChildrenList().get(aboveTIndex);
359: Component belowComponent = null;
360: if (getChildrenList().getLast() != belowTweener) {
361: belowComponent = getChildrenList().get(belowTIndex + 1);
362: }
363:
364: childrenPanelLayout.removeLayoutComponent(component);
365: childrenPanelLayout.removeLayoutComponent(belowTweener);
366: childrenPanel.remove(component);
367: childrenPanel.remove(belowTweener);
368: fireComponentRemoved();
369:
370: if (belowComponent != null) {
371: childrenPanelLayout.putConstraint(SpringLayout.NORTH,
372: belowComponent, getInterComponentVerticalSpacing(),
373: SpringLayout.SOUTH, aboveTweener);
374: }
375:
376: getChildrenList().remove(component);
377: getChildrenList().remove(belowTweener);
378:
379: adjustChildrenPanelSize();
380: revalidate();
381: repaint();
382:
383: }
384:
385: private void addComponentEventListener(Component comp) {
386: comp.addComponentListener(new ComponentListener() {
387: public void componentHidden(ComponentEvent e) {
388: adjustChildrenPanelSize();
389: }
390:
391: public void componentMoved(ComponentEvent e) {
392: }
393:
394: int callCount = 1;
395:
396: public void componentResized(ComponentEvent e) {
397: if ((callCount <= 2)
398: && ((ContainerPanel.this instanceof GlobalComplextypeContainerPanel) || (ContainerPanel.this instanceof GlobalElementsContainerPanel))) {
399: //skip this event for the first time.
400: //If not done then there is a 10 sec delay in the UI after expanding
401: //many nodes in a huge schema.
402: callCount++;
403: return;
404: }
405: adjustChildrenPanelSize();
406: }
407:
408: public void componentShown(ComponentEvent e) {
409: adjustChildrenPanelSize();
410: }
411: });
412: }
413:
414: public Component getAboveAdjacentComponent(int index) {
415: return getChildrenList().get(index * 2);
416: }
417:
418: public Component getBelowAdjacentComponent(int index) {
419: int ind = (index * 2) + 1;
420: if (ind >= getChildrenList().size())
421: return null;
422: return getChildrenList().get(ind);
423: }
424:
425: public void adjustChildrenPanelSize() {
426: SwingUtilities.invokeLater(new Runnable() {
427: public void run() {
428: doAdjustChildrenPanelSize();
429: }
430: });
431: }
432:
433: private void doAdjustChildrenPanelSize() {
434: int width = 0;
435: int height = 0;
436: int indent = getChildrenIndent();
437: int spacing = getInterComponentVerticalSpacing();
438: for (Component child : childrenPanel.getComponents()) {
439: if (!child.isVisible())
440: break;
441: Dimension dim = child.getPreferredSize();
442: int curWidth = dim.width + indent;//+50;
443: if (curWidth > width)
444: width = curWidth;
445: height += dim.height + spacing;
446: }
447: //add some fudge
448: width += 20;
449: Dimension dim = new Dimension(width, height + 3);
450:
451: Dimension old = childrenPanel.getPreferredSize();
452: boolean revalidateChild = false;
453: if ((old.height != dim.height) || (old.width != dim.width)) {
454: childrenPanel.setPreferredSize(dim);
455: childrenPanel.setMinimumSize(dim);
456: revalidateChild = true;
457: }
458:
459: dim = _getMinimumSize();
460: old = getPreferredSize();
461: boolean revalidateMe = false;
462: if ((old.height != dim.height) || (old.width != dim.width)) {
463: setMinimumSize(dim);
464: setPreferredSize(dim);
465: revalidateMe = true;
466: }
467: if (revalidateChild)
468: childrenPanel.revalidate();
469: if (revalidateMe)
470: revalidate();
471: }
472:
473: //Following set of methods needed for the tag size calculation and horizontal bar display logic
474: public Dimension getPreferredSize() {
475: return getMinimumSize();
476: }
477:
478: public Dimension _getMinimumSize() {
479: int width = 0;
480: int height = 0;
481: synchronized (this ) {
482: for (Component child : this .getComponents()) {
483: if (!child.isVisible())
484: break;
485: Dimension dim = child.getPreferredSize();
486: height += dim.getHeight();
487: int this W = dim.width;
488: width = width < this W ? this W : width;
489: }
490: }
491: return new Dimension(width, height);
492: }
493:
494: public ABEBaseDropPanel isAlreadyAdded(AXIComponent axiComp) {
495: for (Component comp : getChildrenList()) {
496: //check if this component is already added to the list. if yes return
497: if (comp instanceof ABEBaseDropPanel) {
498: if (((ABEBaseDropPanel) comp).getAXIComponent() == axiComp)
499: return (ABEBaseDropPanel) comp;
500: }
501: }
502: return null;
503: }
504:
505: public boolean tweenerDragAccept(TweenerPanel tweener,
506: DnDHelper.PaletteItem paletteItem) {
507: return true;
508: }
509:
510: public void tweenerDrop(TweenerPanel tweener,
511: DnDHelper.PaletteItem paletteItem) {
512: }
513:
514: public void tweenerDragExited(TweenerPanel tweener) {
515: }
516:
517: public void tweenerDragEntered(TweenerPanel tweener,
518: DnDHelper.PaletteItem paletteItem) {
519: }
520:
521: protected void addTweenerListener(final TweenerPanel tweener) {
522: tweener.addTweenerListener(new TweenerListener() {
523: public boolean dragAccept(DnDHelper.PaletteItem paletteItem) {
524: return ContainerPanel.this .tweenerDragAccept(tweener,
525: paletteItem);
526: }
527:
528: public void drop(DnDHelper.PaletteItem paletteItem) {
529: ContainerPanel.this .tweenerDrop(tweener, paletteItem);
530: }
531:
532: public void dragExited() {
533: ContainerPanel.this .tweenerDragExited(tweener);
534: }
535:
536: public void dragEntered(DnDHelper.PaletteItem paletteItem) {
537: ContainerPanel.this .tweenerDragEntered(tweener,
538: paletteItem);
539: }
540: });
541: }
542:
543: public int getInterComponentVerticalSpacing() {
544: return INTER_PANEL_VERTICAL_SPACE;
545: }
546:
547: public void paintComponent(Graphics g) {
548: Graphics2D g2d = (Graphics2D) g;
549: super .paintComponent(g2d);
550: }
551:
552: public AXIComponent getAXIParent() {
553: return axiParent;
554: }
555:
556: public void setAXIParent(AXIComponent axiParent) {
557: this .axiParent = axiParent;
558: }
559:
560: public int getPanelIndendation() {
561: return panelIndendation;
562: }
563:
564: public void setPanelIndendation(int panelIndendation) {
565: this .panelIndendation = panelIndendation;
566: }
567:
568: public LinkedList<Component> getChildrenList() {
569: return childrenList;
570: }
571:
572: public void setChildrenList(LinkedList<Component> childrenList) {
573: this .childrenList = childrenList;
574: }
575:
576: public void visit(AnyAttribute attribute) {
577: }
578:
579: public void visit(Element element) {
580: }
581:
582: public void visit(ContentModel element) {
583: }
584:
585: public void visit(Datatype datatype) {
586: }
587:
588: public void visit(AnyElement element) {
589: }
590:
591: public void visit(AXIDocument root) {
592: }
593:
594: public void visit(Attribute attribute) {
595: }
596:
597: public void visit(Compositor compositor) {
598: }
599:
600: public AXIComponent getAXIComponent() {
601: return getAXIParent();
602: }
603:
604: public ABEBaseDropPanel getChildUIComponentFor(
605: AXIComponent axiComponent) {
606: this .addAllChildren();
607: this .setVisible(true);
608: for (Component comp : getChildrenList()) {
609: if (comp instanceof ABEBaseDropPanel) {
610: ABEBaseDropPanel uiComp = ((ABEBaseDropPanel) comp)
611: .getUIComponentFor(axiComponent);
612: if (uiComp != null)
613: return uiComp;
614: }
615: }
616: return null;
617: }
618:
619: public ABEBaseDropPanel getParentContainerPanel() {
620: return (ABEBaseDropPanel) this.parentPanel;
621: }
622: }
|