Mozilla/mozilla/layout/forms/nsIsIndexFrame.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

580 lines
18 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 "nsIsIndexFrame.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 "nsINameSpaceManager.h"
#include "nsCOMPtr.h"
#include "nsISupportsArray.h"
#include "nsIDOMElement.h"
#include "nsIDOMDocument.h"
#include "nsIDocument.h"
#include "nsIPresShell.h"
#include "nsIDOMHTMLInputElement.h"
#include "nsIStatefulFrame.h"
#include "nsISupportsPrimitives.h"
#include "nsIComponentManager.h"
#include "nsITextContent.h"
#include "nsHTMLParts.h"
#include "nsLinebreakConverter.h"
#include "nsILinkHandler.h"
#include "nsIHTMLDocument.h"
#include "nsXPIDLString.h"
#include "nsNetUtil.h"
#include "nsICharsetConverterManager.h"
#include "nsEscape.h"
#include "nsIDOMKeyListener.h"
#include "nsIDOMKeyEvent.h"
#include "nsIFormControlFrame.h"
#include "nsINodeInfo.h"
#include "nsIDOMEventReceiver.h"
static NS_DEFINE_CID(kCharsetConverterManagerCID, NS_ICHARSETCONVERTERMANAGER_CID);
nsresult
NS_NewIsIndexFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame)
{
NS_PRECONDITION(aNewFrame, "null OUT ptr");
if (nsnull == aNewFrame) {
return NS_ERROR_NULL_POINTER;
}
nsIsIndexFrame* it = new (aPresShell) nsIsIndexFrame();
if (!it) {
return NS_ERROR_OUT_OF_MEMORY;
}
*aNewFrame = it;
return NS_OK;
}
nsIsIndexFrame::nsIsIndexFrame():
mTextContent(nsnull),
mInputContent(nsnull)
{
//Shrink the area around it's contents
SetFlags(NS_BLOCK_SHRINK_WRAP);
}
nsIsIndexFrame::~nsIsIndexFrame()
{
if (mTextContent) {
NS_RELEASE(mTextContent);
}
// remove ourself as a listener of the text control (bug 40533)
if (mInputContent) {
nsCOMPtr<nsIDOMEventReceiver> reciever(do_QueryInterface(mInputContent));
reciever->RemoveEventListenerByIID(this, NS_GET_IID(nsIDOMKeyListener));
NS_RELEASE(mInputContent);
}
}
NS_IMETHODIMP
nsIsIndexFrame::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);
}
NS_IMETHODIMP
nsIsIndexFrame::UpdatePromptLabel()
{
nsresult result;
if (!mTextContent) return NS_ERROR_UNEXPECTED;
// Get the text from the "prompt" attribute.
// If it is zero length, set it to a default value (localized)
nsAutoString prompt;
if (mContent) {
nsCOMPtr<nsIHTMLContent> htmlContent = do_QueryInterface(mContent, &result);
if ((NS_OK == result) && htmlContent) {
nsHTMLValue value;
result = htmlContent->GetHTMLAttribute(nsHTMLAtoms::prompt, value);
if (NS_CONTENT_ATTR_HAS_VALUE == result) {
if (eHTMLUnit_String == value.GetUnit()) {
value.GetStringValue(prompt);
}
}
}
}
if (prompt.Length() == 0) {
// Generate localized label.
// We can't make any assumption as to what the default would be
// because the value is localized for non-english platforms, thus
// it might not be the string "This is a searchable index. Enter search keywords: "
result = nsFormControlHelper::GetLocalizedString(nsFormControlHelper::GetHTMLPropertiesFileName(),
"IsIndexPrompt", prompt);
}
nsCOMPtr<nsITextContent> text = do_QueryInterface(mTextContent);
result = text->SetText(prompt.GetUnicode(), prompt.Length(), PR_TRUE);
return result;
}
NS_IMETHODIMP
nsIsIndexFrame::GetInputFrame(nsIPresContext* aPresContext,
nsIFormControlFrame** oFrame)
{
nsCOMPtr<nsIPresShell> presShell;
aPresContext->GetShell(getter_AddRefs(presShell));
if (presShell) {
nsIFrame *frame;
presShell->GetPrimaryFrameFor(mInputContent, &frame);
if (frame) {
return frame->QueryInterface(NS_GET_IID(nsIFormControlFrame), (void**) oFrame);
}
}
return NS_OK;
}
NS_IMETHODIMP
nsIsIndexFrame::GetInputValue(nsIPresContext* aPresContext,
nsString& oString)
{
nsIFormControlFrame* frame = nsnull;
GetInputFrame(aPresContext, &frame);
if (frame) {
((nsNewFrame*)frame)->GetTextControlFrameState(oString);
}
return NS_OK;
}
NS_IMETHODIMP
nsIsIndexFrame::SetInputValue(nsIPresContext* aPresContext,
const nsString aString)
{
nsIFormControlFrame* frame = nsnull;
GetInputFrame(aPresContext, &frame);
if (frame) {
((nsNewFrame*)frame)->SetTextControlFrameState(aString);
}
return NS_OK;
}
void
nsIsIndexFrame::SetFocus(PRBool aOn, PRBool aRepaint)
{
nsIFormControlFrame* frame = nsnull;
GetInputFrame(mPresContext, &frame);
if (frame) {
frame->SetFocus(aOn, aRepaint);
}
}
NS_IMETHODIMP
nsIsIndexFrame::CreateAnonymousContent(nsIPresContext* aPresContext,
nsISupportsArray& aChildList)
{
nsresult result;
// Get the node info manager (used to create hr's and input's)
nsCOMPtr<nsIDocument> doc;
mContent->GetDocument(*getter_AddRefs(doc));
nsCOMPtr<nsINodeInfoManager> nimgr;
nsresult rv = doc->GetNodeInfoManager(*getter_AddRefs(nimgr));
NS_ENSURE_SUCCESS(rv, rv);
// Create an hr
nsCOMPtr<nsINodeInfo> hrInfo;
nsCOMPtr<nsIHTMLContent> prehr;
nimgr->GetNodeInfo(nsHTMLAtoms::hr, nsnull, kNameSpaceID_None,
*getter_AddRefs(hrInfo));
if (NS_OK == NS_NewHTMLHRElement(getter_AddRefs(prehr), hrInfo)) {
result = aChildList.AppendElement(prehr);
}
// Add a child text content node for the label
if (NS_SUCCEEDED(result)) {
nsCOMPtr<nsIContent> labelContent;
result = NS_NewTextNode(getter_AddRefs(labelContent));
if (NS_SUCCEEDED(result) && labelContent) {
// set the value of the text node and add it to the child list
result = labelContent->QueryInterface(NS_GET_IID(nsITextContent),(void**)&mTextContent);
if (NS_SUCCEEDED(result) && mTextContent) {
UpdatePromptLabel();
result = aChildList.AppendElement(mTextContent);
}
}
}
// Create text input field
nsCOMPtr<nsINodeInfo> inputInfo;
nimgr->GetNodeInfo(nsHTMLAtoms::input, nsnull, kNameSpaceID_None,
*getter_AddRefs(inputInfo));
if (NS_OK == NS_NewHTMLInputElement(&mInputContent, inputInfo)) {
mInputContent->SetAttribute(kNameSpaceID_None, nsHTMLAtoms::type, NS_ConvertASCIItoUCS2("text"), PR_FALSE);
aChildList.AppendElement(mInputContent);
// Register as an event listener to submit on Enter press
nsCOMPtr<nsIDOMEventReceiver> reciever(do_QueryInterface(mInputContent));
reciever->AddEventListenerByIID(this, NS_GET_IID(nsIDOMKeyListener));
}
// Create an hr
nsCOMPtr<nsIHTMLContent> posthr;
if (NS_OK == NS_NewHTMLHRElement(getter_AddRefs(posthr), hrInfo)) {
aChildList.AppendElement(posthr);
}
return result;
}
// Frames are not refcounted, no need to AddRef
NS_IMETHODIMP
nsIsIndexFrame::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(nsIStatefulFrame))) {
*aInstancePtr = (void*)(nsIStatefulFrame*) this;
return NS_OK;
} else if (aIID.Equals(NS_GET_IID(nsIDOMKeyListener))) {
*aInstancePtr = (void*)(nsIDOMKeyListener*) this;
return NS_OK;
}
return nsHTMLContainerFrame::QueryInterface(aIID, aInstancePtr);
}
void
nsIsIndexFrame::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);
}
}
}
NS_IMETHODIMP nsIsIndexFrame::Reflow(nsIPresContext* aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
DO_GLOBAL_REFLOW_COUNT("nsIsIndexFrame", aReflowState.reason);
// 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);
return rv;
}
NS_IMETHODIMP
nsIsIndexFrame::AttributeChanged(nsIPresContext* aPresContext,
nsIContent* aChild,
PRInt32 aNameSpaceID,
nsIAtom* aAttribute,
PRInt32 aHint)
{
nsresult rv = NS_OK;
if (nsHTMLAtoms::prompt == aAttribute) {
rv = UpdatePromptLabel();
} else {
rv = nsAreaFrame::AttributeChanged(aPresContext, aChild, aNameSpaceID, aAttribute, aHint);
}
return rv;
}
nsresult
nsIsIndexFrame::KeyPress(nsIDOMEvent* aEvent)
{
nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aEvent);
if (keyEvent) {
PRUint32 code;
keyEvent->GetKeyCode(&code);
if (code == 0) {
keyEvent->GetCharCode(&code);
}
if (nsIDOMKeyEvent::DOM_VK_RETURN == code) {
OnSubmit(mPresContext);
aEvent->PreventDefault(); // XXX Needed?
}
}
return NS_OK;
}
#ifdef NS_DEBUG
NS_IMETHODIMP
nsIsIndexFrame::GetFrameName(nsString& aResult) const
{
return MakeFrameName("IsIndex", aResult);
}
#endif
// submission
// much of this is cut and paste from nsFormFrame::OnSubmit
NS_IMETHODIMP
nsIsIndexFrame::OnSubmit(nsIPresContext* aPresContext)
{
nsresult result;
if (!mContent || !mInputContent) {
return NS_ERROR_UNEXPECTED;
}
// Begin ProcessAsURLEncoded
nsAutoString data;
nsCOMPtr<nsIUnicodeEncoder> encoder;
if(NS_FAILED(GetEncoder(getter_AddRefs(encoder)))) // Non-fatal error
encoder = nsnull;
nsAutoString value;
GetInputValue(aPresContext, value);
URLEncode(value, encoder, data);
// End ProcessAsURLEncoded
// make the url string
nsCOMPtr<nsILinkHandler> handler;
if (NS_OK == aPresContext->GetLinkHandler(getter_AddRefs(handler))) {
nsAutoString href;
// Get the document.
// We'll need it now to form the URL we're submitting to.
// We'll also need it later to get the DOM window when notifying form submit observers (bug 33203)
nsCOMPtr<nsIDocument> document;
mContent->GetDocument(*getter_AddRefs(document));
if (!document) return NS_OK; // No doc means don't submit, see Bug 28988
// Resolve url to an absolute url
nsCOMPtr<nsIURI> docURL;
document->GetBaseURL(*getter_AddRefs(docURL));
NS_ASSERTION(docURL, "No Base URL found in Form Submit!\n");
if (!docURL) return NS_OK; // No base URL -> exit early, see Bug 30721
// If an action is not specified and we are inside
// a HTML document then reload the URL. This makes us
// compatible with 4.x browsers.
// If we are in some other type of document such as XML or
// XUL, do nothing. This prevents undesirable reloading of
// a document inside XUL.
nsresult rv;
nsCOMPtr<nsIHTMLDocument> htmlDoc;
htmlDoc = do_QueryInterface(document, &rv);
if (NS_FAILED(rv)) {
// Must be a XML, XUL or other non-HTML document type
// so do nothing.
return NS_OK;
}
// Necko's MakeAbsoluteURI doesn't reuse the baseURL's rel path if it is
// passed a zero length rel path.
nsXPIDLCString relPath;
docURL->GetSpec(getter_Copies(relPath));
NS_ASSERTION(relPath, "Rel path couldn't be formed in form submit!\n");
if (relPath) {
href.AppendWithConversion(relPath);
// If re-using the same URL, chop off old query string (bug 25330)
PRInt32 queryStart = href.FindChar('?');
if (kNotFound != queryStart) {
href.Truncate(queryStart);
}
} else {
return NS_ERROR_OUT_OF_MEMORY;
}
// Add the URI encoded form values to the URI
// Get the scheme of the URI.
nsCOMPtr<nsIURI> actionURL;
nsXPIDLCString scheme;
if (NS_SUCCEEDED(result = NS_NewURI(getter_AddRefs(actionURL), href, docURL))) {
result = actionURL->GetScheme(getter_Copies(scheme));
}
nsAutoString theScheme; theScheme.AssignWithConversion( NS_STATIC_CAST(const char*, scheme) );
// Append the URI encoded variable/value pairs for GET's
if (!theScheme.EqualsIgnoreCase("javascript")) { // Not for JS URIs, see bug 26917
if (href.FindChar('?', PR_FALSE, 0) == kNotFound) { // Add a ? if needed
href.AppendWithConversion('?');
} else { // Adding to existing query string
if (href.Last() != '&' && href.Last() != '?') { // Add a & if needed
href.AppendWithConversion('&');
}
}
href.Append(data);
}
nsAutoString absURLSpec;
result = NS_MakeAbsoluteURI(absURLSpec, href, docURL);
if (NS_FAILED(result)) return result;
// Now pass on absolute url to the click handler
if (handler) {
handler->OnLinkClick(mContent, eLinkVerb_Replace,
absURLSpec.GetUnicode(),
nsnull, nsnull);
}
}
return result;
}
void nsIsIndexFrame::GetSubmitCharset(nsString& oCharset)
{
oCharset.AssignWithConversion("UTF-8"); // default to utf-8
nsresult rv;
// XXX
// We may want to get it from the HTML 4 Accept-Charset attribute first
// see 17.3 The FORM element in HTML 4 for details
// Get the charset from document
nsIDocument* doc = nsnull;
mContent->GetDocument(doc);
if( nsnull != doc ) {
rv = doc->GetDocumentCharacterSet(oCharset);
NS_RELEASE(doc);
}
}
NS_IMETHODIMP nsIsIndexFrame::GetEncoder(nsIUnicodeEncoder** encoder)
{
*encoder = nsnull;
nsAutoString charset;
nsresult rv = NS_OK;
GetSubmitCharset(charset);
// Get Charset, get the encoder.
nsICharsetConverterManager * ccm = nsnull;
rv = nsServiceManager::GetService(kCharsetConverterManagerCID ,
NS_GET_IID(nsICharsetConverterManager),
(nsISupports**)&ccm);
if(NS_SUCCEEDED(rv) && (nsnull != ccm)) {
rv = ccm->GetUnicodeEncoder(&charset, encoder);
nsServiceManager::ReleaseService( kCharsetConverterManagerCID, ccm);
if (nsnull == encoder) {
rv = NS_ERROR_FAILURE;
}
if (NS_SUCCEEDED(rv)) {
rv = (*encoder)->SetOutputErrorBehavior(nsIUnicodeEncoder::kOnError_Replace, nsnull, (PRUnichar)'?');
}
}
return NS_OK;
}
// XXX i18n helper routines
char*
nsIsIndexFrame::UnicodeToNewBytes(const PRUnichar* aSrc, PRUint32 aLen, nsIUnicodeEncoder* encoder)
{
char* res = nsnull;
if(NS_SUCCEEDED(encoder->Reset()))
{
PRInt32 maxByteLen = 0;
if(NS_SUCCEEDED(encoder->GetMaxLength(aSrc, (PRInt32) aLen, &maxByteLen)))
{
res = new char[maxByteLen+1];
if(nsnull != res)
{
PRInt32 reslen = maxByteLen;
PRInt32 reslen2 ;
PRInt32 srclen = aLen;
encoder->Convert(aSrc, &srclen, res, &reslen);
reslen2 = maxByteLen-reslen;
encoder->Finish(res+reslen, &reslen2);
res[reslen+reslen2] = '\0';
}
}
}
return res;
}
// XXX i18n helper routines
void
nsIsIndexFrame::URLEncode(const nsString& aString, nsIUnicodeEncoder* encoder, nsString& oString)
{
char* inBuf = nsnull;
if(encoder)
inBuf = UnicodeToNewBytes(aString.GetUnicode(), aString.Length(), encoder);
if(nsnull == inBuf)
inBuf = aString.ToNewCString();
// convert to CRLF breaks
char* convertedBuf = nsLinebreakConverter::ConvertLineBreaks(inBuf,
nsLinebreakConverter::eLinebreakAny, nsLinebreakConverter::eLinebreakNet);
delete [] inBuf;
char* outBuf = nsEscape(convertedBuf, url_XPAlphas);
oString.AssignWithConversion(outBuf);
nsCRT::free(outBuf);
nsMemory::Free(convertedBuf);
}
//----------------------------------------------------------------------
// nsIStatefulFrame
//----------------------------------------------------------------------
NS_IMETHODIMP
nsIsIndexFrame::GetStateType(nsIPresContext* aPresContext, nsIStatefulFrame::StateType* aStateType)
{
*aStateType = nsIStatefulFrame::eFileType; // XXX eIsIndexType
return NS_OK;
}
NS_IMETHODIMP
nsIsIndexFrame::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;
GetInputValue(aPresContext, stateString);
(*aState)->SetStateProperty(NS_ConvertASCIItoUCS2("value"), stateString);
return NS_OK;
}
NS_IMETHODIMP
nsIsIndexFrame::RestoreState(nsIPresContext* aPresContext, nsIPresState* aState)
{
nsAutoString string;
aState->GetStateProperty(NS_ConvertASCIItoUCS2("value"), string);
SetInputValue(aPresContext, string);
return NS_OK;
}