/*
** 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
**