moved attributes from position to display struct git-svn-id: svn://10.0.0.236/trunk@2304 18797224-902f-48f8-a5cc-f745e15eee43
1997 lines
58 KiB
C++
1997 lines
58 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 "nsVoidArray.h"
|
|
#include "prprf.h"
|
|
#include "nsIDOMText.h"
|
|
#include "nsIDocument.h"
|
|
|
|
// Selection includes
|
|
#include "nsISelection.h"
|
|
#include "nsSelectionRange.h"
|
|
|
|
#define gCalcDebug 0
|
|
|
|
PRBool IsInRange(nsIContent * aStart, nsIContent * aEnd, nsIContent * aContent);
|
|
|
|
|
|
#ifdef NS_DEBUG
|
|
#undef NOISY
|
|
#undef 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(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, nsIFrame* aParentFrame);
|
|
|
|
NS_IMETHOD Paint(nsIPresContext& aPresContext,
|
|
nsIRenderingContext& aRenderingContext,
|
|
const nsRect& aDirtyRect);
|
|
|
|
NS_IMETHOD Reflow(nsIPresContext* aCX,
|
|
nsReflowMetrics& aDesiredSize,
|
|
const nsReflowState& aReflowState,
|
|
nsReflowStatus& aStatus);
|
|
|
|
NS_IMETHOD GetReflowMetrics(nsIPresContext* aPresContext,
|
|
nsReflowMetrics& aMetrics);
|
|
|
|
NS_IMETHOD JustifyReflow(nsIPresContext* aCX,
|
|
nscoord aAvailableSpace);
|
|
|
|
NS_IMETHOD GetCursorAt(nsIPresContext& aPresContext,
|
|
const nsPoint& aPoint,
|
|
nsIFrame** aFrame,
|
|
PRInt32& aCursor);
|
|
|
|
NS_IMETHOD List(FILE* out, PRInt32 aIndent) const;
|
|
|
|
PRInt32 GetPosition(nsIPresContext& aCX,
|
|
nsGUIEvent* aEvent,
|
|
nsIFrame * aNewFrame);
|
|
|
|
void CalcCursorPosition(nsIPresContext& aPresContext,
|
|
nsGUIEvent* aEvent,
|
|
TextFrame * aNewFrame,
|
|
PRInt32 & aOffset,
|
|
PRInt32 & aWidth);
|
|
|
|
void CalcActualPosition(PRUint32 &aMsgType,
|
|
const PRUnichar* aCPStart,
|
|
const PRUnichar* aCP,
|
|
PRInt32 & aOffset,
|
|
PRInt32 & aWidth,
|
|
nsIFontMetrics * aFM);
|
|
|
|
PRInt32 GetContentOffset() { return mContentOffset;}
|
|
PRInt32 GetContentLength() { return mContentLength;}
|
|
|
|
protected:
|
|
virtual ~TextFrame();
|
|
|
|
void PaintRegularText(nsIPresContext& aPresContext,
|
|
nsIRenderingContext& aRenderingContext,
|
|
const nsRect& aDirtyRect,
|
|
nscoord dx, nscoord dy);
|
|
|
|
void PaintJustifiedText(nsIRenderingContext& aRenderingContext,
|
|
const nsRect& aDirtyRect,
|
|
nscoord dx, nscoord dy);
|
|
|
|
nsReflowStatus ReflowPre(nsIPresContext* aCX,
|
|
nsReflowMetrics& aDesiredSize,
|
|
const nsSize& aMaxSize,
|
|
nsSize* aMaxElementSize,
|
|
nsStyleFont& aFont,
|
|
PRInt32 aStartingOffset,
|
|
nsLineLayout* aLineState);
|
|
|
|
nsReflowStatus ReflowNormal(nsIPresContext* aCX,
|
|
nsReflowMetrics& aDesiredSize,
|
|
const nsSize& aMaxSize,
|
|
nsSize* aMaxElementSize,
|
|
nsStyleFont& aFont,
|
|
nsStyleText& aTextStyle,
|
|
PRInt32 aStartingOffset,
|
|
nsLineLayout* aLineState);
|
|
|
|
public:
|
|
PRInt32 mContentOffset;
|
|
PRInt32 mContentLength;
|
|
|
|
// XXX need a better packing
|
|
PRUint32 mFlags;
|
|
PRUint32 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 0x01
|
|
#define TEXT_HAS_MULTIBYTE 0x02
|
|
#define TEXT_IS_PRE 0x04
|
|
#define TEXT_BLINK_ON 0x08
|
|
#define TEXT_ENDS_IN_WHITESPACE 0x10
|
|
|
|
#define TEXT_GET_TAB_COUNT(_mf) ((_mf) >> 5)
|
|
#define TEXT_SET_TAB_COUNT(_mf,_tabs) (_mf) = (_mf) | ((_tabs)<< 5)
|
|
|
|
// 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 nsresult CreateFrame(nsIPresContext* aPresContext,
|
|
nsIFrame* aParentFrame,
|
|
nsIStyleContext* aStyleContext,
|
|
nsIFrame*& aResult);
|
|
|
|
// 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, view);
|
|
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, nsIFrame* aParentFrame)
|
|
: nsSplittableFrame(aContent, 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;
|
|
}
|
|
}
|
|
|
|
NS_METHOD TextFrame::GetCursorAt(nsIPresContext& aPresContext,
|
|
const nsPoint& aPoint,
|
|
nsIFrame** aFrame,
|
|
PRInt32& aCursor)
|
|
{
|
|
aCursor = NS_STYLE_CURSOR_IBEAM;
|
|
return NS_OK;
|
|
}
|
|
|
|
// XXX it would be nice to use a method pointer here, but I don't want
|
|
// to pay for the storage.
|
|
|
|
NS_METHOD TextFrame::Paint(nsIPresContext& aPresContext,
|
|
nsIRenderingContext& aRenderingContext,
|
|
const nsRect& aDirtyRect)
|
|
{
|
|
if ((0 != (mFlags & TEXT_BLINK_ON)) && gBlinkTextOff) {
|
|
return NS_OK;
|
|
}
|
|
|
|
nsStyleDisplay* disp =
|
|
(nsStyleDisplay*)mStyleContext->GetData(eStyleStruct_Display);
|
|
|
|
if (disp->mVisible) {
|
|
// Get style data
|
|
nsStyleColor* color =
|
|
(nsStyleColor*)mStyleContext->GetData(eStyleStruct_Color);
|
|
nsStyleFont* font =
|
|
(nsStyleFont*)mStyleContext->GetData(eStyleStruct_Font);
|
|
|
|
// 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 NS_OK;
|
|
}
|
|
PaintRegularText(aPresContext, aRenderingContext, aDirtyRect, 0, 0);
|
|
//nsFrame::Paint(aPresContext, aRenderingContext, aDirtyRect);
|
|
if (font->mThreeD) {
|
|
nscoord onePixel = nscoord(1.0f * aPresContext.GetPixelsToTwips());
|
|
aRenderingContext.SetColor(color->mBackgroundColor);
|
|
PaintRegularText(aPresContext, aRenderingContext, aDirtyRect, onePixel, onePixel);
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void TextFrame::PaintRegularText(nsIPresContext& aPresContext,
|
|
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--;
|
|
}
|
|
}
|
|
|
|
// Get Selection Object
|
|
nsIPresShell * shell = aPresContext.GetShell();
|
|
nsIDocument * doc = shell->GetDocument();
|
|
nsISelection * selection = doc->GetSelection();
|
|
nsSelectionRange * range = selection->GetRange();
|
|
|
|
NS_RELEASE(shell);
|
|
NS_RELEASE(doc);
|
|
NS_RELEASE(selection);
|
|
|
|
|
|
if (IsInRange(range->GetStartContent(), range->GetEndContent(), mContent)) {
|
|
nsRect rect;
|
|
GetRect(rect);
|
|
rect.width--;
|
|
rect.height--;
|
|
|
|
nsIFontMetrics * fm = aRenderingContext.GetFontMetrics();
|
|
|
|
aRenderingContext.SetColor(NS_RGB(0,0,0));
|
|
aRenderingContext.FillRect(rect);
|
|
|
|
aRenderingContext.SetColor(NS_RGB(255,255,255));
|
|
|
|
// Render the text
|
|
aRenderingContext.DrawString(s0, s - s0, dx, dy, mRect.width);
|
|
nsStyleColor* color = (nsStyleColor*)mStyleContext->GetData(eStyleStruct_Color);
|
|
aRenderingContext.SetColor(color->mColor);
|
|
} else {
|
|
// Render the text
|
|
nsStyleColor* color = (nsStyleColor*)mStyleContext->GetData(eStyleStruct_Color);
|
|
aRenderingContext.SetColor(color->mColor);
|
|
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--;
|
|
}
|
|
}
|
|
|
|
// Get Selection Object
|
|
nsIPresShell * shell = aPresContext.GetShell();
|
|
nsIDocument * doc = shell->GetDocument();
|
|
nsISelection * selection = doc->GetSelection();
|
|
nsSelectionRange * range = selection->GetRange();
|
|
|
|
NS_RELEASE(shell);
|
|
NS_RELEASE(doc);
|
|
NS_RELEASE(selection);
|
|
|
|
if (IsInRange(range->GetStartContent(), range->GetEndContent(), mContent)) {
|
|
nsRect rect;
|
|
GetRect(rect);
|
|
rect.width--;
|
|
rect.height--;
|
|
|
|
nsIFontMetrics * fm = aRenderingContext.GetFontMetrics();
|
|
|
|
aRenderingContext.SetColor(NS_RGB(0,0,0));
|
|
aRenderingContext.FillRect(rect);
|
|
|
|
aRenderingContext.SetColor(NS_RGB(255,255,255));
|
|
|
|
// Render the text
|
|
aRenderingContext.DrawString(s0, s - s0, dx, dy, mRect.width);
|
|
nsStyleColor* color = (nsStyleColor*)mStyleContext->GetData(eStyleStruct_Color);
|
|
aRenderingContext.SetColor(color->mColor);
|
|
} else {
|
|
// Render the text
|
|
nsStyleColor* color = (nsStyleColor*)mStyleContext->GetData(eStyleStruct_Color);
|
|
aRenderingContext.SetColor(color->mColor);
|
|
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;
|
|
}
|
|
}
|
|
|
|
NS_METHOD TextFrame::GetReflowMetrics(nsIPresContext* aCX,
|
|
nsReflowMetrics& aMetrics)
|
|
{
|
|
// Get cached state for containing block frame
|
|
nsLineLayout* lineLayoutState = nsnull;
|
|
nsBlockReflowState* state = nsBlockFrame::FindBlockReflowState(aCX, this);
|
|
if (nsnull != state) {
|
|
lineLayoutState = state->mCurrentLine;
|
|
if (nsnull != lineLayoutState) {
|
|
lineLayoutState->mReflowResult = NS_LINE_LAYOUT_REFLOW_RESULT_AWARE;
|
|
}
|
|
|
|
if (0 == (mFlags & TEXT_IS_PRE)) {
|
|
if (mRect.width != 0) {
|
|
lineLayoutState->mSkipLeadingWhiteSpace =
|
|
(0 != (mFlags & TEXT_ENDS_IN_WHITESPACE));
|
|
}
|
|
}
|
|
}
|
|
|
|
aMetrics.width = mRect.width;
|
|
aMetrics.height = mRect.height;
|
|
|
|
nsStyleFont* font =
|
|
(nsStyleFont*)mStyleContext->GetData(eStyleStruct_Font);
|
|
nsIFontMetrics* fm = aCX->GetMetricsFor(font->mFont);
|
|
aMetrics.ascent = fm->GetMaxAscent();
|
|
aMetrics.descent = fm->GetMaxDescent();
|
|
NS_RELEASE(fm);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_METHOD TextFrame::Reflow(nsIPresContext* aCX,
|
|
nsReflowMetrics& aDesiredSize,
|
|
const nsReflowState& aReflowState,
|
|
nsReflowStatus& aStatus)
|
|
{
|
|
#ifdef NOISY
|
|
ListTag(stdout);
|
|
printf(": resize reflow into %g,%g\n",
|
|
NS_TWIPS_TO_POINTS_FLOAT(aReflowState.maxSize.width),
|
|
NS_TWIPS_TO_POINTS_FLOAT(aReflowState.maxSize.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
|
|
nsLineLayout* lineLayoutState = nsnull;
|
|
nsBlockReflowState* state = nsBlockFrame::FindBlockReflowState(aCX, this);
|
|
if (nsnull != state) {
|
|
lineLayoutState = state->mCurrentLine;
|
|
if (nsnull != lineLayoutState) {
|
|
lineLayoutState->mReflowResult = NS_LINE_LAYOUT_REFLOW_RESULT_AWARE;
|
|
}
|
|
}
|
|
|
|
nsStyleFont* font =
|
|
(nsStyleFont*)mStyleContext->GetData(eStyleStruct_Font);
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
|
|
nsStyleText* text =
|
|
(nsStyleText*)mStyleContext->GetData(eStyleStruct_Text);
|
|
|
|
if (NS_STYLE_WHITESPACE_PRE == text->mWhiteSpace) {
|
|
// Use a specialized routine for pre-formatted text
|
|
aStatus = ReflowPre(aCX, aDesiredSize, aReflowState.maxSize,
|
|
aDesiredSize.maxElementSize, *font, startingOffset,
|
|
lineLayoutState);
|
|
} else {
|
|
// Use normal wrapping routine for non-pre text (this includes
|
|
// text that is not wrapping)
|
|
aStatus = ReflowNormal(aCX, aDesiredSize, aReflowState.maxSize,
|
|
aDesiredSize.maxElementSize, *font, *text,
|
|
startingOffset, lineLayoutState);
|
|
}
|
|
|
|
#ifdef NOISY
|
|
ListTag(stdout);
|
|
printf(": reflow %scomplete [flags=%x]\n",
|
|
(NS_FRAME_IS_COMPLETE(aStatus) ? "" : "not "), mFlags);
|
|
#endif
|
|
return NS_OK;
|
|
}
|
|
|
|
// 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.
|
|
nsReflowStatus
|
|
TextFrame::ReflowNormal(nsIPresContext* aCX,
|
|
nsReflowMetrics& aDesiredSize,
|
|
const nsSize& aMaxSize,
|
|
nsSize* aMaxElementSize,
|
|
nsStyleFont& aFont,
|
|
nsStyleText& aTextStyle,
|
|
PRInt32 aStartingOffset,
|
|
nsLineLayout* aLineState)
|
|
{
|
|
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 != aTextStyle.mWhiteSpace) {
|
|
wrapping = PR_FALSE;
|
|
}
|
|
|
|
// Set whitespace skip flag
|
|
PRBool skipWhitespace = PR_FALSE;
|
|
if (nsnull != aLineState) {
|
|
if (aLineState->mSkipLeadingWhiteSpace) {
|
|
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 maxWordWidth = 0;
|
|
const PRUnichar* lastWordEnd = cpStart;
|
|
const PRUnichar* lastWordStart = cpStart;
|
|
PRBool hasMultibyte = PR_FALSE;
|
|
PRBool endsInWhitespace = PR_FALSE;
|
|
while (cp < end) {
|
|
PRUnichar ch = *cp++;
|
|
PRBool isWhitespace;
|
|
nscoord width;
|
|
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;
|
|
lastWordStart = wordStart;
|
|
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;
|
|
endsInWhitespace = isWhitespace;
|
|
}
|
|
if (hasMultibyte) {
|
|
mFlags |= TEXT_HAS_MULTIBYTE;
|
|
}
|
|
if (endsInWhitespace) {
|
|
mFlags |= TEXT_ENDS_IN_WHITESPACE;
|
|
}
|
|
|
|
if (nsnull != aLineState) {
|
|
if (0 == x) {
|
|
// Since we collapsed into nothingness (all our whitespace
|
|
// is ignored) leave the aState->mSkipLeadingWhiteSpace
|
|
// flag alone since it doesn't want leading whitespace
|
|
}
|
|
else {
|
|
aLineState->mSkipLeadingWhiteSpace = 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 NS_FRAME_COMPLETE;
|
|
}
|
|
}
|
|
|
|
// 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) ? NS_FRAME_COMPLETE : NS_FRAME_NOT_COMPLETE;
|
|
}
|
|
|
|
nsReflowStatus
|
|
TextFrame::ReflowPre(nsIPresContext* aCX,
|
|
nsReflowMetrics& aDesiredSize,
|
|
const nsSize& aMaxSize,
|
|
nsSize* aMaxElementSize,
|
|
nsStyleFont& aFont,
|
|
PRInt32 aStartingOffset,
|
|
nsLineLayout* aLineState)
|
|
{
|
|
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 != aLineState) {
|
|
col = aLineState->mColumn;
|
|
}
|
|
mColumn = (PRUint16) col;
|
|
nscoord spaceWidth = widths[' '];
|
|
while (cp < end) {
|
|
PRUnichar ch = *cp++;
|
|
if (ch == '\n') {
|
|
if (nsnull != aLineState) {
|
|
aLineState->mReflowResult = NS_LINE_LAYOUT_REFLOW_RESULT_BREAK_AFTER;
|
|
}
|
|
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 != aLineState) {
|
|
aLineState->mColumn = 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) ? NS_FRAME_COMPLETE : NS_FRAME_NOT_COMPLETE;
|
|
}
|
|
|
|
#define NUM_WORDS 20
|
|
|
|
NS_METHOD TextFrame::JustifyReflow(nsIPresContext* aCX, nscoord aAvailableSpace)
|
|
{
|
|
if (mFlags & TEXT_IS_PRE) {
|
|
// no way
|
|
return NS_OK;
|
|
}
|
|
|
|
nsStyleFont* font =
|
|
(nsStyleFont*)mStyleContext->GetData(eStyleStruct_Font);
|
|
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* 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);
|
|
return NS_OK;
|
|
}
|
|
#undef NUM_WORDS
|
|
|
|
//--------------------------------------------------------------------------
|
|
// CalcActualPosition
|
|
// This finds the actual interger offset into the text and the width of the
|
|
// offset in Twips
|
|
//--------------------------------------------------------------------------
|
|
void TextFrame::CalcActualPosition(PRUint32 &aMsgType,
|
|
const PRUnichar* aCPStart,
|
|
const PRUnichar* aCP,
|
|
PRInt32 & aOffset,
|
|
PRInt32 & aWidth,
|
|
nsIFontMetrics * aFM) {
|
|
|
|
//printf("**STOPPED! Offset is [%d][%d]\n", width, PRUint32(cp - cpStart));
|
|
|
|
if (aMsgType == NS_MOUSE_LEFT_BUTTON_UP || aMsgType == NS_MOUSE_MOVE) {
|
|
aOffset = PRUint32(aCP - aCPStart);
|
|
aWidth = aFM->GetWidth(aCPStart, aOffset-1);
|
|
} else if (aMsgType == NS_MOUSE_LEFT_BUTTON_DOWN) {
|
|
aOffset = PRUint32(aCP - aCPStart)-1;
|
|
if (aOffset == 0) {
|
|
aWidth = 0;
|
|
} else {
|
|
aWidth = aFM->GetWidth(aCPStart, aOffset);
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
//-- GetPosition
|
|
//--------------------------------------------------------------------------
|
|
PRInt32 TextFrame::GetPosition(nsIPresContext& aCX,
|
|
nsGUIEvent * aEvent,
|
|
nsIFrame * aNewFrame) {
|
|
|
|
PRInt32 offset;
|
|
PRInt32 width;
|
|
CalcCursorPosition(aCX, aEvent, (TextFrame *)aNewFrame, offset, width);
|
|
offset += ((TextFrame *)aNewFrame)->GetContentOffset();
|
|
|
|
return offset;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
//-- CalcCursorPosition
|
|
//--------------------------------------------------------------------------
|
|
void TextFrame::CalcCursorPosition(nsIPresContext& aCX,
|
|
nsGUIEvent* aEvent,
|
|
TextFrame * aNewFrame,
|
|
PRInt32 & aOffset,
|
|
PRInt32 & aWidth) {
|
|
|
|
|
|
if (gCalcDebug) printf("Cursor Point [%d,%d]\n", aEvent->point.x, aEvent->point.y);
|
|
|
|
// Get starting offset into the content
|
|
PRInt32 startingOffset = 0;
|
|
if (nsnull != aNewFrame->GetPrevInFlow()) {
|
|
TextFrame* prev = (TextFrame*) aNewFrame->GetPrevInFlow();
|
|
startingOffset = prev->GetContentOffset() + prev->GetContentLength();
|
|
}
|
|
|
|
// Get cached state for containing block frame
|
|
nsLineLayout* lineLayoutState = nsnull;
|
|
nsBlockReflowState* state = nsBlockFrame::FindBlockReflowState(&aCX, this);
|
|
if (nsnull != state) {
|
|
lineLayoutState = state->mCurrentLine;
|
|
if (nsnull != lineLayoutState) {
|
|
lineLayoutState->mReflowResult = NS_LINE_LAYOUT_REFLOW_RESULT_AWARE;
|
|
}
|
|
}
|
|
|
|
|
|
nsIContent * content;
|
|
GetContent(content);
|
|
|
|
Text* txt = (Text*) content;
|
|
|
|
const PRUnichar* cp = txt->mText + startingOffset;
|
|
const PRUnichar* end = cp + txt->mLength - startingOffset;
|
|
const PRUnichar* cpStart = cp;
|
|
//mContentOffset = startingOffset;
|
|
|
|
nsIStyleContext * styleContext;
|
|
|
|
aNewFrame->GetStyleContext(&aCX, styleContext);
|
|
nsStyleFont *font = (nsStyleFont*)styleContext->GetData(eStyleStruct_Font);
|
|
nsStyleText *styleText = (nsStyleText*)styleContext->GetData(eStyleStruct_Text);
|
|
NS_RELEASE(styleContext);
|
|
|
|
nsIFontMetrics* fm = aCX.GetMetricsFor(font->mFont);
|
|
PRInt32 spaceWidth = fm->GetWidth(' ');
|
|
PRBool atLeftMargin = PR_TRUE;
|
|
PRBool wrapping = PR_TRUE;
|
|
if (NS_STYLE_WHITESPACE_NORMAL != styleText->mWhiteSpace) {
|
|
wrapping = PR_FALSE;
|
|
}
|
|
|
|
// Set whitespace skip flag
|
|
PRBool skipWhitespace = PR_FALSE;
|
|
if (nsnull != lineLayoutState) {
|
|
if (lineLayoutState->mSkipLeadingWhiteSpace) {
|
|
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 PR_TRUE for the unicode   character
|
|
// XXX what about &zwj and it's cousins?
|
|
nscoord x = 0;
|
|
nscoord maxWidth = 100000;//aMaxSize.width;
|
|
nscoord width = 0;
|
|
nscoord maxWordWidth = 0;
|
|
|
|
const PRUnichar* lastWordEnd = cpStart;
|
|
|
|
PRBool hasMultibyte = PR_FALSE;
|
|
PRBool endsInWhitespace = PR_FALSE;
|
|
|
|
int w = 0;
|
|
while (cp < end) {
|
|
width = fm->GetWidth(cpStart, PRUint32(cp - cpStart));
|
|
|
|
if (gCalcDebug) printf("Cursor %d ******>> Width %d Pos: %d end: %d \n", aEvent->point.x,
|
|
width,
|
|
PRUint32(cp - cpStart),
|
|
end-cpStart);
|
|
if (width >= aEvent->point.x) {
|
|
if (gCalcDebug) printf("*********************************\n*STOPPED**! Offset is [%d][%d]\n", width, PRUint32(cp - cpStart));
|
|
if (gCalcDebug) printf("**********************************\n");
|
|
CalcActualPosition(aEvent->message, cpStart, cp, aOffset, aWidth, fm);
|
|
|
|
//--------- Major Debugging ----
|
|
/*if (aEvent->message == NS_MOUSE_MOVE ||
|
|
aEvent->message == NS_MOUSE_LEFT_BUTTON_UP) {
|
|
int len = txt->mLength - startingOffset;
|
|
|
|
//printf("Len %d\n", len);
|
|
PRUnichar buf0[256];
|
|
PRUnichar buf1[256];
|
|
PRUnichar buf2[256];
|
|
const PRUnichar* start = txt->mText + startingOffset;
|
|
|
|
printf("mStart %d mEnd %d len %d\n", mStartSelect, mEndSelect, len);
|
|
|
|
for (PRUint32 i=0;i<mStartSelect;i++) {
|
|
printf("%c/", *start);
|
|
buf0[i] = *start;
|
|
start++;
|
|
}
|
|
if (mEndSelect-mStartSelect > 4) {
|
|
int x = 0;
|
|
x++;
|
|
}
|
|
printf("\n%d>", mEndSelect-mStartSelect);
|
|
buf0[i] = 0;
|
|
for (i=0;i<mEndSelect-mStartSelect;i++) {
|
|
printf("%c/", *start);
|
|
buf1[i] = *start;
|
|
start++;
|
|
}
|
|
printf("\n%d>", len-mEndSelect);
|
|
buf1[i] = 0;
|
|
for (i=0;i<len-mEndSelect;i++) {
|
|
printf("%c/", *start);
|
|
buf2[i] = *start;
|
|
start++;
|
|
}
|
|
printf("\n");
|
|
buf2[i] = 0;
|
|
//printf("[%s][%s][%s]\n", buf0, buf1, buf2);
|
|
|
|
int l0 = fm->GetWidth(cpStart, PRUint32(mStartSelect));
|
|
int l1 = fm->GetWidth(cpStart+mStartSelect, PRUint32(mEndSelect-mStartSelect));
|
|
int l2 = fm->GetWidth(cpStart+mEndSelect, PRUint32(len-mEndSelect));
|
|
int l3 = fm->GetWidth(cpStart, PRUint32(mEndSelect));
|
|
printf("%d %d %d/\n", mStartSelect, mEndSelect-mStartSelect, len-mEndSelect);
|
|
printf("%d %d %d %d %d\n", l0, l1, l2, l3, (l0+l1+l2));
|
|
|
|
printf("mStart %d,%d mEnd %d,%d whole width %d len %d\n", mStartSelect, mStartWidth, mEndSelect, mEndWidth, fm->GetWidth(cpStart, PRUint32(txt->mLength-startingOffset)), PRUint32(txt->mLength-startingOffset));
|
|
if (mEndSelect-mStartSelect > 0) {
|
|
mSelectWidth = fm->GetWidth(cpStart+mStartSelect, PRUint32(mEndSelect-mStartSelect));
|
|
} else {
|
|
mSelectWidth = 0;
|
|
}
|
|
}*/
|
|
//------
|
|
|
|
//----
|
|
NS_RELEASE(content);
|
|
return;
|
|
}
|
|
|
|
PRUnichar ch = *cp++;
|
|
|
|
PRBool isWhitespace;
|
|
if (XP_IS_SPACE(ch)) {
|
|
if (gCalcDebug) printf("Before 11111111111111111111111111111111\n");
|
|
// 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;
|
|
if (gCalcDebug) printf("After 11111111111111111111111111111111\n");
|
|
} else {
|
|
if (gCalcDebug) printf("Before 2222222222222222222222222222222222222\n");
|
|
// 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) {
|
|
width = fm->GetWidth(cpStart, PRUint32(cp - cpStart));
|
|
|
|
if (gCalcDebug) printf("-Cursor %d ******>> Width %d Pos: %d end: %d \n", aEvent->point.x,
|
|
width,
|
|
PRUint32(cp - cpStart),
|
|
end-cpStart);
|
|
if (width >= aEvent->point.x) {
|
|
if (gCalcDebug) printf("**********************************\nSTOPPED! Offset is [%d][%d]\n", width, PRUint32(cp - cpStart));
|
|
if (gCalcDebug) printf("**********************************\n");
|
|
CalcActualPosition(aEvent->message, cpStart, cp, aOffset, aWidth, fm);
|
|
NS_RELEASE(content);
|
|
return;
|
|
}
|
|
|
|
ch = *cp;
|
|
if (ch >= 256) {
|
|
hasMultibyte = PR_TRUE;
|
|
}
|
|
if (!XP_IS_SPACE(ch)) {
|
|
cp++;
|
|
continue;
|
|
}
|
|
if (gCalcDebug) printf("At bottom...breaking..........................\n");
|
|
break;
|
|
}
|
|
if (gCalcDebug) printf("After 2222222222222222222222222222222222222\n");
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
if (gCalcDebug) printf(">>>>> Breaking\n");
|
|
break;
|
|
} // if
|
|
|
|
// 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 (gCalcDebug) printf("Bottom--------------------------------\n");
|
|
} // while
|
|
|
|
NS_RELEASE(content);
|
|
|
|
}
|
|
|
|
|
|
NS_METHOD TextFrame::List(FILE* out, PRInt32 aIndent) const
|
|
{
|
|
PRInt32 i;
|
|
for (i = aIndent; --i >= 0; ) fputs(" ", out);
|
|
PRInt32 contentIndex;
|
|
GetContentIndex(contentIndex);
|
|
fprintf(out, "Text(%d)@%p[%d,%d] ",
|
|
contentIndex, this,
|
|
mContentOffset, mContentOffset+mContentLength-1);
|
|
out << mRect;
|
|
if (0 != mState) {
|
|
fprintf(out, " [state=%08x]", mState);
|
|
}
|
|
fputs("<\n", out);
|
|
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
|
|
return NS_OK;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
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
|
|
|
|
nsresult
|
|
Text::CreateFrame(nsIPresContext* aPresContext,
|
|
nsIFrame* aParentFrame,
|
|
nsIStyleContext* aStyleContext,
|
|
nsIFrame*& aResult)
|
|
{
|
|
nsIFrame* frame = new TextFrame(this, aParentFrame);
|
|
if (nsnull == frame) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
frame->SetStyleContext(aPresContext, aStyleContext);
|
|
aResult = frame;
|
|
return NS_OK;
|
|
}
|
|
|
|
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.SetString(mText, mLength);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult Text::SetData(nsString &aString)
|
|
{
|
|
if (mText) delete[] mText;
|
|
|
|
mLength = aString.Length();
|
|
mText = aString.ToNewUnicode();
|
|
|
|
// Notify the document that the text changed
|
|
mDocument->ContentChanged(this, nsnull);
|
|
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, aData.GetUnicode(), length * sizeof(PRUnichar));
|
|
|
|
if (mText) delete[] mText;
|
|
mLength += length;
|
|
mText = text;
|
|
|
|
// Notify the document that the text changed
|
|
mDocument->ContentChanged(this, nsnull);
|
|
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;
|
|
|
|
// Notify the document that the text changed
|
|
mDocument->ContentChanged(this, nsnull);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult Text::Delete(int offset, int count)
|
|
{
|
|
// Make sure the offset is something sensible
|
|
if (offset < mLength) {
|
|
PRInt32 subLen = mLength - offset;
|
|
|
|
// Check if they're deleting the rest of the text
|
|
if (count >= subLen) {
|
|
mLength = offset;
|
|
} else {
|
|
PRUnichar* subText = mText + offset;
|
|
|
|
nsCRT::memmove(subText, subText + count, (subLen - count) * sizeof(PRUnichar));
|
|
mLength -= count;
|
|
}
|
|
|
|
// Notify the document that the text changed
|
|
mDocument->ContentChanged(this, nsnull);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult Text::Replace(int offset, int count, nsString &aData)
|
|
{
|
|
// Make sure the offset is something sensible
|
|
if (offset < mLength) {
|
|
// We can only replace as many characters as there are in aData
|
|
if (count > aData.Length()) {
|
|
count = aData.Length();
|
|
}
|
|
|
|
PRInt32 numChars = PR_MIN(count, mLength - offset);
|
|
if (numChars > 0) {
|
|
nsCRT::memcpy(mText + offset, aData.GetUnicode(), numChars * sizeof(PRUnichar));
|
|
}
|
|
|
|
// Notify the document that the text changed
|
|
mDocument->ContentChanged(this, nsnull);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
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);
|
|
}
|