/* ** Routines used for processing HTML makeup for forms. ** ** 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 #include "htmlform.h" /* ** Unmap any input control that is currently mapped. */ void HtmlUnmapControls(HtmlWidget *htmlPtr){ HtmlElement *p; for(p=htmlPtr->firstInput; p; p=p->input.pNext){ if( p->input.tkwin!=0 && Tk_IsMapped(p->input.tkwin) ){ Tk_UnmapWindow(p->input.tkwin); } } } /* ** Map any control that should be visible according to the ** current scroll position. At the same time, if any controls that ** should not be visible are mapped, unmap them. After this routine ** finishes, all controls should be in their proper places ** regardless of where they might have been before. ** ** Return the number of controls that are currently visible. */ int HtmlMapControls(HtmlWidget *htmlPtr){ HtmlElement *p; /* For looping over all controls */ int x, y, w, h; /* Part of the virtual canvas that is visible */ int cnt = 0; /* Number of visible controls */ x = htmlPtr->xOffset; y = htmlPtr->yOffset; w = Tk_Width(htmlPtr->clipwin); h = Tk_Height(htmlPtr->clipwin); for(p=htmlPtr->firstInput; p; p=p->input.pNext){ if( p->input.tkwin==0 ) continue; if( p->input.y < y+h && p->input.y + p->input.h > y && p->input.x < x+w && p->input.x + p->input.w > x ){ /* The control should be visible. Make is so if it isn't already */ Tk_MoveResizeWindow(p->input.tkwin, p->input.x - x, p->input.y - y, p->input.w, p->input.h); if( !Tk_IsMapped(p->input.tkwin) ){ Tk_MapWindow(p->input.tkwin); } cnt++; }else{ /* This control should not be visible. Unmap it. */ if( Tk_IsMapped(p->input.tkwin) ){ Tk_UnmapWindow(p->input.tkwin); } } } return cnt; } /* ** Delete all input controls. This happens when the HTML widget ** is cleared. ** ** When the TCL "exit" command is invoked, the order of operations ** here is very touchy. */ void HtmlDeleteControls(HtmlWidget *htmlPtr){ HtmlElement *p; /* For looping over all controls */ Tcl_Interp *interp; /* The interpreter */ interp = htmlPtr->interp; p = htmlPtr->firstInput; htmlPtr->firstInput = 0; htmlPtr->lastInput = 0; htmlPtr->nInput = 0; if( p==0 || htmlPtr->tkwin==0 ) return; HtmlLock(htmlPtr); for(; p; p=p->input.pNext){ if( p->input.pForm && p->input.pForm->form.id>0 && htmlPtr->zFormCommand && htmlPtr->zFormCommand[0] && !Tcl_InterpDeleted(interp) && htmlPtr->clipwin ){ Tcl_DString cmd; int result; char zBuf[60]; Tcl_DStringInit(&cmd); Tcl_DStringAppend(&cmd, htmlPtr->zFormCommand, -1); sprintf(zBuf," %d flush", p->input.pForm->form.id); Tcl_DStringAppend(&cmd, zBuf, -1); result = Tcl_GlobalEval(htmlPtr->interp, Tcl_DStringValue(&cmd)); Tcl_DStringFree(&cmd); if( !Tcl_InterpDeleted(interp) ){ if( result != TCL_OK ){ Tcl_AddErrorInfo(htmlPtr->interp, "\n (-formcommand flush callback executed by html widget)"); Tcl_BackgroundError(htmlPtr->interp); TestPoint(0); } Tcl_ResetResult(htmlPtr->interp); } p->input.pForm->form.id = 0; } if( p->input.tkwin ){ if( htmlPtr->clipwin!=0 ) Tk_DestroyWindow(p->input.tkwin); p->input.tkwin = 0; } p->input.sized = 0; } HtmlUnlock(htmlPtr); } /* ** Return an appropriate type value for the given markup. */ static int InputType(HtmlElement *p){ int type = INPUT_TYPE_Unknown; char *z; int i; static struct { char *zName; int type; } types[] = { { "checkbox", INPUT_TYPE_Checkbox }, { "file", INPUT_TYPE_File }, { "hidden", INPUT_TYPE_Hidden }, { "image", INPUT_TYPE_Image }, { "password", INPUT_TYPE_Password }, { "radio", INPUT_TYPE_Radio }, { "reset", INPUT_TYPE_Reset }, { "submit", INPUT_TYPE_Submit }, { "text", INPUT_TYPE_Text }, }; switch( p->base.type ){ case Html_INPUT: z = HtmlMarkupArg(p, "type", "text"); if( z==0 ){ TestPoint(0); break; } for(i=0; iclipwin)); zBuf = HtmlAlloc( n + 20 ); sprintf(zBuf,"%s.x%d",Tk_PathName(htmlPtr->clipwin), pElem->input.cnt); return zBuf; } /* ** A Input element is the input. Mark this element as being ** empty. It has no widget and doesn't appear on the screen. ** ** This is called for HIDDEN inputs or when the -formcommand ** callback doesn't create the widget. */ static void EmptyInput(HtmlElement *pElem){ pElem->input.tkwin = 0; pElem->input.w = 0; pElem->input.h = 0; pElem->base.flags &= !HTML_Visible; pElem->base.style.flags |= STY_Invisible; pElem->input.sized = 1; } /* ** This routine is called when one of the child windows for a form ** wants to change its size. */ static void HtmlInputRequestProc(ClientData clientData, Tk_Window tkwin){ HtmlElement *pElem = (HtmlElement*)clientData; if( pElem->base.type!=Html_INPUT ){ CANT_HAPPEN; return; } if( pElem->input.tkwin!=tkwin ){ CANT_HAPPEN; return; } pElem->input.w = Tk_ReqWidth(tkwin); pElem->input.h = Tk_ReqHeight(tkwin); if( pElem->input.htmlPtr && pElem->input.htmlPtr->tkwin!=0 ){ pElem->input.htmlPtr->flags |= RELAYOUT; HtmlScheduleRedraw(pElem->input.htmlPtr); } } /* ** This routine is called when another entity takes over geometry ** management for a widget corresponding to an input element. */ static void HtmlInputLostSlaveProc(ClientData clientData, Tk_Window tkwin){ HtmlElement *pElem = (HtmlElement*)clientData; if( pElem->base.type!=Html_INPUT ){ CANT_HAPPEN; return; } if( pElem->input.tkwin!=tkwin ){ CANT_HAPPEN; return; } EmptyInput(pElem); if( pElem->input.htmlPtr && pElem->input.htmlPtr->tkwin!=0 ){ pElem->input.htmlPtr->flags |= RELAYOUT; HtmlScheduleRedraw(pElem->input.htmlPtr); } } /* ** This routine catches DestroyNotify events on a INPUT window so ** that we will know the window is been deleted. */ static void HtmlInputEventProc(ClientData clientData, XEvent *eventPtr){ HtmlElement *pElem = (HtmlElement*)clientData; /* if( pElem->base.type!=Html_INPUT ){ CANT_HAPPEN; return; } */ if( eventPtr->type==DestroyNotify ){ EmptyInput(pElem); if( pElem->input.htmlPtr && pElem->input.htmlPtr->tkwin!=0 ){ pElem->input.htmlPtr->flags |= RELAYOUT; HtmlScheduleRedraw(pElem->input.htmlPtr); } } } /* ** The geometry manager for the HTML widget */ static Tk_GeomMgr htmlGeomType = { "html", /* Name */ HtmlInputRequestProc, /* Called when widget changes size */ HtmlInputLostSlaveProc, /* Called when someone else takes over management */ }; /* ** zWin is the name of a child widget that is used to implement an ** input element. Query Tk for information about this widget (such ** as its size) and put that information in the pElem structure ** that represents the input. */ static void SizeAndLink(HtmlWidget *htmlPtr, char *zWin, HtmlElement *pElem){ pElem->input.tkwin = Tk_NameToWindow(htmlPtr->interp, zWin, htmlPtr->clipwin); if( pElem->input.tkwin==0 ){ Tcl_ResetResult(htmlPtr->interp); EmptyInput(pElem); }else if( pElem->input.type==INPUT_TYPE_Hidden ){ pElem->input.w = 0; pElem->input.h = 0; pElem->base.flags &= !HTML_Visible; pElem->base.style.flags |= STY_Invisible; }else{ pElem->input.w = Tk_ReqWidth(pElem->input.tkwin); pElem->input.h = Tk_ReqHeight(pElem->input.tkwin); pElem->base.flags |= HTML_Visible; pElem->input.htmlPtr = htmlPtr; Tk_ManageGeometry(pElem->input.tkwin, &htmlGeomType, pElem); Tk_CreateEventHandler(pElem->input.tkwin, StructureNotifyMask, HtmlInputEventProc, pElem); } pElem->input.pNext = 0; if( htmlPtr->firstInput==0 ){ htmlPtr->firstInput = pElem; }else{ htmlPtr->lastInput->input.pNext = pElem; } htmlPtr->lastInput = pElem; pElem->input.sized = 1; } /* Append all text and space tokens between pStart and pEnd to ** the given Tcl_DString. */ static void HtmlAppendText( Tcl_DString *str, /* Append the text here */ HtmlElement *pFirst, /* The first token */ HtmlElement *pEnd /* The last token */ ){ while( pFirst && pFirst!=pEnd ){ switch( pFirst->base.type ){ case Html_Text: { Tcl_DStringAppend(str, pFirst->text.zText, -1); break; } case Html_Space: { if( pFirst->base.flags & HTML_NewLine ){ Tcl_DStringAppend(str, "\n", 1); }else{ int cnt; static char zSpaces[] = " "; cnt = pFirst->base.count; while( cnt>sizeof(zSpaces)-1 ){ Tcl_DStringAppend(str, zSpaces, sizeof(zSpaces)-1); cnt -= sizeof(zSpaces)-1; } if( cnt>0 ){ Tcl_DStringAppend(str, zSpaces, cnt); } } break; } default: /* Do nothing */ break; } pFirst = pFirst->pNext; } } /* ** The "p" argument points to a ) looking for **