Author: nbelaevski
Date: 2010-10-31 21:02:06 -0400 (Sun, 31 Oct 2010)
New Revision: 19809
Added:
sandbox/trunk/ui/tree-actual/api/src/main/java/org/richfaces/component/
sandbox/trunk/ui/tree-actual/api/src/main/java/org/richfaces/component/Selection.java
sandbox/trunk/ui/tree-actual/api/src/main/java/org/richfaces/event/TreeSelectionEvent.java
sandbox/trunk/ui/tree-actual/api/src/main/java/org/richfaces/event/TreeSelectionListener.java
sandbox/trunk/ui/tree-actual/ui/src/main/java/org/richfaces/component/SelectionImpl.java
Modified:
sandbox/trunk/ui/tree-actual/ui/src/main/java/org/richfaces/component/AbstractTree.java
sandbox/trunk/ui/tree-actual/ui/src/main/java/org/richfaces/renderkit/TreeRendererBase.java
sandbox/trunk/ui/tree-actual/ui/src/main/resources/META-INF/resources/org.richfaces/tree.js
sandbox/trunk/ui/tree-actual/ui/src/main/templates/tree.template.xml
Log:
https://jira.jboss.org/browse/RF-9315
Added:
sandbox/trunk/ui/tree-actual/api/src/main/java/org/richfaces/component/Selection.java
===================================================================
--- sandbox/trunk/ui/tree-actual/api/src/main/java/org/richfaces/component/Selection.java
(rev 0)
+++
sandbox/trunk/ui/tree-actual/api/src/main/java/org/richfaces/component/Selection.java 2010-11-01
01:02:06 UTC (rev 19809)
@@ -0,0 +1,43 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, Red Hat, Inc. and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.richfaces.component;
+
+import java.util.Iterator;
+
+/**
+ * @author Nick Belaevski
+ *
+ */
+public interface Selection {
+
+ public boolean addToSelection(Object rowKey);
+
+ public boolean isSelected(Object rowKey);
+
+ public Iterator<Object> getSelectionIterator();
+
+ public boolean removeFromSelection(Object rowKey);
+
+ public boolean isEmpty();
+
+ public void clear();
+}
Added:
sandbox/trunk/ui/tree-actual/api/src/main/java/org/richfaces/event/TreeSelectionEvent.java
===================================================================
---
sandbox/trunk/ui/tree-actual/api/src/main/java/org/richfaces/event/TreeSelectionEvent.java
(rev 0)
+++
sandbox/trunk/ui/tree-actual/api/src/main/java/org/richfaces/event/TreeSelectionEvent.java 2010-11-01
01:02:06 UTC (rev 19809)
@@ -0,0 +1,67 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, Red Hat, Inc. and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.richfaces.event;
+
+import java.util.Collection;
+import java.util.HashSet;
+
+import javax.faces.component.UIComponent;
+import javax.faces.event.FacesEvent;
+import javax.faces.event.FacesListener;
+
+/**
+ * @author Nick Belaevski
+ *
+ */
+public class TreeSelectionEvent extends FacesEvent {
+
+ private static final long serialVersionUID = 6292604445872458007L;
+
+ private Collection<Object> addedKeys = new HashSet<Object>();
+
+ private Collection<Object> removedKeys = new HashSet<Object>();
+
+ public TreeSelectionEvent(UIComponent component, Collection<Object> addedKeys,
Collection<Object> removedKeys) {
+ super(component);
+
+ this.addedKeys = addedKeys;
+ this.removedKeys = removedKeys;
+ }
+
+ @Override
+ public boolean isAppropriateListener(FacesListener listener) {
+ return listener instanceof TreeSelectionListener;
+ }
+
+ @Override
+ public void processListener(FacesListener listener) {
+ ((TreeSelectionListener) listener).processSelection(this);
+ }
+
+ public Collection<Object> getAddedKeys() {
+ return addedKeys;
+ }
+
+ public Collection<Object> getRemovedKeys() {
+ return removedKeys;
+ }
+}
Added:
sandbox/trunk/ui/tree-actual/api/src/main/java/org/richfaces/event/TreeSelectionListener.java
===================================================================
---
sandbox/trunk/ui/tree-actual/api/src/main/java/org/richfaces/event/TreeSelectionListener.java
(rev 0)
+++
sandbox/trunk/ui/tree-actual/api/src/main/java/org/richfaces/event/TreeSelectionListener.java 2010-11-01
01:02:06 UTC (rev 19809)
@@ -0,0 +1,34 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, Red Hat, Inc. and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.richfaces.event;
+
+import javax.faces.event.FacesListener;
+
+/**
+ * @author Nick Belaevski
+ *
+ */
+public interface TreeSelectionListener extends FacesListener {
+
+ public void processSelection(TreeSelectionEvent event);
+
+}
Modified:
sandbox/trunk/ui/tree-actual/ui/src/main/java/org/richfaces/component/AbstractTree.java
===================================================================
---
sandbox/trunk/ui/tree-actual/ui/src/main/java/org/richfaces/component/AbstractTree.java 2010-11-01
00:58:49 UTC (rev 19808)
+++
sandbox/trunk/ui/tree-actual/ui/src/main/java/org/richfaces/component/AbstractTree.java 2010-11-01
01:02:06 UTC (rev 19809)
@@ -32,6 +32,7 @@
import javax.faces.component.UIComponent;
import javax.faces.component.UpdateModelException;
import javax.faces.component.visit.VisitCallback;
+import javax.faces.component.visit.VisitContext;
import javax.faces.component.visit.VisitResult;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
@@ -55,7 +56,10 @@
import org.richfaces.context.ExtendedVisitContext;
import org.richfaces.context.ExtendedVisitContextMode;
import org.richfaces.convert.SequenceRowKeyConverter;
+import org.richfaces.event.TreeSelectionEvent;
+import org.richfaces.event.TreeSelectionListener;
import org.richfaces.event.TreeToggleEvent;
+import org.richfaces.event.TreeToggleListener;
import org.richfaces.model.TreeDataModelImpl;
import org.richfaces.renderkit.MetaComponentRenderer;
@@ -80,6 +84,8 @@
public static final String NODE_META_COMPONENT_ID = "node";
+ public static final String SELECTION_META_COMPONENT_ID = "selection";
+
private static final Predicate<UIComponent> RENDERED_UITREE_NODE = new
Predicate<UIComponent>() {
public boolean apply(UIComponent input) {
return (input instanceof AbstractTreeNode) && input.isRendered();
@@ -99,7 +105,7 @@
}
private enum PropertyKeys {
- expanded
+ expanded, selection
}
private transient TreeDecoderHelper treeDecoderHelper = new TreeDecoderHelper(this);
@@ -120,6 +126,29 @@
@Attribute(defaultValue = "SwitchType.DEFAULT")
public abstract SwitchType getToggleMode();
+ @Attribute(defaultValue = "SwitchType.client")
+ public abstract SwitchType getSelectionMode();
+
+ public Selection getSelection() {
+ Selection selection = (Selection) getStateHelper().eval(PropertyKeys.selection);
+ if (selection == null) {
+ selection = new SelectionImpl();
+
+ ValueExpression ve = getValueExpression(PropertyKeys.selection.toString());
+ if (ve != null) {
+ ve.setValue(getFacesContext().getELContext(), selection);
+ } else {
+ getStateHelper().put(PropertyKeys.selection, selection);
+ }
+ }
+
+ return selection;
+ }
+
+ public void setSelection(Selection selection) {
+ getStateHelper().put(PropertyKeys.selection, selection);
+ }
+
@SuppressWarnings("unchecked")
protected Boolean getLocalExpandedValue(FacesContext facesContext) {
Map<String, Object> stateMap = (Map<String, Object>)
getStateHelper().get(PropertyKeys.expanded);
@@ -253,6 +282,18 @@
} else {
setExpanded(newExpandedValue);
}
+ } else if (event instanceof TreeSelectionEvent) {
+ TreeSelectionEvent selectionEvent = (TreeSelectionEvent) event;
+
+ Selection selection = getSelection();
+
+ for (Object addedKey: selectionEvent.getAddedKeys()) {
+ selection.addToSelection(addedKey);
+ }
+
+ for (Object removedKey: selectionEvent.getRemovedKeys()) {
+ selection.removeFromSelection(removedKey);
+ }
}
}
@@ -270,6 +311,22 @@
return super.visitDataChildrenMetaComponents(extendedVisitContext, callback);
}
+ @Override
+ protected boolean visitFixedChildren(VisitContext visitContext, VisitCallback
callback) {
+ if (visitContext instanceof ExtendedVisitContext) {
+ ExtendedVisitContext extendedVisitContext = (ExtendedVisitContext)
visitContext;
+
+ if (ExtendedVisitContextMode.RENDER == extendedVisitContext.getVisitMode())
{
+ VisitResult result =
extendedVisitContext.invokeMetaComponentVisitCallback(this, callback,
SELECTION_META_COMPONENT_ID);
+ if (result != VisitResult.ACCEPT) {
+ return result == VisitResult.COMPLETE;
+ }
+ }
+ }
+
+ return super.visitFixedChildren(visitContext, callback);
+ }
+
void decodeMetaComponent(FacesContext context, String metaComponentId) {
((MetaComponentRenderer) getRenderer(context)).decodeMetaComponent(context, this,
metaComponentId);
}
@@ -279,7 +336,7 @@
}
public String resolveClientId(FacesContext facesContext, UIComponent
contextComponent, String metaComponentId) {
- if (NODE_META_COMPONENT_ID.equals(metaComponentId)) {
+ if (NODE_META_COMPONENT_ID.equals(metaComponentId) ||
SELECTION_META_COMPONENT_ID.equals(metaComponentId)) {
return getClientId(facesContext) +
MetaComponentResolver.META_COMPONENT_SEPARATOR_CHAR + metaComponentId;
}
@@ -296,4 +353,27 @@
return new TreeComponentState();
}
+ public void addToggleListener(TreeToggleListener listener) {
+ addFacesListener(listener);
+ }
+
+ public TreeToggleListener[] getTreeToggleListeners() {
+ return (TreeToggleListener[]) getFacesListeners(TreeToggleListener.class);
+ }
+
+ public void removeToggleListener(TreeToggleListener listener) {
+ removeFacesListener(listener);
+ }
+
+ public void addSelectionListener(TreeSelectionListener listener) {
+ addFacesListener(listener);
+ }
+
+ public TreeSelectionListener[] getSelectionListeners() {
+ return (TreeSelectionListener[]) getFacesListeners(TreeSelectionListener.class);
+ }
+
+ public void removeSelectionListener(TreeSelectionListener listener) {
+ removeFacesListener(listener);
+ }
}
Added:
sandbox/trunk/ui/tree-actual/ui/src/main/java/org/richfaces/component/SelectionImpl.java
===================================================================
---
sandbox/trunk/ui/tree-actual/ui/src/main/java/org/richfaces/component/SelectionImpl.java
(rev 0)
+++
sandbox/trunk/ui/tree-actual/ui/src/main/java/org/richfaces/component/SelectionImpl.java 2010-11-01
01:02:06 UTC (rev 19809)
@@ -0,0 +1,65 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, Red Hat, Inc. and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.richfaces.component;
+
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import com.google.common.collect.Iterators;
+
+/**
+ * @author Nick Belaevski
+ *
+ */
+public class SelectionImpl implements Selection, Serializable {
+
+ private static final long serialVersionUID = 7738700662652136887L;
+
+ private Set<Object> selectedKeys = new HashSet<Object>();
+
+ public boolean addToSelection(Object rowKey) {
+ return selectedKeys.add(rowKey);
+ }
+
+ public boolean isSelected(Object rowKey) {
+ return selectedKeys.contains(rowKey);
+ }
+
+ public Iterator<Object> getSelectionIterator() {
+ return Iterators.unmodifiableIterator(selectedKeys.iterator());
+ }
+
+ public boolean removeFromSelection(Object rowKey) {
+ return selectedKeys.remove(rowKey);
+ }
+
+ public boolean isEmpty() {
+ return selectedKeys.isEmpty();
+ }
+
+ public void clear() {
+ selectedKeys = new HashSet<Object>();
+ }
+
+}
Modified:
sandbox/trunk/ui/tree-actual/ui/src/main/java/org/richfaces/renderkit/TreeRendererBase.java
===================================================================
---
sandbox/trunk/ui/tree-actual/ui/src/main/java/org/richfaces/renderkit/TreeRendererBase.java 2010-11-01
00:58:49 UTC (rev 19808)
+++
sandbox/trunk/ui/tree-actual/ui/src/main/java/org/richfaces/renderkit/TreeRendererBase.java 2010-11-01
01:02:06 UTC (rev 19809)
@@ -22,30 +22,41 @@
package org.richfaces.renderkit;
import static org.richfaces.component.AbstractTree.NODE_META_COMPONENT_ID;
+import static org.richfaces.component.AbstractTree.SELECTION_META_COMPONENT_ID;
import static org.richfaces.renderkit.util.AjaxRendererUtils.AJAX_FUNCTION_NAME;
import static org.richfaces.renderkit.util.AjaxRendererUtils.buildAjaxFunction;
import static org.richfaces.renderkit.util.AjaxRendererUtils.buildEventOptions;
import java.io.IOException;
+import java.util.HashSet;
+import java.util.Iterator;
import java.util.Map;
+import java.util.Set;
import javax.faces.component.UIComponent;
import javax.faces.component.UINamingContainer;
import javax.faces.context.FacesContext;
+import javax.faces.context.PartialResponseWriter;
import javax.faces.context.PartialViewContext;
+import javax.faces.context.ResponseWriter;
import org.ajax4jsf.javascript.JSFunction;
import org.ajax4jsf.javascript.JSReference;
import org.richfaces.component.AbstractTree;
import org.richfaces.component.AbstractTreeNode;
import org.richfaces.component.MetaComponentResolver;
+import org.richfaces.component.Selection;
import org.richfaces.component.SwitchType;
import org.richfaces.component.TreeDecoderHelper;
+import org.richfaces.event.TreeSelectionEvent;
import org.richfaces.event.TreeToggleEvent;
import org.richfaces.log.Logger;
import org.richfaces.log.RichfacesLogger;
+import com.google.common.base.Strings;
+import com.google.common.collect.Iterators;
+
/**
* @author Nick Belaevski
*
@@ -53,22 +64,24 @@
public abstract class TreeRendererBase extends RendererBase implements
MetaComponentRenderer {
static final Logger LOGGER = RichfacesLogger.RENDERKIT.getLogger();
-
+
private static final JSReference TOGGLE_PARAMS = new
JSReference("toggleParams");
private static final JSReference TOGGLE_SOURCE = new
JSReference("toggleSource");
private static final String NEW_NODE_TOGGLE_STATE =
"__NEW_NODE_TOGGLE_STATE";
+ private static final String SELECTION_STATE = "__SELECTION_STATE";
+
enum NodeState {
expanded("rf-tr-nd-exp", "rf-trn-hnd-exp",
"rf-trn-ico-nd"),
collapsed("rf-tr-nd-colps", "rf-trn-hnd-colps",
"rf-trn-ico-nd"),
leaf("rf-tr-nd-lf", "rf-trn-hnd-lf",
"rf-trn-ico-lf");
-
+
private String nodeClass;
-
+
private String handleClass;
-
+
private String iconClass;
private NodeState(String nodeClass, String handleClass, String iconClass) {
@@ -76,11 +89,11 @@
this.handleClass = handleClass;
this.iconClass = iconClass;
}
-
+
public String getNodeClass() {
return nodeClass;
}
-
+
public String getHandleClass() {
return handleClass;
}
@@ -88,19 +101,19 @@
public String getIconClass() {
return iconClass;
}
-
+
}
-
+
static final class QueuedData {
-
+
private Object rowKey;
private boolean lastNode;
private boolean expanded;
-
+
private boolean encoded;
-
+
public QueuedData(Object rowKey, boolean lastNode, boolean expanded) {
this.rowKey = rowKey;
this.lastNode = lastNode;
@@ -110,11 +123,11 @@
public void setEncoded(boolean encoded) {
this.encoded = encoded;
}
-
+
public boolean isEncoded() {
return encoded;
}
-
+
public Object getRowKey() {
return rowKey;
}
@@ -122,78 +135,190 @@
public boolean isLastNode() {
return lastNode;
}
-
+
public boolean isExpanded() {
return expanded;
}
}
-
+
public void encodeTree(FacesContext context, UIComponent component) throws
IOException {
AbstractTree tree = (AbstractTree) component;
new TreeEncoderFull(context, tree).encode();
}
-
+
protected String getDecoderHelperId(FacesContext facesContext) {
return UINamingContainer.getSeparatorChar(facesContext) +
TreeDecoderHelper.HELPER_ID;
}
-
+
protected String getAjaxToggler(FacesContext context, UIComponent component) {
AbstractTree tree = (AbstractTree) component;
-
+
SwitchType toggleMode = tree.getToggleMode();
if (toggleMode != SwitchType.ajax) {
return null;
}
-
+
JSFunction ajaxFunction = buildAjaxFunction(context, component,
AJAX_FUNCTION_NAME);
AjaxEventOptions eventOptions = buildEventOptions(context, component);
eventOptions.setAjaxComponent(TOGGLE_SOURCE);
eventOptions.setClientParameters(TOGGLE_PARAMS);
-
+
if (!eventOptions.isEmpty()) {
ajaxFunction.addParameter(eventOptions);
}
return ajaxFunction.toScript();
}
+ protected void encodeSelectionStateInput(FacesContext context, UIComponent component)
throws IOException {
+ ResponseWriter writer = context.getResponseWriter();
+ writer.startElement(HtmlConstants.INPUT_ELEM, component);
+ writer.writeAttribute(HtmlConstants.TYPE_ATTR, "hidden", null);
+ String selectionStateInputId = getSelectionStateInputId(context, component);
+ writer.writeAttribute(HtmlConstants.NAME_ATTRIBUTE, selectionStateInputId,
null);
+ writer.writeAttribute(HtmlConstants.ID_ATTRIBUTE, selectionStateInputId, null);
+ writer.writeAttribute(HtmlConstants.CLASS_ATTRIBUTE, "rf-tr-sel-inp",
null);
+
+ String selectedNodeId = "";
+ AbstractTree tree = (AbstractTree) component;
+
+ Iterator<Object> selectedKeys =
tree.getSelection().getSelectionIterator();
+
+ if (selectedKeys.hasNext()) {
+ Object selectionKey = selectedKeys.next();
+ Object initialKey = tree.getRowKey();
+ try {
+ tree.setRowKey(context, selectionKey);
+ if (tree.isRowAvailable()) {
+ selectedNodeId = tree.getClientId(context);
+ }
+ } finally {
+ try {
+ tree.setRowKey(context, initialKey);
+ } catch (Exception e) {
+ LOGGER.error(e.getMessage(), e);
+ }
+ }
+ }
+
+ if (selectedKeys.hasNext()) {
+ //TODO - better message
+ throw new IllegalArgumentException("Selection object should not contain
more than selected keys!");
+ }
+
+ writer.writeAttribute(HtmlConstants.VALUE_ATTRIBUTE, selectedNodeId, null);
+
+ writer.endElement(HtmlConstants.INPUT_ELEM);
+ }
+
+ protected String getSelectionStateInputId(FacesContext context, UIComponent
component) {
+ return component.getClientId(context) + SELECTION_STATE;
+ }
+
+ protected SwitchType getSelectionMode(FacesContext context, UIComponent component) {
+ AbstractTree tree = (AbstractTree) component;
+
+ SwitchType selectionMode = tree.getSelectionMode();
+ if (selectionMode != null && selectionMode != SwitchType.ajax &&
selectionMode != SwitchType.client) {
+ //TODO - better message
+ throw new IllegalArgumentException(String.valueOf(selectionMode));
+ }
+
+ return selectionMode;
+ }
+
+ protected String getNamingContainerSeparatorChar(FacesContext context) {
+ return String.valueOf(UINamingContainer.getSeparatorChar(context));
+ }
+
/* (non-Javadoc)
* @see
org.richfaces.renderkit.MetaComponentRenderer#encodeMetaComponent(javax.faces.context.FacesContext,
javax.faces.component.UIComponent, java.lang.String)
*/
public void encodeMetaComponent(FacesContext context, UIComponent component, String
metaComponentId)
throws IOException {
-
+
if (NODE_META_COMPONENT_ID.equals(metaComponentId)) {
AbstractTree tree = (AbstractTree) component;
new TreeEncoderPartial(context, tree).encode();
+ } else if (SELECTION_META_COMPONENT_ID.equals(metaComponentId)) {
+ PartialResponseWriter writer =
context.getPartialViewContext().getPartialResponseWriter();
+
+ writer.startUpdate(getSelectionStateInputId(context, component));
+ encodeSelectionStateInput(context, component);
+ writer.endUpdate();
+
+ writer.startEval();
+
+ JSFunction function = new JSFunction("RichFaces.$",
component.getClientId(context));
+ writer.write(function.toScript() + ".__updateSelection();");
+
+ writer.endEval();
} else {
throw new IllegalArgumentException(metaComponentId);
}
-
+
// TODO Auto-generated method stub
-
+
}
-
+
public void decodeMetaComponent(FacesContext context, UIComponent component, String
metaComponentId) {
if (NODE_META_COMPONENT_ID.equals(metaComponentId)) {
final Map<String, String> map =
context.getExternalContext().getRequestParameterMap();
String newToggleState = map.get(component.getClientId(context) +
NEW_NODE_TOGGLE_STATE);
if (newToggleState != null) {
-
+
AbstractTree tree = (AbstractTree) component;
AbstractTreeNode treeNode = tree.getTreeNodeComponent();
boolean expanded = Boolean.valueOf(newToggleState);
if (tree.isExpanded() ^ expanded) {
new TreeToggleEvent(treeNode, expanded).queue();
}
-
+
PartialViewContext pvc = context.getPartialViewContext();
if (pvc.isAjaxRequest()) {
- pvc.getRenderIds().add(tree.getClientId(context) +
MetaComponentResolver.META_COMPONENT_SEPARATOR_CHAR + "node");
+ pvc.getRenderIds().add(tree.getClientId(context) +
MetaComponentResolver.META_COMPONENT_SEPARATOR_CHAR
+ + AbstractTree.NODE_META_COMPONENT_ID);
}
}
}
}
-
+
+ @Override
+ public void decode(FacesContext context, UIComponent component) {
+ super.decode(context, component);
+
+ Map<String, String> map =
context.getExternalContext().getRequestParameterMap();
+ String selectedNode = map.get(getSelectionStateInputId(context, component));
+ AbstractTree tree = (AbstractTree) component;
+
+ Object selectionRowKey = null;
+
+ if (!Strings.isNullOrEmpty(selectedNode)) {
+ String selectionRowKeyString =
selectedNode.substring(component.getClientId(context).length() + 1 /* naming container
separator char */);
+ selectionRowKey = tree.getRowKeyConverter().getAsObject(context, component,
selectionRowKeyString);
+ }
+
+ Selection selection = tree.getSelection();
+
+ Set<Object> addedKeys = new HashSet<Object>(2);
+ Set<Object> removedKeys = new HashSet<Object>(2);
+
+ if (selectionRowKey == null) {
+ Iterators.addAll(removedKeys, selection.getSelectionIterator());
+ } else if (!selection.isSelected(selectionRowKey)) {
+ addedKeys.add(selectionRowKey);
+ Iterators.addAll(removedKeys, selection.getSelectionIterator());
+ }
+
+ if (!removedKeys.isEmpty() || !addedKeys.isEmpty()) {
+ new TreeSelectionEvent(component, addedKeys, removedKeys).queue();
+ }
+
+ PartialViewContext pvc = context.getPartialViewContext();
+ if (pvc.isAjaxRequest()) {
+ pvc.getRenderIds().add(tree.getClientId(context) +
MetaComponentResolver.META_COMPONENT_SEPARATOR_CHAR
+ + AbstractTree.SELECTION_META_COMPONENT_ID);
+ }
+ }
}
Modified:
sandbox/trunk/ui/tree-actual/ui/src/main/resources/META-INF/resources/org.richfaces/tree.js
===================================================================
---
sandbox/trunk/ui/tree-actual/ui/src/main/resources/META-INF/resources/org.richfaces/tree.js 2010-11-01
00:58:49 UTC (rev 19808)
+++
sandbox/trunk/ui/tree-actual/ui/src/main/resources/META-INF/resources/org.richfaces/tree.js 2010-11-01
01:02:06 UTC (rev 19809)
@@ -21,15 +21,12 @@
*/
(function($, richfaces) {
- var __initializeChildNodes = function(elt) {
- var _this = this;
- $(elt).children(".rf-tr-nd").each(function() {
- _this.addChild(new richfaces.ui.TreeNode(this));
- });
- }
+ var DECODER_HELPER_ID = "__treeDecoderHelper";
+
+ var NEW_NODE_TOGGLE_STATE = "__NEW_NODE_TOGGLE_STATE";
- var NEW_NODE_TOGGLE_STATE = "__NEW_NODE_TOGGLE_STATE"
-
+ var SELECTION_STATE = "__SELECTION_STATE";
+
var TREE_CLASSES = ["rf-tr-nd-colps", "rf-tr-nd-exp"];
var TREE_HANDLE_CLASSES = ["rf-trn-hnd-colps", "rf-trn-hnd-exp"];
@@ -42,17 +39,45 @@
init: function (id) {
this.id = id;
+ this.elt = $(this.attachToDom());
this.__children = new Array();
- this.elt = $(this.attachToDom());
+ this.__initializeChildren();
+ },
+
+ destroy: function() {
+ if (this.isSelected()) {
+ this.getTree().__resetSelection();
+ }
- this.handler = this.elt.find(" > .rf-trn:first > .rf-trn-hnd:first");
- this.handler.click($.proxy(this.toggle, this));
+ if (this.parent) {
+ this.parent.removeChild(this);
+ this.parent = null;
+ }
- __initializeChildNodes.call(this, this.elt[0]);
+ this.__clientToggleStateInput = null;
+
+ this.__clearChildren();
+
+ this.elt = null;
},
+
+ __initializeChildren: function() {
+ var _this = this;
+ this.elt.children(".rf-tr-nd").each(function() {
+ _this.addChild(new richfaces.ui.TreeNode(this));
+ });
+ },
+ __getHandle: function() {
+ return this.elt.find(" > .rf-trn:first > .rf-trn-hnd:first");
+ },
+
+ __getContent: function() {
+ return this.elt.find(" > .rf-trn:first > .rf-trn-cnt:first");
+ },
+
getParent: function() {
return this.__parent;
},
@@ -87,7 +112,7 @@
}
},
- clearChildren: function() {
+ __clearChildren: function() {
for (var i = 0; i < this.__children.length; i++) {
this.__children[i].setParent(undefined);
}
@@ -96,15 +121,15 @@
},
isExpanded: function() {
- return !this.isLeaf() && this.handler.hasClass("rf-trn-hnd-exp");
+ return !this.isLeaf() &&
this.__getHandle().hasClass("rf-trn-hnd-exp");
},
isCollapsed: function() {
- return !this.isLeaf() && this.handler.hasClass("rf-trn-hnd-colps");
+ return !this.isLeaf() &&
this.__getHandle().hasClass("rf-trn-hnd-colps");
},
isLeaf: function() {
- return this.handler.hasClass("rf-trn-hnd-lf");
+ return this.__getHandle().hasClass("rf-trn-hnd-lf");
},
toggle: function() {
@@ -120,33 +145,34 @@
},
__updateClientToggleStateInput: function(newState) {
- if (!this.__clientStateInput) {
- this.__clientStateInput = $("<input type='hidden'
/>").appendTo(this.elt)
+ if (!this.__clientToggleStateInput) {
+ this.__clientToggleStateInput = $("<input type='hidden'
/>").appendTo(this.elt)
.attr({name: this.elt.attr("id") + NEW_NODE_TOGGLE_STATE});
}
- this.__clientStateInput.val(newState.toString());
+ this.__clientToggleStateInput.val(newState.toString());
},
__changeToggleState: function(newState) {
if (!this.isLeaf()) {
- var tree = this.getTree();
-
- switch (tree.getToggleMode()) {
- case 'client':
- this.elt.addClass(TREE_CLASSES[newState ? 1 :
0]).removeClass(TREE_CLASSES[!newState ? 1 : 0]);
- this.handler.addClass(TREE_HANDLE_CLASSES[newState ? 1 :
0]).removeClass(TREE_HANDLE_CLASSES[!newState ? 1 : 0]);
- this.__updateClientToggleStateInput(newState);
- break;
+ if (newState ^ this.isExpanded()) {
+ var tree = this.getTree();
- case 'ajax':
- case 'server':
- //TODO - event?
- tree.sendToggleRequest(null, richfaces.getDomElement(this.id).id, newState);
- break;
+ switch (tree.getToggleMode()) {
+ case 'client':
+ this.elt.addClass(TREE_CLASSES[newState ? 1 :
0]).removeClass(TREE_CLASSES[!newState ? 1 : 0]);
+ this.__getHandle().addClass(TREE_HANDLE_CLASSES[newState ? 1 :
0]).removeClass(TREE_HANDLE_CLASSES[!newState ? 1 : 0]);
+ this.__updateClientToggleStateInput(newState);
+ break;
+
+ case 'ajax':
+ case 'server':
+ //TODO - event?
+ tree.__sendToggleRequest(null, this.getId(), newState);
+ break;
+ }
}
-
}
},
@@ -158,26 +184,29 @@
this.__changeToggleState(true);
},
- getTree: function() {
- var component = this;
- while (component && component.name != "Tree") {
- component = component.getParent();
+ __setSelected: function(value) {
+ var content = this.__getContent();
+ if (value) {
+ content.addClass("rf-tr-nd-sel");
+ } else {
+ content.removeClass("rf-tr-nd-sel");
}
- return component;
+
+ this.__selected = value;
},
- destroy: function() {
- this.__clientStateInput = null;
-
- if (this.parent) {
- this.parent.removeChild(this);
- }
-
- this.clearChildren();
-
- this.elt = null;
- this.handler = null;
+ isSelected: function() {
+ return this.__selected;
+ },
+
+ getTree: function() {
+ return this.getParent().getTree();
+ },
+
+ getId: function() {
+ return richfaces.getDomElement(this.id).id;
}
+
});
richfaces.ui.TreeNode.initNodeByAjax = function(nodeId) {
@@ -192,33 +221,84 @@
var idx = node.prevAll(".rf-tr-nd").length;
var parentNode = richfaces.$(parent[0]);
- parentNode.addChild(new richfaces.ui.TreeNode(node[0]), idx);
+ var newChild = new richfaces.ui.TreeNode(node[0]);
+ parentNode.addChild(newChild, idx);
+ parentNode.getTree().__updateSelection();
};
- var decoderHelperId;
+ var findTree = function(elt) {
+ return richfaces.$($(elt).closest(".rf-tr"));
+ };
+ var findTreeNode = function(elt) {
+ return richfaces.$($(elt).closest(".rf-tr-nd"));
+ };
+
+ var isEventForAnotherTree = function(tree, elt) {
+ return tree != findTree(elt);
+ };
+
+ var ncSepChar;
+
richfaces.ui.Tree = richfaces.ui.TreeNode.extendClass({
name: "Tree",
init: function (id, options) {
- this.$super.init.call(this, id, options);
+ this.$super.init.call(this, id);
this.__toggleMode = options.toggleMode || 'ajax';
- this.__selectionMode = options.selectionMode || 'ajax';
+ this.__selectionMode = options.selectionMode || 'client';
if (options.ajaxToggler) {
this.__ajaxToggler = new Function("event", "toggleSource",
"toggleParams", options.ajaxToggler);
}
+
+ this.__selectionInput = $(" > .rf-tr-sel-inp", this.elt);
+
+ this.elt.delegate(".rf-trn-hnd", "click", this,
this.__itemHandleClicked);
+ this.elt.delegate(".rf-trn-cnt", "mousedown", this,
this.__itemContentClicked);
+
+ this.__updateSelection();
},
destroy: function() {
this.$super.destroy();
+
+ this.elt.undelegate(".rf-trn-hnd", "click",
this.__itemHandleClicked);
+ this.elt.undelegate(".rf-trn-cnt", "mousedown",
this.__itemContentClicked);
+ this.__itemContentClickedHandler = null;
+ this.__selectionInput = null;
this.__ajaxToggler = null;
},
- sendToggleRequest: function(event, toggleSource, newNodeState) {
+ __itemHandleClicked: function(event) {
+ var theTree = event.data;
+ if (isEventForAnotherTree(theTree, this)) {
+ return;
+ }
+
+ var treeNode = findTreeNode(this);
+ treeNode.toggle();
+ },
+
+ __itemContentClicked: function(event) {
+ var theTree = event.data;
+ if (isEventForAnotherTree(theTree, this)) {
+ return;
+ }
+
+ var treeNode = findTreeNode(this);
+
+ if (event.ctrlKey) {
+ theTree.__toggleSelection(treeNode);
+ } else {
+ theTree.__addToSelection(treeNode);
+ }
+ },
+
+ __sendToggleRequest: function(event, toggleSource, newNodeState) {
var clientParams = {};
clientParams[toggleSource + NEW_NODE_TOGGLE_STATE] = newNodeState;
@@ -226,7 +306,7 @@
var form = $(richfaces.getDomElement(this.id)).closest('form');
richfaces.submitForm(form, clientParams);
} else {
- this.__ajaxToggler(event, toggleSource + decoderHelperId, clientParams);
+ this.__ajaxToggler(event, toggleSource + ncSepChar + DECODER_HELPER_ID,
clientParams);
}
},
@@ -236,11 +316,72 @@
getSelectionMode: function() {
return this.__selectionMode;
+ },
+
+ getTree: function() {
+ return this;
+ },
+
+ __bindFocusHandler: function(elt) {
+ elt.mousedown(this.__itemContentClickedHandler);
+ },
+
+ __isSelected: function(node) {
+ return this.__selectedNodeId == node.getId();
+ },
+
+ __handleSelectionChange: function() {
+ if (this.__selectionMode == 'client') {
+ this.__updateSelection();
+ } else {
+ this.__ajaxToggler(null, this.id);
+ }
+ },
+
+ __toggleSelection: function(node) {
+ if (this.__isSelected(node)) {
+ this.__selectionInput.val("");
+ } else {
+ this.__selectionInput.val(node.getId());
+ }
+
+ this.__handleSelectionChange();
+ },
+
+ __addToSelection: function(node) {
+ this.__selectionInput.val(node.getId());
+
+ this.__handleSelectionChange();
+ },
+
+ __resetSelection: function() {
+ this.__selectedNodeId = null;
+ this.__selectionInput.val("");
+ },
+
+ __updateSelection: function() {
+ var oldSelection = this.__selectedNodeId;
+ if (oldSelection) {
+ var oldSelectionNode = richfaces.$(oldSelection);
+ if (oldSelectionNode) {
+ oldSelectionNode.__setSelected(false);
+ }
+ }
+
+ var nodeId = this.__selectionInput.val();
+
+ var newSelectionNode = richfaces.$(nodeId);
+ if (newSelectionNode) {
+ newSelectionNode.__setSelected(true);
+ this.__selectedNodeId = nodeId;
+ } else {
+ this.__resetSelection();
+ }
}
});
- richfaces.ui.Tree.setDecoderHelperId = function(id) {
- decoderHelperId = id;
+ richfaces.ui.Tree.setNamingContainerSeparatorChar = function(s) {
+ ncSepChar = s.charAt(0);
};
}(jQuery, RichFaces));
\ No newline at end of file
Modified: sandbox/trunk/ui/tree-actual/ui/src/main/templates/tree.template.xml
===================================================================
--- sandbox/trunk/ui/tree-actual/ui/src/main/templates/tree.template.xml 2010-11-01
00:58:49 UTC (rev 19808)
+++ sandbox/trunk/ui/tree-actual/ui/src/main/templates/tree.template.xml 2010-11-01
01:02:06 UTC (rev 19809)
@@ -24,11 +24,14 @@
<cdk:body>
<cdk:call expression="encodeTree(facesContext, component)"
/>
</cdk:body>
+
+ <cdk:call expression="encodeSelectionStateInput(facesContext,
component)" />
<script type="text/javascript">
-
RichFaces.ui.Tree.setDecoderHelperId(#{toScriptArgs(getDecoderHelperId(facesContext))});
+
RichFaces.ui.Tree.setNamingContainerSeparatorChar(#{toScriptArgs(getNamingContainerSeparatorChar(facesContext))});
<cdk:scriptObject name="options">
- <cdk:scriptOption attributes="toggleMode selectionMode"
defaultValue="SwitchType.DEFAULT" />
+ <cdk:scriptOption attributes="toggleMode"
defaultValue="SwitchType.DEFAULT" />
+ <cdk:scriptOption name="selectionMode"
value="#{getSelectionMode(facesContext, component)}"
defaultValue="SwitchType.client" />
<cdk:scriptOption name="ajaxToggler"
value="#{getAjaxToggler(facesContext, component)}" />
</cdk:scriptObject>