/* -*- 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.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */
#include "nsCOMPtr.h"
#include "nsComboboxControlFrame.h"
#include "nsFormFrame.h"
#include "nsButtonControlFrame.h"
#include "nsTextControlFrame.h"
#include "nsIContent.h"
#include "prtypes.h"
#include "nsIAtom.h"
#include "nsIPresContext.h"
#include "nsIHTMLContent.h"
#include "nsHTMLIIDs.h"
#include "nsHTMLAtoms.h"
#include "nsIFileWidget.h"
#include "nsITextWidget.h"
#include "nsWidgetsCID.h"
#include "nsIComponentManager.h"
#include "nsIView.h"
#include "nsHTMLParts.h"
#include "nsIDOMHTMLInputElement.h"
#include "nsIFormControl.h"
#include "nsINameSpaceManager.h"
#include "nsITextContent.h"

// Used for Paint
#include "nsCSSRendering.h"
#include "nsIDeviceContext.h"

#include "nsIPresShell.h"
#include "nsIView.h"
#include "nsIViewManager.h"
#include "nsViewsCID.h"
#include "nsIDOMElement.h"
#include "nsListControlFrame.h"
#include "nsIListControlFrame.h"
#include "nsIDOMHTMLCollection.h" 
#include "nsIDOMHTMLSelectElement.h" 
#include "nsIDOMHTMLOptionElement.h" 

static NS_DEFINE_IID(kViewCID,                   NS_VIEW_CID);
static NS_DEFINE_IID(kIViewIID,                  NS_IVIEW_IID);
static NS_DEFINE_IID(kCFileWidgetCID,            NS_FILEWIDGET_CID);
static NS_DEFINE_IID(kIFileWidgetIID,            NS_IFILEWIDGET_IID);
static NS_DEFINE_IID(kITextWidgetIID,            NS_ITEXTWIDGET_IID);
static NS_DEFINE_IID(kIFormControlFrameIID,      NS_IFORMCONTROLFRAME_IID);
static NS_DEFINE_IID(kIComboboxControlFrameIID,  NS_ICOMBOBOXCONTROLFRAME_IID);
static NS_DEFINE_IID(kIDOMHTMLSelectElementIID,  NS_IDOMHTMLSELECTELEMENT_IID);
static NS_DEFINE_IID(kIDOMHTMLOptionElementIID,  NS_IDOMHTMLOPTIONELEMENT_IID);
static NS_DEFINE_IID(kIListControlFrameIID,      NS_ILISTCONTROLFRAME_IID);

// 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.

nsresult
NS_NewComboboxControlFrame(nsIFrame** aNewFrame)
{
  NS_PRECONDITION(aNewFrame, "null OUT ptr");
  if (nsnull == aNewFrame) {
    return NS_ERROR_NULL_POINTER;
  }
  nsComboboxControlFrame* it = new nsComboboxControlFrame;
  if (!it) {
    return NS_ERROR_OUT_OF_MEMORY;
  }
  *aNewFrame = it;
  return NS_OK;
}

nsComboboxControlFrame::nsComboboxControlFrame()
  : nsHTMLContainerFrame()
{
  mFormFrame                   = nsnull;
  mListFrame                   = nsnull;
  mListControlFrame            = nsnull;
  mPlaceHolderFrame            = nsnull;
  mVisibleStyleContext         = nsnull;
  mHiddenStyleContext          = nsnull;
  mCurrentStyleContext         = nsnull;
  mBlockTextStyle              = nsnull;
  mBlockTextSelectedStyle      = nsnull;
  mBlockTextSelectedFocusStyle = nsnull;
  mFirstTime                   = PR_TRUE;
  mGotFocus                    = PR_FALSE;
}

//--------------------------------------------------------------
nsComboboxControlFrame::~nsComboboxControlFrame()
{
  NS_IF_RELEASE(mVisibleStyleContext);
  NS_IF_RELEASE(mHiddenStyleContext);
  NS_IF_RELEASE(mBlockTextStyle);
  mFormFrame = nsnull;
}

