Mozilla/mozilla/layout/forms/nsFileControlFrame.cpp
rods%netscape.com b23e7af50b This checkin enables mozilla to support the printing of selection, the printing of page ranges, and
the printing of headers and footers.
Printing of selection is implemented by the frames figuring out if they are in the selection and painting
if they or not they they don't paint. This also only allows the printing of the first page of
selections, alothough it is well documented where this is implemeted so it can be removed.
Bugs 63426, 31218, 61075 r=dcone,kmcclusk,erik,buster sr=waterson


git-svn-id: svn://10.0.0.236/trunk@85624 18797224-902f-48f8-a5cc-f745e15eee43
2001-01-27 14:09:34 +00:00

702 lines
21 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):
*/
#include "nsFileControlFrame.h"
#include "nsFormFrame.h"
#include "nsIContent.h"
#include "prtypes.h"
#include "nsIAtom.h"
#include "nsIPresContext.h"
#include "nsIHTMLContent.h"
#include "nsHTMLIIDs.h"
#include "nsHTMLAtoms.h"
#include "nsIPresState.h"
#include "nsWidgetsCID.h"
#include "nsIComponentManager.h"
#include "nsIView.h"
#include "nsHTMLParts.h"
#include "nsIDOMHTMLInputElement.h"
#include "nsIFormControl.h"
#include "nsINameSpaceManager.h"
#include "nsCOMPtr.h"
#include "nsISupportsArray.h"
#include "nsIDOMElement.h"
#include "nsIDOMDocument.h"
#include "nsIDocument.h"
#include "nsIDOMMouseListener.h"
#include "nsIPresShell.h"
#include "nsIDOMHTMLInputElement.h"
#include "nsIStatefulFrame.h"
#include "nsISupportsPrimitives.h"
#include "nsIComponentManager.h"
#include "nsIDOMWindowInternal.h"
#include "nsIFilePicker.h"
#include "nsIDOMMouseEvent.h"
#include "nsINodeInfo.h"
#include "nsIDOMEventReceiver.h"
#include "nsIScriptGlobalObject.h"
nsresult
NS_NewFileControlFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame)
{
NS_PRECONDITION(aNewFrame, "null OUT ptr");
if (nsnull == aNewFrame) {
return NS_ERROR_NULL_POINTER;
}
nsFileControlFrame* it = new (aPresShell) nsFileControlFrame();
if (!it) {
return NS_ERROR_OUT_OF_MEMORY;
}
*aNewFrame = it;
return NS_OK;
}
nsFileControlFrame::nsFileControlFrame():
mTextFrame(nsnull),
mFormFrame(nsnull),
mTextContent(nsnull),
mCachedState(nsnull)
{
//Shrink the area around it's contents
SetFlags(NS_BLOCK_SHRINK_WRAP);
}
nsFileControlFrame::~nsFileControlFrame()
{
NS_IF_RELEASE(mTextContent);
// remove ourself as a listener of the button (bug 40533)
if (mBrowse) {
nsCOMPtr<nsIDOMEventReceiver> reciever(do_QueryInterface(mBrowse));
reciever->RemoveEventListenerByIID(this, NS_GET_IID(nsIDOMMouseListener));
}
if (mCachedState) {
delete mCachedState;
mCachedState = nsnull;
}
if (mFormFrame) {
mFormFrame->RemoveFormControlFrame(*this);
mFormFrame = nsnull;
}
}
NS_IMETHODIMP
nsFileControlFrame::CreateAnonymousContent(nsIPresContext* aPresContext,
nsISupportsArray& aChildList)
{
// create text field
nsCOMPtr<nsIDocument> doc;
mContent->GetDocument(*getter_AddRefs(doc));
nsCOMPtr<nsINodeInfoManager> nimgr;
nsresult rv = doc->GetNodeInfoManager(*getter_AddRefs(nimgr));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsINodeInfo> nodeInfo;
nimgr->GetNodeInfo(nsHTMLAtoms::input, nsnull, kNameSpaceID_None,
*getter_AddRefs(nodeInfo));
if (NS_OK == NS_NewHTMLInputElement(&mTextContent, nodeInfo)) {
mTextContent->SetAttribute(kNameSpaceID_None, nsHTMLAtoms::type, NS_ConvertASCIItoUCS2("text"), PR_FALSE);
if (nsFormFrame::GetDisabled(this)) {
nsCOMPtr<nsIDOMHTMLInputElement> textControl = do_QueryInterface(mTextContent);
if (textControl) {
textControl->SetDisabled(nsFormFrame::GetDisabled(this));
}
}
aChildList.AppendElement(mTextContent);
}
// create browse button
if (NS_OK == NS_NewHTMLInputElement(getter_AddRefs(mBrowse), nodeInfo)) {
mBrowse->SetAttribute(kNameSpaceID_None, nsHTMLAtoms::type, NS_ConvertASCIItoUCS2("button"), PR_FALSE);
//browse->SetAttribute(kNameSpaceID_None, nsHTMLAtoms::value, nsAutoString("browse..."), PR_FALSE);
aChildList.AppendElement(mBrowse);
// register as an event listener of the button to open file dialog on mouse click
nsCOMPtr<nsIDOMEventReceiver> reciever(do_QueryInterface(mBrowse));
reciever->AddEventListenerByIID(this, NS_GET_IID(nsIDOMMouseListener));
}
nsString value;
if (NS_CONTENT_ATTR_HAS_VALUE == mContent->GetAttribute(kNameSpaceID_None, nsHTMLAtoms::size, value)) {
mTextContent->SetAttribute(kNameSpaceID_None, nsHTMLAtoms::size, value, PR_FALSE);
}
return NS_OK;
}
// Frames are not refcounted, no need to AddRef
NS_IMETHODIMP
nsFileControlFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr)
{
NS_PRECONDITION(0 != aInstancePtr, "null ptr");
if (NULL == aInstancePtr) {
return NS_ERROR_NULL_POINTER;
} else if (aIID.Equals(NS_GET_IID(nsIAnonymousContentCreator))) {
*aInstancePtr = (void*)(nsIAnonymousContentCreator*) 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(nsIDOMMouseListener))) {
*aInstancePtr = (void*)(nsIDOMMouseListener*) this;
return NS_OK;
} else if (aIID.Equals(NS_GET_IID(nsIStatefulFrame))) {
*aInstancePtr = (void*)(nsIStatefulFrame*) this;
return NS_OK;
}
return nsHTMLContainerFrame::QueryInterface(aIID, aInstancePtr);
}
PRBool
nsFileControlFrame::IsSuccessful(nsIFormControlFrame* aSubmitter)
{
nsAutoString name;
PRBool disabled = PR_FALSE;
nsFormControlHelper::GetDisabled(mContent, &disabled);
return !disabled && (NS_CONTENT_ATTR_HAS_VALUE == GetName(&name));
}
void
nsFileControlFrame::Reset(nsIPresContext* aPresContext)
{
if (mTextFrame) {
mTextFrame->Reset(aPresContext);
}
}
NS_IMETHODIMP
nsFileControlFrame::GetType(PRInt32* aType) const
{
*aType = NS_FORM_INPUT_FILE;
return NS_OK;
}
void
nsFileControlFrame::SetFocus(PRBool aOn, PRBool aRepaint)
{
// Fix for Bug 6133
if (mTextFrame) {
nsCOMPtr<nsIContent> content;
mTextFrame->GetContent(getter_AddRefs(content));
if (content) {
content->SetFocus(mPresContext);
}
}
}
void
nsFileControlFrame::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);
}
}
}
/**
* This is called when our browse button is clicked
*/
nsresult
nsFileControlFrame::MouseClick(nsIDOMEvent* aMouseEvent)
{
// only allow the left button
nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aMouseEvent);
if (mouseEvent) {
PRUint16 whichButton;
if (NS_SUCCEEDED(mouseEvent->GetButton(&whichButton))) {
if (whichButton != 1) {
return NS_OK;
}
}
}
nsresult result;
// Get parent nsIDOMWindowInternal object.
nsCOMPtr<nsIContent> content;
result = GetContent(getter_AddRefs(content));
if (!content)
return NS_FAILED(result) ? result : NS_ERROR_FAILURE;
nsCOMPtr<nsIDocument> doc;
result = content->GetDocument(*getter_AddRefs(doc));
if (!doc)
return NS_FAILED(result) ? result : NS_ERROR_FAILURE;
nsCOMPtr<nsIScriptGlobalObject> scriptGlobalObject;
result = doc->GetScriptGlobalObject(getter_AddRefs(scriptGlobalObject));
if (!scriptGlobalObject)
return NS_FAILED(result) ? result : NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMWindowInternal> parentWindow = do_QueryInterface(scriptGlobalObject);
if (!parentWindow)
return NS_ERROR_FAILURE;
// Get Loc title
nsString title;
nsFormControlHelper::GetLocalizedString(nsFormControlHelper::GetHTMLPropertiesFileName(),
"FileUpload", title);
nsCOMPtr<nsIFilePicker> filePicker = do_CreateInstance("@mozilla.org/filepicker;1");
if (!filePicker)
return NS_ERROR_FAILURE;
result = filePicker->Init(parentWindow, title.GetUnicode(), nsIFilePicker::modeOpen);
if (NS_FAILED(result))
return result;
// Set filter "All Files"
filePicker->AppendFilters(nsIFilePicker::filterAll);
// Set default directry and filename
nsAutoString defaultName;
GetProperty(nsHTMLAtoms::value, defaultName);
nsCOMPtr<nsILocalFile> currentFile = do_CreateInstance("@mozilla.org/file/local;1");
if (currentFile && !defaultName.IsEmpty()) {
result = currentFile->InitWithUnicodePath(defaultName.GetUnicode());
if (NS_SUCCEEDED(result)) {
PRUnichar *leafName = nsnull;
currentFile->GetUnicodeLeafName(&leafName);
if (leafName) {
filePicker->SetDefaultString(leafName);
nsMemory::Free(leafName);
}
// set directory
nsCOMPtr<nsIFile> parentFile;
currentFile->GetParent(getter_AddRefs(parentFile));
if (parentFile) {
nsCOMPtr<nsILocalFile> parentLocalFile = do_QueryInterface(parentFile, &result);
if (parentLocalFile)
filePicker->SetDisplayDirectory(parentLocalFile);
}
}
}
// Open dialog
PRInt16 mode;
result = filePicker->Show(&mode);
if (NS_FAILED(result))
return result;
if (mode == nsIFilePicker::returnCancel)
return NS_OK;
// Set property
nsCOMPtr<nsILocalFile> localFile;
result = filePicker->GetFile(getter_AddRefs(localFile));
if (localFile) {
PRUnichar *nativePath = nsnull;
result = localFile->GetUnicodePath(&nativePath);
if (nativePath) {
nsAutoString pathName(nativePath);
mTextFrame->SetProperty(mPresContext, nsHTMLAtoms::value, pathName);
nsMemory::Free(nativePath);
return NS_OK;
}
}
return NS_FAILED(result) ? result : NS_ERROR_FAILURE;
}
NS_IMETHODIMP nsFileControlFrame::Reflow(nsIPresContext* aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
DO_GLOBAL_REFLOW_COUNT("nsFileControlFrame", aReflowState.reason);
aStatus = NS_FRAME_COMPLETE;
if (mFormFrame == nsnull && eReflowReason_Initial == aReflowState.reason) {
// add ourself as an nsIFormControlFrame
nsFormFrame::AddFormControlFrame(aPresContext, *NS_STATIC_CAST(nsIFrame*, this));
mTextFrame = GetTextControlFrame(aPresContext, this);
if (!mTextFrame) return NS_ERROR_UNEXPECTED;
if (mCachedState) {
mTextFrame->SetProperty(aPresContext, nsHTMLAtoms::value, *mCachedState);
delete mCachedState;
mCachedState = nsnull;
}
}
// The Areaframe takes care of all our reflow
// except for when style is used to change its size.
nsresult rv = nsAreaFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus);
if (NS_SUCCEEDED(rv) && mTextFrame != nsnull) {
nsIFrame * child;
FirstChild(aPresContext, nsnull, &child);
while (child == mTextFrame) {
child->GetNextSibling(&child);
}
if (child != nsnull) {
nsRect buttonRect;
nsRect txtRect;
mTextFrame->GetRect(txtRect);
child->GetRect(buttonRect);
// check to see if we must reflow just the texField
// because style width or height was set.
if (txtRect.width + buttonRect.width != aDesiredSize.width ||
txtRect.height != aDesiredSize.height) {
nsSize txtAvailSize(aDesiredSize.width - buttonRect.width, aDesiredSize.height);
nsHTMLReflowMetrics txtKidSize(&txtAvailSize);
nsHTMLReflowState txtKidReflowState(aPresContext, aReflowState, mTextFrame, txtAvailSize);
txtKidReflowState.reason = eReflowReason_Resize;
txtKidReflowState.mComputedWidth = txtAvailSize.width;
txtKidReflowState.mComputedHeight = txtAvailSize.height;
mTextFrame->WillReflow(aPresContext);
nsReflowStatus status;
rv = mTextFrame->Reflow(aPresContext, txtKidSize, txtKidReflowState, status);
if (NS_FAILED(rv)) return rv;
rv = mTextFrame->DidReflow(aPresContext, aStatus);
if (NS_FAILED(rv)) return rv;
// now adjust the frame positions
buttonRect.x = aDesiredSize.width - buttonRect.width + aReflowState.mComputedBorderPadding.left;
child->SetRect(aPresContext, buttonRect);
txtRect.y = aReflowState.mComputedBorderPadding.top;
txtRect.height = aDesiredSize.height;
txtRect.width = aDesiredSize.width - buttonRect.width;
mTextFrame->SetRect(aPresContext, txtRect);
}
}
}
return rv;
}
/*
NS_IMETHODIMP
nsFileControlFrame::SetInitialChildList(nsIPresContext* aPresContext,
nsIAtom* aListName,
nsIFrame* aChildList)
{
nsresult r = nsAreaFrame::SetInitialChildList(aPresContext, aListName, aChildList);
// given that the CSS frame constuctor created all our frames. We need to find the text field
// so we can get info from it.
mTextFrame = GetTextControlFrame(this);
}
*/
/**
* Given a start node. Find the given text node inside. We need to do this because the
* frame constuctor create the frame and its implementation. So we are given the text
* node from the constructor and we find it in our tree.
*/
nsNewFrame*
nsFileControlFrame::GetTextControlFrame(nsIPresContext* aPresContext, nsIFrame* aStart)
{
nsNewFrame* result = nsnull;
#ifndef DEBUG_NEWFRAME
// find the text control frame.
nsIFrame* childFrame = nsnull;
aStart->FirstChild(aPresContext, nsnull, &childFrame);
while (childFrame) {
// see if the child is a text control
nsCOMPtr<nsIContent> content;
nsresult res = childFrame->GetContent(getter_AddRefs(content));
if (NS_SUCCEEDED(res) && content) {
nsCOMPtr<nsIAtom> atom;
res = content->GetTag(*getter_AddRefs(atom));
if (NS_SUCCEEDED(res) && atom) {
if (atom.get() == nsHTMLAtoms::input) {
// It's an input, is it a text input?
nsAutoString value;
if (NS_CONTENT_ATTR_HAS_VALUE == content->GetAttribute(kNameSpaceID_None, nsHTMLAtoms::type, value)) {
if (value.EqualsIgnoreCase("text")) {
result = (nsNewFrame*)childFrame;
}
}
}
}
}
// if not continue looking
nsNewFrame* frame = GetTextControlFrame(aPresContext, childFrame);
if (frame)
result = frame;
res = childFrame->GetNextSibling(&childFrame);
NS_ASSERTION(res == NS_OK,"failed to get next child");
}
return result;
#else
return nsnull;
#endif
}
PRIntn
nsFileControlFrame::GetSkipSides() const
{
return 0;
}
NS_IMETHODIMP
nsFileControlFrame::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
nsFileControlFrame::GetMaxNumValues()
{
return 1;
}
PRBool
nsFileControlFrame::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;
nsresult status = PR_FALSE;
if (NS_SUCCEEDED(mTextFrame->GetProperty(nsHTMLAtoms::value, aValues[0]))) {
aNumValues = 1;
status = PR_TRUE;
}
return status;
}
NS_IMETHODIMP
nsFileControlFrame::AttributeChanged(nsIPresContext* aPresContext,
nsIContent* aChild,
PRInt32 aNameSpaceID,
nsIAtom* aAttribute,
PRInt32 aHint)
{
// set the text control to readonly or not
if (nsHTMLAtoms::disabled == aAttribute) {
nsCOMPtr<nsIDOMHTMLInputElement> textControl = do_QueryInterface(mTextContent);
if (textControl)
{
textControl->SetDisabled(nsFormFrame::GetDisabled(this));
}
} else if (nsHTMLAtoms::size == aAttribute) {
nsString value;
if (nsnull != mTextContent && NS_CONTENT_ATTR_HAS_VALUE == mContent->GetAttribute(kNameSpaceID_None, nsHTMLAtoms::size, value)) {
mTextContent->SetAttribute(kNameSpaceID_None, nsHTMLAtoms::size, value, PR_TRUE);
if (aHint != NS_STYLE_HINT_REFLOW) {
nsFormFrame::StyleChangeReflow(aPresContext, this);
}
}
}
return nsAreaFrame::AttributeChanged(aPresContext, aChild, aNameSpaceID, aAttribute, aHint);
}
NS_IMETHODIMP
nsFileControlFrame::GetFrameForPoint(nsIPresContext* aPresContext,
const nsPoint& aPoint,
nsFramePaintLayer aWhichLayer,
nsIFrame** aFrame)
{
#ifndef DEBUG_NEWFRAME
if ( nsFormFrame::GetDisabled(this) && mRect.Contains(aPoint) ) {
const nsStyleDisplay* disp = (const nsStyleDisplay*)
mStyleContext->GetStyleData(eStyleStruct_Display);
if (disp->IsVisible()) {
*aFrame = this;
return NS_OK;
}
} else {
return nsAreaFrame::GetFrameForPoint(aPresContext, aPoint, aWhichLayer, aFrame);
}
#endif
return NS_OK;
}
#ifdef NS_DEBUG
NS_IMETHODIMP
nsFileControlFrame::GetFrameName(nsString& aResult) const
{
return MakeFrameName("FileControl", aResult);
}
#endif
NS_IMETHODIMP
nsFileControlFrame::GetFormContent(nsIContent*& aContent) const
{
nsIContent* content;
nsresult rv;
rv = GetContent(&content);
aContent = content;
return rv;
}
NS_IMETHODIMP
nsFileControlFrame::GetFont(nsIPresContext* aPresContext,
const nsFont*& aFont)
{
return nsFormControlHelper::GetFont(this, aPresContext, mStyleContext, aFont);
}
nscoord
nsFileControlFrame::GetVerticalInsidePadding(nsIPresContext* aPresContext,
float aPixToTwip,
nscoord aInnerHeight) const
{
return 0;
}
nscoord
nsFileControlFrame::GetHorizontalInsidePadding(nsIPresContext* aPresContext,
float aPixToTwip,
nscoord aInnerWidth,
nscoord aCharWidth) const
{
return 0;
}
nsresult nsFileControlFrame::RequiresWidget(PRBool& aRequiresWidget)
{
aRequiresWidget = PR_FALSE;
return NS_OK;
}
NS_IMETHODIMP nsFileControlFrame::SetProperty(nsIPresContext* aPresContext,
nsIAtom* aName,
const nsAReadableString& aValue)
{
nsresult rv = NS_OK;
if (nsHTMLAtoms::value == aName) {
if (mTextFrame) {
mTextFrame->SetTextControlFrameState(aValue);
} else {
if (mCachedState) delete mCachedState;
mCachedState = new nsString(aValue);
if (!mCachedState) rv = NS_ERROR_OUT_OF_MEMORY;
}
}
return rv;
}
NS_IMETHODIMP nsFileControlFrame::GetProperty(nsIAtom* aName, nsAWritableString& aValue)
{
// Return the value of the property from the widget it is not null.
// If widget is null, assume the widget is GFX-rendered and return a member variable instead.
if (nsHTMLAtoms::value == aName) {
if (mTextFrame)
mTextFrame->GetTextControlFrameState(aValue);
}
return NS_OK;
}
NS_METHOD
nsFileControlFrame::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;
}
return nsAreaFrame::Paint(aPresContext, aRenderingContext, aDirtyRect, aWhichLayer);
}
//----------------------------------------------------------------------
// nsIStatefulFrame
//----------------------------------------------------------------------
NS_IMETHODIMP
nsFileControlFrame::GetStateType(nsIPresContext* aPresContext, nsIStatefulFrame::StateType* aStateType)
{
*aStateType = nsIStatefulFrame::eFileType;
return NS_OK;
}
NS_IMETHODIMP
nsFileControlFrame::SaveState(nsIPresContext* aPresContext, nsIPresState** aState)
{
// Construct a pres state.
NS_NewPresState(aState); // The addref happens here.
// This string will hold a single item, whether or not we're checked.
nsAutoString stateString;
GetProperty(nsHTMLAtoms::value, stateString);
(*aState)->SetStateProperty(NS_ConvertASCIItoUCS2("checked"), stateString);
return NS_OK;
}
NS_IMETHODIMP
nsFileControlFrame::RestoreState(nsIPresContext* aPresContext, nsIPresState* aState)
{
nsAutoString string;
aState->GetStateProperty(NS_ConvertASCIItoUCS2("checked"), string);
SetProperty(aPresContext, nsHTMLAtoms::value, string);
return NS_OK;
}