/*
** Routines to implement the HTML widget commands
**
** 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
#include
#include
#include "htmlcmd.h"
/*
** WIDGET resolve ?URI ...?
**
** Call the TCL command specified by the -resolvercommand option
** to resolve the URL.
*/
int HtmlResolveCmd(
HtmlWidget *htmlPtr, /* The HTML widget */
Tcl_Interp *interp, /* The interpreter */
int argc, /* Number of arguments */
const char **argv /* List of all arguments */
){
return HtmlCallResolver(htmlPtr, (char**)argv+2);
}
/*
** WIDGET cget CONFIG-OPTION
**
** Retrieve the value of a configuration option
*/
int HtmlCgetCmd(
HtmlWidget *htmlPtr, /* The HTML widget */
Tcl_Interp *interp, /* The interpreter */
int argc, /* Number of arguments */
const char **argv /* List of all arguments */
){
TestPoint(0);
return Tk_ConfigureValue(interp, htmlPtr->tkwin, HtmlConfigSpec(),
(char *) htmlPtr, argv[2], 0);
}
/*
** WIDGET clear
**
** Erase all HTML from this widget and clear the screen. This is
** typically done before loading a new document.
*/
int HtmlClearCmd(
HtmlWidget *htmlPtr, /* The HTML widget */
Tcl_Interp *interp, /* The interpreter */
int argc, /* Number of arguments */
const char **argv /* List of all arguments */
){
HtmlClear(htmlPtr);
htmlPtr->flags |= REDRAW_TEXT | VSCROLL | HSCROLL;
HtmlScheduleRedraw(htmlPtr);
TestPoint(0);
return TCL_OK;
}
/*
** WIDGET configure ?OPTIONS?
**
** The standard Tk configure command.
*/
int HtmlConfigCmd(
HtmlWidget *htmlPtr, /* The HTML widget */
Tcl_Interp *interp, /* The interpreter */
int argc, /* Number of arguments */
const char **argv /* List of all arguments */
){
if (argc == 2) {
TestPoint(0);
return Tk_ConfigureInfo(interp, htmlPtr->tkwin, HtmlConfigSpec(),
(char *) htmlPtr, (char *) NULL, 0);
} else if (argc == 3) {
TestPoint(0);
return Tk_ConfigureInfo(interp, htmlPtr->tkwin, HtmlConfigSpec(),
(char *) htmlPtr, argv[2], 0);
} else {
TestPoint(0);
return ConfigureHtmlWidget(interp, htmlPtr, argc-2, argv+2,
TK_CONFIG_ARGV_ONLY, 0);
}
}
/*
** WIDGET href X Y
**
** Returns the URL on the hyperlink that is beneath the position X,Y.
** Returns {} if there is no hyperlink beneath X,Y.
*/
int HtmlHrefCmd(
HtmlWidget *htmlPtr, /* The HTML widget */
Tcl_Interp *interp, /* The interpreter */
int argc, /* Number of arguments */
const char **argv /* List of all arguments */
){
int x, y;
char *z;
if( Tcl_GetInt(interp, argv[2], &x) != TCL_OK
|| Tcl_GetInt(interp, argv[3], &y) != TCL_OK
){
TestPoint(0);
return TCL_ERROR;
}
z = HtmlGetHref(htmlPtr, x + htmlPtr->xOffset, y + htmlPtr->yOffset);
if( z ){
HtmlLock(htmlPtr);
z = HtmlResolveUri(htmlPtr, z);
if( !HtmlUnlock(htmlPtr) ){
Tcl_SetResult(interp, z, TCL_DYNAMIC);
}
}
return TCL_OK;
}
/*
** WIDGET names
**
** Returns a list of names associated with tags.
*/
int HtmlNamesCmd(
HtmlWidget *htmlPtr, /* The HTML widget */
Tcl_Interp *interp, /* The interpreter */
int argc, /* Number of arguments */
const char **argv /* List of all arguments */
){
HtmlElement *p;
char *z;
TestPoint(0);
for(p=htmlPtr->pFirst; p; p=p->pNext){
if( p->base.type!=Html_A ) continue;
z = HtmlMarkupArg(p,"name",0);
if( z ){
Tcl_AppendElement(interp,z);
}else{
z = HtmlMarkupArg(p,"id",0);
if( z ){
Tcl_AppendElement(interp,z);
}
}
}
return TCL_OK;
}
/*
** WIDGET parse HTML
**
** Appends the given HTML text to the end of any HTML text that may have
** been inserted by prior calls to this command. Then it runs the
** tokenizer, parser and layout engine as far as possible with the
** text that is available. The display is updated appropriately.
*/
int HtmlParseCmd(
HtmlWidget *htmlPtr, /* The HTML widget */
Tcl_Interp *interp, /* The interpreter */
int argc, /* Number of arguments */
const char **argv /* List of all arguments */
){
HtmlElement *endPtr;
endPtr = htmlPtr->pLast;
HtmlLock(htmlPtr);
HtmlTokenizerAppend(htmlPtr, argv[2]);
if( HtmlIsDead(htmlPtr) ){
return TCL_OK;
}
if( endPtr ){
if( endPtr->pNext ){
HtmlAddStyle(htmlPtr, endPtr->pNext);
}
}else if( htmlPtr->pFirst ){
htmlPtr->paraAlignment = ALIGN_None;
htmlPtr->rowAlignment = ALIGN_None;
htmlPtr->anchorFlags = 0;
htmlPtr->inDt = 0;
htmlPtr->anchorStart = 0;
htmlPtr->formStart = 0;
htmlPtr->innerList = 0;
HtmlAddStyle(htmlPtr, htmlPtr->pFirst);
TestPoint(0);
}
if( !HtmlUnlock(htmlPtr) ){
htmlPtr->flags |= EXTEND_LAYOUT;
HtmlScheduleRedraw(htmlPtr);
TestPoint(0);
}
return TCL_OK;
}
/*
** WIDGET xview ?SCROLL-OPTIONS...?
**
** Implements horizontal scrolling in the usual Tk way.
*/
int HtmlXviewCmd(
HtmlWidget *htmlPtr, /* The HTML widget */
Tcl_Interp *interp, /* The interpreter */
int argc, /* Number of arguments */
const char **argv /* List of all arguments */
){
if( argc==2 ){
HtmlComputeHorizontalPosition(htmlPtr,(char*)Tcl_GetStringResult(interp));
TestPoint(0);
}else{
int count;
double fraction;
int maxX = htmlPtr->maxX;
int w = HtmlUsableWidth(htmlPtr);
int offset = htmlPtr->xOffset;
int type = Tk_GetScrollInfo(interp,argc,(const char**)argv,&fraction,&count);
switch( type ){
case TK_SCROLL_ERROR:
TestPoint(0);
return TCL_ERROR;
case TK_SCROLL_MOVETO:
offset = fraction * maxX;
TestPoint(0);
break;
case TK_SCROLL_PAGES:
offset += (count * w * 9)/10;
TestPoint(0);
break;
case TK_SCROLL_UNITS:
offset += (count * w)/10;
TestPoint(0);
break;
}
if( offset + w > maxX ){
offset = maxX - w;
TestPoint(0);
}else{
TestPoint(0);
}
if( offset < 0 ){
offset = 0;
TestPoint(0);
}else{
TestPoint(0);
}
HtmlHorizontalScroll(htmlPtr, offset);
}
return TCL_OK;
}
/*
** WIDGET yview ?SCROLL-OPTIONS...?
**
** Implements vertical scrolling in the usual Tk way, but with one
** enhancement. If the argument is a single word, the widget looks
** for a tag with that word as the "name" and scrolls
** to the position of that tag.
*/
int HtmlYviewCmd(
HtmlWidget *htmlPtr, /* The HTML widget */
Tcl_Interp *interp, /* The interpreter */
int argc, /* Number of arguments */
const char **argv /* List of all arguments */
){
if( argc==2 ){
HtmlComputeVerticalPosition(htmlPtr,(char*)Tcl_GetStringResult(interp));
TestPoint(0);
}else if( argc==3 ){
char *z;
HtmlElement *p;
for(p=htmlPtr->pFirst; p; p=p->pNext){
if( p->base.type!=Html_A ) continue;
z = HtmlMarkupArg(p,"name",0);
if( z==0 ){
TestPoint(0);
continue;
}
if( strcmp(z,argv[2])!=0 ){
TestPoint(0);
continue;
}
HtmlVerticalScroll(htmlPtr, p->anchor.y);
TestPoint(0);
break;
}
} else if( argc==4 && !strncmp(argv[2],"text",4)) {
HtmlElement *p;
int i;
HtmlLock(htmlPtr);
if( HtmlGetIndex(htmlPtr, argv[3], &p, &i)!=0 ){
if( !HtmlUnlock(htmlPtr) ){
Tcl_AppendResult(interp,"malformed index: \"", argv[3], "\"", 0);
}
TestPoint(0);
return TCL_ERROR;
}
if( !HtmlUnlock(htmlPtr) && p ){
if( p->base.type==Html_Text ) {
int offset = p->text.y-20;
if (offset<0)
offset = 0;
HtmlVerticalScroll(htmlPtr, offset);
}
TestPoint(0);
}
}
else{
int count;
double fraction;
int maxY = htmlPtr->maxY;
int h = HtmlUsableHeight(htmlPtr);
int offset = htmlPtr->yOffset;
int type = Tk_GetScrollInfo(interp,argc,(const char**)argv,&fraction,&count);
switch( type ){
case TK_SCROLL_ERROR:
TestPoint(0);
return TCL_ERROR;
case TK_SCROLL_MOVETO:
offset = fraction * maxY;
TestPoint(0);
break;
case TK_SCROLL_PAGES:
offset += (count * h * 9)/10;
TestPoint(0);
break;
case TK_SCROLL_UNITS:
offset += (count * h)/10;
TestPoint(0);
break;
}
if( offset + h > maxY ){
offset = maxY - h;
TestPoint(0);
}else{
TestPoint(0);
}
if( offset < 0 ){
offset = 0;
TestPoint(0);
}else{
TestPoint(0);
}
HtmlVerticalScroll(htmlPtr, offset);
}
return TCL_OK;
}
/*
** WIDGET token handler TAG ?SCRIPT?
*/
int HtmlTokenHandlerCmd(
HtmlWidget *htmlPtr, /* The HTML widget */
Tcl_Interp *interp, /* The interpreter */
int argc, /* Number of arguments */
const char **argv /* List of all arguments */
){
int type = HtmlNameToType(argv[3]);
if( type==Html_Unknown ){
Tcl_AppendResult(interp,"unknown tag: \"", argv[3], "\"", 0);
return TCL_ERROR;
}
if( argc==4 ){
if( htmlPtr->zHandler[type]!=0 ){
Tcl_SetResult(interp,htmlPtr->zHandler[type],NULL);
}
}else{
if( htmlPtr->zHandler[type]!=0 ){
HtmlFree(htmlPtr->zHandler[type]);
}
htmlPtr->zHandler[type] = HtmlAlloc( strlen(argv[4]) + 1 );
if( htmlPtr->zHandler[type] ){
strcpy(htmlPtr->zHandler[type],argv[4]);
}
}
return TCL_OK;
}
/*
** WIDGET index INDEX
*/
int HtmlIndexCmd(
HtmlWidget *htmlPtr, /* The HTML widget */
Tcl_Interp *interp, /* The interpreter */
int argc, /* Number of arguments */
const char **argv /* List of all arguments */
){
HtmlElement *p;
int i;
HtmlLock(htmlPtr);
if( HtmlGetIndex(htmlPtr, argv[2], &p, &i)!=0 ){
if( !HtmlUnlock(htmlPtr) ){
Tcl_AppendResult(interp,"malformed index: \"", argv[2], "\"", 0);
}
TestPoint(0);
return TCL_ERROR;
}
if( !HtmlUnlock(htmlPtr) && p ){
sprintf((char*)Tcl_GetStringResult(interp), "%d.%d", HtmlTokenNumber(p), i);
TestPoint(0);
}else{
TestPoint(0);
}
return TCL_OK;
}
/* The pSelStartBlock and pSelEndBlock values have been changed.
** This routine's job is to loop over all HtmlBlocks and either
** set or clear the HTML_Selected bits in the .base.flags field
** as appropriate. For every HtmlBlock where the bit changes,
** mark that block for redrawing.
*/
static void UpdateSelection(HtmlWidget *htmlPtr){
int selected = 0;
HtmlIndex tempIndex;
HtmlBlock *pTempBlock;
int temp;
HtmlBlock *p;
for(p=htmlPtr->firstBlock; p; p=p->pNext){
if( p==htmlPtr->pSelStartBlock ){
selected = 1;
HtmlRedrawBlock(htmlPtr, p);
TestPoint(0);
}else if( !selected && p==htmlPtr->pSelEndBlock ){
selected = 1;
tempIndex = htmlPtr->selBegin;
htmlPtr->selBegin = htmlPtr->selEnd;
htmlPtr->selEnd = tempIndex;
pTempBlock = htmlPtr->pSelStartBlock;
htmlPtr->pSelStartBlock = htmlPtr->pSelEndBlock;
htmlPtr->pSelEndBlock = pTempBlock;
temp = htmlPtr->selStartIndex;
htmlPtr->selStartIndex = htmlPtr->selEndIndex;
htmlPtr->selEndIndex = temp;
HtmlRedrawBlock(htmlPtr, p);
TestPoint(0);
}else{
TestPoint(0);
}
if( p->base.flags & HTML_Selected ){
if( !selected ){
p->base.flags &= ~HTML_Selected;
HtmlRedrawBlock(htmlPtr,p);
TestPoint(0);
}else{
TestPoint(0);
}
}else{
if( selected ){
p->base.flags |= HTML_Selected;
HtmlRedrawBlock(htmlPtr,p);
TestPoint(0);
}else{
TestPoint(0);
}
}
if( p==htmlPtr->pSelEndBlock ){
selected = 0;
HtmlRedrawBlock(htmlPtr, p);
TestPoint(0);
}else{
TestPoint(0);
}
}
}
/* Given the selection end-points in htmlPtr->selBegin
** and htmlPtr->selEnd, recompute pSelBeginBlock and
** pSelEndBlock, then call UpdateSelection to update the
** display.
**
** This routine should be called whenever the selection
** changes or whenever the set of HtmlBlock structures
** change.
*/
void HtmlUpdateSelection(HtmlWidget *htmlPtr, int forceUpdate){
HtmlBlock *pBlock;
int index;
int needUpdate = forceUpdate;
int temp;
if( htmlPtr->selEnd.p==0 ){
htmlPtr->selBegin.p = 0;
TestPoint(0);
}else{
TestPoint(0);
}
HtmlIndexToBlockIndex(htmlPtr, htmlPtr->selBegin, &pBlock, &index);
if( needUpdate || pBlock != htmlPtr->pSelStartBlock ){
needUpdate = 1;
HtmlRedrawBlock(htmlPtr, htmlPtr->pSelStartBlock);
htmlPtr->pSelStartBlock = pBlock;
htmlPtr->selStartIndex = index;
TestPoint(0);
}else if( index != htmlPtr->selStartIndex ){
HtmlRedrawBlock(htmlPtr, pBlock);
htmlPtr->selStartIndex = index;
TestPoint(0);
}else{
TestPoint(0);
}
if( htmlPtr->selBegin.p==0 ){
htmlPtr->selEnd.p = 0;
TestPoint(0);
}else{
TestPoint(0);
}
HtmlIndexToBlockIndex(htmlPtr, htmlPtr->selEnd, &pBlock, &index);
if( needUpdate || pBlock != htmlPtr->pSelEndBlock ){
needUpdate = 1;
HtmlRedrawBlock(htmlPtr, htmlPtr->pSelEndBlock);
htmlPtr->pSelEndBlock = pBlock;
htmlPtr->selEndIndex = index;
TestPoint(0);
}else if( index != htmlPtr->selEndIndex ){
HtmlRedrawBlock(htmlPtr, pBlock);
htmlPtr->selEndIndex = index;
TestPoint(0);
}else{
TestPoint(0);
}
if( htmlPtr->pSelStartBlock
&& htmlPtr->pSelStartBlock==htmlPtr->pSelEndBlock
&& htmlPtr->selStartIndex > htmlPtr->selEndIndex
){
temp = htmlPtr->selStartIndex;
htmlPtr->selStartIndex = htmlPtr->selEndIndex;
htmlPtr->selEndIndex = temp;
TestPoint(0);
}else{
TestPoint(0);
}
if( needUpdate ){
UpdateSelection(htmlPtr);
TestPoint(0);
}else{
TestPoint(0);
}
}
/*
** WIDGET selection set INDEX INDEX
*/
int HtmlSelectionSetCmd(
HtmlWidget *htmlPtr, /* The HTML widget */
Tcl_Interp *interp, /* The interpreter */
int argc, /* Number of arguments */
const char **argv /* List of all arguments */
){
HtmlIndex selBegin, selEnd;
HtmlLock(htmlPtr);
if( HtmlGetIndex(htmlPtr, argv[3], &selBegin.p, &selBegin.i) ){
if( !HtmlUnlock(htmlPtr) ){
Tcl_AppendResult(interp,"malformed index: \"", argv[3], "\"", 0);
}
TestPoint(0);
return TCL_ERROR;
}
if( HtmlIsDead(htmlPtr) ) return TCL_OK;
if( HtmlGetIndex(htmlPtr, argv[4], &selEnd.p, &selEnd.i) ){
if( !HtmlUnlock(htmlPtr) ){
Tcl_AppendResult(interp,"malformed index: \"", argv[4], "\"", 0);
}
TestPoint(0);
return TCL_ERROR;
}
if( HtmlUnlock(htmlPtr) ) return TCL_OK;
htmlPtr->selBegin = selBegin;
htmlPtr->selEnd = selEnd;
HtmlUpdateSelection(htmlPtr,0);
TestPoint(0);
return TCL_OK;
}
/*
** WIDGET selection clear
*/
int HtmlSelectionClearCmd(
HtmlWidget *htmlPtr, /* The HTML widget */
Tcl_Interp *interp, /* The interpreter */
int argc, /* Number of arguments */
const char **argv /* List of all arguments */
){
htmlPtr->pSelStartBlock = 0;
htmlPtr->pSelEndBlock = 0;
htmlPtr->selBegin.p = 0;
htmlPtr->selEnd.p = 0;
UpdateSelection(htmlPtr);
TestPoint(0);
return TCL_OK;
}
/*
** Recompute the position of the insertion cursor based on the
** position in htmlPtr->ins.
*/
void HtmlUpdateInsert(HtmlWidget *htmlPtr){
HtmlIndexToBlockIndex(htmlPtr, htmlPtr->ins,
&htmlPtr->pInsBlock, &htmlPtr->insIndex);
HtmlRedrawBlock(htmlPtr, htmlPtr->pInsBlock);
if( htmlPtr->insTimer==0 ){
htmlPtr->insStatus = 0;
HtmlFlashCursor(htmlPtr);
TestPoint(0);
}else{
TestPoint(0);
}
}
/*
** WIDGET insert INDEX
*/
int HtmlInsertCmd(
HtmlWidget *htmlPtr, /* The HTML widget */
Tcl_Interp *interp, /* The interpreter */
int argc, /* Number of arguments */
const char **argv /* List of all arguments */
){
HtmlIndex ins;
if( argv[2][0]==0 ){
HtmlRedrawBlock(htmlPtr, htmlPtr->pInsBlock);
htmlPtr->insStatus = 0;
htmlPtr->pInsBlock = 0;
htmlPtr->ins.p = 0;
TestPoint(0);
}else{
HtmlLock(htmlPtr);
if( HtmlGetIndex(htmlPtr, argv[2], &ins.p, &ins.i) ){
if( !HtmlUnlock(htmlPtr) ){
Tcl_AppendResult(interp,"malformed index: \"", argv[2], "\"", 0);
}
TestPoint(0);
return TCL_ERROR;
}
if( HtmlUnlock(htmlPtr) ) return TCL_OK;
HtmlRedrawBlock(htmlPtr, htmlPtr->pInsBlock);
htmlPtr->ins = ins;
HtmlUpdateInsert(htmlPtr);
TestPoint(0);
}
return TCL_OK;
}
/*
** WIDGET token list START END
*/
int HtmlTokenListCmd(
HtmlWidget *htmlPtr, /* The HTML widget */
Tcl_Interp *interp, /* The interpreter */
int argc, /* Number of arguments */
const char **argv /* List of all arguments */
){
HtmlElement *pStart, *pEnd;
int i;
if( HtmlGetIndex(htmlPtr, argv[3], &pStart, &i)!=0 ){
Tcl_AppendResult(interp,"malformed index: \"", argv[3], "\"", 0);
return TCL_ERROR;
}
if( HtmlGetIndex(htmlPtr, argv[4], &pEnd, &i)!=0 ){
Tcl_AppendResult(interp,"malformed index: \"", argv[4], "\"", 0);
return TCL_ERROR;
}
if( pStart ){
HtmlTclizeList(interp,pStart,pEnd ? pEnd->base.pNext : 0);
}
return TCL_OK;
}
void* HtmlAlloc(size_t A)
{
void* ptr = Tcl_Alloc(A);
if (ptr)
memset(ptr,0,A);
return ptr;
}
#ifdef DEBUG
/*
** WIDGET debug dump START END
*/
int HtmlDebugDumpCmd(
HtmlWidget *htmlPtr, /* The HTML widget */
Tcl_Interp *interp, /* The interpreter */
int argc, /* Number of arguments */
const char **argv /* List of all arguments */
){
HtmlElement *pStart, *pEnd;
int i;
if( HtmlGetIndex(htmlPtr, argv[3], &pStart, &i)!=0 ){
Tcl_AppendResult(interp,"malformed index: \"", argv[3], "\"", 0);
return TCL_ERROR;
}
if( HtmlGetIndex(htmlPtr, argv[4], &pEnd, &i)!=0 ){
Tcl_AppendResult(interp,"malformed index: \"", argv[4], "\"", 0);
return TCL_ERROR;
}
if( pStart ){
HtmlPrintList(pStart,pEnd ? pEnd->base.pNext : 0);
}
return TCL_OK;
}
/*
** WIDGET debug testpt FILENAME
*/
int HtmlDebugTestPtCmd(
HtmlWidget *htmlPtr, /* The HTML widget */
Tcl_Interp *interp, /* The interpreter */
int argc, /* Number of arguments */
const char **argv /* List of all arguments */
){
HtmlTestPointDump(argv[3]);
return TCL_OK;
}
#endif