//--------------------------------------------------------------
nsresult
nsComboboxControlFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr)
{
  NS_PRECONDITION(0 != aInstancePtr, "null ptr");
  if (NULL == aInstancePtr) {
    return NS_ERROR_NULL_POINTER;
  }
  if (aIID.Equals(kIComboboxControlFrameIID)) {
    *aInstancePtr = (void*) ((nsIComboboxControlFrame*) this);
    return NS_OK;
  }
  if (aIID.Equals(kIFormControlFrameIID)) {
    *aInstancePtr = (void*) ((nsIFormControlFrame*) this);
    return NS_OK;
  }

  return nsHTMLContainerFrame::QueryInterface(aIID, aInstancePtr);
}

//--------------------------------------------------------------
PRBool
nsComboboxControlFrame::IsSuccessful(nsIFormControlFrame* aSubmitter)
{
  nsAutoString name;
  return (NS_CONTENT_ATTR_HAS_VALUE == GetName(&name));
}


// 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()
{
   // Update the selected text string
  mListControlFrame->GetSelectedItem(mTextStr);

  if (mTextStr == "") {
     // No selection so use the first item in the list box
    nsIFormControlFrame* fcFrame = nsnull;
    nsresult result = mListFrame->QueryInterface(kIFormControlFrameIID, (void**)&fcFrame);
    if ((NS_OK == result) && (nsnull != fcFrame)) {
       // Set listbox selection to first item in the list box
      fcFrame->SetProperty(nsHTMLAtoms::selectedindex, "0");
       // Get the listbox selection as a string
      mListControlFrame->GetSelectedItem(mTextStr);
    }
  }
}

//--------------------------------------------------------------

void 
nsComboboxControlFrame::Reset()
{
  SetFocus(PR_TRUE, PR_TRUE);
  nsIFormControlFrame* fcFrame = nsnull;
  nsresult result = mListFrame->QueryInterface(kIFormControlFrameIID, (void**)&fcFrame);
  if ((NS_OK == result) && (nsnull != fcFrame)) {
    fcFrame->Reset(); 
  }
 
  InitTextStr();
}

void 
nsComboboxControlFrame::PostCreateWidget(nsIPresContext* aPresContext,
                                       nscoord& aWidth,
                                       nscoord& aHeight)
{
  Reset();
}


//--------------------------------------------------------------
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, 
                             nsFont&                aFont)
{
  nsFormControlHelper::GetFont(this, aPresContext, mStyleContext, aFont);
  return NS_OK;
}


//--------------------------------------------------------------
nscoord 
nsComboboxControlFrame::GetVerticalBorderWidth(float aPixToTwip) const
{
   return NSIntPixelsToTwips(3, aPixToTwip);
}


//--------------------------------------------------------------
nscoord 
nsComboboxControlFrame::GetHorizontalBorderWidth(float aPixToTwip) const
{
  return GetVerticalBorderWidth(aPixToTwip);
}


//--------------------------------------------------------------
nscoord 
nsComboboxControlFrame::GetVerticalInsidePadding(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)
{
  //XXX: TODO Make set focus work 
  //mContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, "SELECTED", PR_TRUE);
  mGotFocus = aOn;
  if (aRepaint) {
    nsFormControlHelper::ForceDrawFrame(this);
  }
}

