001: /*
002: * MyGWT Widget Library
003: * Copyright(c) 2007, MyGWT.
004: * licensing@mygwt.net
005: *
006: * http://mygwt.net/license
007: */
008: package net.mygwt.ui.client.fx;
009:
010: import java.util.ArrayList;
011: import java.util.List;
012:
013: import net.mygwt.ui.client.Events;
014: import net.mygwt.ui.client.Style;
015: import net.mygwt.ui.client.MyDOM;
016: import net.mygwt.ui.client.event.BaseEvent;
017: import net.mygwt.ui.client.event.Listener;
018: import net.mygwt.ui.client.util.Observable;
019: import net.mygwt.ui.client.util.Rectangle;
020: import net.mygwt.ui.client.widget.Component;
021:
022: import com.google.gwt.user.client.DOM;
023: import com.google.gwt.user.client.Element;
024: import com.google.gwt.user.client.Event;
025: import com.google.gwt.user.client.EventPreview;
026: import com.google.gwt.user.client.ui.RootPanel;
027: import com.google.gwt.user.client.ui.Widget;
028: import com.google.gwt.user.client.ui.WidgetHelper;
029:
030: /**
031: * Applies drag handles to a widget to make it resizable.
032: *
033: * <dl>
034: * <dt><b>Events:</b></dt>
035: *
036: * <dd><b>ResizeStart</b> : (source, widget, event) <br>
037: * Fires after a resize operation has started.
038: * <ul>
039: * <li>source : this</li>
040: * <li>widget : resize widget</li>
041: * <li>event : the dom event</li>
042: * </ul>
043: * </dd>
044: *
045: * <dd><b>ResizeEnd</b> : (source, widget, event)<br>
046: * Fires after a resize.
047: * <ul>
048: * <li>source : this</li>
049: * <li>widget : resize widget</li>
050: * <li>event : the dom event</li>
051: * </ul>
052: * </dd>
053: * </dl>
054: */
055: public class Resizable extends Observable {
056:
057: private class ResizeHandle extends Widget {
058:
059: public int dir;
060:
061: public ResizeHandle() {
062: setElement(DOM.createDiv());
063: sinkEvents(Event.MOUSEEVENTS);
064: }
065:
066: public void onBrowserEvent(Event event) {
067: switch (DOM.eventGetType(event)) {
068: case Event.ONMOUSEDOWN:
069: DOM.eventCancelBubble(event, true);
070: DOM.eventPreventDefault(event);
071: handleMouseDown(event, this );
072: break;
073: }
074: }
075: }
076:
077: /**
078: * The minumum width for the widget. Default value is 50.
079: */
080: public int minWidth = 50;
081:
082: /**
083: * The maximum width for the widget. Default value is 2000;
084: */
085: public int maxWidth = 2000;
086:
087: /**
088: * The minimum height for the widget. Default value is 50;
089: */
090: public int minHeight = 50;
091:
092: /**
093: * The maximum height for the widget. Default value is 2000;
094: */
095: public int maxHeight = 2000;
096:
097: /**
098: * proxyStyle is the style name used for proxy drags. Default value is
099: * 'my-resize-proxy'.
100: */
101: public String proxyStyle = "my-resize-proxy";
102:
103: /**
104: * <code>true</code> to resize the element while dragging instead of using a
105: * proxy.
106: */
107: public boolean dynamic = false;
108:
109: private Component resize;
110: private List handles;
111: private boolean enabled = true;
112: private boolean dragging;
113: private Element proxyElem, dragElem;
114: private int startX, startY;
115: private int dir;
116: private EventPreview preview;
117: private Rectangle start;
118: private Listener listener;
119:
120: /**
121: * Creates a new resizable instance.
122: *
123: * @param resize the resize widget
124: */
125: public Resizable(Component resize) {
126: this (resize, new ResizeConfig());
127: }
128:
129: /**
130: * Creates a new resizable instance.
131: *
132: * @param resize the resize widget
133: * @param cfg the config
134: */
135: public Resizable(Component resize, ResizeConfig cfg) {
136: this .resize = resize;
137:
138: MyDOM.makePositionable(resize.getElement());
139:
140: handles = new ArrayList();
141:
142: if (cfg.south)
143: create(Style.SOUTH, "s");
144: if (cfg.southEast)
145: create(Style.SE, "se");
146: if (cfg.east)
147: create(Style.EAST, "e");
148:
149: listener = new Listener() {
150: public void handleEvent(BaseEvent be) {
151: switch (be.type) {
152: case Events.Attach:
153: onAttach();
154: break;
155: case Events.Detach:
156: onDetach();
157: break;
158: }
159: }
160: };
161:
162: resize.addListener(Events.Attach, listener);
163: resize.addListener(Events.Detach, listener);
164:
165: if (resize.isAttached()) {
166: onAttach();
167: }
168:
169: preview = new EventPreview() {
170: public boolean onEventPreview(Event event) {
171: switch (DOM.eventGetType(event)) {
172: case Event.ONMOUSEMOVE:
173: int x = DOM.eventGetClientX(event);
174: int y = DOM.eventGetClientY(event);
175: handleMouseMove(x, y);
176: break;
177: case Event.ONMOUSEUP:
178: handleMouseUp(event);
179: break;
180: }
181: return false;
182: }
183: };
184:
185: }
186:
187: /**
188: * Returns <code>true</code> if if resizing.
189: *
190: * @return the resize state
191: */
192: public boolean isResizing() {
193: return dragging;
194: }
195:
196: /**
197: * Removes the drag handles.
198: */
199: public void release() {
200: onDetach();
201:
202: resize.removeListener(Events.Attach, listener);
203: resize.removeListener(Events.Detach, listener);
204: for (int i = 0; i < handles.size(); i++) {
205: ResizeHandle handle = (ResizeHandle) handles.remove(0);
206: DOM.removeChild(resize.getElement(), handle.getElement());
207: }
208: }
209:
210: /**
211: * Enables or disables the drag handles.
212: *
213: * @param enable <code>true</code> to enable
214: */
215: public void setEnabled(boolean enable) {
216: for (int i = 0; i < handles.size(); i++) {
217: ResizeHandle handle = (ResizeHandle) handles.get(i);
218: MyDOM.setVisibility(handle.getElement(), enable);
219: }
220: }
221:
222: protected void onAttach() {
223: for (int i = 0; i < handles.size(); i++) {
224: Widget w = (Widget) handles.get(i);
225: WidgetHelper.doAttach(w);
226: }
227: }
228:
229: protected void onDetach() {
230: for (int i = 0; i < handles.size(); i++) {
231: Widget w = (Widget) handles.get(i);
232: WidgetHelper.doDetach(w);
233: }
234: }
235:
236: private ResizeHandle create(int dir, String cls) {
237: ResizeHandle rh = new ResizeHandle();
238: rh.setStyleName("my-resize-handle");
239: rh.addStyleName("my-resize-handle-" + cls);
240: rh.dir = dir;
241: DOM.appendChild(resize.getElement(), rh.getElement());
242: handles.add(rh);
243: return rh;
244: }
245:
246: private void handleMouseDown(Event event, ResizeHandle handle) {
247: if (!enabled) {
248: return;
249: }
250:
251: dir = handle.dir;
252: start = MyDOM.getBounds(resize.getElement(), false);
253: startX = DOM.eventGetClientX(event);
254: startY = DOM.eventGetClientY(event);
255: dragging = true;
256:
257: if (!dynamic) {
258: if (proxyElem == null) {
259: proxyElem = DOM.createDiv();
260: MyDOM.setStyleName(proxyElem, proxyStyle, true);
261: MyDOM.disableTextSelection(proxyElem, true);
262:
263: Element body = RootPanel.getBodyElement();
264: DOM.appendChild(body, proxyElem);
265: }
266:
267: MyDOM.setLeft(proxyElem, start.x);
268: MyDOM.setTop(proxyElem, start.y);
269: MyDOM.setSize(proxyElem, start.width, start.height);
270: MyDOM.setVisible(proxyElem, true);
271: dragElem = proxyElem;
272:
273: } else {
274: dragElem = resize.getElement();
275: }
276: DOM.addEventPreview(preview);
277:
278: BaseEvent be = new BaseEvent();
279: be.source = this ;
280: be.widget = resize;
281: be.event = event;
282: fireEvent(Events.ResizeStart, be);
283: }
284:
285: private void handleMouseMove(int xin, int yin) {
286: if (dragging) {
287: int w = 0;
288: int h = 0;
289:
290: int difX = xin - startX;
291: int difY = yin - startY;
292:
293: w = start.width + difX;
294: h = start.height + difY;
295:
296: // enforce min max
297: w = Math.min(Math.max(minWidth, w), maxWidth);
298: h = Math.min(Math.max(minHeight, h), maxHeight);
299:
300: if (dir == Style.EAST || dir == Style.NE) {
301: MyDOM.setWidth(dragElem, w);
302: }
303: if (dir == Style.SOUTH || dir == Style.SW) {
304: MyDOM.setHeight(dragElem, h);
305: }
306: if (dir == Style.SE) {
307: MyDOM.setSize(dragElem, w, h);
308: }
309: }
310:
311: }
312:
313: private void handleMouseUp(Event event) {
314: dragging = false;
315: DOM.removeEventPreview(preview);
316: Rectangle rect = MyDOM.getBounds(dragElem, false);
317:
318: rect.width = Math.min(rect.width, maxWidth);
319: rect.height = Math.min(rect.height, maxHeight);
320:
321: if (proxyElem != null) {
322: MyDOM.disableTextSelection(proxyElem, false);
323: }
324:
325: resize.setBounds(rect);
326:
327: MyDOM.setVisible(dragElem, false);
328:
329: BaseEvent be = new BaseEvent();
330: be.source = this;
331: be.widget = resize;
332: be.event = event;
333: fireEvent(Events.ResizeEnd, be);
334: }
335:
336: }
|