906 lines
28 KiB
C++
906 lines
28 KiB
C++
/*************************************************************************
|
||
*
|
||
* $RCSfile: rect.cxx,v $
|
||
*
|
||
* $Revision: 1.4 $
|
||
*
|
||
* last change: $Author: tl $ $Date: 2001-08-03 13:54:04 $
|
||
*
|
||
* 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(0xE070), MS_EMPTYSET,
|
||
xub_Unicode(0x2113), xub_Unicode(0xE0D6), xub_Unicode(0x2107),
|
||
xub_Unicode(0x2127), xub_Unicode(0x210A), MS_HBAR,
|
||
MS_LAMBDABAR, MS_SETN, MS_SETZ,
|
||
MS_SETQ, MS_SETR, MS_SETC,
|
||
xub_Unicode(0x2373), xub_Unicode(0xE0A5), xub_Unicode(0x2112),
|
||
xub_Unicode(0x2130), xub_Unicode(0x2131),
|
||
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 enth<74>lt nicht genau ein Zeichen");
|
||
xub_Unicode cChar = rText.GetChar(0);
|
||
|
||
// ist es ein griechisches Zeichen ?
|
||
if (xub_Unicode(0xE0AC) <= cChar && cChar <= xub_Unicode(0xE0D4))
|
||
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( FONTNAME_MATH );
|
||
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)
|
||
{
|
||
OutputDevice *pWindow = Application::GetDefaultDevice();
|
||
|
||
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
|
||
// Fonth<74>he 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)
|
||
{
|
||
// f<>r 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 ben<65>tigt wird) kann dies zu einer
|
||
//! Verzerrung der Proportionen im 'pGlyphDev' gegn<67>ber 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 bez<65>glich 'pGlyphDev' und skalieren
|
||
// ihn passen f<>r '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<64> die Texth<74>hen an den beiden
|
||
// Devices im wesentlichen gleich sind und skalieren diese R<>nder 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 *k<>nnen* 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;
|
||
}
|
||
|
||
|