//--------------------------------------------------------------
// 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)
{
  if (nsnull != mListControlFrame) {
  //XXX: Make this work  SetFocus(PR_FALSE, PR_TRUE);  
    mCurrentStyleContext = (mCurrentStyleContext == mHiddenStyleContext ? mVisibleStyleContext : mHiddenStyleContext);
    if (mCurrentStyleContext == mVisibleStyleContext) {
      mListControlFrame->AboutToDropDown();
    
      nsIFormControlFrame* fcFrame = nsnull;
      nsresult result = mListFrame->QueryInterface(kIFormControlFrameIID, (void**)&fcFrame);
      if ((NS_OK == result) && (nsnull != fcFrame)) {
    //XXX: Make this work    fcFrame->SetFocus(PR_TRUE, PR_FALSE);
      }

    } else {
   //XXX: Make this work  SetFocus(PR_TRUE, PR_TRUE);    
    }

     //XXX: This should not be necessary. Need to restructure the combo box as follows:
     // Derive nsComboboxFrame from nsAreaFrame. Attach the placeholder frame, a label frame and
     // a button frame. Override reresolve style context and reresolve the style on the ListBox.
     // The event state manager should then be asked to set active and non-active based on
     // The mouse click this would get rid of all of the ugly code here. The setting of the active
     // Should cause re-resolution of the AreaFrame which will re-sync it. KMM.
    mListFrame->ReResolveStyleContext(aPresContext, mCurrentStyleContext, NS_STYLE_HINT_NONE, nsnull, nsnull);
   
      // Resync view with frame.
    const nsStyleDisplay* disp = (const
    nsStyleDisplay*)mCurrentStyleContext->GetStyleData(eStyleStruct_Display); 
    nsIView * view; 
    mListFrame->GetView(&view); 
    if (view) { 
     view->SetVisibility(NS_STYLE_VISIBILITY_HIDDEN == disp->mVisible ?
      nsViewVisibility_kHide:nsViewVisibility_kShow); 
    } 
    nsFormControlHelper::ForceDrawFrame(mListFrame); 
    //XXX: End of the ugly code.
  
  }
}

