summaryrefslogtreecommitdiff
path: root/Build/source/libs/graphite/src/painter/SegmentPainter.cpp
diff options
context:
space:
mode:
authorPeter Breitenlohner <peb@mppmu.mpg.de>2009-03-19 13:59:30 +0000
committerPeter Breitenlohner <peb@mppmu.mpg.de>2009-03-19 13:59:30 +0000
commit0b7b2c69ec83497d9accab064da0f4ad07662d42 (patch)
treee8f22a2acecbf3035850f56beb1c8161c53cdf2d /Build/source/libs/graphite/src/painter/SegmentPainter.cpp
parentee507bbd1fc483c58acb33cca25509a4dd81df9b (diff)
prepare for new build system
git-svn-id: svn://tug.org/texlive/trunk@12429 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/libs/graphite/src/painter/SegmentPainter.cpp')
-rw-r--r--Build/source/libs/graphite/src/painter/SegmentPainter.cpp2986
1 files changed, 2986 insertions, 0 deletions
diff --git a/Build/source/libs/graphite/src/painter/SegmentPainter.cpp b/Build/source/libs/graphite/src/painter/SegmentPainter.cpp
new file mode 100644
index 00000000000..ee92cbe8caf
--- /dev/null
+++ b/Build/source/libs/graphite/src/painter/SegmentPainter.cpp
@@ -0,0 +1,2986 @@
+/*--------------------------------------------------------------------*//*:Ignore this sentence.
+Copyright (C) 1999, 2001 SIL International. All rights reserved.
+
+Distributable under the terms of either the Common Public License or the
+GNU Lesser General Public License, as specified in the LICENSING.txt file.
+
+File: SegmentPainter.cpp
+Responsibility: Sharon Correll
+Last reviewed: Not yet.
+
+Description:
+ Implements the object that handles drawing, mouse clicks, and other UI-related behavior
+ for a Graphite segment.
+-------------------------------------------------------------------------------*//*:End Ignore*/
+
+//:>********************************************************************************************
+//:> Include files
+//:>********************************************************************************************
+#include "Main.h"
+#ifdef _MSC_VER
+#pragma hdrstop
+#endif
+// any other headers (not precompiled)
+#include <math.h>
+#ifndef _WIN32
+#include <stdlib.h>
+#endif
+
+#undef THIS_FILE
+DEFINE_THIS_FILE
+
+//:>********************************************************************************************
+//:> Forward declarations
+//:>********************************************************************************************
+
+//:>********************************************************************************************
+//:> Local Constants and static variables
+//:>********************************************************************************************
+namespace
+{
+
+static bool g_fDrawing;
+
+} // namespace
+
+
+namespace gr
+{
+
+//:>********************************************************************************************
+//:> Methods
+//:>********************************************************************************************
+
+/*----------------------------------------------------------------------------------------------
+ Constructors.
+----------------------------------------------------------------------------------------------*/
+//SegmentPainter::SegmentPainter(Segment * pseg)
+//{
+// m_pseg = pseg;
+//
+// m_xsOrigin = 0;
+// m_ysOrigin = 0;
+// m_xdPosition = 0;
+// m_ydPosition = 0;
+// m_xFactor = 1.0;
+// m_yFactor = 1.0;
+//}
+
+SegmentPainter::SegmentPainter(Segment * pseg, float xsOrigin, float ysOrigin)
+{
+ m_pseg = pseg;
+
+ m_xsOrigin = xsOrigin;
+ m_ysOrigin = ysOrigin;
+ m_xdPosition = m_xsOrigin;
+ m_ydPosition = m_ysOrigin;
+ m_xFactor = 1.0;
+ m_yFactor = 1.0;
+
+ //m_xsOriginInt = GrEngine::RoundFloat(xsOrigin);
+ //m_xdPositionInt = m_xsOriginInt;
+ //m_xFactor64 = 64;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Destructor.
+----------------------------------------------------------------------------------------------*/
+SegmentPainter::~SegmentPainter()
+{
+}
+
+/*----------------------------------------------------------------------------------------------
+ Initialization.
+
+ The correspondence with the old rectangle approach is:
+ m_xsOrigin = sourceRect.left;
+ m_ysOrigin = sourceRect.top;
+ m_xdPosition = destRect.left;
+ m_ydPosition = destRect.top;
+
+ m_xFactor = (destRect.right - destRect.left) / (sourceRect.right - sourceRect.left);
+ m_yFactor = (destRect.top - destRect.bottom) / (sourceRect.top - sourceRect.bottom);
+
+----------------------------------------------------------------------------------------------*/
+void SegmentPainter::setOrigin(float xsOrigin, float ysOrigin)
+{
+ m_xsOrigin = xsOrigin;
+ m_ysOrigin = ysOrigin;
+
+ //m_xsOriginInt = GrEngine::RoundFloat(xsOrigin);
+}
+
+void SegmentPainter::setPosition(float xdPosition, float ydPosition)
+{
+ m_xdPosition = xdPosition;
+ m_ydPosition = ydPosition;
+
+ //m_xdPositionInt = GrEngine::RoundFloat(xdPosition);
+}
+
+void SegmentPainter::setScalingFactors(float xFactor, float yFactor)
+{
+ m_xFactor = xFactor;
+ m_yFactor = yFactor;
+
+ //int temp = GrEngine::RoundFloat(xFactor);
+ //m_xFactor64 = GrEngine::RoundFloat(xFactor * 64);
+}
+
+
+//:>********************************************************************************************
+//:> SegmentPainter interface methods
+//:>********************************************************************************************
+
+/*----------------------------------------------------------------------------------------------
+ Draw the text of the segment at the specified point in the graphics context.
+ This method must be implemented by a concrete subclass for painters that really need
+ to paint.
+----------------------------------------------------------------------------------------------*/
+//void SegmentPainter::paint()
+//{
+// Assert(false);
+//}
+
+/*----------------------------------------------------------------------------------------------
+ Indicate if the character position is a valid place for an insertion point.
+
+ @oaram ich - position to test
+ @param pipvr - answer: kipvrOK = IP here is valid;
+ kipvrBad = IP here no good;
+ kipvrUnknown = this seg can't decide, ask the next
+----------------------------------------------------------------------------------------------*/
+LgIpValidResult SegmentPainter::isValidInsertionPoint(int ichw)
+{
+ Assert(m_pseg->m_dxsWidth >= 0);
+ Assert(m_pseg->m_dysAscent >= 0);
+
+ int ichwSegOffset = ichw - m_pseg->m_ichwMin;
+ if (ichwSegOffset < m_pseg->m_ichwAssocsMin || ichwSegOffset >= m_pseg->m_ichwAssocsLim)
+ {
+ // If we're pointing at a hard line break character (Unicode 2028), then we know
+ // that it's a good insertion point in spite of missing the other conditions.
+ utf16 ch;
+ m_pseg->m_pgts->fetch(ichw, 1, &ch);
+ if (ch != knLineSep)
+ return kipvrUnknown; // Definitely in a different segment of the paragraph.
+ return kipvrOK;
+ }
+
+ int isloutLig = m_pseg->m_prgisloutLigature[ichwSegOffset - m_pseg->m_ichwAssocsMin];
+ if (isloutLig != kNegInfinity)
+ {
+ return kipvrOK;
+ }
+
+ // TODO: do we need to test ALL the associated glyphs? Here we just test the first.
+
+ LgIpValidResult ipvrRet;
+
+ int isloutBefore = m_pseg->UnderlyingToLogicalSurface(ichw, true);
+ int isloutAfter = m_pseg->UnderlyingToLogicalSurface(ichw - 1, false);
+ int isloutTest = m_pseg->UnderlyingToLogicalInThisSeg(ichw);
+ if (isloutBefore == isloutAfter)
+ // If inserting before this character yields the same visible result as
+ // inserting after the previous, then this character is invisible, or has
+ // been merged with the previous, and it doesn't make sense to put an insertion
+ // point here.
+ ipvrRet = kipvrBad;
+ else if (isloutTest == kNegInfinity || isloutTest == kPosInfinity)
+ ipvrRet = kipvrBad; // can't figure it out
+ else if (!m_pseg->OutputSlot(isloutTest)->InsertBefore())
+ ipvrRet = kipvrBad;
+ else if (m_pseg->SameSurfaceGlyphs(ichw - 1, ichw))
+ // The insertion point would be between two characters that map to the same
+ // set of glyphs. Not a good place.
+ ipvrRet = kipvrBad;
+ else
+ ipvrRet = kipvrOK;
+
+ return ipvrRet;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Answer whether the logical and physical boundaries of the string coincide.
+ This method is called by the application to handle writing system boundaries, and by
+ other segments in a writing-system chain to handle line-breaks.
+
+ @param fBoundaryEnd - asking about the logical end boundary?
+ @param fBoundaryRight - asking about the physical right boundary?
+ @param pfResult - return value
+----------------------------------------------------------------------------------------------*/
+bool SegmentPainter::doBoundariesCoincide(bool fBoundaryEnd, bool fBoundaryRight)
+{
+ float xsBefore, ysBeforeTop, ysBeforeBottom;
+ float xsAfter, ysAfterTop, ysAfterBottom;
+ bool fRtlBefore, fRtlAfter;
+
+ if (m_pseg->m_cginf == 0)
+ {
+ // Empty segment.
+ return true;
+ }
+
+ //SetUpGraphics(ichwBase, pgg, true);
+
+ Assert(m_pseg->m_dxsWidth >= 0);
+ Assert(m_pseg->m_dysAscent >= 0);
+
+ if (fBoundaryEnd)
+ {
+ CalcIP(m_pseg->m_ichwMin + m_pseg->m_dichwLim, true,
+ &xsBefore, &ysBeforeTop, &ysBeforeBottom, &fRtlBefore);
+ CalcIP(m_pseg->m_ichwMin + m_pseg->m_dichwLim, false,
+ &xsAfter, &ysAfterTop, &ysAfterBottom, &fRtlAfter);
+ }
+ else
+ {
+ CalcIP(m_pseg->m_ichwMin, true,
+ &xsBefore, &ysBeforeTop, &ysBeforeBottom, &fRtlBefore);
+ CalcIP(m_pseg->m_ichwMin, false,
+ &xsAfter, &ysAfterTop, &ysAfterBottom, &fRtlAfter);
+ }
+
+ // We allow for rounding errors which might cause differences between the expected
+ // and the actual values, hence the test for a difference of <= 1.
+ bool fRet;
+ if (fBoundaryRight)
+ {
+ // right edge
+ if (fBoundaryEnd) // logical end
+ fRet = (!fRtlAfter && fabsf(xsAfter - (m_pseg->m_dxsTotalWidth + m_pseg->m_dxsStretch)) <= 1);
+ else // logical start
+ fRet = (fRtlBefore && fabsf(xsBefore - (m_pseg->m_dxsTotalWidth + m_pseg->m_dxsStretch)) <= 1);
+ }
+ else
+ {
+ // left edge
+ if (fBoundaryEnd) // logical end
+ fRet = (fRtlAfter && fabsf(xsAfter) <= 1);
+ else // logical start
+ fRet = (!fRtlBefore && fabsf(xsBefore) <= 1);
+ }
+
+ return fRet;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Draw an insertion point at an appropriate position.
+
+ @param twx, twy - same origin used to Draw segment
+ @param ich - character position; must be valid
+ @param fAssocPrev - associated with previous character?
+ @param bOn - turning on or off? Caller should alternate, first turning on
+ (ignored in this implementation)
+ @param dm - draw mode:
+ kdmNormal = draw complete insertion pt (I-beam or split cursor);
+ kdmPrimary = only draw primary half of split cursor;
+ kdmSecondary = only draw secondary half of split cursor
+----------------------------------------------------------------------------------------------*/
+void SegmentPainter::drawInsertionPoint(int ichwIP, bool fAssocPrev,
+ bool bOn, bool fForceSplit) // LgIPDrawMode dm)
+{
+ if (g_fDrawing)
+ return;
+
+ g_fDrawing = true;
+
+ //SetUpGraphics(ichwBase, pgg, true);
+
+ Assert(m_pseg->m_dxsWidth >= 0);
+ Assert(m_pseg->m_dysAscent >= 0);
+
+ CalcOrDrawInsertionPoint(ichwIP,
+ fAssocPrev, bOn, fForceSplit,
+ NULL, NULL); // passing NULLs here means we want to draw
+
+ //RestoreFont(pgg);
+
+ g_fDrawing = false;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Fill in bounding rectangles for the (possibly 2-part) IP, in destination device coordinates.
+ Return flags indicating which of the 2 parts where rendered here.
+
+ @param rs,rd - source/destination coordinates, for scaling
+ @param ichwIP - insertion point
+ @param fAssocPrev - is the IP "leaning" backward?
+ @param dm - draw mode:
+ kdmNormal = draw complete insertion pt (I-beam or split cursor);
+ kdmPrimary = only draw primary half of split cursor;
+ kdmSecondary = only draw secondary half of split cursor
+ @param prdPrimary - return location of primary selection, in dest coords
+ @param prdSecondary - return location of secondary selection, in dest coords
+----------------------------------------------------------------------------------------------*/
+void SegmentPainter::positionsOfIP(
+ int ichwIP, bool fAssocPrev, bool fForceSplit,
+ Rect * prdPrimary, Rect * prdSecondary)
+{
+ ChkGrArgPtr(prdPrimary);
+ ChkGrArgPtr(prdSecondary);
+
+ //SetUpGraphics(ichwBase, pgg, true);
+
+ Assert(m_pseg->m_dxsWidth >= 0);
+ Assert(m_pseg->m_dysAscent >= 0);
+
+ CalcOrDrawInsertionPoint(ichwIP, fAssocPrev, true, fForceSplit,
+ prdPrimary, prdSecondary);
+
+ //RestoreFont(pgg);
+}
+
+/*----------------------------------------------------------------------------------------------
+ Highlight a range of text.
+
+ @param ichwAnchor/End - selected range
+ @param ydLineTop/Bottom - top/bottom of area to highlight if whole line height;
+ includes half of inter-line spacing.
+ @param bOn - true if we are turning on (ignored in this implementation)
+----------------------------------------------------------------------------------------------*/
+bool SegmentPainter::drawSelectionRange(int ichwAnchor, int ichwEnd,
+ float ydLineTop, float ydLineBottom, bool bOn)
+{
+ if (g_fDrawing)
+ return true;
+
+ g_fDrawing = true;
+
+ //SetUpGraphics(ichwBase, pgg, true);
+
+ Assert(m_pseg->m_dxsWidth >= 0);
+ Assert(m_pseg->m_dysAscent >= 0);
+
+ float xsSegLeft = 0;
+ float ysSegTop = 0;
+
+ float xdSegRight = ScaleXToDest(m_pseg->m_dxsTotalWidth);
+
+ int ichwMinSel = min(ichwAnchor, ichwEnd);
+ int ichwLimSel = max(ichwAnchor, ichwEnd);
+
+ int ichwMinSeg = max(ichwMinSel, m_pseg->m_ichwMin + m_pseg->m_ichwAssocsMin);
+ int ichwLimSeg = min(ichwLimSel, m_pseg->m_ichwMin + m_pseg->m_ichwAssocsLim);
+
+ std::vector<Rect> vrs;
+ std::vector<bool> vfEntireHt;
+
+ bool * prgfAllCompsSelected = new bool[m_pseg->m_dichwLim];
+ std::fill_n(prgfAllCompsSelected, m_pseg->m_dichwLim, false);
+ CalcPartialLigatures(prgfAllCompsSelected, ichwMinSeg, ichwLimSeg,
+ ichwMinSel, ichwLimSel);
+
+ // Array to keep track of which glyphs were highlighted.
+ int cginf = m_pseg->m_cginf;
+ bool * prgfHighlighted = new bool[cginf];
+ std::fill_n(prgfHighlighted, cginf, false);
+
+ for (int ichwLp = ichwMinSeg; ichwLp < ichwLimSeg; ichwLp++)
+ {
+ CalcHighlightRect(ichwLp, vrs, vfEntireHt,
+ !prgfAllCompsSelected[ichwLp - m_pseg->m_ichwMin], prgfHighlighted, false);
+ }
+
+ // If all the glyphs of a given cluster were highlighted, highlight the whole cluster
+ // as a unit.
+ for (int islout = 0; islout < m_pseg->m_cslout; islout++)
+ {
+ CalcCompleteCluster(islout, vrs, vfEntireHt, prgfHighlighted);
+ }
+
+ // Convert to destination coordinates; also extend full-line rectangles to the top
+ // and bottom of the line height.
+ std::vector<Rect> vrd;
+ for (size_t irs = 0; irs < vrs.size(); irs++)
+ {
+ Rect rsTmp = vrs[irs];
+ Rect rdTmp;
+ if (vfEntireHt[irs])
+ {
+ rdTmp.top = ydLineTop;
+ rdTmp.bottom = ydLineBottom;
+ }
+ else
+ {
+ float ydTmp = ScaleYToDest(rsTmp.top + ysSegTop);
+ rdTmp.top = min(ydLineBottom, max(ydLineTop, ydTmp));
+ ydTmp = ScaleYToDest(rsTmp.bottom + ysSegTop);
+ rdTmp.bottom = max(ydLineTop, min(ydLineBottom, ydTmp));
+ }
+ float xdTmp = ScaleXToDest(rsTmp.left + xsSegLeft);
+ rdTmp.left = min(xdSegRight, max(float(0), xdTmp));
+ xdTmp = ScaleXToDest(rsTmp.right + xsSegLeft);
+ rdTmp.right = max(float(0), min(xdSegRight, xdTmp));
+ if (rdTmp.left > rdTmp.right)
+ {
+ // I think this can happen due to rounding errors.
+ float tmp = rdTmp.left;
+ rdTmp.left = rdTmp.right;
+ rdTmp.right = tmp;
+ }
+ vrd.push_back(rdTmp);
+ }
+
+ // Create a list of rectangles with no overlaps, so the selection draws cleanly.
+ std::vector<Rect> vrdNoOverlaps;
+ size_t irect;
+ for (irect = 0; irect < vrd.size(); irect++)
+ AddRectWithoutOverlaps(vrdNoOverlaps, vrd[irect]);
+
+ AssertNoOverlaps(vrdNoOverlaps);
+
+ // Draw the hightlight.
+ for (irect = 0; irect < vrdNoOverlaps.size(); irect++)
+ {
+ Rect rdTmp = vrdNoOverlaps[irect];
+ InvertRect(rdTmp.left, rdTmp.top, rdTmp.right, rdTmp.bottom);
+ }
+
+ delete[] prgfAllCompsSelected;
+ delete[] prgfHighlighted;
+
+ //RestoreFont(pgg);
+
+ g_fDrawing = false;
+
+ return (vrdNoOverlaps.size() > 0);
+}
+
+/*----------------------------------------------------------------------------------------------
+ Get a bounding rectangle that will contain the area highlighted by this segment
+ when drawing the specified range.
+
+ @param ichwAnchor/End - selected range
+ @param ydLineTop/Bottom - top/bottom of area to highlight if whole line height;
+ includes half of inter-line spacing.
+ @param prdBounds - return location of range
+ @return - true if the return rectange has a meaningful value
+----------------------------------------------------------------------------------------------*/
+bool SegmentPainter::positionsOfRange(int ichwAnchor, int ichwEnd,
+ float ydLineTop, float ydLineBottom, Rect * prdBounds)
+{
+ ChkGrArgPtr(prdBounds);
+
+ std::vector<Rect> vrs;
+ std::vector<bool> vfEntireHt;
+
+ //SetUpGraphics(ichwBase, pgg, true);
+
+ Assert(m_pseg->m_dxsWidth >= 0);
+ Assert(m_pseg->m_dysAscent >= 0);
+
+ int ichwMin = min(ichwAnchor, ichwEnd);
+ int ichwLim = max(ichwAnchor, ichwEnd);
+
+ for (int ichwLp = ichwMin; ichwLp < ichwLim; ichwLp++)
+ {
+ CalcHighlightRect(ichwLp, vrs, vfEntireHt, true, NULL, false);
+ }
+
+ if (vrs.size() == 0)
+ {
+ return false;
+ }
+
+ Rect * prsBounds = &(vrs[0]);
+ for (size_t irect = 1; irect < vrs.size(); irect++)
+ {
+ Rect * prs = &(vrs[irect]);
+ prsBounds->left = min(prsBounds->left, prs->left);
+ prsBounds->right = max(prsBounds->right, prs->right);
+ }
+
+ prdBounds->top = ydLineTop;
+ prdBounds->bottom = ydLineBottom;
+ prdBounds->left = ScaleXToDest(prsBounds->left);
+ prdBounds->right = ScaleXToDest(prsBounds->right);
+
+ return true;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Convert a click position to a character position.
+
+ @param zptdClickPosition - relative to the segment draw origin
+ @param pichw - return character clicked before
+ @param pfAssocPrev - return true if they clicked on the trailing half of
+ the previous char, false if click was on the
+ leading half of the following
+----------------------------------------------------------------------------------------------*/
+void SegmentPainter::pointToChar(Point zptdClickPosition, int * pichw, bool * pfAssocPrev)
+{
+ ChkGrOutPtr(pichw);
+ ChkGrOutPtr(pfAssocPrev);
+
+ //SetUpGraphics(ichwBase, pgg, true);
+
+ bool fRtl = m_pseg->rightToLeft();
+
+ if (m_pseg->m_dysAscent < 0 || m_pseg->m_dxsWidth < 0)
+ m_pseg->ComputeDimensions();
+
+ if (m_pseg->m_dichwLim == 0 || m_pseg->m_cginf == 0)
+ {
+ *pichw = m_pseg->m_ichwMin;
+ *pfAssocPrev = false;
+ //RestoreFont(pgg);
+ return;
+ }
+
+ float xsClick = ScaleXToSource(zptdClickPosition.x);
+ float ysClick = ScaleYToSource(zptdClickPosition.y);
+ ysClick = m_pseg->m_dysAscent - ysClick; // make relative to common base line
+
+ float xsTry = xsClick;
+ float ysTry = ysClick;
+
+ float xysInc = 1.0;
+ //res = pgg->LogUnitsFromXPixels(1, &xysInc);
+ //if (ResultFailed(res)) xysInc = 20;
+ int cTries = 1;
+ int nAdjustDir = 0;
+
+ while (true)
+ {
+ float xsGlyphOffset;
+ float xsGlyphWidth;
+ int iginfLoc = PointToCharAux(xsTry, ysTry, &xsGlyphOffset, &xsGlyphWidth);
+
+ bool fClickedAfter;
+ int ichwCharClicked;
+ if (xsTry < m_pseg->m_prgginf[m_pseg->LeftMostGlyph()].bb().left)
+ {
+ fClickedAfter = fRtl;
+ ichwCharClicked = (fClickedAfter) ?
+ m_pseg->m_ichwMin + m_pseg->m_ichwAssocsLim - 1 :
+ m_pseg->m_ichwMin + m_pseg->m_ichwAssocsMin;
+ }
+ else if (xsTry > m_pseg->m_prgginf[m_pseg->RightMostGlyph()].bb().right)
+ {
+ fClickedAfter = !fRtl;
+ ichwCharClicked = (fClickedAfter) ?
+ m_pseg->m_ichwMin + m_pseg->m_ichwAssocsLim - 1 :
+ m_pseg->m_ichwMin + m_pseg->m_ichwAssocsMin;
+ }
+ else
+ {
+ ichwCharClicked = m_pseg->PhysicalSurfaceToUnderlying(iginfLoc,
+ xsTry - xsGlyphOffset, m_pseg->m_dysAscent - ysClick,
+ xsGlyphWidth, m_pseg->m_dysHeight, &fClickedAfter); // handles ligature components
+ }
+
+ if (fClickedAfter)
+ {
+ *pichw = ichwCharClicked + 1;
+ while (!GrCharStream::AtUnicodeCharBoundary(m_pseg->GetString(), *pichw))
+ *pichw = *pichw + 1;
+ *pfAssocPrev = true;
+ }
+ else
+ {
+ *pichw = ichwCharClicked;
+ *pfAssocPrev = false;
+ }
+
+ if (*pichw < m_pseg->m_ichwMin + m_pseg->m_ichwAssocsMin)
+ {
+ //RestoreFont(pgg);
+ return;
+ }
+ else if (*pichw >= m_pseg->m_ichwMin + m_pseg->m_ichwAssocsLim)
+ {
+ //RestoreFont(pgg);
+ return;
+ }
+
+ //int islout = UnderlyingToLogicalInThisSeg(ichwBase, ichwCharClicked);
+ int isloutBefore = m_pseg->UnderlyingToLogicalInThisSeg(*pichw);
+ if (isloutBefore == kNegInfinity || isloutBefore == kPosInfinity)
+ { }
+ else
+ {
+ Assert(0 <= isloutBefore && isloutBefore < m_pseg->m_cslout);
+ if (m_pseg->OutputSlot(isloutBefore)->InsertBefore())
+ {
+ // Legal insertion point
+ //RestoreFont(pgg);
+ return;
+ }
+ }
+
+ // Gradually adjust the click position in various directions and try again
+ // until we find a legal insertion point.
+ switch (nAdjustDir)
+ {
+ case 0: // Adjust to the left
+ case 4:
+ xsTry = xsClick - (xysInc * cTries);
+ ysTry = ysClick;
+ nAdjustDir = 0;
+ break;
+ case 1: // Adjust to the right
+ xsTry = xsClick + (xysInc * cTries);
+ ysTry = ysClick;
+ break;
+ case 2: // Adjust up
+ xsTry = xsClick;
+ ysTry = ysClick - (xysInc * cTries);
+ break;
+ case 3: // Adjust down
+ xsTry = xsClick;
+ ysTry = ysClick + (xysInc * cTries);
+ cTries++;
+ break;
+ default:
+ Assert(false);
+ nAdjustDir = 0;
+ cTries++;
+ }
+ nAdjustDir++;
+
+ if (cTries > 50)
+ { // Safety net
+ *pichw = 0;
+ *pfAssocPrev = false;
+ THROW(kresFail);
+ }
+ else if (cTries > 20)
+ xysInc = xysInc * 2;
+ }
+
+ //RestoreFont(pgg);
+}
+
+/*----------------------------------------------------------------------------------------------
+ Convert a click position to a glyph bounding-box position.
+----------------------------------------------------------------------------------------------*/
+int SegmentPainter::PointToCharAux(float xsTry, float ysTry,
+ float * pxsGlyphOffset, float * pxsGlyphWidth)
+{
+ int iginfLoc = m_pseg->GlyphHit(xsTry, ysTry);
+
+ GlyphInfo * pginf = m_pseg->m_prgginf + iginfLoc;
+ *pxsGlyphOffset = pginf->bb().left;
+ *pxsGlyphWidth = pginf->bb().right - *pxsGlyphOffset;
+
+ return iginfLoc;
+}
+
+// Review: may need a way to do this and subsequent methods with one result per method?
+/*----------------------------------------------------------------------------------------------
+ Returns a value what logical position an arrow key should move the IP to.
+
+ @param ichwIP - initial position
+ @param pfAssocPrev - is the IP "leaning" backwards? input and output
+ @param fRight - direction of desired movement (physical or logical?)
+ @param pfInThisSeg - initially true if ichwIP is in the current segment;
+ return value is true if there is a legitimate output
+----------------------------------------------------------------------------------------------*/
+int SegmentPainter::arrowKeyPosition(int ichwIP, bool * pfAssocPrev,
+ bool fRight, bool * pfInThisSeg)
+//GrResult SegmentPainter::arrowKeyPosition(
+// int * pichwIP, bool * pfAssocPrev,
+// bool fRight, bool fMovingIn,
+// bool * pfResult)
+{
+ ChkGrArgPtr(pfAssocPrev);
+ //ChkGrOutPtr(pfInThisSeg);
+
+ bool fMovingIn = !*pfInThisSeg;
+ int ichwTmp = ichwIP;
+ bool fResult;
+ ArrowKeyPositionAux(&ichwTmp, pfAssocPrev, fRight, fMovingIn, false, false, &fResult);
+ *pfInThisSeg = fResult;
+ return ichwTmp;
+
+ //return ArrowKeyPositionAux(pichwIP, pfAssocPrev,
+ // fRight, fMovingIn, false, false, pfResult);
+}
+
+/*----------------------------------------------------------------------------------------------
+ Indicate what logical position a shift-arrow-key combination should move the
+ end of the selection to.
+
+ @param pichw - initial endpoint and also adjusted result
+ @param fAssocPrevMatch - true if we need a certain orientation for the result;
+ should be false when *pichw = 0 and we are moving within the
+ same segment
+ @param fAssocPrevNeeded - association of the end-point, ie, true if it follows the anchor
+ @param ichAnchor - -1 if anchor is in a different segment
+ @param fRight - direction of desired movement
+ @param fMovingIn - true if we are moving in to this segment;
+ if so, initial pichw meaningless
+ @param pfRet - if false try next seg or string
+----------------------------------------------------------------------------------------------*/
+int SegmentPainter::extendSelectionPosition(int ichwEnd, bool fAssocPrevMatch, bool fAssocPrevNeeded,
+ int ichwAnchor, bool fRight, bool * pfInThisSeg)
+//GrResult SegmentPainter::extendSelectionPosition(
+// int * pichw, bool fAssocPrevMatch, bool fAssocPrevNeeded, int ichwAnchor,
+// bool fRight, bool fMovingIn,
+// bool* pfRet)
+{
+ //ChkGrArgPtr(pichw);
+ //ChkGrOutPtr(pfRet);
+
+ // Make the same adjustment as for an insertion point.
+ bool fAssocPrevCopy = fAssocPrevMatch;
+ bool fMovingIn = !*pfInThisSeg;
+ int ichwOld = ichwEnd;
+ int ichwNew = ichwEnd;
+ bool fResult;
+ ArrowKeyPositionAux(&ichwNew, &fAssocPrevCopy,
+ fRight, fMovingIn, fAssocPrevMatch, fAssocPrevNeeded, &fResult);
+ *pfInThisSeg = fResult;
+
+ //int ichwNew = *pichw;
+ //bool fAssocPrevCopy = fAssocPrevMatch;
+ //// TODO: adjust fAssocPrevCopy based on assoc-prev value of IP
+ //*pfRet = false;
+ //int ichwOld = *pichw;
+ //GrResult res = ArrowKeyPositionAux(&ichwNew, &fAssocPrevCopy,
+ // fRight, fMovingIn,
+ // fAssocPrevMatch, fAssocPrevNeeded,
+ // pfRet);
+
+// if (*pfRet && ichwNew == *pichw)
+// {
+// // The selection wasn't actually moved, just switched the direction it leans.
+// // Try one more time.
+// res = ArrowKeyPositionAux(&ichwNew, &fAssocPrevCopy,
+// fRight, fMovingIn,
+// (ichwAnchor != *pichw && fAssocPrevNeeded), fAssocPrevNeeded,
+// pfRet);
+// }
+ if (ichwAnchor != -1 && fResult &&
+ ((ichwNew < ichwAnchor && ichwAnchor < ichwOld) ||
+ (ichwOld < ichwAnchor && ichwAnchor < ichwNew)))
+ {
+ // Don't switch directions in one operation--force to an insertion point.
+ ichwNew = ichwAnchor;
+ }
+ return ichwNew;
+ //*pichw = ichwNew;
+ //ReturnResult(res);
+}
+
+/*----------------------------------------------------------------------------------------------
+ Handle arrow key movement for both plain and shifted arrows.
+
+ @param pichw - initial endpoint and also adjusted result
+ @param pfAssocPrev - association of the end-point, ie, true if it follows the anchor
+ @param fRight - direction of desired movement
+ @param fMovingIn - true if we are moving in to this segment;
+ if so, initial pichw meaningless
+ @param fAssocPrevMatch - true if the direction of the result should
+ match the requested orientation, to avoid flipping around and
+ infinite loops
+ @param fAssocPrevNeeded - what is needed for the result, if fAssocPrevMatch == true
+ @param pfRet - if false try next segment or string
+----------------------------------------------------------------------------------------------*/
+GrResult SegmentPainter::ArrowKeyPositionAux(
+ int * pichwIP, bool * pfAssocPrev,
+ bool fRight, bool fMovingIn,
+ bool fAssocPrevMatch, bool fAssocPrevNeeded,
+ bool * pfResult)
+{
+ ChkGrArgPtr(pfResult);
+
+ //bool fAssocPrevTmp = *pfAssocPrev;
+
+ Assert(m_pseg->m_dxsWidth >= 0);
+ Assert(m_pseg->m_dysAscent >= 0);
+
+ int nNextOrPrevSeg;
+ if (!fMovingIn)
+ {
+ *pfResult = ArrowKeyPositionInternal(pichwIP, pfAssocPrev, fRight,
+ fAssocPrevMatch, fAssocPrevNeeded, &nNextOrPrevSeg);
+ ReturnResult(kresOk);
+ }
+ else
+ {
+ int ichw;
+ bool fAssocPrevNew;
+ if (fRight)
+ {
+ // Moving right: make selection at left edge.
+ int iginf = m_pseg->LeftMostGlyph();
+ bool fBefore = !m_pseg->rightToLeft();
+ ichw = m_pseg->PhysicalSurfaceToUnderlying(iginf, fBefore) +
+ ((fBefore) ? 0 : 1);
+ fAssocPrevNew = !fBefore; // Review: should it be based on the char dir?
+ }
+ else
+ {
+ // Moving left: make selection at right edge;
+ int iginf = m_pseg->RightMostGlyph();
+ bool fBefore = m_pseg->rightToLeft();
+ ichw = m_pseg->PhysicalSurfaceToUnderlying(iginf, fBefore) +
+ ((fBefore) ? 0 : 1);
+ fAssocPrevNew = !fBefore; // Review: should it be based on the char dir?
+ }
+
+ // Okay, now we have a selection at the edge of the segment. Try moving inside by
+ // one glyph.
+ *pfResult = ArrowKeyPositionInternal(&ichw, &fAssocPrevNew, fRight,
+ fAssocPrevMatch, fAssocPrevNeeded, &nNextOrPrevSeg);
+ if (*pfResult)
+ {
+ *pichwIP = ichw;
+ *pfAssocPrev = fAssocPrevNew;
+ }
+
+ }
+
+ ReturnResult(kresOk);
+}
+
+/*----------------------------------------------------------------------------------------------
+ Used to find out where underlines should be drawn.
+
+ @param ichwMin/Lim - range of text of interest
+ @param rs, rd - source/destination coordinates, for scaling
+ @param fSkipSpace - true if white space should not be underlined; some renderers may
+ ignore this
+ @param cxdMax - number of ranges allowed
+ @param prgxdLefts/Rights/UnderTops
+ - arrays of corresponding values indicating where an underline
+ should be drawn, in logical order
+
+ TODO: fix the bug where if you have trailing spaces in different segments (eg,
+ at different sizes), the spaces all but the last segment will be erroneously included
+ in the underline result.
+----------------------------------------------------------------------------------------------*/
+size_t SegmentPainter::getUnderlinePlacement(int ichwMin, int ichwLim,
+ bool fSkipSpace, size_t cxdMax,
+ float * prgxdLefts, float * prgxdRights, float * prgydUnderTops)
+{
+ ChkGrArrayArg(prgxdLefts, cxdMax);
+ ChkGrArrayArg(prgxdRights, cxdMax);
+ //ChkGrArrayArg(prgydUnderTops, cxdMax);
+
+ GrResult res = kresOk;
+
+ //SetUpGraphics(ichwBase, pgg, true);
+
+ Assert(m_pseg->m_dxsWidth >= 0);
+ Assert(m_pseg->m_dysAscent >= 0);
+
+ float xsSegLeft = m_pseg->m_dxsTotalWidth;
+ for (int iginf = 0; iginf < m_pseg->m_cginf; iginf++)
+ {
+ xsSegLeft = min(xsSegLeft, m_pseg->GlyphLeftEdge(iginf));
+ }
+
+ float xdSegLeft = this->ScaleXToDest(xsSegLeft);
+ float xdSegRight = xdSegLeft + m_pseg->m_dxsTotalWidth;
+
+ int ichwMinSel = min(ichwMin, ichwLim);
+ int ichwLimSel = max(ichwMin, ichwLim);
+
+ int ichwMinSeg = max(ichwMinSel, m_pseg->m_ichwMin + m_pseg->m_ichwAssocsMin);
+ int ichwLimSeg = min(ichwLimSel, m_pseg->m_ichwMin + m_pseg->m_ichwAssocsLim);
+
+ std::vector<Rect> vrs;
+ std::vector<bool> vfEntireHt;
+
+ bool * prgfAllCompsSelected = new bool[m_pseg->m_dichwLim];
+ std::fill_n(prgfAllCompsSelected, m_pseg->m_dichwLim, false);
+ CalcPartialLigatures(prgfAllCompsSelected, ichwMinSeg, ichwLimSeg,
+ ichwMinSel, ichwLimSel);
+
+ // Array to keep track of which glyphs were highlighted:
+ int cginf = m_pseg->m_cginf;
+ bool * prgfHighlighted = new bool[cginf];
+ std::fill_n(prgfHighlighted, cginf, false);
+
+ for (int ichwLp = ichwMinSeg; ichwLp < ichwLimSeg; ichwLp++)
+ {
+ CalcHighlightRect(ichwLp, vrs, vfEntireHt,
+ !prgfAllCompsSelected[ichwLp - m_pseg->m_ichwMin], prgfHighlighted,
+ (m_pseg->m_fEndLine && fSkipSpace));
+ }
+
+ // If all the glyphs of a given cluster were highlighted, highlight the whole cluster
+ // as a unit.
+ for (int islout = 0; islout < m_pseg->m_cslout; islout++)
+ {
+ CalcCompleteCluster(islout, vrs, vfEntireHt, prgfHighlighted);
+ }
+
+ // Convert rectangles in source coordinates to line segments in destination
+ // coordinates.
+ std::vector<LineSeg> vlsd;
+ for (size_t irs = 0; irs < vrs.size(); irs++)
+ {
+ Rect rsTmp = vrs[irs];
+ LineSeg lsdTmp;
+ lsdTmp.left = min(xdSegRight, max(xdSegLeft, ScaleXToDest(rsTmp.left)));
+ lsdTmp.right = max(xdSegLeft, min(xdSegRight, ScaleXToDest(rsTmp.right)));
+ vlsd.push_back(lsdTmp);
+ }
+
+ // Create a list of line segments with no overlaps.
+ std::vector<LineSeg> vlsdNoOverlaps;
+ size_t ils;
+ for (ils = 0; ils < vlsd.size(); ils++)
+ AddLineSegWithoutOverlaps(vlsdNoOverlaps, vlsd[ils]);
+
+ AssertNoOverlaps(vlsdNoOverlaps);
+
+ // The gap between the baseline and underlining is one pixel, on a normal screen
+ // at standard magnification, and as near as we can get to the equivalent in any
+ // special case.
+ //float dydGap = rdHeight / 96;
+ //float dydSubscript = max(float(0), (-1 * GrEngine::GrFloatMulDiv(m_pseg->m_dysOffset, rsHeight, rdHeight)));
+ float dydGap = 1;
+ float dydSubscript = max(float(0), (-1 * (m_pseg->m_dysOffset * m_yFactor)));
+ float ydBottom = ScaleYToDest(m_pseg->m_dysAscent) + dydGap + dydSubscript;
+
+ // Put the results in the output buffers.
+ size_t cxdRet = vlsdNoOverlaps.size();
+ for (ils = 0; ils < min(cxdRet, size_t(cxdMax)); ils++)
+ {
+ prgxdLefts[ils] = vlsdNoOverlaps[ils].left;
+ prgxdRights[ils] = vlsdNoOverlaps[ils].right;
+ if (prgydUnderTops)
+ prgydUnderTops[ils] = ydBottom;
+ }
+ delete[] prgfAllCompsSelected;
+ delete[] prgfHighlighted;
+
+ //RestoreFont(pgg);
+
+ if (vlsdNoOverlaps.size() > cxdMax)
+ res = kresFail;
+
+ return cxdRet;
+}
+
+
+//:>********************************************************************************************
+//:> Other methods
+//:>********************************************************************************************
+
+/*----------------------------------------------------------------------------------------------
+ Handle either drawing an insertion point or returning the position at which it
+ would be drawn.
+
+ @param ichwIP - character position
+ @param fAssocPrev - true if selection is associated with previous character
+ @param bOn - turning on or off? (ignored in this implementation)
+ @param fForceSplit - force a split insertion point
+ @param prdPrimary - if NULL, do drawing; otherwise, return location of primary IP
+ in destination device coordinates
+ @param prdSecondary - return location of secondary IP in dest device coordinates
+ @param pfPrimaryHere - true if primary selection is rendered in this segment
+ @param pfSecHere - true if (distinct) secondary selection is rendered in this segment;
+ false if it is in a different segment, or we are rendering
+ an I-beam (std selection)
+----------------------------------------------------------------------------------------------*/
+void SegmentPainter::CalcOrDrawInsertionPoint(
+ int ichwIP, bool fAssocPrev, bool bOn, bool fForceSplit,
+ Rect * prdPrimary, Rect * prdSecondary)
+{
+ GrResult res = kresOk;
+
+ float xsSegLeft = 0;
+ float ysSegTop = 0;
+
+ if (prdPrimary)
+ {
+ prdPrimary->top = 0;
+ prdPrimary->bottom = 0;
+ prdPrimary->left = 0;
+ prdPrimary->right = 0;
+ }
+ if (prdSecondary)
+ {
+ prdSecondary->top = 0;
+ prdSecondary->bottom = 0;
+ prdSecondary->left = 0;
+ prdSecondary->right = 0;
+ }
+
+ int ichwLim = m_pseg->m_ichwMin + m_pseg->m_dichwLim;
+ bool fRtl = m_pseg->rightToLeft();
+
+ if (m_pseg->m_dxsWidth < 0)
+ m_pseg->ComputeDimensions();
+
+ // Get the locations for the before and after selections.
+
+ float xsBefore, ysBeforeTop, ysBeforeBottom;
+ float xsAfter, ysAfterTop, ysAfterBottom;
+ bool fRtlBefore, fRtlAfter;
+ CalcIP(ichwIP, true, &xsBefore, &ysBeforeTop, &ysBeforeBottom, &fRtlBefore);
+ CalcIP(ichwIP, false, &xsAfter, &ysAfterTop, &ysAfterBottom, &fRtlAfter);
+
+ if (m_pseg->m_cginf == 0 && ichwIP == ichwLim)
+ {
+ Assert(kPosInfFloat == xsBefore && kPosInfFloat == xsAfter);
+ xsBefore = 0;
+ xsAfter = 0;
+ }
+ else if ((kPosInfFloat == xsBefore || kNegInfFloat == xsBefore) &&
+ (kPosInfFloat == xsAfter || kNegInfFloat == xsAfter))
+ {
+ return; // quick exit for nothing-to-draw case
+ }
+
+ bool fPrimary = true;
+ if (fForceSplit)
+ {
+ // Supposedly we can't find both halves of the IP in this segment.
+ Assert(xsBefore == kPosInfFloat || xsBefore == kNegInfFloat
+ || xsAfter == kPosInfFloat || xsAfter == kNegInfFloat);
+ if (xsBefore == kPosInfFloat || xsBefore == kNegInfFloat)
+ {
+ // Can't find the char the IP is before.
+ fPrimary = fAssocPrev;
+ }
+ else // if (xsAfter == kPosInfinity || xsAfter == kNegInfinity)
+ {
+ // Can't find the char the IP is after.
+ fPrimary = !fAssocPrev;
+ }
+ ////Assert((dm == kdmSplitPrimary) == fPrimary);
+ }
+
+ if (xsBefore == kPosInfinity || xsBefore == kNegInfinity)
+ {
+ ysBeforeTop = ysSegTop;
+ ysBeforeBottom = ysSegTop + m_pseg->m_dysHeight;
+ }
+ else
+ {
+ xsBefore += xsSegLeft;
+ ysBeforeTop += ysSegTop;
+ ysBeforeBottom += ysSegTop;
+ }
+ if (xsAfter == kNegInfinity || xsAfter == kPosInfinity)
+ {
+ ysAfterTop = ysSegTop;
+ ysAfterBottom = ysSegTop + m_pseg->m_dysHeight;
+ }
+ else
+ {
+ xsAfter += xsSegLeft;
+ ysAfterTop += ysSegTop;
+ ysAfterBottom += ysSegTop;
+ }
+
+ float xsLeft = xsSegLeft
+ + ((fRtl && m_pseg->m_fEndLine) ? (m_pseg->m_dxsVisibleWidth - m_pseg->m_dxsTotalWidth) :0);
+ float xsRight = xsSegLeft
+ + ((fRtl && m_pseg->m_fEndLine) ? m_pseg->m_dxsVisibleWidth : m_pseg->m_dxsTotalWidth)
+ + m_pseg->m_dxsStretch;
+ float dysMinSplitHeight = m_pseg->m_dysHeight / 4;
+
+ if (!fForceSplit) // kdmNormal == dm)
+ {
+ // Standard I-beam, or both halves of split IP.
+
+ bool fIBeam = false;
+
+ if (xsBefore == xsAfter)
+ // Positions coincide within this segment: make an I-beam.
+ fIBeam = true;
+
+ else if (fabsf(xsBefore - xsLeft) <= 1 && ichwIP == m_pseg->m_ichwMin)
+ {
+ // Logical start is at a left edge: allow I-beam if previous segment has one
+ // at the end (or there is no previous segment).
+ //Assert(!fRtlBefore);
+ //if (m_pseg->m_psegPrev)
+ //{
+ // SegmentPainter segpPrev(m_pseg->m_psegPrev);
+ // fIBeam = segpPrev.doBoundariesCoincide(true, true);
+ //}
+ //else
+
+ fIBeam = true;
+ }
+ else if (fabsf(xsBefore - xsRight) <= 1 && ichwIP == m_pseg->m_ichwMin)
+ {
+ // Logical start is at a right edge: allow I-beam if previous segment has one
+ // at the end (or there is no previous segment).
+ //Assert(fRtlBefore);
+ //if (m_pseg->m_psegPrev)
+ //{
+ // SegmentPainter segpPrev(m_pseg->m_psegPrev);
+ // fIBeam = segpPrev.doBoundariesCoincide(true, false);
+ //}
+ //else
+
+ fIBeam = true;
+ }
+ else if (fabsf(xsAfter - xsLeft) <= 1 && ichwIP == ichwLim)
+ {
+ // Logical end is at a left edge: allow I-beam if next segment has one
+ // at the beginning (or there is no next segment).
+ //Assert(fRtlAfter);
+ //if (m_pseg->m_psegNext == NULL)
+ //{
+ // SegmentPainter segpPrev(m_pseg->m_psegPrev);
+ // fIBeam = segpPrev.doBoundariesCoincide(false, true);
+ //}
+ //else
+
+ fIBeam = true;
+ }
+ else if (fabsf(xsAfter - xsRight) <= 1 && ichwIP == ichwLim)
+ {
+ // Logical end is at a right edge: allow I-beam if next segment has one
+ // at the beginning (or there is no next segment).
+ //Assert(!fRtlAfter);
+ //if (m_pseg->m_psegNext == NULL)
+ //{
+ // SegmentPainter segpPrev(m_pseg->m_psegPrev);
+ // fIBeam = segpPrev.doBoundariesCoincide(false, false);
+ //}
+ //else
+
+ fIBeam = true;
+ }
+ else
+ {
+ // If the horizontal positions of the two halves are sufficiently close,
+ // make an I-beam.
+ fIBeam = CloseIPPositions(
+ &xsBefore, ysBeforeTop, ysBeforeBottom,
+ &xsAfter, ysAfterTop, ysAfterBottom);
+ }
+ if (ResultFailed(res))
+ THROW(WARN(res));
+
+ if (fIBeam &&
+ ((ichwIP == m_pseg->m_ichwMin && fAssocPrev) || (ichwIP == ichwLim && !fAssocPrev)))
+ {
+ // Adjacent segment will show the I-beam, so don't show it here.
+ if (m_pseg->m_cginf == 0)
+ {
+ // Empty segment--assume no adjacent segment.
+ }
+ else
+ return;
+ }
+
+ if (fIBeam)
+ { // I-beam caret
+
+ float ysTop = min(ysBeforeTop, ysAfterTop);
+ float ysBottom = max(ysBeforeBottom, ysAfterBottom);
+ float xsMid = (xsBefore == kPosInfFloat) ? xsAfter : xsBefore;
+ InvertIBeam(xsMid, ysTop, ysBottom, fAssocPrev, prdPrimary);
+ }
+ else
+ { // split caret
+ if (fAssocPrev)
+ { // xsAfter is primary, xsBefore is secondary
+ if (xsAfter != kNegInfinity)
+ {
+ InvertSplitIP(xsAfter, ysAfterTop, ysAfterBottom,
+ true, fRtlAfter, false, dysMinSplitHeight, prdPrimary);
+ }
+ if (xsBefore != kPosInfFloat)
+ {
+ InvertSplitIP(xsBefore, ysBeforeTop, ysBeforeBottom,
+ false, !fRtlBefore, true, dysMinSplitHeight, prdSecondary);
+ }
+ }
+ else
+ { // xsBefore is primary, xsAfter is secondary
+ if (xsBefore != kPosInfinity)
+ {
+ InvertSplitIP(xsBefore, ysBeforeTop, ysBeforeBottom,
+ false, !fRtlBefore, false, dysMinSplitHeight, prdPrimary);
+ }
+ if (xsAfter != kNegInfinity)
+ {
+ InvertSplitIP(xsAfter, ysAfterTop, ysAfterBottom,
+ true, fRtlAfter, true, dysMinSplitHeight, prdSecondary);
+ }
+ }
+ //*pfSecHere = true;
+ }
+ //*pfPrimaryHere = true;
+ }
+ else if (fForceSplit && fPrimary) // kdmSplitPrimary == dm)
+ {
+ // Draw just the primary caret.
+ if (fAssocPrev)
+ { // xsAfter is primary
+ if (xsAfter == kNegInfinity)
+ {} // no primary
+ else
+ InvertSplitIP(xsAfter, ysAfterTop, ysAfterBottom,
+ true, fRtlAfter, false, dysMinSplitHeight, prdPrimary);
+ }
+ else
+ { // xsBefore is primary
+ if (xsBefore == kPosInfinity)
+ {} // no primary
+ else
+ InvertSplitIP(xsBefore, ysBeforeTop, ysBeforeBottom,
+ false, !fRtlBefore, false, dysMinSplitHeight, prdPrimary);
+ }
+ }
+ else // fForceSplit && !fPrimary
+ {
+ // Draw just the secondary caret.
+ //Assert(kdmSplitSecondary == dm);
+ if (fAssocPrev)
+ { // xsBefore is secondary
+ if (xsBefore == kPosInfinity)
+ {} // no secondary
+ else
+ InvertSplitIP(xsBefore, ysBeforeTop, ysBeforeBottom,
+ false, !fRtlBefore, true, dysMinSplitHeight, prdSecondary);
+ }
+ else
+ { // xsAfter is secondary
+ if (xsAfter == kNegInfinity)
+ {} // no secondary
+ else
+ InvertSplitIP(xsAfter, ysAfterTop, ysAfterBottom,
+ true, fRtlAfter, true, dysMinSplitHeight, prdSecondary);
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------------------------
+ Draw an I-beam cursor, or return the location at which it would be drawn.
+
+ Note that currently we do not draw a true I-beam, but rather just a vertical line.
+
+ @param xs - horizontal center of IP
+ @param ysTop - top of glyph (or ligature component) box
+ @param ysBottom - bottom of glyph (or ligature component) box
+ @param fAssocRight - true if the IP is associated with the text to the right
+ @param rs, rd - source/destination device coordinates
+ @param prdRet - return location to be drawn in dest device coords;
+ if NULL, do the drawing
+----------------------------------------------------------------------------------------------*/
+void SegmentPainter::InvertIBeam(float xs, float ysTop, float ysBottom,
+ bool fAssocPrev, Rect * prdRet)
+{
+ float xd = ScaleXToDest(xs);
+ float ydTop = ScaleYToDest(ysTop);
+ float ydBottom = ScaleYToDest(ysBottom);
+
+ // For now, assume that destination coordinates are in terms of pixels.
+ float dxdTwoXPixels = 2;
+
+ float dxdLeftAdjust = dxdTwoXPixels / 2;
+ float dxdRightAdjust = dxdTwoXPixels - dxdLeftAdjust;
+
+// int dysFontAscent;
+// pgg->GetFontAscentSourceUnits(pgg, &dysFontAscent);
+// dysFontAscent += m_dysXAscent;
+// if (dysFontAscent < 20)
+// {
+// dxdRightAdjust--;
+// dxdRightHook = 1;
+// }
+
+ if (prdRet)
+ {
+ prdRet->left = xd - dxdLeftAdjust;
+ prdRet->right = xd + dxdRightAdjust;
+ prdRet->top = ydTop;
+ prdRet->bottom = ydBottom;
+ }
+ else
+ {
+ InvertRect(xd-dxdLeftAdjust, ydTop,
+ xd+dxdRightAdjust, ydBottom);
+
+ // To make a true I-beam:
+// InvertRect(xd-dxdTwoXPixels, ydTop-dydOneYPixel,
+// xd+dxdRightHook, ydTop);
+// InvertRect(xd-dxdTwoXPixels, ydBottom,
+// xd+dxdRightHook, ydBottom+dydOneYPixel);
+ }
+}
+
+/*----------------------------------------------------------------------------------------------
+ Draw a split insertion point, or return the location at which it would be drawn.
+
+ @param xs - horizontal center of IP
+ @param ysTop - top of glyph (or ligature component) box
+ @param ysBottom - bottom of glyph (or ligature component) box
+ @param fTop - true if the IP is to be drawn at the top of the line of text
+ @param fAssocRight - true if the IP is associated with the text to the right
+ @param fSecondary - true if the IP is secondary, false if it is primary
+ @param dysMinSplitHeight
+ - minimum split cursor height
+ @param rs, rd - source/destination device coordinates
+ @param prdRet - return location to be drawn in dest device coords;
+ if NULL, do the drawing
+----------------------------------------------------------------------------------------------*/
+void SegmentPainter::InvertSplitIP(float xs, float ysTop, float ysBottom,
+ bool fTop, bool fAssocRight, bool fSecondary, float dysMinSplitHeight,
+ Rect * prdRet)
+{
+ if (prdRet == NULL && ysTop == ysBottom)
+ return; // invisible IP
+
+ float xd = ScaleXToDest(xs);
+ float ydTop = ScaleYToDest(ysTop);
+ float ydBottom = ScaleYToDest(ysBottom);
+ float dydMinSplitHeight = dysMinSplitHeight;
+
+ float ydMid = (ydTop + ydBottom) / 2;
+
+ // For now, assume that destination coordinates are in terms of pixels.
+ float dxdOneXPixel = 1;
+ float dydOneYPixel = 1;
+ float dxdTwoXPixels = 2;
+
+ float xdLeft, xdRight, dxdHookLen;
+ if (fSecondary)
+ {
+ xdLeft = xd - (dxdOneXPixel / 2);
+ xdRight = xdLeft + 1;
+ dxdHookLen = 2;
+ }
+ else
+ {
+ xdLeft = xd - (dxdTwoXPixels / 2);
+ xdRight = xdLeft + 2;
+ dxdHookLen = 3;
+ }
+
+ float yd1, yd2;
+ if (fTop)
+ {
+ yd1 = ydTop;
+ if (ydMid - ydTop < dydMinSplitHeight)
+ ydMid = ydTop + dydMinSplitHeight;
+ yd2 = (fSecondary) ? ydMid : ydMid + 3;
+ }
+ else
+ {
+ yd2 = ydBottom;
+ if (ydBottom - ydMid < dydMinSplitHeight)
+ ydMid = ydBottom - dydMinSplitHeight;
+ yd1 = (fSecondary) ? ydMid : ydMid - 3;
+ }
+
+ if (prdRet)
+ {
+ prdRet->left = xdLeft;
+ prdRet->right = xdRight;
+ // The most common reason for returning the positions of the IP is to scroll it into
+ // view, in which case we want to return the entire line height, not just the
+ // height of the IP itself.
+ prdRet->top = ydTop; // yd1;
+ prdRet->bottom = ydBottom; // yd2;
+ }
+ else
+ {
+ InvertRect(xdLeft, yd1, xdRight, yd2);
+ }
+
+ if (fAssocRight)
+ {
+ if (prdRet)
+ prdRet->right = xdRight + dxdHookLen;
+ else
+ {
+ if (fTop)
+ InvertRect(xdRight, ydTop,
+ xdRight + dxdHookLen, ydTop + dydOneYPixel);
+ else
+ InvertRect(xdRight, ydBottom-dydOneYPixel,
+ xdRight + dxdHookLen, ydBottom);
+ }
+ }
+ else
+ {
+ if (prdRet)
+ prdRet->left = xdLeft - dxdHookLen;
+ else
+ {
+ if (fTop)
+ InvertRect(xdLeft - dxdHookLen, ydTop,
+ xdLeft, ydTop + dydOneYPixel);
+ else
+ InvertRect(xdLeft - dxdHookLen, ydBottom - dydOneYPixel,
+ xdLeft, ydBottom);
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------------------------
+ Given a character position (relative to the beginning of the string), return the
+ position for the insertion point, relative to the top-left of the segment.
+ Notice that these are before/after positions, not left/right positions.
+ Return an infinity value to indicate no appropriate IP.
+
+ @param ichwSel - selection position, relative to beginning of string
+ @param fBefore - true if we are interested in the character the IP is before.
+ @param pxs, pysTop, pysBottom
+ - return locations for drawing the IP (top and bottom are not
+ adjusted for half-selections)
+ @param pfRtl - return true if the character is right-to-left
+
+ Review: it might be useful to figure out if the two surface glyphs are physically adjacent
+ in such a way that their IPs can be combined into one I-beam. For now we use a simple
+ rule that assumes that any glyph or ligature component is at least 10% as wide as the
+ height of the font (see CloseIPPositions).
+----------------------------------------------------------------------------------------------*/
+void SegmentPainter::CalcIP(int ichwSel, bool fBefore,
+ float * pxs, float * pysTop, float * pysBottom, bool * pfRtl)
+{
+ Font & font = this->m_pseg->getFont();
+
+ int ichw = (fBefore) ? ichwSel : ichwSel - 1;
+
+ int iginfOutput;
+
+ *pfRtl = m_pseg->CharIsRightToLeft(ichw, fBefore);
+
+ GrEngine * pgreng = m_pseg->EngineImpl();
+ GrGlyphTable * pgtbl = (pgreng) ? pgreng->GlyphTable() : NULL;
+
+ float dysFontAscent = m_pseg->m_dysFontAscent;
+ ////GrResult res = GetFontAscentSourceUnits(pgg, &dysFontAscent);
+ ////if (ResultFailed(res))
+ //// THROW(WARN(res));
+ dysFontAscent += m_pseg->m_dysXAscent;
+
+ *pysTop = 0;
+ *pysBottom = m_pseg->m_dysHeight;
+
+ int ichwSegOffset = ichw - m_pseg->m_ichwMin;
+ int isloutLig;
+ if (ichwSegOffset < m_pseg->m_ichwAssocsMin || ichwSegOffset >= m_pseg->m_ichwAssocsLim)
+ isloutLig = kNegInfinity;
+ else
+ isloutLig = m_pseg->m_prgisloutLigature[ichwSegOffset - m_pseg->m_ichwAssocsMin];
+ bool fOkLig = (isloutLig != kNegInfinity);
+ if (pgtbl && fOkLig)
+ {
+ // Ligature component; place the IP at the boundary
+ // of the component box.
+
+ GrSlotOutput * pslout = m_pseg->OutputSlot(isloutLig);
+ int icomp = m_pseg->m_prgiComponent[ichwSegOffset - m_pseg->m_ichwAssocsMin];
+
+ iginfOutput = m_pseg->LogicalToPhysicalSurface(isloutLig);
+
+ float xsGlyphOffset = m_pseg->GlyphLeftEdge(iginfOutput);
+
+ float xsLeft, xsRight, ysTop, ysBottom;
+
+ // TODO: Rework ComponentIndexForGlyph to take slati rather than iForGlyph.
+ int slati = pslout->ComponentId(icomp);
+ int iForGlyph = pgtbl->ComponentIndexForGlyph(pslout->GlyphID(), slati);
+ fOkLig = pgtbl->ComponentBoxLogUnits(m_pseg->m_xysEmSquare, pslout->GlyphID(), iForGlyph,
+ m_pseg->m_mFontEmUnits, m_pseg->m_dysAscent,
+ &xsLeft, &ysTop, &xsRight, &ysBottom);
+ if (fOkLig)
+ {
+ if (fBefore == *pfRtl)
+ // Position of interest is to the right.
+ *pxs = xsGlyphOffset + xsRight;
+ else
+ // Position of interest is to the left.
+ *pxs = xsGlyphOffset + xsLeft;
+
+ GlyphInfo * pginf = m_pseg->m_prgginf + iginfOutput;
+ float ysGlyph = pginf->yOffset();
+
+ *pysTop = ysTop + (m_pseg->m_dysAscent - dysFontAscent) - ysGlyph;
+ *pysBottom = ysBottom + (m_pseg->m_dysAscent - dysFontAscent) - ysGlyph;
+ }
+ }
+
+ if (!fOkLig)
+ {
+ // Not a ligature component.
+
+ int islout = m_pseg->UnderlyingToLogicalSurface(ichw, fBefore);
+ GrSlotOutput * psloutTmp = m_pseg->OutputSlot(islout);
+ iginfOutput = m_pseg->LogicalToPhysicalSurface(islout);
+
+ if (kPosInfinity == iginfOutput || kNegInfinity == iginfOutput)
+ *pxs = kPosInfFloat;
+
+ else
+ {
+ bool fCluster = (psloutTmp->IsPartOfCluster());
+ bool fHighlightBB = fCluster;
+ // Test below should match the one in CalcHighlightRect:
+ if ((psloutTmp->ClusterAdvance() == 0
+ || psloutTmp->GlyphMetricLogUnits(&font, kgmetAdvWidth) == 0)
+ && !psloutTmp->IsSpace())
+ {
+ fHighlightBB = true; // use bounding box for zero-advance glyph
+ }
+ if (fHighlightBB)
+ {
+ // Cluster member or zero-width glyph.
+
+ if (fCluster && AtEdgeOfCluster(psloutTmp, islout, fBefore) &&
+ !CanInsertIntoCluster(psloutTmp, islout))
+ {
+ // Place the IP at the edge of the whole cluster.
+
+ int isloutBase = psloutTmp->ClusterBase();
+ GrSlotOutput * psloutBase = m_pseg->OutputSlot(isloutBase);
+ //int igbbBase = psloutBase->GlyphBbIndex();
+ int iginfBase = m_pseg->LogicalToPhysicalSurface(isloutBase);
+
+ float xsBase = m_pseg->GlyphLeftEdge(iginfBase);
+
+ float xsLeft = xsBase + psloutBase->ClusterXOffset();
+ float xsRight = xsBase + psloutBase->ClusterAdvance();
+
+ if (fBefore == *pfRtl)
+ // Position of interest is to the right
+ *pxs = xsRight;
+ else
+ // Position of interest is to the left.
+ *pxs = xsLeft;
+ }
+ else
+ {
+ // Place the IP on the perimeter of the bounding box of the cluster member.
+
+ float xsGlyphStart = m_pseg->GlyphLeftEdge(iginfOutput);
+
+// float dysFontAscent;
+// res = GetFontAscentSourceUnits(pgg, &dysFontAscent);
+// if (ResultFailed(res)) THROW(WARN(res));
+// dysFontAscent += m_dysXAscent;
+
+ float ysBbTop = psloutTmp->GlyphMetricLogUnits(&font, kgmetBbTop);
+ float ysBbBottom = psloutTmp->GlyphMetricLogUnits(&font, kgmetBbBottom);
+ float xsBbLeft = psloutTmp->GlyphMetricLogUnits(&font, kgmetBbLeft);
+ float xsBbRight = psloutTmp->GlyphMetricLogUnits(&font, kgmetBbRight);
+
+ int twoPixels = 2; // assume dest coords are in pixels
+
+ if (fBefore == *pfRtl)
+ // Position of interest is to the right
+ *pxs = xsGlyphStart + xsBbRight + twoPixels;
+ else
+ // Position of interest is to the left.
+ *pxs = xsGlyphStart + xsBbLeft - twoPixels;
+
+ GlyphInfo * pginf = m_pseg->m_prgginf + iginfOutput;
+ float ysGlyph = pginf->yOffset();
+ float ysGlyphBaseline = m_pseg->m_dysAscent - ysGlyph; // relative to top
+
+ *pysTop = ysGlyphBaseline - ysBbTop - twoPixels;
+ *pysBottom = ysGlyphBaseline - ysBbBottom + twoPixels;
+ }
+ }
+ else
+ {
+ // Insertion point goes at origin or advance width of glyph.
+
+ *pxs = m_pseg->GlyphLeftEdge(iginfOutput);
+ if (fBefore == *pfRtl)
+ {
+ // Position of interest is to the right
+ //*pxs += psloutTmp->GlyphMetricLogUnits(&font, kgmetAdvWidth);
+ *pxs += psloutTmp->ClusterAdvance();
+ }
+ // else position of interest is to the left
+ }
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------------------------
+ If the two positions for the insertion point are close enough, merge them into
+ one insertion point, and return true to indicate that we want an I-beam.
+
+ @param pxsBefore, pxsAfter - for comparing, and also adjusted results
+ @param ysBeforeTop/Bottom, ysAfterTop/Bottom - for comparing
+----------------------------------------------------------------------------------------------*/
+bool SegmentPainter::CloseIPPositions(float * pxsBefore, float ysBeforeTop, float ysBeforeBottom,
+ float * pxsAfter, float ysAfterTop, float ysAfterBottom)
+{
+ // No adjustment if vertical positions are disjoint.
+ if (ysBeforeBottom <= ysAfterTop)
+ return false;
+ if (ysAfterBottom <= ysBeforeTop)
+ return false;
+
+ float dysHt = max(ysBeforeBottom, ysAfterBottom) - min(ysBeforeTop, ysAfterTop);
+ float dxsDiff = fabsf(*pxsBefore - *pxsAfter);
+
+ // If the difference in the x positions is less than 10% of the height,
+ // we want an I-beam--use the average.
+ if (dxsDiff * 10 < dysHt)
+ {
+ *pxsBefore = (*pxsBefore + *pxsAfter) / 2;
+ *pxsAfter = *pxsBefore;
+ return true;
+ }
+
+ return false;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Return true if the slot in question is the first or last slot of its cluster,
+ or if it is not part of a cluster at all. Return false if the specified side
+ of the slot is interior to a cluster.
+
+ @param fBefore - if true, we are examining the slot the insertion point is before,
+ so we are interested in whether this slot is at the
+ leading edge of the cluster.
+----------------------------------------------------------------------------------------------*/
+bool SegmentPainter::AtEdgeOfCluster(GrSlotOutput * pslout, int islout, bool fBefore)
+{
+ if (!pslout->IsPartOfCluster())
+ return true;
+
+ if (fBefore && (islout == 0))
+ return true;
+ if (!fBefore && (m_pseg->m_cslout == islout + 1))
+ return true;
+
+ // If the adjacent slot in whatever direction we're interested in has the same
+ // cluster base, this slot is not on the edge of the cluster.
+ // -- NO, because you could have an intervening glyph that is not part of the
+ // cluster.
+ //int isloutAdjacentBase = (fBefore) ?
+ // m_pseg->OutputSlot(islout - 1)->ClusterBase() :
+ // m_pseg->OutputSlot(islout + 1)->ClusterBase();
+ //return (isloutAdjacentBase != m_pseg->OutputSlot(islout)->ClusterBase());
+
+ // Ask the cluster base.
+ return AtEdgeOfCluster(m_pseg->OutputSlot(pslout->ClusterBase()), pslout->ClusterBase(),
+ pslout, islout, fBefore);
+}
+
+bool SegmentPainter::AtEdgeOfCluster(GrSlotOutput * psloutBase, int isloutBase,
+ GrSlotOutput * pslout, int islout, bool fBefore)
+{
+ // Compare pslout to all the members of the cluster. If it is the minimum or maximum, it
+ // is at an edge.
+ if (fBefore && isloutBase < islout)
+ return false;
+ if (!fBefore && isloutBase > islout)
+ return false;
+
+ std::vector<int> visloutCluster;
+ m_pseg->ClusterMembersForGlyph(isloutBase, psloutBase->ClusterRange(), visloutCluster);
+
+ for (size_t iislout = 0; iislout < visloutCluster.size(); iislout++)
+ {
+ int isloutMember = visloutCluster[iislout];
+ if (fBefore && isloutMember < islout)
+ return false;
+ if (!fBefore && isloutMember > islout)
+ return false;
+ }
+ return true;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Return true if it is permitted to put an insertion point somewhere in the middle of
+ the given slot's cluster.
+----------------------------------------------------------------------------------------------*/
+bool SegmentPainter::CanInsertIntoCluster(GrSlotOutput * pslout, int islout)
+{
+ if (!pslout->IsPartOfCluster())
+ return false;
+
+ int isloutBase = pslout->ClusterBase();
+ if (islout != isloutBase)
+ {
+ // Ask the cluster base.
+ Assert(pslout->ClusterRange() == 0);
+ return CanInsertIntoCluster(m_pseg->OutputSlot(isloutBase), isloutBase);
+ }
+
+ if (!AtEdgeOfCluster(pslout, islout, true) && pslout->InsertBefore())
+ return true;
+
+ std::vector<int> visloutCluster;
+ m_pseg->ClusterMembersForGlyph(isloutBase, pslout->ClusterRange(), visloutCluster);
+
+ for (size_t iislout = 0; iislout < visloutCluster.size(); iislout++)
+ {
+ int isloutMember = visloutCluster[iislout];
+ if (AtEdgeOfCluster(m_pseg->OutputSlot(isloutMember), isloutMember, true))
+ // Inserting before the first member of the cluster doesn't count.
+ continue;
+
+ if (m_pseg->OutputSlot(isloutMember)->InsertBefore())
+ return true;
+ }
+
+ return false;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Determine which characters in the selected range are part of ligatures that are fully
+ selected vs. those that are only partially selected. Those that are only partially selected
+ must have their components highlighted individually, but those that are fully selected
+ should be highlighted fully, to avoid blank spots in the case that the components do not
+ fully cover the surface of the glyph.
+
+ @param prgfAllSelected - array of flags to fill in with results
+ @param ichwMin/LimSeg - range of characters to examine
+ @param ichwMin/LimSel - range of characters that are selected
+----------------------------------------------------------------------------------------------*/
+void SegmentPainter::CalcPartialLigatures(bool * prgfAllSelected,
+ int ichwMinSeg, int ichwLimSeg,
+ int ichwMinSel, int ichwLimSel)
+{
+ GrEngine * pgreng = m_pseg->EngineImpl();
+ GrGlyphTable * pgtbl = (pgreng) ? pgreng->GlyphTable() : NULL;
+
+ for (int ichw = ichwMinSeg; ichw < ichwLimSeg; ichw++)
+ {
+ int ichwSegOffset = ichw - m_pseg->m_ichwMin;
+ int isloutLig;
+ if (ichwSegOffset < m_pseg->m_ichwAssocsMin || ichwSegOffset >= m_pseg->m_ichwAssocsLim)
+ isloutLig = kNegInfinity;
+ else
+ isloutLig = m_pseg->m_prgisloutLigature[ichwSegOffset - m_pseg->m_ichwAssocsMin];
+ bool fLigComp = (pgtbl && isloutLig != kNegInfinity);
+ if (fLigComp && !prgfAllSelected[ichwSegOffset])
+ {
+ GrSlotOutput * pslout = m_pseg->OutputSlot(isloutLig);
+ bool fAll = true;
+ int icomp;
+ for (icomp = 0; icomp < pslout->NumberOfComponents(); icomp++)
+ {
+ int ichwUnderlying = pslout->UnderlyingComponent(icomp);
+ if (ichwUnderlying < ichwMinSel - m_pseg->m_ichwMin ||
+ ichwUnderlying >= ichwLimSel - m_pseg->m_ichwMin)
+ {
+ fAll = false; // found a component that is not selected
+ }
+ }
+ for (icomp = 0; icomp < pslout->NumberOfComponents(); icomp++)
+ {
+ int ichwUnderlying = pslout->UnderlyingComponent(icomp);
+ prgfAllSelected[ichwUnderlying] = fAll;
+ }
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------------------------
+ The given character is part of the selected range. Add to the vector a rectangle (or
+ eventually, several) indicating the corresponding screen area(s) of the character.
+ The rectangle is relative to the top left of the segment.
+
+ @param ichw - selected char, relative to beginning of string
+ @param vrs - list of rectangles to highlight, in source coords, relative to
+ segment draw position (top-left)
+ @param vfEntireHt - for each of the above rectangles, true if the rect should be
+ extended to include the entire line
+ @param fJustComponent - true if we want to highlight just the single ligature component
+ this character maps to; false if the entire ligature is selected
+ @param rgfHighlighted - for each glyph in this segment, whether it is being highlighted
+ by this operation
+ @param fSkipTrSpace - should we skip trailing white space?
+----------------------------------------------------------------------------------------------*/
+void SegmentPainter::CalcHighlightRect(int ichw,
+ std::vector<Rect> & vrs, std::vector<bool> & vfEntireHt,
+ bool fJustComponent, bool * rgfHighlighted, bool fSkipTrSpace)
+{
+ GrResult res;
+
+ GrEngine * pgreng = m_pseg->EngineImpl();
+ GrGlyphTable * pgtbl = (pgreng) ? pgreng->GlyphTable() : NULL;
+ //bool fRtl = m_pseg->rightToLeft();
+
+ Font & font = m_pseg->getFont();
+
+ if (!GrCharStream::AtUnicodeCharBoundary(m_pseg->GetString(), ichw))
+ // Second half of a surrogate pair, or UTF-8: ignore.
+ return;
+
+ // Calculate the location for the glyph that this IP is before.
+
+ int ichwSegOffset = ichw - m_pseg->m_ichwMin;
+ int isloutLig;
+ if (ichwSegOffset < m_pseg->m_ichwAssocsMin || ichwSegOffset >= m_pseg->m_ichwAssocsLim)
+ isloutLig = kNegInfinity;
+ else
+ isloutLig = m_pseg->m_prgisloutLigature[ichwSegOffset - m_pseg->m_ichwAssocsMin];
+ bool fLigComp = (pgtbl && isloutLig != kNegInfinity);
+ if (fLigComp && fJustComponent)
+ {
+ // Character is a ligature component.
+
+ GrSlotOutput * pslout = m_pseg->OutputSlot(isloutLig);
+ int icomp = m_pseg->m_prgiComponent[ichwSegOffset - m_pseg->m_ichwAssocsMin];
+ int iginfOutput = m_pseg->LogicalToPhysicalSurface(isloutLig);
+
+ float xsGlyphStart = m_pseg->GlyphLeftEdge(iginfOutput);
+
+ float xsLeft, xsRight, ysTop, ysBottom;
+
+ // TODO: Rework ComponentIndexForGlyph to take slati rather than iForGlyph.
+ int slati = pslout->ComponentId(icomp);
+ int iForGlyph = pgtbl->ComponentIndexForGlyph(pslout->GlyphID(), slati);
+ pgtbl->ComponentBoxLogUnits(m_pseg->m_xysEmSquare, pslout->GlyphID(), iForGlyph,
+ m_pseg->m_mFontEmUnits, m_pseg->m_dysAscent,
+ &xsLeft, &ysTop, &xsRight, &ysBottom);
+
+ float dysFontAscent = m_pseg->m_dysFontAscent;
+ ////GetFontAscentSourceUnits(pgg, &dysFontAscent);
+ dysFontAscent += m_pseg->m_dysXAscent;
+ GlyphInfo * pginf = m_pseg->m_prgginf + iginfOutput;
+ float ysForGlyph = pginf->yOffset();
+ float dysGlyphTop = (m_pseg->m_dysAscent - dysFontAscent) - ysForGlyph; // rel. to common baseline
+
+ Rect rsNew;
+ rsNew.top = ysTop + dysGlyphTop;
+ rsNew.bottom = ysBottom + dysGlyphTop;
+ rsNew.left = xsGlyphStart + xsLeft;
+ rsNew.right = xsGlyphStart + xsRight;
+ vrs.push_back(rsNew);
+
+ vfEntireHt.push_back(false);
+ }
+ else
+ {
+ std::vector<int> vislout = m_pseg->UnderlyingToLogicalAssocs(ichw);
+ if (vislout.size() == 0)
+ return;
+
+ float xsGlyphLeft, xsGlyphRight;
+
+ res = kresOk;
+
+ for (size_t iislout = 0; iislout < vislout.size(); iislout++)
+ {
+ int islout = vislout[iislout];
+ if (islout == kNegInfinity || islout == kPosInfinity)
+ continue;
+ int iginfTmp = m_pseg->LogicalToPhysicalSurface(islout);
+ Assert(iginfTmp != kNegInfinity);
+ Assert(iginfTmp != kPosInfinity);
+
+ if (fSkipTrSpace && islout >= m_pseg->m_isloutVisLim)
+ continue;
+
+ Rect rsNew;
+ GrSlotOutput * psloutTmp = m_pseg->OutputSlot(islout);
+ bool fHighlightBB = psloutTmp->IsPartOfCluster();
+ // Test below should match the one in CalcIP--
+ // If the glyph has no advance width but does have a real bounding box,
+ // highlight the bounding box. Review: do we want this???
+ if ((psloutTmp->ClusterAdvance() == 0
+ || psloutTmp->GlyphMetricLogUnits(&font, kgmetAdvWidth) == 0)
+ && !psloutTmp->IsSpace())
+ {
+ fHighlightBB = true;
+ }
+ xsGlyphLeft = m_pseg->GlyphLeftEdge(iginfTmp);
+ if (fHighlightBB)
+ {
+ // For now, highlight just the bounding box of this glyph. After we finish
+ // highlighting all glyphs individually, we'll go back and highlight
+ // as one unit any clusters that are completely selected.
+
+// int dysFontAscent;
+// res = GetFontAscentSourceUnits(pgg, &dysFontAscent); // if we restore this code, add in ExtraAscent
+// if (ResultFailed(res)) THROW(WARN(res));
+// dysFontAscent += m_dysXAscent;
+
+ GlyphInfo * pginf = m_pseg->m_prgginf + iginfTmp;
+ float ysForGlyph = pginf->yOffset();
+ float ysGlyphBaseline = m_pseg->m_dysAscent - ysForGlyph; // relative to top
+
+ float ysBbTop = psloutTmp->GlyphMetricLogUnits(&font, kgmetBbTop);
+ float ysBbBottom = psloutTmp->GlyphMetricLogUnits(&font, kgmetBbBottom);
+ float xsBbLeft = psloutTmp->GlyphMetricLogUnits(&font, kgmetBbLeft);
+ float xsBbRight = psloutTmp->GlyphMetricLogUnits(&font, kgmetBbRight);
+
+ rsNew.top = ysGlyphBaseline - ysBbTop - 2;
+ rsNew.bottom = ysGlyphBaseline - ysBbBottom + 2;
+ rsNew.left = xsGlyphLeft + xsBbLeft - 2;
+ rsNew.right = xsGlyphLeft + xsBbRight + 2;
+
+ vfEntireHt.push_back(false);
+ }
+ else
+ {
+ // Highlight entire width of the glyph from the top of the line to the bottom.
+ // Horizontally, highlight from the origin of the glyph to the advance width.
+
+ //xsGlyphRight = xsGlyphLeft + psloutTmp->GlyphMetricLogUnits(&font kgmetAdvWidth);
+ xsGlyphRight = xsGlyphLeft + psloutTmp->ClusterAdvance();
+
+ if (xsGlyphLeft > xsGlyphRight)
+ std::swap(xsGlyphLeft, xsGlyphRight);
+
+ rsNew.top = 0;
+ rsNew.bottom = m_pseg->m_dysHeight;
+ rsNew.left = xsGlyphLeft;
+ rsNew.right = xsGlyphRight;
+
+ vfEntireHt.push_back(true);
+ }
+ vrs.push_back(rsNew);
+
+ if (rgfHighlighted)
+ rgfHighlighted[iginfTmp] = true;
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------------------------
+ If the output slot is a cluster base and all the members of the cluster are highlighted,
+ simply highlight the cluster as a single rectangle, to avoid unslightly gaps
+ between glyphs. (Note that this does not remove the rectangles that were already included
+ to handle the individual members.)
+ The rectangles are relative to the top left of the segment.
+
+ @param islout - output slot index
+ @param vrect - list of rectangles to highlight; add result here
+ @param prgfHighlighted - flags indicating which glyphs have been highlighted, indexed by
+ physical surface position
+----------------------------------------------------------------------------------------------*/
+void SegmentPainter::CalcCompleteCluster(int islout,
+ std::vector<Rect> & vrs, std::vector<bool> & vfEntireHt, bool * prgfHighlighted)
+{
+ //GrEngine * pgreng = m_pseg->EngineImpl();
+
+ GrSlotOutput * pslout = m_pseg->OutputSlot(islout);
+ if (pslout->ClusterRange() == 0)
+ return;
+
+ int iginfDraw = m_pseg->LogicalToPhysicalSurface(islout);
+ if (!prgfHighlighted[iginfDraw])
+ return;
+
+ bool fAllHighlighted = true;
+
+ std::vector<int> visloutCluster;
+ m_pseg->ClusterMembersForGlyph(islout, pslout->ClusterRange(), visloutCluster);
+ for (size_t i = 0; i < visloutCluster.size(); i++)
+ {
+ int iginfMemberDraw = m_pseg->LogicalToPhysicalSurface(visloutCluster[i]);
+ if (!prgfHighlighted[iginfMemberDraw])
+ {
+ fAllHighlighted = false;
+ break;
+ }
+ }
+
+ if (!fAllHighlighted)
+ return;
+
+ // All the members of this cluster are highlighted.
+
+ float xsGlyphStart = m_pseg->GlyphLeftEdge(iginfDraw);
+
+ Rect rsNew;
+ rsNew.top = 0;
+ rsNew.bottom = m_pseg->m_dysHeight;
+ rsNew.left = xsGlyphStart + pslout->ClusterXOffset();
+ rsNew.right = xsGlyphStart + pslout->ClusterAdvance();
+ vrs.push_back(rsNew);
+
+ vfEntireHt.push_back(true);
+}
+
+/*----------------------------------------------------------------------------------------------
+ Add the given rectangle to the vector, eliminating any overlapping areas.
+
+ Note this method can be recursive: if we have to break the current rectangle into
+ two, we'll call the method again the for new
+----------------------------------------------------------------------------------------------*/
+void SegmentPainter::AddRectWithoutOverlaps(std::vector<Rect> & vrect, Rect rectToAdd)
+{
+ std::vector<Rect> vrectMoreToAdd;
+
+ bool fAnythingToAdd = AnyArea(&rectToAdd);
+
+ size_t irect;
+ for (irect = 0; irect < vrect.size(); irect++)
+ {
+ bool f = AdjustRectsToNotOverlap(vrect, irect, &rectToAdd, vrectMoreToAdd);
+ if (!AnyArea(&vrect[irect]))
+ {
+ vrect.erase(vrect.begin() + irect);
+ irect--;
+ }
+ if (!f)
+ {
+ fAnythingToAdd = false;
+ break;
+ }
+ }
+
+ // Now rectToAdd is in a state that it does not intersect with any other rectangles
+ // in the list.
+
+ if (fAnythingToAdd)
+ vrect.push_back(rectToAdd);
+
+ // Add in any extra rectangles that are needed to handle overlaps.
+
+ for (irect = 0; irect < vrectMoreToAdd.size(); irect++)
+ AddRectWithoutOverlaps(vrect, vrectMoreToAdd[irect]);
+}
+
+/*----------------------------------------------------------------------------------------------
+ Return true if the rectangle has an area greater than 0.
+----------------------------------------------------------------------------------------------*/
+bool SegmentPainter::AnyArea(Rect * prect)
+{
+ Assert(prect->left <= prect->right);
+ Assert(prect->top <= prect->bottom);
+
+ return (prect->left < prect->right && prect->top < prect->bottom);
+}
+
+/*----------------------------------------------------------------------------------------------
+ Adjust the rectangle prect2 to avoid overlaps with the given one in the vector.
+ Return false if the rectangle does not need to be added after all.
+
+ If we have to split this rectangle into two, add the second one to vrectMoreToAdd, so
+ we can do the same process on it.
+
+ If we can merge the two rectangles, add the result to vrectMoreToAdd (so it can be properly
+ checked for further overlaps), and adjust the rectangle in the vector so it will be
+ deleted.
+
+ @param vrect - current list of valid rectangles
+ @param irect - item in vrect we're comparing with prect2
+ @param prect2 - rectangle to add
+ @param vrectMoreToAdd - list of accumulated rectangles that need to be added
+ (caused by splitting one into two)
+
+ @return True if we want to add prect2 (possibly adjusted) to the list;
+ false it it is completely subsumed by vrect[irect], or has been merged with it,
+ or has no area.
+----------------------------------------------------------------------------------------------*/
+bool SegmentPainter::AdjustRectsToNotOverlap(std::vector<Rect> & vrect, int irect, Rect * prect2,
+ std::vector<Rect> & vrectMoreToAdd)
+{
+ Rect * prect1 = &(vrect[irect]);
+
+ float xLeft1 = prect1->left;
+ float xRight1 = prect1->right;
+ float yTop1 = prect1->top;
+ float yBottom1 = prect1->bottom;
+ Assert(xLeft1 < xRight1);
+ Assert(yTop1 < yBottom1);
+
+ float xLeft2 = prect2->left;
+ float xRight2 = prect2->right;
+ float yTop2 = prect2->top;
+ float yBottom2 = prect2->bottom;
+ Assert(xLeft2 <= xRight2);
+ Assert(yTop2 <= yBottom2);
+
+ if (!AnyArea(prect2))
+ return false;
+
+ // No overlap: keep both as they are.
+ if (yBottom2 < yTop1 || yBottom1 < yTop2)
+ return true;
+ if (xRight2 < xLeft1 || xRight1 < xLeft2)
+ return true;
+
+ // Present rect subsumes new: ignore new
+ if (xLeft1 <= xLeft2 && xRight2 <= xRight1 && yTop1 <= yTop2 && yBottom2 <= yBottom1)
+ return false;
+
+ // New rect subsumes present: replace.
+ if (xLeft2 <= xLeft1 && xRight1 <= xRight2 && yTop2 <= yTop1 && yBottom1 <= yBottom2)
+ {
+ prect1->bottom = prect1->top; // delete
+ return true; // keep this one
+ }
+
+ // Abutting rectangles of same height or width: merge.
+ if (yTop1 == yTop2 && yBottom1 == yBottom2)
+ {
+ if (xRight1 == xLeft2)
+ {
+ // prect2 is to the right
+ prect2->left = prect1->left;
+ vrectMoreToAdd.push_back(*prect2);
+ prect1->bottom = prect1->top; // delete previous
+ return false;
+ }
+ else if (xRight2 == xLeft1)
+ {
+ // prect2 is to the left
+ prect2->right = prect1->right;
+ vrectMoreToAdd.push_back(*prect2);
+ prect1->bottom = prect1->top; // delete
+ return false;
+ }
+ }
+ if (xLeft1 == xLeft2 && xRight1 == xRight2)
+ {
+ if (yBottom1 == yTop2)
+ {
+ // prect2 is below
+ prect2->top = prect1->top;
+ vrectMoreToAdd.push_back(*prect2);
+ prect1->bottom = prect1->top; // delete
+ return false;
+ }
+ else if (yBottom2 == yTop1)
+ {
+ // prect2 is above
+ prect2->bottom = prect1->bottom;
+ vrectMoreToAdd.push_back(*prect2);
+ prect1->bottom = prect1->top; // delete
+ return false;
+ }
+ }
+
+ // Abutting rectangles of different heights or widths: leave as they are.
+ if (xRight1 == xLeft2 || xRight2 == xLeft1)
+ return true;
+ if (yBottom1 == yTop2 || yBottom2 == yTop1)
+ return true;
+
+ // Overlapping on one axis: adjust smaller rectangle
+ if (yTop1 <= yTop2 && yBottom2 <= yBottom1)
+ {
+ if (xLeft1 <= xLeft2 && xLeft2 < xRight1)
+ {
+ Assert(xRight1 < xRight2); // otherwise rect2 is subsumed
+ prect2->left = prect1->right;
+ return true;
+ }
+ else if (xLeft1 < xRight2 && xRight2 <= xRight1)
+ {
+ Assert(xLeft2 < xLeft1); // otherwise rect2 is subsumed
+ prect2->right = prect1->left;
+ return true;
+ }
+ }
+ else if (yTop2 <= yTop1 && yBottom1 <= yBottom2)
+ {
+ if (xLeft2 <= xLeft1 && xLeft1 < xRight2)
+ {
+ Assert(xRight2 < xRight1); // otherwise rect1 is subsumed
+ prect1->left = prect2->right;
+ return true;
+ }
+ else if (xLeft2 < xRight1 && xRight1 <= xRight2)
+ {
+ Assert(xLeft1 < xLeft2); // otherwise rect1 is subsumed
+ prect1->right = prect2->left;
+ return true;
+ }
+ }
+
+ if (xLeft1 <= xLeft2 && xRight2 <= xRight1)
+ {
+ if (yTop1 <= yTop2 && yTop2 < yBottom1)
+ {
+ Assert(yBottom1 < yBottom2); // otherwise rect2 is subsumed
+ prect2->top = prect1->bottom;
+ return true;
+ }
+ else if (yTop1 < yBottom2 && yBottom2 <= yBottom1)
+ {
+ Assert(yTop2 < yTop1); // otherwise rect2 is subsumed
+ prect2->bottom = prect1->top;
+ return true;
+ }
+ }
+ else if (xLeft2 <= xLeft1 && xRight1 <= xRight2)
+ {
+ if (yTop2 <= yTop1 && yTop1 < yBottom2)
+ {
+ Assert(yBottom2 < yBottom1); // otherwise rect1 is subsumed
+ prect1->top = prect2->bottom;
+ return true;
+ }
+ else if (yTop2 < yBottom1 && yBottom1 <= yBottom2)
+ {
+ Assert(yTop1 < yTop2); // otherwise rect1 is subsumed
+ prect1->bottom = prect2->top;
+ return true;
+ }
+ }
+
+ // Centers overlap: break prect2 into two disjoint rectangles.
+ if (xLeft1 < xLeft2 && xRight2 < xRight1 && yTop2 < yTop1 && yBottom1 < yBottom2)
+ {
+ // create a new rectangle for the bottom of prect2 that is below prect1
+ Rect rectNew = *prect2;
+ rectNew.top = prect1->bottom;
+ prect2->bottom = prect1->top;
+ vrectMoreToAdd.push_back(rectNew);
+ return true;
+ }
+ else if (xLeft2 < xLeft1 && xRight1 < xRight2 && yTop1 < yTop2 && yBottom2 < yBottom1)
+ {
+ // create a new rectangle for the right of prect2 that is to the right of prect1
+ Rect rectNew = *prect2;
+ rectNew.left = prect1->right;
+ prect2->right = prect1->left;
+ vrectMoreToAdd.push_back(rectNew);
+ return true;
+ }
+
+ // Corners overlap: break prect2 into two adjacent rectangles.
+ if (xLeft1 < xLeft2 && xRight1 < xRight2)
+ {
+ // prect2 is partially to the right
+ if (yTop1 < yTop2 && yBottom1 < yBottom2)
+ {
+ // prect2 is partially below
+ Rect rectNew = *prect2;
+ rectNew.left = prect1->right;
+ prect2->top = prect1->bottom;
+ vrectMoreToAdd.push_back(rectNew);
+ return true;
+ }
+ else if (yTop2 < yTop1 && yBottom2 < yBottom1)
+ {
+ // prect2 is partially above
+ Rect rectNew = *prect2;
+ rectNew.left = prect1->right;
+ prect2->bottom = prect1->top;
+ vrectMoreToAdd.push_back(rectNew);
+ return true;
+ }
+ }
+ else if (xLeft2 < xLeft1 && xRight2 < xRight1)
+ {
+ // prect2 is partially to the left
+ if (yTop1 < yTop2 && yBottom1 < yBottom2)
+ {
+ // prect2 is partially below
+ Rect rectNew = *prect2;
+ rectNew.right = prect1->left;
+ prect2->top = prect1->bottom;
+ vrectMoreToAdd.push_back(rectNew);
+ return true;
+ }
+ else if (yTop2 < yTop1 && yBottom2 < yBottom1)
+ {
+ // prect2 is partially above
+ Rect rectNew = *prect2;
+ rectNew.right = prect1->left;
+ prect2->bottom = prect1->top;
+ vrectMoreToAdd.push_back(rectNew);
+ return true;
+ }
+ }
+
+ // No overlap.
+ return true;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Assert that there are no overlaps among all the rectangles in the array, which should
+ be the case if AdjustRectsToNotOverlap is working properly.
+----------------------------------------------------------------------------------------------*/
+void SegmentPainter::AssertNoOverlaps(std::vector<Rect> & vrect)
+{
+#ifdef _DEBUG
+ for (int irect1 = 0; irect1 < signed(vrect.size() - 1); irect1++)
+ {
+ for (int irect2 = irect1 + 1; irect2 < signed(vrect.size()); irect2++)
+ {
+ float xLeft1 = vrect[irect1].left;
+ float xRight1 = vrect[irect1].right;
+ float yTop1 = vrect[irect1].top;
+ float yBottom1 = vrect[irect1].bottom;
+ Assert(xLeft1 < xRight1);
+ Assert(yTop1 < yBottom1);
+
+ float xLeft2 = vrect[irect2].left;
+ float xRight2 = vrect[irect2].right;
+ float yTop2 = vrect[irect2].top;
+ float yBottom2 = vrect[irect2].bottom;
+ Assert(xLeft2 <= xRight2);
+ Assert(yTop2 <= yBottom2);
+
+ Assert(xRight1 <= xLeft2 || xRight2 <= xLeft1 ||
+ yBottom1 <= yTop2 || yBottom2 <= yTop1);
+ }
+ }
+#endif
+}
+
+/*----------------------------------------------------------------------------------------------
+ Add the given line segment to the vector, eliminating any overlapping areas.
+----------------------------------------------------------------------------------------------*/
+void SegmentPainter::AddLineSegWithoutOverlaps(std::vector<LineSeg> & vls, LineSeg lsToAdd)
+{
+ std::vector<LineSeg> vlsMoreToAdd;
+
+ bool fAnythingToAdd = AnyLength(&lsToAdd);
+
+ size_t ils;
+ for (ils = 0; ils < vls.size(); ils++)
+ {
+ bool f = AdjustLineSegsToNotOverlap(vls, ils, &lsToAdd, vlsMoreToAdd);
+ if (!AnyLength(&vls[ils]))
+ {
+ vls.erase(vls.begin() + ils);
+ ils--;
+ }
+ if (!f)
+ {
+ fAnythingToAdd = false;
+ break;
+ }
+ }
+
+ // Now lsToAdd is in a state that it does not overlap with any other line segments
+ // in the list.
+
+ if (fAnythingToAdd)
+ vls.push_back(lsToAdd);
+
+ // Add in any extra line segments that are needed to handle overlaps.
+
+ for (ils = 0; ils < vlsMoreToAdd.size(); ils++)
+ AddLineSegWithoutOverlaps(vls, vlsMoreToAdd[ils]);
+}
+
+/*----------------------------------------------------------------------------------------------
+ Return true if the rectangle has an area greater than 0.
+----------------------------------------------------------------------------------------------*/
+bool SegmentPainter::AnyLength(LineSeg * pls)
+{
+ Assert(pls->left <= pls->right);
+ return (pls->left < pls->right);
+}
+
+/*----------------------------------------------------------------------------------------------
+ Adjust the line segment pls2 to avoid overlaps with the given one in the vector.
+ Return false if the line segment does not need to be added after all.
+
+ If we can merge the two line segments, add the result to vrectMoreToAdd (so it can be properly
+ checked for further overlaps), and adjust the rectangle in the vector so it will be
+ deleted.
+
+ @param vls - current list of valid line segments
+ @param ils - item in vrect we're comparing with pls2
+ @param pls2 - line segment to add
+ @param vlsMoreToAdd - list of accumulated line segments that need to be added
+ (caused by splitting one into two)
+
+ @return True if we want to add pls2 (possibly adjusted) to the list;
+ false it it is completely subsumed by vls[ils], or has been merged with it,
+ or has no length.
+----------------------------------------------------------------------------------------------*/
+bool SegmentPainter::AdjustLineSegsToNotOverlap(std::vector<LineSeg> & vls, int ils, LineSeg * pls2,
+ std::vector<LineSeg> & vlsMoreToAdd)
+{
+ LineSeg * pls1 = &(vls[ils]);
+
+ float xLeft1 = pls1->left;
+ float xRight1 = pls1->right;
+ Assert(xLeft1 < xRight1);
+
+ float xLeft2 = pls2->left;
+ float xRight2 = pls2->right;
+ Assert(xLeft2 <= xRight2);
+
+ if (!AnyLength(pls2))
+ return false;
+
+ // No overlap: keep both as they are.
+ if (xRight2 < xLeft1 || xRight1 < xLeft2)
+ return true;
+
+ // Present line segment subsumes new: ignore new
+ if (xLeft1 <= xLeft2 && xRight2 <= xRight1)
+ return false;
+
+ // New line segment subsumes present: replace.
+ if (xLeft2 <= xLeft1 && xRight1 <= xRight2)
+ {
+ pls1->left = pls1->right; // delete previous
+ return true; // keep this one
+ }
+
+ // Adjacent or partially overlapping line segments: merge.
+ if (xLeft1 <= xLeft2 && xLeft2 <= xRight1 && xRight1 <= xRight2)
+ {
+ pls2->left = xLeft1;
+ vlsMoreToAdd.push_back(*pls2);
+ pls1->left = pls1->right; // delete previous
+ return false;
+ }
+ else if (xLeft2 <= xLeft1 && xLeft1 <= xRight2 && xRight2 <= xRight1)
+ {
+ pls2->right = xRight1;
+ vlsMoreToAdd.push_back(*pls2);
+ pls1->left = pls1->right; // delete previous
+ return false;
+ }
+ else
+ Assert(false);
+
+ return true;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Assert that there are no overlaps among all the rectangles in the array, which should
+ be the case if AdjustRectsToNotOverlap is working properly.
+----------------------------------------------------------------------------------------------*/
+void SegmentPainter::AssertNoOverlaps(std::vector<LineSeg> & vls)
+{
+#ifdef _DEBUG
+ for (int ils1 = 0; ils1 < (int)vls.size() - 1; ils1++)
+ {
+ for (int ils2 = ils1 + 1; ils2 < (int)vls.size(); ils2++)
+ {
+ float xLeft1 = vls[ils1].left;
+ float xRight1 = vls[ils1].right;
+ Assert(xLeft1 < xRight1);
+
+ float xLeft2 = vls[ils2].left;
+ float xRight2 = vls[ils2].right;
+ Assert(xLeft2 <= xRight2);
+
+ Assert(xRight1 <= xLeft2 || xRight2 <= xLeft1);
+ }
+ }
+#endif
+}
+
+/*----------------------------------------------------------------------------------------------
+ Find the next physical location for the IP. Return true if we found one.
+
+ @param pichw - current insertion point, and adjusted result to return
+ @param pfAssocPrev - is the IP "leaning" backwards? adjust to return
+ @param fRight - true if we are moving right
+ @param fAssocPrevMatch - the assoc-prev of the result must match the original selection.
+ This is used when extending range selections; the orientation
+ of the resulting end-point must be the same as previously.
+ This helps avoids circular loops when dealing with reordering.
+ @param fAssocPrevNeeded - value needed for result; ignored if fAssocPrevMatch == false
+ @param pnNextOrPrevSeg - return 1 if we need to try the following segment, -1 if we
+ need to try the previous - CURRENTLY NOT USED for anything
+ practical
+----------------------------------------------------------------------------------------------*/
+bool SegmentPainter::ArrowKeyPositionInternal(
+ int * pichw, bool * pfAssocPrev,
+ bool fRight, bool fAssocPrevMatch, bool fAssocPrevNeeded, int * pnNextOrPrevSeg)
+{
+ int kBefore = true;
+ int kAfter = false;
+
+ bool fResult = false;
+
+ // Handle ligatures.
+
+ fResult = AdjacentLigComponent(pichw, pfAssocPrev, fRight, true);
+ if (fResult)
+ return true;
+
+ // Not within a ligature, or on the outside edge of one.
+
+ int iginfStart;
+ bool fCharRtl;
+ if (*pfAssocPrev)
+ {
+ iginfStart = m_pseg->UnderlyingToPhysicalSurface(*pichw - 1, kAfter);
+ fCharRtl = m_pseg->CharIsRightToLeft(*pichw - 1, kAfter);
+ }
+ else
+ {
+ iginfStart = m_pseg->UnderlyingToPhysicalSurface(*pichw, kBefore);
+ fCharRtl = m_pseg->CharIsRightToLeft(*pichw, kBefore);
+ }
+ if (kNegInfinity == iginfStart || kPosInfinity == iginfStart)
+ {
+ *pnNextOrPrevSeg = 0;
+ return false;
+ }
+
+ //if (*pfAssocPrev)
+ // fCharRtl ? iginfStart : iginfStart++;
+ //else
+ // fCharRtl ? iginfStart++ : iginfStart;
+
+ if (*pfAssocPrev)
+ iginfStart++;
+
+ // At this point iginfStart indicates the index of the glyph we are before.
+
+ int ichwNewIndex;
+ bool fAPNew;
+
+ bool fRtl = m_pseg->rightToLeft();
+ bool fForward = (fRtl) ? !fRight : fRight;
+ int dir = (fForward) ? 1 : -1;
+ int diginf = 0;
+
+ // When moving forward, we first move to the opposite side of the current glyph,
+ // so initially diginf should be zero.
+ if (fForward)
+ diginf = -1;
+
+ while (true)
+ {
+ diginf++;
+ int iginfTry = iginfStart + (dir * diginf);
+
+ if (iginfTry < 0)
+ {
+ *pnNextOrPrevSeg = -1;
+ return false;
+ }
+ if (iginfTry > m_pseg->m_cginf)
+ {
+ *pnNextOrPrevSeg = 1;
+ return false;
+ }
+ if (iginfTry == m_pseg->m_cginf && fForward)
+ {
+ *pnNextOrPrevSeg = 1;
+ return false;
+ }
+
+ bool fAfter;
+ int ichwTryChar;
+ if (iginfTry == m_pseg->m_cginf)
+ {
+ ichwTryChar = m_pseg->PhysicalSurfaceToUnderlying(iginfTry - 1,
+ kPosInfFloat, kPosInfFloat, 0, 0, &fAfter);
+ }
+ else
+ {
+ ichwTryChar = m_pseg->PhysicalSurfaceToUnderlying(iginfTry,
+ (fRight) ? kPosInfFloat : kNegInfFloat,
+ (fRight) ? kPosInfFloat : kNegInfFloat,
+ 0, 0, &fAfter);
+ }
+
+ if (fAfter)
+ {
+ ichwNewIndex = ichwTryChar + 1;
+ fAPNew = true;
+ }
+ else
+ {
+ ichwNewIndex = ichwTryChar;
+ fAPNew = false;
+ }
+
+ if (ichwNewIndex == *pichw)
+ continue; // didn't make any progress
+
+ LgIpValidResult ipvr = isValidInsertionPoint(ichwNewIndex);
+ if (ipvr == kipvrBad)
+ continue; // bad insertion point
+
+ if (fAssocPrevMatch && (fAssocPrevNeeded != fAPNew))
+ {
+ // Try switching fAPNew and see if that gives the right result
+ bool fAPAlt = !fAPNew;
+ int iginfAlt = m_pseg->UnderlyingToPhysicalSurface(
+ fAPAlt ? ichwTryChar - 1 : ichwTryChar + 1,
+ fAPAlt ? kAfter : kBefore);
+ fCharRtl = m_pseg->CharIsRightToLeft(
+ fAPAlt ? ichwTryChar - 1 : ichwTryChar + 1,
+ fAPAlt ? kAfter : kBefore);
+
+ if (fAPAlt)
+ fCharRtl ? iginfAlt : iginfAlt++;
+ else
+ fCharRtl ? iginfAlt++ : iginfAlt;
+
+ if ((fForward && iginfAlt <= iginfStart)
+ || (!fForward && iginfAlt >= iginfStart))
+ {
+ // Switching fAPNew would cause us to move in the opposite direction, so
+ // that's not a good solution. Keep going.
+ if ((iginfStart == 0 && !fForward) || (iginfStart == m_pseg->m_cginf && fForward))
+ {
+ // Can't go any further, so go with this even though AP is wrong.
+ }
+ else
+ continue;
+ }
+ fAPNew = fAPAlt;
+ }
+ break;
+ }
+
+ // If we have a selection within a ligature, adjust to either the right-most or the
+ // left-most.
+ AdjacentLigComponent(&ichwNewIndex, &fAPNew, fRight, false);
+
+ *pichw = ichwNewIndex;
+ *pfAssocPrev = fAPNew;
+ return true;
+}
+
+/****** OLD VERSION
+bool Segment::ArrowKeyPositionInternal(
+ int * pichw, bool * pfAssocPrev,
+ bool fRight, bool fAssocPrevMatch, bool fAssocPrevNeeded, int * pnNextOrPrevSeg)
+{
+ int kBefore = true;
+ int kAfter = false;
+
+ bool fResult = false;
+
+ // Handle ligatures.
+
+ fResult = AdjacentLigComponent(pichw, pfAssocPrev, fRight, true);
+ if (fResult)
+ return true;
+
+ // Not within a ligature, or on the outside edge of one.
+
+ int igbbOutput;
+ bool fCharRtl;
+ if (*pfAssocPrev)
+ {
+ igbbOutput = UnderlyingToPhysicalSurface(*pichw - 1, kAfter);
+ fCharRtl = CharIsRightToLeft(*pichw - 1, kAfter);
+ }
+ else
+ {
+ igbbOutput = UnderlyingToPhysicalSurface(*pichw, kBefore);
+ fCharRtl = CharIsRightToLeft(*pichw, kBefore);
+ }
+ if (kNegInfinity == igbbOutput || kPosInfinity == igbbOutput)
+ {
+ *pnNextOrPrevSeg = 0;
+ return false;
+ }
+
+ if (*pfAssocPrev)
+ fCharRtl ? igbbOutput : igbbOutput++;
+ else
+ fCharRtl ? igbbOutput++ : igbbOutput;
+
+ bool fBackward = (fRight == m_fWsRtl);
+
+ // Try to find an underlying position that would produce an IP in the expected place.
+ // First look in the same direction in the underlying text, then, if necessary, try the
+ // opposite direction (which may be necessary when reordering occurred).
+
+ int igbbNeed = (fRight) ? igbbOutput + 1 : igbbOutput - 1;
+ int ichwNew;
+ bool fAssocPrevNew;
+
+ while (0 <= igbbNeed && igbbNeed <= NumGbb()) // include end-points--we may need to
+ // be before or after them.
+ {
+ int dichwInc = fBackward ? -1 : 1;
+ int dichwLim = m_ichwMin + (fBackward ? 0 : m_dichwLim);
+
+ int nDirectionsTried = 0;
+// while (nDirectionsTried < 2) -- Nope, for now we are being very strict above moving
+ while (nDirectionsTried < 1) // only in the desired direction
+ {
+ for (int ichwTry = *pichw;
+ ((dichwInc == 1 && ichwTry <= dichwLim) ||
+ (dichwInc == -1 && ichwTry >= dichwLim));
+ ichwTry += dichwInc)
+ {
+ int igbbTry = UnderlyingToPhysicalSurface(ichwTry, kBefore);
+ fCharRtl = CharIsRightToLeft(ichwTry, kBefore);
+ igbbTry = fCharRtl ? igbbTry + 1 : igbbTry;
+ if (igbbTry == igbbNeed &&
+ (ichwTry != *pichw || *pfAssocPrev) &&
+ (!fAssocPrevMatch || !fAssocPrevNeeded))
+ {
+ ichwNew = ichwTry;
+ fAssocPrevNew = false;
+ goto LFixLigatures;
+ }
+
+ igbbTry = UnderlyingToPhysicalSurface(ichwTry - 1, kAfter);
+ fCharRtl = CharIsRightToLeft(ichwTry - 1, kAfter);
+ igbbTry = fCharRtl ? igbbTry : igbbTry + 1;
+ if (igbbTry == igbbNeed &&
+ (ichwTry != *pichw || !*pfAssocPrev) &&
+ (!fAssocPrevMatch || fAssocPrevNeeded))
+ {
+ ichwNew = ichwTry;
+ fAssocPrevNew = true;
+ goto LFixLigatures;
+ }
+ }
+
+ nDirectionsTried++;
+
+ // Didn't find an answer going the expected way; try the opposite direction.
+ dichwInc = (fRight) ? -1 : 1;
+ dichwLim = m_ichwMin + ((fRight) ? 0 : m_dichwLim);
+ }
+
+ // Didn't find an underlying position that would produce an IP in the next glyph;
+ // try the glyph after that.
+
+ igbbNeed = (fRight) ? igbbNeed + 1 : igbbNeed - 1;
+ }
+
+ if (igbbNeed >= NumGbb())
+ *pnNextOrPrevSeg = 1; // try next segment
+ else if (igbbNeed < 0)
+ *pnNextOrPrevSeg = -1; // try previous segment
+ else
+ {
+ Assert(false);
+ *pnNextOrPrevSeg = 0;
+ }
+
+ return false;
+
+LFixLigatures:
+
+ // If we have a selection within a ligature, adjust to either the right-most or the
+ // left-most.
+ AdjacentLigComponent(&ichwNew, &fAssocPrevNew, fRight, false);
+
+ *pichw = ichwNew;
+ *pfAssocPrev = fAssocPrevNew;
+ return true;
+}
+******************/
+
+/*----------------------------------------------------------------------------------------------
+ Return position for the physically next or previous ligature component, or the right-most
+ or left-most. Return true if this routine handled it.
+
+ @param pichw - starting underlying position
+ @param pfAssocPrev - is selection leaning backwards?
+ @param fMovingRight - true if right arrow key was pressed, false if left
+ @param fMove - if true, we want to find the next one over from *pichw;
+ if false, we want to find either the right-most
+ (fMovingRight = false)
+ or the left-most (fMovingRight = true)
+----------------------------------------------------------------------------------------------*/
+bool SegmentPainter::AdjacentLigComponent(int * pichw, bool * pfAssocPrev,
+ bool fMovingRight, bool fMove)
+{
+ int kBefore = true;
+ int kAfter = false;
+
+ GrEngine * pgreng = m_pseg->EngineImpl();
+ GrGlyphTable * pgtbl = (pgreng) ? pgreng->GlyphTable() : NULL;
+
+ if (!pgtbl)
+ {
+ return false;
+ }
+
+ // Find the slot containing the ligature glyph.
+
+ int ichwSegOffset = *pichw - m_pseg->m_ichwMin;
+ int isloutLig = kNegInfinity;
+ if (*pfAssocPrev)
+ {
+ if (ichwSegOffset - 1 >= m_pseg->m_ichwAssocsMin && ichwSegOffset - 1 < m_pseg->m_ichwAssocsLim)
+ isloutLig = m_pseg->m_prgisloutLigature[ichwSegOffset - 1 - m_pseg->m_ichwAssocsMin];
+ }
+ else
+ {
+ if (ichwSegOffset >= m_pseg->m_ichwAssocsMin && ichwSegOffset < m_pseg->m_ichwAssocsLim)
+ isloutLig = m_pseg->m_prgisloutLigature[ichwSegOffset - m_pseg->m_ichwAssocsMin];
+ }
+
+ if (isloutLig == kNegInfinity)
+ {
+ return false;
+ }
+
+ if (*pfAssocPrev)
+ ichwSegOffset--;
+
+ bool fGlyphRtl = (*pfAssocPrev) ?
+ m_pseg->CharIsRightToLeft(*pichw - 1, kAfter) :
+ m_pseg->CharIsRightToLeft(*pichw, kBefore);
+
+ // Comparing right-sides.
+ bool fIPOnRight = (fGlyphRtl != *pfAssocPrev);
+
+ // When two ligatures are horizontally equal, take vertical position into account.
+ // In left-to-right writing systems, moving right means moving down.
+ bool fMovingDown = (fMovingRight != m_pseg->m_fWsRtl);
+
+ // Get the coordinates for all the ligatures.
+
+ std::vector<float> vysTops;
+ std::vector<float> vysBottoms;
+ std::vector<float> vxsLefts;
+ std::vector<float> vxsRights;
+
+ float ysTopCurr, ysBottomCurr, xsLeftCurr, xsRightCurr;
+ int icompCurr = -1;
+ if (fMovingRight)
+ ysTopCurr = ysBottomCurr = xsLeftCurr = xsRightCurr = kNegInfFloat;
+ else
+ ysTopCurr = ysBottomCurr = xsLeftCurr = xsRightCurr = kPosInfFloat;
+
+ GrSlotOutput * pslout = m_pseg->OutputSlot(isloutLig);
+ //int iginfOutput = m_pseg->LogicalToPhysicalSurface(isloutLig);
+
+ int icomp;
+ for (icomp = 0; icomp < pslout->NumberOfComponents(); icomp++)
+ {
+ float xsLeft, ysTop, xsRight, ysBottom;
+
+ // TODO: Rework ComponentIndexForGlyph to take slati rather than iForGlyph.
+ int slati = pslout->ComponentId(icomp);
+ int iForGlyph = pgtbl->ComponentIndexForGlyph(pslout->GlyphID(), slati);
+ pgtbl->ComponentBoxLogUnits(m_pseg->m_xysEmSquare, pslout->GlyphID(), iForGlyph,
+ m_pseg->m_mFontEmUnits, m_pseg->m_dysAscent,
+ &xsLeft, &ysTop, &xsRight, &ysBottom);
+
+ vysTops.push_back(ysTop);
+ vysBottoms.push_back(ysBottom);
+ vxsLefts.push_back(xsLeft);
+ vxsRights.push_back(xsRight);
+
+ // If we're moving, record this component as the one we're moving relative to.
+ if (fMove && icomp == m_pseg->m_prgiComponent[ichwSegOffset - m_pseg->m_ichwAssocsMin])
+ {
+ icompCurr = icomp;
+ ysTopCurr = ysTop;
+ ysBottomCurr = ysBottom;
+ xsLeftCurr = xsLeft;
+ xsRightCurr = xsRight;
+ }
+ }
+
+ float xsHorizCurr = (fIPOnRight) ? xsRightCurr : xsLeftCurr;
+ float xsHorizNext;
+
+ // Find the first ligature that is in the proper direction.
+ int icompNext;
+ for (icompNext = 0; icompNext < pslout->NumberOfComponents(); icompNext++)
+ {
+ if (icompNext != icompCurr)
+ {
+ float xsHorizNext = (fIPOnRight) ? vxsRights[icompNext] : vxsLefts[icompNext];
+ if (fMovingRight && xsHorizNext > xsHorizCurr)
+ break;
+ else if (!fMovingRight && xsHorizNext < xsHorizCurr)
+ break;
+ }
+ }
+ if (icompNext == pslout->NumberOfComponents())
+ {
+ bool fResult;
+ // No component in the proper direction. Can we switch to the opposite edge of the
+ // current component?
+ if (fMovingRight && !fIPOnRight)
+ {
+ if (fGlyphRtl)
+ --(*pichw);
+ else
+ ++(*pichw);
+ *pfAssocPrev = !*pfAssocPrev;
+ fResult = true;
+ }
+ else if (!fMovingRight && fIPOnRight)
+ {
+ if (fGlyphRtl)
+ ++(*pichw);
+ else
+ --(*pichw);
+
+ *pfAssocPrev = !*pfAssocPrev;
+ fResult = true;
+ }
+ else
+ fResult = false;
+ return fResult;
+ }
+
+ xsHorizNext = (fIPOnRight) ? vxsRights[icompNext] : vxsLefts[icompNext];
+
+ // Now find the closest one in the proper direction.
+ for (icomp = icompNext + 1; icomp < pslout->NumberOfComponents(); icomp++)
+ {
+ if (icomp != icompCurr)
+ {
+ float xsHorizTry = (fIPOnRight) ? vxsRights[icomp] : vxsLefts[icomp];
+
+ if (fMovingRight && xsHorizTry > xsHorizCurr &&
+ (xsHorizTry < xsHorizNext ||
+ (xsHorizTry == xsHorizNext &&
+ (vysTops[icomp] > vysTops[icompNext]) == fMovingDown)))
+ {
+ icompNext = icomp;
+ xsHorizNext = xsHorizTry;
+ }
+ else if (!fMovingRight && xsHorizTry < xsHorizCurr &&
+ (xsHorizTry > xsHorizNext ||
+ (xsHorizTry == xsHorizNext &&
+ (vysTops[icomp] > vysTops[icompNext]) == fMovingDown)))
+ {
+ icompNext = icomp;
+ xsHorizNext = xsHorizTry;
+ }
+ }
+ }
+
+ *pichw = pslout->UnderlyingComponent(icompNext) + m_pseg->m_ichwMin;
+ if (*pfAssocPrev)
+ ++(*pichw);
+
+ return true;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Scale the x-coordinate by the scaling factors for this painter.
+----------------------------------------------------------------------------------------------*/
+float SegmentPainter::ScaleX(float xs, Rect rs, Rect rd)
+{
+ //return rs.MapXTo(xs, rd);
+
+ float dxs = rs.right - rs.left;
+ float dxd = rd.right - rd.left;
+ Assert(dxs > 0);
+ if (dxs == dxd)
+ return xs + rd.left - rs.left;
+
+ float xd = rd.left + GrEngine::GrFloatMulDiv((xs - rs.left), dxd, dxs);
+ return xd;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Scale the y-coordinate by the scaling factors for this painter.
+----------------------------------------------------------------------------------------------*/
+float SegmentPainter::ScaleY(float ys, Rect rs, Rect rd)
+{
+ //return rs.MapYTo(ys, rd);
+
+ float dys = rs.bottom - rs.top;
+ float dyd = rd.bottom - rd.top;
+ Assert(dys > 0);
+ if (dys == dyd)
+ return ys + rd.top - rs.top;
+ float yd = rd.top + GrEngine::GrFloatMulDiv((ys - rs.top), dyd, dys);
+ return yd;
+}
+
+
+//:>********************************************************************************************
+//:> Low-level platform-specific drawing methods
+//:>********************************************************************************************
+
+/*----------------------------------------------------------------------------------------------
+ Make sure the font is set to use the character properties required by this segment.
+----------------------------------------------------------------------------------------------*/
+void SegmentPainter::SetFontProps(unsigned long clrFore, unsigned long clrBack)
+{
+ return;
+}
+
+} // namespace gr