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.widget.menu;
009:
010: import java.util.List;
011:
012: import net.mygwt.ui.client.Events;
013: import net.mygwt.ui.client.MyDOM;
014: import net.mygwt.ui.client.widget.Container;
015: import net.mygwt.ui.client.widget.Popup;
016:
017: import com.google.gwt.user.client.DOM;
018: import com.google.gwt.user.client.Element;
019: import com.google.gwt.user.client.Event;
020: import com.google.gwt.user.client.ui.VerticalPanel;
021: import com.google.gwt.user.client.ui.Widget;
022: import com.google.gwt.user.client.ui.WidgetHelper;
023:
024: /**
025: * A menu.
026: *
027: * <dl>
028: * <dt><b>Events:</b></dt>
029: *
030: * <dd><b>BeforeOpen</b> : (widget this)<br>
031: * <div>Fired before the menu is opened. Listener can set the doit field to
032: * <code>false</code> to cancel the menu being displayed.</div>
033: * <ul>
034: * <li>widget : this</li>
035: * </ul>
036: * </dd>
037: *
038: * <dd><b>Open</b> : (widget this)<br>
039: * <div>Fired after a the menu is opened</div>
040: * <ul>
041: * <li>widget : this</li>
042: * </ul>
043: * </dd>
044: *
045: * <dd><b>BeforeClose</b> : (widget this)<br>
046: * <div>Fired before the menu is closed. Listener can set the doit field to
047: * <code>false</code> to cancel the menu being hidden.</div>
048: * <ul>
049: * <li>widget : this</li>
050: * </ul>
051: * </dd>
052: *
053: * <dd><b>Close</b> : (widget this)<br>
054: * <div>Fired after the menu is closed</div>
055: * <ul>
056: * <li>widget : this</li>
057: * </ul>
058: * </dd>
059: * </dl>
060: */
061: public class Menu extends Container {
062:
063: protected boolean hideOnClick = true;
064:
065: boolean showing, closeOnSelect;
066: MenuItem parentItem;
067:
068: private int minWidth = 140;
069: private String subMenuAlign = "tl-tr?";
070: private MenuItem activeItem;
071: private VerticalPanel panel;
072: private Popup popup;
073:
074: /**
075: * Creates a new menu.
076: */
077: public Menu() {
078: attachChildren = false;
079: }
080:
081: /**
082: * Adds a item to the menu.
083: *
084: * @param item the new item
085: */
086: public void add(MenuItem item) {
087: insert(item, getItemCount());
088: }
089:
090: /**
091: * Finds a item.
092: *
093: * @param target the target element
094: * @return the item
095: */
096: public MenuItem findItem(Element target) {
097: for (int i = 0; i < items.size(); i++) {
098: MenuItem item = getItem(i);
099: if (DOM.isOrHasChild(item.getElement(), target)) {
100: return item;
101: }
102: }
103: return null;
104: }
105:
106: /**
107: * Returns the item at the given index or <code>null</code> if the index is
108: * out of range.
109: *
110: * @param index the index of the item to return
111: * @return the item at the given index
112: */
113: public MenuItem getItem(int index) {
114: return (MenuItem) items.get(index);
115: }
116:
117: /**
118: * Returns the number of items contained in the menu.
119: *
120: * @return the number of items
121: */
122: public int getItemCount() {
123: return items.size();
124: }
125:
126: /**
127: * Returns the menu's child items.
128: *
129: * @return the child items
130: */
131: public List getItems() {
132: return items;
133: }
134:
135: /**
136: * Returns the minimum width.
137: *
138: * @return the minimum width
139: */
140: public int getMinWidth() {
141: return minWidth;
142: }
143:
144: /**
145: * Returns the menu's parent item, or <code>null</code> if the menu is a
146: * root.
147: *
148: * @return the parent item
149: */
150: public MenuItem getParentItem() {
151: return parentItem;
152: }
153:
154: /**
155: * Returns the sub menu alignment.
156: *
157: * @return the alignment
158: */
159: public String getSubMenuAlign() {
160: return subMenuAlign;
161: }
162:
163: public void hide() {
164: if (fireEvent(Events.BeforeClose)) {
165: popup.hide();
166: showing = false;
167: fireEvent(Events.Close);
168: }
169: }
170:
171: /**
172: * Returns the index of the item, or -1 if not found.
173: *
174: * @param item the item to search for
175: * @return the index or -1 if not found
176: */
177: public int indexOf(MenuItem item) {
178: return items.indexOf(item);
179: }
180:
181: public void insert(MenuItem item, int index) {
182: if (fireEvent(Events.BeforeAdd, this , item, index)) {
183: item.parentMenu = this ;
184: items.add(index, item);
185: if (rendered) {
186: renderItem(item, index);
187: }
188: fireEvent(Events.Add, this , item, index);
189: }
190: }
191:
192: public void onBrowserEvent(Event event) {
193: int type = DOM.eventGetType(event);
194: switch (type) {
195: case Event.ONMOUSEOVER:
196: onMouseOver(event);
197: break;
198: }
199:
200: }
201:
202: public void remove(MenuItem item) {
203: if (fireEvent(Events.BeforeRemove, this , item)) {
204: items.remove(item);
205: if (rendered) {
206: panel.remove(item);
207: }
208: fireEvent(Events.Remove, this , item);
209: }
210: }
211:
212: /**
213: * Removes all menu items from this menu.
214: */
215: public void removeAll() {
216: int size = getItemCount();
217: for (int i = 0; i < size; i++) {
218: remove(getItem(0));
219: }
220: }
221:
222: /**
223: * Sets minimum width of the menu in pixels. Default value is 120.
224: *
225: * @param minWidth the minimum width
226: */
227: public void setMinWidth(int minWidth) {
228: this .minWidth = minWidth;
229: }
230:
231: /**
232: * Sets the {@link MyDOM#alignTo} anchor position value to use for submenus of
233: * this menu (defaults to "tl-tr?").
234: *
235: * @param subMenuAlign the alignment
236: */
237: public void setSubMenuAlign(String subMenuAlign) {
238: this .subMenuAlign = subMenuAlign;
239: }
240:
241: public void show() {
242: if (fireEvent(Events.BeforeOpen)) {
243: createPopup();
244: popup.show();
245: showing = true;
246: fireEvent(Events.Open);
247: }
248: }
249:
250: /**
251: * Shows the menu.
252: *
253: * @param elem the align element
254: * @param pos the position
255: */
256: public void show(Element elem, String pos) {
257: if (fireEvent(Events.BeforeOpen)) {
258: createPopup();
259: popup.show(elem, pos, new int[] { -2, 0 });
260: showing = true;
261: fireEvent(Events.Open);
262: }
263: }
264:
265: /**
266: * Shows the menu at the specified location.
267: *
268: * @param x the x coordinate value
269: * @param y the y corrdinate value
270: */
271: public void show(int x, int y) {
272: if (fireEvent(Events.BeforeOpen)) {
273: createPopup();
274: popup.show(x, y);
275: showing = true;
276: fireEvent(Events.Open);
277: }
278: }
279:
280: /**
281: * Shows the menu.
282: *
283: * @param widget the widget to align to
284: */
285: public void show(Widget widget) {
286: if (fireEvent(Events.BeforeOpen)) {
287: createPopup();
288: popup.show(widget);
289: showing = true;
290: fireEvent(Events.Open);
291: }
292: }
293:
294: protected void doAttachChildren() {
295: WidgetHelper.doAttach(panel);
296: }
297:
298: protected void doDetachChildren() {
299: WidgetHelper.doDetach(panel);
300: }
301:
302: protected void onMouseOut(Event event) {
303: MenuItem item = findItem(DOM.eventGetTarget(event));
304: if (item != null) {
305: if (item == activeItem && item.shouldDeactivate(event)) {
306: activeItem.deactivate();
307: }
308: }
309: }
310:
311: protected void onMouseOver(Event event) {
312: MenuItem item = findItem(DOM.eventGetTarget(event));
313: if (item == null)
314: return;
315: if (item.canActivate && !item.enabled) {
316: setActiveItem(item, true);
317: }
318: }
319:
320: protected void onRender() {
321: setElement(DOM.createDiv());
322: setStyleName("my-menu");
323: panel = new VerticalPanel();
324: DOM.appendChild(getElement(), panel.getElement());
325:
326: setBorders(true);
327:
328: panel.setWidth(minWidth + "px");
329:
330: renderAll();
331:
332: disableContextMenu(true);
333: sinkEvents(Event.ONCLICK | Event.MOUSEEVENTS | Event.KEYEVENTS);
334: }
335:
336: void closeAllMenus() {
337: Menu parent = this ;
338: while (parent != null) {
339: parent.hide();
340: if (parent.parentItem == null) {
341: parent = null;
342: } else {
343: parent = parent.parentItem.parentMenu;
344: }
345: }
346: }
347:
348: private void createPopup() {
349:
350: if (popup == null) {
351: final Menu fMenu = this ;
352: popup = new Popup(true) {
353: protected void afterHide() {
354: super .afterHide();
355: fMenu.fireEvent(Events.Close);
356: }
357:
358: protected boolean onAutoHide(Event event) {
359: if (parentItem != null) {
360: Element target = DOM.eventGetTarget(event);
361: if (DOM.isOrHasChild(parentItem.getElement(),
362: target)) {
363: return false;
364: }
365: }
366: closeAllMenus();
367: return true;
368: }
369:
370: };
371: popup.setShadow(true);
372: popup.setAnimate(false);
373: popup.setWidget(this );
374: }
375: }
376:
377: private void renderAll() {
378: int count = getItemCount();
379: for (int i = 0; i < count; i++) {
380: MenuItem item = getItem(i);
381: renderItem(item, i);
382: }
383: }
384:
385: private void renderItem(MenuItem item, int index) {
386: panel.insert(item, index);
387: }
388:
389: private void setActiveItem(MenuItem item, boolean autoExpand) {
390: if (item != activeItem) {
391: if (activeItem != null) {
392: activeItem.deactivate();
393: }
394: activeItem = item;
395: item.activate(autoExpand);
396: } else if (autoExpand) {
397: item.expandMenu();
398: }
399:
400: }
401:
402: }
|