//--------------------------------------------------------------
NS_IMETHODIMP nsComboboxControlFrame::Reflow(nsIPresContext&          aPresContext, 
                                             nsHTMLReflowMetrics&     aDesiredSize,
                                             const nsHTMLReflowState& aReflowState, 
                                             nsReflowStatus&          aStatus)
{
    // XXX Combo box should be changed to be implemented as a label and button frame.
    // This would eliminate all of this Reflow code. KMM

    // add ourself as an nsIFormControlFrame
  if (!mFormFrame && (eReflowReason_Initial == aReflowState.reason)) {
    nsFormFrame::AddFormControlFrame(aPresContext, *this);
  }


  if (mFirstTime) {
    ReResolveStyleContext(&aPresContext, mStyleContext, NS_STYLE_HINT_REFLOW, nsnull, nsnull); // XXX This temporary
    mListFrame->ReResolveStyleContext(&aPresContext, mCurrentStyleContext, NS_STYLE_HINT_REFLOW, nsnull, nsnull);
    mFirstTime = PR_FALSE;
    InitTextStr();
  }

  PRInt32 numChildren = mFrames.GetLength();
  
  if (1 == numChildren) {
    aPresContext.ResolvePseudoStyleContextFor(mContent, nsHTMLAtoms::comboText,
                                              mStyleContext, PR_FALSE,
                                              &mBlockTextStyle);
 
    // XXX This code should move to Init(), someday when the frame construction
    // changes are all done and Init() is always getting called...
    /*PRBool disabled = */nsFormFrame::GetDisabled(this);
  }
  
  nsIDOMHTMLSelectElement* select = nsListControlFrame::GetSelect(mContent);
  if (!select) {
    return NS_OK;
  }
  nsIDOMHTMLCollection* options = nsListControlFrame::GetOptions(mContent, select);
  if (!options) {
    NS_RELEASE(select);
    return NS_OK;
  }

  // get the css size 
  nsSize styleSize;
  nsFormControlFrame::GetStyleSize(aPresContext, aReflowState, styleSize);

  // get the size of the longest option 
  PRInt32  maxWidth = 1;
  PRUint32 numOptions;
  options->GetLength(&numOptions);
  for (PRUint32 i = 0; i < numOptions; i++) {
    nsIDOMHTMLOptionElement* option = nsListControlFrame::GetOption(*options, i);
    if (option) {
       nsAutoString text;
      if (NS_CONTENT_ATTR_HAS_VALUE != option->GetText(text)) {
        continue;
      }
      nsSize textSize;
      // use the style for the select rather that the option, since widgets don't support it
      nsFormControlHelper::GetTextSize(aPresContext, this, text, textSize, aReflowState.rendContext); 
      if (textSize.width > maxWidth) {
        maxWidth = textSize.width;
      }
      NS_RELEASE(option);
    }
  }

  PRInt32 rowHeight = 0;
  nsSize desiredSize;
  nsSize minSize;
  PRBool widthExplicit, heightExplicit;
  nsInputDimensionSpec textSpec(nsnull, PR_FALSE, nsnull, nsnull,
                                maxWidth, PR_TRUE, nsHTMLAtoms::size, 1);
  // XXX fix CalculateSize to return PRUint32
  PRUint32 numRows = 
    (PRUint32)nsFormControlHelper::CalculateSize(&aPresContext, aReflowState.rendContext, this, 
                                                 styleSize, textSpec, desiredSize, minSize,
                                                 widthExplicit, heightExplicit, rowHeight);

  float sp2t;
  float p2t;
  aPresContext.GetPixelsToTwips(&p2t);
  aPresContext.GetScaledPixelsToTwips(&sp2t);
  nscoord onePixel = NSIntPixelsToTwips(1, sp2t);
  nscoord extra = desiredSize.height - (rowHeight * numRows);
  numRows = (numOptions > 20 ? 20 : numOptions);
  desiredSize.height = (numRows * rowHeight) + extra;
  aDesiredSize.descent = 0;
  nsMargin  border;
  const nsStyleSpacing* mySpacing = (const nsStyleSpacing*)mStyleContext->GetStyleData(eStyleStruct_Spacing);
  mySpacing->CalcBorderFor(this, border);

  float scale;
  float sbWidth;
  float sbHeight;
  nsCOMPtr<nsIDeviceContext> context;
  aPresContext.GetDeviceContext(getter_AddRefs(context));
  context->GetCanonicalPixelScale(scale);
  context->GetScrollBarDimensions(sbWidth, sbHeight);
  PRInt32 scrollbarScaledWidth  = PRInt32(sbWidth * scale);

  nsFont font(aPresContext.GetDefaultFixedFontDeprecated());
  SystemAttrStruct sis;
  sis.mFont = &font;
  context->GetSystemAttribute(eSystemAttr_Font_Tooltips, &sis);

   //XXX: This 14 pixel hardcode value here is bad. The style system should control
   //everyting. KMM
  nscoord horKludgeAdjustment = NSIntPixelsToTwips(14, p2t);
  nscoord horAdjustment = scrollbarScaledWidth + border.left + border.right + horKludgeAdjustment;

  aDesiredSize.width  = desiredSize.width + horAdjustment;

  nscoord verKludgeAdjustment = onePixel;
  nscoord verAdjustment = border.top + border.bottom + verKludgeAdjustment;

  aDesiredSize.height = rowHeight + verAdjustment;

  mButtonRect.SetRect(aDesiredSize.width-scrollbarScaledWidth-border.right, border.top, 
                      scrollbarScaledWidth, aDesiredSize.height - verAdjustment);

  if (nsnull != aDesiredSize.maxElementSize) {
    aDesiredSize.maxElementSize->width  = minSize.width  + verAdjustment;
	  aDesiredSize.maxElementSize->height = minSize.height + horAdjustment;
  }

  nsRect frameRect;
  GetRect(frameRect);
  nsRect curRect;
  mPlaceHolderFrame->GetRect(curRect);
  curRect.x = 0; 

  curRect.y = frameRect.y + aDesiredSize.height;
  mPlaceHolderFrame->SetRect(curRect);
  mListFrame->GetRect(frameRect); 
  
  aDesiredSize.ascent = aDesiredSize.height;
  aDesiredSize.descent = 0;
  aStatus = NS_FRAME_COMPLETE;

  return NS_OK;
}

