1539 lines
42 KiB
C++
1539 lines
42 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.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 "nsHTMLParts.h"
|
|
#include "nsCRT.h"
|
|
#include "nsSplittableFrame.h"
|
|
#include "nsHTMLContent.h"
|
|
#include "nsString.h"
|
|
#include "nsIPresContext.h"
|
|
#include "nsStyleConsts.h"
|
|
#include "nsIStyleContext.h"
|
|
#include "nsCoord.h"
|
|
#include "nsIFontMetrics.h"
|
|
#include "nsIRenderingContext.h"
|
|
#include "nsHTMLIIDs.h"
|
|
#include "nsIPresShell.h"
|
|
#include "nsIView.h"
|
|
#include "nsIViewManager.h"
|
|
#include "nsITimerCallback.h"
|
|
#include "nsITimer.h"
|
|
#include "nsBlockFrame.h"
|
|
#include "prtime.h"
|
|
#include "prprf.h"
|
|
#include "nsIDOMText.h"
|
|
|
|
#ifdef NS_DEBUG
|
|
#undef NOISY
|
|
#define NOISY_BLINK
|
|
#else
|
|
#undef NOISY
|
|
#undef NOISY_BLINK
|
|
#endif
|
|
|
|
// XXX TODO:
|
|
// 0. tune justified text
|
|
// 1. add in a rendering method that can render justified text
|
|
// 2. text renderer should negotiate with rendering context/font
|
|
// metrics to see what it can handle; for example, if the renderer can
|
|
// automatically deal with underlining, strikethrough, justification,
|
|
// etc, then the text renderer should let the rc do the work;
|
|
// otherwise there should be XP fallback code here.
|
|
|
|
// XXX Speedup ideas
|
|
// 1. justified text can use word width information during resize reflows
|
|
// 2. when we are doing an unconstrained reflow we know we are going to
|
|
// get reflowed again; collect up the word widths we are computing as we
|
|
// do this and then save them in the mWords; later on when we get reflowed
|
|
// again we can destroy them
|
|
// 3. when pulling up text get word width information from next-in-flow
|
|
|
|
// XXX temporary
|
|
#define XP_IS_SPACE(_ch) \
|
|
(((_ch) == ' ') || ((_ch) == '\t') || ((_ch) == '\n'))
|
|
|
|
// XXX need more of this in nsIFontMetrics.GetWidth
|
|
#define CH_NBSP 160
|
|
|
|
// XXX use a PreTextFrame for pre-formatted text?
|
|
|
|
#if 0
|
|
static NS_DEFINE_IID(kITextContentIID, NS_ITEXTCONTENT_IID);
|
|
#endif
|
|
static NS_DEFINE_IID(kStyleMoleculeSID, NS_STYLEMOLECULE_SID);
|
|
static NS_DEFINE_IID(kStyleFontSID, NS_STYLEFONT_SID);
|
|
static NS_DEFINE_IID(kStyleColorSID, NS_STYLECOLOR_SID);
|
|
static NS_DEFINE_IID(kIScriptObjectOwner, NS_ISCRIPTOBJECTOWNER_IID);
|
|
|
|
class TextFrame;
|
|
|
|
class TextTimer : public nsITimerCallback {
|
|
public:
|
|
TextTimer();
|
|
~TextTimer();
|
|
|
|
NS_DECL_ISUPPORTS
|
|
|
|
void AddFrame(TextFrame* aFrame);
|
|
|
|
PRBool RemoveFrame(TextFrame* aFrame);
|
|
|
|
PRInt32 FrameCount();
|
|
|
|
void Start();
|
|
|
|
void Stop();
|
|
|
|
virtual void Notify(nsITimer *timer);
|
|
|
|
nsITimer* mTimer;
|
|
nsVoidArray mFrames;
|
|
};
|
|
|
|
class TextFrame : public nsSplittableFrame {
|
|
public:
|
|
TextFrame(nsIContent* aContent,
|
|
PRInt32 aIndexInParent,
|
|
nsIFrame* aParentFrame);
|
|
|
|
virtual void Paint(nsIPresContext& aPresContext,
|
|
nsIRenderingContext& aRenderingContext,
|
|
const nsRect& aDirtyRect);
|
|
|
|
virtual ReflowStatus ResizeReflow(nsIPresContext* aCX,
|
|
nsReflowMetrics& aDesiredSize,
|
|
const nsSize& aMaxSize,
|
|
nsSize* aMaxElementSize);
|
|
|
|
virtual void GetReflowMetrics(nsIPresContext* aPresContext,
|
|
nsReflowMetrics& aMetrics);
|
|
|
|
virtual void JustifyReflow(nsIPresContext* aCX,
|
|
nscoord aAvailableSpace);
|
|
|
|
virtual PRInt32 GetCursorAt(nsIPresContext& aPresContext,
|
|
const nsPoint& aPoint,
|
|
nsIFrame** aFrame);
|
|
|
|
virtual void List(FILE* out, PRInt32 aIndent) const;
|
|
|
|
protected:
|
|
virtual ~TextFrame();
|
|
|
|
void PaintRegularText(nsIRenderingContext& aRenderingContext,
|
|
const nsRect& aDirtyRect,
|
|
nscoord dx, nscoord dy);
|
|
|
|
void PaintJustifiedText(nsIRenderingContext& aRenderingContext,
|
|
const nsRect& aDirtyRect,
|
|
nscoord dx, nscoord dy);
|
|
|
|
ReflowStatus ReflowPre(nsIPresContext* aCX,
|
|
nsReflowMetrics& aDesiredSize,
|
|
const nsSize& aMaxSize,
|
|
nsSize* aMaxElementSize,
|
|
nsStyleFont& aFont,
|
|
PRInt32 aStartingOffset,
|
|
nsBlockReflowState* aState);
|
|
|
|
ReflowStatus ReflowNormal(nsIPresContext* aCX,
|
|
nsReflowMetrics& aDesiredSize,
|
|
const nsSize& aMaxSize,
|
|
nsSize* aMaxElementSize,
|
|
nsStyleFont& aFont,
|
|
nsStyleMolecule& aMol,
|
|
PRInt32 aStartingOffset,
|
|
nsBlockReflowState* aState);
|
|
|
|
public:
|
|
PRInt32 mContentOffset;
|
|
PRInt32 mContentLength;
|
|
PRUint16 mFlags;
|
|
PRUint16 mColumn;
|
|
|
|
// When this text frame is justified, this pointer is not null
|
|
// and points to data about each word/space in the text
|
|
PRUint32* mWords;
|
|
#ifdef DEBUG_JUSTIFY
|
|
PRInt32 mNumWords;
|
|
#endif
|
|
};
|
|
|
|
// Flag information used by rendering code. This information is
|
|
// computed by the ResizeReflow code. Remaining bits are used by the
|
|
// tab count.
|
|
#define TEXT_SKIP_LEADING_WS 0x1
|
|
#define TEXT_HAS_MULTIBYTE 0x2
|
|
#define TEXT_IS_PRE 0x4
|
|
#define TEXT_BLINK_ON 0x8
|
|
|
|
#define TEXT_GET_TAB_COUNT(_mf) ((_mf) >> 4)
|
|
#define TEXT_SET_TAB_COUNT(_mf,_tabs) (_mf) = (_mf) | ((_tabs)<< 4)
|
|
|
|
// mWords bit definitions
|
|
#define WORD_IS_WORD 0x80000000L
|
|
#define WORD_IS_SPACE 0x00000000L
|
|
#define WORD_LENGTH_MASK 0x000000FFL
|
|
#define WORD_LENGTH_SHIFT 0
|
|
#define WORD_WIDTH_MASK 0x7FFFFF00L
|
|
#define WORD_WIDTH_SHIFT 8
|
|
#define MAX_WORD_LENGTH 256
|
|
#define MAX_WORD_WIDTH 0x7FFFFFL
|
|
|
|
#define GET_WORD_LENGTH(_wi) \
|
|
((PRIntn) (((_wi) & WORD_LENGTH_MASK)/* >> WORD_LENGTH_SHIFT*/))
|
|
#define GET_WORD_WIDTH(_wi) \
|
|
((nscoord) ((PRUint32(_wi) & WORD_WIDTH_MASK) >> WORD_WIDTH_SHIFT))
|
|
|
|
class Text : public nsHTMLContent, public nsIDOMText {
|
|
public:
|
|
Text(const PRUnichar* aText, PRInt32 aLength);
|
|
|
|
NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr);
|
|
NS_IMETHOD_(nsrefcnt) AddRef(void);
|
|
NS_IMETHOD_(nsrefcnt) Release(void);
|
|
|
|
#if 0
|
|
virtual PRInt32 GetLength();
|
|
virtual void GetText(nsStringBuf* aBuf, PRInt32 aOffset, PRInt32 aCount);
|
|
#endif
|
|
|
|
virtual void List(FILE* out, PRInt32 aIndent) const;
|
|
|
|
virtual void ToHTMLString(nsString& aBuf) const;
|
|
|
|
virtual nsIFrame* CreateFrame(nsIPresContext* aCX,
|
|
PRInt32 aIndexInParent,
|
|
nsIFrame* aParentFrame);
|
|
|
|
// nsIScriptObjectOwner interface
|
|
virtual nsresult GetScriptObject(JSContext *aContext, void** aScriptObject);
|
|
|
|
// nsIDOMText interface
|
|
virtual nsresult GetNodeType(PRInt32 *aType);
|
|
virtual nsresult GetParentNode(nsIDOMNode **aNode);
|
|
virtual nsresult GetChildNodes(nsIDOMNodeIterator **aIterator);
|
|
virtual nsresult HasChildNodes();
|
|
virtual nsresult GetFirstChild(nsIDOMNode **aNode);
|
|
virtual nsresult GetPreviousSibling(nsIDOMNode **aNode);
|
|
virtual nsresult GetNextSibling(nsIDOMNode **aNode);
|
|
virtual nsresult InsertBefore(nsIDOMNode *newChild, nsIDOMNode *refChild);
|
|
virtual nsresult ReplaceChild(nsIDOMNode *newChild,
|
|
nsIDOMNode *oldChild);
|
|
virtual nsresult RemoveChild(nsIDOMNode *oldChild);
|
|
virtual nsresult GetData(nsString &aString);
|
|
virtual nsresult SetData(nsString &aString);
|
|
virtual nsresult Append(nsString &aData);
|
|
virtual nsresult Insert(int offset, nsString &aData);
|
|
virtual nsresult Delete(int offset, int count);
|
|
virtual nsresult Replace(int offset, int count, nsString &aData);
|
|
virtual nsresult Splice(nsIDOMElement *element, int offset, int count);
|
|
|
|
void ToCString(nsString& aBuf, PRInt32 aOffset, PRInt32 aLen) const;
|
|
|
|
PRUnichar* mText;
|
|
PRInt32 mLength;
|
|
|
|
protected:
|
|
Text();
|
|
virtual ~Text();
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
static PRBool gBlinkTextOff;
|
|
static TextTimer* gTextBlinker;
|
|
#ifdef NOISY_BLINK
|
|
static PRTime gLastTick;
|
|
#endif
|
|
|
|
TextTimer::TextTimer()
|
|
{
|
|
NS_INIT_REFCNT();
|
|
mTimer = nsnull;
|
|
}
|
|
|
|
TextTimer::~TextTimer()
|
|
{
|
|
Stop();
|
|
}
|
|
|
|
void TextTimer::Start()
|
|
{
|
|
nsresult rv = NS_NewTimer(&mTimer);
|
|
if (NS_OK == rv) {
|
|
mTimer->Init(this, 750);
|
|
}
|
|
}
|
|
|
|
void TextTimer::Stop()
|
|
{
|
|
NS_IF_RELEASE(mTimer);
|
|
}
|
|
|
|
static NS_DEFINE_IID(kITimerCallbackIID, NS_ITIMERCALLBACK_IID);
|
|
NS_IMPL_ISUPPORTS(TextTimer, kITimerCallbackIID);
|
|
|
|
void TextTimer::AddFrame(TextFrame* aFrame) {
|
|
mFrames.AppendElement(aFrame);
|
|
if (1 == mFrames.Count()) {
|
|
Start();
|
|
}
|
|
printf("%d blinking frames [add %p]\n", mFrames.Count(), aFrame);
|
|
}
|
|
|
|
PRBool TextTimer::RemoveFrame(TextFrame* aFrame) {
|
|
PRBool rv = mFrames.RemoveElement(aFrame);
|
|
if (0 == mFrames.Count()) {
|
|
Stop();
|
|
}
|
|
printf("%d blinking frames [remove %p] [%s]\n",
|
|
mFrames.Count(), aFrame, rv ? "true" : "FALSE");
|
|
return rv;
|
|
}
|
|
|
|
PRInt32 TextTimer::FrameCount() {
|
|
return mFrames.Count();
|
|
}
|
|
|
|
void TextTimer::Notify(nsITimer *timer)
|
|
{
|
|
// Toggle blink state bit so that text code knows whether or not to
|
|
// render. All text code shares the same flag so that they all blink
|
|
// in unison.
|
|
gBlinkTextOff = PRBool(!gBlinkTextOff);
|
|
|
|
// XXX hack to get auto-repeating timers; restart before doing
|
|
// expensive work so that time between ticks is more even
|
|
Stop();
|
|
Start();
|
|
|
|
#ifdef NOISY_BLINK
|
|
PRTime now = PR_Now();
|
|
char buf[50];
|
|
PRTime delta;
|
|
LL_SUB(delta, now, gLastTick);
|
|
gLastTick = now;
|
|
PR_snprintf(buf, sizeof(buf), "%lldusec", delta);
|
|
printf("%s\n", buf);
|
|
#endif
|
|
|
|
PRInt32 i, n = mFrames.Count();
|
|
for (i = 0; i < n; i++) {
|
|
TextFrame* text = (TextFrame*) mFrames.ElementAt(i);
|
|
|
|
// Determine damaged area and tell view manager to redraw it
|
|
nsPoint offset;
|
|
nsRect bounds;
|
|
text->GetRect(bounds);
|
|
nsIView* view = text->GetOffsetFromView(offset);
|
|
nsIViewManager* vm = view->GetViewManager();
|
|
bounds.x = offset.x;
|
|
bounds.y = offset.y;
|
|
vm->UpdateView(view, &bounds, 0);
|
|
NS_RELEASE(vm);
|
|
NS_RELEASE(view);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
TextFrame::TextFrame(nsIContent* aContent,
|
|
PRInt32 aIndexInParent,
|
|
nsIFrame* aParentFrame)
|
|
: nsSplittableFrame(aContent, aIndexInParent, aParentFrame)
|
|
{
|
|
if (nsnull == gTextBlinker) {
|
|
// Create text timer the first time out
|
|
gTextBlinker = new TextTimer();
|
|
}
|
|
NS_ADDREF(gTextBlinker);
|
|
}
|
|
|
|
TextFrame::~TextFrame()
|
|
{
|
|
if (0 != (mFlags & TEXT_BLINK_ON)) {
|
|
NS_ASSERTION(nsnull != gTextBlinker, "corrupted blinker");
|
|
gTextBlinker->RemoveFrame(this);
|
|
}
|
|
if (0 == gTextBlinker->Release()) {
|
|
// Release text timer when the last text frame is gone
|
|
gTextBlinker = nsnull;
|
|
}
|
|
if (nsnull != mWords) {
|
|
delete mWords;
|
|
}
|
|
}
|
|
|
|
PRInt32 TextFrame::GetCursorAt(nsIPresContext& aPresContext,
|
|
const nsPoint& aPoint,
|
|
nsIFrame** aFrame)
|
|
{
|
|
nsStyleMolecule* mol = (nsStyleMolecule*)
|
|
mStyleContext->GetData(kStyleMoleculeSID);
|
|
if (mol->cursor != NS_STYLE_CURSOR_INHERIT) {
|
|
// If this container has a particular cursor, use it, otherwise
|
|
// let the child decide.
|
|
*aFrame = this;
|
|
}
|
|
return (PRInt32) mol->cursor;
|
|
}
|
|
|
|
// XXX it would be nice to use a method pointer here, but I don't want
|
|
// to pay for the storage.
|
|
|
|
void TextFrame::Paint(nsIPresContext& aPresContext,
|
|
nsIRenderingContext& aRenderingContext,
|
|
const nsRect& aDirtyRect)
|
|
{
|
|
if ((0 != (mFlags & TEXT_BLINK_ON)) && gBlinkTextOff) {
|
|
return;
|
|
}
|
|
|
|
// Get style data
|
|
nsStyleColor* color =
|
|
(nsStyleColor*)mStyleContext->GetData(kStyleColorSID);
|
|
nsStyleFont* font =
|
|
(nsStyleFont*)mStyleContext->GetData(kStyleFontSID);
|
|
|
|
// Set font and color
|
|
aRenderingContext.SetColor(color->mColor);
|
|
aRenderingContext.SetFont(font->mFont);
|
|
|
|
if (nsnull != mWords) {
|
|
PaintJustifiedText(aRenderingContext, aDirtyRect, 0, 0);
|
|
if (font->mThreeD) {
|
|
nscoord onePixel = nscoord(1.0f * aPresContext.GetPixelsToTwips());
|
|
aRenderingContext.SetColor(color->mBackgroundColor);
|
|
PaintJustifiedText(aRenderingContext, aDirtyRect, onePixel, onePixel);
|
|
}
|
|
return;
|
|
}
|
|
PaintRegularText(aRenderingContext, aDirtyRect, 0, 0);
|
|
if (font->mThreeD) {
|
|
nscoord onePixel = nscoord(1.0f * aPresContext.GetPixelsToTwips());
|
|
aRenderingContext.SetColor(color->mBackgroundColor);
|
|
PaintRegularText(aRenderingContext, aDirtyRect, onePixel, onePixel);
|
|
}
|
|
}
|
|
|
|
void TextFrame::PaintRegularText(nsIRenderingContext& aRenderingContext,
|
|
const nsRect& aDirtyRect,
|
|
nscoord dx, nscoord dy)
|
|
{
|
|
// Skip leading space if necessary
|
|
Text* txt = (Text*) mContent;
|
|
const PRUnichar* cp = txt->mText + mContentOffset;
|
|
const PRUnichar* end = cp + mContentLength;
|
|
if ((mFlags & TEXT_SKIP_LEADING_WS) != 0) {
|
|
while (cp < end) {
|
|
PRUnichar ch = *cp++;
|
|
if (!XP_IS_SPACE(ch)) {
|
|
cp--;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((mFlags & TEXT_IS_PRE) != 0) {
|
|
if ((mFlags & TEXT_HAS_MULTIBYTE) != 0) {
|
|
// XXX to be written
|
|
} else {
|
|
PRIntn tabs = TEXT_GET_TAB_COUNT(mFlags);
|
|
|
|
// Make a copy of the text we are to render, translating tabs
|
|
// into whitespace.
|
|
char buf[100];
|
|
char* s = buf;
|
|
char* s0 = s;
|
|
PRInt32 maxLen = (end - cp) + 8*tabs;
|
|
if (maxLen > sizeof(buf)) {
|
|
s0 = s = new char[maxLen];
|
|
}
|
|
|
|
// Translate tabs into whitespace; translate other whitespace into
|
|
// spaces.
|
|
PRIntn col = (PRIntn) mColumn;
|
|
while (cp < end) {
|
|
PRUint8 ch = (PRUint8) *cp++;
|
|
if (XP_IS_SPACE(ch)) {
|
|
if (ch == '\t') {
|
|
PRIntn spaces = 8 - (col & 7);
|
|
col += spaces;
|
|
while (--spaces >= 0) {
|
|
*s++ = ' ';
|
|
}
|
|
continue;
|
|
} else {
|
|
*s++ = ' ';
|
|
}
|
|
} else if (ch == CH_NBSP) {
|
|
*s++ = ' ';
|
|
} else {
|
|
*s++ = ch;
|
|
}
|
|
col++;
|
|
}
|
|
|
|
// Strip trailing whitespace
|
|
if (s != s0) {
|
|
if (s[-1] == ' ') {
|
|
s--;
|
|
}
|
|
}
|
|
|
|
// Render the text
|
|
aRenderingContext.DrawString(s0, s - s0, dx, dy, mRect.width);
|
|
|
|
if (s0 != buf) {
|
|
delete s0;
|
|
}
|
|
}
|
|
} else {
|
|
if ((mFlags & TEXT_HAS_MULTIBYTE) != 0) {
|
|
// XXX to be written
|
|
} else {
|
|
// Make a copy of the text we are to render, compressing out
|
|
// whitespace; translating whitespace to literal spaces;
|
|
// eliminating trailing whitespace.
|
|
char buf[100];
|
|
char* s = buf;
|
|
char* s0 = s;
|
|
PRInt32 maxLen = end - cp;
|
|
if (maxLen > sizeof(buf)) {
|
|
s0 = s = new char[maxLen];
|
|
}
|
|
|
|
// Compress down the whitespace
|
|
while (cp < end) {
|
|
PRUint8 ch = (PRUint8) *cp++;
|
|
if (XP_IS_SPACE(ch)) {
|
|
while (cp < end) {
|
|
ch = (PRUint8) *cp++;
|
|
if (!XP_IS_SPACE(ch)) {
|
|
cp--;
|
|
break;
|
|
}
|
|
}
|
|
*s++ = ' ';
|
|
} else {
|
|
*s++ = ch;
|
|
}
|
|
}
|
|
|
|
// Strip trailing whitespace
|
|
if (s != s0) {
|
|
if (s[-1] == ' ') {
|
|
s--;
|
|
}
|
|
}
|
|
|
|
// Render the text
|
|
aRenderingContext.DrawString(s0, s - s0, dx, dy, mRect.width);
|
|
|
|
if (s0 != buf) {
|
|
delete s0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void TextFrame::PaintJustifiedText(nsIRenderingContext& aRenderingContext,
|
|
const nsRect& aDirtyRect,
|
|
nscoord dx, nscoord dy)
|
|
{
|
|
Text* txt = (Text*) mContent;
|
|
const PRUnichar* cp = txt->mText + mContentOffset;
|
|
const PRUnichar* end = cp + mContentLength;
|
|
|
|
// Get a word buffer that's big enough to hold all of the text in
|
|
// case all of the text happens to be a word (not possible given how
|
|
// the justification workds, but it's the fastest check that gets us
|
|
// a big enough buffer). We only do this if we aren't going to use
|
|
// multibyte rendering.
|
|
char buf[100];
|
|
char* s0 = buf;
|
|
if ((mFlags & TEXT_HAS_MULTIBYTE) == 0) {
|
|
PRInt32 maxLen = end - cp;
|
|
if (maxLen > sizeof(buf)) {
|
|
s0 = new char[maxLen];
|
|
}
|
|
}
|
|
|
|
// XXX justified text doesn't render underlines (etc) over spaces
|
|
// while regular text does...fix this?
|
|
PRUint32* wp = mWords;
|
|
PRInt32 total = mContentLength;
|
|
nscoord x = dx;
|
|
#ifdef DEBUG_JUSTIFY
|
|
PRIntn numWords = mNumWords;
|
|
#endif
|
|
while (total != 0) {
|
|
#ifdef DEBUG_JUSTIFY
|
|
NS_ASSERTION(numWords > 0, "bad word data");
|
|
#endif
|
|
PRUint32 wordInfo = *wp++;
|
|
nscoord wordWidth = GET_WORD_WIDTH(wordInfo);
|
|
PRIntn wordLen = GET_WORD_LENGTH(wordInfo);
|
|
if (wordInfo & WORD_IS_WORD) {
|
|
if (mFlags & TEXT_HAS_MULTIBYTE) {
|
|
aRenderingContext.DrawString(cp, wordLen, x, dy, mRect.width);
|
|
cp += wordLen;
|
|
} else {
|
|
char* s = s0;
|
|
char* es = s + wordLen;
|
|
while (s < es) {
|
|
PRUint8 ch = (PRUint8) *cp++;
|
|
*s++ = ch;
|
|
}
|
|
aRenderingContext.DrawString(s0, s - s0, x, dy, mRect.width);
|
|
}
|
|
} else {
|
|
// skip over space characters in text array
|
|
cp += wordLen;
|
|
}
|
|
x += wordWidth;
|
|
total -= wordLen;
|
|
#ifdef DEBUG_JUSTIFY
|
|
--numWords;
|
|
NS_ASSERTION(total >= 0, "bad word data");
|
|
#endif
|
|
}
|
|
|
|
if (s0 != buf) {
|
|
delete s0;
|
|
}
|
|
}
|
|
|
|
void TextFrame::GetReflowMetrics(nsIPresContext* aCX,
|
|
nsReflowMetrics& aMetrics)
|
|
{
|
|
aMetrics.width = mRect.width;
|
|
aMetrics.height = mRect.height;
|
|
|
|
nsStyleFont* font =
|
|
(nsStyleFont*)mStyleContext->GetData(kStyleFontSID);
|
|
nsIFontMetrics* fm = aCX->GetMetricsFor(font->mFont);
|
|
aMetrics.ascent = fm->GetMaxAscent();
|
|
aMetrics.descent = fm->GetMaxDescent();
|
|
NS_RELEASE(fm);
|
|
}
|
|
|
|
nsIFrame::ReflowStatus
|
|
TextFrame::ResizeReflow(nsIPresContext* aCX,
|
|
nsReflowMetrics& aDesiredSize,
|
|
const nsSize& aMaxSize,
|
|
nsSize* aMaxElementSize)
|
|
{
|
|
#ifdef NOISY
|
|
ListTag(stdout);
|
|
printf(": resize reflow into %g,%g\n",
|
|
NS_TWIPS_TO_POINTS_FLOAT(aMaxSize.width),
|
|
NS_TWIPS_TO_POINTS_FLOAT(aMaxSize.height));
|
|
#endif
|
|
|
|
// Wipe out old justification information since it's going to change
|
|
if (nsnull != mWords) {
|
|
delete mWords;
|
|
mWords = nsnull;
|
|
}
|
|
#ifdef DEBUG_JUSTIFY
|
|
mNumWords = 0;
|
|
#endif
|
|
|
|
// Get starting offset into the content
|
|
PRInt32 startingOffset = 0;
|
|
if (nsnull != mPrevInFlow) {
|
|
TextFrame* prev = (TextFrame*) mPrevInFlow;
|
|
startingOffset = prev->mContentOffset + prev->mContentLength;
|
|
}
|
|
|
|
// Get cached state for containing block frame
|
|
nsBlockReflowState* state = nsnull;
|
|
nsIFrame* parent = mGeometricParent;
|
|
while (nsnull != parent) {
|
|
nsIHTMLFrameType* ft;
|
|
nsresult status = parent->QueryInterface(kIHTMLFrameTypeIID, (void**) &ft);
|
|
if (NS_OK == status) {
|
|
nsHTMLFrameType type = ft->GetFrameType();
|
|
if (eHTMLFrame_Block == type) {
|
|
break;
|
|
}
|
|
}
|
|
parent = parent->GetGeometricParent();
|
|
}
|
|
if (nsnull != parent) {
|
|
nsIPresShell* shell = aCX->GetShell();
|
|
state = (nsBlockReflowState*) shell->GetCachedData(parent);
|
|
NS_RELEASE(shell);
|
|
}
|
|
|
|
nsStyleFont* font =
|
|
(nsStyleFont*)mStyleContext->GetData(kStyleFontSID);
|
|
|
|
// Initialize mFlags (without destroying the TEXT_BLINK_ON bit) bits
|
|
// that are filled in by the reflow routines.
|
|
mFlags &= TEXT_BLINK_ON;
|
|
if (font->mFont.decorations & NS_STYLE_TEXT_DECORATION_BLINK) {
|
|
if (0 == (mFlags & TEXT_BLINK_ON)) {
|
|
mFlags |= TEXT_BLINK_ON;
|
|
gTextBlinker->AddFrame(this);
|
|
}
|
|
}
|
|
|
|
nsStyleMolecule* mol =
|
|
(nsStyleMolecule*)mStyleContext->GetData(kStyleMoleculeSID);
|
|
|
|
ReflowStatus status;
|
|
if (NS_STYLE_WHITESPACE_PRE == mol->whiteSpace) {
|
|
// Use a specialized routine for pre-formatted text
|
|
status = ReflowPre(aCX, aDesiredSize, aMaxSize,
|
|
aMaxElementSize, *font, startingOffset, state);
|
|
} else {
|
|
// Use normal wrapping routine for non-pre text (this includes
|
|
// text that is not wrapping)
|
|
status = ReflowNormal(aCX, aDesiredSize, aMaxSize,
|
|
aMaxElementSize, *font, *mol, startingOffset, state);
|
|
}
|
|
|
|
#ifdef NOISY
|
|
ListTag(stdout);
|
|
printf(": reflow %scomplete [flags=%x]\n",
|
|
((status == frComplete) ? "" : "not "), mFlags);
|
|
#endif
|
|
return status;
|
|
}
|
|
|
|
// Reflow normal text (stuff that doesn't have to deal with horizontal
|
|
// tabs). Normal text reflow may or may not wrap depending on the
|
|
// "whiteSpace" style property.
|
|
nsIFrame::ReflowStatus
|
|
TextFrame::ReflowNormal(nsIPresContext* aCX,
|
|
nsReflowMetrics& aDesiredSize,
|
|
const nsSize& aMaxSize,
|
|
nsSize* aMaxElementSize,
|
|
nsStyleFont& aFont,
|
|
nsStyleMolecule& aMol,
|
|
PRInt32 aStartingOffset,
|
|
nsBlockReflowState* aState)
|
|
{
|
|
Text* txt = (Text*) mContent;
|
|
const PRUnichar* cp = txt->mText + aStartingOffset;
|
|
const PRUnichar* end = cp + txt->mLength - aStartingOffset;
|
|
const PRUnichar* cpStart = cp;
|
|
mContentOffset = aStartingOffset;
|
|
|
|
nsIFontMetrics* fm = aCX->GetMetricsFor(aFont.mFont);
|
|
PRInt32 spaceWidth = fm->GetWidth(' ');
|
|
PRBool atLeftMargin = PR_TRUE;
|
|
PRBool wrapping = PR_TRUE;
|
|
if (NS_STYLE_WHITESPACE_NORMAL != aMol.whiteSpace) {
|
|
wrapping = PR_FALSE;
|
|
}
|
|
|
|
// Set whitespace skip flag
|
|
PRBool skipWhitespace = PR_FALSE;
|
|
if (nsnull != aState) {
|
|
if (!aState->allowLeadingWhitespace) {
|
|
skipWhitespace = PR_TRUE;
|
|
mFlags |= TEXT_SKIP_LEADING_WS;
|
|
}
|
|
}
|
|
|
|
// Try to fit as much of the text as possible. Note that if we are
|
|
// at the left margin then the first word always fits. In addition,
|
|
// we compute the size of the largest word that we contain. If we
|
|
// end up containing nothing (because there isn't enough space for
|
|
// the first word) then we still compute the size of that first
|
|
// non-fitting word.
|
|
|
|
// XXX XP_IS_SPACE must not return true for the unicode   character
|
|
// XXX what about &zwj and it's cousins?
|
|
nscoord x = 0;
|
|
nscoord maxWidth = aMaxSize.width;
|
|
nscoord width = 0;
|
|
nscoord maxWordWidth = 0;
|
|
const PRUnichar* lastWordEnd = cpStart;
|
|
PRBool hasMultibyte = PR_FALSE;
|
|
PRBool endsInWhitespace = PR_FALSE;
|
|
while (cp < end) {
|
|
PRUnichar ch = *cp++;
|
|
PRBool isWhitespace;
|
|
if (XP_IS_SPACE(ch)) {
|
|
// Compress whitespace down to a single whitespace
|
|
while (cp < end) {
|
|
ch = *cp;
|
|
if (XP_IS_SPACE(ch)) {
|
|
cp++;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
if (skipWhitespace) {
|
|
skipWhitespace = PR_FALSE;
|
|
continue;
|
|
}
|
|
width = spaceWidth;
|
|
isWhitespace = PR_TRUE;
|
|
} else {
|
|
// The character is not a space character. Find the end of the
|
|
// word and then measure it.
|
|
if (ch >= 256) {
|
|
hasMultibyte = PR_TRUE;
|
|
}
|
|
const PRUnichar* wordStart = cp - 1;
|
|
while (cp < end) {
|
|
ch = *cp;
|
|
if (ch >= 256) {
|
|
hasMultibyte = PR_TRUE;
|
|
}
|
|
if (!XP_IS_SPACE(ch)) {
|
|
cp++;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
width = fm->GetWidth(wordStart, PRUint32(cp - wordStart));
|
|
skipWhitespace = PR_FALSE;
|
|
isWhitespace = PR_FALSE;
|
|
}
|
|
|
|
// Now that we have the end of the word or whitespace, see if it
|
|
// will fit.
|
|
if (!atLeftMargin && wrapping && (x + width > maxWidth)) {
|
|
// The word/whitespace will not fit.
|
|
cp = lastWordEnd;
|
|
if (x == 0) {
|
|
// Nothing fit at all. In this case, we still may want to know
|
|
// the maxWordWidth so compute it right now.
|
|
maxWordWidth = width;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// The word fits. Add it to the run of text.
|
|
x += width;
|
|
if (width > maxWordWidth) {
|
|
maxWordWidth = width;
|
|
}
|
|
atLeftMargin = PR_FALSE;
|
|
lastWordEnd = cp;
|
|
width = 0;
|
|
endsInWhitespace = isWhitespace;
|
|
}
|
|
if (hasMultibyte) {
|
|
mFlags |= TEXT_HAS_MULTIBYTE;
|
|
}
|
|
|
|
if (nsnull != aState) {
|
|
if (0 == width) {
|
|
// Since we collapsed into nothingness (all our whitespace
|
|
// is ignored) leave the aState->allowLeadingWhitespace
|
|
// flag alone since it doesn't want leading whitespace
|
|
} else {
|
|
aState->allowLeadingWhitespace = !endsInWhitespace;
|
|
}
|
|
}
|
|
|
|
// Now we know our content length
|
|
mContentLength = lastWordEnd - cpStart;
|
|
if (0 == mContentLength) {
|
|
if (cp == end) {
|
|
// The entire chunk of text was whitespace that we skipped over.
|
|
aDesiredSize.width = 0;
|
|
aDesiredSize.height = 0;
|
|
aDesiredSize.ascent = 0;
|
|
aDesiredSize.descent = 0;
|
|
mContentLength = end - cpStart;
|
|
if (nsnull != aMaxElementSize) {
|
|
aMaxElementSize->width = 0;
|
|
aMaxElementSize->height = 0;
|
|
}
|
|
NS_RELEASE(fm);
|
|
return frComplete;
|
|
}
|
|
}
|
|
|
|
// Set desired size to the computed size
|
|
aDesiredSize.width = x;
|
|
aDesiredSize.height = fm->GetHeight();
|
|
aDesiredSize.ascent = fm->GetMaxAscent();
|
|
aDesiredSize.descent = fm->GetMaxDescent();
|
|
if (nsnull != aMaxElementSize) {
|
|
aMaxElementSize->width = maxWordWidth;
|
|
aMaxElementSize->height = fm->GetHeight();
|
|
}
|
|
NS_RELEASE(fm);
|
|
return (cp == end) ? frComplete : frNotComplete;
|
|
}
|
|
|
|
nsIFrame::ReflowStatus
|
|
TextFrame::ReflowPre(nsIPresContext* aCX,
|
|
nsReflowMetrics& aDesiredSize,
|
|
const nsSize& aMaxSize,
|
|
nsSize* aMaxElementSize,
|
|
nsStyleFont& aFont,
|
|
PRInt32 aStartingOffset,
|
|
nsBlockReflowState* aState)
|
|
{
|
|
Text* txt = (Text*) mContent;
|
|
const PRUnichar* cp = txt->mText + aStartingOffset;
|
|
const PRUnichar* cpStart = cp;
|
|
const PRUnichar* end = cp + txt->mLength - aStartingOffset;
|
|
|
|
mFlags |= TEXT_IS_PRE;
|
|
nsIFontMetrics* fm = aCX->GetMetricsFor(aFont.mFont);
|
|
const PRInt32* widths = fm->GetWidths();
|
|
PRInt32 width = 0;
|
|
PRBool hasMultibyte = PR_FALSE;
|
|
PRUint16 tabs = 0;
|
|
PRIntn col = 0;
|
|
if (nsnull != aState) {
|
|
col = aState->column;
|
|
}
|
|
mColumn = (PRUint16) col;
|
|
nscoord spaceWidth = widths[' '];
|
|
while (cp < end) {
|
|
PRUnichar ch = *cp++;
|
|
if (ch == '\n') {
|
|
if (nsnull != aState) {
|
|
aState->breakAfterChild = PR_TRUE;
|
|
}
|
|
break;
|
|
}
|
|
if (ch == '\t') {
|
|
// Advance to next tab stop
|
|
PRIntn spaces = 8 - (col & 7);
|
|
width += spaces * spaceWidth;
|
|
col += spaces;
|
|
tabs++;
|
|
continue;
|
|
}
|
|
if (ch == CH_NBSP) {
|
|
width += spaceWidth;
|
|
col++;
|
|
continue;
|
|
}
|
|
if (ch < 256) {
|
|
width += widths[ch];
|
|
} else {
|
|
widths += fm->GetWidth(ch);
|
|
hasMultibyte = PR_TRUE;
|
|
}
|
|
col++;
|
|
}
|
|
if (nsnull != aState) {
|
|
aState->column = col;
|
|
}
|
|
if (hasMultibyte) {
|
|
mFlags |= TEXT_HAS_MULTIBYTE;
|
|
}
|
|
TEXT_SET_TAB_COUNT(mFlags, tabs);
|
|
|
|
mContentOffset = aStartingOffset;
|
|
mContentLength = cp - cpStart;
|
|
aDesiredSize.width = width;
|
|
aDesiredSize.height = fm->GetHeight();
|
|
aDesiredSize.ascent = fm->GetMaxAscent();
|
|
aDesiredSize.descent = fm->GetMaxDescent();
|
|
if (nsnull != aMaxElementSize) {
|
|
aMaxElementSize->width = aDesiredSize.width;
|
|
aMaxElementSize->height = aDesiredSize.height;
|
|
}
|
|
NS_RELEASE(fm);
|
|
return (cp == end) ? frComplete : frNotComplete;
|
|
}
|
|
|
|
#define NUM_WORDS 20
|
|
|
|
void TextFrame::JustifyReflow(nsIPresContext* aCX, nscoord aAvailableSpace)
|
|
{
|
|
if (mFlags & TEXT_IS_PRE) {
|
|
// no way
|
|
return;
|
|
}
|
|
|
|
nsStyleFont* font =
|
|
(nsStyleFont*)mStyleContext->GetData(kStyleFontSID);
|
|
nsIFontMetrics* fm = aCX->GetMetricsFor(font->mFont);
|
|
PRInt32 spaceWidth = fm->GetWidth(' ');
|
|
|
|
PRUint32 words[NUM_WORDS];
|
|
PRUint32* wp0 = words;
|
|
PRIntn maxWords = NUM_WORDS;
|
|
|
|
PRBool skipWhitespace = PRBool((mFlags & TEXT_SKIP_LEADING_WS) != 0);
|
|
|
|
// Count the number of spaces and words in the text we are going to
|
|
// justify. Also measure each word in the text and skip over leading
|
|
// space and compress down whitespace.
|
|
Text* txt = (Text*) mContent;
|
|
const PRUnichar* cp = txt->mText + mContentOffset;
|
|
const PRUnichar* cpStart = cp;
|
|
const PRUnichar* end = cp + mContentLength;
|
|
PRUint32* wp = wp0;
|
|
PRUint32* wpend = wp0 + maxWords;
|
|
PRIntn spaceCount = 0;
|
|
PRIntn total = 0;
|
|
while (cp < end) {
|
|
if (wp == wpend) {
|
|
// Make space for more words
|
|
PRUint32 numWords = wp - wp0;
|
|
maxWords = maxWords * 2;
|
|
PRUint32* newwp0 = new PRUint32[maxWords];
|
|
if (nsnull == newwp0) {
|
|
goto bail;
|
|
}
|
|
nsCRT::memcpy(newwp0, wp0, sizeof(PRUint32) * numWords);
|
|
if (wp0 != words) {
|
|
delete wp0;
|
|
}
|
|
wp0 = newwp0;
|
|
wp = wp0 + numWords;
|
|
wpend = wp0 + maxWords;
|
|
}
|
|
|
|
PRUint32 wordInfo;
|
|
PRUint32 wordLen;
|
|
nscoord wordWidth;
|
|
const PRUnichar* wordStart = cp;
|
|
PRUnichar ch = *cp++;
|
|
if (XP_IS_SPACE(ch)) {
|
|
// Compress whitespace down to a single whitespace
|
|
while (cp < end) {
|
|
ch = *cp;
|
|
if (XP_IS_SPACE(ch)) {
|
|
cp++;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
// Use wordWidth as a flag for spaces to indicate which spaces
|
|
// we give zero space to and which we don't.
|
|
if (skipWhitespace) {
|
|
wordWidth = 0;
|
|
} else {
|
|
wordWidth = spaceWidth;
|
|
spaceCount++;
|
|
}
|
|
wordInfo = WORD_IS_SPACE;
|
|
wordLen = cp - wordStart;
|
|
} else {
|
|
while (cp < end) {
|
|
ch = *cp;
|
|
if (!XP_IS_SPACE(ch)) {
|
|
cp++;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
wordLen = cp - wordStart;
|
|
wordWidth = fm->GetWidth(wordStart, wordLen);
|
|
skipWhitespace = PR_FALSE;
|
|
wordInfo = WORD_IS_WORD;
|
|
}
|
|
if ((wordWidth > MAX_WORD_WIDTH) || (wordLen > MAX_WORD_LENGTH)) {
|
|
// We can't fit the information about this word into our
|
|
// bitfield. Bail.
|
|
goto bail;
|
|
}
|
|
wordInfo = wordInfo |
|
|
(wordLen << WORD_LENGTH_SHIFT) |
|
|
(wordWidth << WORD_WIDTH_SHIFT);
|
|
*wp++ = wordInfo;
|
|
total += wordLen;
|
|
}
|
|
NS_ASSERTION(total == mContentLength, "bad total");
|
|
|
|
// Now that we know where all the words and spaces are, and we have
|
|
// measured the words, divy up the aAvailableSpace to each of the
|
|
// spaces.
|
|
if (spaceCount != 0) {
|
|
/*
|
|
* See if the last word is a space. If it is, don't count it as a space
|
|
* and give it's space to the available space.
|
|
*/
|
|
PRUint32 wordInfo = wp[-1];
|
|
if ((wordInfo & WORD_IS_WORD) == 0) {
|
|
if (--spaceCount == 0) {
|
|
// Never mind: the one and only space was at the end. Harumph.
|
|
goto bail;
|
|
}
|
|
aAvailableSpace += spaceWidth;
|
|
// Update wordInfo to have a zero width for the trailing space
|
|
wp[-1] = WORD_IS_SPACE | (wordInfo & WORD_LENGTH_MASK);
|
|
}
|
|
nscoord add = aAvailableSpace / spaceCount;
|
|
if (add == 0) {
|
|
add = 1;
|
|
}
|
|
PRIntn numWords = wp - wp0;
|
|
mWords = new PRUint32[numWords];
|
|
if (nsnull == mWords) {
|
|
goto bail;
|
|
}
|
|
wp = wp0;
|
|
PRUint32* tp = mWords;
|
|
PRUint32* tpend = tp + numWords;
|
|
total = 0;
|
|
#ifdef DEBUG_JUSTIFY
|
|
mNumWords = numWords;
|
|
#endif
|
|
while (--numWords >= 0) {
|
|
wordInfo = *wp++;
|
|
if (wordInfo & WORD_IS_WORD) {
|
|
// We already know about the word
|
|
*tp++ = wordInfo;
|
|
total += GET_WORD_LENGTH(wordInfo);
|
|
continue;
|
|
} else {
|
|
nscoord spaceWidth = GET_WORD_WIDTH(wordInfo);
|
|
if (spaceWidth == 0) {
|
|
// This is leading space that doesn't count
|
|
*tp++ = wordInfo;
|
|
total += GET_WORD_LENGTH(wordInfo);
|
|
continue;
|
|
}
|
|
// This is a space that gets some of the available space
|
|
if (add > aAvailableSpace) {
|
|
add = aAvailableSpace;
|
|
}
|
|
spaceWidth += add;
|
|
aAvailableSpace -= add;
|
|
if (aAvailableSpace == 0) {
|
|
// We used it all up already so stop adding
|
|
add = 0;
|
|
}
|
|
if (--spaceCount == 0) {
|
|
// Give the last space the remaining available space
|
|
spaceWidth += aAvailableSpace;
|
|
aAvailableSpace = 0;
|
|
}
|
|
if (spaceWidth > MAX_WORD_WIDTH) {
|
|
// Sad but true; leave it be. Maybe the next word can use it
|
|
*tp++ = wordInfo;
|
|
total += GET_WORD_LENGTH(wordInfo);
|
|
continue;
|
|
}
|
|
wordInfo = (wordInfo & ~WORD_WIDTH_MASK) |
|
|
(spaceWidth << WORD_WIDTH_SHIFT);
|
|
*tp++ = wordInfo;
|
|
total += GET_WORD_LENGTH(wordInfo);
|
|
}
|
|
}
|
|
NS_ASSERTION(aAvailableSpace == 0, "bad divy up");
|
|
NS_ASSERTION(tp == tpend, "bad tp");
|
|
NS_ASSERTION(total == mContentLength, "bad total");
|
|
}
|
|
|
|
bail:
|
|
if (wp0 != words) {
|
|
delete wp0;
|
|
}
|
|
NS_RELEASE(fm);
|
|
}
|
|
#undef NUM_WORDS
|
|
|
|
void TextFrame::List(FILE* out, PRInt32 aIndent) const
|
|
{
|
|
PRInt32 i;
|
|
for (i = aIndent; --i >= 0; ) fputs(" ", out);
|
|
fprintf(out, "Text(%d)@%p[%d,%d] ",
|
|
mIndexInParent, this,
|
|
mContentOffset, mContentOffset+mContentLength-1);
|
|
out << mRect;
|
|
aIndent++;
|
|
|
|
for (i = aIndent; --i >= 0; ) fputs(" ", out);
|
|
Text* txt = (Text*) mContent;
|
|
nsAutoString tmp;
|
|
txt->ToCString(tmp, mContentOffset, mContentLength);
|
|
fputs("\"", out);
|
|
fputs(tmp, out);
|
|
fputs("\"\n", out);
|
|
|
|
aIndent--;
|
|
for (i = aIndent; --i >= 0; ) fputs(" ", out);
|
|
fputs(">\n", out);
|
|
|
|
#ifdef NOISY
|
|
if (nsnull != mWords) {
|
|
for (i = aIndent; --i >= 0; ) fputs(" ", out);
|
|
fputs(" @words ", out);
|
|
PRUint32* wp = mWords;
|
|
PRInt32 total = mContentLength;
|
|
while (total != 0) {
|
|
PRUint32 wordInfo = *wp++;
|
|
nscoord wordWidth = GET_WORD_WIDTH(wordInfo);
|
|
PRIntn wordLen = GET_WORD_LENGTH(wordInfo);
|
|
fprintf(out, "%c-%d-%d ",
|
|
((wordInfo & WORD_IS_WORD) ? 'w' : 's'),
|
|
wordWidth, wordLen);
|
|
total -= wordLen;
|
|
NS_ASSERTION(total >= 0, "bad word data");
|
|
}
|
|
fputs("\n", out);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
static NS_DEFINE_IID(kIDOMTextIID, NS_IDOMTEXT_IID);
|
|
static NS_DEFINE_IID(kIDOMNodeIID, NS_IDOMNODE_IID);
|
|
|
|
|
|
Text::Text(const PRUnichar* aText, PRInt32 aLength)
|
|
{
|
|
if (0 == aLength) {
|
|
aLength = nsCRT::strlen(aText);
|
|
}
|
|
mLength = aLength;
|
|
mText = new PRUnichar[aLength];
|
|
nsCRT::memcpy(mText, aText, aLength * sizeof(PRUnichar));
|
|
}
|
|
|
|
Text::Text()
|
|
{
|
|
mText = nsnull;
|
|
mLength = 0;
|
|
}
|
|
|
|
Text::~Text()
|
|
{
|
|
if (nsnull != mText) {
|
|
delete mText;
|
|
mText = nsnull;
|
|
}
|
|
mLength = 0;
|
|
}
|
|
|
|
nsresult Text::QueryInterface(const nsIID& aIID, void** aInstancePtr)
|
|
{
|
|
NS_PRECONDITION(nsnull != aInstancePtr, "null pointer");
|
|
|
|
if (nsnull == aInstancePtr) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
|
|
if (aIID.Equals(kIDOMTextIID)) {
|
|
*aInstancePtr = (void*)(nsIDOMText*)this;
|
|
nsHTMLContent::AddRef();
|
|
return NS_OK;
|
|
}
|
|
if (aIID.Equals(kIDOMNodeIID)) {
|
|
*aInstancePtr = (void*)(nsIDOMNode*)(nsIDOMText*)this;
|
|
nsHTMLContent::AddRef();
|
|
return NS_OK;
|
|
}
|
|
|
|
return nsHTMLContent::QueryInterface(aIID, aInstancePtr);
|
|
}
|
|
|
|
nsrefcnt Text::AddRef(void)
|
|
{
|
|
return nsHTMLContent::AddRef();
|
|
}
|
|
|
|
nsrefcnt Text::Release(void)
|
|
{
|
|
return nsHTMLContent::Release();
|
|
}
|
|
|
|
void Text::List(FILE* out, PRInt32 aIndent) const
|
|
{
|
|
for (PRInt32 i = aIndent; --i >= 0; ) fputs(" ", out);
|
|
fputs("Text", out);
|
|
ListAttributes(out);
|
|
fprintf(out, " RefCnt=%d<\"", mRefCnt);
|
|
|
|
nsAutoString tmp;
|
|
ToCString(tmp, 0, mLength);
|
|
fputs(tmp, out);
|
|
|
|
fputs("\">\n", out);
|
|
}
|
|
|
|
void Text::ToHTMLString(nsString& aBuf) const
|
|
{
|
|
aBuf.SetLength(0);
|
|
aBuf.Append(mText, mLength);
|
|
}
|
|
|
|
void Text::ToCString(nsString& aBuf, PRInt32 aOffset, PRInt32 aLen) const
|
|
{
|
|
PRUnichar* cp = mText + aOffset;
|
|
PRUnichar* end = cp + aLen;
|
|
while (cp < end) {
|
|
PRUnichar ch = *cp++;
|
|
if (ch == '\r') {
|
|
aBuf.Append("\\r");
|
|
} else if (ch == '\n') {
|
|
aBuf.Append("\\n");
|
|
} else if (ch == '\t') {
|
|
aBuf.Append("\\t");
|
|
} else if ((ch < ' ') || (ch >= 127)) {
|
|
aBuf.Append("\\0");
|
|
aBuf.Append((PRInt32)ch, 8);
|
|
} else {
|
|
aBuf.Append(ch);
|
|
}
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
// From nsITextContent; might want this later
|
|
PRInt32 Text::GetLength()
|
|
{
|
|
return mLength;
|
|
}
|
|
|
|
void Text::GetText(nsString& aBuf, PRInt32 aOffset, PRInt32 aCount)
|
|
{
|
|
aBuf.SetLength(0);
|
|
if ((PRUint32(aOffset) >= PRUint32(mLength)) ||
|
|
(aCount <= 0)) {
|
|
return;
|
|
}
|
|
if (aOffset + aCount > mLength) {
|
|
aCount = mLength - aOffset;
|
|
}
|
|
aBuf.Append(mText + aOffset, aCount);
|
|
}
|
|
#endif
|
|
|
|
nsIFrame* Text::CreateFrame(nsIPresContext* aCX,
|
|
PRInt32 aIndexInParent,
|
|
nsIFrame* aParentFrame)
|
|
{
|
|
nsIFrame* rv = new TextFrame(this, aIndexInParent, aParentFrame);
|
|
return rv;
|
|
}
|
|
|
|
nsresult Text::GetScriptObject(JSContext *aContext, void** aScriptObject)
|
|
{
|
|
nsresult res = NS_OK;
|
|
if (nsnull == mScriptObject) {
|
|
*aScriptObject = nsnull;
|
|
if (nsnull != mParent) {
|
|
nsIScriptObjectOwner *parent;
|
|
if (NS_OK == mParent->QueryInterface(kIScriptObjectOwner, (void**)&parent)) {
|
|
parent->GetScriptObject(aContext, aScriptObject);
|
|
NS_RELEASE(parent);
|
|
}
|
|
}
|
|
res = NS_NewScriptText(aContext, this, (JSObject*)*aScriptObject, (JSObject**)&mScriptObject);
|
|
}
|
|
*aScriptObject = mScriptObject;
|
|
return res;
|
|
}
|
|
|
|
//
|
|
// nsIDOMText interface
|
|
//
|
|
nsresult Text::GetNodeType(PRInt32 *aType)
|
|
{
|
|
*aType = nsHTMLContent::TEXT;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult Text::GetParentNode(nsIDOMNode **aNode)
|
|
{
|
|
return nsHTMLContent::GetParentNode(aNode);
|
|
}
|
|
|
|
nsresult Text::GetChildNodes(nsIDOMNodeIterator **aIterator)
|
|
{
|
|
return nsHTMLContent::GetChildNodes(aIterator);
|
|
}
|
|
|
|
nsresult Text::HasChildNodes()
|
|
{
|
|
return nsHTMLContent::HasChildNodes();
|
|
}
|
|
|
|
nsresult Text::GetFirstChild(nsIDOMNode **aNode)
|
|
{
|
|
return nsHTMLContent::GetFirstChild(aNode);
|
|
}
|
|
|
|
nsresult Text::GetPreviousSibling(nsIDOMNode **aNode)
|
|
{
|
|
return nsHTMLContent::GetPreviousSibling(aNode);
|
|
}
|
|
|
|
nsresult Text::GetNextSibling(nsIDOMNode **aNode)
|
|
{
|
|
return nsHTMLContent::GetNextSibling(aNode);
|
|
}
|
|
|
|
nsresult Text::InsertBefore(nsIDOMNode *newChild, nsIDOMNode *refChild)
|
|
{
|
|
return nsHTMLContent::InsertBefore(newChild, refChild);
|
|
}
|
|
|
|
nsresult Text::ReplaceChild(nsIDOMNode *newChild, nsIDOMNode *oldChild)
|
|
{
|
|
return nsHTMLContent::ReplaceChild(newChild, oldChild);
|
|
}
|
|
|
|
nsresult Text::RemoveChild(nsIDOMNode *oldChild)
|
|
{
|
|
return nsHTMLContent::RemoveChild(oldChild);
|
|
}
|
|
|
|
nsresult Text::GetData(nsString &aString)
|
|
{
|
|
if (nsnull != mText) {
|
|
aString = mText;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult Text::SetData(nsString &aString)
|
|
{
|
|
if (mText) delete[] mText;
|
|
|
|
mLength = aString.Length();
|
|
mText = aString.ToNewUnicode();
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult Text::Append(nsString &aData)
|
|
{
|
|
PRUint32 length = aData.Length();
|
|
PRUnichar *text = new PRUnichar[mLength + length];
|
|
nsCRT::memcpy(text, mText, mLength * sizeof(PRUnichar));
|
|
nsCRT::memcpy(text + mLength * sizeof(PRUnichar), aData.GetUnicode(), length * sizeof(PRUnichar));
|
|
|
|
if (mText) delete[] mText;
|
|
mLength += length;
|
|
mText = text;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult Text::Insert(int offset, nsString &aData)
|
|
{
|
|
if (offset < mLength) {
|
|
PRInt32 length = aData.Length();
|
|
PRUnichar *text = new PRUnichar[mLength + length];
|
|
PRUnichar *data = aData.GetUnicode();
|
|
PRInt32 i;
|
|
|
|
for (i = 0; i < offset; i++) {
|
|
*(text++) = *(mText++);
|
|
}
|
|
for (i = 0; i < length; i++) {
|
|
*(text++) = *(data++);
|
|
}
|
|
for (i = 0; i < mLength - offset; i++) {
|
|
*(text++) = *(mText++);
|
|
}
|
|
|
|
if (mText) delete[] (mText - mLength);
|
|
mLength += length;
|
|
mText = text - mLength;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult Text::Delete(int offset, int count)
|
|
{
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
nsresult Text::Replace(int offset, int count, nsString &aData)
|
|
{
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
nsresult Text::Splice(nsIDOMElement *element, int offset, int count)
|
|
{
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
nsresult
|
|
NS_NewHTMLText(nsIHTMLContent** aInstancePtrResult,
|
|
const PRUnichar* us, PRInt32 uslen)
|
|
{
|
|
nsIHTMLContent* it = new Text(us, uslen);
|
|
if (nsnull == it) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
return it->QueryInterface(kIHTMLContentIID, (void **) aInstancePtrResult);
|
|
}
|
|
|
|
nsresult
|
|
NS_NewHTMLText(nsIHTMLContent** aInstancePtrResult,
|
|
nsIArena* aArena, const PRUnichar* us, PRInt32 uslen)
|
|
{
|
|
nsIHTMLContent* it = new(aArena) Text(us, uslen);
|
|
if (nsnull == it) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
return it->QueryInterface(kIHTMLContentIID, (void **) aInstancePtrResult);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
class SharedText : public Text {
|
|
public:
|
|
SharedText(PRUnichar* aText, PRInt32 aLength);
|
|
|
|
protected:
|
|
virtual ~SharedText();
|
|
};
|
|
|
|
SharedText::SharedText(PRUnichar* aText, PRInt32 aLength)
|
|
: Text()
|
|
{
|
|
if (0 == aLength) {
|
|
aLength = nsCRT::strlen(aText);
|
|
}
|
|
mText = aText;
|
|
mLength = aLength;
|
|
}
|
|
|
|
SharedText::~SharedText()
|
|
{
|
|
mText = nsnull;
|
|
}
|
|
|
|
nsresult
|
|
NS_NewSharedHTMLText(nsIHTMLContent** aInstancePtrResult,
|
|
PRUnichar* us, PRInt32 uslen)
|
|
{
|
|
nsIHTMLContent* it = new SharedText(us, uslen);
|
|
if (nsnull == it) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
return it->QueryInterface(kIHTMLContentIID, (void **) aInstancePtrResult);
|
|
}
|
|
|
|
nsresult
|
|
NS_NewSharedHTMLText(nsIHTMLContent** aInstancePtrResult,
|
|
nsIArena* aArena, PRUnichar* us, PRInt32 uslen)
|
|
{
|
|
nsIHTMLContent* it = new(aArena) SharedText(us, uslen);
|
|
if (nsnull == it) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
return it->QueryInterface(kIHTMLContentIID, (void **) aInstancePtrResult);
|
|
}
|