Import from 1.9a8 tarball
[mozilla-extra.git] / extensions / xforms / nsXFormsItemSetElement.cpp
blobfd24ae64c161385f7db596ded3719a91b303c352
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is Mozilla XForms support.
17 * The Initial Developer of the Original Code is
18 * IBM Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 2004
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Brian Ryner <bryner@brianryner.com>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 #include "nsIXFormsSelectChild.h"
40 #include "nsXFormsStubElement.h"
41 #include "nsIDOMHTMLOptGroupElement.h"
42 #include "nsString.h"
43 #include "nsCOMPtr.h"
44 #include "nsIXTFElementWrapper.h"
45 #include "nsIDOMNodeList.h"
46 #include "nsIDocument.h"
47 #include "nsXFormsUtils.h"
48 #include "nsXFormsModelElement.h"
49 #include "nsIXFormsControlBase.h"
50 #include "nsIXFormsControl.h"
51 #include "nsIXFormsItemSetUIElement.h"
52 #include "nsXFormsDelegateStub.h"
53 #include "nsXFormsModelElement.h"
54 #include "nsIContent.h"
56 class nsXFormsItemSetElement : public nsXFormsDelegateStub,
57 public nsIXFormsSelectChild
59 public:
60 NS_DECL_ISUPPORTS_INHERITED
62 NS_IMETHOD OnCreated(nsIXTFElementWrapper *aWrapper);
64 // nsIXTFElement overrides
65 NS_IMETHOD WillChangeDocument(nsIDOMDocument *aNewDocument);
66 NS_IMETHOD DocumentChanged(nsIDOMDocument *aNewDocument);
67 NS_IMETHOD WillChangeParent(nsIDOMElement *aNewParent);
68 NS_IMETHOD ParentChanged(nsIDOMElement *aNewParent);
69 NS_IMETHOD ChildInserted(nsIDOMNode *aChild, PRUint32 aIndex);
70 NS_IMETHOD ChildAppended(nsIDOMNode *aChild);
71 NS_IMETHOD WillRemoveChild(PRUint32 aIndex);
72 NS_IMETHOD BeginAddingChildren();
73 NS_IMETHOD DoneAddingChildren();
75 // nsIXFormsControlBase overrides
76 NS_IMETHOD Bind(PRBool *aContextChanged);
77 NS_IMETHOD Refresh();
78 NS_IMETHOD GetUsesSingleNodeBinding(PRBool *aUsesSNB);
80 // nsIXFormsSelectChild
81 NS_DECL_NSIXFORMSSELECTCHILD
83 /** The standard notification flags set on nsIXTFElement */
84 const PRUint32 kItemSetNotificationMask;
86 /** Constructor */
87 nsXFormsItemSetElement() :
88 kItemSetNotificationMask(nsIXTFElement::NOTIFY_WILL_CHANGE_DOCUMENT |
89 nsIXTFElement::NOTIFY_DOCUMENT_CHANGED |
90 nsIXTFElement::NOTIFY_WILL_CHANGE_PARENT |
91 nsIXTFElement::NOTIFY_PARENT_CHANGED |
92 nsIXTFElement::NOTIFY_CHILD_INSERTED |
93 nsIXTFElement::NOTIFY_CHILD_APPENDED |
94 nsIXTFElement::NOTIFY_WILL_REMOVE_CHILD |
95 nsIXTFElement::NOTIFY_BEGIN_ADDING_CHILDREN)
96 {};
99 NS_IMPL_ISUPPORTS_INHERITED1(nsXFormsItemSetElement,
100 nsXFormsDelegateStub,
101 nsIXFormsSelectChild)
103 NS_IMETHODIMP
104 nsXFormsItemSetElement::OnCreated(nsIXTFElementWrapper *aWrapper)
106 nsresult rv = nsXFormsDelegateStub::OnCreated(aWrapper);
107 NS_ENSURE_SUCCESS(rv, rv);
109 aWrapper->SetNotificationMask(kItemSetNotificationMask);
110 return NS_OK;
113 NS_IMETHODIMP
114 nsXFormsItemSetElement::WillChangeDocument(nsIDOMDocument *aNewDocument)
116 SetRepeatState(eType_Unknown);
117 return NS_OK;
120 NS_IMETHODIMP
121 nsXFormsItemSetElement::DocumentChanged(nsIDOMDocument *aNewDocument)
123 nsXFormsStubElement::DocumentChanged(aNewDocument);
125 nsCOMPtr<nsIDOMNode> parent;
126 mElement->GetParentNode(getter_AddRefs(parent));
127 UpdateRepeatState(parent);
128 return NS_OK;
131 NS_IMETHODIMP
132 nsXFormsItemSetElement::WillChangeParent(nsIDOMElement *aNewParent)
134 SetRepeatState(eType_Unknown);
135 return NS_OK;
138 NS_IMETHODIMP
139 nsXFormsItemSetElement::ParentChanged(nsIDOMElement *aNewParent)
141 nsXFormsStubElement::ParentChanged(aNewParent);
143 UpdateRepeatState(aNewParent);
145 if (aNewParent)
146 Refresh();
148 return NS_OK;
151 NS_IMETHODIMP
152 nsXFormsItemSetElement::ChildInserted(nsIDOMNode *aChild, PRUint32 aIndex)
154 Refresh();
155 return NS_OK;
158 NS_IMETHODIMP
159 nsXFormsItemSetElement::ChildAppended(nsIDOMNode *aChild)
161 Refresh();
162 return NS_OK;
165 NS_IMETHODIMP
166 nsXFormsItemSetElement::WillRemoveChild(PRUint32 aIndex)
168 Refresh();
169 return NS_OK;
172 NS_IMETHODIMP
173 nsXFormsItemSetElement::BeginAddingChildren()
175 // Suppress child notifications until we're done getting children.
176 nsCOMPtr<nsIXTFElementWrapper> wrapper = do_QueryInterface(mElement);
177 NS_ASSERTION(wrapper, "huh? our element must be an xtf wrapper");
179 wrapper->SetNotificationMask(
180 kItemSetNotificationMask & ~(nsIXTFElement::NOTIFY_CHILD_INSERTED |
181 nsIXTFElement::NOTIFY_CHILD_APPENDED |
182 nsIXTFElement::NOTIFY_WILL_REMOVE_CHILD));
183 return NS_OK;
186 NS_IMETHODIMP
187 nsXFormsItemSetElement::DoneAddingChildren()
189 // Unsuppress child notifications until we're done getting children.
190 nsCOMPtr<nsIXTFElementWrapper> wrapper = do_QueryInterface(mElement);
191 NS_ASSERTION(wrapper, "huh? our element must be an xtf wrapper");
193 wrapper->SetNotificationMask(kItemSetNotificationMask);
195 // Walk our children and get their anonymous content.
196 Refresh();
197 return NS_OK;
200 // nsIXFormsSelectChild
202 NS_IMETHODIMP
203 nsXFormsItemSetElement::SelectItemByValue(const nsAString &aValue, nsIDOMNode **aSelected)
205 NS_ENSURE_ARG_POINTER(aSelected);
206 NS_ENSURE_STATE(mElement);
207 *aSelected = nsnull;
208 // nsIXFormsItemSetUIElement is implemented by the XBL binding.
209 nsCOMPtr<nsIXFormsItemSetUIElement> uiItemSet(do_QueryInterface(mElement));
210 NS_ENSURE_STATE(uiItemSet);
212 nsCOMPtr<nsIDOMElement> anonContent;
213 uiItemSet->GetAnonymousItemSetContent(getter_AddRefs(anonContent));
214 NS_ENSURE_STATE(anonContent);
216 nsCOMPtr<nsIDOMNode> child, tmp;
217 anonContent->GetFirstChild(getter_AddRefs(child));
218 // Trying to select the first possible (generated) \<item\> element.
219 while (child) {
220 nsCOMPtr<nsIXFormsSelectChild> selectChild(do_QueryInterface(child));
221 if (selectChild) {
222 selectChild->SelectItemByValue(aValue, aSelected);
223 if (*aSelected) {
224 return NS_OK;
227 tmp.swap(child);
228 tmp->GetNextSibling(getter_AddRefs(child));
230 return NS_OK;
233 NS_IMETHODIMP
234 nsXFormsItemSetElement::SelectItemByNode(nsIDOMNode *aNode,
235 nsIDOMNode **aSelected)
237 NS_ENSURE_ARG_POINTER(aSelected);
238 NS_ENSURE_STATE(mElement);
239 *aSelected = nsnull;
240 // nsIXFormsItemSetUIElement is implemented by the XBL binding.
241 nsCOMPtr<nsIXFormsItemSetUIElement> uiItemSet(do_QueryInterface(mElement));
242 NS_ENSURE_STATE(uiItemSet);
244 nsCOMPtr<nsIDOMElement> anonContent;
245 uiItemSet->GetAnonymousItemSetContent(getter_AddRefs(anonContent));
246 NS_ENSURE_STATE(anonContent);
248 nsCOMPtr<nsIDOMNode> child, tmp;
249 anonContent->GetFirstChild(getter_AddRefs(child));
250 // Trying to select the first possible (generated) \<item\> element.
251 while (child) {
252 nsCOMPtr<nsIXFormsSelectChild> selectChild(do_QueryInterface(child));
253 if (selectChild) {
254 selectChild->SelectItemByNode(aNode, aSelected);
255 if (*aSelected) {
256 return NS_OK;
259 tmp.swap(child);
260 tmp->GetNextSibling(getter_AddRefs(child));
262 return NS_OK;
265 NS_IMETHODIMP
266 nsXFormsItemSetElement::Bind(PRBool *aContextChanged)
268 NS_ENSURE_ARG(aContextChanged);
269 *aContextChanged = PR_FALSE;
270 return BindToModel();
273 NS_IMETHODIMP
274 nsXFormsItemSetElement::Refresh()
276 // We need to create item elements for each element referenced by the
277 // nodeset. Each of these items will create an anonymous HTML option element
278 // which will return from GetAnonymousNodes. We then clone our template
279 // content and insert the cloned content as children of the HTML option.
281 if (!nsXFormsUtils::IsDocumentReadyForBind(mElement)) {
282 // not ready to bind yet, defer
283 nsXFormsModelElement::DeferElementBind(this);
284 return NS_OK;
287 nsCOMPtr<nsIModelElementPrivate> model;
288 nsCOMPtr<nsIDOMXPathResult> result;
289 nsresult rv = ProcessNodeBinding(NS_LITERAL_STRING("nodeset"),
290 nsIDOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE,
291 getter_AddRefs(result),
292 getter_AddRefs(model));
294 if (NS_FAILED(rv) | !result | !model)
295 return rv;
297 nsCOMPtr<nsIDOMNode> node, templateNode, cloneNode, tmpNode;
298 nsCOMPtr<nsIDOMElement> itemNode, itemWrapperNode, contextContainer;
299 nsCOMPtr<nsIDOMNodeList> templateNodes;
300 mElement->GetChildNodes(getter_AddRefs(templateNodes));
301 PRUint32 templateNodeCount = 0;
302 if (templateNodes)
303 templateNodes->GetLength(&templateNodeCount);
305 nsCOMPtr<nsIContent> content(do_QueryInterface(mElement));
306 NS_ENSURE_STATE(content);
307 nsCOMPtr<nsIDocument> doc = content->GetCurrentDoc();
308 nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(doc));
309 NS_ENSURE_STATE(domDoc);
311 PRUint32 nodeCount;
312 result->GetSnapshotLength(&nodeCount);
314 nsCOMPtr<nsIDOMNode> parent, tmp;
315 mElement->GetParentNode(getter_AddRefs(parent));
317 while (parent) {
318 if (nsXFormsUtils::IsXFormsElement(parent, NS_LITERAL_STRING("select1")) ||
319 nsXFormsUtils::IsXFormsElement(parent, NS_LITERAL_STRING("select"))) {
320 break;
322 tmp.swap(parent);
323 tmp->GetParentNode(getter_AddRefs(parent));
326 nsCOMPtr<nsIXFormsItemSetUIElement> uiItemSet(do_QueryInterface(mElement));
327 nsCOMPtr<nsIDOMElement> anonContent;
328 if (uiItemSet) {
329 uiItemSet->GetAnonymousItemSetContent(getter_AddRefs(anonContent));
332 NS_ENSURE_STATE(anonContent);
334 nsCOMPtr<nsIDOMNode> childNode, nodeReturn;
335 while (NS_SUCCEEDED(anonContent->GetFirstChild(getter_AddRefs(childNode))) &&
336 childNode) {
337 anonContent->RemoveChild(childNode, getter_AddRefs(nodeReturn));
340 for (PRUint32 i = 0; i < nodeCount; ++i) {
341 result->SnapshotItem(i, getter_AddRefs(node));
342 NS_ASSERTION(node, "incorrect snapshot length");
344 rv = domDoc->CreateElementNS(NS_LITERAL_STRING(NS_NAMESPACE_XFORMS),
345 NS_LITERAL_STRING("item"),
346 getter_AddRefs(itemNode));
347 NS_ENSURE_SUCCESS(rv, rv);
349 anonContent->AppendChild(itemNode, getter_AddRefs(tmpNode));
351 nsCOMPtr<nsIXFormsContextControl> ctx(do_QueryInterface(itemNode));
352 if (ctx) {
353 ctx->SetContext(node, i + 1, nodeCount);
356 // Clone the template content under the item
357 for (PRUint32 j = 0; j < templateNodeCount; ++j) {
358 templateNodes->Item(j, getter_AddRefs(templateNode));
359 templateNode->CloneNode(PR_TRUE, getter_AddRefs(cloneNode));
360 itemNode->AppendChild(cloneNode, getter_AddRefs(templateNode));
365 // refresh parent so that it has a chance to reflect the changes we just made
366 if (parent) {
367 nsCOMPtr<nsIXFormsControlBase> control = do_QueryInterface(parent);
368 if (control) {
369 control->Refresh();
373 return NS_OK;
376 NS_IMETHODIMP
377 nsXFormsItemSetElement::GetUsesSingleNodeBinding(PRBool *aUsesSNB)
379 NS_ENSURE_ARG_POINTER(aUsesSNB);
380 *aUsesSNB = PR_FALSE;
381 return NS_OK;
385 NS_HIDDEN_(nsresult)
386 NS_NewXFormsItemSetElement(nsIXTFElement **aResult)
388 *aResult = new nsXFormsItemSetElement();
389 if (!*aResult)
390 return NS_ERROR_OUT_OF_MEMORY;
392 NS_ADDREF(*aResult);
393 return NS_OK;