//------------------------------------------------------------------------------
void
nsComboboxControlFrame::PaintComboboxControl(nsIPresContext&      aPresContext,
                                             nsIRenderingContext& aRenderingContext,
                                             const nsRect&        aDirtyRect,
                                             nsFramePaintLayer    aWhichLayer)
{
     //XXX Combo box should be changed to be implemented as a label and button frame.
     //This would eliminate all of this rendering code. KMM
    const nsStyleDisplay* disp = (const nsStyleDisplay*)mStyleContext->GetStyleData(eStyleStruct_Display);
    if (!disp->mVisible) {
      return;
    }
    aRenderingContext.PushState();

    const nsStyleColor*   myColor   = (const nsStyleColor*)mStyleContext->GetStyleData(eStyleStruct_Color);
    const nsStyleSpacing* mySpacing = (const nsStyleSpacing*)mStyleContext->GetStyleData(eStyleStruct_Spacing);
    const nsStyleFont*    myFont    = (const nsStyleFont*)mStyleContext->GetStyleData(eStyleStruct_Font);

    nsIStyleContext * blkStyle;
    if (mGotFocus) {
      blkStyle = mTextStr.Length() > 0?mBlockTextSelectedFocusStyle:mBlockTextStyle;
    } else {
      blkStyle = mTextStr.Length() > 0?mBlockTextSelectedStyle:mBlockTextStyle;
    }
    const nsStyleColor*   blkColor   = (const nsStyleColor*)blkStyle->GetStyleData(eStyleStruct_Color);
    const nsStyleSpacing* blkSpacing = (const nsStyleSpacing*)blkStyle->GetStyleData(eStyleStruct_Spacing);
//    const nsStyleFont*    blkFont    = (const nsStyleFont*)blkStyle->GetStyleData(eStyleStruct_Font);

    nsRect rect(0, 0, mRect.width, mRect.height);
    nsCSSRendering::PaintBackground(aPresContext, aRenderingContext, this,
                                    aDirtyRect, rect, *myColor, *mySpacing, 0, 0);

    nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, this,
                                aDirtyRect, rect, *mySpacing, mStyleContext, 0);

    nsHTMLContainerFrame::Paint(aPresContext, aRenderingContext, aDirtyRect, aWhichLayer);

    const nsStyleSpacing* spacing =(const nsStyleSpacing*)mStyleContext->GetStyleData(eStyleStruct_Spacing);
    nsMargin border;
    spacing->CalcBorderFor(this, border);

    float p2t;
    aPresContext.GetScaledPixelsToTwips(&p2t);
    nscoord onePixel = NSIntPixelsToTwips(1, p2t);

    nsRect outside(0, 0, mRect.width, mRect.height);
    outside.Deflate(border);

    nsRect inside(outside);
    outside.Deflate(onePixel, onePixel);

    aRenderingContext.SetColor(blkColor->mBackgroundColor);
    aRenderingContext.FillRect(inside.x, inside.y, inside.width, inside.height);

    float appUnits;
    float devUnits;
    float scale;
    nsIDeviceContext * context;
    aRenderingContext.GetDeviceContext(context);

    context->GetCanonicalPixelScale(scale);
    context->GetAppUnitsToDevUnits(devUnits);
    context->GetDevUnitsToAppUnits(appUnits);

    float sbWidth;
    float sbHeight;
    context->GetScrollBarDimensions(sbWidth, sbHeight);
    PRInt32 scrollbarScaledWidth  = PRInt32(sbWidth * scale);
    PRInt32 scrollbarScaledHeight = PRInt32(sbWidth * scale);

    nsFont font(aPresContext.GetDefaultFixedFontDeprecated()); 
    GetFont(&aPresContext, font);

    aRenderingContext.SetFont(myFont->mFont);

    inside.width  -= scrollbarScaledWidth;
    PRBool clipEmpty;
    aRenderingContext.PushState();
    aRenderingContext.SetClipRect(inside, nsClipCombine_kReplace, clipEmpty);

    nscoord x = inside.x + (onePixel * 4);
    nscoord y = inside.y;

    aRenderingContext.SetColor(blkColor->mColor);

    aRenderingContext.DrawString(mTextStr, x, y);
    nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, this,
                                aDirtyRect, inside, *blkSpacing, blkStyle, 0);

    aRenderingContext.PopState(clipEmpty);

    inside.width  -= scrollbarScaledWidth;
    inside.height -= scrollbarScaledHeight;

    const nsStyleSpacing* arrowSpacing = (const nsStyleSpacing*)mArrowStyle->GetStyleData(eStyleStruct_Spacing);

    nsRect srect(0,0,0,0);
    srect = mButtonRect;
    nsFormControlHelper::PaintArrow(nsFormControlHelper::eArrowDirection_Down, aRenderingContext,aPresContext, 
                    aDirtyRect, srect, onePixel, mArrowStyle, *arrowSpacing, this, mRect);
   

    NS_RELEASE(context);

    PRBool status;
    aRenderingContext.PopState(status);
}

