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
459 lines
16 KiB
C++
459 lines
16 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 "nsPageFrame.h"
|
|
#include "nsHTMLParts.h"
|
|
#include "nsIContent.h"
|
|
#include "nsIPresContext.h"
|
|
#include "nsIStyleContext.h"
|
|
#include "nsIReflowCommand.h"
|
|
#include "nsIRenderingContext.h"
|
|
#include "nsHTMLAtoms.h"
|
|
#include "nsHTMLIIDs.h"
|
|
#include "nsLayoutAtoms.h"
|
|
#include "nsIStyleSet.h"
|
|
#include "nsIPresShell.h"
|
|
#include "nsIDeviceContext.h"
|
|
|
|
// for page number localization formatting
|
|
#include "nsTextFormatter.h"
|
|
|
|
// DateTime Includes
|
|
#include "nsDateTimeFormatCID.h"
|
|
#include "nsIDateTimeFormat.h"
|
|
#include "nsIServiceManager.h"
|
|
#include "nsILocale.h"
|
|
#include "nsLocaleCID.h"
|
|
#include "nsILocaleService.h"
|
|
|
|
static NS_DEFINE_CID(kDateTimeFormatCID, NS_DATETIMEFORMAT_CID);
|
|
static NS_DEFINE_CID(kLocaleServiceCID, NS_LOCALESERVICE_CID);
|
|
|
|
// Temporary
|
|
#include "nsIFontMetrics.h"
|
|
|
|
// tstaic data members
|
|
PRUnichar * nsPageFrame::mPageNumFormat = nsnull;
|
|
|
|
nsresult
|
|
NS_NewPageFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame)
|
|
{
|
|
NS_PRECONDITION(aNewFrame, "null OUT ptr");
|
|
if (nsnull == aNewFrame) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
nsPageFrame* it = new (aPresShell) nsPageFrame;
|
|
if (nsnull == it) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
*aNewFrame = it;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsPageFrame::nsPageFrame() :
|
|
mHeadFootFont(nsnull)
|
|
{
|
|
}
|
|
|
|
nsPageFrame::~nsPageFrame()
|
|
{
|
|
if (mHeadFootFont)
|
|
delete mHeadFootFont;
|
|
|
|
if (mPageNumFormat) {
|
|
nsMemory::Free(mPageNumFormat);
|
|
mPageNumFormat = nsnull;
|
|
}
|
|
}
|
|
|
|
NS_METHOD nsPageFrame::Reflow(nsIPresContext* aPresContext,
|
|
nsHTMLReflowMetrics& aDesiredSize,
|
|
const nsHTMLReflowState& aReflowState,
|
|
nsReflowStatus& aStatus)
|
|
{
|
|
DO_GLOBAL_REFLOW_COUNT("nsPageFrame", aReflowState.reason);
|
|
aStatus = NS_FRAME_COMPLETE; // initialize out parameter
|
|
|
|
if (eReflowReason_Incremental == aReflowState.reason) {
|
|
// We don't expect the target of the reflow command to be page frame
|
|
#ifdef NS_DEUG
|
|
NS_ASSERTION(nsnull != aReflowState.reflowCommand, "null reflow command");
|
|
|
|
nsIFrame* target;
|
|
aReflowState.reflowCommand->GetTarget(target);
|
|
NS_ASSERTION(target != this, "page frame is reflow command target");
|
|
#endif
|
|
|
|
// Verify the next reflow command frame is our one and only child frame
|
|
nsIFrame* next;
|
|
aReflowState.reflowCommand->GetNext(next);
|
|
NS_ASSERTION(next == mFrames.FirstChild(), "bad reflow frame");
|
|
|
|
// Dispatch the reflow command to our frame
|
|
nsSize maxSize(aReflowState.availableWidth, aReflowState.availableHeight);
|
|
nsHTMLReflowState kidReflowState(aPresContext, aReflowState,
|
|
mFrames.FirstChild(), maxSize);
|
|
|
|
kidReflowState.isTopOfPage = PR_TRUE;
|
|
ReflowChild(mFrames.FirstChild(), aPresContext, aDesiredSize,
|
|
kidReflowState, 0, 0, 0, aStatus);
|
|
|
|
// Place and size the child. Make sure the child is at least as
|
|
// tall as our max size (the containing window)
|
|
if (aDesiredSize.height < aReflowState.availableHeight) {
|
|
aDesiredSize.height = aReflowState.availableHeight;
|
|
}
|
|
|
|
FinishReflowChild(mFrames.FirstChild(), aPresContext, aDesiredSize,
|
|
0, 0, 0);
|
|
|
|
} else {
|
|
// Do we have any children?
|
|
// XXX We should use the overflow list instead...
|
|
if (mFrames.IsEmpty() && (nsnull != mPrevInFlow)) {
|
|
nsPageFrame* prevPage = (nsPageFrame*)mPrevInFlow;
|
|
|
|
nsIFrame* prevLastChild = prevPage->mFrames.LastChild();
|
|
|
|
// Create a continuing child of the previous page's last child
|
|
nsIPresShell* presShell;
|
|
nsIStyleSet* styleSet;
|
|
nsIFrame* newFrame;
|
|
|
|
aPresContext->GetShell(&presShell);
|
|
presShell->GetStyleSet(&styleSet);
|
|
NS_RELEASE(presShell);
|
|
styleSet->CreateContinuingFrame(aPresContext, prevLastChild, this, &newFrame);
|
|
NS_RELEASE(styleSet);
|
|
mFrames.SetFrames(newFrame);
|
|
}
|
|
|
|
// Resize our frame allowing it only to be as big as we are
|
|
// XXX Pay attention to the page's border and padding...
|
|
if (mFrames.NotEmpty()) {
|
|
nsIFrame* frame = mFrames.FirstChild();
|
|
nsSize maxSize(aReflowState.availableWidth, aReflowState.availableHeight);
|
|
nsHTMLReflowState kidReflowState(aPresContext, aReflowState, frame,
|
|
maxSize);
|
|
kidReflowState.isTopOfPage = PR_TRUE;
|
|
|
|
// Get the child's desired size
|
|
ReflowChild(frame, aPresContext, aDesiredSize, kidReflowState, 0, 0, 0, aStatus);
|
|
|
|
// Make sure the child is at least as tall as our max size (the containing window)
|
|
if (aDesiredSize.height < aReflowState.availableHeight) {
|
|
aDesiredSize.height = aReflowState.availableHeight;
|
|
}
|
|
|
|
// Place and size the child
|
|
FinishReflowChild(frame, aPresContext, aDesiredSize, 0, 0, 0);
|
|
|
|
// Is the frame complete?
|
|
if (NS_FRAME_IS_COMPLETE(aStatus)) {
|
|
nsIFrame* childNextInFlow;
|
|
|
|
frame->GetNextInFlow(&childNextInFlow);
|
|
NS_ASSERTION(nsnull == childNextInFlow, "bad child flow list");
|
|
}
|
|
}
|
|
|
|
// Return our desired size
|
|
aDesiredSize.width = aReflowState.availableWidth;
|
|
aDesiredSize.height = aReflowState.availableHeight;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsPageFrame::GetFrameType(nsIAtom** aType) const
|
|
{
|
|
NS_PRECONDITION(nsnull != aType, "null OUT parameter pointer");
|
|
*aType = nsLayoutAtoms::pageFrame;
|
|
NS_ADDREF(*aType);
|
|
return NS_OK;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
NS_IMETHODIMP
|
|
nsPageFrame::GetFrameName(nsString& aResult) const
|
|
{
|
|
return MakeFrameName("Page", aResult);
|
|
}
|
|
#endif
|
|
|
|
NS_IMETHODIMP
|
|
nsPageFrame::IsPercentageBase(PRBool& aBase) const
|
|
{
|
|
aBase = PR_TRUE;
|
|
return NS_OK;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
nscoord nsPageFrame::GetXPosition(nsIRenderingContext& aRenderingContext,
|
|
const nsRect& aRect,
|
|
PRInt32 aJust,
|
|
const nsString& aStr)
|
|
{
|
|
PRInt32 width;
|
|
aRenderingContext.GetWidth(aStr, width);
|
|
|
|
nscoord x = aRect.x;
|
|
switch (aJust) {
|
|
case NS_PRINT_JUSTIFY_LEFT:
|
|
// do nothing, already set
|
|
break;
|
|
|
|
case NS_PRINT_JUSTIFY_CENTER:
|
|
x += (aRect.width - width) / 2;
|
|
break;
|
|
|
|
case NS_PRINT_JUSTIFY_RIGHT:
|
|
x += aRect.width - width;
|
|
break;
|
|
} // switch
|
|
|
|
return x;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Draw a Header or footer text lrft,right or center justified
|
|
// @parm aRenderingContext - rendering content ot draw into
|
|
// @parm aHeaderFooter - indicates whether it is a header or footer
|
|
// @parm aJust - indicates the justification of the text
|
|
// @parm aStr - The string to be drawn
|
|
// @parm aRect - the rect of the page
|
|
// @parm aHeight - the height of the text
|
|
// @parm aUseHalfThePage - indicates whether the text should limited to the width
|
|
// of the entire page or just half the page
|
|
void
|
|
nsPageFrame::DrawHeaderFooter(nsIRenderingContext& aRenderingContext,
|
|
nsIFrame * aFrame,
|
|
nsHeaderFooterEnum aHeaderFooter,
|
|
PRInt32 aJust,
|
|
const nsString& aStr,
|
|
const nsRect& aRect,
|
|
nscoord aHeight,
|
|
PRBool aUseHalfThePage)
|
|
{
|
|
// first make sure we have a vaild string and that the height of the
|
|
// text will fit in the margin
|
|
if (aStr.Length() > 0 &&
|
|
((aHeaderFooter == eHeader && aHeight < mMargin.top) ||
|
|
(aHeaderFooter == eFooter && aHeight < mMargin.bottom))) {
|
|
// measure the width of the text
|
|
nsString str = aStr;
|
|
PRInt32 width;
|
|
aRenderingContext.GetWidth(str, width);
|
|
PRBool addEllipse = PR_FALSE;
|
|
nscoord halfWidth = aRect.width;
|
|
if (aUseHalfThePage) {
|
|
halfWidth /= 2;
|
|
}
|
|
// trim the text and add the elipses if it won't fit
|
|
while (width >= halfWidth && str.Length() > 1) {
|
|
str.SetLength(str.Length()-1);
|
|
aRenderingContext.GetWidth(str, width);
|
|
addEllipse = PR_TRUE;
|
|
}
|
|
if (addEllipse && str.Length() > 3) {
|
|
str.SetLength(str.Length()-3);
|
|
str.AppendWithConversion("...");
|
|
aRenderingContext.GetWidth(str, width);
|
|
}
|
|
|
|
// cacl the x and y positions of the text
|
|
nsRect rect(aRect);
|
|
nscoord x = GetXPosition(aRenderingContext, rect, aJust, str);
|
|
nscoord y;
|
|
if (aHeaderFooter == eHeader) {
|
|
nscoord offset = ((mMargin.top - aHeight) / 2);
|
|
y = rect.y - offset - aHeight;
|
|
rect.Inflate(0, offset + aHeight);
|
|
} else {
|
|
nscoord offset = ((mMargin.bottom - aHeight) / 2);
|
|
y = rect.y + rect.height + offset;
|
|
rect.height += offset + aHeight;
|
|
}
|
|
|
|
// set up new clip and draw the text
|
|
PRBool clipEmpty;
|
|
aRenderingContext.PushState();
|
|
aRenderingContext.SetClipRect(rect, nsClipCombine_kReplace, clipEmpty);
|
|
aRenderingContext.DrawString(str, x, y);
|
|
aRenderingContext.PopState(clipEmpty);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
NS_IMETHODIMP
|
|
nsPageFrame::Paint(nsIPresContext* aPresContext,
|
|
nsIRenderingContext& aRenderingContext,
|
|
const nsRect& aDirtyRect,
|
|
nsFramePaintLayer aWhichLayer)
|
|
{
|
|
nsresult rv = nsContainerFrame::Paint(aPresContext, aRenderingContext, aDirtyRect, aWhichLayer);
|
|
|
|
if (NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer) {
|
|
// get the current margin
|
|
mPrintOptions->GetMargin(mMargin);
|
|
|
|
nsRect rect(0,0,mRect.width, mRect.height);
|
|
|
|
#if defined(DEBUG_rods) || defined(DEBUG_dcone)
|
|
// XXX Paint a one-pixel border around the page so it's easy to see where
|
|
// each page begins and ends when we're
|
|
float p2t;
|
|
aPresContext->GetPixelsToTwips(&p2t);
|
|
rect.Deflate(NSToCoordRound(p2t), NSToCoordRound(p2t));
|
|
aRenderingContext.SetColor(NS_RGB(0, 0, 0));
|
|
//aRenderingContext.DrawRect(rect);
|
|
rect.Inflate(NSToCoordRound(p2t), NSToCoordRound(p2t));
|
|
printf("SPSF::PaintChild -> Painting Frame %p Page No: %d\n", this, mPageNum);
|
|
#endif
|
|
// use the whole page
|
|
rect.width += mMargin.left + mMargin.right;
|
|
rect.x -= mMargin.left;
|
|
|
|
aRenderingContext.SetFont(*mHeadFootFont);
|
|
aRenderingContext.SetColor(NS_RGB(0,0,0));
|
|
|
|
// Get the FontMetrics to determine width.height of strings
|
|
nsCOMPtr<nsIDeviceContext> deviceContext;
|
|
aPresContext->GetDeviceContext(getter_AddRefs(deviceContext));
|
|
NS_ASSERTION(deviceContext, "Couldn't get the device context");
|
|
nsCOMPtr<nsIFontMetrics> fontMet;
|
|
deviceContext->GetMetricsFor(*mHeadFootFont, *getter_AddRefs(fontMet));
|
|
nscoord visibleHeight = 0;
|
|
if (fontMet) {
|
|
fontMet->GetHeight(visibleHeight);
|
|
}
|
|
|
|
// get the print options bits so we can figure out all the
|
|
// the extra pieces of text that need to be drawn
|
|
PRInt32 printOptBits;
|
|
mPrintOptions->GetPrintOptionsBits(&printOptBits);
|
|
|
|
// print page numbers
|
|
if (printOptBits & NS_PRINT_OPTIONS_PRINT_PAGE_NUMS && mPageNumFormat != nsnull) {
|
|
PRInt32 justify = NS_PRINT_JUSTIFY_LEFT;
|
|
mPrintOptions->GetPageNumJust(&justify);
|
|
|
|
PRUnichar * valStr;
|
|
// print page number totals "x of x"
|
|
if (printOptBits & NS_PRINT_OPTIONS_PRINT_PAGE_TOTAL) {
|
|
valStr = nsTextFormatter::smprintf(mPageNumFormat, mPageNum, mTotNumPages);
|
|
} else {
|
|
valStr = nsTextFormatter::smprintf(mPageNumFormat, mPageNum);
|
|
}
|
|
nsAutoString pageNoStr(valStr);
|
|
nsMemory::Free(valStr);
|
|
DrawHeaderFooter(aRenderingContext, this, eFooter, justify, pageNoStr, rect, visibleHeight);
|
|
}
|
|
|
|
// print localized date
|
|
if (printOptBits & NS_PRINT_OPTIONS_PRINT_DATE_PRINTED) {
|
|
// Get Locale for Formating DateTime
|
|
nsCOMPtr<nsILocale> locale;
|
|
NS_WITH_SERVICE(nsILocaleService, localeSvc, kLocaleServiceCID, &rv);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
|
|
rv = localeSvc->GetApplicationLocale(getter_AddRefs(locale));
|
|
if (NS_SUCCEEDED(rv) && locale) {
|
|
nsCOMPtr<nsIDateTimeFormat> dateTime;
|
|
rv = nsComponentManager::CreateInstance(kDateTimeFormatCID,
|
|
NULL,
|
|
NS_GET_IID(nsIDateTimeFormat),
|
|
(void**) getter_AddRefs(dateTime));
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
nsAutoString dateString;
|
|
time_t ltime;
|
|
time( <ime );
|
|
rv = dateTime->FormatTMTime(locale, kDateFormatShort, kTimeFormatNoSeconds, localtime( <ime ), dateString);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
DrawHeaderFooter(aRenderingContext, this, eFooter, NS_PRINT_JUSTIFY_RIGHT, dateString, rect, visibleHeight);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
PRBool usingHalfThePage = (printOptBits & NS_PRINT_OPTIONS_PRINT_DOC_TITLE) && (printOptBits & NS_PRINT_OPTIONS_PRINT_DOC_LOCATION);
|
|
|
|
// print document title
|
|
PRUnichar * title;
|
|
mPrintOptions->GetTitle(&title); // creates memory
|
|
if (title != nsnull && (printOptBits & NS_PRINT_OPTIONS_PRINT_DOC_TITLE)) {
|
|
DrawHeaderFooter(aRenderingContext, this, eHeader, NS_PRINT_JUSTIFY_LEFT, nsAutoString(title), rect, visibleHeight, usingHalfThePage);
|
|
nsMemory::Free(title);
|
|
}
|
|
|
|
// print document URL
|
|
PRUnichar * url;
|
|
mPrintOptions->GetURL(&url);
|
|
if (title != url && (printOptBits & NS_PRINT_OPTIONS_PRINT_DOC_LOCATION)) {
|
|
DrawHeaderFooter(aRenderingContext, this, eHeader, NS_PRINT_JUSTIFY_RIGHT, nsAutoString(url), rect, visibleHeight, usingHalfThePage);
|
|
nsMemory::Free(url);
|
|
}
|
|
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void
|
|
nsPageFrame::SetPrintOptions(nsIPrintOptions * aPrintOptions)
|
|
{
|
|
NS_ASSERTION(aPrintOptions != nsnull, "Print Options can not be null!");
|
|
|
|
mPrintOptions = aPrintOptions;
|
|
// create a default font
|
|
mHeadFootFont = new nsFont("serif", NS_FONT_STYLE_NORMAL,NS_FONT_VARIANT_NORMAL,
|
|
NS_FONT_WEIGHT_NORMAL,0,NSIntPointsToTwips(10));
|
|
// now get the default font form the print options
|
|
mPrintOptions->GetDefaultFont(*mHeadFootFont);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void
|
|
nsPageFrame::SetPageNumInfo(PRInt32 aPageNumber, PRInt32 aTotalPages)
|
|
{
|
|
mPageNum = aPageNumber;
|
|
mTotNumPages = aTotalPages;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
void
|
|
nsPageFrame::SetPageNumberFormat(PRUnichar * aFormatStr)
|
|
{
|
|
NS_ASSERTION(aFormatStr != nsnull, "Format string cannot be null!");
|
|
|
|
if (mPageNumFormat != nsnull) {
|
|
nsMemory::Free(mPageNumFormat);
|
|
}
|
|
mPageNumFormat = aFormatStr;
|
|
}
|
|
|