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 net.mygwt.ui.client.Events;
011: import net.mygwt.ui.client.Style;
012: import net.mygwt.ui.client.event.BaseEvent;
013: import net.mygwt.ui.client.event.SelectionListener;
014: import net.mygwt.ui.client.event.TypedListener;
015: import net.mygwt.ui.client.widget.Item;
016:
017: import com.google.gwt.user.client.Command;
018: import com.google.gwt.user.client.DOM;
019: import com.google.gwt.user.client.DeferredCommand;
020: import com.google.gwt.user.client.Event;
021:
022: /**
023: * A selectable item in a <code>MenuBar</code>.
024: *
025: * <dl>
026: * <dt><b>Styles:</b></dt>
027: * <dd>PUSH, CHECK, RADIO, MENU, SEPARATOR</dd>
028: *
029: * <dt><b>Events:</b></dt>
030: *
031: * <dd><b>Select</b> : (widget this)<br>
032: * <div>Fired when a item is selected.</div>
033: * <ul>
034: * <li>widget : this</li>
035: * </ul>
036: * </dd>
037: *
038: * <dd><b>CheckChange</b> : (widget)<br>
039: * <div>Fires after a check state change.</div>
040: * <ul>
041: * <li>widget : this</li>
042: * </ul>
043: * </dd>
044: *
045: * <dd><b>BeforeOpen</b> : (widget)<br>
046: * <div>Fires before the sub menu is displayed. Listeners can set the
047: * <code>doit</code> field to <code>false</code> to cancel the action.</div>
048: * <ul>
049: * <li>widget : this</li>
050: * </ul>
051: * </dd>
052: *
053: * <dd><b>Open</b> : (widget)<br>
054: * <div>Fires after the sub menu is shown.</div>
055: * <ul>
056: * <li>widget : this</li>
057: * </ul>
058: * </dd>
059: *
060: * <dt><b>CSS:</b></dt>
061: * <dd>.my-menuitem (the item itself)</dd>
062: * <dd>.my-menuitem-text span (the menu item text)</dd>
063: * </dl>
064: */
065: public class MenuItem extends Item {
066:
067: boolean canActivate = true;
068: boolean enabled;
069: Menu parentMenu;
070: Menu subMenu;
071:
072: private boolean hideOnClick = true;
073: private String group = "";
074: private boolean selected;
075:
076: /**
077: * Creates a new PUSH menu item.
078: */
079: public MenuItem() {
080: this (Style.PUSH);
081: }
082:
083: /**
084: * Creates a new menu item.
085: */
086: public MenuItem(int style) {
087: super (style, "my-menuitem");
088: }
089:
090: /**
091: * Adds a listener interface to receive selection events.
092: *
093: * @param listener the listener to add
094: */
095: public void addSelectionListener(SelectionListener listener) {
096: TypedListener tl = new TypedListener(listener);
097: addListener(Events.Select, tl);
098: }
099:
100: /**
101: * Returns the item's group value.
102: *
103: * @return the group
104: */
105: public String getGroup() {
106: return group;
107: }
108:
109: /**
110: * Returns <code>true</code> if the menu will be closed on a click.
111: *
112: * @return the hide on click state
113: */
114: public boolean getHideOnClick() {
115: return hideOnClick;
116: }
117:
118: /**
119: * Returns the item's sub menu.
120: *
121: * @return the sub menu
122: */
123: public Menu getSubMenu() {
124: return subMenu;
125: }
126:
127: /**
128: * Returns <code>true</code> if the item is selected.
129: * <p>
130: * When the item is of type CHECK or RADIO, it is selected when it is checked.
131: * </p>
132: *
133: * @return the selected state
134: */
135: public boolean isSelected() {
136: return selected;
137: }
138:
139: /**
140: * Removes a previously added listener.
141: *
142: * @param listener the listener to be removed
143: */
144: public void removeSelectionListener(SelectionListener listener) {
145: unhook(Events.EffectStart, listener);
146: }
147:
148: /**
149: * Allows radio items to be grouped together.
150: */
151: public void setGroup(String group) {
152: this .group = group;
153: }
154:
155: /**
156: * Specifies if the menu should be hidden when the item is selected. Default
157: * value is <code>true</code>.
158: *
159: * @param hideOnClick <code>true</code> to enable
160: */
161: public void setHideOnClick(boolean hideOnClick) {
162: this .hideOnClick = hideOnClick;
163: }
164:
165: /**
166: * Sets the item's selected state.
167: * <p>
168: * When the item is of type CHECK or RADIO, it is selected when it is checked.
169: * </p>
170: *
171: * @param selected the select state
172: */
173: public void setSelected(boolean selected) {
174: this .selected = selected;
175: if (isRendered()) {
176: setSelected(selected, true);
177: }
178: }
179:
180: /**
181: * Sets the item's style. Has no effect if called after the item has been
182: * rendered.
183: *
184: * @param style the style
185: */
186: public void setStyle(int style) {
187: if (!isRendered()) {
188: this .style = style;
189: }
190: }
191:
192: /**
193: * Sets the item's sub menu.
194: *
195: * @param menu the sub menu or <code>null</code> to clear the sub menu
196: */
197: public void setSubMenu(Menu menu) {
198: if (menu == null) {
199: this .subMenu = null;
200: removeStyleName("my-menuitem-submenu");
201: } else {
202: this .subMenu = menu;
203: menu.parentItem = this ;
204: addStyleName("my-menuitem-submenu");
205: }
206: }
207:
208: protected void onClick(BaseEvent be) {
209: be.stopEvent();
210: switch (style) {
211: case Style.MENU:
212: if (subMenu != null && subMenu.isVisible()) {
213: return;
214: }
215: break;
216: case Style.CHECK:
217: case Style.RADIO:
218: case Style.PUSH:
219: setSelected(!isSelected());
220: break;
221: }
222: if (hideOnClick) {
223: onMouseOut(be);
224: parentMenu.closeAllMenus();
225: }
226: }
227:
228: protected void onRender() {
229: super .onRender();
230:
231: DOM.appendChild(midRightElem, DOM.createDiv());
232:
233: switch (style) {
234: case Style.SEPARATOR:
235: setElement(DOM.createDiv());
236: setStyleName("my-menu-separator");
237: DOM.appendChild(getElement(), DOM.createDiv());
238: hideOnClick = false;
239: overStyleEnabled = false;
240: break;
241: case Style.CHECK:
242: String s = isSelected() ? "icon-checked"
243: : "icon-notchecked";
244: setIconStyle(s);
245: break;
246: }
247:
248: if (style != Style.SEPARATOR && iconStyle == null) {
249: setIconStyle("blank");
250: }
251:
252: if (selected) {
253: setSelected(true, false);
254: }
255: }
256:
257: void activate(boolean autoOpen) {
258: if (autoOpen) {
259: expandMenu();
260: }
261: }
262:
263: void deactivate() {
264: hideMenu();
265: }
266:
267: void expandMenu() {
268: if (subMenu != null) {
269: if (!subMenu.showing && fireEvent(Events.BeforeOpen)) {
270: subMenu.show(getElement(), "tl-tr-?");
271: fireEvent(Events.Open);
272: }
273: }
274: }
275:
276: void hideMenu() {
277: if (subMenu != null && subMenu.isVisible()) {
278: subMenu.hide();
279: }
280: }
281:
282: boolean shouldDeactivate(Event event) {
283: if (subMenu != null && subMenu.isVisible()) {
284: return DOM.isOrHasChild(getElement(), DOM
285: .eventGetTarget(event));
286: }
287: return false;
288: }
289:
290: private void setSelected(boolean selected, boolean fireEvent) {
291: super .setSelected(selected);
292: switch (style) {
293: case Style.CHECK: {
294: String s = selected ? "icon-checked" : "icon-notchecked";
295: setIconStyle(s);
296: if (fireEvent) {
297: fireEvent(Events.CheckChange);
298: }
299: break;
300: }
301: case Style.RADIO: {
302: if (parentMenu == null) {
303: setIconStyle("icon-group-sel");
304: break;
305: }
306: int size = parentMenu.getItemCount();
307: for (int i = 0; i < size; i++) {
308: MenuItem item = parentMenu.getItem(i);
309: if (item.style == Style.RADIO) {
310: if (!item.group.equals(group)) {
311: continue;
312: }
313: if (item == this ) {
314: item.setIconStyle("icon-group-sel");
315: } else {
316: item.setIconStyle("my-none");
317: item.selected = false;
318: }
319:
320: }
321: }
322: if (fireEvent) {
323: fireEvent(Events.CheckChange);
324: }
325: break;
326: }
327: }
328: if (fireEvent) {
329: // let the event processing finish before firing event
330: DeferredCommand.addCommand(new Command() {
331: public void execute() {
332: fireEvent(Events.Select);
333: }
334: });
335: }
336:
337: }
338: }
|