mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-01-09 05:14:24 +00:00
c20f157140
Now all the changes are performed, but no reflows are issued until the timer fires, which would be after the script is done executing. The selection that take place during the script execution are also cached and done afterward. Bug 53165 r=pollmann sr=attinasi a=asa
2609 lines
96 KiB
C++
2609 lines
96 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public
|
|
* License Version 1.1 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
* implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code is mozilla.org code.
|
|
*
|
|
* The Initial Developer of the Original Code is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Pierre Phaneuf <pp@ludusdesign.com>
|
|
*/
|
|
#include "nsCOMPtr.h"
|
|
#include "nsComboboxControlFrame.h"
|
|
#include "nsIFrameManager.h"
|
|
#include "nsFormFrame.h"
|
|
#include "nsFormControlFrame.h"
|
|
#include "nsIHTMLContent.h"
|
|
#include "nsHTMLIIDs.h"
|
|
#include "nsHTMLAtoms.h"
|
|
#include "nsHTMLParts.h"
|
|
#include "nsIFormControl.h"
|
|
#include "nsINameSpaceManager.h"
|
|
#include "nsLayoutAtoms.h"
|
|
#include "nsIDOMElement.h"
|
|
#include "nsIListControlFrame.h"
|
|
#include "nsIDOMHTMLCollection.h"
|
|
#include "nsIDOMHTMLSelectElement.h"
|
|
#include "nsIDOMHTMLOptionElement.h"
|
|
#include "nsIDOMNSHTMLOptionCollectn.h"
|
|
#include "nsIPresShell.h"
|
|
#include "nsIPresState.h"
|
|
#include "nsISupportsArray.h"
|
|
#include "nsIDeviceContext.h"
|
|
#include "nsIView.h"
|
|
#include "nsIScrollableView.h"
|
|
#include "nsIEventStateManager.h"
|
|
#include "nsIDOMNode.h"
|
|
#include "nsIPrivateDOMEvent.h"
|
|
#include "nsIStatefulFrame.h"
|
|
#include "nsISupportsArray.h"
|
|
#include "nsISelectControlFrame.h"
|
|
#include "nsISupportsPrimitives.h"
|
|
#include "nsIComponentManager.h"
|
|
#include "nsIDOMMouseListener.h"
|
|
#include "nsITextContent.h"
|
|
#include "nsTextFragment.h"
|
|
#include "nsCSSFrameConstructor.h"
|
|
#include "nsIDocument.h"
|
|
#include "nsINodeInfo.h"
|
|
#include "nsIScrollableFrame.h"
|
|
#include "nsIScrollableView.h"
|
|
#include "nsListControlFrame.h"
|
|
#include "nsIElementFactory.h"
|
|
#include "nsContentCID.h"
|
|
#include "nsIAccessibilityService.h"
|
|
#include "nsIServiceManager.h"
|
|
#include "nsIDOMNode.h"
|
|
|
|
|
|
static NS_DEFINE_CID(kTextNodeCID, NS_TEXTNODE_CID);
|
|
static NS_DEFINE_CID(kHTMLElementFactoryCID, NS_HTML_ELEMENT_FACTORY_CID);
|
|
|
|
#include "nsIXULDocument.h" // Temporary fix for Bug 36558
|
|
|
|
#ifdef DO_NEW_REFLOW
|
|
#include "nsIFontMetrics.h"
|
|
#endif
|
|
|
|
|
|
#define FIX_FOR_BUG_53259
|
|
|
|
// Drop down list event management.
|
|
// The combo box uses the following strategy for managing the drop-down list.
|
|
// If the combo box or it's arrow button is clicked on the drop-down list is displayed
|
|
// If mouse exit's the combo box with the drop-down list displayed the drop-down list
|
|
// is asked to capture events
|
|
// The drop-down list will capture all events including mouse down and up and will always
|
|
// return with ListWasSelected method call regardless of whether an item in the list was
|
|
// actually selected.
|
|
// The ListWasSelected code will turn off mouse-capture for the drop-down list.
|
|
// The drop-down list does not explicitly set capture when it is in the drop-down mode.
|
|
|
|
|
|
//XXX: This is temporary. It simulates psuedo states by using a attribute selector on
|
|
|
|
const char * kMozDropdownActive = "-moz-dropdown-active";
|
|
|
|
const PRInt32 kSizeNotSet = -1;
|
|
|
|
// static class data member for Bug 32920
|
|
nsComboboxControlFrame * nsComboboxControlFrame::mFocused = nsnull;
|
|
nscoord nsComboboxControlFrame::mCachedScrollbarWidth = kSizeNotSet;
|
|
|
|
nsresult
|
|
NS_NewComboboxControlFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame, PRUint32 aStateFlags)
|
|
{
|
|
NS_PRECONDITION(aNewFrame, "null OUT ptr");
|
|
if (nsnull == aNewFrame) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
nsComboboxControlFrame* it = new (aPresShell) nsComboboxControlFrame;
|
|
if (!it) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
// set the state flags (if any are provided)
|
|
nsFrameState state;
|
|
it->GetFrameState( &state );
|
|
state |= aStateFlags;
|
|
it->SetFrameState( state );
|
|
*aNewFrame = it;
|
|
return NS_OK;
|
|
}
|
|
|
|
//-----------------------------------------------------------
|
|
// Reflow Debugging Macros
|
|
// These let us "see" how many reflow counts are happening
|
|
//-----------------------------------------------------------
|
|
#ifdef DO_REFLOW_COUNTER
|
|
|
|
#define MAX_REFLOW_CNT 1024
|
|
static PRInt32 gTotalReqs = 0;;
|
|
static PRInt32 gTotalReflows = 0;;
|
|
static PRInt32 gReflowControlCntRQ[MAX_REFLOW_CNT];
|
|
static PRInt32 gReflowControlCnt[MAX_REFLOW_CNT];
|
|
static PRInt32 gReflowInx = -1;
|
|
|
|
#define REFLOW_COUNTER() \
|
|
if (mReflowId > -1) \
|
|
gReflowControlCnt[mReflowId]++;
|
|
|
|
#define REFLOW_COUNTER_REQUEST() \
|
|
if (mReflowId > -1) \
|
|
gReflowControlCntRQ[mReflowId]++;
|
|
|
|
#define REFLOW_COUNTER_DUMP(__desc) \
|
|
if (mReflowId > -1) {\
|
|
gTotalReqs += gReflowControlCntRQ[mReflowId];\
|
|
gTotalReflows += gReflowControlCnt[mReflowId];\
|
|
printf("** Id:%5d %s RF: %d RQ: %d %d/%d %5.2f\n", \
|
|
mReflowId, (__desc), \
|
|
gReflowControlCnt[mReflowId], \
|
|
gReflowControlCntRQ[mReflowId],\
|
|
gTotalReflows, gTotalReqs, float(gTotalReflows)/float(gTotalReqs)*100.0f);\
|
|
}
|
|
|
|
#define REFLOW_COUNTER_INIT() \
|
|
if (gReflowInx < MAX_REFLOW_CNT) { \
|
|
gReflowInx++; \
|
|
mReflowId = gReflowInx; \
|
|
gReflowControlCnt[mReflowId] = 0; \
|
|
gReflowControlCntRQ[mReflowId] = 0; \
|
|
} else { \
|
|
mReflowId = -1; \
|
|
}
|
|
|
|
// reflow messages
|
|
#define REFLOW_DEBUG_MSG(_msg1) printf((_msg1))
|
|
#define REFLOW_DEBUG_MSG2(_msg1, _msg2) printf((_msg1), (_msg2))
|
|
#define REFLOW_DEBUG_MSG3(_msg1, _msg2, _msg3) printf((_msg1), (_msg2), (_msg3))
|
|
#define REFLOW_DEBUG_MSG4(_msg1, _msg2, _msg3, _msg4) printf((_msg1), (_msg2), (_msg3), (_msg4))
|
|
|
|
#else //-------------
|
|
|
|
#define REFLOW_COUNTER_REQUEST()
|
|
#define REFLOW_COUNTER()
|
|
#define REFLOW_COUNTER_DUMP(__desc)
|
|
#define REFLOW_COUNTER_INIT()
|
|
|
|
#define REFLOW_DEBUG_MSG(_msg)
|
|
#define REFLOW_DEBUG_MSG2(_msg1, _msg2)
|
|
#define REFLOW_DEBUG_MSG3(_msg1, _msg2, _msg3)
|
|
#define REFLOW_DEBUG_MSG4(_msg1, _msg2, _msg3, _msg4)
|
|
|
|
|
|
#endif
|
|
|
|
//------------------------------------------
|
|
// This is for being VERY noisy
|
|
//------------------------------------------
|
|
#ifdef DO_VERY_NOISY
|
|
#define REFLOW_NOISY_MSG(_msg1) printf((_msg1))
|
|
#define REFLOW_NOISY_MSG2(_msg1, _msg2) printf((_msg1), (_msg2))
|
|
#define REFLOW_NOISY_MSG3(_msg1, _msg2, _msg3) printf((_msg1), (_msg2), (_msg3))
|
|
#define REFLOW_NOISY_MSG4(_msg1, _msg2, _msg3, _msg4) printf((_msg1), (_msg2), (_msg3), (_msg4))
|
|
#else
|
|
#define REFLOW_NOISY_MSG(_msg)
|
|
#define REFLOW_NOISY_MSG2(_msg1, _msg2)
|
|
#define REFLOW_NOISY_MSG3(_msg1, _msg2, _msg3)
|
|
#define REFLOW_NOISY_MSG4(_msg1, _msg2, _msg3, _msg4)
|
|
#endif
|
|
|
|
//------------------------------------------
|
|
// Displays value in pixels or twips
|
|
//------------------------------------------
|
|
#ifdef DO_PIXELS
|
|
#define PX(__v) __v / 15
|
|
#else
|
|
#define PX(__v) __v
|
|
#endif
|
|
|
|
//------------------------------------------
|
|
// Asserts if we return a desired size that
|
|
// doesn't correctly match the mComputedWidth
|
|
//------------------------------------------
|
|
#ifdef DO_UNCONSTRAINED_CHECK
|
|
#define UNCONSTRAINED_CHECK() \
|
|
if (aReflowState.mComputedWidth != NS_UNCONSTRAINEDSIZE) { \
|
|
nscoord width = aDesiredSize.width - borderPadding.left - borderPadding.right; \
|
|
if (width != aReflowState.mComputedWidth) { \
|
|
printf("aDesiredSize.width %d %d != aReflowState.mComputedWidth %d\n", aDesiredSize.width, width, aReflowState.mComputedWidth); \
|
|
} \
|
|
NS_ASSERTION(width == aReflowState.mComputedWidth, "Returning bad value when constrained!"); \
|
|
}
|
|
#else
|
|
#define UNCONSTRAINED_CHECK()
|
|
#endif
|
|
//------------------------------------------------------
|
|
//-- Done with macros
|
|
//------------------------------------------------------
|
|
|
|
nsComboboxControlFrame::nsComboboxControlFrame()
|
|
: nsAreaFrame()
|
|
{
|
|
mPresContext = nsnull;
|
|
mFormFrame = nsnull;
|
|
mListControlFrame = nsnull;
|
|
mDroppedDown = PR_FALSE;
|
|
mDisplayFrame = nsnull;
|
|
mButtonFrame = nsnull;
|
|
mDropdownFrame = nsnull;
|
|
mSelectedIndex = -1;
|
|
|
|
mCacheSize.width = kSizeNotSet;
|
|
mCacheSize.height = kSizeNotSet;
|
|
mCachedMaxElementSize.width = kSizeNotSet;
|
|
mCachedMaxElementSize.height = kSizeNotSet;
|
|
mCachedAvailableSize.width = kSizeNotSet;
|
|
mCachedAvailableSize.height = kSizeNotSet;
|
|
mCachedUncDropdownSize.width = kSizeNotSet;
|
|
mCachedUncDropdownSize.height = kSizeNotSet;
|
|
mCachedUncComboSize.width = kSizeNotSet;
|
|
mCachedUncComboSize.height = kSizeNotSet;
|
|
mItemDisplayWidth = 0;
|
|
|
|
mGoodToGo = PR_FALSE;
|
|
|
|
//Shrink the area around it's contents
|
|
//SetFlags(NS_BLOCK_SHRINK_WRAP);
|
|
|
|
REFLOW_COUNTER_INIT()
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
nsComboboxControlFrame::~nsComboboxControlFrame()
|
|
{
|
|
REFLOW_COUNTER_DUMP("nsCCF");
|
|
|
|
if (mFormFrame) {
|
|
mFormFrame->RemoveFormControlFrame(*this);
|
|
mFormFrame = nsnull;
|
|
}
|
|
|
|
NS_IF_RELEASE(mPresContext);
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
// Frames are not refcounted, no need to AddRef
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr)
|
|
{
|
|
NS_PRECONDITION(0 != aInstancePtr, "null ptr");
|
|
if (NULL == aInstancePtr) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
|
|
if (aIID.Equals(NS_GET_IID(nsIComboboxControlFrame))) {
|
|
*aInstancePtr = (void*) ((nsIComboboxControlFrame*) this);
|
|
return NS_OK;
|
|
} else if (aIID.Equals(NS_GET_IID(nsIFormControlFrame))) {
|
|
*aInstancePtr = (void*) ((nsIFormControlFrame*) this);
|
|
return NS_OK;
|
|
} else if (aIID.Equals(NS_GET_IID(nsIAnonymousContentCreator))) {
|
|
*aInstancePtr = (void*)(nsIAnonymousContentCreator*) this;
|
|
return NS_OK;
|
|
} else if (aIID.Equals(NS_GET_IID(nsISelectControlFrame))) {
|
|
*aInstancePtr = (void *)(nsISelectControlFrame*)this;
|
|
return NS_OK;
|
|
} else if (aIID.Equals(NS_GET_IID(nsIStatefulFrame))) {
|
|
*aInstancePtr = (void *)(nsIStatefulFrame*)this;
|
|
return NS_OK;
|
|
} else if (aIID.Equals(NS_GET_IID(nsIRollupListener))) {
|
|
*aInstancePtr = (void*)(nsIRollupListener*)this;
|
|
return NS_OK;
|
|
} else if (aIID.Equals(NS_GET_IID(nsIScrollableViewProvider))) {
|
|
*aInstancePtr = (void*)(nsIScrollableViewProvider*)this;
|
|
return NS_OK;
|
|
}
|
|
|
|
return nsAreaFrame::QueryInterface(aIID, aInstancePtr);
|
|
}
|
|
|
|
NS_IMETHODIMP nsComboboxControlFrame::GetAccessible(nsIAccessible** aAccessible)
|
|
{
|
|
nsCOMPtr<nsIAccessibilityService> accService = do_GetService("@mozilla.org/accessibilityService;1");
|
|
|
|
if (accService) {
|
|
nsCOMPtr<nsIDOMNode> node = do_QueryInterface(mContent);
|
|
return accService->CreateHTMLSelectAccessible(nsLayoutAtoms::popupList, node, mPresContext, aAccessible);
|
|
}
|
|
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::Init(nsIPresContext* aPresContext,
|
|
nsIContent* aContent,
|
|
nsIFrame* aParent,
|
|
nsIStyleContext* aContext,
|
|
nsIFrame* aPrevInFlow)
|
|
{
|
|
// Need to hold on the pres context because it is used later in methods
|
|
// which don't have it passed in.
|
|
mPresContext = aPresContext;
|
|
NS_ADDREF(mPresContext);
|
|
|
|
//-------------------------------
|
|
// Start - Temporary fix for Bug 36558
|
|
//-------------------------------
|
|
mGoodToGo = PR_FALSE;
|
|
nsCOMPtr<nsIDocument> document;
|
|
nsresult rv = aContent->GetDocument(*getter_AddRefs(document));
|
|
if (NS_SUCCEEDED(rv) && document) {
|
|
nsCOMPtr<nsIXULDocument> xulDoc(do_QueryInterface(document));
|
|
mGoodToGo = xulDoc?PR_FALSE:PR_TRUE;
|
|
}
|
|
//-------------------------------
|
|
// Done - Temporary fix for Bug 36558
|
|
//-------------------------------
|
|
|
|
return nsAreaFrame::Init(aPresContext, aContent, aParent, aContext, aPrevInFlow);
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
PRBool
|
|
nsComboboxControlFrame::IsSuccessful(nsIFormControlFrame* aSubmitter)
|
|
{
|
|
nsAutoString name;
|
|
PRBool disabled = PR_FALSE;
|
|
nsFormControlHelper::GetDisabled(mContent, &disabled);
|
|
return !disabled && (NS_CONTENT_ATTR_HAS_VALUE == GetName(&name));
|
|
}
|
|
|
|
// If nothing is selected, and we have options, select item 0
|
|
// This is a UI decision that goes against the HTML 4 spec.
|
|
// See bugzilla bug 15841 for justification of this deviation.
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::MakeSureSomethingIsSelected(nsIPresContext* aPresContext)
|
|
{
|
|
REFLOW_DEBUG_MSG("CBX::MakeSureSomethingIsSelected\n");
|
|
|
|
nsIFormControlFrame* fcFrame = nsnull;
|
|
nsresult rv = mDropdownFrame->QueryInterface(NS_GET_IID(nsIFormControlFrame), (void**)&fcFrame);
|
|
if (NS_SUCCEEDED(rv) && fcFrame) {
|
|
// If nothing selected, and there are options, default selection to item 0
|
|
rv = mListControlFrame->GetSelectedIndex(&mSelectedIndex);
|
|
if (NS_SUCCEEDED(rv) && (mSelectedIndex < 0)) {
|
|
// Find out if there are any options in the list to select
|
|
PRInt32 length = 0;
|
|
mListControlFrame->GetNumberOfOptions(&length);
|
|
if (length > 0) {
|
|
// Set listbox selection to first item in the list box
|
|
rv = fcFrame->SetProperty(aPresContext, nsHTMLAtoms::selectedindex, NS_ConvertASCIItoUCS2("0"));
|
|
mSelectedIndex = 0;
|
|
} else {
|
|
UpdateSelection(PR_FALSE, PR_TRUE, mSelectedIndex); // Needed to reflow when removing last option
|
|
}
|
|
}
|
|
|
|
// Don't NS_RELEASE fcFrame here as it isn't addRef'd in the QI (???)
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
// Initialize the text string in the combobox using either the current
|
|
// selection in the list box or the first item item in the list box.
|
|
void
|
|
nsComboboxControlFrame::InitTextStr(nsIPresContext* aPresContext, PRBool aUpdate)
|
|
{
|
|
MakeSureSomethingIsSelected(aPresContext);
|
|
|
|
// Update the selected text string
|
|
mListControlFrame->GetSelectedItem(mTextStr);
|
|
|
|
// Update the display by setting the value attribute
|
|
mDisplayContent->SetText(mTextStr.GetUnicode(), mTextStr.Length(), aUpdate);
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
|
|
// Reset the combo box back to it original state.
|
|
|
|
void
|
|
nsComboboxControlFrame::Reset(nsIPresContext* aPresContext)
|
|
{
|
|
if (mPresState) {
|
|
nsIStatefulFrame* sFrame = nsnull;
|
|
nsresult res = mListControlFrame->QueryInterface(NS_GET_IID(nsIStatefulFrame),
|
|
(void**)&sFrame);
|
|
if (NS_SUCCEEDED(res) && sFrame) {
|
|
res = sFrame->RestoreState(mPresContext, mPresState);
|
|
NS_RELEASE(sFrame);
|
|
}
|
|
mPresState = do_QueryInterface(nsnull);
|
|
}
|
|
|
|
// Reset the dropdown list to its original state
|
|
nsIFormControlFrame* fcFrame = nsnull;
|
|
nsresult result = mDropdownFrame->QueryInterface(NS_GET_IID(nsIFormControlFrame), (void**)&fcFrame);
|
|
if ((NS_OK == result) && (nsnull != fcFrame)) {
|
|
fcFrame->Reset(aPresContext);
|
|
}
|
|
|
|
// Update the combobox using the text string returned from the dropdown list
|
|
InitTextStr(aPresContext, PR_TRUE);
|
|
}
|
|
|
|
void
|
|
nsComboboxControlFrame::InitializeControl(nsIPresContext* aPresContext)
|
|
{
|
|
Reset(aPresContext);
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::GetType(PRInt32* aType) const
|
|
{
|
|
*aType = NS_FORM_SELECT;
|
|
return NS_OK;
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::GetFormContent(nsIContent*& aContent) const
|
|
{
|
|
nsIContent* content;
|
|
nsresult rv;
|
|
rv = GetContent(&content);
|
|
aContent = content;
|
|
return rv;
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::GetFont(nsIPresContext* aPresContext,
|
|
const nsFont*& aFont)
|
|
{
|
|
return nsFormControlHelper::GetFont(this, aPresContext, mStyleContext, aFont);
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------
|
|
nscoord
|
|
nsComboboxControlFrame::GetVerticalBorderWidth(float aPixToTwip) const
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------
|
|
nscoord
|
|
nsComboboxControlFrame::GetHorizontalBorderWidth(float aPixToTwip) const
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------
|
|
nscoord
|
|
nsComboboxControlFrame::GetVerticalInsidePadding(nsIPresContext* aPresContext,
|
|
float aPixToTwip,
|
|
nscoord aInnerHeight) const
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
nscoord
|
|
nsComboboxControlFrame::GetHorizontalInsidePadding(nsIPresContext* aPresContext,
|
|
float aPixToTwip,
|
|
nscoord aInnerWidth,
|
|
nscoord aCharWidth) const
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
void
|
|
nsComboboxControlFrame::SetFocus(PRBool aOn, PRBool aRepaint)
|
|
{
|
|
if (aOn) {
|
|
mFocused = this;
|
|
} else {
|
|
mFocused = nsnull;
|
|
if (mDroppedDown) {
|
|
ToggleList(mPresContext);
|
|
}
|
|
}
|
|
// This is needed on a temporary basis. It causes the focus
|
|
// rect to be drawn. This is much faster than ReResolvingStyle
|
|
// Bug 32920
|
|
Invalidate(mPresContext, nsRect(0,0,mRect.width,mRect.height), PR_TRUE);
|
|
}
|
|
|
|
void
|
|
nsComboboxControlFrame::ScrollIntoView(nsIPresContext* aPresContext)
|
|
{
|
|
if (aPresContext) {
|
|
nsCOMPtr<nsIPresShell> presShell;
|
|
aPresContext->GetShell(getter_AddRefs(presShell));
|
|
if (presShell) {
|
|
presShell->ScrollFrameIntoView(this,
|
|
NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE,NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
nsComboboxControlFrame::ShowPopup(PRBool aShowPopup)
|
|
{
|
|
nsIView* view = nsnull;
|
|
mDropdownFrame->GetView(mPresContext, &view);
|
|
nsCOMPtr<nsIViewManager> viewManager;
|
|
view->GetViewManager(*getter_AddRefs(viewManager));
|
|
|
|
if (aShowPopup) {
|
|
nsRect rect;
|
|
mDropdownFrame->GetRect(rect);
|
|
viewManager->ResizeView(view, rect.width, rect.height);
|
|
nsIScrollableView* scrollingView;
|
|
if (NS_SUCCEEDED(view->QueryInterface(NS_GET_IID(nsIScrollableView), (void**)&scrollingView))) {
|
|
scrollingView->ComputeScrollOffsets(PR_TRUE);
|
|
}
|
|
viewManager->SetViewVisibility(view, nsViewVisibility_kShow);
|
|
} else {
|
|
viewManager->SetViewVisibility(view, nsViewVisibility_kHide);
|
|
viewManager->ResizeView(view, 0, 0);
|
|
}
|
|
|
|
// fire a popup dom event
|
|
nsEventStatus status = nsEventStatus_eIgnore;
|
|
nsMouseEvent event;
|
|
event.eventStructType = NS_EVENT;
|
|
event.message = aShowPopup ? NS_MENU_CREATE : NS_MENU_DESTROY;
|
|
event.isShift = PR_FALSE;
|
|
event.isControl = PR_FALSE;
|
|
event.isAlt = PR_FALSE;
|
|
event.isMeta = PR_FALSE;
|
|
event.clickCount = 0;
|
|
event.widget = nsnull;
|
|
|
|
nsCOMPtr<nsIPresShell> shell;
|
|
nsresult rv = mPresContext->GetShell(getter_AddRefs(shell));
|
|
if (NS_SUCCEEDED(rv) && shell)
|
|
rv = shell->HandleDOMEventWithTarget(mContent, &event, &status);
|
|
}
|
|
|
|
// Show the dropdown list
|
|
|
|
void
|
|
nsComboboxControlFrame::ShowList(nsIPresContext* aPresContext, PRBool aShowList)
|
|
{
|
|
nsCOMPtr<nsIWidget> widget;
|
|
|
|
// Get parent view
|
|
nsIFrame * listFrame;
|
|
if (NS_OK == mListControlFrame->QueryInterface(NS_GET_IID(nsIFrame), (void **)&listFrame)) {
|
|
nsIView * view = nsnull;
|
|
listFrame->GetView(aPresContext, &view);
|
|
NS_ASSERTION(view != nsnull, "nsComboboxControlFrame view is null");
|
|
if (view) {
|
|
view->GetWidget(*getter_AddRefs(widget));
|
|
}
|
|
}
|
|
|
|
if (PR_TRUE == aShowList) {
|
|
ShowPopup(PR_TRUE);
|
|
mDroppedDown = PR_TRUE;
|
|
|
|
// The listcontrol frame will call back to the nsComboboxControlFrame's ListWasSelected
|
|
// which will stop the capture.
|
|
mListControlFrame->AboutToDropDown();
|
|
mListControlFrame->CaptureMouseEvents(aPresContext, PR_TRUE);
|
|
|
|
} else {
|
|
ShowPopup(PR_FALSE);
|
|
mDroppedDown = PR_FALSE;
|
|
}
|
|
|
|
nsCOMPtr<nsIPresShell> presShell;
|
|
aPresContext->GetShell(getter_AddRefs(presShell));
|
|
presShell->FlushPendingNotifications();
|
|
|
|
if (widget)
|
|
widget->CaptureRollupEvents((nsIRollupListener *)this, mDroppedDown, PR_TRUE);
|
|
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------
|
|
// this is in response to the MouseClick from the containing browse button
|
|
// XXX: TODO still need to get filters from accept attribute
|
|
void
|
|
nsComboboxControlFrame::MouseClicked(nsIPresContext* aPresContext)
|
|
{
|
|
//ToggleList(aPresContext);
|
|
}
|
|
|
|
|
|
nsresult
|
|
nsComboboxControlFrame::ReflowComboChildFrame(nsIFrame* aFrame,
|
|
nsIPresContext* aPresContext,
|
|
nsHTMLReflowMetrics& aDesiredSize,
|
|
const nsHTMLReflowState& aReflowState,
|
|
nsReflowStatus& aStatus,
|
|
nscoord aAvailableWidth,
|
|
nscoord aAvailableHeight)
|
|
{
|
|
// Constrain the child's width and height to aAvailableWidth and aAvailableHeight
|
|
nsSize availSize(aAvailableWidth, aAvailableHeight);
|
|
nsHTMLReflowState kidReflowState(aPresContext, aReflowState, aFrame,
|
|
availSize);
|
|
kidReflowState.mComputedWidth = aAvailableWidth;
|
|
kidReflowState.mComputedHeight = aAvailableHeight;
|
|
|
|
// ensure we start off hidden
|
|
if (aReflowState.reason == eReflowReason_Initial) {
|
|
nsIView* view = nsnull;
|
|
mDropdownFrame->GetView(mPresContext, &view);
|
|
nsCOMPtr<nsIViewManager> viewManager;
|
|
view->GetViewManager(*getter_AddRefs(viewManager));
|
|
viewManager->SetViewVisibility(view, nsViewVisibility_kHide);
|
|
viewManager->ResizeView(view, 0, 0);
|
|
}
|
|
|
|
// Reflow child
|
|
nsRect rect;
|
|
aFrame->GetRect(rect);
|
|
nsresult rv = ReflowChild(aFrame, aPresContext, aDesiredSize, kidReflowState,
|
|
rect.x, rect.y, NS_FRAME_NO_MOVE_VIEW |NS_FRAME_NO_SIZE_VIEW | NS_FRAME_NO_VISIBILITY, aStatus);
|
|
|
|
// Set the child's width and height to it's desired size
|
|
FinishReflowChild(aFrame, aPresContext, aDesiredSize, rect.x, rect.y, NS_FRAME_NO_MOVE_VIEW |NS_FRAME_NO_SIZE_VIEW | NS_FRAME_NO_VISIBILITY);
|
|
return rv;
|
|
}
|
|
|
|
// Suggest a size for the child frame.
|
|
// Only frames which implement the nsIFormControlFrame interface and
|
|
// honor the SetSuggestedSize method will be placed and sized correctly.
|
|
|
|
void
|
|
nsComboboxControlFrame::SetChildFrameSize(nsIFrame* aFrame, nscoord aWidth, nscoord aHeight)
|
|
{
|
|
nsIFormControlFrame* fcFrame = nsnull;
|
|
nsresult result = aFrame->QueryInterface(NS_GET_IID(nsIFormControlFrame), (void**)&fcFrame);
|
|
if (NS_SUCCEEDED(result) && (nsnull != fcFrame)) {
|
|
fcFrame->SetSuggestedSize(aWidth, aHeight);
|
|
}
|
|
}
|
|
|
|
nsresult
|
|
nsComboboxControlFrame::GetPrimaryComboFrame(nsIPresContext* aPresContext, nsIContent* aContent, nsIFrame** aFrame)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
// Get the primary frame from the presentation shell.
|
|
nsCOMPtr<nsIPresShell> presShell;
|
|
rv = aPresContext->GetShell(getter_AddRefs(presShell));
|
|
if (NS_SUCCEEDED(rv) && presShell) {
|
|
presShell->GetPrimaryFrameFor(aContent, aFrame);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsComboboxControlFrame::PositionDropdown(nsIPresContext* aPresContext,
|
|
nscoord aHeight,
|
|
nsRect aAbsoluteTwipsRect,
|
|
nsRect aAbsolutePixelRect)
|
|
{
|
|
// Position the dropdown list. It is positioned below the display frame if there is enough
|
|
// room on the screen to display the entire list. Otherwise it is placed above the display
|
|
// frame.
|
|
|
|
// Note: As first glance, it appears that you could simply get the absolute bounding box for the
|
|
// dropdown list by first getting it's view, then getting the view's nsIWidget, then asking the nsIWidget
|
|
// for it's AbsoluteBounds. The problem with this approach, is that the dropdown lists y location can
|
|
// change based on whether the dropdown is placed below or above the display frame.
|
|
// The approach, taken here is to get use the absolute position of the display frame and use it's location
|
|
// to determine if the dropdown will go offscreen.
|
|
|
|
// Use the height calculated for the area frame so it includes both
|
|
// the display and button heights.
|
|
nsresult rv = NS_OK;
|
|
nscoord dropdownYOffset = aHeight;
|
|
// XXX: Enable this code to debug popping up above the display frame, rather than below it
|
|
nsRect dropdownRect;
|
|
mDropdownFrame->GetRect(dropdownRect);
|
|
|
|
nscoord screenHeightInPixels = 0;
|
|
if (NS_SUCCEEDED(nsFormControlFrame::GetScreenHeight(aPresContext, screenHeightInPixels))) {
|
|
// Get the height of the dropdown list in pixels.
|
|
float t2p;
|
|
aPresContext->GetTwipsToPixels(&t2p);
|
|
nscoord absoluteDropDownHeight = NSTwipsToIntPixels(dropdownRect.height, t2p);
|
|
|
|
// Check to see if the drop-down list will go offscreen
|
|
if (NS_SUCCEEDED(rv) && ((aAbsolutePixelRect.y + aAbsolutePixelRect.height + absoluteDropDownHeight) > screenHeightInPixels)) {
|
|
// move the dropdown list up
|
|
dropdownYOffset = - (dropdownRect.height);
|
|
}
|
|
}
|
|
|
|
dropdownRect.x = 0;
|
|
dropdownRect.y = dropdownYOffset;
|
|
nsRect currentRect;
|
|
mDropdownFrame->GetRect(currentRect);
|
|
|
|
mDropdownFrame->SetRect(aPresContext, dropdownRect);
|
|
#ifdef DEBUG_rodsXXXXXX
|
|
printf("%d Position Dropdown at: %d %d %d %d\n", counter++, dropdownRect.x, dropdownRect.y, dropdownRect.width, dropdownRect.height);
|
|
#endif
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
// Experimental Reflow
|
|
////////////////////////////////////////////////////////////////
|
|
#if defined(DO_NEW_REFLOW) || defined(DO_REFLOW_COUNTER)
|
|
//---------------------------------------------------------
|
|
// Returns the nsIDOMHTMLOptionElement for a given index
|
|
// in the select's collection
|
|
//---------------------------------------------------------
|
|
static nsIDOMHTMLOptionElement*
|
|
GetOption(nsIDOMHTMLCollection& aCollection, PRInt32 aIndex)
|
|
{
|
|
nsIDOMNode* node = nsnull;
|
|
if (NS_SUCCEEDED(aCollection.Item(aIndex, &node))) {
|
|
if (nsnull != node) {
|
|
nsIDOMHTMLOptionElement* option = nsnull;
|
|
node->QueryInterface(NS_GET_IID(nsIDOMHTMLOptionElement), (void**)&option);
|
|
NS_RELEASE(node);
|
|
return option;
|
|
}
|
|
}
|
|
return nsnull;
|
|
}
|
|
//---------------------------------------------------------
|
|
// for a given piece of content it returns nsIDOMHTMLSelectElement object
|
|
// or null
|
|
//---------------------------------------------------------
|
|
static nsIDOMHTMLSelectElement*
|
|
GetSelect(nsIContent * aContent)
|
|
{
|
|
nsIDOMHTMLSelectElement* selectElement = nsnull;
|
|
nsresult result = aContent->QueryInterface(NS_GET_IID(nsIDOMHTMLSelectElement),
|
|
(void**)&selectElement);
|
|
if (NS_SUCCEEDED(result) && selectElement) {
|
|
return selectElement;
|
|
} else {
|
|
return nsnull;
|
|
}
|
|
}
|
|
//---------------------------------------------------------
|
|
//---------------------------------------------------------
|
|
// This returns the collection for nsIDOMHTMLSelectElement or
|
|
// the nsIContent object is the select is null (AddRefs)
|
|
//---------------------------------------------------------
|
|
static nsIDOMHTMLCollection*
|
|
GetOptions(nsIContent * aContent, nsIDOMHTMLSelectElement* aSelect = nsnull)
|
|
{
|
|
nsIDOMHTMLCollection* options = nsnull;
|
|
if (!aSelect) {
|
|
nsCOMPtr<nsIDOMHTMLSelectElement> selectElement = getter_AddRefs(GetSelect(aContent));
|
|
if (selectElement) {
|
|
selectElement->GetOptions(&options); // AddRefs (1)
|
|
}
|
|
} else {
|
|
aSelect->GetOptions(&options); // AddRefs (1)
|
|
}
|
|
return options;
|
|
}
|
|
|
|
#ifdef DO_NEW_REFLOW
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::ReflowItems(nsIPresContext* aPresContext,
|
|
const nsHTMLReflowState& aReflowState,
|
|
nsHTMLReflowMetrics& aDesiredSize)
|
|
{
|
|
//printf("*****************\n");
|
|
const nsStyleFont* dspFont;
|
|
mDisplayFrame->GetStyleData(eStyleStruct_Font, (const nsStyleStruct *&)dspFont);
|
|
nsCOMPtr<nsIDeviceContext> deviceContext;
|
|
aPresContext->GetDeviceContext(getter_AddRefs(deviceContext));
|
|
NS_ASSERTION(deviceContext, "Couldn't get the device context");
|
|
nsIFontMetrics * fontMet;
|
|
deviceContext->GetMetricsFor(dspFont->mFont, fontMet);
|
|
|
|
nscoord visibleHeight;
|
|
//nsCOMPtr<nsIFontMetrics> fontMet;
|
|
//nsresult res = nsFormControlHelper::GetFrameFontFM(aPresContext, this, getter_AddRefs(fontMet));
|
|
if (fontMet) {
|
|
fontMet->GetHeight(visibleHeight);
|
|
}
|
|
|
|
nsAutoString maxStr;
|
|
nscoord maxWidth = 0;
|
|
//nsIRenderingContext * rc = aReflowState.rendContext;
|
|
nsresult rv = NS_ERROR_FAILURE;
|
|
nsCOMPtr<nsIDOMHTMLCollection> options = getter_AddRefs(GetOptions(mContent));
|
|
if (options) {
|
|
PRUint32 numOptions;
|
|
options->GetLength(&numOptions);
|
|
//printf("--- Num of Items %d ---\n", numOptions);
|
|
for (PRUint32 i=0;i<numOptions;i++) {
|
|
nsCOMPtr<nsIDOMHTMLOptionElement> optionElement = getter_AddRefs(GetOption(*options, i));
|
|
if (optionElement) {
|
|
nsAutoString text;
|
|
rv = optionElement->GetLabel(text);
|
|
if (NS_CONTENT_ATTR_HAS_VALUE != rv || 0 == text.Length()) {
|
|
if (NS_OK == optionElement->GetText(text)) {
|
|
nscoord width;
|
|
aReflowState.rendContext->GetWidth(text, width);
|
|
if (width > maxWidth) {
|
|
maxStr = text;
|
|
maxWidth = width;
|
|
}
|
|
//maxWidth = PR_MAX(width, maxWidth);
|
|
//printf("[%d] - %d %s \n", i, width, text.ToNewCString());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (maxWidth == 0) {
|
|
maxWidth = 11 * 15;
|
|
}
|
|
char * str = maxStr.ToNewCString();
|
|
printf("id: %d maxWidth %d [%s]\n", mReflowId, maxWidth, str);
|
|
delete [] str;
|
|
|
|
// get the borderPadding for the display area
|
|
nsMargin dspBorderPadding(0, 0, 0, 0);
|
|
mDisplayFrame->CalcBorderPadding(dspBorderPadding);
|
|
|
|
nscoord frmWidth = maxWidth+dspBorderPadding.left+dspBorderPadding.right+
|
|
aReflowState.mComputedBorderPadding.left + aReflowState.mComputedBorderPadding.right;
|
|
nscoord frmHeight = visibleHeight+dspBorderPadding.top+dspBorderPadding.bottom+
|
|
aReflowState.mComputedBorderPadding.top + aReflowState.mComputedBorderPadding.bottom;
|
|
|
|
#if 0
|
|
aDesiredSize.width = frmWidth;
|
|
aDesiredSize.height = frmHeight;
|
|
#else
|
|
printf("Size frm:%d,%d DS:%d,%d DIF:%d,%d(tp) %d,%d(px)\n",
|
|
frmWidth, frmHeight,
|
|
aDesiredSize.width, aDesiredSize.height,
|
|
frmWidth-aDesiredSize.width, frmHeight-aDesiredSize.height,
|
|
(frmWidth-aDesiredSize.width)/15, (frmHeight-aDesiredSize.height)/15);
|
|
#endif
|
|
NS_RELEASE(fontMet);
|
|
return NS_OK;
|
|
}
|
|
#endif
|
|
|
|
#endif
|
|
|
|
//------------------------------------------------------------------
|
|
// This Method reflow just the contents of the ComboBox
|
|
// The contents are a Block frame containing a Text Frame - This is the display area
|
|
// and then the GfxButton - The dropdown button
|
|
//--------------------------------------------------------------------------
|
|
void
|
|
nsComboboxControlFrame::ReflowCombobox(nsIPresContext * aPresContext,
|
|
const nsHTMLReflowState& aReflowState,
|
|
nsHTMLReflowMetrics& aDesiredSize,
|
|
nsReflowStatus& aStatus,
|
|
nsIFrame * aDisplayFrame,
|
|
nsIFrame * aDropDownBtn,
|
|
nscoord& aDisplayWidth,
|
|
nscoord aBtnWidth,
|
|
const nsMargin& aBorderPadding,
|
|
nscoord aFallBackHgt,
|
|
PRBool aCheckHeight)
|
|
{
|
|
// start out by using the cached height
|
|
// XXX later this will change when we better handle constrained height
|
|
nscoord dispHeight = mCacheSize.height - aBorderPadding.top - aBorderPadding.bottom;
|
|
nscoord dispWidth = aDisplayWidth;
|
|
|
|
REFLOW_NOISY_MSG3("+++1 AdjustCombo DW:%d DH:%d ", PX(dispWidth), PX(dispHeight));
|
|
REFLOW_NOISY_MSG3("BW:%d BH:%d ", PX(aBtnWidth), PX(dispHeight));
|
|
REFLOW_NOISY_MSG3("mCacheSize.height:%d - %d\n", PX(mCacheSize.height), PX((aBorderPadding.top + aBorderPadding.bottom)));
|
|
|
|
// get the border and padding for the DisplayArea (block frame & textframe)
|
|
nsMargin dspBorderPadding(0, 0, 0, 0);
|
|
mDisplayFrame->CalcBorderPadding(dspBorderPadding);
|
|
|
|
// adjust the height
|
|
if (mCacheSize.height == kSizeNotSet) {
|
|
if (aFallBackHgt == kSizeNotSet) {
|
|
NS_ASSERTION(aFallBackHgt != kSizeNotSet, "Fallback can't be kSizeNotSet when mCacheSize.height == kSizeNotSet");
|
|
} else {
|
|
dispHeight = aFallBackHgt;
|
|
REFLOW_NOISY_MSG2("+++3 Adding (dspBorderPadding.top + dspBorderPadding.bottom): %d\n", (dspBorderPadding.top + dspBorderPadding.bottom));
|
|
dispHeight += (dspBorderPadding.top + dspBorderPadding.bottom);
|
|
}
|
|
}
|
|
|
|
// Fix for Bug 58220 (part of it)
|
|
// make sure we size correctly if the CSS width is set to something really small like 0, 1, or 2 pixels
|
|
nscoord computedWidth = aReflowState.mComputedWidth + aBorderPadding.left + aBorderPadding.right;
|
|
if (aReflowState.mComputedWidth != NS_UNCONSTRAINEDSIZE && computedWidth <= 0) {
|
|
nsRect buttonRect(0,0,0,0);
|
|
nsRect displayRect(0,0,0,0);
|
|
aBtnWidth = 0;
|
|
aDisplayFrame->SetRect(aPresContext, displayRect);
|
|
aDropDownBtn->SetRect(aPresContext, buttonRect);
|
|
SetChildFrameSize(aDropDownBtn, aBtnWidth, aDesiredSize.height);
|
|
aDesiredSize.width = 0;
|
|
aDesiredSize.height = dispHeight;
|
|
return;
|
|
}
|
|
|
|
REFLOW_NOISY_MSG3("+++2 AdjustCombo DW:%d DH:%d ", PX(dispWidth), PX(dispHeight));
|
|
REFLOW_NOISY_MSG3(" BW:%d BH:%d\n", PX(aBtnWidth), PX(dispHeight));
|
|
|
|
// This sets the button to be a specific size
|
|
// so no matter what it reflows at these values
|
|
SetChildFrameSize(aDropDownBtn, aBtnWidth, dispHeight);
|
|
|
|
#ifdef FIX_FOR_BUG_53259
|
|
// Make sure we obey min/max-width and min/max-height
|
|
if (dispWidth > aReflowState.mComputedMaxWidth) {
|
|
dispWidth = aReflowState.mComputedMaxWidth - aBorderPadding.left - aBorderPadding.right;
|
|
}
|
|
if (dispWidth < aReflowState.mComputedMinWidth) {
|
|
dispWidth = aReflowState.mComputedMinWidth - aBorderPadding.left - aBorderPadding.right;
|
|
}
|
|
|
|
if (dispHeight > aReflowState.mComputedMaxHeight) {
|
|
dispHeight = aReflowState.mComputedMaxHeight - aBorderPadding.top - aBorderPadding.bottom;
|
|
}
|
|
if (dispHeight < aReflowState.mComputedMinHeight) {
|
|
dispHeight = aReflowState.mComputedMinHeight - aBorderPadding.top - aBorderPadding.bottom;
|
|
}
|
|
#endif
|
|
|
|
// now that we know what the overall display width & height will be
|
|
// set up a new reflow state and reflow the area frame at that size
|
|
nsSize availSize(dispWidth + aBorderPadding.left + aBorderPadding.right,
|
|
dispHeight + aBorderPadding.top + aBorderPadding.bottom);
|
|
nsHTMLReflowState kidReflowState(aPresContext, aReflowState, this, availSize);
|
|
kidReflowState.mComputedWidth = dispWidth;
|
|
kidReflowState.mComputedHeight = dispHeight;
|
|
|
|
#ifdef IBMBIDI
|
|
const nsStyleVisibility* vis;
|
|
GetStyleData(eStyleStruct_Visibility, (const nsStyleStruct*&)vis);
|
|
|
|
// M14 didn't calculate the RightEdge in the reflow
|
|
// Unless we set the width to some thing other than unrestricted
|
|
// the code changed this may not be the best place to put it
|
|
// in this->Reflow like this :
|
|
//
|
|
// Reflow display + button
|
|
// nsAreaFrame::Reflow(aPresContext, aDesiredSize, firstPassState, aStatus);
|
|
|
|
if (vis->mDirection == NS_STYLE_DIRECTION_RTL)
|
|
{
|
|
kidReflowState.mComputedWidth = 0;
|
|
}
|
|
#endif // IBMBIDI
|
|
|
|
// do reflow
|
|
nsAreaFrame::Reflow(aPresContext, aDesiredSize, kidReflowState, aStatus);
|
|
|
|
/////////////////////////////////////////////////////////
|
|
// The DisplayFrame is a Block frame containing a TextFrame
|
|
// and it is completely anonymous, so we must manually reflow it
|
|
nsSize txtAvailSize(dispWidth - aBtnWidth, dispHeight);
|
|
nsHTMLReflowMetrics txtKidSize(&txtAvailSize);
|
|
nsHTMLReflowState txtKidReflowState(aPresContext, aReflowState, aDisplayFrame, txtAvailSize);
|
|
|
|
aDisplayFrame->WillReflow(aPresContext);
|
|
//aDisplayFrame->MoveTo(aPresContext, dspBorderPadding.left + aBorderPadding.left, dspBorderPadding.top + aBorderPadding.top);
|
|
aDisplayFrame->MoveTo(aPresContext, aBorderPadding.left, aBorderPadding.top);
|
|
nsIView* view;
|
|
aDisplayFrame->GetView(aPresContext, &view);
|
|
if (view) {
|
|
nsAreaFrame::PositionFrameView(aPresContext, aDisplayFrame, view);
|
|
}
|
|
nsReflowStatus status;
|
|
nsresult rv = aDisplayFrame->Reflow(aPresContext, txtKidSize, txtKidReflowState, status);
|
|
if (NS_FAILED(rv)) return;
|
|
|
|
/////////////////////////////////////////////////////////
|
|
// If we are Constrained then the AreaFrame Reflow is the correct size
|
|
// if we are unconstrained then
|
|
//if (aReflowState.mComputedWidth == NS_UNCONSTRAINEDSIZE) {
|
|
// aDesiredSize.width += txtKidSize.width;
|
|
//}
|
|
|
|
// Apparently, XUL lays out differently than HTML
|
|
// (the code above works for HTML and not XUL),
|
|
// so instead of using the above calculation
|
|
// I just set it to what it should be.
|
|
aDesiredSize.width = availSize.width;
|
|
//aDesiredSize.height = availSize.height;
|
|
|
|
// now we need to adjust layout, because the AreaFrame
|
|
// doesn't position things exactly where we want them
|
|
nscoord insideHeight = aDesiredSize.height - aBorderPadding.top - aBorderPadding.bottom;
|
|
|
|
// the gets for the Display "block" frame and for the button
|
|
// make adjustments
|
|
nsRect buttonRect;
|
|
nsRect displayRect;
|
|
aDisplayFrame->GetRect(displayRect);
|
|
aDropDownBtn->GetRect(buttonRect);
|
|
|
|
// If the css width has been set to something very small
|
|
//i.e. smaller than the dropdown button, set the button's width to zero
|
|
if (aBtnWidth > dispWidth) {
|
|
aBtnWidth = 0;
|
|
}
|
|
// set the display rect to be left justifed and
|
|
// fills the entire area except the button
|
|
nscoord x = aBorderPadding.left;
|
|
displayRect.x = x;
|
|
displayRect.y = aBorderPadding.top;
|
|
displayRect.height = insideHeight;
|
|
displayRect.width = PR_MAX(dispWidth - aBtnWidth, 0); // make sure the width is never negative
|
|
aDisplayFrame->SetRect(aPresContext, displayRect);
|
|
x += displayRect.width;
|
|
|
|
// right justify the button
|
|
buttonRect.x = x;
|
|
buttonRect.y = aBorderPadding.top;
|
|
buttonRect.height = insideHeight;
|
|
buttonRect.width = aBtnWidth;
|
|
#ifdef IBMBIDI
|
|
if (vis->mDirection == NS_STYLE_DIRECTION_RTL)
|
|
{
|
|
if (buttonRect.x > displayRect.x)
|
|
{
|
|
buttonRect.x = displayRect.x;
|
|
displayRect.x += buttonRect.width;
|
|
aDisplayFrame->SetRect(aPresContext, displayRect);
|
|
}
|
|
}
|
|
#endif // IBMBIDI
|
|
aDropDownBtn->SetRect(aPresContext, buttonRect);
|
|
|
|
// since we have changed the height of the button
|
|
// make sure it has these new values
|
|
SetChildFrameSize(aDropDownBtn, aBtnWidth, aDesiredSize.height);
|
|
|
|
// This is a last minute adjustment, if the CSS width was set and
|
|
// we calculated it to be a little big, then make sure we are no bigger the computed size
|
|
// this only comes into play when the css width has been set to something smaller than
|
|
// the dropdown arrow
|
|
if (aReflowState.mComputedWidth != NS_UNCONSTRAINEDSIZE && aDesiredSize.width > computedWidth) {
|
|
aDesiredSize.width = computedWidth;
|
|
}
|
|
|
|
REFLOW_NOISY_MSG3("**AdjustCombobox - Reflow: WW: %d HH: %d\n", aDesiredSize.width, aDesiredSize.height);
|
|
|
|
if (nsnull != aDesiredSize.maxElementSize) {
|
|
aDesiredSize.maxElementSize->width = aDesiredSize.width;
|
|
aDesiredSize.maxElementSize->height = aDesiredSize.height;
|
|
}
|
|
// Now cache the available height as our height without border and padding
|
|
// This sets up the optimization for if a new available width comes in and we are equal or
|
|
// less than it we can bail
|
|
if (aDesiredSize.width != mCacheSize.width || aDesiredSize.height != mCacheSize.height) {
|
|
if (aReflowState.availableWidth != NS_UNCONSTRAINEDSIZE) {
|
|
mCachedAvailableSize.width = aDesiredSize.width - (aBorderPadding.left + aBorderPadding.right);
|
|
}
|
|
if (aReflowState.availableHeight != NS_UNCONSTRAINEDSIZE) {
|
|
mCachedAvailableSize.height = aDesiredSize.height - (aBorderPadding.top + aBorderPadding.bottom);
|
|
}
|
|
nsFormControlFrame::SetupCachedSizes(mCacheSize, mCachedMaxElementSize, aDesiredSize);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
// This is an experimental reflow that is turned off in the build
|
|
#ifdef DO_NEW_REFLOW
|
|
ReflowItems(aPresContext, aReflowState, aDesiredSize);
|
|
#endif
|
|
///////////////////////////////////////////////////////////////////
|
|
}
|
|
|
|
//----------------------------------------------------------
|
|
//
|
|
//----------------------------------------------------------
|
|
#ifdef DO_REFLOW_DEBUG
|
|
static int myCounter = 0;
|
|
|
|
static void printSize(char * aDesc, nscoord aSize)
|
|
{
|
|
printf(" %s: ", aDesc);
|
|
if (aSize == NS_UNCONSTRAINEDSIZE) {
|
|
printf("UC");
|
|
} else {
|
|
printf("%d", PX(aSize));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//-------------------------------------------------------------------
|
|
//-- Main Reflow for the Combobox
|
|
//-------------------------------------------------------------------
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::Reflow(nsIPresContext* aPresContext,
|
|
nsHTMLReflowMetrics& aDesiredSize,
|
|
const nsHTMLReflowState& aReflowState,
|
|
nsReflowStatus& aStatus)
|
|
{
|
|
DO_GLOBAL_REFLOW_COUNT("nsComboboxControlFrame", aReflowState.reason);
|
|
|
|
aStatus = NS_FRAME_COMPLETE;
|
|
|
|
REFLOW_COUNTER_REQUEST();
|
|
|
|
#ifdef DO_REFLOW_DEBUG
|
|
printf("-------------Starting Combobox Reflow ----------------------------\n");
|
|
printf("%p ** Id: %d nsCCF::Reflow %d R: ", this, mReflowId, myCounter++);
|
|
switch (aReflowState.reason) {
|
|
case eReflowReason_Initial:
|
|
printf("Ini");break;
|
|
case eReflowReason_Incremental:
|
|
printf("Inc");break;
|
|
case eReflowReason_Resize:
|
|
printf("Rsz");break;
|
|
case eReflowReason_StyleChange:
|
|
printf("Sty");break;
|
|
case eReflowReason_Dirty:
|
|
printf("Drt ");
|
|
break;
|
|
default:printf("<unknown>%d", aReflowState.reason);break;
|
|
}
|
|
|
|
printSize("AW", aReflowState.availableWidth);
|
|
printSize("AH", aReflowState.availableHeight);
|
|
printSize("CW", aReflowState.mComputedWidth);
|
|
printSize("CH", aReflowState.mComputedHeight);
|
|
|
|
nsCOMPtr<nsIDOMHTMLCollection> optionsTemp = getter_AddRefs(GetOptions(mContent));
|
|
PRUint32 numOptions;
|
|
optionsTemp->GetLength(&numOptions);
|
|
printSize("NO", (nscoord)numOptions);
|
|
|
|
printf(" *\n");
|
|
|
|
#endif
|
|
|
|
|
|
PRBool bailOnWidth;
|
|
PRBool bailOnHeight;
|
|
|
|
// Do initial check to see if we can bail out
|
|
// If it is an Initial or Incremental Reflow we never bail out here
|
|
// XXX right now we only bail if the width meets the criteria
|
|
//
|
|
// We bail:
|
|
// if mComputedWidth == NS_UNCONSTRAINEDSIZE and
|
|
// availableWidth == NS_UNCONSTRAINEDSIZE and
|
|
// we have cached an available size
|
|
//
|
|
// We bail:
|
|
// if mComputedWidth == NS_UNCONSTRAINEDSIZE and
|
|
// availableWidth != NS_UNCONSTRAINEDSIZE and
|
|
// availableWidth minus its border equals our cached available size
|
|
//
|
|
// We bail:
|
|
// if mComputedWidth != NS_UNCONSTRAINEDSIZE and
|
|
// cached availableSize.width == aReflowState.mComputedWidth and
|
|
// cached AvailableSize.width == aCacheSize.width
|
|
//
|
|
// NOTE: this returns whether we are doing an Incremental reflow
|
|
nsFormControlFrame::SkipResizeReflow(mCacheSize,
|
|
mCachedMaxElementSize,
|
|
mCachedAvailableSize,
|
|
aDesiredSize, aReflowState,
|
|
aStatus,
|
|
bailOnWidth, bailOnHeight);
|
|
if (bailOnWidth) {
|
|
#ifdef DO_REFLOW_DEBUG // check or size
|
|
nsMargin borderPadding(0, 0, 0, 0);
|
|
CalcBorderPadding(borderPadding);
|
|
UNCONSTRAINED_CHECK();
|
|
#endif
|
|
REFLOW_DEBUG_MSG3("^** Done nsCCF DW: %d DH: %d\n\n", PX(aDesiredSize.width), PX(aDesiredSize.height));
|
|
NS_ASSERTION(aDesiredSize.width != kSizeNotSet, "aDesiredSize.width != kSizeNotSet");
|
|
NS_ASSERTION(aDesiredSize.height != kSizeNotSet, "aDesiredSize.height != kSizeNotSet");
|
|
aDesiredSize.mOverflowArea.x = 0;
|
|
aDesiredSize.mOverflowArea.y = 0;
|
|
aDesiredSize.mOverflowArea.width = aDesiredSize.width;
|
|
aDesiredSize.mOverflowArea.height = aDesiredSize.height;
|
|
return NS_OK;
|
|
}
|
|
|
|
// add ourself to the form control
|
|
if (!mFormFrame && (eReflowReason_Initial == aReflowState.reason)) {
|
|
nsFormFrame::AddFormControlFrame(aPresContext, *NS_STATIC_CAST(nsIFrame*,this));
|
|
if (NS_FAILED(CreateDisplayFrame(aPresContext))) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
}
|
|
|
|
// Go get all of the important frame
|
|
nsresult rv = NS_OK;
|
|
// Don't try to do any special sizing and positioning unless all of the frames
|
|
// have been created.
|
|
if ((nsnull == mDisplayFrame) ||
|
|
(nsnull == mButtonFrame) ||
|
|
(nsnull == mDropdownFrame))
|
|
{
|
|
// Since combobox frames are missing just do a normal area frame reflow
|
|
return nsAreaFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus);
|
|
}
|
|
|
|
// size of each part of the combo box
|
|
nsRect displayRect;
|
|
nsRect buttonRect;
|
|
nsRect dropdownRect;
|
|
|
|
// get our border and padding,
|
|
// XXX - should be the same mComputedBorderPadding?
|
|
// maybe we should use that?
|
|
nsMargin borderPadding(0, 0, 0, 0);
|
|
CalcBorderPadding(borderPadding);
|
|
|
|
// Get the current sizes of the combo box child frames
|
|
mDisplayFrame->GetRect(displayRect);
|
|
mButtonFrame->GetRect(buttonRect);
|
|
mDropdownFrame->GetRect(dropdownRect);
|
|
|
|
// We should cache this instead getting it everytime
|
|
// the default size of the of scrollbar
|
|
// that will be the default width of the dropdown button
|
|
// the height will be the height of the text
|
|
if (mCachedScrollbarWidth == kSizeNotSet) {
|
|
nsCOMPtr<nsIDeviceContext> dx;
|
|
aPresContext->GetDeviceContext(getter_AddRefs(dx));
|
|
if (dx) {
|
|
// Get the width in Device pixels (in this case screen)
|
|
SystemAttrStruct info;
|
|
dx->GetSystemAttribute(eSystemAttr_Size_ScrollbarWidth, &info);
|
|
// Get the pixels to twips conversion for the current device (screen or printer)
|
|
float p2t;
|
|
aPresContext->GetPixelsToTwips(&p2t);
|
|
// Get the scale factor for mapping from one device (screen)
|
|
// to another device (screen or printer)
|
|
// Typically when it is a screen the scale 1.0
|
|
// when it is a printer is could be anything
|
|
float scale;
|
|
dx->GetCanonicalPixelScale(scale);
|
|
mCachedScrollbarWidth = NSIntPixelsToTwips(info.mSize, p2t*scale);
|
|
}
|
|
}
|
|
|
|
// set up a new reflow state for use throughout
|
|
nsHTMLReflowState firstPassState(aReflowState);
|
|
nsHTMLReflowMetrics dropdownDesiredSize(nsnull);
|
|
|
|
// Check to see if this a fully unconstrained reflow
|
|
PRBool fullyUnconstrained = firstPassState.mComputedWidth == NS_UNCONSTRAINEDSIZE &&
|
|
firstPassState.mComputedHeight == NS_UNCONSTRAINEDSIZE;
|
|
|
|
PRBool forceReflow = PR_FALSE;
|
|
|
|
// Only reflow the display and button
|
|
// if they are the target of the incremental reflow, unless they change size.
|
|
if (eReflowReason_Incremental == aReflowState.reason) {
|
|
nsIFrame* targetFrame;
|
|
firstPassState.reflowCommand->GetTarget(targetFrame);
|
|
// Check to see if we are the target of the Incremental Reflow
|
|
if (targetFrame == this) {
|
|
// We need to check here to see if we can get away with just reflowing
|
|
// the combobox and not the dropdown
|
|
REFLOW_DEBUG_MSG("-----------------Target is Combobox------------\n");
|
|
|
|
// If the mComputedWidth matches our cached display width
|
|
// then we get away with bailing out
|
|
PRBool doFullReflow = firstPassState.mComputedWidth != NS_UNCONSTRAINEDSIZE &&
|
|
firstPassState.mComputedWidth != mItemDisplayWidth;
|
|
if (!doFullReflow) {
|
|
// OK, so we got lucky and the size didn't change
|
|
// so do a simple reflow and bail out
|
|
REFLOW_DEBUG_MSG("------------Reflowing AreaFrame and bailing----\n\n");
|
|
ReflowCombobox(aPresContext, firstPassState, aDesiredSize, aStatus,
|
|
mDisplayFrame, mButtonFrame, mItemDisplayWidth,
|
|
mCachedScrollbarWidth, borderPadding);
|
|
REFLOW_COUNTER();
|
|
UNCONSTRAINED_CHECK();
|
|
REFLOW_DEBUG_MSG3("&** Done nsCCF DW: %d DH: %d\n\n", PX(aDesiredSize.width), PX(aDesiredSize.height));
|
|
NS_ASSERTION(aDesiredSize.width != kSizeNotSet, "aDesiredSize.width != kSizeNotSet");
|
|
NS_ASSERTION(aDesiredSize.height != kSizeNotSet, "aDesiredSize.height != kSizeNotSet");
|
|
aDesiredSize.mOverflowArea.x = 0;
|
|
aDesiredSize.mOverflowArea.y = 0;
|
|
aDesiredSize.mOverflowArea.width = aDesiredSize.width;
|
|
aDesiredSize.mOverflowArea.height = aDesiredSize.height;
|
|
return rv;
|
|
}
|
|
// Nope, something changed that affected our size
|
|
// so we need to do a full reflow and resize ourself
|
|
REFLOW_DEBUG_MSG("------------Do Full Reflow----\n\n");
|
|
firstPassState.reason = eReflowReason_StyleChange;
|
|
firstPassState.reflowCommand = nsnull;
|
|
forceReflow = PR_TRUE;
|
|
|
|
} else {
|
|
// Now, see if our target is the dropdown
|
|
// If so, maybe an items was added or some style changed etc.
|
|
// OR
|
|
// We get an Incremental reflow on the dropdown when it is being
|
|
// shown or hidden.
|
|
if (targetFrame == mDropdownFrame) {
|
|
REFLOW_DEBUG_MSG("---------Target is Dropdown (Clearing Unc DD Size)---\n");
|
|
// Nope, we were unlucky so now we do a full reflow
|
|
mCachedUncDropdownSize.width = kSizeNotSet;
|
|
mCachedUncDropdownSize.height = kSizeNotSet;
|
|
REFLOW_DEBUG_MSG("---- Doing Full Reflow\n");
|
|
// This is an incremental reflow targeted at the dropdown list
|
|
// and it didn't have anything to do with being show or hidden.
|
|
//
|
|
// The incremental reflow will not get to the dropdown list
|
|
// because it is in the "popup" list
|
|
// when this flow of control drops out of this if it will do a reflow
|
|
// on the AreaFrame which SHOULD make it get tothe drop down
|
|
// except that it is in the popup list, so we have it reflowed as
|
|
// a StyleChange, this is not as effecient as doing an Incremental
|
|
//
|
|
// At this point we want to by pass the reflow optimization in the dropdown
|
|
// because we aren't why it is getting an incremental reflow, but we do
|
|
// know that it needs to be resized or restyled
|
|
//mListControlFrame->SetOverrideReflowOptimization(PR_TRUE);
|
|
|
|
} else if (targetFrame == mDisplayFrame || targetFrame == mButtonFrame) {
|
|
REFLOW_DEBUG_MSG2("-----------------Target is %s------------\n", (targetFrame == mDisplayFrame?"DisplayItem Frame":"DropDown Btn Frame"));
|
|
// The incremental reflow is targeted at either the block or the button
|
|
REFLOW_DEBUG_MSG("---- Doing AreaFrame Reflow and then bailing out\n");
|
|
// Do simple reflow and bail out
|
|
ReflowCombobox(aPresContext, firstPassState, aDesiredSize, aStatus,
|
|
mDisplayFrame, mButtonFrame,
|
|
mItemDisplayWidth, mCachedScrollbarWidth, borderPadding, kSizeNotSet, PR_TRUE);
|
|
REFLOW_DEBUG_MSG3("+** Done nsCCF DW: %d DH: %d\n\n", PX(aDesiredSize.width), PX(aDesiredSize.height));
|
|
REFLOW_COUNTER();
|
|
UNCONSTRAINED_CHECK();
|
|
NS_ASSERTION(aDesiredSize.width != kSizeNotSet, "aDesiredSize.width != kSizeNotSet");
|
|
NS_ASSERTION(aDesiredSize.height != kSizeNotSet, "aDesiredSize.height != kSizeNotSet");
|
|
aDesiredSize.mOverflowArea.x = 0;
|
|
aDesiredSize.mOverflowArea.y = 0;
|
|
aDesiredSize.mOverflowArea.width = aDesiredSize.width;
|
|
aDesiredSize.mOverflowArea.height = aDesiredSize.height;
|
|
return rv;
|
|
} else {
|
|
nsIFrame * plainLstFrame;
|
|
if (NS_SUCCEEDED(mListControlFrame->QueryInterface(NS_GET_IID(nsIFrame), (void**)&plainLstFrame))) {
|
|
nsIFrame * frame;
|
|
plainLstFrame->FirstChild(aPresContext, nsnull, &frame);
|
|
nsIScrollableFrame * scrollFrame;
|
|
if (NS_SUCCEEDED(frame->QueryInterface(NS_GET_IID(nsIScrollableFrame), (void**)&scrollFrame))) {
|
|
nsIFrame * incrementalChild;
|
|
aReflowState.reflowCommand->GetNext(incrementalChild);
|
|
nsRect rect;
|
|
plainLstFrame->GetRect(rect);
|
|
plainLstFrame->Reflow(aPresContext, aDesiredSize, aReflowState, aStatus);
|
|
|
|
aDesiredSize.width = mCacheSize.width;
|
|
aDesiredSize.height = mCacheSize.height;
|
|
|
|
if (aDesiredSize.maxElementSize != nsnull) {
|
|
aDesiredSize.maxElementSize->width = mCachedMaxElementSize.width;
|
|
aDesiredSize.maxElementSize->height = mCachedMaxElementSize.height;
|
|
}
|
|
NS_ASSERTION(aDesiredSize.width != kSizeNotSet, "aDesiredSize.width != kSizeNotSet");
|
|
NS_ASSERTION(aDesiredSize.height != kSizeNotSet, "aDesiredSize.height != kSizeNotSet");
|
|
aDesiredSize.mOverflowArea.x = 0;
|
|
aDesiredSize.mOverflowArea.y = 0;
|
|
aDesiredSize.mOverflowArea.width = aDesiredSize.width;
|
|
aDesiredSize.mOverflowArea.height = aDesiredSize.height;
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
// Here the target of the reflow was a child of the dropdown list
|
|
// so we must do a full reflow
|
|
REFLOW_DEBUG_MSG("-----------------Target is Dropdown's Child (Option Item)------------\n");
|
|
REFLOW_DEBUG_MSG("---- Doing Reflow as StyleChange\n");
|
|
}
|
|
firstPassState.reason = eReflowReason_StyleChange;
|
|
firstPassState.reflowCommand = nsnull;
|
|
mListControlFrame->SetOverrideReflowOptimization(PR_TRUE);
|
|
forceReflow = PR_TRUE;
|
|
}
|
|
}
|
|
|
|
#ifdef IBMBIDI
|
|
else if (eReflowReason_StyleChange == aReflowState.reason) {
|
|
forceReflow = PR_TRUE;
|
|
}
|
|
#endif // IBMBIDI
|
|
|
|
// This ifdef is for the new approach to reflow
|
|
// where we don't reflow the dropdown
|
|
// we just figure out or width from the list of items
|
|
//
|
|
// This next section is the Current implementation
|
|
// the else contains the new reflow code
|
|
#ifndef DO_NEW_REFLOW_X
|
|
|
|
// Here is another special optimization
|
|
// Only reflow the dropdown if it has never been reflowed unconstrained
|
|
//
|
|
// Or someone up above here may want to force it to be reflowed
|
|
// by setting one or both of these to kSizeNotSet
|
|
if ((mCachedUncDropdownSize.width == kSizeNotSet &&
|
|
mCachedUncDropdownSize.height == kSizeNotSet) || forceReflow) {
|
|
REFLOW_DEBUG_MSG3("---Re %d,%d\n", PX(mCachedUncDropdownSize.width), PX(mCachedUncDropdownSize.height));
|
|
|
|
// Tell it we are doing the first pass, which means it will
|
|
// do the unconstained reflow and skip the second reflow this time around
|
|
nsListControlFrame * lcf = NS_STATIC_CAST(nsListControlFrame*, mDropdownFrame);
|
|
lcf->SetPassId(1);
|
|
// A width has not been specified for the select so size the display area
|
|
// to match the width of the longest item in the drop-down list. The dropdown
|
|
// list has already been reflowed and sized to shrink around its contents above.
|
|
ReflowComboChildFrame(mDropdownFrame, aPresContext, dropdownDesiredSize, firstPassState,
|
|
aStatus, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
|
|
lcf->SetPassId(0); // reset it back
|
|
|
|
if (forceReflow) {
|
|
mCachedUncDropdownSize.width = dropdownDesiredSize.width;
|
|
mCachedUncDropdownSize.height = dropdownDesiredSize.height;
|
|
}
|
|
} else {
|
|
// Here we pretended we did an unconstrained reflow
|
|
// so we set the cached values and continue on
|
|
REFLOW_DEBUG_MSG3("--- Using Cached ListBox Size %d,%d\n", PX(mCachedUncDropdownSize.width), PX(mCachedUncDropdownSize.height));
|
|
dropdownDesiredSize.width = mCachedUncDropdownSize.width;
|
|
dropdownDesiredSize.height = mCachedUncDropdownSize.height;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// XXX - I need to clean this nect part up a little it is very redundant
|
|
|
|
// Check here to if this is a mComputed unconstrained reflow
|
|
PRBool computedUnconstrained = firstPassState.mComputedWidth == NS_UNCONSTRAINEDSIZE &&
|
|
firstPassState.mComputedHeight == NS_UNCONSTRAINEDSIZE;
|
|
if (computedUnconstrained && !forceReflow) {
|
|
// Because Incremental reflows aren't actually getting to the dropdown
|
|
// we cache the size from when it did a fully unconstrained reflow
|
|
// we then check to see if the size changed at all,
|
|
// if not then bail out we don't need to worry
|
|
if (mCachedUncDropdownSize.width == kSizeNotSet && mCachedUncDropdownSize.height == kSizeNotSet) {
|
|
mCachedUncDropdownSize.width = dropdownDesiredSize.width;
|
|
mCachedUncDropdownSize.height = dropdownDesiredSize.height;
|
|
REFLOW_DEBUG_MSG3("---1 Caching mCachedUncDropdownSize %d,%d\n", PX(mCachedUncDropdownSize.width), PX(mCachedUncDropdownSize.height));
|
|
|
|
} else if (mCachedUncDropdownSize.width == dropdownDesiredSize.width &&
|
|
mCachedUncDropdownSize.height == dropdownDesiredSize.height) {
|
|
|
|
if (mCachedUncComboSize.width != kSizeNotSet && mCachedUncComboSize.height != kSizeNotSet) {
|
|
REFLOW_DEBUG_MSG3("--- Bailing because of mCachedUncDropdownSize %d,%d\n\n", PX(mCachedUncDropdownSize.width), PX(mCachedUncDropdownSize.height));
|
|
aDesiredSize.width = mCachedUncComboSize.width;
|
|
aDesiredSize.height = mCachedUncComboSize.height;
|
|
|
|
if (aDesiredSize.maxElementSize != nsnull) {
|
|
aDesiredSize.maxElementSize->width = mCachedMaxElementSize.width;
|
|
aDesiredSize.maxElementSize->height = mCachedMaxElementSize.height;
|
|
}
|
|
aDesiredSize.ascent = aDesiredSize.height;
|
|
aDesiredSize.descent = 0;
|
|
UNCONSTRAINED_CHECK();
|
|
REFLOW_DEBUG_MSG3("#** Done nsCCF DW: %d DH: %d\n\n", PX(aDesiredSize.width), PX(aDesiredSize.height));
|
|
NS_ASSERTION(aDesiredSize.width != kSizeNotSet, "aDesiredSize.width != kSizeNotSet");
|
|
NS_ASSERTION(aDesiredSize.height != kSizeNotSet, "aDesiredSize.height != kSizeNotSet");
|
|
aDesiredSize.mOverflowArea.x = 0;
|
|
aDesiredSize.mOverflowArea.y = 0;
|
|
aDesiredSize.mOverflowArea.width = aDesiredSize.width;
|
|
aDesiredSize.mOverflowArea.height = aDesiredSize.height;
|
|
return NS_OK;
|
|
}
|
|
} else {
|
|
mCachedUncDropdownSize.width = dropdownDesiredSize.width;
|
|
mCachedUncDropdownSize.height = dropdownDesiredSize.height;
|
|
}
|
|
}
|
|
// clean up stops here
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
// So this point we know we flowed the dropdown unconstrained
|
|
// now we get to figure out how big we need to be and
|
|
//
|
|
// We don't reflow the combobox here at the new size
|
|
// we cache its new size and reflow it on the dropdown
|
|
nsSize size;
|
|
PRInt32 length = 0;
|
|
mListControlFrame->GetNumberOfOptions(&length);
|
|
|
|
// dropdownRect will hold the content size (minus border padding)
|
|
// for the display area
|
|
mDropdownFrame->GetRect(dropdownRect);
|
|
|
|
// Get maximum size of the largest item in the dropdown
|
|
// The height of the display frame will be that height
|
|
// the width will be the same as
|
|
// the dropdown width (minus its borderPadding) OR
|
|
// a caculation off the mComputedWidth from reflow
|
|
mListControlFrame->GetMaximumSize(size);
|
|
|
|
// the variable "size" will now be
|
|
// the default size of the dropdown btn
|
|
if (mCachedScrollbarWidth > 0) {
|
|
size.width = mCachedScrollbarWidth;
|
|
}
|
|
|
|
// Get the border and padding for the dropdown
|
|
nsMargin dropBorderPadding(0, 0, 0, 0);
|
|
mDropdownFrame->CalcBorderPadding(dropBorderPadding);
|
|
|
|
// get the borderPadding for the display area
|
|
nsMargin dspBorderPadding(0, 0, 0, 0);
|
|
mDisplayFrame->CalcBorderPadding(dspBorderPadding);
|
|
|
|
// Substract dropdown's borderPadding from the width of the dropdown rect
|
|
// to get the size of the content area
|
|
//
|
|
// the height will come from the mDisplayFrame's height
|
|
// declare a size for the item display frame
|
|
|
|
//Set the desired size for the button and display frame
|
|
if (NS_UNCONSTRAINEDSIZE == firstPassState.mComputedWidth) {
|
|
REFLOW_DEBUG_MSG("Unconstrained.....\n");
|
|
REFLOW_DEBUG_MSG4("*B mItemDisplayWidth %d dropdownRect.width:%d dropdownRect.w+h %d\n", PX(mItemDisplayWidth), PX(dropdownRect.width), PX((dropBorderPadding.left + dropBorderPadding.right)));
|
|
|
|
// Start with the dropdown rect's width
|
|
mItemDisplayWidth = dropdownRect.width;
|
|
|
|
REFLOW_DEBUG_MSG2("* mItemDisplayWidth %d\n", PX(mItemDisplayWidth));
|
|
|
|
// mItemDisplayWidth must be the size of the "display" frame including it's
|
|
// border and padding, but NOT including the comboboxes border and padding
|
|
mItemDisplayWidth += dspBorderPadding.left + dspBorderPadding.right;
|
|
mItemDisplayWidth -= aReflowState.mComputedBorderPadding.left + aReflowState.mComputedBorderPadding.right;
|
|
|
|
REFLOW_DEBUG_MSG2("*A mItemDisplayWidth %d\n", PX(mItemDisplayWidth));
|
|
|
|
} else {
|
|
REFLOW_DEBUG_MSG("Constrained.....\n");
|
|
if (firstPassState.mComputedWidth > 0) {
|
|
// Compute the display item's width from reflow's mComputedWidth
|
|
// mComputedWidth has already excluded border and padding
|
|
// so subtract off the button's size
|
|
REFLOW_DEBUG_MSG3("B mItemDisplayWidth %d %d\n", PX(mItemDisplayWidth), PX(dspBorderPadding.right));
|
|
// Display Frame's width comes from the mComputedWidth and therefore implies that it
|
|
// includes the "display" frame's border and padding.
|
|
mItemDisplayWidth = firstPassState.mComputedWidth;
|
|
REFLOW_DEBUG_MSG2("A mItemDisplayWidth %d\n", PX(mItemDisplayWidth));
|
|
REFLOW_DEBUG_MSG4("firstPassState.mComputedWidth %d - size.width %d dspBorderPadding.right %d\n", PX(firstPassState.mComputedWidth), PX(size.width), PX(dspBorderPadding.right));
|
|
}
|
|
}
|
|
|
|
// Fix for Bug 44788 (remove this comment later)
|
|
if (firstPassState.mComputedHeight > 0 && NS_UNCONSTRAINEDSIZE != firstPassState.mComputedHeight) {
|
|
size.height = firstPassState.mComputedHeight;
|
|
}
|
|
|
|
|
|
// this reflows and makes and last minute adjustments
|
|
ReflowCombobox(aPresContext, firstPassState, aDesiredSize, aStatus,
|
|
mDisplayFrame, mButtonFrame, mItemDisplayWidth, mCachedScrollbarWidth,
|
|
borderPadding, size.height);
|
|
|
|
// The dropdown was reflowed UNCONSTRAINED before, now we need to check to see
|
|
// if it needs to be resized.
|
|
//
|
|
// Optimization - The style (font, etc.) maybe different for the display item
|
|
// than for any particular item in the dropdown. So, if the new size of combobox
|
|
// is smaller than the dropdown, that is OK, The dropdown MUST always be either the same
|
|
//size as the combo or larger if necessary
|
|
if (aDesiredSize.width > dropdownDesiredSize.width) {
|
|
if (eReflowReason_Initial == firstPassState.reason) {
|
|
firstPassState.reason = eReflowReason_Resize;
|
|
}
|
|
REFLOW_DEBUG_MSG3("*** Reflowing ListBox to width: %d it was %d\n", PX(aDesiredSize.width), PX(dropdownDesiredSize.width));
|
|
|
|
// Tell it we are doing the second pass, which means we will skip
|
|
// doing the unconstained reflow, we already know that size
|
|
nsListControlFrame * lcf = NS_STATIC_CAST(nsListControlFrame*, mDropdownFrame);
|
|
lcf->SetPassId(2);
|
|
// Reflow the dropdown list to match the width of the display + button
|
|
ReflowComboChildFrame(mDropdownFrame, aPresContext, dropdownDesiredSize, firstPassState, aStatus,
|
|
aDesiredSize.width, NS_UNCONSTRAINEDSIZE);
|
|
lcf->SetPassId(0); // reset it back
|
|
}
|
|
|
|
#else // DO_NEW_REFLOW
|
|
|
|
#ifdef IBMBIDI
|
|
if (eReflowReason_StyleChange == aReflowState.reason
|
|
|| mCacheSize.width == kSizeNotSet) {
|
|
#else
|
|
if (mCacheSize.width == kSizeNotSet) {
|
|
#endif // IBMBIDI
|
|
ReflowItems(aPresContext, aReflowState, aDesiredSize);
|
|
} else {
|
|
aDesiredSize.width = mCacheSize.width;
|
|
aDesiredSize.height = mCacheSize.height;
|
|
}
|
|
|
|
// get the borderPadding for the display area
|
|
nsMargin dspBorderPadding(0, 0, 0, 0);
|
|
mDisplayFrame->CalcBorderPadding(dspBorderPadding);
|
|
|
|
if (NS_UNCONSTRAINEDSIZE == firstPassState.mComputedWidth) {
|
|
mItemDisplayWidth = aDesiredSize.width - (dspBorderPadding.left + dspBorderPadding.right);
|
|
mItemDisplayWidth -= borderPadding.left + borderPadding.right;
|
|
} else {
|
|
if (firstPassState.mComputedWidth > 0) {
|
|
// Compute the display item's width from reflow's mComputedWidth
|
|
// mComputedWidth has already excluded border and padding
|
|
// so subtract off the button's size
|
|
mItemDisplayWidth = firstPassState.mComputedWidth - dspBorderPadding.left - dspBorderPadding.right;
|
|
}
|
|
}
|
|
|
|
// this reflows and makes and last minute adjustments
|
|
ReflowCombobox(aPresContext, firstPassState, aDesiredSize, aStatus,
|
|
mDisplayFrame, mButtonFrame, mItemDisplayWidth, mCachedScrollbarWidth,
|
|
borderPadding,
|
|
aDesiredSize.height- borderPadding.top - borderPadding.bottom -
|
|
dspBorderPadding.top - dspBorderPadding.bottom);
|
|
#endif // DO_NEW_REFLOW
|
|
|
|
// Set the max element size to be the same as the desired element size.
|
|
if (nsnull != aDesiredSize.maxElementSize) {
|
|
aDesiredSize.maxElementSize->width = aDesiredSize.width;
|
|
aDesiredSize.maxElementSize->height = aDesiredSize.height;
|
|
}
|
|
|
|
#if 0
|
|
COMPARE_QUIRK_SIZE("nsComboboxControlFrame", 127, 22)
|
|
#endif
|
|
|
|
// cache the availabe size to be our desired size minus the borders
|
|
// this is so if our cached avilable size is ever equal to or less
|
|
// than the real avilable size we can bail out
|
|
if (aReflowState.availableWidth != NS_UNCONSTRAINEDSIZE) {
|
|
mCachedAvailableSize.width = aDesiredSize.width - (borderPadding.left + borderPadding.right);
|
|
}
|
|
if (aReflowState.availableHeight != NS_UNCONSTRAINEDSIZE) {
|
|
mCachedAvailableSize.height = aDesiredSize.height - (borderPadding.top + borderPadding.bottom);
|
|
}
|
|
|
|
nsFormControlFrame::SetupCachedSizes(mCacheSize, mCachedMaxElementSize, aDesiredSize);
|
|
|
|
REFLOW_DEBUG_MSG3("** Done nsCCF DW: %d DH: %d\n\n", PX(aDesiredSize.width), PX(aDesiredSize.height));
|
|
REFLOW_COUNTER();
|
|
UNCONSTRAINED_CHECK();
|
|
|
|
// If this was a fully unconstrained reflow we cache
|
|
// the combobox's unconstrained size
|
|
if (fullyUnconstrained) {
|
|
mCachedUncComboSize.width = aDesiredSize.width;
|
|
mCachedUncComboSize.height = aDesiredSize.height;
|
|
}
|
|
|
|
NS_ASSERTION(aDesiredSize.width != kSizeNotSet, "aDesiredSize.width != kSizeNotSet");
|
|
NS_ASSERTION(aDesiredSize.height != kSizeNotSet, "aDesiredSize.height != kSizeNotSet");
|
|
aDesiredSize.mOverflowArea.x = 0;
|
|
aDesiredSize.mOverflowArea.y = 0;
|
|
aDesiredSize.mOverflowArea.width = aDesiredSize.width;
|
|
aDesiredSize.mOverflowArea.height = aDesiredSize.height;
|
|
return rv;
|
|
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::GetName(nsString* aResult)
|
|
{
|
|
nsresult result = NS_FORM_NOTOK;
|
|
if (mContent) {
|
|
nsIHTMLContent* formControl = nsnull;
|
|
result = mContent->QueryInterface(kIHTMLContentIID, (void**)&formControl);
|
|
if ((NS_OK == result) && formControl) {
|
|
nsHTMLValue value;
|
|
result = formControl->GetHTMLAttribute(nsHTMLAtoms::name, value);
|
|
if (NS_CONTENT_ATTR_HAS_VALUE == result) {
|
|
if (eHTMLUnit_String == value.GetUnit()) {
|
|
value.GetStringValue(*aResult);
|
|
}
|
|
}
|
|
NS_RELEASE(formControl);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
PRInt32
|
|
nsComboboxControlFrame::GetMaxNumValues()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
/*XXX-REMOVE
|
|
PRBool
|
|
nsComboboxControlFrame::GetNamesValues(PRInt32 aMaxNumValues, PRInt32& aNumValues,
|
|
nsString* aValues, nsString* aNames)
|
|
{
|
|
nsAutoString name;
|
|
nsresult result = GetName(&name);
|
|
if ((aMaxNumValues <= 0) || (NS_CONTENT_ATTR_HAS_VALUE != result)) {
|
|
return PR_FALSE;
|
|
}
|
|
|
|
// use our name and the text widgets value
|
|
aNames[0] = name;
|
|
aValues[0] = mTextStr;
|
|
aNumValues = 1;
|
|
nsresult status = PR_TRUE;
|
|
return status;
|
|
}
|
|
*/
|
|
|
|
PRBool
|
|
nsComboboxControlFrame::GetNamesValues(PRInt32 aMaxNumValues, PRInt32& aNumValues,
|
|
nsString* aValues, nsString* aNames)
|
|
{
|
|
nsIFormControlFrame* fcFrame = nsnull;
|
|
nsresult result = mDropdownFrame->QueryInterface(NS_GET_IID(nsIFormControlFrame), (void**)&fcFrame);
|
|
if ((NS_SUCCEEDED(result)) && (nsnull != fcFrame)) {
|
|
return fcFrame->GetNamesValues(aMaxNumValues, aNumValues, aValues, aNames);
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::GetFrameForPoint(nsIPresContext* aPresContext,
|
|
const nsPoint& aPoint,
|
|
nsFramePaintLayer aWhichLayer,
|
|
nsIFrame** aFrame)
|
|
{
|
|
// The button is getting the hover events so...
|
|
// None of the children frames of the combobox get
|
|
// the events. (like the button frame), that way
|
|
// all event based style rules affect the combobox
|
|
// and not the any of the child frames. (The inability
|
|
// of the parent to be in the :hover state at the same
|
|
// time as its children is really a bug (#5693 / #33736)
|
|
// in the implementation of :hover.)
|
|
|
|
// It would be theoretically more elegant to check the
|
|
// children when not disabled, and then use event
|
|
// capturing. It would correctly handle situations (obscure!!)
|
|
// where the children were visible but the parent was not.
|
|
// Now the functionality of the OPTIONs depends on the SELECT
|
|
// being visible. Oh well...
|
|
|
|
if ( mRect.Contains(aPoint) &&
|
|
(aWhichLayer == NS_FRAME_PAINT_LAYER_FOREGROUND) ) {
|
|
const nsStyleVisibility* vis =
|
|
(const nsStyleVisibility*)mStyleContext->GetStyleData(eStyleStruct_Visibility);
|
|
if (vis->IsVisible()) {
|
|
*aFrame = this;
|
|
return NS_OK;
|
|
}
|
|
}
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------
|
|
|
|
#ifdef NS_DEBUG
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::GetFrameName(nsString& aResult) const
|
|
{
|
|
return MakeFrameName("ComboboxControl", aResult);
|
|
}
|
|
#endif
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
// nsIComboboxControlFrame
|
|
//----------------------------------------------------------------------
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::ShowDropDown(PRBool aDoDropDown)
|
|
{
|
|
if (nsFormFrame::GetDisabled(this)) {
|
|
return NS_OK;
|
|
}
|
|
|
|
if (!mDroppedDown && aDoDropDown) {
|
|
// XXX Temporary for Bug 19416
|
|
nsIView * lstView;
|
|
mDropdownFrame->GetView(mPresContext, &lstView);
|
|
if (lstView) {
|
|
lstView->IgnoreSetPosition(PR_FALSE);
|
|
}
|
|
if (mListControlFrame) {
|
|
mListControlFrame->SyncViewWithFrame(mPresContext);
|
|
}
|
|
// XXX Temporary for Bug 19416
|
|
if (lstView) {
|
|
lstView->IgnoreSetPosition(PR_TRUE);
|
|
}
|
|
ToggleList(mPresContext);
|
|
return NS_OK;
|
|
} else if (mDroppedDown && !aDoDropDown) {
|
|
ToggleList(mPresContext);
|
|
return NS_OK;
|
|
}
|
|
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::SetDropDown(nsIFrame* aDropDownFrame)
|
|
{
|
|
mDropdownFrame = aDropDownFrame;
|
|
|
|
if (NS_OK != mDropdownFrame->QueryInterface(NS_GET_IID(nsIListControlFrame), (void**)&mListControlFrame)) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
// The ListControlFrame was just created and added to the comboxbox
|
|
// so provide it with a PresState so it can restore itself
|
|
// when it does its first "Reset"
|
|
if (mPresState) {
|
|
mListControlFrame->SetPresState(mPresState);
|
|
mPresState = do_QueryInterface(nsnull);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::GetDropDown(nsIFrame** aDropDownFrame)
|
|
{
|
|
if (nsnull == aDropDownFrame) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
*aDropDownFrame = mDropdownFrame;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::ListWasSelected(nsIPresContext* aPresContext, PRBool aForceUpdate, PRBool aSendEvent) // Added "aForceUpdate" for Bug 42661
|
|
{
|
|
if (aPresContext == nsnull) {
|
|
aPresContext = mPresContext;
|
|
}
|
|
ShowList(aPresContext, PR_FALSE);
|
|
mListControlFrame->CaptureMouseEvents(aPresContext, PR_FALSE);
|
|
|
|
PRInt32 indx;
|
|
mListControlFrame->GetSelectedIndex(&indx);
|
|
|
|
UpdateSelection(aSendEvent, aForceUpdate, indx); // Added "aForceUpdate" for Bug 42661
|
|
|
|
return NS_OK;
|
|
}
|
|
// Toggle dropdown list.
|
|
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::ToggleList(nsIPresContext* aPresContext)
|
|
{
|
|
|
|
ShowList(aPresContext, (PR_FALSE == mDroppedDown));
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::UpdateSelection(PRBool aDoDispatchEvent, PRBool aForceUpdate, PRInt32 aNewIndex)
|
|
{
|
|
if (mListControlFrame) {
|
|
// Check to see if the selection changed
|
|
if (mSelectedIndex != aNewIndex || aForceUpdate) {
|
|
mListControlFrame->GetSelectedItem(mTextStr); // Update text box
|
|
|
|
// Fix for Bug 42661 (remove comment later)
|
|
#ifdef DO_REFLOW_DEBUG
|
|
char * str = mTextStr.ToNewCString();
|
|
REFLOW_DEBUG_MSG2("UpdateSelection %s\n", str);
|
|
delete [] str;
|
|
#endif
|
|
mSelectedIndex = aNewIndex;
|
|
mListControlFrame->UpdateSelection(aDoDispatchEvent, aForceUpdate, mContent);
|
|
} else {
|
|
mSelectedIndex = aNewIndex;
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::AbsolutelyPositionDropDown()
|
|
{
|
|
nsRect absoluteTwips;
|
|
nsRect absolutePixels;
|
|
|
|
nsRect rect;
|
|
this->GetRect(rect);
|
|
if (NS_SUCCEEDED(nsFormControlFrame::GetAbsoluteFramePosition(mPresContext, this, absoluteTwips, absolutePixels))) {
|
|
PositionDropdown(mPresContext, rect.height, absoluteTwips, absolutePixels);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::GetAbsoluteRect(nsRect* aRect)
|
|
{
|
|
nsRect absoluteTwips;
|
|
nsRect rect;
|
|
this->GetRect(rect);
|
|
nsresult rv = nsFormControlFrame::GetAbsoluteFramePosition(mPresContext, this, absoluteTwips, *aRect);
|
|
|
|
return rv;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::SelectionChanged()
|
|
{
|
|
// Send reflow command because the new text maybe larger
|
|
nsresult rv = NS_OK;
|
|
if (mDisplayContent) {
|
|
nsAutoString value;
|
|
const nsTextFragment* fragment;
|
|
nsresult result = mDisplayContent->GetText(&fragment);
|
|
if (NS_SUCCEEDED(result)) {
|
|
fragment->AppendTo(value);
|
|
}
|
|
PRBool shouldSetValue = PR_FALSE;
|
|
if (NS_FAILED(result) || value.Length() == 0) {
|
|
shouldSetValue = PR_TRUE;
|
|
} else {
|
|
shouldSetValue = value != mTextStr;
|
|
REFLOW_DEBUG_MSG3("**** CBX::SelectionChanged Old[%s] New[%s]\n", value.ToNewCString(), mTextStr.ToNewCString());
|
|
}
|
|
if (shouldSetValue) {
|
|
if (mTextStr.Length() == 0) {
|
|
nsAutoString space; space.AssignWithConversion(" ");
|
|
rv = mDisplayContent->SetText(space.GetUnicode(), space.Length(), PR_TRUE);
|
|
} else {
|
|
rv = mDisplayContent->SetText(mTextStr.GetUnicode(), mTextStr.Length(), PR_TRUE);
|
|
}
|
|
nsFrameState state;
|
|
//mTextFrame->GetFrameState(&state);
|
|
//state |= NS_FRAME_IS_DIRTY;
|
|
//mTextFrame->SetFrameState(state);
|
|
mDisplayFrame->GetFrameState(&state);
|
|
state |= NS_FRAME_IS_DIRTY;
|
|
mDisplayFrame->SetFrameState(state);
|
|
nsCOMPtr<nsIPresShell> shell;
|
|
rv = mPresContext->GetShell(getter_AddRefs(shell));
|
|
ReflowDirtyChild(shell, (nsIFrame*) mDisplayFrame);
|
|
|
|
nsCOMPtr<nsIPresShell> presShell;
|
|
mPresContext->GetShell(getter_AddRefs(presShell));
|
|
presShell->FlushPendingNotifications();
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::GetIndexOfDisplayArea(PRInt32* aSelectedIndex)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aSelectedIndex);
|
|
*aSelectedIndex = mSelectedIndex;
|
|
return NS_OK;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// nsISelectControlFrame
|
|
//----------------------------------------------------------------------
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::DoneAddingContent(PRBool aIsDone)
|
|
{
|
|
nsISelectControlFrame* listFrame = nsnull;
|
|
nsresult rv = NS_ERROR_FAILURE;
|
|
if (mDropdownFrame != nsnull) {
|
|
rv = mDropdownFrame->QueryInterface(NS_GET_IID(nsISelectControlFrame),
|
|
(void**)&listFrame);
|
|
if (NS_SUCCEEDED(rv) && listFrame) {
|
|
rv = listFrame->DoneAddingContent(aIsDone);
|
|
NS_RELEASE(listFrame);
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::AddOption(nsIPresContext* aPresContext, PRInt32 aIndex)
|
|
{
|
|
#ifdef DO_REFLOW_DEBUGXX
|
|
printf("*********AddOption: %d\n", aIndex);
|
|
#endif
|
|
nsresult rv = NS_ERROR_FAILURE;
|
|
if (mDropdownFrame) {
|
|
nsListControlFrame * lcf = NS_STATIC_CAST(nsListControlFrame*, mDropdownFrame);
|
|
rv = lcf->AddOption(aPresContext, aIndex);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::RemoveOption(nsIPresContext* aPresContext, PRInt32 aIndex)
|
|
{
|
|
nsresult rv = NS_ERROR_FAILURE;
|
|
if (mDropdownFrame != nsnull) {
|
|
nsListControlFrame * lcf = NS_STATIC_CAST(nsListControlFrame*, mDropdownFrame);
|
|
rv = lcf->RemoveOption(aPresContext, aIndex);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::SetOptionSelected(PRInt32 aIndex, PRBool aValue)
|
|
{
|
|
nsISelectControlFrame* listFrame = nsnull;
|
|
nsresult rv = mDropdownFrame->QueryInterface(NS_GET_IID(nsISelectControlFrame),
|
|
(void**)&listFrame);
|
|
if (NS_SUCCEEDED(rv) && listFrame) {
|
|
rv = listFrame->SetOptionSelected(aIndex, aValue);
|
|
NS_RELEASE(listFrame);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::GetOptionSelected(PRInt32 aIndex, PRBool* aValue)
|
|
{
|
|
nsISelectControlFrame* listFrame = nsnull;
|
|
nsresult rv = mDropdownFrame->QueryInterface(NS_GET_IID(nsISelectControlFrame),
|
|
(void**)&listFrame);
|
|
if (NS_SUCCEEDED(rv) && listFrame) {
|
|
rv = listFrame->GetOptionSelected(aIndex, aValue);
|
|
NS_RELEASE(listFrame);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::OptionDisabled(nsIContent * aContent)
|
|
{
|
|
nsISelectControlFrame* listFrame = nsnull;
|
|
nsresult rv = mDropdownFrame->QueryInterface(NS_GET_IID(nsISelectControlFrame),
|
|
(void**)&listFrame);
|
|
if (NS_SUCCEEDED(rv) && listFrame) {
|
|
rv = listFrame->OptionDisabled(aContent);
|
|
NS_RELEASE(listFrame);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::HandleEvent(nsIPresContext* aPresContext,
|
|
nsGUIEvent* aEvent,
|
|
nsEventStatus* aEventStatus)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aEventStatus);
|
|
if (nsEventStatus_eConsumeNoDefault == *aEventStatus) {
|
|
return NS_OK;
|
|
}
|
|
if (nsFormFrame::GetDisabled(this)) {
|
|
return NS_OK;
|
|
}
|
|
|
|
// If we have style that affects how we are selected, feed event down to
|
|
// nsFrame::HandleEvent so that selection takes place when appropriate.
|
|
const nsStyleUserInterface* uiStyle;
|
|
GetStyleData(eStyleStruct_UserInterface, (const nsStyleStruct *&)uiStyle);
|
|
if (uiStyle->mUserInput == NS_STYLE_USER_INPUT_NONE || uiStyle->mUserInput == NS_STYLE_USER_INPUT_DISABLED)
|
|
return nsAreaFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
nsresult
|
|
nsComboboxControlFrame::RequiresWidget(PRBool& aRequiresWidget)
|
|
{
|
|
aRequiresWidget = PR_FALSE;
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::SetProperty(nsIPresContext* aPresContext, nsIAtom* aName, const nsAReadableString& aValue)
|
|
{
|
|
nsIFormControlFrame* fcFrame = nsnull;
|
|
nsresult result = mDropdownFrame->QueryInterface(NS_GET_IID(nsIFormControlFrame), (void**)&fcFrame);
|
|
if ((NS_SUCCEEDED(result)) && (nsnull != fcFrame)) {
|
|
return fcFrame->SetProperty(aPresContext, aName, aValue);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::GetProperty(nsIAtom* aName, nsAWritableString& aValue)
|
|
{
|
|
nsIFormControlFrame* fcFrame = nsnull;
|
|
nsresult result = mDropdownFrame->QueryInterface(NS_GET_IID(nsIFormControlFrame), (void**)&fcFrame);
|
|
if ((NS_SUCCEEDED(result)) && (nsnull != fcFrame)) {
|
|
return fcFrame->GetProperty(aName, aValue);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::CreateDisplayFrame(nsIPresContext* aPresContext)
|
|
{
|
|
if (mGoodToGo) {
|
|
return NS_OK;
|
|
}
|
|
|
|
nsCOMPtr<nsIPresShell> shell;
|
|
aPresContext->GetShell(getter_AddRefs(shell));
|
|
nsresult rv = NS_NewBlockFrame(shell, (nsIFrame**)&mDisplayFrame, NS_BLOCK_SPACE_MGR);
|
|
if (NS_FAILED(rv)) { return rv; }
|
|
if (!mDisplayFrame) { return NS_ERROR_NULL_POINTER; }
|
|
|
|
// create the style context for the anonymous frame
|
|
nsCOMPtr<nsIStyleContext> styleContext;
|
|
rv = aPresContext->ResolvePseudoStyleContextFor(mContent,
|
|
nsHTMLAtoms::mozDisplayComboboxControlFrame,
|
|
mStyleContext,
|
|
PR_FALSE,
|
|
getter_AddRefs(styleContext));
|
|
if (NS_FAILED(rv)) { return rv; }
|
|
if (!styleContext) { return NS_ERROR_NULL_POINTER; }
|
|
|
|
// create a text frame and put it inside the block frame
|
|
rv = NS_NewTextFrame(shell, &mTextFrame);
|
|
if (NS_FAILED(rv)) { return rv; }
|
|
if (!mTextFrame) { return NS_ERROR_NULL_POINTER; }
|
|
nsCOMPtr<nsIStyleContext> textStyleContext;
|
|
rv = aPresContext->ResolvePseudoStyleContextFor(mContent,
|
|
nsHTMLAtoms::mozDisplayComboboxControlFrame,
|
|
styleContext,
|
|
PR_FALSE,
|
|
getter_AddRefs(textStyleContext));
|
|
if (NS_FAILED(rv)) { return rv; }
|
|
if (!textStyleContext) { return NS_ERROR_NULL_POINTER; }
|
|
nsCOMPtr<nsIContent> content(do_QueryInterface(mDisplayContent));
|
|
mTextFrame->Init(aPresContext, content, mDisplayFrame, textStyleContext, nsnull);
|
|
mTextFrame->SetInitialChildList(aPresContext, nsnull, nsnull);
|
|
nsCOMPtr<nsIPresShell> presShell;
|
|
rv = aPresContext->GetShell(getter_AddRefs(presShell));
|
|
if (NS_FAILED(rv)) { return rv; }
|
|
if (!presShell) { return NS_ERROR_NULL_POINTER; }
|
|
nsCOMPtr<nsIFrameManager> frameManager;
|
|
rv = presShell->GetFrameManager(getter_AddRefs(frameManager));
|
|
if (NS_FAILED(rv)) { return rv; }
|
|
if (!frameManager) { return NS_ERROR_NULL_POINTER; }
|
|
frameManager->SetPrimaryFrameFor(content, mTextFrame);
|
|
|
|
rv = mDisplayFrame->Init(aPresContext, content, this, styleContext, nsnull);
|
|
if (NS_FAILED(rv)) { return rv; }
|
|
|
|
mDisplayFrame->SetInitialChildList(aPresContext, nsnull, mTextFrame);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::CreateAnonymousContent(nsIPresContext* aPresContext,
|
|
nsISupportsArray& aChildList)
|
|
{
|
|
// The frames used to display the combo box and the button used to popup the dropdown list
|
|
// are created through anonymous content. The dropdown list is not created through anonymous
|
|
// content because it's frame is initialized specifically for the drop-down case and it is placed
|
|
// a special list referenced through NS_COMBO_FRAME_POPUP_LIST_INDEX to keep separate from the
|
|
// layout of the display and button.
|
|
//
|
|
// Note: The value attribute of the display content is set when an item is selected in the dropdown list.
|
|
// If the content specified below does not honor the value attribute than nothing will be displayed.
|
|
// In addition, if the frame created by content below for does not implement the nsIFormControlFrame
|
|
// interface and honor the SetSuggestedSize method the placement and size of the display area will not
|
|
// match what is normally desired for a combobox.
|
|
|
|
|
|
// For now the content that is created corresponds to two input buttons. It would be better to create the
|
|
// tag as something other than input, but then there isn't any way to create a button frame since it
|
|
// isn't possible to set the display type in CSS2 to create a button frame.
|
|
|
|
// create content used for display
|
|
//nsIAtom* tag = NS_NewAtom("mozcombodisplay");
|
|
|
|
// Add a child text content node for the label
|
|
nsresult result;
|
|
nsCOMPtr<nsIContent> labelContent(do_CreateInstance(kTextNodeCID,&result));
|
|
nsAutoString value; value.AssignWithConversion("X");
|
|
if (NS_SUCCEEDED(result) && labelContent) {
|
|
// set the value of the text node
|
|
mDisplayContent = do_QueryInterface(labelContent);
|
|
mDisplayContent->SetText(value.GetUnicode(), value.Length(), PR_TRUE);
|
|
|
|
nsCOMPtr<nsIDocument> doc;
|
|
mContent->GetDocument(*getter_AddRefs(doc));
|
|
// mContent->AppendChildTo(labelContent, PR_FALSE, PR_FALSE);
|
|
|
|
nsCOMPtr<nsINodeInfoManager> nimgr;
|
|
result = doc->GetNodeInfoManager(*getter_AddRefs(nimgr));
|
|
NS_ENSURE_SUCCESS(result, result);
|
|
|
|
nsCOMPtr<nsINodeInfo> nodeInfo;
|
|
nimgr->GetNodeInfo(nsHTMLAtoms::input, nsnull, kNameSpaceID_None,
|
|
*getter_AddRefs(nodeInfo));
|
|
|
|
aChildList.AppendElement(labelContent);
|
|
|
|
// create button which drops the list down
|
|
nsCOMPtr<nsIElementFactory> ef(do_CreateInstance(kHTMLElementFactoryCID));
|
|
if (ef) {
|
|
nsCOMPtr<nsIContent> content;
|
|
result = ef->CreateInstanceByTag(nodeInfo,getter_AddRefs(content));
|
|
if (NS_SUCCEEDED(result)) {
|
|
nsCOMPtr<nsIHTMLContent> btnContent(do_QueryInterface(content));
|
|
if (btnContent) {
|
|
btnContent->SetAttribute(kNameSpaceID_None, nsHTMLAtoms::type, NS_ConvertASCIItoUCS2("button"), PR_FALSE);
|
|
aChildList.AppendElement(btnContent);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::CreateFrameFor(nsIPresContext* aPresContext,
|
|
nsIContent * aContent,
|
|
nsIFrame** aFrame)
|
|
{
|
|
NS_PRECONDITION(nsnull != aFrame, "null ptr");
|
|
NS_PRECONDITION(nsnull != aContent, "null ptr");
|
|
NS_PRECONDITION(nsnull != aPresContext, "null ptr");
|
|
|
|
*aFrame = nsnull;
|
|
NS_ASSERTION(mDisplayContent != nsnull, "mDisplayContent can't be null!");
|
|
|
|
if (!mGoodToGo) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
nsCOMPtr<nsIContent> content(do_QueryInterface(mDisplayContent));
|
|
if (aContent == content.get()) {
|
|
// Get PresShell
|
|
nsCOMPtr<nsIPresShell> shell;
|
|
aPresContext->GetShell(getter_AddRefs(shell));
|
|
|
|
// Start by by creating a containing frame
|
|
nsresult rv = NS_NewBlockFrame(shell, (nsIFrame**)&mDisplayFrame, NS_BLOCK_SPACE_MGR);
|
|
if (NS_FAILED(rv)) { return rv; }
|
|
if (!mDisplayFrame) { return NS_ERROR_NULL_POINTER; }
|
|
|
|
// create the style context for the anonymous block frame
|
|
nsCOMPtr<nsIStyleContext> styleContext;
|
|
rv = aPresContext->ResolvePseudoStyleContextFor(content,
|
|
nsHTMLAtoms::mozDisplayComboboxControlFrame,
|
|
mStyleContext,
|
|
PR_FALSE,
|
|
getter_AddRefs(styleContext));
|
|
if (NS_FAILED(rv)) { return rv; }
|
|
if (!styleContext) { return NS_ERROR_NULL_POINTER; }
|
|
|
|
// Create a text frame and put it inside the block frame
|
|
rv = NS_NewTextFrame(shell, &mTextFrame);
|
|
if (NS_FAILED(rv)) { return rv; }
|
|
if (!mTextFrame) { return NS_ERROR_NULL_POINTER; }
|
|
nsCOMPtr<nsIStyleContext> textStyleContext;
|
|
rv = aPresContext->ResolvePseudoStyleContextFor(content,
|
|
nsHTMLAtoms::mozDisplayComboboxControlFrame,//nsHTMLAtoms::textPseudo,
|
|
styleContext,
|
|
PR_FALSE,
|
|
getter_AddRefs(textStyleContext));
|
|
if (NS_FAILED(rv)) { return rv; }
|
|
if (!textStyleContext) { return NS_ERROR_NULL_POINTER; }
|
|
|
|
// initialize the text frame
|
|
nsCOMPtr<nsIContent> content(do_QueryInterface(mDisplayContent));
|
|
mTextFrame->Init(aPresContext, content, mDisplayFrame, textStyleContext, nsnull);
|
|
mTextFrame->SetInitialChildList(aPresContext, nsnull, nsnull);
|
|
|
|
/*nsCOMPtr<nsIFrameManager> frameManager;
|
|
rv = shell->GetFrameManager(getter_AddRefs(frameManager));
|
|
if (NS_FAILED(rv)) { return rv; }
|
|
if (!frameManager) { return NS_ERROR_NULL_POINTER; }
|
|
frameManager->SetPrimaryFrameFor(content, mTextFrame);
|
|
*/
|
|
|
|
rv = mDisplayFrame->Init(aPresContext, content, this, styleContext, nsnull);
|
|
if (NS_FAILED(rv)) { return rv; }
|
|
|
|
mDisplayFrame->SetInitialChildList(aPresContext, nsnull, mTextFrame);
|
|
*aFrame = mDisplayFrame;
|
|
return NS_OK;
|
|
}
|
|
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::SetSuggestedSize(nscoord aWidth, nscoord aHeight)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::Destroy(nsIPresContext* aPresContext)
|
|
{
|
|
nsFormControlFrame::RegUnRegAccessKey(mPresContext, NS_STATIC_CAST(nsIFrame*, this), PR_FALSE);
|
|
|
|
if (mDroppedDown) {
|
|
nsCOMPtr<nsIWidget> widget;
|
|
// Get parent view
|
|
nsIFrame * listFrame;
|
|
if (NS_OK == mListControlFrame->QueryInterface(NS_GET_IID(nsIFrame), (void **)&listFrame)) {
|
|
nsIView * view = nsnull;
|
|
listFrame->GetView(aPresContext, &view);
|
|
NS_ASSERTION(view != nsnull, "nsComboboxControlFrame view is null");
|
|
if (view) {
|
|
view->GetWidget(*getter_AddRefs(widget));
|
|
if (widget)
|
|
widget->CaptureRollupEvents((nsIRollupListener *)this, PR_FALSE, PR_TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Cleanup frames in popup child list
|
|
mPopupFrames.DestroyFrames(aPresContext);
|
|
|
|
if (!mGoodToGo) {
|
|
if (mDisplayFrame) {
|
|
mFrameConstructor->RemoveMappingsForFrameSubtree(aPresContext, mDisplayFrame, nsnull);
|
|
mDisplayFrame->Destroy(aPresContext);
|
|
mDisplayFrame=nsnull;
|
|
}
|
|
}
|
|
|
|
return nsAreaFrame::Destroy(aPresContext);
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::FirstChild(nsIPresContext* aPresContext,
|
|
nsIAtom* aListName,
|
|
nsIFrame** aFirstChild) const
|
|
{
|
|
if (nsLayoutAtoms::popupList == aListName) {
|
|
*aFirstChild = mPopupFrames.FirstChild();
|
|
} else {
|
|
nsAreaFrame::FirstChild(aPresContext, aListName, aFirstChild);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::SetInitialChildList(nsIPresContext* aPresContext,
|
|
nsIAtom* aListName,
|
|
nsIFrame* aChildList)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
if (nsLayoutAtoms::popupList == aListName) {
|
|
mPopupFrames.SetFrames(aChildList);
|
|
} else {
|
|
rv = nsAreaFrame::SetInitialChildList(aPresContext, aListName, aChildList);
|
|
InitTextStr(aPresContext, PR_FALSE);
|
|
|
|
nsIFrame * child = aChildList;
|
|
while (child != nsnull) {
|
|
nsIFormControlFrame* fcFrame = nsnull;
|
|
rv = child->QueryInterface(NS_GET_IID(nsIFormControlFrame), (void**)&fcFrame);
|
|
if (NS_FAILED(rv) && fcFrame == nsnull) {
|
|
mDisplayFrame = child;
|
|
} else if (fcFrame != nsnull) {
|
|
PRInt32 type;
|
|
fcFrame->GetType(&type);
|
|
if (type == NS_FORM_INPUT_BUTTON) {
|
|
mButtonFrame = child;
|
|
}
|
|
}
|
|
child->GetNextSibling(&child);
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::GetAdditionalChildListName(PRInt32 aIndex,
|
|
nsIAtom** aListName) const
|
|
{
|
|
// Maintain a seperate child list for the dropdown list (i.e. popup listbox)
|
|
// This is necessary because we don't want the listbox to be included in the layout
|
|
// of the combox's children because it would take up space, when it is suppose to
|
|
// be floating above the display.
|
|
NS_PRECONDITION(nsnull != aListName, "null OUT parameter pointer");
|
|
if (aIndex <= NS_BLOCK_FRAME_ABSOLUTE_LIST_INDEX) {
|
|
return nsAreaFrame::GetAdditionalChildListName(aIndex, aListName);
|
|
}
|
|
|
|
*aListName = nsnull;
|
|
if (NS_COMBO_FRAME_POPUP_LIST_INDEX == aIndex) {
|
|
*aListName = nsLayoutAtoms::popupList;
|
|
NS_ADDREF(*aListName);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
PRIntn
|
|
nsComboboxControlFrame::GetSkipSides() const
|
|
{
|
|
// Don't skip any sides during border rendering
|
|
return 0;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
//nsIRollupListener
|
|
//----------------------------------------------------------------------
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::Rollup()
|
|
{
|
|
if (mDroppedDown) {
|
|
mListControlFrame->AboutToRollup();
|
|
ShowDropDown(PR_FALSE);
|
|
mListControlFrame->CaptureMouseEvents(mPresContext, PR_FALSE);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// nsIStatefulFrame
|
|
// XXX Do we need to implement this here? It is already implemented in
|
|
// the ListControlFrame, our child...
|
|
//----------------------------------------------------------------------
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::SaveState(nsIPresContext* aPresContext, nsIPresState** aState)
|
|
{
|
|
if (!mListControlFrame) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
// The ListControlFrame ignores requests to Save its state
|
|
// when it is owned by a combobox
|
|
// so we call the internal SaveState here
|
|
return mListControlFrame->SaveStateInternal(aPresContext, aState);
|
|
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsComboboxControlFrame::RestoreState(nsIPresContext* aPresContext, nsIPresState* aState)
|
|
{
|
|
// The ListControlFrame ignores requests to Restore its state
|
|
// when it is owned by a combobox
|
|
// so we cache it here if the frame hasn't been created yet
|
|
// or we call the internal RestoreState here
|
|
if (!mListControlFrame) {
|
|
mPresState = aState;
|
|
return NS_OK;
|
|
}
|
|
|
|
return mListControlFrame->RestoreStateInternal(aPresContext, aState);
|
|
}
|
|
|
|
NS_METHOD
|
|
nsComboboxControlFrame::Paint(nsIPresContext* aPresContext,
|
|
nsIRenderingContext& aRenderingContext,
|
|
const nsRect& aDirtyRect,
|
|
nsFramePaintLayer aWhichLayer)
|
|
{
|
|
PRBool isVisible;
|
|
if (NS_SUCCEEDED(IsVisibleForPainting(aPresContext, aRenderingContext, PR_TRUE, &isVisible)) && !isVisible) {
|
|
return NS_OK;
|
|
}
|
|
#ifdef NOISY
|
|
printf("%p paint layer %d at (%d, %d, %d, %d)\n", this, aWhichLayer,
|
|
aDirtyRect.x, aDirtyRect.y, aDirtyRect.width, aDirtyRect.height);
|
|
#endif
|
|
nsAreaFrame::Paint(aPresContext,aRenderingContext,aDirtyRect,aWhichLayer);
|
|
|
|
//if (mGoodToGo) {
|
|
// return NS_OK;
|
|
//}
|
|
|
|
if (NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer) {
|
|
if (mDisplayFrame) {
|
|
aRenderingContext.PushState();
|
|
PRBool clipEmpty;
|
|
nsRect clipRect;
|
|
mDisplayFrame->GetRect(clipRect);
|
|
aRenderingContext.SetClipRect(clipRect, nsClipCombine_kReplace, clipEmpty);
|
|
PaintChild(aPresContext, aRenderingContext, aDirtyRect,
|
|
mDisplayFrame, NS_FRAME_PAINT_LAYER_BACKGROUND);
|
|
PaintChild(aPresContext, aRenderingContext, aDirtyRect,
|
|
mDisplayFrame, NS_FRAME_PAINT_LAYER_FOREGROUND);
|
|
|
|
/////////////////////
|
|
// draw focus
|
|
// XXX This is only temporary
|
|
const nsStyleVisibility* vis =
|
|
(const nsStyleVisibility*)mStyleContext->GetStyleData(eStyleStruct_Visibility);
|
|
// Only paint the focus if we're visible
|
|
if (vis->IsVisible()) {
|
|
nsCOMPtr<nsIEventStateManager> stateManager;
|
|
nsresult rv = mPresContext->GetEventStateManager(getter_AddRefs(stateManager));
|
|
if (NS_SUCCEEDED(rv)) {
|
|
if (NS_SUCCEEDED(rv) && !nsFormFrame::GetDisabled(this) && mFocused == this) {
|
|
aRenderingContext.SetLineStyle(nsLineStyle_kDotted);
|
|
aRenderingContext.SetColor(0);
|
|
} else {
|
|
const nsStyleBackground* myColor =
|
|
(const nsStyleBackground*)mStyleContext->GetStyleData(eStyleStruct_Background);
|
|
aRenderingContext.SetColor(myColor->mBackgroundColor);
|
|
aRenderingContext.SetLineStyle(nsLineStyle_kSolid);
|
|
}
|
|
//aRenderingContext.DrawRect(clipRect);
|
|
float p2t;
|
|
aPresContext->GetPixelsToTwips(&p2t);
|
|
nscoord onePixel = NSIntPixelsToTwips(1, p2t);
|
|
clipRect.width -= onePixel;
|
|
clipRect.height -= onePixel;
|
|
aRenderingContext.DrawLine(clipRect.x, clipRect.y,
|
|
clipRect.x+clipRect.width, clipRect.y);
|
|
aRenderingContext.DrawLine(clipRect.x+clipRect.width, clipRect.y,
|
|
clipRect.x+clipRect.width, clipRect.y+clipRect.height);
|
|
aRenderingContext.DrawLine(clipRect.x+clipRect.width, clipRect.y+clipRect.height,
|
|
clipRect.x, clipRect.y+clipRect.height);
|
|
aRenderingContext.DrawLine(clipRect.x, clipRect.y+clipRect.height,
|
|
clipRect.x, clipRect.y);
|
|
aRenderingContext.DrawLine(clipRect.x, clipRect.y+clipRect.height,
|
|
clipRect.x, clipRect.y);
|
|
}
|
|
}
|
|
/////////////////////
|
|
aRenderingContext.PopState(clipEmpty);
|
|
}
|
|
}
|
|
|
|
// Call to the base class to draw selection borders when appropriate
|
|
return nsFrame::Paint(aPresContext,aRenderingContext,aDirtyRect,aWhichLayer);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
//nsIScrollableViewProvider
|
|
//----------------------------------------------------------------------
|
|
NS_METHOD
|
|
nsComboboxControlFrame::GetScrollableView(nsIScrollableView** aView)
|
|
{
|
|
*aView = nsnull;
|
|
nsIView* view = nsnull;
|
|
mDropdownFrame->GetView(mPresContext, &view);
|
|
if (view) {
|
|
nsIScrollableView* sv = nsnull;
|
|
nsresult rv = view->QueryInterface(NS_GET_IID(nsIScrollableView), (void**) &sv);
|
|
if (NS_SUCCEEDED(rv) && sv)
|
|
*aView = sv;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|