001: /*
002: * Version: MPL 1.1/GPL 2.0/LGPL 2.1
003: *
004: * "The contents of this file are subject to the Mozilla Public License
005: * Version 1.1 (the "License"); you may not use this file except in
006: * compliance with the License. You may obtain a copy of the License at
007: * http://www.mozilla.org/MPL/
008: *
009: * Software distributed under the License is distributed on an "AS IS"
010: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
011: * License for the specific language governing rights and limitations under
012: * the License.
013: *
014: * The Original Code is ICEfaces 1.5 open source software code, released
015: * November 5, 2006. The Initial Developer of the Original Code is ICEsoft
016: * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
017: * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
018: *
019: * Contributor(s): _____________________.
020: *
021: * Alternatively, the contents of this file may be used under the terms of
022: * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
023: * License), in which case the provisions of the LGPL License are
024: * applicable instead of those above. If you wish to allow use of your
025: * version of this file only under the terms of the LGPL License and not to
026: * allow others to use your version of this file under the MPL, indicate
027: * your decision by deleting the provisions above and replace them with
028: * the notice and other provisions required by the LGPL License. If you do
029: * not delete the provisions above, a recipient may use your version of
030: * this file under either the MPL or the LGPL License."
031: *
032: */
033:
034: package com.icesoft.faces.component.panelpositioned;
035:
036: import com.icesoft.faces.component.panelseries.UISeries;
037: import com.icesoft.faces.context.DOMContext;
038: import com.icesoft.faces.context.effects.EffectsArguments;
039: import com.icesoft.faces.context.effects.JavascriptContext;
040: import com.icesoft.faces.renderkit.dom_html_basic.DomBasicRenderer;
041: import com.icesoft.faces.renderkit.dom_html_basic.HTML;
042: import com.icesoft.faces.utils.DnDCache;
043: import org.apache.commons.logging.Log;
044: import org.apache.commons.logging.LogFactory;
045: import org.w3c.dom.Element;
046: import org.w3c.dom.Node;
047:
048: import javax.faces.component.NamingContainer;
049: import javax.faces.component.UIComponent;
050: import javax.faces.component.UIViewRoot;
051: import javax.faces.context.FacesContext;
052: import java.beans.Beans;
053: import java.io.IOException;
054: import java.util.ArrayList;
055: import java.util.Iterator;
056: import java.util.List;
057: import java.util.Map;
058: import java.util.Random;
059: import java.util.StringTokenizer;
060:
061: /**
062: * Renderer for Positioned Panel
063: */
064: public class PanelPositionedRenderer extends DomBasicRenderer {
065:
066: private static Log log = LogFactory
067: .getLog(PanelPositionedRenderer.class);
068:
069: private static final String INPUT_ID = "colOrder";
070:
071: public boolean getRendersChildren() {
072: return true;
073: }
074:
075: public void encodeBegin(FacesContext facesContext,
076: UIComponent uiComponent) throws IOException {
077:
078: try {
079: DOMContext domContext = DOMContext.attachDOMContext(
080: facesContext, uiComponent);
081: if (!domContext.isInitialized()) {
082: Element root = domContext
083: .createRootElement(HTML.DIV_ELEM);
084: domContext.setRootNode(root);
085: setRootElementId(facesContext, root, uiComponent);
086:
087: String style = ((PanelPositioned) uiComponent)
088: .getStyle();
089: String styleClass = ((PanelPositioned) uiComponent)
090: .getStyleClass();
091: if (style != null && style.length() > 0)
092: root.setAttribute(HTML.STYLE_ATTR, style);
093: else
094: root.removeAttribute(HTML.STYLE_ATTR);
095: if (styleClass != null && styleClass.length() > 0)
096: root.setAttribute(HTML.CLASS_ATTR, styleClass);
097: else
098: root.removeAttribute(HTML.CLASS_ATTR);
099: Element orderField = domContext
100: .createElement(HTML.INPUT_ELEM);
101: String orderFieldId = getHiddenFieldName(facesContext,
102: uiComponent, INPUT_ID);
103: orderField.setAttribute(HTML.ID_ATTR, orderFieldId);
104: orderField.setAttribute(HTML.TYPE_ATTR,
105: HTML.INPUT_TYPE_HIDDEN);
106: orderField.setAttribute(HTML.NAME_ATTR, orderFieldId);
107: orderField.setAttribute(HTML.VALUE_ATTR, "");
108:
109: if (isChanged(facesContext)) {
110: // Force the re rendering of the entire component. This is due to a strange quick with positioned
111: // panel. When an element is moved in the same list then it container element moves with it
112: // When the update occurs the elements are replaced but because the containers have moved
113: // then the result looks the same. (But a refresh shows otherwise)
114: Node node = domContext.createTextNode("<!-- "
115: + (new Random().nextInt(1000)) + "-->");
116: root.appendChild(node);
117: }
118: root.appendChild(orderField);
119:
120: }
121:
122: Element root = (Element) domContext.getRootNode();
123: if (!Beans.isDesignTime()) {
124: DOMContext.removeChildrenByTagName(root, HTML.DIV_ELEM);
125: }
126: domContext.streamWrite(facesContext, uiComponent);
127: } catch (Exception e) {
128: log.error("Encode Begin", e);
129: }
130: }
131:
132: public void encodeEnd(FacesContext facesContext,
133: UIComponent uiComponent) throws IOException {
134: validateParameters(facesContext, uiComponent, null);
135: DOMContext domContext = DOMContext.getDOMContext(facesContext,
136: uiComponent);
137: String id = uiComponent.getClientId(facesContext);
138: PanelPositioned panelPositioned = (PanelPositioned) uiComponent;
139: String orderFieldId = getHiddenFieldName(facesContext,
140: uiComponent, INPUT_ID);
141: EffectsArguments ea = new EffectsArguments();
142: ea.add("tag", "div");
143: String o = panelPositioned.getConstraint();
144: if (o == null) {
145: ea.add("constraint", false);
146: } else {
147: ea.add("constraint", panelPositioned.getConstraint());
148: }
149: ea.add("dropOnEmpty", true);
150: ea.add("containment", false);
151: o = panelPositioned.getHandle();
152: if (o != null) {
153: ea.add("handle", o);
154: }
155: o = panelPositioned.getHoverclass();
156: if (o != null) {
157: ea.add("hoverclass", o);
158: }
159: o = panelPositioned.getOverlap();
160: if (o != null) {
161: ea.add("overlap", o);
162: }
163:
164: String updateCode = "function(){var o = Sortable.options('"
165: + id + "');" + "var s = o.serializeValue;" + "f = $('"
166: + orderFieldId + "');" + "f.value = s;" + "}";
167: ea.addFunction("onUpdate", updateCode);
168:
169: String call = "Sortable.create('" + id + "'" + ea.toString();
170:
171: JavascriptContext.addJavascriptCall(facesContext, call);
172: DOMContext.getDOMContext(facesContext, uiComponent).stepOver();
173: domContext.streamWrite(facesContext, uiComponent);
174:
175: }
176:
177: public void encodeChildren(FacesContext facesContext,
178: UIComponent uiComponent) throws IOException {
179: try {
180: validateParameters(facesContext, uiComponent, null);
181: DOMContext domContext = DOMContext.attachDOMContext(
182: facesContext, uiComponent);
183:
184: Element root = (Element) domContext.getRootNode();
185: UISeries series = (UISeries) uiComponent;
186: List seriesList = (List) series.getValue();
187: if (seriesList != null) {
188: if (log.isTraceEnabled()) {
189: for (int i = 0; i < seriesList.size(); i++) {
190: log.trace("Encode index[" + i + "] value ["
191: + seriesList.get(i) + "]");
192: }
193: }
194: Iterator cells = seriesList.iterator();
195: int index = 0;
196: PanelPositionedModel ppm = PanelPositionedModel
197: .resetInstance(facesContext, uiComponent);
198: while (cells.hasNext()) {
199: series.setRowIndex(index);
200: Object cell = cells.next();
201:
202: Iterator childs;
203: childs = uiComponent.getChildren().iterator();
204: while (childs.hasNext()) {
205: UIComponent nextChild = (UIComponent) childs
206: .next();
207: if (nextChild.isRendered()) {
208: domContext.setCursorParent(root);
209: domContext.streamWrite(facesContext,
210: uiComponent, root, root);
211: encodeParentAndChildren(facesContext,
212: nextChild);
213: String childId = nextChild
214: .getClientId(facesContext);
215:
216: ppm.setIndex(childId, index);
217: DnDCache.getInstance(facesContext, false)
218: .putPositionPanelValue(childId,
219: seriesList, index);
220: }
221: }
222: index++;
223: }
224: series.setRowIndex(-1);
225: }
226: // set the cursor here since nothing happens in encodeEnd
227: domContext.setCursorParent(root);
228: } catch (Exception e) {
229: log.error("Encode Children", e);
230: }
231: }
232:
233: public void decode(FacesContext context, UIComponent component) {
234:
235: try {
236: super .decode(context, component);
237:
238: if (component instanceof PanelPositioned) {
239: Map requestParameters = context.getExternalContext()
240: .getRequestParameterMap();
241: PanelPositioned uiSeries = (PanelPositioned) component;
242: PanelPositionedModel sortOrder = PanelPositionedModel
243: .getInstance(context, component);
244:
245: String baseName = getHiddenFieldName(context,
246: component, INPUT_ID);
247: Iterator names = requestParameters.keySet().iterator();
248: names = requestParameters.keySet().iterator();
249:
250: while (names.hasNext()) {
251: String name = (String) names.next();
252: int lastIndex = -1;
253: if (name.equals(baseName)) {
254: String value = (String) requestParameters
255: .get(name);
256:
257: StringTokenizer st = new StringTokenizer(value,
258: ";");
259: Object o = uiSeries.getValue();
260: List newList = new ArrayList();
261: List oldList = null;
262:
263: if (o instanceof List) {
264: oldList = (List) o;
265: } else {
266: throw new RuntimeException(
267: "PanelPositioned must have a java.util.List instance set as "
268: + "its value");
269: }
270: if (st.hasMoreTokens()) { // Don't do a thing if its blank
271: st.nextToken();//Last Token
272: String last = st.nextToken(); //Last ID dragged
273:
274: lastIndex = sortOrder.getIndex(last);
275:
276: String s = st.nextToken(); // Third token is always the keyword 'changed' used to indicate this filed has changed. (Even if its blank now)
277:
278: int currentIndex = 0;
279:
280: while (st.hasMoreTokens()) {
281: String id = st.nextToken();
282:
283: int index = sortOrder.getIndex(id);
284:
285: if (index != -1) {
286: Object obj = oldList.get(index);
287: if (log.isTraceEnabled()) {
288: log.trace("Moving ID[" + id
289: + "] Value ["
290: + obj.toString()
291: + "] from index ["
292: + index + "] to ["
293: + currentIndex + "]");
294: }
295: newList.add(obj);
296:
297: } else {
298: // Value is not from this list, check the cache
299: PanelPositionedValue ppv = DnDCache
300: .getInstance(context, false)
301: .getPositionedPanelValue(id);
302: if (ppv != null) {
303: List source = ppv
304: .getSourceList();
305: Object sourceValue = source
306: .get(ppv
307: .getValueIndex());
308: if (log.isTraceEnabled()) {
309: log
310: .trace("Added value ["
311: + sourceValue
312: + "]");
313: }
314: newList.add(sourceValue);
315:
316: } else {
317: throw new RuntimeException(
318: "Unable to find Value for ID["
319: + id + "]");
320: }
321:
322: }
323: currentIndex++;
324: }
325: int[] eventInfo = getEventInfo(oldList,
326: newList);
327: int event_type = eventInfo[0];
328: int newIndex = eventInfo[1];
329: int oldIndex = eventInfo[2];
330: if (event_type == PanelPositionedEvent.TYPE_MOVE) {
331:
332: if (lastIndex != oldIndex) {
333: int a = newIndex;
334: newIndex = oldIndex;
335: oldIndex = a;
336:
337: }
338: }
339:
340: if (log.isTraceEnabled()) {
341: for (int i = 0; i < newList.size(); i++) {
342:
343: log.trace("New Index [" + i
344: + "] Value ["
345: + newList.get(i) + "]");
346:
347: }
348: }
349: setChanged(context);
350:
351: if (uiSeries.getListener() != null) {
352: uiSeries
353: .queueEvent(new PanelPositionedEvent(
354: component, uiSeries
355: .getListener(),
356: event_type, newIndex,
357: oldIndex, oldList,
358: newList));
359: }
360:
361: }
362:
363: }
364: }
365: }
366: } catch (Exception e) {
367: log.error("Decode Error Positioned Panel ", e);
368: }
369: }
370:
371: private void setChanged(FacesContext context) {
372: context.getExternalContext().getRequestMap().put(
373: PanelPositionedRenderer.class.getName(), Boolean.TRUE);
374: }
375:
376: private boolean isChanged(FacesContext context) {
377: Boolean b = (Boolean) context.getExternalContext()
378: .getRequestMap().get(
379: PanelPositionedRenderer.class.getName());
380: if (b != null && b.booleanValue()) {
381: return true;
382: }
383: return false;
384: }
385:
386: private String getHiddenFieldName(FacesContext facesContext,
387: UIComponent uiComponent, String name) {
388: UIComponent form = findForm(uiComponent);
389: if (form == null) {
390: throw new NullPointerException(
391: "PanelPositioned must be contained withing an <ice:form>");
392: }
393: String formId = form.getClientId(facesContext);
394: String clientId = uiComponent.getClientId(facesContext);
395: return formId + NamingContainer.SEPARATOR_CHAR
396: + UIViewRoot.UNIQUE_ID_PREFIX + clientId + name;
397: }
398:
399: private int[] getEventInfo(List l1, List l2) {
400: int type;
401: int newIndex = -1;
402: int oldIndex = -1;
403: if (l1.size() > l2.size()) {
404: type = PanelPositionedEvent.TYPE_REMOVE;
405: } else if (l1.size() < l2.size()) {
406: type = PanelPositionedEvent.TYPE_ADD;
407: List l = l1;
408: l1 = l2;
409: l2 = l;
410: } else {
411: type = PanelPositionedEvent.TYPE_MOVE;
412: for (int i = 0; i < l1.size(); i++) {
413: if (l1.get(i) != l2.get(i)) {
414: if (newIndex == -1)
415: newIndex = i;
416: else
417: oldIndex = i;
418: }
419: }
420: }
421: if (type != PanelPositionedEvent.TYPE_MOVE) {
422: for (int i = 0; i < l1.size(); i++) {
423: // Find the odd one
424: if (!l2.contains(l1.get(i))) {
425: newIndex = i;
426: }
427: }
428: }
429: return new int[] { type, newIndex, oldIndex };
430: }
431:
432: }
|