diff options
Diffstat (limited to 'starmath/source/rect.cxx')
-rw-r--r-- | starmath/source/rect.cxx | 908 |
1 files changed, 908 insertions, 0 deletions
diff --git a/starmath/source/rect.cxx b/starmath/source/rect.cxx new file mode 100644 index 000000000000..12853c07189c --- /dev/null +++ b/starmath/source/rect.cxx @@ -0,0 +1,908 @@ +/************************************************************************* + * + * $RCSfile: rect.cxx,v $ + * + * $Revision: 1.1.1.1 $ + * + * last change: $Author: hr $ $Date: 2000-09-18 16:57:26 $ + * + * The Contents of this file are made available subject to the terms of + * either of the following licenses + * + * - GNU Lesser General Public License Version 2.1 + * - Sun Industry Standards Source License Version 1.1 + * + * Sun Microsystems Inc., October, 2000 + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2000 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * + * Sun Industry Standards Source License Version 1.1 + * ================================================= + * The contents of this file are subject to the Sun Industry Standards + * Source 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.openoffice.org/license.html. + * + * Software provided under this License is provided on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, + * WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS, + * MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING. + * See the License for the specific provisions governing your rights and + * obligations concerning the Software. + * + * The Initial Developer of the Original Code is: Sun Microsystems, Inc. + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * All Rights Reserved. + * + * Contributor(s): _______________________________________ + * + * + ************************************************************************/ + +#pragma hdrstop + +#ifndef _STRING_HXX //autogen +#include <tools/string.hxx> +#endif +#ifndef _TOOLS_DEBUG_HXX //autogen +#include <tools/debug.hxx> +#endif +#ifndef _SV_SVAPP_HXX //autogen +#include <vcl/svapp.hxx> +#endif +#ifndef _SV_WRKWIN_HXX //autogen +#include <vcl/wrkwin.hxx> +#endif +#ifndef _SV_VIRDEV_HXX //autogen +#include <vcl/virdev.hxx> +#endif + + +#include "rect.hxx" +#include "types.hxx" +#include "xchar.hxx" +#include "utility.hxx" +#include "smmod.hxx" + + +//////////////////////////////////////////////////////////////////////////////// + + +// '\0' terminiertes Array mit Zeichen, die im StarMath Font als Buchstaben +// betrachtet werden sollen, (um im Gegensatz zu den anderen Operatoren +// und Symbolen ein "normales"(ungecliptes) SmRect zu erhalten). +static xub_Unicode __READONLY_DATA aMathAlpha[] = +{ + MS_ALEPH, MS_IM, MS_RE, MS_WP, + xub_Unicode('\x70'), MS_EMPTYSET, xub_Unicode('\xF0'), xub_Unicode('\xF1'), + xub_Unicode('\xF2'), xub_Unicode('\xF3'), xub_Unicode('\xF4'), MS_HBAR, + MS_LAMBDABAR, MS_SETN, MS_SETZ, MS_SETQ, + MS_SETR, MS_SETC, xub_Unicode('\xB4'), xub_Unicode('\xB5'), + xub_Unicode('\xB8'), xub_Unicode('\xB9'), xub_Unicode('\xBA'), + xub_Unicode('\0') +}; + +BOOL SmIsMathAlpha(const XubString &rText) + // ergibt genau dann TRUE, wenn das Zeichen (aus dem StarMath Font) wie ein + // Buchstabe behandelt werden soll. +{ + if (rText.Len() == 0) + return FALSE; + + DBG_ASSERT(rText.Len() == 1, "Sm : String enthlt nicht genau ein Zeichen"); + xub_Unicode cChar = rText.GetChar(0); + + // ist es ein griechisches Zeichen ? + if (xub_Unicode('\xC6') <= cChar && cChar <= xub_Unicode('\xEE')) + return TRUE; + else + { + // kommt es in 'aMathAlpha' vor ? + const xub_Unicode *pChar = aMathAlpha; + while (*pChar && *pChar != cChar) + pChar++; + return *pChar != xub_Unicode('\0'); + } +} + + +//////////////////////////////////////// +// +// SmRect members +// + + +SmRect::SmRect() + // constructs empty rectangle at (0, 0) with width and height 0. +{ + DBG_ASSERT(aTopLeft == Point(0, 0), "Sm: ooops..."); + DBG_ASSERT(aSize == Size(0, 0), "Sm: ooops..."); + + bHasBaseline = bHasAlignInfo = FALSE; + nBaseline = nAlignT = nAlignM = nAlignB = + nGlyphTop = nGlyphBottom = + nItalicLeftSpace = nItalicRightSpace = + nLoAttrFence = nHiAttrFence = 0; +} + + +SmRect::SmRect(const SmRect &rRect) +: aTopLeft(rRect.aTopLeft), + aSize(rRect.aSize) +{ + bHasBaseline = rRect.bHasBaseline; + nBaseline = rRect.nBaseline; + nAlignT = rRect.nAlignT; + nAlignM = rRect.nAlignM; + nAlignB = rRect.nAlignB; + nGlyphTop = rRect.nGlyphTop; + nGlyphBottom = rRect.nGlyphBottom; + nHiAttrFence = rRect.nHiAttrFence; + nLoAttrFence = rRect.nLoAttrFence; + bHasAlignInfo = rRect.bHasAlignInfo; + nItalicLeftSpace = rRect.nItalicLeftSpace; + nItalicRightSpace = rRect.nItalicRightSpace; +} + + +void SmRect::CopyAlignInfo(const SmRect &rRect) +{ + nBaseline = rRect.nBaseline; + bHasBaseline = rRect.bHasBaseline; + nAlignT = rRect.nAlignT; + nAlignM = rRect.nAlignM; + nAlignB = rRect.nAlignB; + bHasAlignInfo = rRect.bHasAlignInfo; + nLoAttrFence = rRect.nLoAttrFence; + nHiAttrFence = rRect.nHiAttrFence; +} + + +void SmRect::BuildRect(const OutputDevice &rDev, const SmFormat *pFormat, + const XubString &rText, long nBorderWidth) +{ +#ifndef PRODUCT + if (rDev.GetOutDevType() != OUTDEV_PRINTER) + DBG_WARNING("Sm : Referenz-Device ist kein Drucker"); +#endif + + DBG_ASSERT(aTopLeft == Point(0, 0), "Sm: Ooops..."); + + aSize = Size(rDev.GetTextWidth(rText), rDev.GetTextHeight()); + + const FontMetric aFM (rDev.GetFontMetric()); + BOOL bIsMath = aFM.GetName().EqualsIgnoreCaseAscii("StarMath"); + BOOL bAllowSmaller = bIsMath && !SmIsMathAlpha(rText); + const long nFontHeight = rDev.GetFont().GetSize().Height(); + + bHasAlignInfo = TRUE; + bHasBaseline = TRUE; + nBaseline = aFM.GetAscent(); + nAlignT = nBaseline - nFontHeight * 750L / 1000L; + nAlignM = nBaseline - nFontHeight * 121L / 422L; + // that's where the horizontal bars of '+', '-', ... are + // (1/3 of ascent over baseline) + // (121 = 1/3 of 12pt ascent, 422 = 12pt fontheight) + nAlignB = nBaseline; + + // workaround for printer fonts with very small (possible 0 or even + // negative(!)) leading + if (aFM.GetLeading() < 5 && rDev.GetOutDevType() == OUTDEV_PRINTER) + { +#if SUPD >= 593 + OutputDevice *pWindow = Application::GetDefaultDevice(); +#else + Window *pWindow = Application::GetAppWindow(); +#endif + + pWindow->Push(PUSH_MAPMODE | PUSH_FONT); + + pWindow->SetMapMode(rDev.GetMapMode()); + pWindow->SetFont(rDev.GetFontMetric()); + + long nDelta = pWindow->GetFontMetric().GetLeading(); + if (nDelta == 0) + { // dieser Wert entspricht etwa einem Leading von 80 bei einer + // Fonthhe von 422 (12pt) + nDelta = nFontHeight * 8L / 43; + } + SetTop(GetTop() - nDelta); + + pWindow->Pop(); + } + + // get GlyphBoundRect + Rectangle aGlyphRect; + BOOL bSuccess = SmGetGlyphBoundRect(rDev, rText, aGlyphRect); + DBG_ASSERT(bSuccess, "Sm : Ooops... (fehlt evtl. der Font?)"); + + nItalicLeftSpace = GetLeft() - aGlyphRect.Left() + nBorderWidth; + nItalicRightSpace = aGlyphRect.Right() - GetRight() + nBorderWidth; + if (nItalicLeftSpace < 0 && !bAllowSmaller) + nItalicLeftSpace = 0; + if (nItalicRightSpace < 0 && !bAllowSmaller) + nItalicRightSpace = 0; + + long nDist = 0; + if (pFormat) + nDist = (rDev.GetFont().GetSize().Height() + * pFormat->GetDistance(DIS_ORNAMENTSIZE)) / 100L; + + nHiAttrFence = aGlyphRect.TopLeft().Y() - 1 - nBorderWidth - nDist; + nLoAttrFence = SmFromTo(GetAlignB(), GetBottom(), 0.0); + + nGlyphTop = aGlyphRect.Top() - nBorderWidth; + nGlyphBottom = aGlyphRect.Bottom() + nBorderWidth; + + if (bAllowSmaller) + { + // fr Symbole und Operatoren aus dem StarMath Font passen wir den + // oberen und unteren Rand dem Zeichen an. + SetTop(nGlyphTop); + SetBottom(nGlyphBottom); + } + + if (nHiAttrFence < GetTop()) + nHiAttrFence = GetTop(); + + if (nLoAttrFence > GetBottom()) + nLoAttrFence = GetBottom(); + + DBG_ASSERT(rText.Len() == 0 || !IsEmpty(), + "Sm: leeres Rechteck erzeugt"); +} + + +void SmRect::Init(const OutputDevice &rDev, const SmFormat *pFormat, + const XubString &rText, long nBorderWidth) + // get rectangle fitting for drawing 'rText' on OutputDevice 'rDev' +{ + SmRectCache *pRectCache = SM_MOD1()->GetRectCache(); + DBG_ASSERT(pRectCache, "Sm : NULL pointer"); + + // build key for rectangle (to look up in cache for) + const SmRectCache::Key aKey (rText, rDev.GetFont()); + + const SmRect *pResult = pRectCache->Search(aKey); + if (pResult) + *this = *pResult; + else + { // build rectangle and put it in cache + BuildRect(rDev, pFormat, rText, nBorderWidth); + pResult = pRectCache->Add(aKey, *this); + } + DBG_ASSERT(pResult, "Sm : NULL pointer"); +} + + +SmRect::SmRect(const OutputDevice &rDev, const SmFormat *pFormat, + const XubString &rText, long nBorderWidth) +{ + Init(rDev, pFormat, rText, nBorderWidth); +} + + +SmRect::SmRect(const OutputDevice &rDev, const SmFormat *pFormat, + const SmPolygon &rPoly, long nBorderWidth) +{ + Init(rDev, pFormat, rPoly.GetChar(), nBorderWidth); + + // den Offset in der Zeichenzelle passend waehlen + Point aPolyOffset (rPoly.GetOrigPos()); + aPolyOffset.X() *= rPoly.GetScaleX(); + aPolyOffset.Y() *= rPoly.GetScaleY(); + + // und es an diese Position schieben + Rectangle aPolyRect ( rPoly.GetBoundRect(rDev) ); + Point aDelta (aPolyOffset - aPolyRect.TopLeft()); + aPolyRect.Move( aDelta.X(), aDelta.Y() ); + + aTopLeft.X() = aPolyRect.Left() - nBorderWidth; + aTopLeft.Y() = aPolyRect.Top() - nBorderWidth; + + aSize = aPolyRect.GetSize(); + aSize.Width() += 2 * nBorderWidth; + aSize.Height() += 2 * nBorderWidth; + + nItalicLeftSpace = nItalicRightSpace = 0; +} + + +SmRect::SmRect(long nWidth, long nHeight) + // this constructor should never be used for anything textlike because + // it will not provide useful values for baseline, AlignT and AlignB! + // It's purpose is to get a 'SmRect' for the horizontal line in fractions + // as used in 'SmBinVerNode'. +: aSize(nWidth, nHeight) +{ + DBG_ASSERT(aTopLeft == Point(0, 0), "Sm: ooops..."); + + bHasBaseline = FALSE; + bHasAlignInfo = TRUE; + nBaseline = 0; + nAlignT = GetTop(); + nAlignB = GetBottom(); + nAlignM = (nAlignT + nAlignB) / 2; // this is the default + nItalicLeftSpace = nItalicRightSpace = 0; + nGlyphTop = nHiAttrFence = GetTop(); + nGlyphBottom = nLoAttrFence = GetBottom(); +} + + +void SmRect::SetLeft(long nLeft) +{ + if (nLeft <= GetRight()) + { aSize.Width() = GetRight() - nLeft + 1; + aTopLeft.X() = nLeft; + } +} + + +void SmRect::SetRight(long nRight) +{ + if (nRight >= GetLeft()) + aSize.Width() = nRight - GetLeft() + 1; +} + + +void SmRect::SetBottom(long nBottom) +{ + if (nBottom >= GetTop()) + aSize.Height() = nBottom - GetTop() + 1; +} + + +void SmRect::SetTop(long nTop) +{ + if (nTop <= GetBottom()) + { aSize.Height() = GetBottom() - nTop + 1; + aTopLeft.Y() = nTop; + } +} + + +void SmRect::Move(const Point &rPosition) + // move rectangle by position 'rPosition'. +{ + aTopLeft += rPosition; + + long nDelta = rPosition.Y(); + nBaseline += nDelta; + nAlignT += nDelta; + nAlignM += nDelta; + nAlignB += nDelta; + nGlyphTop += nDelta; + nGlyphBottom += nDelta; + nHiAttrFence += nDelta; + nLoAttrFence += nDelta; +} + + +const Point SmRect::AlignTo(const SmRect &rRect, RectPos ePos, + RectHorAlign eHor, RectVerAlign eVer) const +{ Point aPos (GetTopLeft()); + // will become the topleft point of the new rectangle position + + // set horizontal or vertical new rectangle position depending on + // 'ePos' is one of 'RP_LEFT', 'RP_RIGHT' or 'RP_TOP', 'RP_BOTTOM' + switch (ePos) + { case RP_LEFT : + aPos.X() = rRect.GetItalicLeft() - GetItalicRightSpace() + - GetWidth(); + break; + case RP_RIGHT : + aPos.X() = rRect.GetItalicRight() + 1 + GetItalicLeftSpace(); + break; + case RP_TOP : + aPos.Y() = rRect.GetTop() - GetHeight(); + break; + case RP_BOTTOM : + aPos.Y() = rRect.GetBottom() + 1; + break; + case RP_ATTRIBUT : + aPos.X() = rRect.GetItalicCenterX() - GetItalicWidth() / 2 + + GetItalicLeftSpace(); + break; + default : + DBG_ASSERT(FALSE, "Sm: unbekannter Fall"); + } + + // check if horizontal position is already set + if (ePos == RP_LEFT || ePos == RP_RIGHT || ePos == RP_ATTRIBUT) + // correct error in current vertical position + switch (eVer) + { case RVA_TOP : + aPos.Y() += rRect.GetAlignT() - GetAlignT(); + break; + case RVA_MID : + aPos.Y() += rRect.GetAlignM() - GetAlignM(); + break; + case RVA_BASELINE : + // align baselines if possible else align mid's + if (HasBaseline() && rRect.HasBaseline()) + aPos.Y() += rRect.GetBaseline() - GetBaseline(); + else + aPos.Y() += rRect.GetAlignM() - GetAlignM(); + break; + case RVA_BOTTOM : + aPos.Y() += rRect.GetAlignB() - GetAlignB(); + break; + case RVA_CENTERY : + aPos.Y() += rRect.GetCenterY() - GetCenterY(); + break; + case RVA_ATTRIBUT_HI: + aPos.Y() += rRect.GetHiAttrFence() - GetBottom(); + break; + case RVA_ATTRIBUT_MID : + aPos.Y() += SmFromTo(rRect.GetAlignB(), rRect.GetAlignT(), 0.4) + - GetCenterY(); + break; + case RVA_ATTRIBUT_LO : + aPos.Y() += rRect.GetLoAttrFence() - GetTop(); + break; + default : + DBG_ASSERT(FALSE, "Sm: unbekannter Fall"); + } + + // check if vertical position is already set + if (ePos == RP_TOP || ePos == RP_BOTTOM) + // correct error in current horizontal position + switch (eHor) + { case RHA_LEFT : + aPos.X() += rRect.GetItalicLeft() - GetItalicLeft(); + break; + case RHA_CENTER : + aPos.X() += rRect.GetItalicCenterX() - GetItalicCenterX(); + break; + case RHA_RIGHT : + aPos.X() += rRect.GetItalicRight() - GetItalicRight(); + break; + default : + DBG_ASSERT(FALSE, "Sm: unbekannter Fall"); + } + + return aPos; +} + + +SmRect & SmRect::Union(const SmRect &rRect) + // rectangle union of current one with 'rRect'. The result is to be the + // smallest rectangles that covers the space of both rectangles. + // (empty rectangles cover no space) + //! Italic correction is NOT taken into account here! +{ + if (rRect.IsEmpty()) + return *this; + + long nL = rRect.GetLeft(), + nR = rRect.GetRight(), + nT = rRect.GetTop(), + nB = rRect.GetBottom(), + nGT = rRect.nGlyphTop, + nGB = rRect.nGlyphBottom; + if (!IsEmpty()) + { long nTmp; + + if ((nTmp = GetLeft()) < nL) + nL = nTmp; + if ((nTmp = GetRight()) > nR) + nR = nTmp; + if ((nTmp = GetTop()) < nT) + nT = nTmp; + if ((nTmp = GetBottom()) > nB) + nB = nTmp; + if ((nTmp = nGlyphTop) < nGT) + nGT = nTmp; + if ((nTmp = nGlyphBottom) > nGB) + nGB = nTmp; + } + + SetLeft(nL); + SetRight(nR); + SetTop(nT); + SetBottom(nB); + nGlyphTop = nGT; + nGlyphBottom = nGB; + + return *this; +} + + +SmRect & SmRect::ExtendBy(const SmRect &rRect, RectCopyMBL eCopyMode) + // let current rectangle be the union of itself and 'rRect' + // (the smallest rectangle surrounding both). Also adapt values for + // 'AlignT', 'AlignM', 'AlignB', baseline and italic-spaces. + // The baseline is set according to 'eCopyMode'. + // If one of the rectangles has no relevant info the other one is copied. +{ + // get some values used for (italic) spaces adaption + // ! (need to be done before changing current SmRect) ! + long nL = Min(GetItalicLeft(), rRect.GetItalicLeft()), + nR = Max(GetItalicRight(), rRect.GetItalicRight()); + + Union(rRect); + + SetItalicSpaces(GetLeft() - nL, nR - GetRight()); + + if (!HasAlignInfo()) + CopyAlignInfo(rRect); + else if (rRect.HasAlignInfo()) + { nAlignT = Min(GetAlignT(), rRect.GetAlignT()); + nAlignB = Max(GetAlignB(), rRect.GetAlignB()); + nHiAttrFence = Min(GetHiAttrFence(), rRect.GetHiAttrFence()); + nLoAttrFence = Max(GetLoAttrFence(), rRect.GetLoAttrFence()); + DBG_ASSERT(HasAlignInfo(), "Sm: ooops..."); + + switch (eCopyMode) + { case RCP_THIS: + // already done + break; + case RCP_ARG: + CopyMBL(rRect); + break; + case RCP_NONE: + ClearBaseline(); + nAlignM = (nAlignT + nAlignB) / 2; + break; + case RCP_XOR: + if (!HasBaseline()) + CopyMBL(rRect); + break; + default : + DBG_ASSERT(FALSE, "Sm: unbekannter Fall"); + } + } + + return *this; +} + + +SmRect & SmRect::ExtendBy(const SmRect &rRect, RectCopyMBL eCopyMode, + long nNewAlignM) + // as 'ExtendBy' but sets AlignM value to 'nNewAlignM'. + // (this version will be used in 'SmBinVerNode' to provide means to + // align eg "{a over b} over c" correctly where AlignM should not + // be (AlignT + AlignB) / 2) +{ + DBG_ASSERT(HasAlignInfo(), "Sm: keine Align Info"); + + ExtendBy(rRect, eCopyMode); + nAlignM = nNewAlignM; + + return *this; +} + + +SmRect & SmRect::ExtendBy(const SmRect &rRect, RectCopyMBL eCopyMode, + BOOL bKeepVerAlignParams) + // as 'ExtendBy' but keeps original values for AlignT, -M and -B and + // baseline. + // (this is used in 'SmSupSubNode' where the sub-/supscripts shouldn't + // be allowed to modify these values.) +{ + long nOldAlignT = GetAlignT(), + nOldAlignM = GetAlignM(), + nOldAlignB = GetAlignB(), + nOldBaseline = nBaseline; //! depends not on 'HasBaseline' + BOOL bOldHasAlignInfo = HasAlignInfo(); + + ExtendBy(rRect, eCopyMode); + + if (bKeepVerAlignParams) + { nAlignT = nOldAlignT; + nAlignM = nOldAlignM; + nAlignB = nOldAlignB; + nBaseline = nOldBaseline; + bHasAlignInfo = bOldHasAlignInfo; + } + + return *this; +} + + +SmRect & SmRect::ExtendBy(const Point &rPoint) + // extend current rectangle to include 'rPoint'. + // The effect should be similar to + // "ExtendBy(rRect, RCP_THIS, (BOOL) TRUE)" + // where 'rRect' is a SmRect of size and width 1 with no italic spaces + // (as by "SmRect (1, 1)") and position at 'rPoint'. +{ + // get some values used for italic spaces adaption + // ! (need to be done before changing current SmRect) ! + long nL = Min(GetItalicLeft(), rPoint.X()), + nR = Max(GetItalicRight(), rPoint.X()); + + // this is the adaption of rectangle union + if (rPoint.X() < GetLeft()) + SetLeft(rPoint.X()); + if (rPoint.X() > GetRight()) + SetRight(rPoint.X()); + if (rPoint.Y() < GetTop()) + SetTop(rPoint.Y()); + if (rPoint.Y() > GetBottom()) + SetBottom(rPoint.Y()); + + SetItalicSpaces(GetLeft() - nL, nR - GetRight()); + + return *this; +} + + +long SmRect::OrientedDist(const Point &rPoint) const + // return oriented distance of rPoint to the current rectangle, + // especially the return value is <= 0 iff the point is inside the + // rectangle. + // For simplicity the maximum-norm is used. +{ + BOOL bIsInside = IsInsideItalicRect(rPoint); + + // build reference point to define the distance + Point aRef; + if (bIsInside) + { Point aIC (GetItalicCenterX(), GetCenterY()); + + aRef.X() = rPoint.X() >= aIC.X() ? GetItalicRight() : GetItalicLeft(); + aRef.Y() = rPoint.Y() >= aIC.Y() ? GetBottom() : GetTop(); + } + else + { + // x-coordinate + if (rPoint.X() > GetItalicRight()) + aRef.X() = GetItalicRight(); + else if (rPoint.X() < GetItalicLeft()) + aRef.X() = GetItalicLeft(); + else + aRef.X() = rPoint.X(); + // y-coordinate + if (rPoint.Y() > GetBottom()) + aRef.Y() = GetBottom(); + else if (rPoint.Y() < GetTop()) + aRef.Y() = GetTop(); + else + aRef.Y() = rPoint.Y(); + } + + // build distance vector + Point aDist (aRef - rPoint); + + long nAbsX = labs(aDist.X()), + nAbsY = labs(aDist.Y()); + + return bIsInside ? - Min(nAbsX, nAbsY) : Max (nAbsX, nAbsY); +} + + +BOOL SmRect::IsInsideRect(const Point &rPoint) const +{ + return rPoint.Y() >= GetTop() + && rPoint.Y() <= GetBottom() + && rPoint.X() >= GetLeft() + && rPoint.X() <= GetRight(); +} + + +BOOL SmRect::IsInsideItalicRect(const Point &rPoint) const +{ + return rPoint.Y() >= GetTop() + && rPoint.Y() <= GetBottom() + && rPoint.X() >= GetItalicLeft() + && rPoint.X() <= GetItalicRight(); +} + +SmRect SmRect::AsGlyphRect() const +{ + SmRect aRect (*this); + aRect.SetTop(nGlyphTop); + aRect.SetBottom(nGlyphBottom); + return aRect; +} + + +// forward declaration +void SmDrawFrame(OutputDevice &rDev, const Rectangle &rRec, + const Color aCol = COL_BLACK); + +void SmRect::Draw(OutputDevice &rDev, const Point &rPosition, int nFlags) const +{ + if (IsEmpty()) + return; + + rDev.Push(PUSH_LINECOLOR); + + if (nFlags & SM_RECT_LINES) + { long nLeftSpace = 0, + nRightSpace = 0; + + if (nFlags & SM_RECT_ITALIC) + { nLeftSpace = GetItalicLeftSpace(); + nRightSpace = GetItalicRightSpace(); + } + + long nLeft = GetLeft() - nLeftSpace, + nRight = GetRight() + nRightSpace; + + Point aOffset (rPosition - GetTopLeft()); + + rDev.SetLineColor(COL_LIGHTBLUE); + rDev.DrawLine(Point(nLeft, GetAlignB()) += aOffset, + Point(nRight, GetAlignB()) += aOffset); + rDev.DrawLine(Point(nLeft, GetAlignT()) += aOffset, + Point(nRight, GetAlignT()) += aOffset); + if (HasBaseline()) + rDev.DrawLine(Point(nLeft, GetBaseline()) += aOffset, + Point(nRight, GetBaseline()) += aOffset); + + rDev.SetLineColor(COL_GRAY); + rDev.DrawLine(Point(nLeft, GetHiAttrFence()) += aOffset, + Point(nRight, GetHiAttrFence()) += aOffset); + } + + if (nFlags & SM_RECT_MID) + { Point aCenter = rPosition + + (Point(GetItalicCenterX(), GetAlignM()) -= GetTopLeft()), + aLenX (GetWidth() / 5, 0), + aLenY (0, GetHeight() / 16); + + rDev.SetLineColor(COL_LIGHTGREEN); + rDev.DrawLine(aCenter - aLenX, aCenter + aLenX); + rDev.DrawLine(aCenter - aLenY, aCenter + aLenY); + } + + if (nFlags & SM_RECT_ITALIC) + SmDrawFrame(rDev, Rectangle(rPosition - Point(GetItalicLeftSpace(), 0), + GetItalicSize())); + + if (nFlags & SM_RECT_CORE) + SmDrawFrame(rDev, Rectangle(rPosition, GetSize()), COL_LIGHTRED); + + rDev.Pop(); +} + + + +//////////////////////////////////////// +// misc functions +// + + +void SmDrawFrame(OutputDevice &rDev, const Rectangle &rRec, + const Color aCol) +{ + rDev.Push(PUSH_LINECOLOR); + + rDev.SetLineColor(aCol); + + rDev.DrawLine(rRec.TopLeft(), rRec.BottomLeft()); + rDev.DrawLine(rRec.BottomLeft(), rRec.BottomRight()); + rDev.DrawLine(rRec.BottomRight(), rRec.TopRight()); + rDev.DrawLine(rRec.TopRight(), rRec.TopLeft()); + + rDev.Pop(); +} + + +BOOL SmGetGlyphBoundRect(const OutputDevice &rDev, + const XubString &rText, Rectangle &rRect) + // basically the same as 'GetGlyphBoundRect' (in class 'OutputDevice') + // but with a string as argument. +{ + // handle special case first + xub_StrLen nLen = rText.Len(); + if (nLen == 0) + { rRect.SetEmpty(); + return TRUE; + } + + // get a device where 'OutputDevice::GetGlyphBoundRect' will be successful + OutputDevice *pGlyphDev; + if (rDev.GetOutDevType() != OUTDEV_PRINTER) + pGlyphDev = (OutputDevice *) &rDev; + else + { + // since we format for the printer (where GetGlyphBoundRect will fail) + // we need a virtual device here. + pGlyphDev = SM_MOD1()->GetRectCache()->GetVirDev(); + } + + const FontMetric aDevFM (rDev.GetFontMetric()); + + pGlyphDev->Push(PUSH_FONT); + pGlyphDev->SetFont(rDev.GetFont()); + //! Da in der FontMetric die Weite immer != 0 ist (was fuer wide-Attribute + //! und skalierbare Klammern auch so bentigt wird) kann dies zu einer + //! Verzerrung der Proportionen im 'pGlyphDev' gegnber dem 'rDev' kommen! + + const BOOL bOptimize = FALSE; + BOOL bSuccess = TRUE; + Point aPoint; + Rectangle aResult (aPoint, Size(rDev.GetTextWidth(rText), rDev.GetTextHeight())), + aTmp; + long nDelta; + + // setzen des linken Randes (dabei Leerzeichen erhalten!) + xub_Unicode cChar = rText.GetChar(0); + if (cChar != xub_Unicode(' ')) + { + bSuccess &= pGlyphDev->GetGlyphBoundRect(cChar, aTmp, bOptimize); + if (!aTmp.IsEmpty()) + { + // linken Rand am 'rDev' ermitteln + // (wir nehmen den linken Rand bezglich 'pGlyphDev' und skalieren + // ihn passen fr 'rDev') + long nLeftSpace = aTmp.Left() * rDev.GetTextWidth(cChar) + / pGlyphDev->GetTextWidth(cChar); + aResult.Left() += nLeftSpace; + } + } + + // setzen des rechten Randes (dabei Leerzeichen erhalten!) + cChar = rText.GetChar(nLen - 1); + if (cChar != xub_Unicode(' ')) + { + bSuccess &= pGlyphDev->GetGlyphBoundRect(cChar, aTmp, bOptimize); + if (!aTmp.IsEmpty()) + { + // rechten Rand am 'rDev' ermitteln (analog wie beim linken Rand) + long nGlyphWidth = pGlyphDev->GetTextWidth(cChar), + nRightSpace = (nGlyphWidth - 1 - aTmp.Right()) + * rDev.GetTextWidth(cChar) + / nGlyphWidth; + aResult.Right() -= nRightSpace; + } + } + + // oberen und unteren Rand bestimmen. + // Im Augenblick gehen wird davon aus, da die Texthhen an den beiden + // Devices im wesentlichen gleich sind und skalieren diese Rnder daher + // nicht um. + long nTop = aResult.Bottom() + 1, + nBottom = aResult.Top() - 1; + for (USHORT i = 0; i < nLen; i++) + { + cChar = rText.GetChar(i); + if (cChar != xub_Unicode(' ')) + { + //! Anmerkung: Leerzeichen *knnen* leere Rechtecke ergeben, aber + //! der Returnwert sollte auch dann TRUE sein. + bSuccess &= pGlyphDev->GetGlyphBoundRect(cChar, aTmp, bOptimize); + + if (!aTmp.IsEmpty() && aTmp.Top() < nTop) + nTop = aTmp.Top(); + if (!aTmp.IsEmpty() && aTmp.Bottom() > nBottom) + nBottom = aTmp.Bottom(); + } + } + aResult.Top() = nTop; + aResult.Bottom() = nBottom; + + // move rectangle to match possibly different baselines + // (because of different devices) + nDelta = aDevFM.GetAscent() - pGlyphDev->GetFontMetric().GetAscent(); + aResult.Move(0, nDelta); + + rRect = aResult; + pGlyphDev->Pop(); + return bSuccess; +} + + |