gecko-dev/layout/xul/base/src/nsTextBoxFrame.cpp

858 lines
27 KiB
C++
Raw Normal View History

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2000-02-16 23:00:52 +00:00
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Peter Annema <disttsc@bart.nl>
* Dean Tessman <dean_tessman@hotmail.com>
2000-02-16 23:00:52 +00:00
*/
//
// Eric Vaughan
// Netscape Communications
//
// See documentation in associated header file
//
2000-03-31 07:02:06 +00:00
#include "nsTextBoxFrame.h"
2000-02-16 23:00:52 +00:00
#include "nsCOMPtr.h"
#include "nsIDeviceContext.h"
#include "nsIFontMetrics.h"
#include "nsHTMLAtoms.h"
#include "nsXULAtoms.h"
#include "nsIPresContext.h"
#include "nsIRenderingContext.h"
#include "nsIStyleContext.h"
#include "nsIContent.h"
#include "nsINameSpaceManager.h"
2000-03-31 07:02:06 +00:00
#include "nsBoxLayoutState.h"
#include "nsMenuBarListener.h"
#include "nsIPref.h"
#include "nsXPIDLString.h"
#include "nsIServiceManager.h"
2000-02-16 23:00:52 +00:00
#ifdef IBMBIDI
#include "nsIUBidiUtils.h"
#include "nsBidiPresUtils.h"
#endif // IBMBIDI
#define ELLIPSIS "..."
2000-02-16 23:00:52 +00:00
#define CROP_LEFT "left"
#define CROP_RIGHT "right"
#define CROP_CENTER "center"
#define NS_STATE_NEED_LAYOUT 0x01000000
2000-02-16 23:00:52 +00:00
class nsAccessKeyInfo
{
public:
PRInt32 mAccesskeyIndex;
nscoord mBeforeWidth, mAccessWidth, mAccessUnderlineSize, mAccessOffset;
2000-02-16 23:00:52 +00:00
};
PRBool nsTextBoxFrame::gAlwaysAppendAccessKey = PR_FALSE;
PRBool nsTextBoxFrame::gAccessKeyPrefInitialized = PR_FALSE;
2000-02-16 23:00:52 +00:00
//
// NS_NewToolbarFrame
//
// Creates a new Toolbar frame and returns it in |aNewFrame|
//
nsresult
2000-03-31 07:02:06 +00:00
NS_NewTextBoxFrame ( nsIPresShell* aPresShell, nsIFrame** aNewFrame )
2000-02-16 23:00:52 +00:00
{
NS_PRECONDITION(aNewFrame, "null OUT ptr");
if (nsnull == aNewFrame) {
return NS_ERROR_NULL_POINTER;
}
nsTextBoxFrame* it = new (aPresShell) nsTextBoxFrame (aPresShell);
if (nsnull == it)
return NS_ERROR_OUT_OF_MEMORY;
// it->SetFlags(aFlags);
*aNewFrame = it;
return NS_OK;
2000-02-16 23:00:52 +00:00
} // NS_NewTextFrame
NS_IMETHODIMP
2000-03-31 07:02:06 +00:00
nsTextBoxFrame::AttributeChanged(nsIPresContext* aPresContext,
nsIContent* aChild,
PRInt32 aNameSpaceID,
nsIAtom* aAttribute,
PRInt32 aHint)
2000-02-16 23:00:52 +00:00
{
mState |= NS_STATE_NEED_LAYOUT;
PRBool aResize;
PRBool aRedraw;
UpdateAttributes(aPresContext, aAttribute, aResize, aRedraw);
if (aResize) {
nsBoxLayoutState state(aPresContext);
MarkDirty(state);
} else if (aRedraw) {
nsBoxLayoutState state(aPresContext);
Redraw(state);
}
2000-02-16 23:00:52 +00:00
return NS_OK;
2000-02-16 23:00:52 +00:00
}
2000-04-16 11:19:26 +00:00
nsTextBoxFrame::nsTextBoxFrame(nsIPresShell* aShell):nsLeafBoxFrame(aShell), mCropType(CropRight),mAccessKeyInfo(nsnull)
2000-02-16 23:00:52 +00:00
{
mState |= NS_STATE_NEED_LAYOUT;
NeedsRecalc();
2000-02-16 23:00:52 +00:00
}
2000-03-31 07:02:06 +00:00
nsTextBoxFrame::~nsTextBoxFrame()
2000-02-16 23:00:52 +00:00
{
if (mAccessKeyInfo)
delete mAccessKeyInfo;
}
NS_IMETHODIMP
2000-03-31 07:02:06 +00:00
nsTextBoxFrame::Init(nsIPresContext* aPresContext,
nsIContent* aContent,
nsIFrame* aParent,
nsIStyleContext* aContext,
nsIFrame* aPrevInFlow)
2000-02-16 23:00:52 +00:00
{
nsresult rv = nsLeafBoxFrame::Init(aPresContext, aContent, aParent, aContext, aPrevInFlow);
2000-02-16 23:00:52 +00:00
mState |= NS_STATE_NEED_LAYOUT;
PRBool aResize;
PRBool aRedraw;
if (!gAccessKeyPrefInitialized) {
nsCOMPtr<nsIPref> prefs = do_GetService(NS_PREF_CONTRACTID);
gAccessKeyPrefInitialized = PR_TRUE;
if (prefs) {
nsXPIDLString prefValue;
nsresult res = prefs->GetLocalizedUnicharPref("intl.menuitems.alwaysappendacceskeys", getter_Copies(prefValue));
if (NS_SUCCEEDED(res)) {
gAlwaysAppendAccessKey = nsDependentString(prefValue).Equals(NS_LITERAL_STRING("true"));
}
}
}
UpdateAttributes(aPresContext, nsnull, aResize, aRedraw); /* update all */
2000-02-16 23:00:52 +00:00
return rv;
2000-02-16 23:00:52 +00:00
}
void
nsTextBoxFrame::UpdateAttributes(nsIPresContext* aPresContext,
nsIAtom* aAttribute,
PRBool& aResize,
PRBool& aRedraw)
2000-02-16 23:00:52 +00:00
{
PRBool doUpdateTitle = PR_FALSE;
2000-02-16 23:00:52 +00:00
aResize = PR_FALSE;
aRedraw = PR_FALSE;
if (aAttribute == nsnull || aAttribute == nsXULAtoms::crop) {
nsAutoString value;
mContent->GetAttribute(kNameSpaceID_None, nsXULAtoms::crop, value);
CroppingStyle cropType;
if (value.EqualsIgnoreCase(CROP_LEFT))
cropType = CropLeft;
else if (value.EqualsIgnoreCase(CROP_CENTER))
cropType = CropCenter;
else if (value.EqualsIgnoreCase(CROP_RIGHT))
cropType = CropRight;
else
2000-02-16 23:00:52 +00:00
cropType = CropNone;
if (cropType != mCropType) {
aResize = PR_TRUE;
mCropType = cropType;
}
}
if (aAttribute == nsnull || aAttribute == nsHTMLAtoms::value) {
nsAutoString value;
mContent->GetAttribute(kNameSpaceID_None, nsHTMLAtoms::value, value);
if (!value.Equals(mTitle)) {
mTitle = value;
doUpdateTitle = PR_TRUE;
2000-02-16 23:00:52 +00:00
}
}
if (aAttribute == nsnull || aAttribute == nsXULAtoms::accesskey) {
nsAutoString accesskey;
mContent->GetAttribute(kNameSpaceID_None, nsXULAtoms::accesskey, accesskey);
if (!accesskey.Equals(mAccessKey)) {
if (!doUpdateTitle) {
// Need to get clean mTitle and didn't already
nsAutoString value;
mContent->GetAttribute(kNameSpaceID_None, nsHTMLAtoms::value, value);
mTitle = value;
doUpdateTitle = PR_TRUE;
}
mAccessKey = accesskey;
}
2000-02-16 23:00:52 +00:00
}
if (doUpdateTitle) {
UpdateAccessTitle();
aResize = PR_TRUE;
}
2000-02-16 23:00:52 +00:00
}
NS_IMETHODIMP
nsTextBoxFrame::Paint(nsIPresContext* aPresContext,
nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect,
nsFramePaintLayer aWhichLayer)
{
const nsStyleVisibility* vis =
(const nsStyleVisibility*)mStyleContext->GetStyleData(eStyleStruct_Visibility);
if (!vis->IsVisible())
return NS_OK;
2000-02-16 23:00:52 +00:00
if (NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer) {
2000-02-16 23:00:52 +00:00
// remove the border and padding
nsStyleBorderPadding bPad;
mStyleContext->GetBorderPaddingFor(bPad);
nsMargin border(0,0,0,0);
bPad.GetBorderPadding(border);
2000-02-16 23:00:52 +00:00
nsRect textRect(0,0,mRect.width, mRect.height);
textRect.Deflate(border);
2000-02-16 23:00:52 +00:00
PaintTitle(aPresContext, aRenderingContext, aDirtyRect, textRect);
2000-02-16 23:00:52 +00:00
}
2000-02-16 23:00:52 +00:00
return nsLeafFrame::Paint(aPresContext, aRenderingContext, aDirtyRect, aWhichLayer);
}
NS_IMETHODIMP
nsTextBoxFrame::PaintTitle(nsIPresContext* aPresContext,
nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect,
const nsRect& aRect)
2000-02-16 23:00:52 +00:00
{
if (mTitle.Length() == 0)
return NS_OK;
2000-02-16 23:00:52 +00:00
// determine (cropped) title and underline position
LayoutTitle(aPresContext, aRenderingContext, aRect);
// make the rect as small as our (cropped) text.
nsRect textRect(aRect);
textRect.width = mTitleWidth;
2001-08-14 00:07:36 +00:00
// Align our text within the overall rect by checking our text-align property.
const nsStyleText* textStyle = (const nsStyleText*)mStyleContext->GetStyleData(eStyleStruct_Text);
if (textStyle->mTextAlign == NS_STYLE_TEXT_ALIGN_CENTER)
textRect.x += (aRect.width - textRect.width)/2;
else if (textStyle->mTextAlign == NS_STYLE_TEXT_ALIGN_RIGHT)
textRect.x += (aRect.width - textRect.width);
// don't draw if the title is not dirty
if (PR_FALSE == aDirtyRect.Intersects(textRect))
return NS_OK;
// paint the title
nscolor overColor;
nscolor underColor;
nscolor strikeColor;
nsIStyleContext* context = mStyleContext;
PRUint8 decorations = NS_STYLE_TEXT_DECORATION_NONE; // Begin with no decorations
PRUint8 decorMask = NS_STYLE_TEXT_DECORATION_UNDERLINE | NS_STYLE_TEXT_DECORATION_OVERLINE |
NS_STYLE_TEXT_DECORATION_LINE_THROUGH; // A mask of all possible decorations.
PRBool hasDecorations = context->HasTextDecorations();
NS_ADDREF(context);
do { // find decoration colors
const nsStyleTextReset* styleText =
(const nsStyleTextReset*)context->GetStyleData(eStyleStruct_TextReset);
if (decorMask & styleText->mTextDecoration) { // a decoration defined here
const nsStyleColor* styleColor =
(const nsStyleColor*)context->GetStyleData(eStyleStruct_Color);
if (NS_STYLE_TEXT_DECORATION_UNDERLINE & decorMask & styleText->mTextDecoration) {
underColor = styleColor->mColor;
decorMask &= ~NS_STYLE_TEXT_DECORATION_UNDERLINE;
decorations |= NS_STYLE_TEXT_DECORATION_UNDERLINE;
}
if (NS_STYLE_TEXT_DECORATION_OVERLINE & decorMask & styleText->mTextDecoration) {
overColor = styleColor->mColor;
decorMask &= ~NS_STYLE_TEXT_DECORATION_OVERLINE;
decorations |= NS_STYLE_TEXT_DECORATION_OVERLINE;
}
if (NS_STYLE_TEXT_DECORATION_LINE_THROUGH & decorMask & styleText->mTextDecoration) {
strikeColor = styleColor->mColor;
decorMask &= ~NS_STYLE_TEXT_DECORATION_LINE_THROUGH;
decorations |= NS_STYLE_TEXT_DECORATION_LINE_THROUGH;
}
}
if (0 != decorMask) {
nsIStyleContext* lastContext = context;
context = context->GetParent();
hasDecorations = context->HasTextDecorations();
NS_RELEASE(lastContext);
}
} while ((nsnull != context) && hasDecorations && (0 != decorMask));
NS_IF_RELEASE(context);
nsStyleFont* fontStyle = (nsStyleFont*)mStyleContext->GetStyleData(eStyleStruct_Font);
nscoord offset;
nscoord size;
nscoord baseline;
if (decorations & (NS_FONT_DECORATION_OVERLINE | NS_FONT_DECORATION_UNDERLINE)) {
nsCOMPtr<nsIDeviceContext> deviceContext;
aPresContext->GetDeviceContext(getter_AddRefs(deviceContext));
nsCOMPtr<nsIFontMetrics> fontMet;
deviceContext->GetMetricsFor(fontStyle->mFont, *getter_AddRefs(fontMet));
fontMet->GetMaxAscent(baseline);
fontMet->GetUnderline(offset, size);
if (decorations & NS_FONT_DECORATION_OVERLINE) {
aRenderingContext.SetColor(overColor);
aRenderingContext.FillRect(textRect.x, textRect.y, mRect.width, size);
}
if (decorations & NS_FONT_DECORATION_UNDERLINE) {
aRenderingContext.SetColor(underColor);
aRenderingContext.FillRect(textRect.x, textRect.y + baseline - offset, mRect.width, size);
}
}
if (decorations & NS_FONT_DECORATION_LINE_THROUGH) {
nsCOMPtr<nsIDeviceContext> deviceContext;
aPresContext->GetDeviceContext(getter_AddRefs(deviceContext));
nsCOMPtr<nsIFontMetrics> fontMet;
deviceContext->GetMetricsFor(fontStyle->mFont, *getter_AddRefs(fontMet));
fontMet->GetMaxAscent(baseline);
fontMet->GetStrikeout(offset, size);
aRenderingContext.SetColor(strikeColor);
aRenderingContext.FillRect(textRect.x, textRect.y + baseline - offset, mRect.width, size);
}
aRenderingContext.SetFont(fontStyle->mFont);
CalculateUnderline(aRenderingContext);
const nsStyleColor* colorStyle = (const nsStyleColor*)mStyleContext->GetStyleData(eStyleStruct_Color);
aRenderingContext.SetColor(colorStyle->mColor);
#ifdef IBMBIDI
nsresult rv = NS_ERROR_FAILURE;
if (mState & NS_FRAME_IS_BIDI) {
nsBidiPresUtils* bidiUtils;
aPresContext->GetBidiUtils(&bidiUtils);
if (bidiUtils) {
nsCOMPtr<nsIBidi> bidiEngine;
bidiUtils->GetBidiEngine(getter_AddRefs(bidiEngine));
if (bidiEngine) {
const nsStyleVisibility* vis;
GetStyleData(eStyleStruct_Visibility, (const nsStyleStruct*&) vis);
PRUnichar* buffer = (PRUnichar*) mCroppedTitle.get();
PRInt32 runCount;
nsBidiDirection direction =
(NS_STYLE_DIRECTION_RTL == vis->mDirection)
? NSBIDI_RTL : NSBIDI_LTR;
rv = bidiEngine->SetPara(buffer, mCroppedTitle.Length(), direction, nsnull);
if (NS_SUCCEEDED(rv) ) {
rv = bidiEngine->CountRuns(&runCount);
if (NS_SUCCEEDED(rv) ) {
nscoord width;
PRBool isRTL = PR_FALSE;
PRInt32 i, start, limit, length;
nsCharType charType;
nsBidiLevel level;
PRBool isBidiSystem;
PRUint32 hints = 0;
aRenderingContext.GetHints(hints);
isBidiSystem = (hints & NS_RENDERING_HINT_BIDI_REORDERING);
for (i = 0; i < runCount; i++) {
rv = bidiEngine->GetVisualRun(i, &start, &length, &direction);
if (NS_FAILED(rv) ) {
break;
}
bidiEngine->GetCharTypeAt(start, &charType);
rv = bidiEngine->GetLogicalRun(start, &limit, &level);
if (NS_FAILED(rv) ) {
break;
}
if (eCharType_RightToLeftArabic == charType) {
isBidiSystem = (hints & NS_RENDERING_HINT_ARABIC_SHAPING);
}
if (isBidiSystem && (CHARTYPE_IS_RTL(charType) ^ isRTL) ) {
// set reading order into DC
isRTL = !isRTL;
aRenderingContext.SetRightToLeftText(isRTL);
}
bidiUtils->FormatUnicodeText(aPresContext, buffer + start, length,
charType, level & 1,
isBidiSystem);
aRenderingContext.GetWidth(buffer + start, length, width, nsnull);
aRenderingContext.DrawString(buffer + start, length, textRect.x,
textRect.y, width);
textRect.x += width;
} // for
// Restore original x (for aRenderingContext.FillRect below),
// as well as reading order
textRect.x = aRect.x;
if (isRTL) {
aRenderingContext.SetRightToLeftText(PR_FALSE);
}
}
}
} // bidiEngine
} // bidiUtils
} // frame is bidi
if (NS_FAILED(rv) )
#endif // IBMBIDI
aRenderingContext.DrawString(mCroppedTitle, textRect.x, textRect.y);
if (mAccessKeyInfo && mAccessKeyInfo->mAccesskeyIndex != kNotFound) {
aRenderingContext.FillRect(textRect.x + mAccessKeyInfo->mBeforeWidth,
textRect.y + mAccessKeyInfo->mAccessOffset,
mAccessKeyInfo->mAccessWidth,
mAccessKeyInfo->mAccessUnderlineSize);
2000-02-16 23:00:52 +00:00
}
return NS_OK;
2000-02-16 23:00:52 +00:00
}
void
nsTextBoxFrame::LayoutTitle(nsIPresContext* aPresContext,
nsIRenderingContext& aRenderingContext,
const nsRect& aRect)
2000-02-16 23:00:52 +00:00
{
// and do caculations if our size changed
if ((mState & NS_STATE_NEED_LAYOUT)) {
2000-02-16 23:00:52 +00:00
// determine (cropped) title which fits in aRect.width and its width
CalculateTitleForWidth(aPresContext, aRenderingContext, aRect.width);
2000-02-16 23:00:52 +00:00
// determine if and at which position to put the underline
UpdateAccessIndex();
2000-02-16 23:00:52 +00:00
// ok layout complete
mState &= ~NS_STATE_NEED_LAYOUT;
}
}
2000-02-16 23:00:52 +00:00
void
nsTextBoxFrame::CalculateUnderline(nsIRenderingContext& aRenderingContext)
{
if (mAccessKeyInfo && mAccessKeyInfo->mAccesskeyIndex != kNotFound) {
2000-02-16 23:00:52 +00:00
// get all the underline-positioning stuff
// XXX are attribute values always two byte?
const PRUnichar *titleString;
titleString = mCroppedTitle.get();
if (mAccessKeyInfo->mAccesskeyIndex > 0)
2000-02-16 23:00:52 +00:00
aRenderingContext.GetWidth(titleString, mAccessKeyInfo->mAccesskeyIndex,
mAccessKeyInfo->mBeforeWidth);
else
mAccessKeyInfo->mBeforeWidth = 0;
2000-02-16 23:00:52 +00:00
aRenderingContext.GetWidth(titleString[mAccessKeyInfo->mAccesskeyIndex],
mAccessKeyInfo->mAccessWidth);
nscoord offset, baseline;
nsIFontMetrics *metrics;
aRenderingContext.GetFontMetrics(metrics);
metrics->GetUnderline(offset, mAccessKeyInfo->mAccessUnderlineSize);
metrics->GetMaxAscent(baseline);
NS_RELEASE(metrics);
mAccessKeyInfo->mAccessOffset = baseline - offset;
}
2000-02-16 23:00:52 +00:00
}
void
nsTextBoxFrame::CalculateTitleForWidth(nsIPresContext* aPresContext,
nsIRenderingContext& aRenderingContext,
nscoord aWidth)
2000-02-16 23:00:52 +00:00
{
if (mTitle.Length() == 0)
return;
2000-02-16 23:00:52 +00:00
const nsStyleFont* fontStyle = (const nsStyleFont*)mStyleContext->GetStyleData(eStyleStruct_Font);
2000-02-16 23:00:52 +00:00
nsCOMPtr<nsIDeviceContext> deviceContext;
aPresContext->GetDeviceContext(getter_AddRefs(deviceContext));
2000-02-16 23:00:52 +00:00
nsCOMPtr<nsIFontMetrics> fontMet;
deviceContext->GetMetricsFor(fontStyle->mFont, *getter_AddRefs(fontMet));
aRenderingContext.SetFont(fontMet);
2000-02-16 23:00:52 +00:00
// see if the text will completely fit in the width given
aRenderingContext.GetWidth(mTitle, mTitleWidth);
2000-02-16 23:00:52 +00:00
if (mTitleWidth <= aWidth) {
mCroppedTitle = mTitle;
#ifdef IBMBIDI
PRInt32 length = mTitle.Length();
for (PRInt32 i = 0; i < length; i++) {
if (CHAR_IS_BIDI(mTitle.CharAt(i) ) ) {
mState |= NS_FRAME_IS_BIDI;
break;
}
}
#endif // IBMBIDI
return; // fits, done.
}
2000-02-16 23:00:52 +00:00
// start with an ellipsis
mCroppedTitle.AssignWithConversion(ELLIPSIS);
2000-02-16 23:00:52 +00:00
// see if the width is even smaller than the ellipsis
// if so, clear the text (XXX set as many '.' as we can?).
nscoord ellipsisWidth;
aRenderingContext.GetWidth(ELLIPSIS, ellipsisWidth);
2000-02-16 23:00:52 +00:00
if (ellipsisWidth > aWidth) {
mCroppedTitle.SetLength(0);
mTitleWidth = aWidth;
return;
}
2000-02-16 23:00:52 +00:00
// if the ellipsis fits perfectly, no use in trying to insert
if (ellipsisWidth == aWidth) {
mTitleWidth = aWidth;
return;
}
2000-02-16 23:00:52 +00:00
aWidth -= ellipsisWidth;
2000-02-16 23:00:52 +00:00
// ok crop things
2000-02-16 23:00:52 +00:00
switch (mCropType)
{
case CropNone:
case CropRight:
{
nscoord cwidth;
nscoord twidth = 0;
int length = mTitle.Length();
int i;
for (i = 0; i < length; ++i) {
PRUnichar ch = mTitle.CharAt(i);
aRenderingContext.GetWidth(ch,cwidth);
if (twidth + cwidth > aWidth)
break;
twidth += cwidth;
#ifdef IBMBIDI
if (CHAR_IS_BIDI(ch) ) {
mState |= NS_FRAME_IS_BIDI;
}
#endif // IBMBIDI
}
if (i == 0)
return;
// insert what character we can in.
nsAutoString title( mTitle );
title.Truncate(i);
mCroppedTitle.Insert(title, 0);
}
break;
case CropLeft:
{
nscoord cwidth;
nscoord twidth = 0;
int length = mTitle.Length();
int i;
for (i=length-1; i >= 0; --i) {
PRUnichar ch = mTitle.CharAt(i);
aRenderingContext.GetWidth(ch,cwidth);
if (twidth + cwidth > aWidth)
break;
twidth += cwidth;
#ifdef IBMBIDI
if (CHAR_IS_BIDI(ch) ) {
mState |= NS_FRAME_IS_BIDI;
}
#endif // IBMBIDI
}
if (i == length-1)
break;
nsAutoString copy;
mTitle.Right(copy, length-1-i);
mCroppedTitle += copy;
}
break;
case CropCenter:
{
nscoord stringWidth = 0;
aRenderingContext.GetWidth(mTitle, stringWidth);
if (stringWidth <= aWidth) {
// the entire string will fit in the maximum width
mCroppedTitle.Insert(mTitle, 0);
break;
}
// determine how much of the string will fit in the max width
nscoord charWidth = 0;
nscoord totalWidth = 0;
PRUnichar ch;
int leftPos, rightPos;
nsAutoString leftString, rightString;
rightPos = mTitle.Length() - 1;
for (leftPos = 0; leftPos <= rightPos;) {
// look at the next character on the left end
ch = mTitle.CharAt(leftPos);
aRenderingContext.GetWidth(ch, charWidth);
totalWidth += charWidth;
if (totalWidth > aWidth)
// greater than the allowable width
break;
leftString.Insert(ch, leftString.Length());
#ifdef IBMBIDI
if (CHAR_IS_BIDI(ch))
mState |= NS_FRAME_IS_BIDI;
#endif
// look at the next character on the right end
if (rightPos > leftPos) {
// haven't looked at this character yet
ch = mTitle.CharAt(rightPos);
aRenderingContext.GetWidth(ch, charWidth);
totalWidth += charWidth;
if (totalWidth > aWidth)
// greater than the allowable width
break;
rightString.Insert(ch, 0);
#ifdef IBMBIDI
if (CHAR_IS_BIDI(ch))
mState |= NS_FRAME_IS_BIDI;
#endif
}
// look at the next two characters
leftPos++;
rightPos--;
}
// form the new cropped string
nsAutoString ellipsisString;
ellipsisString.AssignWithConversion(ELLIPSIS);
mCroppedTitle = leftString + ellipsisString + rightString;
}
break;
2000-02-16 23:00:52 +00:00
}
aRenderingContext.GetWidth(mCroppedTitle, mTitleWidth);
}
// the following block is to append the accesskey to mTitle if there is an accesskey
// but the mTitle doesn't have the character
2000-02-16 23:00:52 +00:00
void
nsTextBoxFrame::UpdateAccessTitle()
2000-02-16 23:00:52 +00:00
{
PRInt32 menuAccessKey;
nsMenuBarListener::GetMenuAccessKey(&menuAccessKey);
if (menuAccessKey) {
if (!mAccessKey.IsEmpty()) {
if ((mTitle.Find(mAccessKey, PR_TRUE) == kNotFound) || gAlwaysAppendAccessKey) {
nsAutoString tmpstring; tmpstring.AssignWithConversion("(");
tmpstring += mAccessKey;
tmpstring.ToUpperCase();
tmpstring.AppendWithConversion(")");
PRInt32 offset = mTitle.RFind("...");
if (offset != kNotFound)
mTitle.Insert(tmpstring,NS_STATIC_CAST(PRUint32, offset));
else
mTitle += tmpstring;
}
2000-02-16 23:00:52 +00:00
}
}
}
void
nsTextBoxFrame::UpdateAccessIndex()
{
PRInt32 menuAccessKey;
nsMenuBarListener::GetMenuAccessKey(&menuAccessKey);
if (menuAccessKey) {
if (mAccessKey.IsEmpty()) {
if (mAccessKeyInfo) {
delete mAccessKeyInfo;
mAccessKeyInfo = nsnull;
}
} else {
if (!mAccessKeyInfo)
mAccessKeyInfo = new nsAccessKeyInfo();
if (!gAlwaysAppendAccessKey) {
// not appending access key - do case-sensitive search first
mAccessKeyInfo->mAccesskeyIndex = mCroppedTitle.Find(mAccessKey, PR_FALSE);
if (mAccessKeyInfo->mAccesskeyIndex == kNotFound) {
// didn't find it - perform a case-insensitive search
mAccessKeyInfo->mAccesskeyIndex = mCroppedTitle.Find(mAccessKey, PR_TRUE);
}
} else {
// use case-insensitive, reverse find for appended access keys
mAccessKeyInfo->mAccesskeyIndex = mCroppedTitle.RFind(mAccessKey, PR_TRUE);
}
}
}
}
2000-02-16 23:00:52 +00:00
NS_IMETHODIMP
nsTextBoxFrame::DoLayout(nsBoxLayoutState& aBoxLayoutState)
2000-02-16 23:00:52 +00:00
{
mState |= NS_STATE_NEED_LAYOUT;
2000-02-16 23:00:52 +00:00
return nsLeafBoxFrame::DoLayout(aBoxLayoutState);
2000-03-31 07:02:06 +00:00
}
NS_IMETHODIMP
nsTextBoxFrame::NeedsRecalc()
{
mNeedsRecalc = PR_TRUE;
return NS_OK;
}
void
nsTextBoxFrame::GetTextSize(nsIPresContext* aPresContext, nsIRenderingContext& aRenderingContext,
const nsString& aString, nsSize& aSize, nscoord& aAscent)
{
const nsStyleFont* fontStyle = (const nsStyleFont*)mStyleContext->GetStyleData(eStyleStruct_Font);
nsCOMPtr<nsIDeviceContext> deviceContext;
aPresContext->GetDeviceContext(getter_AddRefs(deviceContext));
nsCOMPtr<nsIFontMetrics> fontMet;
deviceContext->GetMetricsFor(fontStyle->mFont, *getter_AddRefs(fontMet));
fontMet->GetHeight(aSize.height);
aRenderingContext.SetFont(fontMet);
aRenderingContext.GetWidth(aString, aSize.width);
fontMet->GetMaxAscent(aAscent);
2000-03-31 07:02:06 +00:00
}
void
nsTextBoxFrame::CalcTextSize(nsBoxLayoutState& aBoxLayoutState)
{
if (mNeedsRecalc)
{
nsSize size;
nsIPresContext* presContext = aBoxLayoutState.GetPresContext();
const nsHTMLReflowState* rstate = aBoxLayoutState.GetReflowState();
if (!rstate)
return;
nsIRenderingContext* rendContext = rstate->rendContext;
if (rendContext) {
GetTextSize(presContext, *rendContext,
mTitle, size, mAscent);
mTextSize = size;
mNeedsRecalc = PR_FALSE;
}
2000-03-31 07:02:06 +00:00
}
2000-02-16 23:00:52 +00:00
}
/**
* Ok return our dimensions
*/
NS_IMETHODIMP
2000-03-31 07:02:06 +00:00
nsTextBoxFrame::GetPrefSize(nsBoxLayoutState& aBoxLayoutState, nsSize& aSize)
2000-02-16 23:00:52 +00:00
{
CalcTextSize(aBoxLayoutState);
aSize = mTextSize;
AddBorderAndPadding(aSize);
AddInset(aSize);
nsIBox::AddCSSPrefSize(aBoxLayoutState, this, aSize);
return NS_OK;
2000-03-31 07:02:06 +00:00
}
/**
* Ok return our dimensions
*/
NS_IMETHODIMP
nsTextBoxFrame::GetMinSize(nsBoxLayoutState& aBoxLayoutState, nsSize& aSize)
{
CalcTextSize(aBoxLayoutState);
aSize = mTextSize;
// if there is cropping our min width becomes our border and padding
if (mCropType != CropNone) {
aSize.width = 0;
}
AddBorderAndPadding(aSize);
AddInset(aSize);
nsIBox::AddCSSMinSize(aBoxLayoutState, this, aSize);
return NS_OK;
2000-03-31 07:02:06 +00:00
}
NS_IMETHODIMP
nsTextBoxFrame::GetAscent(nsBoxLayoutState& aBoxLayoutState, nscoord& aAscent)
{
CalcTextSize(aBoxLayoutState);
aAscent = mAscent;
2000-03-31 07:02:06 +00:00
nsMargin m(0,0,0,0);
GetBorderAndPadding(m);
aAscent += m.top;
GetInset(m);
aAscent += m.top;
2000-03-31 07:02:06 +00:00
return NS_OK;
2000-02-16 23:00:52 +00:00
}
NS_IMETHODIMP
2000-03-31 07:02:06 +00:00
nsTextBoxFrame::GetFrameName(nsString& aResult) const
2000-02-16 23:00:52 +00:00
{
aResult.AssignWithConversion("Text[value=");
aResult += mTitle;
aResult.AppendWithConversion("]");
return NS_OK;
2000-02-16 23:00:52 +00:00
}