Back to index

lightning-sunbird  0.9+nobinonly
nsXFormsSwitchElement.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  * Olli Pettay.
00019  * Portions created by the Initial Developer are Copyright (C) 2004
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Olli Pettay <Olli.Pettay@helsinki.fi> (original author)
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or 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 "nsCOMPtr.h"
00040 #include "nsIDOMElement.h"
00041 #include "nsIDOMDocument.h"
00042 #include "nsString.h"
00043 #include "nsIDocument.h"
00044 #include "nsIDOMNodeList.h"
00045 
00046 // For focus control
00047 #include "nsPIDOMWindow.h"
00048 #include "nsIFocusController.h"
00049 
00050 #include "nsXFormsUtils.h"
00051 #include "nsXFormsControlStub.h"
00052 #include "nsIModelElementPrivate.h"
00053 #include "nsIXFormsSwitchElement.h"
00054 #include "nsIXFormsCaseElement.h"
00055 #include "nsXFormsDelegateStub.h"
00056 
00063 class nsXFormsSwitchElement : public nsIXFormsSwitchElement,
00064                               public nsXFormsDelegateStub
00065 {
00066 public:
00067   nsXFormsSwitchElement() : mAddingChildren(PR_FALSE) {}
00068 
00069   NS_DECL_ISUPPORTS_INHERITED
00070   NS_DECL_NSIXFORMSSWITCHELEMENT
00071 
00072   NS_IMETHOD OnCreated(nsIXTFBindableElementWrapper *aWrapper);
00073   NS_IMETHOD ChildInserted(nsIDOMNode *aChild, PRUint32 aIndex);
00074   NS_IMETHOD ChildAppended(nsIDOMNode *aChild);
00075   NS_IMETHOD WillRemoveChild(PRUint32 aIndex);
00076   NS_IMETHOD BeginAddingChildren();
00077   NS_IMETHOD DoneAddingChildren();
00078 
00079   // nsIXFormsControl
00080   NS_IMETHOD IsEventTarget(PRBool *aOK);
00081 
00082 #ifdef DEBUG_smaug
00083   virtual const char* Name() { return "switch"; }
00084 #endif
00085 
00086 private:
00095   already_AddRefed<nsIDOMElement>
00096     FindFirstSelectedCase(nsIDOMElement* aDeselected = nsnull);
00097 
00098   void Init(nsIDOMElement* aDeselected = nsnull);
00099 
00103   void CaseChanged(nsIDOMNode* aCase, PRBool aRemoved);
00104 
00109   void SetFocus(nsIDOMElement* aDeselected, nsIDOMElement* aSelected);
00110 
00111   nsCOMPtr<nsIDOMElement> mSelected;
00112   PRBool                  mAddingChildren;
00113 };
00114 
00115 
00116 NS_IMPL_ISUPPORTS_INHERITED1(nsXFormsSwitchElement,
00117                              nsXFormsDelegateStub,
00118                              nsIXFormsSwitchElement)
00119 
00120 NS_IMETHODIMP
00121 nsXFormsSwitchElement::OnCreated(nsIXTFBindableElementWrapper *aWrapper)
00122 {
00123   nsresult rv = nsXFormsDelegateStub::OnCreated(aWrapper);
00124   NS_ENSURE_SUCCESS(rv, rv);
00125 
00126   aWrapper->SetNotificationMask(kStandardNotificationMask |
00127                                 nsIXTFElement::NOTIFY_BEGIN_ADDING_CHILDREN |
00128                                 nsIXTFElement::NOTIFY_DONE_ADDING_CHILDREN |
00129                                 nsIXTFElement::NOTIFY_CHILD_APPENDED |
00130                                 nsIXTFElement::NOTIFY_CHILD_INSERTED |
00131                                 nsIXTFElement::NOTIFY_WILL_REMOVE_CHILD);
00132 
00133   return NS_OK;
00134 }
00135 
00136 NS_IMETHODIMP
00137 nsXFormsSwitchElement::ChildInserted(nsIDOMNode *aChild, PRUint32 aIndex)
00138 {
00139   if (!mAddingChildren)
00140     CaseChanged(aChild, PR_FALSE);
00141   return NS_OK;
00142 }
00143 
00144 NS_IMETHODIMP
00145 nsXFormsSwitchElement::ChildAppended(nsIDOMNode *aChild)
00146 {
00147   if (!mAddingChildren)
00148     CaseChanged(aChild, PR_FALSE);
00149   return NS_OK;
00150 }
00151 
00152 NS_IMETHODIMP
00153 nsXFormsSwitchElement::WillRemoveChild(PRUint32 aIndex)
00154 {
00155   if (!mAddingChildren) {
00156     nsCOMPtr<nsIDOMNodeList> list;
00157     mElement->GetChildNodes(getter_AddRefs(list));
00158     if (list) {
00159       nsCOMPtr<nsIDOMNode> child;
00160       list->Item(aIndex, getter_AddRefs(child));
00161       CaseChanged(child, PR_TRUE);
00162     }
00163   }
00164   return NS_OK;
00165 }
00166 
00167 NS_IMETHODIMP
00168 nsXFormsSwitchElement::BeginAddingChildren()
00169 {
00170   mAddingChildren = PR_TRUE;
00171   return NS_OK;
00172 }
00173 
00174 NS_IMETHODIMP
00175 nsXFormsSwitchElement::DoneAddingChildren()
00176 {
00177   if (!mElement)
00178     return NS_OK;
00179 
00180   Init();
00181   mAddingChildren = PR_FALSE;
00182   return NS_OK;
00183 }
00184 
00185 // nsXFormsSwitchElement
00186 
00187 void
00188 nsXFormsSwitchElement::Init(nsIDOMElement* aDeselected)
00189 {
00190   nsCOMPtr<nsIDOMElement> firstCase = FindFirstSelectedCase(aDeselected);
00191   mSelected = firstCase;
00192   nsCOMPtr<nsIXFormsCaseElement> selected(do_QueryInterface(mSelected));
00193   if (selected) {
00194     nsresult rv = selected->SetSelected(PR_TRUE);
00195 
00196     // it is ok to fail if Init is called during the initialization phase since
00197     // XBL might not have attached on the xf:case, yet.
00198     // nsXFormsCaseElement::SetSelected will fail if it cannot QI the case
00199     // element to nsIXFormsCaseElementUI.
00200     NS_WARN_IF_FALSE(mAddingChildren || NS_SUCCEEDED(rv),
00201                      "Failed to select case");
00202   }
00203 }
00204 
00205 already_AddRefed<nsIDOMElement>
00206 nsXFormsSwitchElement::FindFirstSelectedCase(nsIDOMElement* aDeselected)
00207 {
00208   nsCOMPtr<nsIDOMNode> child;
00209   mElement->GetFirstChild(getter_AddRefs(child));
00210   nsCOMPtr<nsIDOMElement> firstCase;
00211   while (child) {
00212     nsCOMPtr<nsIDOMElement> childElement(do_QueryInterface(child));
00213     if (childElement && childElement != aDeselected) {
00214       if (nsXFormsUtils::IsXFormsElement(child, NS_LITERAL_STRING("case"))) {
00215         if (!firstCase)
00216           firstCase = childElement;
00217 
00218         nsCOMPtr<nsIXFormsCaseElement> caseElem(do_QueryInterface(child));
00219         if (caseElem) {
00220           PRBool selected;
00221           caseElem->GetInitialSelectedState(&selected);
00222           if (selected) {
00223             firstCase = childElement;
00224             break;
00225           }
00226         }
00227       }
00228     }
00229 
00230     nsCOMPtr<nsIDOMNode> tmp;
00231     child->GetNextSibling(getter_AddRefs(tmp));
00232     child.swap(tmp);
00233   }
00234   nsIDOMElement* result;
00235   NS_IF_ADDREF(result = firstCase);
00236   return result;
00237 }
00238 
00239 // nsIXFormsSwitchElement
00240 
00241 NS_IMETHODIMP
00242 nsXFormsSwitchElement::GetSelected(nsIDOMElement **aCase)
00243 {
00244   NS_ENSURE_ARG_POINTER(aCase);
00245   NS_IF_ADDREF(*aCase = mSelected);
00246   return NS_OK;
00247 }
00248 
00249 
00250 NS_IMETHODIMP
00251 nsXFormsSwitchElement::SetSelected(nsIDOMElement *aCase, PRBool aValue)
00252 {
00253   if (!mElement)
00254     return NS_OK;
00255 
00256   // There is no need to change the selected case, If we are trying to select 
00257   // a case which is already selected or if deselecting a case,
00258   // which is not selected.
00259   if (mSelected &&
00260       (((mSelected == aCase) && aValue) || ((mSelected != aCase) && !aValue)))
00261     return NS_OK;
00262 
00263   // Swapping the status of the mSelected and aCase, dispatching events.
00264   if (aValue && mSelected) {
00265     nsCOMPtr<nsIDOMElement> oldSel = mSelected;
00266     mSelected = aCase;
00267     nsCOMPtr<nsIXFormsCaseElement> deselected(do_QueryInterface(oldSel));
00268     if (deselected)
00269       deselected->SetSelected(PR_FALSE);
00270     nsCOMPtr<nsIXFormsCaseElement> selected(do_QueryInterface(mSelected));
00271     if (selected)
00272       selected->SetSelected(PR_TRUE);
00273     SetFocus(oldSel, mSelected);
00274     nsXFormsUtils::DispatchEvent(oldSel, eEvent_Deselect);
00275     nsXFormsUtils::DispatchEvent(mSelected, eEvent_Select);
00276     return NS_OK;
00277   }
00278 
00279   nsCOMPtr<nsIDOMElement> firstCase = 
00280     FindFirstSelectedCase(aValue ? nsnull : aCase);
00281 
00282   // If a selectable case was found, bring it to front, set focus
00283   // and dispatch events.
00284   if (firstCase) {
00285     mSelected = firstCase;
00286     nsCOMPtr<nsIXFormsCaseElement> deselected(do_QueryInterface(aCase));
00287     if (deselected)
00288       deselected->SetSelected(PR_FALSE);
00289     nsCOMPtr<nsIXFormsCaseElement> selected(do_QueryInterface(mSelected));
00290     if (selected)
00291       selected->SetSelected(PR_TRUE);
00292     SetFocus(aCase, mSelected);
00293     nsXFormsUtils::DispatchEvent(aCase, eEvent_Deselect);
00294     nsXFormsUtils::DispatchEvent(mSelected, eEvent_Select);
00295   }
00296 
00297   // If there is only one case, we (re)select it. No need to
00298   // set focus.
00299   else {
00300     mSelected = aCase;
00301     nsCOMPtr<nsIXFormsCaseElement> selected(do_QueryInterface(mSelected));
00302     if (selected)
00303       selected->SetSelected(PR_TRUE);
00304     nsXFormsUtils::DispatchEvent(mSelected, eEvent_Deselect);
00305     nsXFormsUtils::DispatchEvent(mSelected, eEvent_Select);
00306   }
00307   return NS_OK;
00308 }
00309 
00310 // nsXFormsSwitchElement
00311 
00312 void
00313 nsXFormsSwitchElement::SetFocus(nsIDOMElement* aDeselected,
00314                                nsIDOMElement* aSelected)
00315 {
00316   if (aDeselected == aSelected)
00317     return;
00318 
00319   nsCOMPtr<nsIDOMDocument> domDoc;
00320   mElement->GetOwnerDocument(getter_AddRefs(domDoc));
00321   nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
00322   if (!doc)
00323     return;
00324 
00325   nsCOMPtr<nsPIDOMWindow> win = doc->GetWindow();
00326   if (!win)
00327     return;
00328 
00329   nsIFocusController *focusController = win->GetRootFocusController();
00330   if (!focusController)
00331     return;
00332 
00333   nsCOMPtr<nsIDOMElement> focused;
00334   focusController->GetFocusedElement(getter_AddRefs(focused));
00335   if (!focused)
00336     return;
00337   
00338   PRBool hasFocus = PR_FALSE;
00339   nsCOMPtr<nsIDOMNode> current(do_QueryInterface(focused));
00340   do {
00341     nsCOMPtr<nsIDOMElement> currentElement(do_QueryInterface(current));
00342     if (aDeselected == current) {
00343       hasFocus = PR_TRUE;
00344       break;
00345     }
00346     nsCOMPtr<nsIDOMNode> parent;
00347     current->GetParentNode(getter_AddRefs(parent));
00348     current.swap(parent);
00349   } while(current);
00350   
00351   if (hasFocus) {
00352     // Flush layout before moving focus. Otherwise focus controller might
00353     // (re)focus something in the deselected <case>.
00354     doc->FlushPendingNotifications(Flush_Layout);
00355     focusController->MoveFocus(PR_TRUE, mElement);
00356   }
00357 }
00358 
00359 void
00360 nsXFormsSwitchElement::CaseChanged(nsIDOMNode* aCase, PRBool aRemoved)
00361 {
00362   if (!aCase)
00363     return;
00364 
00365   if (aRemoved) {
00366     if (aCase == mSelected) {
00367       nsXFormsUtils::DispatchEvent(mSelected, eEvent_Deselect);
00368       nsCOMPtr<nsIDOMElement> el(do_QueryInterface(aCase));
00369       Init(el);
00370       if (mSelected)
00371         nsXFormsUtils::DispatchEvent(mSelected, eEvent_Select);
00372     }
00373     return;
00374   }
00375 
00376   nsCOMPtr<nsIDOMElement> element(do_QueryInterface(aCase));
00377   if (!element)
00378     return;
00379 
00380   if (!mSelected) {
00381     Init();
00382     if (mSelected)
00383       nsXFormsUtils::DispatchEvent(mSelected, eEvent_Select);
00384     return;
00385   }
00386 
00387   if (!nsXFormsUtils::IsXFormsElement(aCase, NS_LITERAL_STRING("case")))
00388     return;
00389 
00390   nsCOMPtr<nsIXFormsCaseElement> caseElem(do_QueryInterface(element));
00391   if (caseElem) {
00392     PRBool selected;
00393     caseElem->GetInitialSelectedState(&selected);
00394     if (selected) {
00395       SetSelected(element, PR_TRUE);
00396     }
00397   }
00398 }
00399 
00400 // nsIXFormsControl
00401 
00402 NS_IMETHODIMP
00403 nsXFormsSwitchElement::IsEventTarget(PRBool *aOK)
00404 {
00405   *aOK = PR_FALSE;
00406   return NS_OK;
00407 }
00408 
00409 NS_HIDDEN_(nsresult)
00410 NS_NewXFormsSwitchElement(nsIXTFElement **aResult)
00411 {
00412   *aResult = new nsXFormsSwitchElement();
00413   if (!*aResult)
00414     return NS_ERROR_OUT_OF_MEMORY;
00415 
00416   NS_ADDREF(*aResult);
00417   return NS_OK;
00418 }