NS_METHOD 
nsComboboxControlFrame::Paint(nsIPresContext& aPresContext,
                          nsIRenderingContext& aRenderingContext,
                          const nsRect& aDirtyRect,
                          nsFramePaintLayer aWhichLayer)
{
  if (NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer) {
    PaintComboboxControl(aPresContext, aRenderingContext, aDirtyRect, aWhichLayer);
  }
  return NS_OK;
}

//--------------------------------------------------------------
PRIntn
nsComboboxControlFrame::GetSkipSides() const
{
  return 0;
}


//--------------------------------------------------------------
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;
}
  
//--------------------------------------------------------------
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;
}

//--------------------------------------------------------------
NS_IMETHODIMP
nsComboboxControlFrame::GetFrameName(nsString& aResult) const
{
  return MakeFrameName("ComboboxControl", aResult);
}


void
nsComboboxControlFrame::RefreshStyleContext(nsIPresContext* aPresContext,
                                            nsIAtom *         aNewContentPseudo,
                                            nsIStyleContext*& aCurrentStyle,
                                            nsIContent *      aContent,
                                            nsIStyleContext*  aParentStyle) 

{
  nsIStyleContext* newStyleContext;
  aPresContext->ProbePseudoStyleContextFor(aContent,
                                           aNewContentPseudo,
                                           aParentStyle,
                                           PR_FALSE,
                                           &newStyleContext);
  if (newStyleContext != aCurrentStyle) {
    NS_IF_RELEASE(aCurrentStyle);
    aCurrentStyle = newStyleContext;
  } else {
    NS_IF_RELEASE(newStyleContext);
  }
}

