// $Id: TGHtmlIndex.cxx,v 1.2 2007/05/20 23:22:13 rdm Exp $ // Author: Valeriy Onuchin 03/05/2007 /************************************************************************* * Copyright (C) 1995-2001, Rene Brun, Fons Rademakers and Reiner Rohlfs * * All rights reserved. * * * * For the licensing terms see $ROOTSYS/LICENSE. * * For the list of contributors see $ROOTSYS/README/CREDITS. * *************************************************************************/ /************************************************************************** HTML widget for xclass. Based on tkhtml 1.28 Copyright (C) 1997-2000 D. Richard Hipp Copyright (C) 2002-2003 Hector Peraza. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. **************************************************************************/ // Routines that deal with indexes #include #include #include #include "TGHtml.h" //////////////////////////////////////////////////////////////////////////////// /// Return a pointer to the Nth TGHtmlElement in the list. If there /// is no Nth element, return 0 if flag==0 and return either the first /// or last element (whichever is closest) if flag!=0 TGHtmlElement *TGHtml::TokenByIndex(int N, int /*flag*/) { TGHtmlElement *p; int n; if (N == 0) return fPFirst; if (N > fNToken / 2) { // Start at the end and work back toward the beginning for (p = fPLast, n = fNToken; p; p = p->fPPrev) { if (p->fType != Html_Block) { if (p->fElId == N) break; --n; } } } else { // Start at the beginning and work forward for (p = fPFirst; p; p = p->fPNext) { if (p->fType != Html_Block) { --N; if (N == p->fElId) break; } } } return p; } //////////////////////////////////////////////////////////////////////////////// /// Return the token number for the given TGHtmlElement int TGHtml::TokenNumber(TGHtmlElement *p) { //int n = 0; if (!p) return -1; return p->fElId; /// while (p) { /// if (p->fType != Html_Block) ++n; /// p = p->fPPrev; /// } /// /// return n; } //////////////////////////////////////////////////////////////////////////////// /// Find the maximum index for the given token void TGHtml::MaxIndex(TGHtmlElement *p, int *pIndex, int isLast) { if (p == 0) { *pIndex = 0; } else { switch (p->fType) { case Html_Text: *pIndex = p->fCount - isLast; break; case Html_Space: if (p->fStyle.fFlags & STY_Preformatted) { *pIndex = p->fCount - isLast; } else { *pIndex = 0; } break; default: *pIndex = 0; break; } } } //////////////////////////////////////////////////////////////////////////////// /// Given a Block and an x coordinate, find the Index of the character /// that is closest to the given x coordinate. /// /// The x-coordinate might specify a point to the left of the block, /// in which case the procedure returns the first token and a character /// index of 0. Or the x-coordinate might specify a point to the right /// of the block, in which case the last token is returned with an index /// equal to its last character. void TGHtml::FindIndexInBlock(TGHtmlBlock *pBlock, int x, TGHtmlElement **ppToken, int *pIndex) { TGHtmlElement *p; TGFont *font; int len; int n; p = pBlock->fPNext; font = GetFont(p->fStyle.fFont); if (x <= pBlock->fLeft) { *ppToken = p; *pIndex = 0; return; } else if (x >= pBlock->fRight) { *ppToken = p; *pIndex = 0; while (p && p->fType != Html_Block) { *ppToken = p; p = p->fPNext; } p = *ppToken; if (p && p->fType == Html_Text) { *pIndex = p->fCount - 1; } return; } if (pBlock->fN == 0) { *ppToken = p; *pIndex = 0; } n = font->MeasureChars(pBlock->fZ, pBlock->fN, x - pBlock->fLeft, 0, &len); *pIndex = 0; *ppToken = 0; while (p && n >= 0) { switch (p->fType) { case Html_Text: if (n < p->fCount) { *pIndex = n; } else { *pIndex = p->fCount - 1; } *ppToken = p; n -= p->fCount; break; case Html_Space: if (p->fStyle.fFlags & STY_Preformatted) { if (n < p->fCount) { *pIndex = n; } else { *pIndex = p->fCount - 1; } *ppToken = p; n -= p->fCount; } else { *pIndex = 0; *ppToken = p; --n; } break; default: break; } if (p) p = p->fPNext; } } //////////////////////////////////////////////////////////////////////////////// /// Convert an Element-based index into a Block-based index. /// /// In other words, given a pointer to an element and an index /// of a particular character within that element, compute a /// pointer to the TGHtmlBlock used to display that character and /// the index in the TGHtmlBlock of the character. void TGHtml::IndexToBlockIndex(SHtmlIndex_t sIndex, TGHtmlBlock **ppBlock, int *piIndex) { int n = sIndex.fI; TGHtmlElement *p; if (sIndex.fP == 0) { *ppBlock = 0; *piIndex = 0; return; } p = sIndex.fP->fPPrev; while (p && p->fType != Html_Block) { switch (p->fType) { case Html_Text: n += p->fCount; break; case Html_Space: if (p->fStyle.fFlags & STY_Preformatted) { n += p->fCount; } else { n++; } break; default: break; } p = p->fPPrev; } if (p) { *ppBlock = (TGHtmlBlock *) p; *piIndex = n; return; } for (p = sIndex.fP; p && p->fType != Html_Block; p = p->fPNext) {} *ppBlock = (TGHtmlBlock *) p; *piIndex = 0; } //////////////////////////////////////////////////////////////////////////////// /// Modify an index for both pointer and char +/-/=N int TGHtml::IndexMod(TGHtmlElement **pp, int *ip, char *cp) { char nbuf[50]; int i, x, cnt, ccnt[2], cflag[2]; if (pp == 0 || !*pp) return -1; ccnt[0] = ccnt[1] = cflag[0] = cflag[1] = 0; x = 0; while (*cp && x < 2) { cnt = 0; i = 1; while (i < 45 && isdigit(cp[i])) { nbuf[i-1] = cp[i]; i++; } if (i > 1) { nbuf[i-1] = 0; cnt = atoi(nbuf); if (cnt < 0) return -1; } switch (*cp) { case '+': if (i == 1) ccnt[x] = 1; else ccnt[x] = cnt; break; case '-': if (i == 1) ccnt[x] = -1; else ccnt[x] = -cnt; break; case '=': ccnt[x] = 0; cflag[x] = 1; break; default: return -1; } cp += i; ++x; } if (ccnt[0] > 0) { for (i = 0; i < ccnt[0] && (*pp)->fPNext; ++i) { *pp = (*pp)->fPNext; while ((*pp)->fType == Html_Block && (*pp)->fPNext) { *pp = (*pp)->fPNext; } } } else if (ccnt[0] < 0) { for (i = 0; ccnt[0] < i && (*pp)->fPPrev; --i) { //printf("i=%d, cnt=%d\n", i, ccnt[0]); *pp = (*pp)->fPPrev; while ((*pp)->fType == Html_Block && (*pp)->fPPrev) { *pp = (*pp)->fPPrev; } } } if (ccnt[1] > 0) { for (i = 0; i < ccnt[1]; ++i) (*ip)++; } else if (ccnt[1] < 0) { for (i = 0; i > ccnt[1]; --i) (*ip)--; } return 0; } //////////////////////////////////////////////////////////////////////////////// /// Given a base index name (without any modifiers) return a pointer /// to the token described, and the character within that token. /// /// Valid input forms include: /// /// N.M Token number N (with numbering starting at 1) and /// character number M (with numbering starting at 0). /// /// M.X Like above, but token is markup and X is an attribute. /// /// begin The start of all text /// /// end The end of all text /// /// N.last Last character of token number N. /// /// N.end One past last character of token number N. /// /// sel.first First character of the selection. /// /// sel.last Last character of the selection. /// /// sel.end On past last character of the selection. /// /// insert The character holding the insertion cursor. /// /// @X,Y The character a location X,Y of the clipping window. /// /// &DOM The DOM Address of a token. /// /// Zero is returned if we are successful and non-zero if there is /// any kind of error. /// /// If the given token doesn't exist (for example if there are only 10 /// tokens and 11.5 is requested) then *ppToken is left pointing to NULL. /// But the function still returns 0 for success. int TGHtml::DecodeBaseIndex(const char *baseIx, TGHtmlElement **ppToken, int *pIndex) { int i, n, x, y; TGHtmlElement *p = 0; TGHtmlBlock *pBlock; TGHtmlBlock *pNearby; int dist = 1000000; int rc = 0; char buf[200], *base = buf, *suffix, *ep; strlcpy(buf, baseIx, sizeof(buf)); while (isspace((unsigned char)*base)) base++; ep = base; while (*ep && !isspace((unsigned char)*ep)) ep++; *ep = 0; if ((suffix = strchr(base, ':'))) *suffix = 0; switch (*base) { case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '0': // coverity[secure_coding] n = sscanf(base, "%d.%d", &x, &y); if (n > 0) { p = *ppToken = TokenByIndex(x, 0); } if (n == 2) { *pIndex = y; } else { for (i = 1; isdigit(base[i]); ++i) {} if (base[i] == 0) { *pIndex = 0; } else if (strcmp(&base[i], ".last") == 0) { MaxIndex(p, pIndex, 1); } else if (strcmp(&base[i], ".end") == 0) { MaxIndex(p, pIndex, 0); (*pIndex)++; } else { if (n == 1 && p && p->IsMarkup() && base[i] == '.' && p->MarkupArg(fZBase + i + 1, 0)) { *pIndex = 0; } else { rc = 1; } } } break; case 'b': if (strcmp(base, "begin") == 0) { p = *ppToken = fPFirst; *pIndex = 0; } else { rc = 1; } break; case 'e': if (strcmp(base, "end") == 0) { p = *ppToken = fPLast; MaxIndex(p, pIndex, 0); } else { rc = 1; } break; case 'l': if (strcmp(base, "last") == 0) { p = *ppToken = fPLast; MaxIndex(p, pIndex, 1); } else { rc = 1; } break; case 's': if (strcmp(base, "sel.first") == 0) { *ppToken = fSelBegin.fP; *pIndex = fSelBegin.fI; } else if (strcmp(base, "sel.last") == 0) { *ppToken = fSelEnd.fP; *pIndex = fSelEnd.fI; } else if (strcmp(base, "sel.end") == 0) { *ppToken = fSelEnd.fP; *pIndex = fSelEnd.fI + 1; } else { rc = 1; } break; case 'i': if (strcmp(baseIx, "insert") == 0) { *ppToken = fIns.fP; *pIndex = fIns.fI; } else { rc = 1; } break; #if 0 case '&': *pIndex = 0; if (DomIdLookup("id", base + 1, ppToken)) rc = 1; break; #endif case '@': n = sscanf(base, "@%d,%d", &x, &y); if (n != 2) { rc = 1; break; } x += fVisible.fX; y += fVisible.fY; pNearby = 0; *ppToken = fPLast; *pIndex = 0; for (pBlock = fFirstBlock; pBlock; pBlock = pBlock->fBNext) { int dotest; if (pBlock->fN == 0) { switch (pBlock->fPNext->fType) { case Html_LI: case Html_IMG: case Html_INPUT: case Html_TEXTAREA: case Html_SELECT: dotest = 1; break; default: dotest = 0; break; } } else { dotest = 1; } if (dotest) { if (pBlock->fTop <= y && pBlock->fBottom >= y) { if (pBlock->fLeft > x) { if (pBlock->fLeft - x < dist) { dist = pBlock->fLeft - x; pNearby = pBlock; } } else if (pBlock->fRight < x) { if (x - pBlock->fRight < dist) { dist = x - pBlock->fRight; pNearby = pBlock; } } else { FindIndexInBlock(pBlock, x, ppToken, pIndex); break; } } else { int distY; int distX; if (pBlock->fBottom < y) { distY = y - pBlock->fBottom; } else { distY = pBlock->fTop - y; } if (pBlock->fLeft > x) { distX = pBlock->fLeft - x; } else if (pBlock->fRight < x) { distX = x - pBlock->fRight; } else { distX = 0; } if (distX + 4*distY < dist) { dist = distX + 4*distY; pNearby = pBlock; } } } } if (pBlock == 0) { if (pNearby) { FindIndexInBlock(pNearby, x, ppToken, pIndex); } } break; default: rc = 1; break; } if (suffix) IndexMod(ppToken, pIndex, suffix + 1); return rc; } //////////////////////////////////////////////////////////////////////////////// /// This routine decodes a complete index specification. A complete /// index consists of the base specification followed by modifiers. int TGHtml::GetIndex(const char *zIndex, TGHtmlElement **ppToken, int *pIndex) { return DecodeBaseIndex(zIndex, ppToken, pIndex); }