001: /*
002: #IFNDEF ALT_LICENSE
003: ThinWire(R) RIA Ajax Framework
004: Copyright (C) 2003-2007 Custom Credit Systems
005:
006: This library is free software; you can redistribute it and/or modify it under
007: the terms of the GNU Lesser General Public License as published by the Free
008: Software Foundation; either version 2.1 of the License, or (at your option) any
009: later version.
010:
011: This library is distributed in the hope that it will be useful, but WITHOUT ANY
012: WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
013: PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
014:
015: You should have received a copy of the GNU Lesser General Public License along
016: with this library; if not, write to the Free Software Foundation, Inc., 59
017: Temple Place, Suite 330, Boston, MA 02111-1307 USA
018:
019: Users who would rather have a commercial license, warranty or support should
020: contact the following company who invented, built and supports the technology:
021:
022: Custom Credit Systems, Richardson, TX 75081, USA.
023: email: info@thinwire.com ph: +1 (888) 644-6405
024: http://www.thinwire.com
025: #ENDIF
026: [ v1.2_RC2 ]
027: */
028: package thinwire.render.web;
029:
030: import java.util.HashMap;
031: import java.util.List;
032: import java.util.Map;
033: import java.util.regex.Pattern;
034:
035: import thinwire.ui.Component;
036: import thinwire.ui.Window;
037: import thinwire.ui.HierarchyComponent;
038: import thinwire.ui.Menu;
039: import thinwire.ui.event.ActionEvent;
040: import thinwire.ui.event.ItemChangeEvent;
041: import thinwire.ui.event.ItemChangeListener;
042: import thinwire.ui.event.KeyPressEvent;
043: import thinwire.ui.event.KeyPressListener;
044: import thinwire.ui.event.PropertyChangeEvent;
045:
046: /**
047: * @author Joshua J. Gertzen
048: */
049: final class MenuRenderer extends ComponentRenderer implements
050: ItemChangeListener {
051: private static final Pattern REGEX_AMP = Pattern
052: .compile("[&](\\w)");
053: private static final Pattern REGEX_DAMP = Pattern
054: .compile("[&]{1,2}");
055: private static final String MENU_CLASS = "tw_Menu";
056: private static final String ITEM_REMOVE = "itemRemove";
057: private static final String ITEM_LOAD = "itemLoad";
058: private static final String ITEM_SET_TEXT = "itemSetText";
059: private static final String ITEM_SET_KEY_PRESS_COMBO = "itemSetKeyPressCombo";
060: private static final String ITEM_SET_IMAGE_URL = "itemSetImageUrl";
061: private static final String ITEM_SET_ENABLED = "itemSetEnabled";
062: private Menu menu;
063: private Map<Menu.Item, KeyPressListener> itemToKeyPressListeners;
064: private StringBuilder sb;
065:
066: void render(WindowRenderer wr, Component c,
067: ComponentRenderer container) {
068: init(MENU_CLASS, wr, c, container);
069:
070: boolean windowMenu = container instanceof WindowRenderer
071: && comp
072: .equals(((Window) ((WindowRenderer) container).comp)
073: .getMenu());
074:
075: //a menu does not support the focus, enabled, x, y, width or height properties
076: if (windowMenu) {
077: setPropertyChangeIgnored(Component.PROPERTY_X, true);
078: setPropertyChangeIgnored(Component.PROPERTY_Y, true);
079: setPropertyChangeIgnored(Component.PROPERTY_WIDTH, true);
080: setPropertyChangeIgnored(Component.PROPERTY_VISIBLE, true);
081: }
082:
083: setPropertyChangeIgnored(Component.PROPERTY_FOCUS, true);
084:
085: menu = (Menu) c;
086: sb = new StringBuilder();
087: buildMenuChildrenInit((Menu.Item) menu.getRootItem(), true);
088: menu.addItemChangeListener(this );
089: addInitProperty("initData", sb);
090: addInitProperty("windowMenu", windowMenu);
091: super .render(wr, c, container);
092: sb.setLength(0);
093: }
094:
095: void destroy() {
096: Window w = (Window) wr.comp;
097: super .destroy();
098:
099: if (itemToKeyPressListeners != null) {
100: for (KeyPressListener l : itemToKeyPressListeners.values()) {
101: w.removeKeyPressListener(l);
102: }
103: }
104:
105: menu.removeItemChangeListener(this );
106: menu = null;
107: sb = null;
108: }
109:
110: public void propertyChange(PropertyChangeEvent pce) {
111: String name = pce.getPropertyName();
112: Object newValue = pce.getNewValue();
113: Object source = pce.getSource();
114:
115: if (source instanceof Menu.Item) {
116: Menu.Item item = (Menu.Item) source;
117: String fullIndex = TreeRenderer.fullIndex(item);
118:
119: if (name.equals(HierarchyComponent.Item.PROPERTY_ITEM_TEXT)) {
120: String text = (String) newValue;
121: String oldText = (String) pce.getOldValue();
122: boolean oldDivider = oldText.length() < 1;
123: boolean newDivider = text.length() < 1;
124:
125: if (oldDivider != newDivider) {
126: postClientEvent(ITEM_REMOVE, fullIndex);
127: fullIndex = TreeRenderer.fullIndex((Menu.Item) item
128: .getParent());
129:
130: if (text.length() == 0) {
131: buildDividerInit((Menu.Item) source, item
132: .getIndex());
133: } else {
134: buildMenuInit((Menu.Item) source, item
135: .getIndex(), false);
136: }
137:
138: postClientEvent(ITEM_LOAD, sb.toString(), fullIndex);
139: sb.setLength(0);
140: } else {
141: postClientEvent(ITEM_SET_TEXT, fullIndex, text);
142: }
143: } else if (name
144: .equals(HierarchyComponent.Item.PROPERTY_ITEM_IMAGE)) {
145: postClientEvent(ITEM_SET_IMAGE_URL, fullIndex,
146: getQualifiedURL((String) newValue));
147: } else if (name
148: .equals(Menu.Item.PROPERTY_ITEM_KEY_PRESS_COMBO)) {
149: setupKeyPressListener(item);
150: postClientEvent(ITEM_SET_KEY_PRESS_COMBO, fullIndex,
151: newValue);
152: } else if (name.equals(Menu.Item.PROPERTY_ITEM_ENABLED)) {
153: postClientEvent(ITEM_SET_ENABLED, fullIndex, newValue);
154: }
155: } else {
156: super .propertyChange(pce);
157: }
158: }
159:
160: public void itemChange(ItemChangeEvent ice) {
161: ItemChangeEvent.Type type = ice.getType();
162: Menu.Item container = (Menu.Item) ice.getSource();
163: Integer pos = (Integer) ice.getPosition();
164:
165: if (type == ItemChangeEvent.Type.REMOVE
166: || type == ItemChangeEvent.Type.SET) {
167: String fullIndex = pos.toString();
168: if (menu.getRootItem() != container)
169: fullIndex = TreeRenderer.fullIndex(container) + "."
170: + fullIndex;
171: postClientEvent(ITEM_REMOVE, fullIndex);
172: }
173:
174: if (type == ItemChangeEvent.Type.ADD
175: || type == ItemChangeEvent.Type.SET) {
176: String fullIndex = TreeRenderer.fullIndex(container);
177: Menu.Item nc = (Menu.Item) ice.getNewValue();
178:
179: if (nc.getText().length() == 0) {
180: buildDividerInit(nc, pos.intValue());
181: } else {
182: buildMenuInit(nc, pos.intValue(), false);
183: }
184:
185: postClientEvent(ITEM_LOAD, sb, fullIndex);
186: sb.setLength(0);
187: }
188: }
189:
190: private void setupKeyPressListener(final Menu.Item item) {
191: Window w = (Window) wr.comp;
192:
193: if (itemToKeyPressListeners == null)
194: itemToKeyPressListeners = new HashMap<Menu.Item, KeyPressListener>();
195: KeyPressListener listener = itemToKeyPressListeners.get(item);
196:
197: if (listener == null) {
198: listener = new KeyPressListener() {
199: public void keyPress(KeyPressEvent ev) {
200: Menu menu = item.getHierarchy();
201: if (menu != null)
202: menu.fireAction(new ActionEvent(
203: Menu.ACTION_CLICK, menu, item));
204: }
205: };
206:
207: itemToKeyPressListeners.put(item, listener);
208: w.addKeyPressListener(item.getKeyPressCombo(), listener);
209: }
210: }
211:
212: private void buildMenuInit(Menu.Item item, int index, boolean isRoot) {
213: sb.append('{');
214: sb.append("en:").append(item.isEnabled());
215: String text = item.getText();
216:
217: if (text.indexOf('&') >= 0) {
218: text = REGEX_AMP.matcher(text).replaceFirst("<u>$1</u>");
219: text = REGEX_DAMP.matcher(text).replaceAll("&");
220: }
221:
222: sb.append(",t:");
223: GridBoxRenderer.getValue(this , text, null, sb);
224: String keyPressCombo = item.getKeyPressCombo();
225:
226: if (keyPressCombo.length() > 0) {
227: sb.append(",k:\"").append(keyPressCombo).append('"');
228: setupKeyPressListener(item);
229: }
230:
231: if (index != -1)
232: sb.append(",x:").append(index);
233:
234: if (item.getChildren().size() > 0) {
235: sb.append(",c:");
236: buildMenuChildrenInit(item, false);
237: }
238:
239: String img = item.getImage();
240: if (img.length() > 0)
241: sb.append(",g:\"").append(getQualifiedURL(item.getImage()))
242: .append('"');
243: sb.append('}');
244: }
245:
246: private void buildMenuChildrenInit(Menu.Item menu, boolean isRoot) {
247: sb.append('[');
248: List<Menu.Item> content = menu.getChildren();
249:
250: for (int i = 0, cnt = content.size(); i < cnt; i++) {
251: Menu.Item c = content.get(i);
252:
253: if (isRoot || c.getText().length() > 0) {
254: buildMenuInit(c, -1, isRoot);
255: sb.append(',');
256: } else {
257: buildDividerInit(c, -1);
258: sb.append(',');
259: }
260: }
261:
262: if (sb.charAt(sb.length() - 1) == '[')
263: sb.append(']');
264: else
265: sb.setCharAt(sb.length() - 1, ']');
266: }
267:
268: private void buildDividerInit(Menu.Item menu, int index) {
269: sb.append("{");
270: sb.append("en:").append(menu.isEnabled());
271: if (index != -1)
272: sb.append(",x:").append(index);
273: sb.append('}');
274: }
275: }
|