summaryrefslogtreecommitdiffstats
path: root/tkhtml1/generic/htmlindex.c
diff options
context:
space:
mode:
Diffstat (limited to 'tkhtml1/generic/htmlindex.c')
-rw-r--r--tkhtml1/generic/htmlindex.c505
1 files changed, 505 insertions, 0 deletions
diff --git a/tkhtml1/generic/htmlindex.c b/tkhtml1/generic/htmlindex.c
new file mode 100644
index 0000000..7b488a1
--- /dev/null
+++ b/tkhtml1/generic/htmlindex.c
@@ -0,0 +1,505 @@
+/*
+** Routines that deal with indexes
+**
+** Copyright (C) 1997-2000 D. Richard Hipp
+**
+** 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., 59 Temple Place - Suite 330,
+** Boston, MA 02111-1307, USA.
+**
+** Author contact information:
+** drh@acm.org
+** http://www.hwaci.com/drh/
+*/
+#include <ctype.h>
+#include <tk.h>
+#include <string.h>
+#include "htmlindex.h"
+
+/*
+** Return a pointer to the Nth HtmlElement 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
+*/
+HtmlElement *HtmlTokenByIndex(HtmlWidget *htmlPtr, int N, int flag){
+ HtmlElement *p;
+ int n;
+
+ if( N > htmlPtr->nToken/2 ){
+ /* Start at the end and work back toward the beginning */
+ for(p=htmlPtr->pLast, n=htmlPtr->nToken; p; p=p->base.pPrev){
+ if( p->base.type!=Html_Block ){
+ if( n==N ){ TestPoint(0); break; }
+ n--;
+ TestPoint(0);
+ }else{
+ TestPoint(0);
+ }
+ }
+ }else{
+ /* Start at the beginning and work forward */
+ for(p=htmlPtr->pFirst; p; p = p->base.pNext){
+ if( p->base.type!=Html_Block ){
+ N--;
+ if( N<=0 ){ TestPoint(0); break; }
+ }else{
+ TestPoint(0);
+ }
+ }
+ }
+ return p;
+}
+
+/*
+** Return the token number for the given HtmlElement
+*/
+int HtmlTokenNumber(HtmlElement *p){
+ int n = 0;
+
+ while( p ){
+ if( p->base.type!=Html_Block ){
+ TestPoint(0);
+ n++;
+ }else{
+ TestPoint(0);
+ }
+ p = p->base.pPrev;
+ }
+ return n;
+}
+
+/*
+** Find the maximum index for the given token
+*/
+static void maxIndex(HtmlElement *p, int *pIndex){
+ if( p==0 ){
+ *pIndex = 0;
+ TestPoint(0);
+ }else{
+ switch( p->base.type ){
+ case Html_Text:
+ *pIndex = p->base.count-1;
+ TestPoint(0);
+ break;
+ case Html_Space:
+ if( p->base.style.flags & STY_Preformatted ){
+ *pIndex = p->base.count-1;
+ TestPoint(0);
+ }else{
+ *pIndex = 0;
+ TestPoint(0);
+ }
+ break;
+ default:
+ *pIndex = 0;
+ TestPoint(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.
+*/
+static void FindIndexInBlock(
+ HtmlWidget *htmlPtr, /* The widget */
+ HtmlBlock *pBlock, /* The block */
+ int x, /* The x coordinate */
+ HtmlElement **ppToken, /* Write the closest token here */
+ int *pIndex /* Write the charater index in ppToken here */
+){
+ HtmlElement *p;
+ Tk_Font font;
+ int len;
+ int n;
+
+ p = pBlock->base.pNext;
+ HtmlLock(htmlPtr);
+ font = HtmlGetFont(htmlPtr, p->base.style.font);
+ if( HtmlUnlock(htmlPtr) ){
+ *ppToken = p;
+ *pIndex = 0;
+ return;
+ }
+ if( x <= pBlock->left ){
+ *ppToken = p;
+ *pIndex = 0;
+ TestPoint(0);
+ return;
+ }else if( x>= pBlock->right ){
+ *ppToken = p;
+ *pIndex = 0;
+ while( p && p->base.type!=Html_Block ){
+ *ppToken = p;
+ p = p->base.pNext;
+ TestPoint(0);
+ }
+ p = *ppToken;
+ if( p && p->base.type==Html_Text ){
+ *pIndex = p->base.count - 1;
+ TestPoint(0);
+ }else{
+ TestPoint(0);
+ }
+ return;
+ }
+ if( pBlock->n==0 ){
+ *ppToken = p;
+ *pIndex = 0;
+ TestPoint(0);
+ }else{
+ TestPoint(0);
+ }
+ n = Tk_MeasureChars(font, pBlock->z, pBlock->n, x - pBlock->left, 0, &len);
+ *pIndex = 0;
+ *ppToken = 0;
+ while( p && n>=0 ){
+ switch( p->base.type ){
+ case Html_Text:
+ if( n<p->base.count ){
+ *pIndex = n;
+ TestPoint(0);
+ }else{
+ *pIndex = p->base.count - 1;
+ TestPoint(0);
+ }
+ *ppToken = p;
+ n -= p->base.count;
+ break;
+ case Html_Space:
+ if( p->base.style.flags & STY_Preformatted ){
+ if( n<p->base.count ){
+ *pIndex = n;
+ TestPoint(0);
+ }else{
+ *pIndex = p->base.count - 1;
+ TestPoint(0);
+ }
+ *ppToken = p;
+ n -= p->base.count;
+ }else{
+ *pIndex = 0;
+ *ppToken = p;
+ n--;
+ TestPoint(0);
+ }
+ break;
+ default:
+ TestPoint(0);
+ break;
+ }
+ if( p ){
+ p = p->base.pNext;
+ TestPoint(0);
+ }else{
+ TestPoint(0);
+ }
+ }
+}
+
+/*
+** 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 HtmlBlock used to display that character and
+** the index in the HtmlBlock of the character.
+*/
+void HtmlIndexToBlockIndex(
+ HtmlWidget *htmlPtr, /* The widget */
+ HtmlIndex sIndex, /* The index to be translated */
+ HtmlBlock **ppBlock, /* Write the corresponding block here */
+ int *piIndex /* Write the block index here */
+){
+ int n = sIndex.i;
+ HtmlElement *p;
+
+ if( sIndex.p==0 ){
+ *ppBlock = 0;
+ *piIndex = 0;
+ TestPoint(0);
+ return;
+ }
+ p = sIndex.p->base.pPrev;
+ while( p && p->base.type!=Html_Block ){
+ switch( p->base.type ){
+ case Html_Text:
+ n += p->base.count;
+ TestPoint(0);
+ break;
+ case Html_Space:
+ if( p->base.style.flags & STY_Preformatted ){
+ n += p->base.count;
+ TestPoint(0);
+ }else{
+ n++;
+ TestPoint(0);
+ }
+ break;
+ default:
+ TestPoint(0);
+ break;
+ }
+ p = p->base.pPrev;
+ }
+ if( p ){
+ *ppBlock = &p->block;
+ *piIndex = n;
+ TestPoint(0);
+ return;
+ }
+ for(p=sIndex.p; p && p->base.type!=Html_Block; p=p->base.pNext){
+ TestPoint(0);
+ }
+ *ppBlock = &p->block;
+ *piIndex = 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).
+**
+** end The end of all text
+**
+** N.last Last character of token number N.
+**
+** sel.first First character of the selection.
+**
+** sel.last Last character of the selection.
+**
+** ins The character holding the insertion cursor.
+**
+** @X,Y The character a location X,Y of the clipping window.
+**
+** 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.
+*/
+static int DecodeBaseIndex(
+ HtmlWidget *htmlPtr, /* The HTML widget we are dealing with */
+ const char *zBase, /* The base index string */
+ HtmlElement **ppToken, /* Write the pointer to the token here */
+ int *pIndex /* Write the character offset here */
+){
+ int x, y;
+ int n;
+ int i;
+ HtmlElement *p;
+ HtmlBlock *pBlock;
+ HtmlBlock *pNearby;
+ int dist = 1000000;
+ int rc = 0;
+
+ while( isspace(*zBase) ){ TestPoint(0); zBase++; }
+ switch( *zBase ){
+ case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9':
+ n = sscanf(zBase,"%d.%d",&x,&y);
+ if( n>0 ){
+ p = *ppToken = HtmlTokenByIndex(htmlPtr, x, 0);
+ TestPoint(0);
+ }else{
+ TestPoint(0);
+ }
+ if( n==2 ){
+ *pIndex = y;
+ TestPoint(0);
+ }else{
+ for(i=1; isdigit(zBase[i]); i++){ TestPoint(0); }
+ if( zBase[i]==0 ){
+ *pIndex = 0;
+ TestPoint(0);
+ }else if( strcmp(&zBase[i],".last")==0 ){
+ maxIndex(p,pIndex);
+ TestPoint(0);
+ }else{
+ rc = 1;
+ TestPoint(0);
+ }
+ }
+ break;
+ case 'e':
+ if( strcmp(zBase,"end")==0 ){
+ p = *ppToken = htmlPtr->pLast;
+ maxIndex(p,pIndex);
+ TestPoint(0);
+ }else{
+ rc = 1;
+ TestPoint(0);
+ }
+ break;
+ case 's':
+ if( strcmp(zBase,"sel.first")==0 ){
+ *ppToken = htmlPtr->selBegin.p;
+ *pIndex = htmlPtr->selBegin.i;
+ TestPoint(0);
+ }else if( strcmp(zBase,"sel.last")==0 ){
+ *ppToken = htmlPtr->selEnd.p;
+ *pIndex = htmlPtr->selEnd.i;
+ TestPoint(0);
+ }else{
+ rc = 1;
+ TestPoint(0);
+ }
+ break;
+ case 'i':
+ if( strcmp(zBase,"insert")==0 ){
+ *ppToken = htmlPtr->ins.p;
+ *pIndex = htmlPtr->ins.i;
+ TestPoint(0);
+ }else{
+ rc = 1;
+ TestPoint(0);
+ }
+ break;
+ case '@':
+ n = sscanf(zBase,"@%d,%d",&x,&y);
+ if( n!=2 ){
+ rc = 1;
+ TestPoint(0);
+ break;
+ }
+ x += htmlPtr->xOffset;
+ y += htmlPtr->yOffset;
+ pNearby = 0;
+ *ppToken = htmlPtr->pLast;
+ *pIndex = 0;
+ for(pBlock=htmlPtr->firstBlock; pBlock; pBlock=pBlock->pNext){
+ int dotest;
+ if( pBlock->n==0 ){
+ switch( pBlock->base.pNext->base.type ){
+ case Html_LI:
+ case Html_IMG:
+ case Html_INPUT:
+ case Html_TEXTAREA:
+ case Html_SELECT:
+ dotest = 1;
+ TestPoint(0);
+ break;
+ default:
+ dotest = 0;
+ TestPoint(0);
+ break;
+ }
+ }else{
+ dotest = 1;
+ TestPoint(0);
+ }
+ if (dotest){
+ if( pBlock->top <= y && pBlock->bottom >= y ){
+ if( pBlock->left > x ){
+ if( pBlock->left - x < dist ){
+ dist = pBlock->left - x;
+ pNearby = pBlock;
+ TestPoint(0);
+ }else{
+ TestPoint(0);
+ }
+ }else if( pBlock->right < x ){
+ if( x - pBlock->right < dist ){
+ dist = x - pBlock->right;
+ pNearby = pBlock;
+ TestPoint(0);
+ }else{
+ TestPoint(0);
+ }
+ }else{
+ HtmlLock(htmlPtr);
+ FindIndexInBlock(htmlPtr, pBlock, x, ppToken, pIndex);
+ if( HtmlUnlock(htmlPtr) ) return 1;
+ TestPoint(0);
+ break;
+ }
+ }else{
+ int distY;
+ int distX;
+
+ if( pBlock->bottom < y ){
+ distY = y - pBlock->bottom;
+ TestPoint(0);
+ }else{
+ distY = pBlock->top - y;
+ TestPoint(0);
+ }
+ if( pBlock->left > x ){
+ distX = pBlock->left - x;
+ TestPoint(0);
+ }else if( pBlock->right < x ){
+ distX = x - pBlock->right;
+ TestPoint(0);
+ }else{
+ distX = 0;
+ TestPoint(0);
+ }
+ if( distX + 4*distY < dist ){
+ dist = distX + 4*distY;
+ pNearby = pBlock;
+ TestPoint(0);
+ }else{
+ TestPoint(0);
+ }
+ }
+ }
+ }
+ if( pBlock==0 ){
+ if( pNearby ){
+ HtmlLock(htmlPtr);
+ FindIndexInBlock(htmlPtr, pNearby, x, ppToken, pIndex);
+ if( HtmlUnlock(htmlPtr) ) return 1;
+ TestPoint(0);
+ }else{
+ TestPoint(0);
+ }
+ }else{
+ TestPoint(0);
+ }
+ break;
+ default:
+ rc = 1;
+ TestPoint(0);
+ break;
+ }
+ return rc;
+}
+
+/*
+** This routine decodes a complete index specification. A complete
+** index consists of the base specification followed by modifiers.
+*/
+int HtmlGetIndex(
+ HtmlWidget *htmlPtr, /* The widget */
+ const char *zIndex, /* Complete text of the index spec */
+ HtmlElement **ppToken, /* Write the pointer to the token here */
+ int *pIndex /* Write the character offset here */
+){
+ TestPoint(0);
+ return DecodeBaseIndex(htmlPtr, zIndex, ppToken, pIndex);
+}