Back to index

lightning-sunbird  0.9+nobinonly
nsTextBoxFrame.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Communicator client code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Peter Annema <disttsc@bart.nl>
00024  *   Dean Tessman <dean_tessman@hotmail.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 //
00041 // Eric Vaughan
00042 // Netscape Communications
00043 //
00044 // See documentation in associated header file
00045 //
00046 
00047 #include "nsReadableUtils.h"
00048 #include "nsTextBoxFrame.h"
00049 #include "nsCOMPtr.h"
00050 #include "nsIDeviceContext.h"
00051 #include "nsIFontMetrics.h"
00052 #include "nsHTMLAtoms.h"
00053 #include "nsXULAtoms.h"
00054 #include "nsPresContext.h"
00055 #include "nsIRenderingContext.h"
00056 #include "nsStyleContext.h"
00057 #include "nsIContent.h"
00058 #include "nsINameSpaceManager.h"
00059 #include "nsBoxLayoutState.h"
00060 #include "nsMenuBarListener.h"
00061 #include "nsXPIDLString.h"
00062 #include "nsIServiceManager.h"
00063 #include "nsIDocument.h"
00064 #include "nsIDOMDocument.h"
00065 #include "nsIDOMElement.h"
00066 #include "nsIDOMXULLabelElement.h"
00067 #include "nsIEventStateManager.h"
00068 #include "nsITheme.h"
00069 #include "nsUnicharUtils.h"
00070 #include "nsContentUtils.h"
00071 #include "nsIReflowCallback.h"
00072 
00073 #ifdef IBMBIDI
00074 #include "nsBidiUtils.h"
00075 #include "nsBidiPresUtils.h"
00076 #endif // IBMBIDI
00077 
00078 #define ELLIPSIS "..."
00079 
00080 #define CROP_LEFT   "left"
00081 #define CROP_RIGHT  "right"
00082 #define CROP_CENTER "center"
00083 #define CROP_START  "start"
00084 #define CROP_END    "end"
00085 
00086 #define NS_STATE_NEED_LAYOUT 0x01000000
00087 
00088 class nsAccessKeyInfo
00089 {
00090 public:
00091     PRInt32 mAccesskeyIndex;
00092     nscoord mBeforeWidth, mAccessWidth, mAccessUnderlineSize, mAccessOffset;
00093 };
00094 
00095 
00096 PRBool nsTextBoxFrame::gAlwaysAppendAccessKey          = PR_FALSE;
00097 PRBool nsTextBoxFrame::gAccessKeyPrefInitialized       = PR_FALSE;
00098 PRBool nsTextBoxFrame::gInsertSeparatorBeforeAccessKey = PR_FALSE;
00099 PRBool nsTextBoxFrame::gInsertSeparatorPrefInitialized = PR_FALSE;
00100 
00101 //
00102 // NS_NewToolbarFrame
00103 //
00104 // Creates a new Toolbar frame and returns it in |aNewFrame|
00105 //
00106 nsresult
00107 NS_NewTextBoxFrame ( nsIPresShell* aPresShell, nsIFrame** aNewFrame )
00108 {
00109     NS_PRECONDITION(aNewFrame, "null OUT ptr");
00110     if (nsnull == aNewFrame) {
00111         return NS_ERROR_NULL_POINTER;
00112     }
00113     nsTextBoxFrame* it = new (aPresShell) nsTextBoxFrame (aPresShell);
00114     if (nsnull == it)
00115         return NS_ERROR_OUT_OF_MEMORY;
00116 
00117     // it->SetFlags(aFlags);
00118     *aNewFrame = it;
00119     return NS_OK;
00120 
00121 } // NS_NewTextFrame
00122 
00123 
00124 NS_IMETHODIMP
00125 nsTextBoxFrame::AttributeChanged(nsIContent*     aChild,
00126                                  PRInt32         aNameSpaceID,
00127                                  nsIAtom*        aAttribute,
00128                                  PRInt32         aModType)
00129 {
00130     mState |= NS_STATE_NEED_LAYOUT;
00131     PRBool aResize;
00132     PRBool aRedraw;
00133     nsPresContext* presContext = GetPresContext();
00134 
00135     UpdateAttributes(presContext, aAttribute, aResize, aRedraw);
00136 
00137     if (aResize) {
00138         nsBoxLayoutState state(presContext);
00139         MarkDirty(state);
00140     } else if (aRedraw) {
00141         nsBoxLayoutState state(presContext);
00142         Redraw(state);
00143     }
00144 
00145     // If the accesskey changed, register for the new value
00146     // The old value has been unregistered in nsXULElement::SetAttr
00147     if (aAttribute == nsXULAtoms::accesskey || aAttribute == nsXULAtoms::control)
00148         RegUnregAccessKey(presContext, PR_TRUE);
00149 
00150     return NS_OK;
00151 }
00152 
00153 nsTextBoxFrame::nsTextBoxFrame(nsIPresShell* aShell)
00154   : nsLeafBoxFrame(aShell), mCropType(CropRight), mAccessKeyInfo(nsnull), 
00155     mNeedsReflowCallback(PR_FALSE)
00156 {
00157     mState |= NS_STATE_NEED_LAYOUT;
00158     NeedsRecalc();
00159 }
00160 
00161 nsTextBoxFrame::~nsTextBoxFrame()
00162 {
00163     delete mAccessKeyInfo;
00164 }
00165 
00166 
00167 NS_IMETHODIMP
00168 nsTextBoxFrame::Init(nsPresContext*  aPresContext,
00169                      nsIContent*      aContent,
00170                      nsIFrame*        aParent,
00171                      nsStyleContext*  aContext,
00172                      nsIFrame*        aPrevInFlow)
00173 {
00174     nsresult rv = nsTextBoxFrameSuper::Init(aPresContext, aContent, aParent, aContext, aPrevInFlow);
00175     if (NS_FAILED(rv))
00176         return rv;
00177 
00178     mState |= NS_STATE_NEED_LAYOUT;
00179     PRBool aResize;
00180     PRBool aRedraw;
00181     UpdateAttributes(aPresContext, nsnull, aResize, aRedraw); /* update all */
00182 
00183     // register access key
00184     RegUnregAccessKey(aPresContext, PR_TRUE);
00185 
00186     return NS_OK;
00187 }
00188 
00189 NS_IMETHODIMP
00190 nsTextBoxFrame::Destroy(nsPresContext* aPresContext)
00191 {
00192     // unregister access key
00193     RegUnregAccessKey(aPresContext, PR_FALSE);
00194 
00195     return nsTextBoxFrameSuper::Destroy(aPresContext);
00196 }
00197 
00198 PRBool
00199 nsTextBoxFrame::AlwaysAppendAccessKey()
00200 {
00201   if (!gAccessKeyPrefInitialized) 
00202   {
00203     gAccessKeyPrefInitialized = PR_TRUE;
00204 
00205     const char* prefName = "intl.menuitems.alwaysappendaccesskeys";
00206     nsAdoptingString val = nsContentUtils::GetLocalizedStringPref(prefName);
00207     gAlwaysAppendAccessKey = val.Equals(NS_LITERAL_STRING("true"));
00208   }
00209   return gAlwaysAppendAccessKey;
00210 }
00211 
00212 PRBool
00213 nsTextBoxFrame::InsertSeparatorBeforeAccessKey()
00214 {
00215   if (!gInsertSeparatorPrefInitialized)
00216   {
00217     gInsertSeparatorPrefInitialized = PR_TRUE;
00218 
00219     const char* prefName = "intl.menuitems.insertseparatorbeforeaccesskeys";
00220     nsAdoptingString val = nsContentUtils::GetLocalizedStringPref(prefName);
00221     gInsertSeparatorBeforeAccessKey = val.EqualsLiteral("true");
00222   }
00223   return gInsertSeparatorBeforeAccessKey;
00224 }
00225 
00226 class nsAsyncAccesskeyUpdate : public nsIReflowCallback
00227 {
00228 public:
00229     nsAsyncAccesskeyUpdate(nsIFrame* aFrame) : mWeakFrame(aFrame)
00230     {
00231     }
00232 
00233     NS_DECL_ISUPPORTS
00234 
00235     NS_IMETHOD ReflowFinished(nsIPresShell* aShell, PRBool* aFlushFlag)
00236     {
00237         nsTextBoxFrame* frame =
00238             NS_STATIC_CAST(nsTextBoxFrame*, mWeakFrame.GetFrame());
00239         if (frame && frame->UpdateAccesskey(mWeakFrame)) {
00240             *aFlushFlag = PR_TRUE;
00241         }
00242         return NS_OK;
00243     }
00244 
00245     nsWeakFrame mWeakFrame;
00246 };
00247 
00248 NS_IMPL_ISUPPORTS1(nsAsyncAccesskeyUpdate, nsIReflowCallback)
00249 
00250 PRBool
00251 nsTextBoxFrame::UpdateAccesskey(nsWeakFrame& aWeakThis)
00252 {
00253     nsAutoString accesskey;
00254     nsCOMPtr<nsIDOMXULLabelElement> labelElement = do_QueryInterface(mContent);
00255     if (labelElement) {
00256         // Accesskey may be stored on control.
00257         nsCxPusher cx;
00258         if (cx.Push(mContent)) {
00259           labelElement->GetAccessKey(accesskey);
00260           NS_ENSURE_TRUE(aWeakThis.IsAlive(), PR_FALSE);
00261         }
00262     }
00263     else {
00264         mContent->GetAttr(kNameSpaceID_None, nsXULAtoms::accesskey, accesskey);
00265     }
00266 
00267     if (!accesskey.Equals(mAccessKey)) {
00268         // Need to get clean mTitle.
00269         mContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::value, mTitle);
00270         mAccessKey = accesskey;
00271         UpdateAccessTitle();
00272         nsPresContext* presContext = GetPresContext();
00273         nsBoxLayoutState state(presContext);
00274         MarkDirty(state);
00275         return PR_TRUE;
00276     }
00277     return PR_FALSE;
00278 }
00279 
00280 void
00281 nsTextBoxFrame::UpdateAttributes(nsPresContext*  aPresContext,
00282                                  nsIAtom*         aAttribute,
00283                                  PRBool&          aResize,
00284                                  PRBool&          aRedraw)
00285 {
00286     PRBool doUpdateTitle = PR_FALSE;
00287     aResize = PR_FALSE;
00288     aRedraw = PR_FALSE;
00289 
00290     if (aAttribute == nsnull || aAttribute == nsXULAtoms::crop) {
00291         nsAutoString value;
00292         mContent->GetAttr(kNameSpaceID_None, nsXULAtoms::crop, value);
00293         CroppingStyle cropType;
00294 
00295         if (value.EqualsASCII(CROP_LEFT) ||
00296             value.EqualsASCII(CROP_START))
00297             cropType = CropLeft;
00298         else if (value.EqualsASCII(CROP_CENTER))
00299             cropType = CropCenter;
00300         else if (value.EqualsASCII(CROP_RIGHT) ||
00301                  value.EqualsASCII(CROP_END))
00302             cropType = CropRight;
00303         else
00304             cropType = CropNone;
00305 
00306         if (cropType != mCropType) {
00307             aResize = PR_TRUE;
00308             mCropType = cropType;
00309         }
00310     }
00311 
00312     if (aAttribute == nsnull || aAttribute == nsHTMLAtoms::value) {
00313         mContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::value, mTitle);
00314         doUpdateTitle = PR_TRUE;
00315     }
00316 
00317     if (aAttribute == nsnull || aAttribute == nsXULAtoms::accesskey) {
00318         mNeedsReflowCallback = PR_TRUE;
00319         // Ensure that layout is refreshed and reflow callback called.
00320         aResize = PR_TRUE;
00321     }
00322 
00323     if (doUpdateTitle) {
00324         UpdateAccessTitle();
00325         aResize = PR_TRUE;
00326     }
00327 
00328 }
00329 
00330 NS_IMETHODIMP
00331 nsTextBoxFrame::Paint(nsPresContext*      aPresContext,
00332                       nsIRenderingContext& aRenderingContext,
00333                       const nsRect&        aDirtyRect,
00334                       nsFramePaintLayer    aWhichLayer,
00335                       PRUint32             aFlags)
00336 {
00337     if (!GetStyleVisibility()->IsVisible())
00338         return NS_OK;
00339 
00340     if (NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer) {
00341 
00342         // remove the border and padding
00343         nsStyleBorderPadding  bPad;
00344         mStyleContext->GetBorderPaddingFor(bPad);
00345         nsMargin border(0,0,0,0);
00346         bPad.GetBorderPadding(border);
00347 
00348         nsRect textRect(0,0,mRect.width, mRect.height);
00349         textRect.Deflate(border);
00350 
00351         PaintTitle(aPresContext, aRenderingContext, aDirtyRect, textRect);
00352     }
00353 
00354     return nsLeafFrame::Paint(aPresContext, aRenderingContext, aDirtyRect, aWhichLayer);
00355 }
00356 
00357 NS_IMETHODIMP
00358 nsTextBoxFrame::PaintTitle(nsPresContext*      aPresContext,
00359                            nsIRenderingContext& aRenderingContext,
00360                            const nsRect&        aDirtyRect,
00361                            const nsRect&        aRect)
00362 {
00363     if (mTitle.IsEmpty())
00364         return NS_OK;
00365 
00366     // determine (cropped) title and underline position
00367     LayoutTitle(aPresContext, aRenderingContext, aRect);
00368 
00369     // make the rect as small as our (cropped) text.
00370     nsRect textRect(aRect);
00371     textRect.width = mTitleWidth;
00372 
00373     // Align our text within the overall rect by checking our text-align property.
00374     const nsStyleVisibility* vis = GetStyleVisibility();
00375     const nsStyleText* textStyle = GetStyleText();
00376 
00377     if (textStyle->mTextAlign == NS_STYLE_TEXT_ALIGN_CENTER)
00378       textRect.x += (aRect.width - textRect.width)/2;
00379     else if (textStyle->mTextAlign == NS_STYLE_TEXT_ALIGN_RIGHT) {
00380       if (vis->mDirection == NS_STYLE_DIRECTION_LTR)
00381         textRect.x += (aRect.width - textRect.width);
00382     }
00383     else {
00384       if (vis->mDirection == NS_STYLE_DIRECTION_RTL)
00385         textRect.x += (aRect.width - textRect.width);
00386     }
00387 
00388     // don't draw if the title is not dirty
00389     if (PR_FALSE == aDirtyRect.Intersects(textRect))
00390         return NS_OK;
00391 
00392     // paint the title
00393     nscolor overColor;
00394     nscolor underColor;
00395     nscolor strikeColor;
00396     nsStyleContext* context = mStyleContext;
00397   
00398     PRUint8 decorations = NS_STYLE_TEXT_DECORATION_NONE; // Begin with no decorations
00399     PRUint8 decorMask = NS_STYLE_TEXT_DECORATION_UNDERLINE | NS_STYLE_TEXT_DECORATION_OVERLINE |
00400                         NS_STYLE_TEXT_DECORATION_LINE_THROUGH; // A mask of all possible decorations.
00401     PRBool hasDecorations = context->HasTextDecorations();
00402 
00403     do {  // find decoration colors
00404       const nsStyleTextReset* styleText = context->GetStyleTextReset();
00405       
00406       if (decorMask & styleText->mTextDecoration) {  // a decoration defined here
00407         nscolor color = context->GetStyleColor()->mColor;
00408     
00409         if (NS_STYLE_TEXT_DECORATION_UNDERLINE & decorMask & styleText->mTextDecoration) {
00410           underColor = color;
00411           decorMask &= ~NS_STYLE_TEXT_DECORATION_UNDERLINE;
00412           decorations |= NS_STYLE_TEXT_DECORATION_UNDERLINE;
00413         }
00414         if (NS_STYLE_TEXT_DECORATION_OVERLINE & decorMask & styleText->mTextDecoration) {
00415           overColor = color;
00416           decorMask &= ~NS_STYLE_TEXT_DECORATION_OVERLINE;
00417           decorations |= NS_STYLE_TEXT_DECORATION_OVERLINE;
00418         }
00419         if (NS_STYLE_TEXT_DECORATION_LINE_THROUGH & decorMask & styleText->mTextDecoration) {
00420           strikeColor = color;
00421           decorMask &= ~NS_STYLE_TEXT_DECORATION_LINE_THROUGH;
00422           decorations |= NS_STYLE_TEXT_DECORATION_LINE_THROUGH;
00423         }
00424       }
00425       if (0 != decorMask) {
00426         context = context->GetParent();
00427         if (context) {
00428           hasDecorations = context->HasTextDecorations();
00429         }
00430       }
00431     } while (context && hasDecorations && (0 != decorMask));
00432 
00433     const nsStyleFont* fontStyle = GetStyleFont();
00434     
00435     nscoord offset;
00436     nscoord size;
00437     nscoord baseline;
00438     nsCOMPtr<nsIFontMetrics> fontMet;
00439     aPresContext->DeviceContext()->GetMetricsFor(fontStyle->mFont,
00440                                                  *getter_AddRefs(fontMet));
00441     fontMet->GetMaxAscent(baseline);
00442 
00443     if (decorations & (NS_FONT_DECORATION_OVERLINE | NS_FONT_DECORATION_UNDERLINE)) {
00444       fontMet->GetUnderline(offset, size);
00445       if (decorations & NS_FONT_DECORATION_OVERLINE) {
00446         aRenderingContext.SetColor(overColor);
00447         aRenderingContext.FillRect(textRect.x, textRect.y, textRect.width, size);
00448       }
00449       if (decorations & NS_FONT_DECORATION_UNDERLINE) {
00450         aRenderingContext.SetColor(underColor);
00451         aRenderingContext.FillRect(textRect.x, textRect.y + baseline - offset, textRect.width, size);
00452       }
00453     }
00454     if (decorations & NS_FONT_DECORATION_LINE_THROUGH) {
00455       fontMet->GetStrikeout(offset, size);
00456       aRenderingContext.SetColor(strikeColor);
00457       aRenderingContext.FillRect(textRect.x, textRect.y + baseline - offset, textRect.width, size);
00458     }
00459  
00460     aRenderingContext.SetFont(fontStyle->mFont, nsnull);
00461 
00462     CalculateUnderline(aRenderingContext);
00463 
00464     aRenderingContext.SetColor(GetStyleColor()->mColor);
00465 
00466 #ifdef IBMBIDI
00467     nsresult rv = NS_ERROR_FAILURE;
00468 
00469     if (mState & NS_FRAME_IS_BIDI) {
00470       aPresContext->SetBidiEnabled(PR_TRUE);
00471       nsBidiPresUtils* bidiUtils = aPresContext->GetBidiUtils();
00472 
00473       if (bidiUtils) {
00474         const nsStyleVisibility* vis = GetStyleVisibility();
00475         nsBidiDirection direction = (NS_STYLE_DIRECTION_RTL == vis->mDirection) ? NSBIDI_RTL : NSBIDI_LTR;
00476         if (mAccessKeyInfo && mAccessKeyInfo->mAccesskeyIndex != kNotFound) {
00477            // We let the RenderText function calculate the mnemonic's
00478            // underline position for us.
00479            nsBidiPositionResolve posResolve;
00480            posResolve.logicalIndex = mAccessKeyInfo->mAccesskeyIndex;
00481            rv = bidiUtils->RenderText(mCroppedTitle.get(), mCroppedTitle.Length(), direction,
00482                                       aPresContext, aRenderingContext,
00483                                       textRect.x, textRect.y + baseline,
00484                                       &posResolve,
00485                                       1);
00486            mAccessKeyInfo->mBeforeWidth = posResolve.visualLeftTwips;
00487         }
00488         else
00489         {
00490            rv = bidiUtils->RenderText(mCroppedTitle.get(), mCroppedTitle.Length(), direction,
00491                                       aPresContext, aRenderingContext,
00492                                       textRect.x, textRect.y + baseline);
00493         }
00494       }
00495     }
00496     if (NS_FAILED(rv) )
00497 #endif // IBMBIDI
00498     {
00499        if (mAccessKeyInfo && mAccessKeyInfo->mAccesskeyIndex != kNotFound) {
00500            // In the simple (non-BiDi) case, we calculate the mnemonic's
00501            // underline position by getting the text metric.
00502            // XXX are attribute values always two byte?
00503            if (mAccessKeyInfo->mAccesskeyIndex > 0)
00504                aRenderingContext.GetWidth(mCroppedTitle.get(), mAccessKeyInfo->mAccesskeyIndex,
00505                                           mAccessKeyInfo->mBeforeWidth);
00506            else
00507                mAccessKeyInfo->mBeforeWidth = 0;
00508        }
00509 
00510        aRenderingContext.DrawString(mCroppedTitle, textRect.x, textRect.y + baseline);
00511     }
00512 
00513     if (mAccessKeyInfo && mAccessKeyInfo->mAccesskeyIndex != kNotFound) {
00514         aRenderingContext.FillRect(textRect.x + mAccessKeyInfo->mBeforeWidth,
00515                                    textRect.y + mAccessKeyInfo->mAccessOffset,
00516                                    mAccessKeyInfo->mAccessWidth,
00517                                    mAccessKeyInfo->mAccessUnderlineSize);
00518     }
00519 
00520     return NS_OK;
00521 }
00522 
00523 void
00524 nsTextBoxFrame::LayoutTitle(nsPresContext*      aPresContext,
00525                             nsIRenderingContext& aRenderingContext,
00526                             const nsRect&        aRect)
00527 {
00528     // and do caculations if our size changed
00529     if ((mState & NS_STATE_NEED_LAYOUT)) {
00530 
00531         // determine (cropped) title which fits in aRect.width and its width
00532         CalculateTitleForWidth(aPresContext, aRenderingContext, aRect.width);
00533 
00534         // determine if and at which position to put the underline
00535         UpdateAccessIndex();
00536 
00537         // ok layout complete
00538         mState &= ~NS_STATE_NEED_LAYOUT;
00539     }
00540 }
00541 
00542 void
00543 nsTextBoxFrame::CalculateUnderline(nsIRenderingContext& aRenderingContext)
00544 {
00545     if (mAccessKeyInfo && mAccessKeyInfo->mAccesskeyIndex != kNotFound) {
00546          // Calculate all fields of mAccessKeyInfo which
00547          // are the same for both BiDi and non-BiDi rames.
00548          const PRUnichar *titleString = mCroppedTitle.get();
00549          aRenderingContext.GetWidth(titleString[mAccessKeyInfo->mAccesskeyIndex],
00550                                     mAccessKeyInfo->mAccessWidth);
00551 
00552          nscoord offset, baseline;
00553          nsIFontMetrics *metrics;
00554          aRenderingContext.GetFontMetrics(metrics);
00555          metrics->GetUnderline(offset, mAccessKeyInfo->mAccessUnderlineSize);
00556          metrics->GetMaxAscent(baseline);
00557          NS_RELEASE(metrics);
00558          mAccessKeyInfo->mAccessOffset = baseline - offset;
00559     }
00560 }
00561 
00562 void
00563 nsTextBoxFrame::CalculateTitleForWidth(nsPresContext*      aPresContext,
00564                                        nsIRenderingContext& aRenderingContext,
00565                                        nscoord              aWidth)
00566 {
00567     if (mTitle.IsEmpty())
00568         return;
00569 
00570     nsCOMPtr<nsIFontMetrics> fontMet;
00571     aPresContext->DeviceContext()->GetMetricsFor(GetStyleFont()->mFont,
00572                                                  *getter_AddRefs(fontMet));
00573     aRenderingContext.SetFont(fontMet);
00574 
00575     // see if the text will completely fit in the width given
00576     aRenderingContext.GetWidth(mTitle, mTitleWidth);
00577 
00578     if (mTitleWidth <= aWidth) {
00579         mCroppedTitle = mTitle;
00580 #ifdef IBMBIDI
00581         PRInt32 length = mTitle.Length();
00582         for (PRInt32 i = 0; i < length; i++) {
00583           if ((UCS2_CHAR_IS_BIDI(mTitle.CharAt(i)) ) ||
00584               ((IS_HIGH_SURROGATE(mTitle.CharAt(i))) &&
00585                (++i < length) &&
00586                (IS_LOW_SURROGATE(mTitle.CharAt(i))) &&
00587                (UTF32_CHAR_IS_BIDI(SURROGATE_TO_UCS4(mTitle.CharAt(i-1),
00588                                                      mTitle.CharAt(i)))))) {
00589             mState |= NS_FRAME_IS_BIDI;
00590             break;
00591           }
00592         }
00593 #endif // IBMBIDI
00594         return;  // fits, done.
00595     }
00596 
00597     // start with an ellipsis
00598     mCroppedTitle.AssignASCII(ELLIPSIS);
00599 
00600     // see if the width is even smaller than the ellipsis
00601     // if so, clear the text (XXX set as many '.' as we can?).
00602     nscoord ellipsisWidth;
00603     aRenderingContext.GetWidth(ELLIPSIS, ellipsisWidth);
00604 
00605     if (ellipsisWidth > aWidth) {
00606         mCroppedTitle.SetLength(0);
00607         mTitleWidth = aWidth;
00608         return;
00609     }
00610 
00611     // if the ellipsis fits perfectly, no use in trying to insert
00612     if (ellipsisWidth == aWidth) {
00613         mTitleWidth = aWidth;
00614         return;
00615     }
00616 
00617     aWidth -= ellipsisWidth;
00618 
00619     // XXX: This whole block should probably take surrogates into account
00620     // ok crop things
00621     switch (mCropType)
00622     {
00623         case CropNone:
00624         case CropRight:
00625         {
00626             nscoord cwidth;
00627             nscoord twidth = 0;
00628             int length = mTitle.Length();
00629             int i;
00630             for (i = 0; i < length; ++i) {
00631                 PRUnichar ch = mTitle.CharAt(i);
00632                 aRenderingContext.GetWidth(ch,cwidth);
00633                 if (twidth + cwidth > aWidth)
00634                     break;
00635 
00636                 twidth += cwidth;
00637 #ifdef IBMBIDI
00638                 if (UCS2_CHAR_IS_BIDI(ch) ) {
00639                   mState |= NS_FRAME_IS_BIDI;
00640                 }
00641 #endif // IBMBIDI
00642             }
00643 
00644             if (i == 0)
00645                 return;
00646 
00647             // insert what character we can in.
00648             nsAutoString title( mTitle );
00649             title.Truncate(i);
00650             mCroppedTitle.Insert(title, 0);
00651         }
00652         break;
00653 
00654         case CropLeft:
00655         {
00656             nscoord cwidth;
00657             nscoord twidth = 0;
00658             int length = mTitle.Length();
00659             int i;
00660             for (i=length-1; i >= 0; --i) {
00661                 PRUnichar ch = mTitle.CharAt(i);
00662                 aRenderingContext.GetWidth(ch,cwidth);
00663                 if (twidth + cwidth > aWidth)
00664                     break;
00665 
00666                 twidth += cwidth;
00667 #ifdef IBMBIDI
00668                 if (UCS2_CHAR_IS_BIDI(ch) ) {
00669                   mState |= NS_FRAME_IS_BIDI;
00670                 }
00671 #endif // IBMBIDI
00672             }
00673 
00674             if (i == length-1)
00675                 break;
00676 
00677             nsAutoString copy;
00678             mTitle.Right(copy, length-1-i);
00679             mCroppedTitle += copy;
00680         }
00681         break;
00682 
00683         case CropCenter:
00684         {
00685             nscoord stringWidth = 0;
00686             aRenderingContext.GetWidth(mTitle, stringWidth);
00687             if (stringWidth <= aWidth) {
00688                 // the entire string will fit in the maximum width
00689                 mCroppedTitle.Insert(mTitle, 0);
00690                 break;
00691             }
00692 
00693             // determine how much of the string will fit in the max width
00694             nscoord charWidth = 0;
00695             nscoord totalWidth = 0;
00696             PRUnichar ch;
00697             int leftPos, rightPos;
00698             nsAutoString leftString, rightString;
00699 
00700             rightPos = mTitle.Length() - 1;
00701             for (leftPos = 0; leftPos <= rightPos;) {
00702                 // look at the next character on the left end
00703                 ch = mTitle.CharAt(leftPos);
00704                 aRenderingContext.GetWidth(ch, charWidth);
00705                 totalWidth += charWidth;
00706                 if (totalWidth > aWidth)
00707                     // greater than the allowable width
00708                     break;
00709                 leftString.Insert(ch, leftString.Length());
00710 
00711 #ifdef IBMBIDI
00712                 if (UCS2_CHAR_IS_BIDI(ch))
00713                     mState |= NS_FRAME_IS_BIDI;
00714 #endif
00715 
00716                 // look at the next character on the right end
00717                 if (rightPos > leftPos) {
00718                     // haven't looked at this character yet
00719                     ch = mTitle.CharAt(rightPos);
00720                     aRenderingContext.GetWidth(ch, charWidth);
00721                     totalWidth += charWidth;
00722                     if (totalWidth > aWidth)
00723                         // greater than the allowable width
00724                         break;
00725                     rightString.Insert(ch, 0);
00726 
00727 #ifdef IBMBIDI
00728                     if (UCS2_CHAR_IS_BIDI(ch))
00729                         mState |= NS_FRAME_IS_BIDI;
00730 #endif
00731                 }
00732 
00733                 // look at the next two characters
00734                 leftPos++;
00735                 rightPos--;
00736             }
00737 
00738             // form the new cropped string
00739             nsAutoString ellipsisString;
00740             ellipsisString.AssignASCII(ELLIPSIS);
00741 
00742             mCroppedTitle = leftString + ellipsisString + rightString;
00743         }
00744         break;
00745     }
00746 
00747     aRenderingContext.GetWidth(mCroppedTitle, mTitleWidth);
00748 }
00749 
00750 // the following block is to append the accesskey to mTitle if there is an accesskey
00751 // but the mTitle doesn't have the character
00752 void
00753 nsTextBoxFrame::UpdateAccessTitle()
00754 {
00755     PRInt32 menuAccessKey;
00756     nsMenuBarListener::GetMenuAccessKey(&menuAccessKey);
00757     if (menuAccessKey) {
00758         if (!mAccessKey.IsEmpty()) {
00759             if (( !FindInReadable(mAccessKey, mTitle, nsCaseInsensitiveStringComparator()))
00760                 || AlwaysAppendAccessKey()) 
00761             {
00762                 nsAutoString tmpstring;
00763                 if (InsertSeparatorBeforeAccessKey() &&
00764                     !mTitle.IsEmpty() && !NS_IS_SPACE(mTitle.Last())) {
00765                   tmpstring += ' ';
00766                 }
00767                 tmpstring += '(';
00768                 tmpstring += mAccessKey;
00769                 ToUpperCase(tmpstring);
00770                 tmpstring.Append(NS_LITERAL_STRING(")"));
00771                 PRInt32 offset = mTitle.RFind("...");
00772                 if (offset != kNotFound) {
00773                     mTitle.Insert(tmpstring,NS_STATIC_CAST(PRUint32, offset));
00774                 } else {
00775                     PRUint32 l = mTitle.Length();
00776                     if((l > 0) && (PRUnichar(':')==mTitle[l-1])) {
00777                       mTitle.Insert(tmpstring,l-1);
00778                     } else {
00779                       mTitle += tmpstring;
00780                     }
00781                 }
00782             }
00783         }
00784     }
00785 }
00786 
00787 void
00788 nsTextBoxFrame::UpdateAccessIndex()
00789 {
00790     PRInt32 menuAccessKey;
00791     nsMenuBarListener::GetMenuAccessKey(&menuAccessKey);
00792     if (menuAccessKey) {
00793         if (mAccessKey.IsEmpty()) {
00794             if (mAccessKeyInfo) {
00795                 delete mAccessKeyInfo;
00796                 mAccessKeyInfo = nsnull;
00797             }
00798         } else {
00799             if (!mAccessKeyInfo) {
00800                 mAccessKeyInfo = new nsAccessKeyInfo();
00801                 if (!mAccessKeyInfo)
00802                     return;
00803             }
00804 
00805             nsAString::const_iterator start, end;
00806                 
00807             mCroppedTitle.BeginReading(start);
00808             mCroppedTitle.EndReading(end);
00809             
00810             // remember the beginning of the string
00811             nsAString::const_iterator originalStart = start;
00812 
00813             PRBool found;
00814             if (!AlwaysAppendAccessKey()) {
00815                 // not appending access key - do case-sensitive search
00816                 // first
00817                 found = FindInReadable(mAccessKey, start, end);
00818                 if (!found) {
00819                     // didn't find it - perform a case-insensitive search
00820                     start = originalStart;
00821                     found = FindInReadable(mAccessKey, start, end,
00822                                            nsCaseInsensitiveStringComparator());
00823                 }
00824             } else {
00825                 found = RFindInReadable(mAccessKey, start, end,
00826                                         nsCaseInsensitiveStringComparator());
00827             }
00828             
00829             if (found)
00830                 mAccessKeyInfo->mAccesskeyIndex = Distance(originalStart, start);
00831             else
00832                 mAccessKeyInfo->mAccesskeyIndex = kNotFound;
00833         }
00834     }
00835 }
00836 
00837 NS_IMETHODIMP
00838 nsTextBoxFrame::DoLayout(nsBoxLayoutState& aBoxLayoutState)
00839 {
00840     if (mNeedsReflowCallback) {
00841         nsCOMPtr<nsIReflowCallback> cb = new nsAsyncAccesskeyUpdate(this);
00842         if (cb) {
00843             GetPresContext()->PresShell()->PostReflowCallback(cb);
00844         }
00845         mNeedsReflowCallback = PR_FALSE;
00846     }
00847 
00848     mState |= NS_STATE_NEED_LAYOUT;
00849 
00850     return nsLeafBoxFrame::DoLayout(aBoxLayoutState);
00851 }
00852 
00853 NS_IMETHODIMP
00854 nsTextBoxFrame::NeedsRecalc()
00855 {
00856     mNeedsRecalc = PR_TRUE;
00857     return NS_OK;
00858 }
00859 
00860 void
00861 nsTextBoxFrame::GetTextSize(nsPresContext* aPresContext, nsIRenderingContext& aRenderingContext,
00862                                 const nsString& aString, nsSize& aSize, nscoord& aAscent)
00863 {
00864     nsCOMPtr<nsIFontMetrics> fontMet;
00865     aPresContext->DeviceContext()->GetMetricsFor(GetStyleFont()->mFont,
00866                                                  *getter_AddRefs(fontMet));
00867     fontMet->GetHeight(aSize.height);
00868     aRenderingContext.SetFont(fontMet);
00869     aRenderingContext.GetWidth(aString, aSize.width);
00870     fontMet->GetMaxAscent(aAscent);
00871 }
00872 
00873 void
00874 nsTextBoxFrame::CalcTextSize(nsBoxLayoutState& aBoxLayoutState)
00875 {
00876     if (mNeedsRecalc)
00877     {
00878         nsSize size;
00879         nsPresContext* presContext = aBoxLayoutState.PresContext();
00880         const nsHTMLReflowState* rstate = aBoxLayoutState.GetReflowState();
00881         if (!rstate)
00882             return;
00883 
00884         nsIRenderingContext* rendContext = rstate->rendContext;
00885 
00886         if (rendContext) {
00887             GetTextSize(presContext, *rendContext,
00888                         mTitle, size, mAscent);
00889             mTextSize = size;
00890             mNeedsRecalc = PR_FALSE;
00891         }
00892     }
00893 }
00894 
00898 NS_IMETHODIMP
00899 nsTextBoxFrame::GetPrefSize(nsBoxLayoutState& aBoxLayoutState, nsSize& aSize)
00900 {
00901     CalcTextSize(aBoxLayoutState);
00902 
00903     aSize = mTextSize;
00904 
00905     AddBorderAndPadding(aSize);
00906     AddInset(aSize);
00907     nsIBox::AddCSSPrefSize(aBoxLayoutState, this, aSize);
00908 
00909     return NS_OK;
00910 }
00911 
00915 NS_IMETHODIMP
00916 nsTextBoxFrame::GetMinSize(nsBoxLayoutState& aBoxLayoutState, nsSize& aSize)
00917 {
00918     CalcTextSize(aBoxLayoutState);
00919 
00920     aSize = mTextSize;
00921 
00922      // if there is cropping our min width becomes our border and  padding
00923     if (mCropType != CropNone) {
00924        aSize.width = 0;
00925     }
00926 
00927     AddBorderAndPadding(aSize);
00928     AddInset(aSize);
00929     nsIBox::AddCSSMinSize(aBoxLayoutState, this, aSize);
00930 
00931     return NS_OK;
00932 }
00933 
00934 NS_IMETHODIMP
00935 nsTextBoxFrame::GetAscent(nsBoxLayoutState& aBoxLayoutState, nscoord& aAscent)
00936 {
00937     CalcTextSize(aBoxLayoutState);
00938 
00939     aAscent = mAscent;
00940 
00941     nsMargin m(0,0,0,0);
00942     GetBorderAndPadding(m);
00943     aAscent += m.top;
00944     GetInset(m);
00945     aAscent += m.top;
00946 
00947     return NS_OK;
00948 }
00949 
00950 #ifdef DEBUG
00951 NS_IMETHODIMP
00952 nsTextBoxFrame::GetFrameName(nsAString& aResult) const
00953 {
00954     MakeFrameName(NS_LITERAL_STRING("Text"), aResult);
00955     aResult += NS_LITERAL_STRING("[value=") + mTitle + NS_LITERAL_STRING("]");
00956     return NS_OK;
00957 }
00958 #endif
00959 
00960 // If you make changes to this function, check its counterparts 
00961 // in nsBoxFrame and nsAreaFrame
00962 nsresult
00963 nsTextBoxFrame::RegUnregAccessKey(nsPresContext* aPresContext,
00964                                   PRBool          aDoReg)
00965 {
00966     // if we have no content, we can't do anything
00967     if (!mContent)
00968         return NS_ERROR_FAILURE;
00969 
00970     // check if we have a |control| attribute
00971     // do this check first because few elements have control attributes, and we
00972     // can weed out most of the elements quickly.
00973 
00974     // XXXjag a side-effect is that we filter out anonymous <label>s
00975     // in e.g. <menu>, <menuitem>, <button>. These <label>s inherit
00976     // |accesskey| and would otherwise register themselves, overwriting
00977     // the content we really meant to be registered.
00978     if (!mContent->HasAttr(kNameSpaceID_None, nsXULAtoms::control))
00979         return NS_OK;
00980 
00981     // see if we even have an access key
00982     nsAutoString accessKey;
00983     mContent->GetAttr(kNameSpaceID_None, nsXULAtoms::accesskey, accessKey);
00984 
00985     if (accessKey.IsEmpty())
00986         return NS_OK;
00987 
00988     nsresult rv;
00989 
00990     // With a valid PresContext we can get the ESM 
00991     // and (un)register the access key
00992     nsIEventStateManager *esm = aPresContext->EventStateManager();
00993 
00994     PRUint32 key = accessKey.First();
00995     if (aDoReg)
00996         rv = esm->RegisterAccessKey(mContent, key);
00997     else
00998         rv = esm->UnregisterAccessKey(mContent, key);
00999 
01000     return rv;
01001 }