NS_IMETHODIMP
nsComboboxControlFrame::ReResolveStyleContext(nsIPresContext* aPresContext,
                                              nsIStyleContext* aParentContext,
                                              PRInt32 aParentChange,
                                              nsStyleChangeList* aChangeList,
                                              PRInt32* aLocalChange)
{

  // NOTE: using nsFrame's ReResolveStyleContext method to avoid
  // useless version in base classes.
  PRInt32 ourChange = aParentChange;
  nsresult rv = nsHTMLContainerFrame::ReResolveStyleContext(aPresContext, aParentContext, 
                                                         ourChange, aChangeList, &ourChange); 
  if (NS_FAILED(rv)) {
    return rv;
  }

  if (aLocalChange) {
    *aLocalChange = ourChange;
  }

  if (NS_COMFALSE != rv) {
    PRInt32 childChange;
    PRBool currentIsVisible = (mCurrentStyleContext == mVisibleStyleContext?PR_TRUE:PR_FALSE);

    RefreshStyleContext(aPresContext, nsHTMLAtoms::dropDownVisible, mVisibleStyleContext, mContent, mStyleContext);

    RefreshStyleContext(aPresContext, nsHTMLAtoms::dropDownHidden, mHiddenStyleContext, mContent, mStyleContext);
    mCurrentStyleContext = (currentIsVisible?mVisibleStyleContext:mHiddenStyleContext);

    mListFrame->ReResolveStyleContext(aPresContext, 
                                      (nsnull != mCurrentStyleContext? mCurrentStyleContext : mStyleContext),
                                      ourChange, aChangeList, &childChange);

    // Button Style
    RefreshStyleContext(aPresContext, nsHTMLAtoms::dropDownBtnOut, mBtnOutStyleContext, mContent, mStyleContext);
    RefreshStyleContext(aPresContext, nsHTMLAtoms::dropDownBtnPressed, mBtnPressedStyleContext, mContent, mStyleContext);

    //Need to reset the mArrowStyle here, otherwise it might end up pointing
    //to memory that has been freed, by the RefreshStyleContext above.
    mArrowStyle = mBtnOutStyleContext;

    RefreshStyleContext(aPresContext, nsHTMLAtoms::comboText, mBlockTextStyle, mContent, mStyleContext);
    RefreshStyleContext(aPresContext, nsHTMLAtoms::comboTextSelected, mBlockTextSelectedStyle, mContent, mStyleContext);
    RefreshStyleContext(aPresContext, nsHTMLAtoms::comboTextSelectedFocus, mBlockTextSelectedFocusStyle, mContent, mStyleContext);
  }

  return rv;

}
//----------------------------------------------------------------------
NS_IMETHODIMP nsComboboxControlFrame::HandleEvent(nsIPresContext& aPresContext, 
                                                   nsGUIEvent*     aEvent,
                                                   nsEventStatus&  aEventStatus)
{
  if (aEvent->message == NS_MOUSE_EXIT) {
     // If dropdown is visible and we have exited the combo box then leave the
     // list up and have the listbox capture all of the mouse events.
    if (mCurrentStyleContext == mVisibleStyleContext) {
      mListControlFrame->CaptureMouseEvents(PR_TRUE);
    }
  }

  if (nsEventStatus_eConsumeNoDefault == aEventStatus) {
    return NS_OK;
  }

  aEventStatus = nsEventStatus_eConsumeNoDefault;

  if (aEvent->message == NS_MOUSE_LEFT_BUTTON_UP) {
    mArrowStyle = mBtnOutStyleContext;
    nsFormControlHelper::ForceDrawFrame(this);
  } else if (aEvent->message == NS_MOUSE_LEFT_BUTTON_DOWN) {
    mArrowStyle = mBtnPressedStyleContext;
    nsFormControlHelper::ForceDrawFrame(this);
    MouseClicked(&aPresContext);
  }
  return NS_OK;
}

//----------------------------------------------------------------------
NS_IMETHODIMP
nsComboboxControlFrame::GetFrameForPoint(const nsPoint& aPoint, nsIFrame** aFrame)
{
  *aFrame = this;
  return NS_OK;
}


//----------------------------------------------------------------------
// nsIComboboxControlFrame
//----------------------------------------------------------------------

NS_IMETHODIMP
nsComboboxControlFrame::SetDropDown(nsIFrame* aPlaceHolderFrame, nsIFrame* aDropDownFrame)
{
  mPlaceHolderFrame = aPlaceHolderFrame;
  mListFrame        = aDropDownFrame;

  if (NS_OK != mListFrame->QueryInterface(kIListControlFrameIID, (void**)&mListControlFrame)) {
    return NS_ERROR_FAILURE;
  }

  // Ok, since we now know we have the ListFrame, and we are assuming at this point it has been initialized
  // Let's get the currently selected item, but we make the call using the Interface
  mListControlFrame->GetSelectedItem(mTextStr);
  
  return NS_OK;
}


