Back to index

lightning-sunbird  0.9+nobinonly
nsXFormsItemSetElement.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla XForms support.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * IBM Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 2004
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *  Brian Ryner <bryner@brianryner.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsIXFormsSelectChild.h"
00040 #include "nsXFormsStubElement.h"
00041 #include "nsIDOMHTMLOptGroupElement.h"
00042 #include "nsString.h"
00043 #include "nsCOMPtr.h"
00044 #include "nsIXTFBindableElementWrapper.h"
00045 #include "nsIDOMNodeList.h"
00046 #include "nsIDocument.h"
00047 #include "nsXFormsUtils.h"
00048 #include "nsXFormsModelElement.h"
00049 #include "nsIXFormsControlBase.h"
00050 #include "nsIXFormsControl.h"
00051 #include "nsIXFormsItemSetUIElement.h"
00052 #include "nsXFormsDelegateStub.h"
00053 #include "nsXFormsModelElement.h"
00054 #include "nsIContent.h"
00055 
00056 class nsXFormsItemSetElement : public nsXFormsDelegateStub,
00057                                public nsIXFormsSelectChild
00058 {
00059 public:
00060   NS_DECL_ISUPPORTS_INHERITED
00061 
00062   NS_IMETHOD OnCreated(nsIXTFBindableElementWrapper *aWrapper);
00063 
00064   // nsIXTFElement overrides
00065   NS_IMETHOD WillChangeDocument(nsIDOMDocument *aNewDocument);
00066   NS_IMETHOD DocumentChanged(nsIDOMDocument *aNewDocument);
00067   NS_IMETHOD WillChangeParent(nsIDOMElement *aNewParent);
00068   NS_IMETHOD ParentChanged(nsIDOMElement *aNewParent);
00069   NS_IMETHOD ChildInserted(nsIDOMNode *aChild, PRUint32 aIndex);
00070   NS_IMETHOD ChildAppended(nsIDOMNode *aChild);
00071   NS_IMETHOD WillRemoveChild(PRUint32 aIndex);
00072   NS_IMETHOD BeginAddingChildren();
00073   NS_IMETHOD DoneAddingChildren();
00074 
00075   // nsIXFormsControlBase overrides
00076   NS_IMETHOD Bind(PRBool *aContextChanged);
00077   NS_IMETHOD Refresh();
00078   NS_IMETHOD GetUsesSingleNodeBinding(PRBool *aUsesSNB);
00079 
00080   // nsIXFormsSelectChild
00081   NS_DECL_NSIXFORMSSELECTCHILD
00082 
00084   const PRUint32 kItemSetNotificationMask;
00085 
00087   nsXFormsItemSetElement() :
00088     kItemSetNotificationMask(nsIXTFElement::NOTIFY_WILL_CHANGE_DOCUMENT |
00089                              nsIXTFElement::NOTIFY_DOCUMENT_CHANGED |
00090                              nsIXTFElement::NOTIFY_WILL_CHANGE_PARENT |
00091                              nsIXTFElement::NOTIFY_PARENT_CHANGED |
00092                              nsIXTFElement::NOTIFY_CHILD_INSERTED |
00093                              nsIXTFElement::NOTIFY_CHILD_APPENDED |
00094                              nsIXTFElement::NOTIFY_WILL_REMOVE_CHILD |
00095                              nsIXTFElement::NOTIFY_BEGIN_ADDING_CHILDREN)
00096     {};
00097 };
00098 
00099 NS_IMPL_ISUPPORTS_INHERITED1(nsXFormsItemSetElement,
00100                              nsXFormsDelegateStub,
00101                              nsIXFormsSelectChild)
00102 
00103 NS_IMETHODIMP
00104 nsXFormsItemSetElement::OnCreated(nsIXTFBindableElementWrapper *aWrapper)
00105 {
00106   nsresult rv = nsXFormsDelegateStub::OnCreated(aWrapper);
00107   NS_ENSURE_SUCCESS(rv, rv);
00108 
00109   aWrapper->SetNotificationMask(nsIXTFElement::NOTIFY_PARENT_CHANGED |
00110                                 nsIXTFElement::NOTIFY_CHILD_INSERTED |
00111                                 nsIXTFElement::NOTIFY_CHILD_APPENDED |
00112                                 nsIXTFElement::NOTIFY_WILL_REMOVE_CHILD |
00113                                 nsIXTFElement::NOTIFY_BEGIN_ADDING_CHILDREN);
00114   aWrapper->SetNotificationMask(kItemSetNotificationMask);
00115   return NS_OK;
00116 }
00117 
00118 NS_IMETHODIMP
00119 nsXFormsItemSetElement::WillChangeDocument(nsIDOMDocument *aNewDocument)
00120 {
00121   SetRepeatState(eType_Unknown);
00122   return NS_OK;
00123 }
00124 
00125 NS_IMETHODIMP
00126 nsXFormsItemSetElement::DocumentChanged(nsIDOMDocument *aNewDocument)
00127 {
00128   nsXFormsDelegateStub::DocumentChanged(aNewDocument);
00129 
00130   nsCOMPtr<nsIDOMNode> parent;
00131   mElement->GetParentNode(getter_AddRefs(parent));
00132   UpdateRepeatState(parent);
00133   return NS_OK;
00134 }
00135 
00136 NS_IMETHODIMP
00137 nsXFormsItemSetElement::WillChangeParent(nsIDOMElement *aNewParent)
00138 {
00139   SetRepeatState(eType_Unknown);
00140   return NS_OK;
00141 }
00142 
00143 NS_IMETHODIMP
00144 nsXFormsItemSetElement::ParentChanged(nsIDOMElement *aNewParent)
00145 {
00146   nsXFormsDelegateStub::ParentChanged(aNewParent);
00147 
00148   UpdateRepeatState(aNewParent);
00149 
00150   if (aNewParent)
00151     Refresh();
00152 
00153   return NS_OK;
00154 }
00155 
00156 NS_IMETHODIMP
00157 nsXFormsItemSetElement::ChildInserted(nsIDOMNode *aChild, PRUint32 aIndex)
00158 {
00159   Refresh();
00160   return NS_OK;
00161 }
00162 
00163 NS_IMETHODIMP
00164 nsXFormsItemSetElement::ChildAppended(nsIDOMNode *aChild)
00165 {
00166   Refresh();
00167   return NS_OK;
00168 }
00169 
00170 NS_IMETHODIMP
00171 nsXFormsItemSetElement::WillRemoveChild(PRUint32 aIndex)
00172 {
00173   Refresh();
00174   return NS_OK;
00175 }
00176 
00177 NS_IMETHODIMP
00178 nsXFormsItemSetElement::BeginAddingChildren()
00179 {
00180   // Suppress child notifications until we're done getting children.
00181   nsCOMPtr<nsIXTFElementWrapper> wrapper = do_QueryInterface(mElement);
00182   NS_ASSERTION(wrapper, "huh? our element must be an xtf wrapper");
00183 
00184   wrapper->SetNotificationMask(
00185     kItemSetNotificationMask & ~(nsIXTFElement::NOTIFY_CHILD_INSERTED |
00186                                  nsIXTFElement::NOTIFY_CHILD_APPENDED |
00187                                  nsIXTFElement::NOTIFY_WILL_REMOVE_CHILD));
00188   return NS_OK;
00189 }
00190 
00191 NS_IMETHODIMP
00192 nsXFormsItemSetElement::DoneAddingChildren()
00193 {
00194   // Unsuppress child notifications until we're done getting children.
00195   nsCOMPtr<nsIXTFElementWrapper> wrapper = do_QueryInterface(mElement);
00196   NS_ASSERTION(wrapper, "huh? our element must be an xtf wrapper");
00197 
00198   wrapper->SetNotificationMask(kItemSetNotificationMask);
00199 
00200   // Walk our children and get their anonymous content.
00201   Refresh();
00202   return NS_OK;
00203 }
00204 
00205 // nsIXFormsSelectChild
00206 
00207 NS_IMETHODIMP
00208 nsXFormsItemSetElement::SelectItemByValue(const nsAString &aValue, nsIDOMNode **aSelected)
00209 {
00210   NS_ENSURE_ARG_POINTER(aSelected);
00211   NS_ENSURE_STATE(mElement);
00212   *aSelected = nsnull;
00213   // nsIXFormsItemSetUIElement is implemented by the XBL binding.
00214   nsCOMPtr<nsIXFormsItemSetUIElement> uiItemSet(do_QueryInterface(mElement));
00215   NS_ENSURE_STATE(uiItemSet);
00216 
00217   nsCOMPtr<nsIDOMElement> anonContent;
00218   uiItemSet->GetAnonymousItemSetContent(getter_AddRefs(anonContent));
00219   NS_ENSURE_STATE(anonContent);
00220 
00221   nsCOMPtr<nsIDOMNode> child, tmp;
00222   anonContent->GetFirstChild(getter_AddRefs(child));
00223   // Trying to select the first possible (generated) <item> element.
00224   while (child) {
00225     nsCOMPtr<nsIXFormsSelectChild> selectChild(do_QueryInterface(child));
00226     if (selectChild) {
00227       selectChild->SelectItemByValue(aValue, aSelected);
00228       if (*aSelected) {
00229         return NS_OK;
00230       }
00231     }
00232     tmp.swap(child);
00233     tmp->GetNextSibling(getter_AddRefs(child));
00234   }
00235   return NS_OK;
00236 }
00237 
00238 NS_IMETHODIMP
00239 nsXFormsItemSetElement::SelectItemByNode(nsIDOMNode *aNode,
00240                                          nsIDOMNode **aSelected)
00241 {
00242   NS_ENSURE_ARG_POINTER(aSelected);
00243   NS_ENSURE_STATE(mElement);
00244   *aSelected = nsnull;
00245   // nsIXFormsItemSetUIElement is implemented by the XBL binding.
00246   nsCOMPtr<nsIXFormsItemSetUIElement> uiItemSet(do_QueryInterface(mElement));
00247   NS_ENSURE_STATE(uiItemSet);
00248 
00249   nsCOMPtr<nsIDOMElement> anonContent;
00250   uiItemSet->GetAnonymousItemSetContent(getter_AddRefs(anonContent));
00251   NS_ENSURE_STATE(anonContent);
00252 
00253   nsCOMPtr<nsIDOMNode> child, tmp;
00254   anonContent->GetFirstChild(getter_AddRefs(child));
00255   // Trying to select the first possible (generated) <item> element.
00256   while (child) {
00257     nsCOMPtr<nsIXFormsSelectChild> selectChild(do_QueryInterface(child));
00258     if (selectChild) {
00259       selectChild->SelectItemByNode(aNode, aSelected);
00260       if (*aSelected) {
00261         return NS_OK;
00262       }
00263     }
00264     tmp.swap(child);
00265     tmp->GetNextSibling(getter_AddRefs(child));
00266   }
00267   return NS_OK;
00268 }
00269 
00270 NS_IMETHODIMP
00271 nsXFormsItemSetElement::Bind(PRBool *aContextChanged)
00272 {
00273   NS_ENSURE_ARG(aContextChanged);
00274   *aContextChanged = PR_FALSE;
00275   return BindToModel();
00276 }
00277 
00278 NS_IMETHODIMP
00279 nsXFormsItemSetElement::Refresh()
00280 {
00281   // We need to create item elements for each element referenced by the
00282   // nodeset.  Each of these items will create an anonymous HTML option element
00283   // which will return from GetAnonymousNodes.  We then clone our template
00284   // content and insert the cloned content as children of the HTML option.
00285 
00286   if (!nsXFormsUtils::IsDocumentReadyForBind(mElement)) {
00287     // not ready to bind yet, defer
00288     nsXFormsModelElement::DeferElementBind(this);
00289     return NS_OK;
00290   }
00291 
00292   nsCOMPtr<nsIModelElementPrivate> model;
00293   nsCOMPtr<nsIDOMXPathResult> result;
00294   nsresult rv = ProcessNodeBinding(NS_LITERAL_STRING("nodeset"),
00295                                    nsIDOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE,
00296                                    getter_AddRefs(result),
00297                                    getter_AddRefs(model));
00298 
00299   if (NS_FAILED(rv) | !result | !model)
00300     return rv;
00301 
00302   nsCOMPtr<nsIDOMNode> node, templateNode, cloneNode, tmpNode;
00303   nsCOMPtr<nsIDOMElement> itemNode, itemWrapperNode, contextContainer;
00304   nsCOMPtr<nsIDOMNodeList> templateNodes;
00305   mElement->GetChildNodes(getter_AddRefs(templateNodes));
00306   PRUint32 templateNodeCount = 0;
00307   if (templateNodes)
00308     templateNodes->GetLength(&templateNodeCount);
00309 
00310   nsCOMPtr<nsIContent> content(do_QueryInterface(mElement));
00311   NS_ENSURE_STATE(content);
00312   nsCOMPtr<nsIDocument> doc = content->GetCurrentDoc();
00313   nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(doc));
00314   NS_ENSURE_STATE(domDoc);
00315 
00316   PRUint32 nodeCount;
00317   result->GetSnapshotLength(&nodeCount);
00318 
00319   nsCOMPtr<nsIDOMNode> parent, tmp;
00320   mElement->GetParentNode(getter_AddRefs(parent));
00321 
00322   while (parent) {
00323     if (nsXFormsUtils::IsXFormsElement(parent, NS_LITERAL_STRING("select1")) ||
00324         nsXFormsUtils::IsXFormsElement(parent, NS_LITERAL_STRING("select"))) {
00325       break;
00326     }
00327     tmp.swap(parent);
00328     tmp->GetParentNode(getter_AddRefs(parent));
00329   }
00330 
00331   nsCOMPtr<nsIXFormsItemSetUIElement> uiItemSet(do_QueryInterface(mElement));
00332   nsCOMPtr<nsIDOMElement> anonContent;
00333   if (uiItemSet) {
00334     uiItemSet->GetAnonymousItemSetContent(getter_AddRefs(anonContent));
00335   }
00336 
00337   NS_ENSURE_STATE(anonContent);
00338 
00339   nsCOMPtr<nsIDOMNode> childNode, nodeReturn;
00340   while (NS_SUCCEEDED(anonContent->GetFirstChild(getter_AddRefs(childNode))) &&
00341        childNode) {
00342     anonContent->RemoveChild(childNode, getter_AddRefs(nodeReturn));
00343   }
00344 
00345   for (PRUint32 i = 0; i < nodeCount; ++i) {
00346     result->SnapshotItem(i, getter_AddRefs(node));
00347     NS_ASSERTION(node, "incorrect snapshot length");
00348 
00349     rv = domDoc->CreateElementNS(NS_LITERAL_STRING(NS_NAMESPACE_XFORMS),
00350                                  NS_LITERAL_STRING("item"),
00351                                  getter_AddRefs(itemNode));
00352     NS_ENSURE_SUCCESS(rv, rv);
00353 
00354     anonContent->AppendChild(itemNode, getter_AddRefs(tmpNode));
00355 
00356     nsCOMPtr<nsIXFormsContextControl> ctx(do_QueryInterface(itemNode));
00357     if (ctx) {
00358       ctx->SetContext(node, i + 1, nodeCount);
00359     }
00360 
00361     // Clone the template content under the item
00362     for (PRUint32 j = 0; j < templateNodeCount; ++j) {
00363       templateNodes->Item(j, getter_AddRefs(templateNode));
00364       templateNode->CloneNode(PR_TRUE, getter_AddRefs(cloneNode));
00365       itemNode->AppendChild(cloneNode, getter_AddRefs(templateNode));
00366     }
00367 
00368   }
00369 
00370   // refresh parent so that it has a chance to reflect the changes we just made
00371   if (parent) {
00372     nsCOMPtr<nsIXFormsControlBase> control = do_QueryInterface(parent);
00373     if (control) {
00374       control->Refresh();
00375     }
00376   }
00377 
00378   return NS_OK;
00379 }
00380 
00381 NS_IMETHODIMP
00382 nsXFormsItemSetElement::GetUsesSingleNodeBinding(PRBool *aUsesSNB)
00383 {
00384   NS_ENSURE_ARG_POINTER(aUsesSNB);
00385   *aUsesSNB = PR_FALSE;
00386   return NS_OK;
00387 }
00388 
00389 
00390 NS_HIDDEN_(nsresult)
00391 NS_NewXFormsItemSetElement(nsIXTFElement **aResult)
00392 {
00393   *aResult = new nsXFormsItemSetElement();
00394   if (!*aResult)
00395     return NS_ERROR_OUT_OF_MEMORY;
00396 
00397   NS_ADDREF(*aResult);
00398   return NS_OK;
00399 }
00400