Back to index

lightning-sunbird  0.9+nobinonly
nsGfxButtonControlFrame.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.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nsGfxButtonControlFrame.h"
00039 #include "nsIButton.h"
00040 #include "nsWidgetsCID.h"
00041 #include "nsIFontMetrics.h"
00042 #include "nsFormControlFrame.h"
00043 #include "nsIFormControl.h"
00044 #include "nsISupportsArray.h"
00045 #include "nsINameSpaceManager.h"
00046 #ifdef ACCESSIBILITY
00047 #include "nsIAccessibilityService.h"
00048 #endif
00049 #include "nsIServiceManager.h"
00050 #include "nsIDOMNode.h"
00051 #include "nsLayoutAtoms.h"
00052 #include "nsReflowPath.h"
00053 #include "nsAutoPtr.h"
00054 #include "nsStyleSet.h"
00055 #include "nsContentUtils.h"
00056 // MouseEvent suppression in PP
00057 #include "nsGUIEvent.h"
00058 
00059 #include "nsNodeInfoManager.h"
00060 #include "nsIDOMHTMLInputElement.h"
00061 
00062 const nscoord kSuggestedNotSet = -1;
00063 
00064 nsGfxButtonControlFrame::nsGfxButtonControlFrame()
00065 {
00066   mSuggestedWidth  = kSuggestedNotSet;
00067   mSuggestedHeight = kSuggestedNotSet;
00068 }
00069 
00070 nsresult
00071 NS_NewGfxButtonControlFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame)
00072 {
00073   NS_PRECONDITION(aNewFrame, "null OUT ptr");
00074   if (nsnull == aNewFrame) {
00075     return NS_ERROR_NULL_POINTER;
00076   }
00077   nsGfxButtonControlFrame* it = new (aPresShell) nsGfxButtonControlFrame;
00078   if (!it) {
00079     return NS_ERROR_OUT_OF_MEMORY;
00080   }
00081   *aNewFrame = it;
00082   return NS_OK;
00083 }
00084       
00085 nsIAtom*
00086 nsGfxButtonControlFrame::GetType() const
00087 {
00088   return nsLayoutAtoms::gfxButtonControlFrame;
00089 }
00090 
00091 // Special check for the browse button of a file input.
00092 //
00093 // We'll return PR_TRUE if type is NS_FORM_INPUT_BUTTON and our parent
00094 // is a file input.
00095 PRBool
00096 nsGfxButtonControlFrame::IsFileBrowseButton(PRInt32 type)
00097 {
00098   PRBool rv = PR_FALSE;
00099   if (NS_FORM_INPUT_BUTTON == type) {
00100     // Check to see if parent is a file input
00101     nsCOMPtr<nsIFormControl> formCtrl =
00102       do_QueryInterface(mContent->GetParent());
00103 
00104     rv = formCtrl && formCtrl->GetType() == NS_FORM_INPUT_FILE;
00105   }
00106   return rv;
00107 }
00108 
00109 /*
00110  * FIXME: this ::GetIID() method has no meaning in life and should be
00111  * removed.
00112  * Pierre Phaneuf <pp@ludusdesign.com>
00113  */
00114 const nsIID&
00115 nsGfxButtonControlFrame::GetIID()
00116 {
00117   return NS_GET_IID(nsIButton);
00118 }
00119   
00120 const nsIID&
00121 nsGfxButtonControlFrame::GetCID()
00122 {
00123   static NS_DEFINE_IID(kButtonCID, NS_BUTTON_CID);
00124   return kButtonCID;
00125 }
00126 
00127 #ifdef DEBUG
00128 NS_IMETHODIMP
00129 nsGfxButtonControlFrame::GetFrameName(nsAString& aResult) const
00130 {
00131   return MakeFrameName(NS_LITERAL_STRING("ButtonControl"), aResult);
00132 }
00133 #endif
00134 
00135 NS_IMETHODIMP 
00136 nsGfxButtonControlFrame::AddComputedBorderPaddingToDesiredSize(nsHTMLReflowMetrics& aDesiredSize,
00137                                                                const nsHTMLReflowState& aSuggestedReflowState)
00138 {
00139   if (kSuggestedNotSet == mSuggestedWidth) {
00140     aDesiredSize.width  += aSuggestedReflowState.mComputedBorderPadding.left + aSuggestedReflowState.mComputedBorderPadding.right;
00141   }
00142 
00143   if (kSuggestedNotSet == mSuggestedHeight) {
00144     aDesiredSize.height += aSuggestedReflowState.mComputedBorderPadding.top + aSuggestedReflowState.mComputedBorderPadding.bottom;
00145   }
00146   return NS_OK;
00147 }
00148 
00149 // Create the text content used as label for the button.
00150 // The frame will be generated by the frame constructor.
00151 NS_IMETHODIMP
00152 nsGfxButtonControlFrame::CreateAnonymousContent(nsPresContext* aPresContext,
00153                                                 nsISupportsArray& aChildList)
00154 {
00155   nsXPIDLString label;
00156   GetLabel(label);
00157 
00158   // Add a child text content node for the label
00159   nsCOMPtr<nsITextContent> labelContent;
00160   nsINodeInfo* nodeInfo = mContent->GetNodeInfo();
00161   if (nodeInfo) {
00162     NS_NewTextNode(getter_AddRefs(labelContent), nodeInfo->NodeInfoManager());
00163   }
00164   if (labelContent) {
00165     // set the value of the text node and add it to the child list
00166     mTextContent.swap(labelContent);
00167     mTextContent->SetText(label, PR_FALSE);
00168     aChildList.AppendElement(mTextContent);
00169   }
00170   return NS_OK;
00171 }
00172 
00173 // Create the text content used as label for the button.
00174 // The frame will be generated by the frame constructor.
00175 NS_IMETHODIMP
00176 nsGfxButtonControlFrame::CreateFrameFor(nsPresContext*   aPresContext,
00177                                         nsIContent *      aContent,
00178                                         nsIFrame**        aFrame)
00179 {
00180   nsIFrame * newFrame = nsnull;
00181   nsresult rv = NS_ERROR_FAILURE;
00182 
00183   if (aFrame) 
00184     *aFrame = nsnull; 
00185 
00186   nsCOMPtr<nsIContent> content(do_QueryInterface(mTextContent));
00187   if (aContent == content.get()) {
00188     nsIFrame * parentFrame = mFrames.FirstChild();
00189     nsStyleContext* styleContext = parentFrame->GetStyleContext();
00190 
00191     rv = NS_NewTextFrame(aPresContext->PresShell(), &newFrame);
00192     if (NS_FAILED(rv)) { return rv; }
00193     if (!newFrame)   { return NS_ERROR_NULL_POINTER; }
00194     nsRefPtr<nsStyleContext> textStyleContext;
00195     textStyleContext = aPresContext->StyleSet()->
00196       ResolveStyleForNonElement(styleContext);
00197     if (!textStyleContext) { return NS_ERROR_NULL_POINTER; }
00198 
00199     if (styleContext) {
00200       // initialize the text frame
00201       newFrame->Init(aPresContext, content, parentFrame, textStyleContext, nsnull);
00202       newFrame->SetInitialChildList(aPresContext, nsnull, nsnull);
00203       rv = NS_OK;
00204     }
00205   }
00206 
00207   if (aFrame) {
00208     *aFrame = newFrame;
00209   }
00210   return rv;
00211 }
00212 
00213 #ifdef ACCESSIBILITY
00214 NS_IMETHODIMP nsGfxButtonControlFrame::GetAccessible(nsIAccessible** aAccessible)
00215 {
00216   nsCOMPtr<nsIAccessibilityService> accService = do_GetService("@mozilla.org/accessibilityService;1");
00217 
00218   if (accService) {
00219     return accService->CreateHTMLButtonAccessible(NS_STATIC_CAST(nsIFrame*, this), aAccessible);
00220   }
00221 
00222   return NS_ERROR_FAILURE;
00223 }
00224 #endif
00225 
00226 // Frames are not refcounted, no need to AddRef
00227 NS_IMETHODIMP
00228 nsGfxButtonControlFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr)
00229 {
00230   NS_ENSURE_ARG_POINTER(aInstancePtr);
00231 
00232   if (aIID.Equals(NS_GET_IID(nsIAnonymousContentCreator))) {
00233     *aInstancePtr = NS_STATIC_CAST(nsIAnonymousContentCreator*, this);
00234   }
00235 else {
00236     return nsHTMLButtonControlFrame::QueryInterface(aIID, aInstancePtr);
00237   }
00238 
00239   return NS_OK;
00240 }
00241 
00242 // Initially we hardcoded the default strings here.
00243 // Next, we used html.css to store the default label for various types
00244 // of buttons. (nsGfxButtonControlFrame::DoNavQuirksReflow rev 1.20)
00245 // However, since html.css is not internationalized, we now grab the default
00246 // label from a string bundle as is done for all other UI strings.
00247 // See bug 16999 for further details.
00248 nsresult
00249 nsGfxButtonControlFrame::GetDefaultLabel(nsXPIDLString& aString) 
00250 {
00251   nsresult rv = NS_OK;
00252   PRInt32 type = GetFormControlType();
00253   const char *prop;
00254   if (type == NS_FORM_INPUT_RESET) {
00255     prop = "Reset";
00256   } 
00257   else if (type == NS_FORM_INPUT_SUBMIT) {
00258     prop = "Submit";
00259   } 
00260   else if (IsFileBrowseButton(type)) {
00261     prop = "Browse";
00262   }
00263   else {
00264     aString.Truncate();
00265     return NS_OK;
00266   }
00267 
00268   return nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
00269                                             prop, aString);
00270 }
00271 
00272 nsresult
00273 nsGfxButtonControlFrame::GetLabel(nsXPIDLString& aLabel)
00274 {
00275   // Get the text from the "value" property on our content if there is
00276   // one; otherwise set it to a default value (localized).
00277   nsresult rv;
00278   nsCOMPtr<nsIDOMHTMLInputElement> elt = do_QueryInterface(mContent);
00279   if (mContent->HasAttr(kNameSpaceID_None, nsHTMLAtoms::value) && elt) {
00280     rv = elt->GetValue(aLabel);
00281   } else {
00282     // Generate localized label.
00283     // We can't make any assumption as to what the default would be
00284     // because the value is localized for non-english platforms, thus
00285     // it might not be the string "Reset", "Submit Query", or "Browse..."
00286     rv = GetDefaultLabel(aLabel);
00287   }
00288 
00289   NS_ENSURE_SUCCESS(rv, rv);
00290 
00291   // Compress whitespace out of label if needed.
00292   if (!GetStyleText()->WhiteSpaceIsSignificant()) {
00293     aLabel.CompressWhitespace();
00294   } else if (aLabel.Length() > 2 && aLabel.First() == ' ' &&
00295              aLabel.CharAt(aLabel.Length() - 1) == ' ') {
00296     // This is a bit of a hack.  The reason this is here is as follows: we now
00297     // have default padding on our buttons to make them non-ugly.
00298     // Unfortunately, IE-windows does not have such padding, so people will
00299     // stick values like " ok " (with the spaces) in the buttons in an attempt
00300     // to make them look decent.  Unfortunately, if they do this the button
00301     // looks way too big in Mozilla.  Worse yet, if they do this _and_ set a
00302     // fixed width for the button we run into trouble because our focus-rect
00303     // border/padding and outer border take up 10px of the horizontal button
00304     // space or so; the result is that the text is misaligned, even with the
00305     // recentering we do in nsHTMLButtonFrame::Reflow.  So to solve this, even
00306     // if the whitespace is significant, single leading and trailing _spaces_
00307     // (and not other whitespace) are removed.  The proper solution, of
00308     // course, is to not have the focus rect painting taking up 6px of
00309     // horizontal space. We should do that instead (via XBL form controls or
00310     // changing the renderer) and remove this.
00311     aLabel.Cut(0, 1);
00312     aLabel.Truncate(aLabel.Length() - 1);
00313   }
00314 
00315   return NS_OK;
00316 }
00317 
00318 NS_IMETHODIMP
00319 nsGfxButtonControlFrame::AttributeChanged(nsIContent*     aChild,
00320                                           PRInt32         aNameSpaceID,
00321                                           nsIAtom*        aAttribute,
00322                                           PRInt32         aModType)
00323 {
00324   nsresult rv = NS_OK;
00325 
00326   // If the value attribute is set, update the text of the label
00327   if (nsHTMLAtoms::value == aAttribute) {
00328     if (mTextContent && mContent) {
00329       nsXPIDLString label;
00330       rv = GetLabel(label);                                                    
00331       NS_ENSURE_SUCCESS(rv, rv);
00332 
00333       mTextContent->SetText(label, PR_TRUE);
00334     } else {
00335       rv = NS_ERROR_UNEXPECTED;
00336     }
00337 
00338   // defer to HTMLButtonControlFrame
00339   } else {
00340     rv = nsHTMLButtonControlFrame::AttributeChanged(aChild, aNameSpaceID, aAttribute, aModType);
00341   }
00342   return rv;
00343 }
00344 
00345 PRBool
00346 nsGfxButtonControlFrame::IsLeaf() const
00347 {
00348   return PR_TRUE;
00349 }
00350 
00351 NS_IMETHODIMP 
00352 nsGfxButtonControlFrame::Reflow(nsPresContext*          aPresContext, 
00353                                 nsHTMLReflowMetrics&     aDesiredSize,
00354                                 const nsHTMLReflowState& aReflowState, 
00355                                 nsReflowStatus&          aStatus)
00356 {
00357   DO_GLOBAL_REFLOW_COUNT("nsGfxButtonControlFrame", aReflowState.reason);
00358   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
00359 
00360   if ((kSuggestedNotSet != mSuggestedWidth) || 
00361       (kSuggestedNotSet != mSuggestedHeight)) {
00362     nsHTMLReflowState suggestedReflowState(aReflowState);
00363 
00364       // Honor the suggested width and/or height.
00365     if (kSuggestedNotSet != mSuggestedWidth) {
00366       suggestedReflowState.mComputedWidth = mSuggestedWidth;
00367     }
00368 
00369     if (kSuggestedNotSet != mSuggestedHeight) {
00370       suggestedReflowState.mComputedHeight = mSuggestedHeight;
00371     }
00372 
00373     return nsHTMLButtonControlFrame::Reflow(aPresContext, aDesiredSize, suggestedReflowState, aStatus);
00374 
00375   }
00376   
00377   // Normal reflow.
00378 
00379   return nsHTMLButtonControlFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus);
00380 }
00381 
00382 NS_IMETHODIMP 
00383 nsGfxButtonControlFrame::SetSuggestedSize(nscoord aWidth, nscoord aHeight)
00384 {
00385   mSuggestedWidth = aWidth;
00386   mSuggestedHeight = aHeight;
00387   //mState |= NS_FRAME_IS_DIRTY;
00388   return NS_OK;
00389 }
00390 
00391 NS_IMETHODIMP
00392 nsGfxButtonControlFrame::HandleEvent(nsPresContext* aPresContext, 
00393                                       nsGUIEvent*     aEvent,
00394                                       nsEventStatus*  aEventStatus)
00395 {
00396   // temp fix until Bug 124990 gets fixed
00397   if (aPresContext->IsPaginated() && NS_IS_MOUSE_EVENT(aEvent)) {
00398     return NS_OK;
00399   }
00400   // Override the HandleEvent to prevent the nsFrame::HandleEvent
00401   // from being called. The nsFrame::HandleEvent causes the button label
00402   // to be selected (Drawn with an XOR rectangle over the label)
00403 
00404   // do we have user-input style?
00405   const nsStyleUserInterface* uiStyle = GetStyleUserInterface();
00406   if (uiStyle->mUserInput == NS_STYLE_USER_INPUT_NONE || uiStyle->mUserInput == NS_STYLE_USER_INPUT_DISABLED)
00407     return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
00408   
00409   return NS_OK;
00410 }