//--------------------------------------------------------------
NS_IMETHODIMP
nsComboboxControlFrame::SetDropDownStyleContexts(nsIStyleContext * aVisible, nsIStyleContext * aHidden)
{
  mVisibleStyleContext = aVisible;
  mHiddenStyleContext  = aHidden;
  mCurrentStyleContext = mHiddenStyleContext;

  NS_ADDREF(mVisibleStyleContext);
  NS_ADDREF(mHiddenStyleContext);
  return NS_OK;
}

//--------------------------------------------------------------
NS_IMETHODIMP
nsComboboxControlFrame::SetButtonStyleContexts(nsIStyleContext * aOut, nsIStyleContext * aPressed)
{
  mBtnOutStyleContext     = aOut;
  mBtnPressedStyleContext = aPressed;
  mArrowStyle             = aOut;

  NS_ADDREF(mBtnOutStyleContext);
  NS_ADDREF(mBtnPressedStyleContext);
  return NS_OK;
}

void
nsComboboxControlFrame::SelectionChanged()
{
  // Generate a NS_ONCHANGE event.

  //XXX: Todo complete the implementation of selection changed
//  nsIViewManager    *vm;
//  view->GetViewManager(vm);
//  nsGUIEvent event;
//  event.eventStructType = NS_GUI_EVENT;
//  event.widget = this; //XXX: How do I get a widget
// NS_ADDREF(event.widget);
//  NS_CONTROL_CHANGE
//  InitEvent(event, aMsg);
//  vm->DispatchEvent(&event, result);
//  NS_RELEASE(event.widget);
//  NS_RELEASE(vm);

}

//--------------------------------------------------------------
NS_IMETHODIMP
nsComboboxControlFrame::ListWasSelected(nsIPresContext* aPresContext)
{
  mArrowStyle = mBtnOutStyleContext;
  MouseClicked(aPresContext);

  nsString str;
  if (nsnull != mListControlFrame) {
    mListControlFrame->GetSelectedItem(str);
     // Check to see if the selection changed
    if (PR_FALSE == str.Equals(mTextStr)) {
      mTextStr = str;
      SelectionChanged();
    } 
    nsIFormControlFrame* fcFrame = nsnull;
    nsresult result = mListFrame->QueryInterface(kIFormControlFrameIID, (void**)&fcFrame);
    if ((NS_OK == result) && (nsnull != fcFrame)) {
      fcFrame->SetFocus(PR_FALSE, PR_FALSE);
    }
    SetFocus(PR_TRUE, PR_TRUE);    
  }
 
  mListControlFrame->CaptureMouseEvents(PR_FALSE);

  return NS_OK;
}


nsresult nsComboboxControlFrame::RequiresWidget(PRBool& aRequiresWidget)
{
  aRequiresWidget = PR_FALSE;
  return NS_OK;
}


NS_IMETHODIMP nsComboboxControlFrame::SetProperty(nsIAtom* aName, const nsString& aValue)
{
  nsIFormControlFrame* fcFrame = nsnull;
  nsresult result = mListFrame->QueryInterface(kIFormControlFrameIID, (void**)&fcFrame);
  if ((NS_SUCCEEDED(result)) && (nsnull != fcFrame)) {
    return fcFrame->SetProperty(aName, aValue);
  }
  return result;
}

NS_IMETHODIMP nsComboboxControlFrame::GetProperty(nsIAtom* aName, nsString& aValue)
{
  nsIFormControlFrame* fcFrame = nsnull;
  nsresult result = mListFrame->QueryInterface(kIFormControlFrameIID, (void**)&fcFrame);
  if ((NS_SUCCEEDED(result)) && (nsnull != fcFrame)) {
    return fcFrame->GetProperty(aName, aValue);
  }
  return result;
}

