001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: /**
019: * @author Vadim L. Bogdanov
020: * @version $Revision$
021: */package org.apache.harmony.x.swing;
022:
023: import java.awt.Component;
024: import java.awt.Container;
025: import java.awt.Cursor;
026: import java.awt.Dimension;
027: import java.awt.Insets;
028: import java.awt.Point;
029: import java.awt.Rectangle;
030: import java.awt.Window;
031: import java.awt.event.MouseEvent;
032:
033: import javax.swing.JComponent;
034: import javax.swing.JRootPane;
035: import javax.swing.SwingConstants;
036: import javax.swing.SwingUtilities;
037:
038: /**
039: * This class helps to implement dragging/resising of components
040: * with a mouse.
041: * For example, it is used in <code>BasicInternalFrameUI.BorderListener</code>.
042: *
043: */
044: public class ComponentDragImplHelper implements SwingConstants {
045: /**
046: * This field is used in resizing. It shows which area is considered
047: * as a corner and resizement is made in diagonal direction.
048: */
049: // Note: the value is hardcoded;
050: // the same value is hardcoded in MetalBorders.InternalFrameBorder
051: public static final int FRAME_CORNER_SIZE = 15;
052:
053: /**
054: * This value shows that there is no resizement, only dragging.
055: */
056: public static final int RESIZE_NONE = 0;
057:
058: /*
059: * Maps directions from Rectangle to directions from SwingConstants.
060: */
061: private static final int[] decodeResizeDirTable = { 0, RESIZE_NONE,
062: Rectangle.OUT_BOTTOM, SOUTH, Rectangle.OUT_RIGHT, EAST,
063: Rectangle.OUT_LEFT, WEST, Rectangle.OUT_TOP, NORTH,
064: Rectangle.OUT_TOP | Rectangle.OUT_LEFT, NORTH_WEST,
065: Rectangle.OUT_TOP | Rectangle.OUT_RIGHT, NORTH_EAST,
066: Rectangle.OUT_BOTTOM | Rectangle.OUT_LEFT, SOUTH_WEST,
067: Rectangle.OUT_BOTTOM | Rectangle.OUT_RIGHT, SOUTH_EAST };
068:
069: /*
070: * Maps directions to cursor types.
071: */
072: private static final int[] ResizeDirToCursorTypeTable = {
073: RESIZE_NONE, Cursor.DEFAULT_CURSOR, SOUTH,
074: Cursor.S_RESIZE_CURSOR, EAST, Cursor.E_RESIZE_CURSOR, WEST,
075: Cursor.W_RESIZE_CURSOR, NORTH, Cursor.N_RESIZE_CURSOR,
076: NORTH_WEST, Cursor.NW_RESIZE_CURSOR, NORTH_EAST,
077: Cursor.NE_RESIZE_CURSOR, SOUTH_WEST,
078: Cursor.SW_RESIZE_CURSOR, SOUTH_EAST,
079: Cursor.SE_RESIZE_CURSOR };
080:
081: /*
082: * The coordinates where the mouse was pressed.
083: */
084: private Point start;
085:
086: /*
087: * The component to be resized/moved.
088: */
089: private Component comp;
090:
091: /*
092: * The parent of the resized/moved component. It can be null if
093: * the component is a window.
094: */
095: private Container parent;
096:
097: /*
098: * The size of the parent.
099: */
100: private Dimension parentSize;
101:
102: /*
103: * Becomes true if the component is being dragged or being resized.
104: */
105: private boolean dragging;
106:
107: /*
108: * The rectangle of available mouse dragging.
109: */
110: private Rectangle dragArea = new Rectangle();
111:
112: /*
113: * The bounds of the frame prior to resize/drag.
114: */
115: private Rectangle oldBounds;
116:
117: /*
118: * Shows the direction of resizing
119: */
120: private int resizeDirection = RESIZE_NONE;
121:
122: /**
123: * Starts dragging operation. This function is usually called from
124: * {@link java.awt.event.MouseListener#mousePressed(MouseEvent)} function.
125: *
126: * @param e the mouse event
127: * @param comp the component to be dragged
128: * @param parent the parent of the component being dragged
129: */
130: public final void beginDragging(final MouseEvent e,
131: final JComponent comp, final Container parent) {
132: beginOperationImpl(e, comp, parent, RESIZE_NONE);
133: }
134:
135: /**
136: * Starts resizing operation. This function is usually called from
137: * {@link java.awt.event.MouseListener#mousePressed(MouseEvent)} function.
138: *
139: * @param e the mouse event
140: * @param comp the component to be resized
141: * @param parent the parent of the component being resized
142: */
143: public final void beginResizing(final MouseEvent e,
144: final JComponent comp, final Container parent) {
145: beginOperationImpl(e, comp, parent, getResizeDirection(e, comp));
146: }
147:
148: private void beginOperationImpl(final MouseEvent e,
149: final Component comp, final Container parent,
150: final int resizeDirection) {
151: this .comp = comp;
152: this .parent = parent;
153: parentSize = getParentSize(parent);
154: oldBounds = comp.getBounds(oldBounds);
155: start = getPointInParentCoordinates(e, false);
156: this .resizeDirection = resizeDirection;
157: setDragArea(e);
158: dragging = true;
159: }
160:
161: /**
162: * Starts dragging or resizing operation. This function is usually
163: * called from {@link java.awt.event.MouseListener#mousePressed(MouseEvent)}
164: * function.
165: *
166: * @param e the mouse event
167: * @param window the window to be resized/moved
168: * @param rootPane the <code>rootPane</code> of the window
169: */
170: public final void mousePressed(final MouseEvent e,
171: final Window window, final JRootPane rootPane) {
172: beginOperationImpl(e, window, null, getResizeDirection(e,
173: rootPane));
174: }
175:
176: /**
177: * This function is called when the mouse is dragged during
178: * resizing/moving operation. It receives the <code>MOUSE_DRAGGED</code>
179: * event and returns the new bounds of the component being dragged/resized.
180: *
181: * @param e the <code>MOUSE_DRAGGED</code> event
182: *
183: * @return the new bounds of the component being dragged/resized
184: */
185: public final Rectangle mouseDragged(final MouseEvent e) {
186: if (resizeDirection == RESIZE_NONE) {
187: Point p = keepPointInsideOfDragArea(e);
188: p.translate(-start.x, -start.y);
189: Rectangle r = oldBounds.getBounds();
190: r.translate(p.x, p.y);
191: return r;
192: }
193:
194: return getNewBounds(e);
195: }
196:
197: /**
198: * Ends resizing or dragging operation. This function is usually called from
199: * {@link java.awt.event.MouseListener#mouseReleased(MouseEvent)} function.
200: *
201: * @param e the mouse event
202: */
203: public final void endDraggingOrResizing(final MouseEvent e) {
204: dragging = false;
205: }
206:
207: /**
208: * Shows if the component is currently being dragged/resized.
209: *
210: * @return <code>true</code> if the component is being dragged/resized;
211: * otherwise <code>false</code> is returned;
212: */
213: public final boolean isDragging() {
214: return dragging;
215: }
216:
217: private Point getPointInParentCoordinates(final MouseEvent e,
218: final boolean useMouseInfo) {
219: if (parent != null) {
220: return SwingUtilities.convertPoint(e.getComponent(), e
221: .getPoint(), parent);
222: }
223:
224: Point p = useMouseInfo ? Utilities
225: .getMousePointerScreenLocation() : null;
226: if (p == null) {
227: p = e.getPoint();
228: SwingUtilities.convertPointToScreen(p, e.getComponent());
229: }
230:
231: return p;
232: }
233:
234: private Dimension getParentSize(final Component parent) {
235: return parent != null ? parent.getSize() : new Dimension(
236: Short.MAX_VALUE, Short.MAX_VALUE);
237: }
238:
239: /*
240: * Calculates and sets dragArea (when starting the movement/resizement).
241: * "draggingRect" determines bounds in which the mouse cursor can be
242: * moved. It depends on desktop pane size (the mouse cursor cannot
243: * leave it), internal frame's location and its minimum size
244: * (the size of the internal frame cannot be less than its minimum
245: * size).
246: */
247: private void setDragArea(final MouseEvent e) {
248: if (resizeDirection == RESIZE_NONE) {
249: if (parent != null) {
250: Insets insets = parent.getInsets();
251: parent.getBounds(dragArea);
252: dragArea.setRect(insets.left, insets.top,
253: dragArea.width - insets.right - insets.left,
254: dragArea.height - insets.top - insets.bottom);
255: } else {
256: dragArea.setBounds(0, 0, parentSize.width,
257: parentSize.height);
258: }
259: return;
260: }
261:
262: Dimension minSize = comp.getMinimumSize();
263: int dX = comp.getWidth() - minSize.width;
264: int dY = comp.getHeight() - minSize.height;
265:
266: // the result is the minimum rectangle that contains p1, p2
267: Point p1 = e.getPoint();
268: Point p2 = start.getLocation();
269:
270: switch (resizeDirection) {
271: case NORTH_WEST:
272: case NORTH:
273: case WEST:
274: p2.translate(dX, dY);
275: break;
276:
277: case NORTH_EAST:
278: p1.translate(parentSize.width - comp.getWidth(), 0);
279: p2.translate(-dX, dY);
280: break;
281:
282: case SOUTH_EAST:
283: case SOUTH:
284: case EAST:
285: p1.translate(parentSize.width - comp.getWidth(),
286: parentSize.height - comp.getHeight());
287: p2.translate(-dX, -dY);
288: break;
289:
290: default: // SOUTH_WEST
291: p1.translate(0, parentSize.height - comp.getHeight());
292: p2.translate(dX, -dY);
293: }
294:
295: // construct the minumim rectangle that contains both p1, p2
296: dragArea.setLocation(p1);
297: dragArea.setSize(0, 0);
298: dragArea.add(p2);
299: }
300:
301: /*
302: * This method is used when resizing the frame.
303: * Calculates the new size of the frame.
304: */
305: private Rectangle getNewBounds(final MouseEvent e) {
306: Rectangle bounds = new Rectangle();
307:
308: Point p = keepPointInsideOfDragArea(e);
309:
310: int x = (p.x - start.x);
311: int y = (p.y - start.y);
312:
313: int dX = 0;
314: int dY = 0;
315: int dWidth = 0;
316: int dHeight = 0;
317:
318: switch (resizeDirection) {
319: case NORTH:
320: case NORTH_EAST:
321: case NORTH_WEST:
322: dY = y;
323: dHeight = -y;
324: break;
325: }
326:
327: switch (resizeDirection) {
328: case SOUTH:
329: case SOUTH_EAST:
330: case SOUTH_WEST:
331: dHeight = y;
332: break;
333: }
334:
335: switch (resizeDirection) {
336: case EAST:
337: case SOUTH_EAST:
338: case NORTH_EAST:
339: dWidth = x;
340: break;
341: }
342:
343: switch (resizeDirection) {
344: case WEST:
345: case SOUTH_WEST:
346: case NORTH_WEST:
347: dX = x;
348: dWidth = -x;
349: break;
350: }
351:
352: bounds.setLocation(oldBounds.x + dX, oldBounds.y + dY);
353: bounds.width = oldBounds.width + dWidth;
354: bounds.height = oldBounds.height + dHeight;
355:
356: return bounds;
357: }
358:
359: private Point keepPointInsideOfDragArea(final MouseEvent e) {
360: Point p = getPointInParentCoordinates(e, true);
361: p.x = Math.max(dragArea.x, p.x);
362: p.y = Math.max(dragArea.y, p.y);
363: p.x = Math.min(dragArea.x + dragArea.width, p.x);
364: p.y = Math.min(dragArea.y + dragArea.height, p.y);
365: return p;
366: }
367:
368: /*
369: * Finds the key in the array that consists of pairs: key, value.
370: * If the key is found, returns the corresponding value; returns
371: * 0 if the key is not found.
372: */
373: private static int findValueInArray(final int[] array, final int key) {
374: for (int i = 0; i < array.length; i += 2) {
375: if (array[i] == key) {
376: return array[i + 1];
377: }
378: }
379: return -1;
380: }
381:
382: /*
383: * Decodes the direction from Rectangle direction to
384: * one of the SwingConstants direction constants.
385: */
386: private static int decodeResizeDirection(final int rawDirection) {
387: int result = findValueInArray(decodeResizeDirTable,
388: rawDirection);
389: if (result == -1) {
390: assert false : "invalid direction";
391: return RESIZE_NONE;
392: }
393:
394: return result;
395: }
396:
397: /**
398: * @param e the mouse event
399: * @param comp the resizing component
400: *
401: * @return the direction of the resizement.
402: */
403: public static final int getResizeDirection(final MouseEvent e,
404: final JComponent comp) {
405: Point p = SwingUtilities.convertPoint(
406: (Component) e.getSource(), e.getPoint(), comp);
407:
408: Rectangle inner = SwingUtilities.calculateInnerArea(comp, null);
409: if (inner.contains(p)) {
410: // the point is inside of the frame and
411: // the border doesn't contain it
412: return RESIZE_NONE;
413: }
414:
415: if (!SwingUtilities.getLocalBounds(comp).contains(p)) {
416: // the point is outside of the frame;
417: // this can occur when the mouse button is released
418: return RESIZE_NONE;
419: }
420:
421: inner.grow(-FRAME_CORNER_SIZE, -FRAME_CORNER_SIZE);
422: if (inner.width < 0) {
423: inner.width = 1;
424: }
425: if (inner.height < 0) {
426: inner.height = 1;
427: }
428: return decodeResizeDirection(inner.outcode(p));
429: }
430:
431: /**
432: * This function is used to determine the cursor that shows the resize
433: * direction on the border of the component.
434: *
435: * @param e the mouse event
436: * @param comp the component
437: */
438: public static final Cursor getUpdatedCursor(final MouseEvent e,
439: final JComponent comp) {
440: int cursorType = findValueInArray(ResizeDirToCursorTypeTable,
441: getResizeDirection(e, comp));
442:
443: if (cursorType == -1) {
444: assert false : "invalid direction";
445: cursorType = Cursor.DEFAULT_CURSOR;
446: }
447:
448: return Cursor.getPredefinedCursor(cursorType);
449: }
450: }
|