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: package org.netbeans.modules.xml.xam.ui.layout;
043:
044: import java.awt.Component;
045: import java.awt.Container;
046: import java.awt.Cursor;
047: import java.awt.Dimension;
048: import java.awt.Frame;
049: import java.awt.Graphics;
050: import java.awt.Insets;
051: import java.awt.Point;
052: import java.awt.Rectangle;
053: import java.awt.Window;
054: import java.awt.event.MouseEvent;
055: import javax.swing.JPanel;
056: import javax.swing.JScrollPane;
057: import javax.swing.JViewport;
058: import javax.swing.SwingUtilities;
059:
060: /**
061: * A class that implements a splitter bar. It is only intended to be used
062: * in a SplitterLayout. Because it is dervied from Panel, a JSplitterBar
063: * can do anything a normal panel can do. However, if you add any
064: * components to this panel, the scroll handle will not be accessible. In a
065: * case like this, you need to explicitly add a JSplitterSpace component to the
066: * JSplitterBar, guaranteeing a place for the handle to be available.
067: * <p/>
068: * <p>Use this code at your own risk! MageLang Institute is not
069: * responsible for any damage caused directly or indirctly through
070: * use of this code.
071: * <p><p>
072: * <b>SOFTWARE RIGHTS</b>
073: * <p/>
074: * MageLang support classes, version 1.0, MageLang Institute
075: * <p/>
076: * We reserve no legal rights to this code--it is fully in the
077: * public domain. An individual or company may do whatever
078: * they wish with source code distributed with it, including
079: * including the incorporation of it into commerical software.
080: * <p/>
081: * <p>However, this code cannot be sold as a standalone product.
082: * <p/>
083: * We encourage users to develop software with this code. However,
084: * we do ask that credit is given to us for developing it
085: * By "credit", we mean that if you use these components or
086: * incorporate any source code into one of your programs
087: * (commercial product, research project, or otherwise) that
088: * you acknowledge this fact somewhere in the documentation,
089: * research report, etc... If you like these components and have
090: * developed a nice tool with the output, please mention that
091: * you developed it using these components. In addition, we ask that
092: * the headers remain intact in our source code. As long as these
093: * guidelines are kept, we expect to continue enhancing this
094: * system and expect to make other tools available as they are
095: * completed.
096: * <p/>
097: * The MageLang Support Classes Gang:
098: *
099: * @author <a href="http:www.scruz.net/~thetick">Scott Stanchfield</a>, <a href=http://www.MageLang.com>MageLang Institute</a>
100: * @version MageLang Support Classes 1.0, MageLang Insitute, 1997
101: * @see SplitterLayout
102: * @see JSplitterSpace
103: *
104: * @author Santhosh Kumar - santhosh@in.fiorano.com
105: *
106: * @author Jeri Lockhart - jeri.lockhart@sun.com
107: * Modified for use in the NbColumnView widget. Only Horizontal layout is supported.
108: * When the user moves the splitter bar to the left, the column that is adjacent to the
109: * left maintains its minimum size.
110: * When the user moves the splitter bar to the right, the columns to the right of the
111: * splitter bar, maintain their widths.
112: *
113: * mouseDrag() - creates a visible bar to show where the splitter can be dragged.
114: * The limit of leftward dragging is the boundary of the min size of the column to the left
115: * of the splitter.
116: * The limit of rightward dragging is the parent container's right boundary.
117: *
118: * mouseRelease() - sets the bounds of the actual splitter. Calls
119: * checkOtherComponents().
120: *
121: * checkOtherComponenets() - For leftward drag: Make the column to the left of
122: * this splitter more narrow. Then, set the bounds of the columns and splitters
123: * to the right of this splitter so that they are relocated by the correct
124: * offset to the left, maintaining their respective widths.
125: *
126: * For rightward drag: Make the column to the left of this splitter wider.
127: * Then, set the bounds of the columns and splitters to the right of this splitter
128: * so that they are relocated by the correct offset towards the right,
129: * maintaining their respective widths.
130: *
131: */
132: public class JSplitterBar extends JPanel {
133:
134: static final long serialVersionUID = 1L;
135: static final Cursor VERT_CURSOR = new Cursor(Cursor.N_RESIZE_CURSOR);
136: static final Cursor HORIZ_CURSOR = new Cursor(
137: Cursor.E_RESIZE_CURSOR);
138: static final Cursor DEF_CURSOR = new Cursor(Cursor.DEFAULT_CURSOR);
139: private int orientation = SplitterLayout.HORIZONTAL;
140:
141: private boolean alreadyDrawn = false;
142: private Rectangle originalBounds = null;
143: private Window wBar;
144: private boolean mouseInside = false;
145:
146: /**
147: * Creates a new SpliiterBar
148: */
149: public JSplitterBar() {
150: addMouseMotionListener(new SplitterBarMouseMotionListener(this ));
151: addMouseListener(new SplitterBarMouseListener(this ));
152: }
153:
154: private void checkOtherComponents() {
155: //System.out.println("checkOtherComponents start");
156: Rectangle currBounds = getBounds(); // get current position
157: Component comps[] = getParent().getComponents();
158:
159: // orientation == HORIZONTAL
160: if (currBounds.x < originalBounds.x) { // moved left
161: //System.out.println("checkOtherComponents moved left");
162: int offset = originalBounds.x - currBounds.x;
163: //System.out.println("checkOtherComnponents offset" + offset);
164: for (int i = 0; i < comps.length; i++) {
165: if (comps[i] == this ) {
166: // make left column more narrow
167: assert i - 1 > -1 : "There should be a column to the left of this splitter";
168: if (i - 1 > -1) {
169: Rectangle oldB = comps[i - 1].getBounds();
170: //System.out.println("checkOtherComponents left col orig bounds " + oldB);
171: comps[i - 1].setBounds(oldB.x, oldB.y,
172: oldB.width - offset, oldB.height);
173: //System.out.println("checkOtherComponents make left comp narrower" + (i-1));
174: }
175: // move left all columns and splitters to the right of this splitter
176: // set all visible
177: for (int k = i + 1; k < comps.length; k++) {
178: Rectangle old = comps[k].getBounds();
179: //System.out.println("checkOtherComponents " + k + " orig bounds " + old);
180: comps[k].setBounds(old.x - offset, old.y,
181: old.width, old.height);
182: //System.out.println("checkOtherComponents move right comps to the left, set bounds "+ comps[k] + " " + comps[k].getBounds());
183: comps[k].setVisible(true);
184:
185: }
186: }
187: }
188: } // HORIZONTAL -- end moved left
189: else if (currBounds.x > originalBounds.x) { // moved right
190: //System.out.println("checkOtherComponents moved right");
191: int offset = currBounds.x - originalBounds.x;
192: //System.out.println("checkOtherComnponents offset" + offset);
193: // widen the column to the left of this splitter
194: Component left = null;
195: int this Idx = -1;
196: for (int i = 0; i < comps.length; i++) {
197: if (comps[i] == this ) {
198: this Idx = i;
199: assert i > 0 : "There should be a column to the left of this splitter.";
200: if (i - 1 > -1) {
201: left = comps[i - 1];
202: Rectangle old = left.getBounds();
203: left.setBounds(old.x, old.y,
204: old.width + offset, old.height);
205: //System.out.println("checkOtherComponents set bounds "+ (i-1) + " " + left.getBounds());
206: }
207: }
208: }
209:
210: // relocate the columns and splitters to the right of this splitter
211: if (this Idx + 1 < comps.length) {
212: for (int j = this Idx + 1; j < comps.length; j++) {
213: Rectangle old = comps[j].getBounds();
214: comps[j].setBounds(old.x + offset, old.y,
215: old.width, old.height);
216: //System.out.println("checkOtherComponents set bounds "+ j + " " + comps[j].getBounds());
217: }
218: }
219: } // HORIZONTAL -- moved right
220: } // checkComponents()
221:
222: void mouseDrag(MouseEvent e) {
223: if (SplitterLayout.dragee == null) {
224: SplitterLayout.dragee = this ;
225: } else if (SplitterLayout.dragee != this ) {
226: return;
227: }
228: // The NbColumnView mainParentPanel is in a scrollpane
229: Container cp = getParent(); // this is the NbColumnView mainParentPanel
230: Container viewport = cp.getParent();
231: Rectangle viewRect = null;
232: Rectangle columnViewRect = null;
233: Component columnView = null;
234: // int vBarWidth = 0;
235: if (viewport instanceof JViewport) {
236: JScrollPane scrollPane = (JScrollPane) ((JViewport) viewport)
237: .getParent();
238: // vBarWidth = scrollPane.getVerticalScrollBar().getPreferredSize().width;
239: // System.out.println("vBar width " + vBarWidth);
240: viewRect = ((JViewport) viewport).getViewRect();
241: ////System.out.println("mouseDrag: viewRect " + viewRect); //
242: columnView = ((JViewport) viewport).getParent().getParent();
243: if (columnView != null) {
244: columnViewRect = columnView.getBounds();
245: ////System.out.println("mouseDrag: columnViewRect " +columnViewRect );
246: } else {
247: // We would get NPE if we continued.
248: return;
249: }
250: } else {
251: // We would get NPE if we continued.
252: return;
253: }
254: Component c = getParent();
255: // Point fl = c.getLocationOnScreen(); // location of this bar on screen
256: while (c.getParent() != null) {
257: c = c.getParent();
258: }
259: ////System.out.println("mouseDrag parent bounds " + c.getBounds()); // Main IDE window org.netbeans.core.windows.view.ui.MainWindow
260: if (!alreadyDrawn && c instanceof Frame) {
261: originalBounds = getBounds();
262: wBar = new Window((Frame) c);
263: wBar.setBackground(getBackground().darker());
264: }
265: ////System.out.println("mouseDrag mainParentPanel cp " + cp);
266: // Dimension parentDim = cp.getSize();
267: Rectangle parentRect = cp.getBounds(); // this could be bigger than the scrollpane
268: ////System.out.println("mouseDrag: cp parentRect " + parentRect);
269: Point l = getLocationOnScreen();
270: Insets insets = cp.getInsets();
271: ////System.out.println("mouseDrag insets " + insets);
272: parentRect.height -= insets.top + insets.bottom;
273: // Rectangle r = getBounds(); // mouse event is relative to this...
274: ////System.out.println("mouseDrag l " + l);
275: ////System.out.println("mouseDrag e.getX " + e.getX());
276: int x = l.x + e.getX();
277: ////System.out.println("mouseDrag x " + x);
278: // int y = l.y;
279: Point viewRectLocation = viewRect.getLocation();
280: ////System.out.println("mouseDrag: viewRect location " + viewRectLocation);
281: SwingUtilities.convertPointToScreen(viewRectLocation, viewport);
282: ////System.out.println("mouseDrag: viewRect screen location " + viewRectLocation);
283: Point columnViewRectLocation = columnViewRect.getLocation();
284: ////System.out.println("mouseDrag: columnViewRect location " + columnViewRectLocation);
285: SwingUtilities.convertPointToScreen(columnViewRectLocation,
286: columnView);
287: ////System.out.println("mouseDrag: columnViewRect screen location " + columnViewRectLocation);
288: int y = viewRectLocation.y;
289: // right boundary is the right location of the
290: // viewport or the column view minus the width of the vertical scrollbar
291: //
292: // System.out.println("JSplitterBar width "+ this.getWidth());
293: int rightBoundaryScreenPos = columnViewRectLocation.x
294: + columnViewRect.width - this .getWidth();
295: ////System.out.println("mouseDrag view width " + viewRect.width);
296: ////System.out.println("mouseDrag columView width "+ columnViewRect.width);
297: ////System.out.println("mouseDrag: rightBoundaryScreenPos "+ rightBoundaryScreenPos);
298:
299: // don't let x be less than the min size boundary position of the left-adjacent column
300: // there should always be a column to the left of this bar
301: int leftBoundaryScreenPos = 0;
302: Component left = null;
303: Component[] comps = getParent().getComponents();
304: for (Component comp : comps) {
305: if (comp == this ) {
306: Dimension min = left.getMinimumSize();
307: Point p = left.getLocationOnScreen();
308: // use the larger of the viewport left position or the neighbor's minimum size boundary
309: // (the neighbor may be scrolled to the left and partially hidden)
310: ////System.out.println("mouseDrag: left neighbor's boundary " + (p.x+ min.width));
311: ////System.out.println("mouseDrag: columnView left " + columnViewRectLocation.x);
312: leftBoundaryScreenPos = Math.max(p.x + min.width,
313: columnViewRectLocation.x);
314: break;
315: }
316: left = comp;
317: }
318: ////System.out.println("mouseDrag leftBoundaryScreenPos " + leftBoundaryScreenPos);
319: if (x < leftBoundaryScreenPos) {
320: x = leftBoundaryScreenPos;
321: ////System.out.println("mouseDrag x=leftBoundaryScreenPos");
322: } else if (x > rightBoundaryScreenPos) {
323: x = rightBoundaryScreenPos;
324: ////System.out.println("mouseDrag x=rightBoundaryScreenPos");
325: }
326: wBar.setBounds(x, y, 3, columnViewRect.height);
327: //System.out.println("mouseDrag wBar bounds " + wBar.getBounds());
328: if (!alreadyDrawn) {
329: wBar.setVisible(true);
330: alreadyDrawn = true;
331: }
332: }
333:
334: void mouseEnter(MouseEvent e) {
335: if (SplitterLayout.dragee != null)
336: return;
337: setCursor(HORIZ_CURSOR);
338: mouseInside = true;
339: invalidate();
340: validate();
341: repaint();
342: }
343:
344: void mouseExit(MouseEvent e) {
345: if (SplitterLayout.dragee != null)
346: return;
347: setCursor(DEF_CURSOR);
348: mouseInside = false;
349: invalidate();
350: validate();
351: repaint();
352: }
353:
354: void mouseRelease(MouseEvent e) {
355: if (alreadyDrawn) {
356: if (SplitterLayout.dragee != this )
357: return;
358: SplitterLayout.dragee = null;
359: Point p = wBar.getLocationOnScreen();
360: SwingUtilities.convertPointFromScreen(p, getParent());
361: wBar.setVisible(false);
362: wBar.dispose();
363: wBar = null;
364: alreadyDrawn = false;
365: Rectangle r = getBounds(); // mouse event is relative to this...
366: r.x += e.getX();
367: // //System.out.println("mouseReleased converted point "+ p);
368: // //System.out.println("mouseRelease bounds of this spliltter " + r);
369: setLocation(p.x, r.y);
370: setCursor(DEF_CURSOR);
371:
372: // check to see if we need to move other splitters and hide other
373: // components that are controlled by the layout
374: // First -- find what component this one is
375:
376: checkOtherComponents();
377: mouseInside = false;
378: invalidate();
379: getParent().validate();
380: SplitterLayout.dragee = null;
381: }
382: }
383:
384: /**
385: * Paints the image of a JSplitterBar. If nothing was added to
386: * the JSplitterBar, this image will only be a thin, 3D raised line that
387: * will act like a handle for moving the JSplitterBar.
388: * If other components were added the JSplitterBar, the thin 3D raised
389: * line will onlty appear where JSplitterSpace components were added.
390: */
391: public void paint(Graphics g) {
392: super .paint(g);
393: g.setColor(getBackground());
394: // if(mouseInside)
395: // g.setColor(Color.yellow);
396: // else
397: // g.setColor(Colors.lightSkyBlue3);
398: Component c[] = getComponents();
399: if (c != null && c.length > 0)
400: for (int i = 0; i < c.length; i++) {
401: if (c[i] instanceof JSplitterSpace) {
402: // only draw boxes where JSplitterSpace components appear
403: Rectangle r = c[i].getBounds();
404: g.fill3DRect(r.x + r.width / 2 - 1, r.y + 2, 3, r.y
405: + r.height - 5, true);
406: }
407: }
408: else {
409: Rectangle r = getBounds();
410: g.fill3DRect(r.width / 2 - 1, 2, 3, r.height - 5, true);
411: }
412: }
413:
414: /**
415: * Called by AWT to update the image produced by the JSplitterBar
416: */
417: public void update(Graphics g) {
418: paint(g);
419: }
420: }
|