/*****
* frames.c : XmHTML frame support
*
* This file Version $Revision: 1.15 $
*
* Creation date: Tue Mar 25 18:53:12 GMT+0100 1997
* Last modification: $Date: 1999/07/29 01:26:28 $
* By: $Author: sopwith $
* Current State: $State: Exp $
*
* Author: newt
*
* Copyright (C) 1994-1997 by Ripley Software Development
* All Rights Reserved
*
* This file is part of the XmHTML Widget Library.
*
* 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.
*
*****/
/*****
* ChangeLog
* $Log: frames.c,v $
* Revision 1.15 1999/07/29 01:26:28 sopwith
*
*
* Fix all warnings.
*
* Revision 1.14 1998/02/12 03:08:57 unammx
* Merge to Koen's XmHTML 1.1.2 + following fixes:
*
* Wed Feb 11 20:27:19 1998 Miguel de Icaza
*
* * gtk-forms.c (freeForm): gtk_destroy_widget is no longer needed
* with the refcounting changes.
*
* * gtk-xmhtml.c (gtk_xmhtml_remove): Only god knows why I was
* adding the just removed widget.
*
* Revision 1.13 1998/01/06 04:48:39 unammx
* More forms work, today we got:
*
* Input file selectors.
* Listboxes.
* Option menus.
* Working input lines.
*
* Missing:
*
* Text entries (really, really, really trivial).
*
* Enjoy,
* Miguel.
*
* Revision 1.12 1997/12/31 20:22:31 unammx
* Frames work.
*
* There is a bug in the frame engine from Koen, it is not a bug of the
* port anymore, hopefully it will get fixed soonish.
*
* Miguel.
*
* Revision 1.11 1997/12/31 05:20:55 unammx
* Frames work. Resizing a window still does not propagate to child
* frames, will fix that next.
*
* Elliot: check how the frames are loaded, you need to attach to the
* frame signal and load the contents when the widget asks for them.
*
* Miguel.
*
* Revision 1.10 1997/12/30 03:32:52 unammx
* More work on getting the frames working, still some bits are missing - Miguel
*
* Revision 1.9 1997/12/29 22:16:28 unammx
* This version does:
*
* - Sync with Koen to version Beta 1.1.2c of the XmHTML widget.
* Includes various table fixes.
*
* - Callbacks are now properly checked for the Gtk edition (ie,
* signals).
*
* Revision 1.8 1997/12/29 03:17:10 sopwith
* amessagetosatisfyCVS
*
* Revision 1.7 1997/12/28 17:45:51 unammx
* Make it compile
*
* Revision 1.6 1997/12/27 20:58:18 unammx
* More access functions to the widget internals. I missed these
* yesterday (ie, those that did not require SetValues validation
* now have an explicit routine to change the values).
*
* Frame support depends on the client of the widget, we should catch
* that signal and do something with it, I have not figured out exacly
* how it works, but example_2 in the XmHTML-1.1.1 distribution has an
* example of this working.
*
* Miguel.
*
* Revision 1.5 1997/12/26 21:03:32 sopwith
* A few miscellaneous XmHTML bug fixes, including a note to miguel so he can fix frames ;-)
*
* Revision 1.4 1997/12/25 01:34:11 unammx
* Good news for the day:
*
* I have upgraded our XmHTML sources to XmHTML 1.1.1.
*
* This basically means that we got table support :-)
*
* Still left to do:
*
* - Set/Get gtk interface for all of the toys in the widget.
* - Frame support is broken, dunno why.
* - Form support (ie adding widgets to it)
*
* Miguel.
*
* Revision 1.3 1997/12/24 17:53:54 unammx
* Fun stuff:
*
* The widget now handles mouse motion, mouse clicks, anchors can
* be clicked.
*
* The widget emits signals for all of the interesting events
* (the same events that were used by the Motif port, we just use
* signals instead of XtCallbacks).
*
* Boring stuff:
*
* The widget now handles focusin/focusout/enternotif/leavenotify
*
* More code sharing between the Motif frontend an the Gtk
* frontned; More portability macros;
*
* Cleaned up some more the privte widget header files.
*
* Revision 1.2 1997/12/17 04:40:28 unammx
* Your daily XmHTML code is here. It almost links. Only the
* images.c file is left to port. Once this is ported we are all
* set to start debugging this baby.
*
* btw, Dickscrape is a Motif based web browser that is entirely
* based on this widget, I just tested it today, very impressive.
*
* Miguel.
*
* Revision 1.1 1997/12/16 00:34:49 unammx
* More progress on the XmHTML work. This time, I did frames.c, not
* as nice as I would like it to be.
*
* It still does not link.
*
* Revision 1.8 1997/10/23 00:25:01 newt
* XmHTML Beta 1.1.0 release
*
* Revision 1.7 1997/08/31 17:35:37 newt
* Several fixes in form creation & destruction and widget reuse. kd & rr
*
* Revision 1.6 1997/08/30 01:04:16 newt
* _XmHTMLWarning proto & color changes: XmHTML now uses manager's color fields.
*
* Revision 1.5 1997/08/01 13:01:40 newt
* my_strdup -> strdup, minor bugfixes and updated comments.
*
* Revision 1.4 1997/05/28 01:48:13 newt
* Sped up _XmHTMLCheckForFrames considerably.
*
* Revision 1.3 1997/04/29 14:27:00 newt
* Header files modifications.
*
* Revision 1.2 1997/04/03 05:35:36 newt
* Changed default name from _top to _frame appended with a number
*
* Revision 1.1 1997/03/28 07:02:46 newt
* Initial Revision
*
*****/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include
#include "XmHTMLP.h"
#include "XmHTMLfuncs.h"
#include "toolkit.h"
/*** External Function Prototype Declarations ***/
/*** Public Variable Declarations ***/
/* how many times may we retry frame alignment? */
#define MAX_FRAME_ITERATIONS 100
#define ROW 1
#define COL 2
#define ROW_DONE (1<<1)
#define COL_DONE (1<<2)
/*** Private Datatype Declarations ****/
/* definition of a HTML frameset */
typedef struct _frameSet{
int type; /* type of this set, either ROW or COL */
int border; /* frame border value */
int *sizes; /* array of child sizes */
int curr_x; /* current x insertion position */
int curr_y; /* current y insertion position */
FrameSize *size_types; /* array of possible size specifications */
int nchilds; /* max no of childs */
int childs_done; /* no of childs processed so far */
int insert_pos; /* insertion position of current child */
struct _frameSet *parent; /* parent frameset of this frameset */
struct _frameSet *childs; /* list of childs */
struct _frameSet *next; /* ptr to next frameSet */
}frameSet;
/* stack of framesets */
typedef struct _frameStack{
frameSet *frame_set;
struct _frameStack *next;
}frameStack;
/*** Private Function Prototype Declarations ****/
static frameSet *popFrameSet(void);
static frameSet *doFrameSet(String attributes);
static void insertFrameSetChild(frameSet *parent, frameSet *child);
static void insertFrameChild(frameSet *current_set, XmHTMLFrameWidget *frame);
static void pushFrameSet(frameSet *frame_set);
static void makeFrameSets(XmHTMLWidget html, XmHTMLObject *frameset);
static void destroyFrameSets(frameSet *set);
static XmHTMLFrameWidget *doFrame(XmHTMLWidget html, String attributes);
static void mapFrames(XmHTMLWidget html);
static void adjustConstraints(XmHTMLWidget html);
static void frameDoneCallback(XmHTMLWidget html, XmHTMLFrameWidget *frame,
TWidget widget);
/*** Private Variable Declarations ***/
static int current_frame; /* running frame counter */
static frameSet *frame_sets; /* list of all framesets processed */
static frameStack frame_base, *frame_stack;
/*****
* Name: doFrameSet
* Return Type: frameSet*
* Description: creates and fills a frameSet structure with the info in it's
* attributes
* In:
* attributes: attributes for this frameset
* Returns:
* a newly created frameset.
* Note:
* this routine inserts each frameset it creates in a linked list which
* is used for stack purposes.
*****/
static frameSet*
doFrameSet(String attributes)
{
frameSet *list, *tmp;
String chPtr, tmpPtr, ptr;
int i;
/* create new entry */
list = (frameSet*)malloc(sizeof(frameSet));
(void)memset(list, 0, sizeof(frameSet));
list->type = ROW;
if((chPtr = _XmHTMLTagGetValue(attributes, "rows")) == NULL)
{
if((chPtr = _XmHTMLTagGetValue(attributes, "cols")) == NULL)
return(frame_sets);
else
list->type = COL;
}
/*
* count how many childs this frameset has: the no of childs is given by
* the no of entries within the COLS or ROWS tag
* Note that childs can be frames and/or framesets as well.
*/
for(tmpPtr = chPtr; *tmpPtr != '\0'; tmpPtr++)
if(*tmpPtr == ',')
list->nchilds++;
list->nchilds++;
list->sizes = (int*)calloc(list->nchilds, sizeof(int));
list->size_types = (FrameSize*)calloc(list->nchilds, sizeof(FrameSize));
list->childs = (frameSet*)calloc(list->nchilds, sizeof(frameSet));
/*
* get dimensions: when we encounter a ``*'' in a size definition it
* means we are free to choose any size we want. When its a number
* followed by a ``%'' we must choose the size relative against the total
* width of the render area. When it's a number not followed by anything
* we have an absolute size.
*/
tmpPtr = ptr = chPtr;
i = 0;
while(True)
{
if(*tmpPtr == ',' || *tmpPtr == '\0')
{
if(*(tmpPtr-1) == '*')
list->size_types[i] = FRAME_SIZE_OPTIONAL;
else if(*(tmpPtr-1) == '%')
list->size_types[i] = FRAME_SIZE_RELATIVE;
else
list->size_types[i] = FRAME_SIZE_FIXED;
list->sizes[i++] = atoi(ptr);
if(*tmpPtr == '\0')
break;
ptr = tmpPtr+1;
}
tmpPtr++;
/* sanity */
if(i == list->nchilds)
break;
}
free(chPtr);
/*
* Frame borders can be specified by both frameborder or border, they
* are equal.
*/
if((chPtr = _XmHTMLTagGetValue(attributes, "frameborder")) != NULL)
{
/*
* Sigh, stupid Netscape frameset definition allows a tag to have
* a textvalue or a number.
*/
if(!(strcasecmp(chPtr, "no")) || *chPtr == '0')
list->border = 0;
else
list->border = atoi(chPtr);
free(chPtr);
}
else
list->border = _XmHTMLTagGetNumber(attributes, "border", 5);
/* insert this new frame in the overal frameset list. */
if(frame_sets == NULL)
frame_sets = list;
else
{
for(tmp = frame_sets; tmp != NULL && tmp->next != NULL;
tmp = tmp->next);
tmp->next = list;
}
return(list);
}
/*****
* Name: doFrame
* Return Type: XmHTMLFrameWidget*
* Description: fills a HTML frame structure with data from it's attributes
* In:
* html: XmHTMLWidget id;
* attributes: frame attributes
* Returns:
* updated frame
* Note:
* this routine takes the frame to update from an already allocated list
* of frames and increments the running frame counter when it returns.
*****/
static XmHTMLFrameWidget*
doFrame(XmHTMLWidget html, String attributes)
{
XmHTMLFrameWidget *frame;
String chPtr;
frame = html->html.frames[current_frame];
/* default frame sizing */
frame->height_type = FRAME_SIZE_FIXED;
frame->width_type = FRAME_SIZE_FIXED;
/* pick up all remaining frame attributes */
frame->src = _XmHTMLTagGetValue(attributes, "src");
/* get frame name, default to _frame if not present */
if((frame->name = _XmHTMLTagGetValue(attributes, "name")) == NULL)
{
char buf[24];
sprintf(buf, "_frame%i", current_frame);
frame->name = strdup(buf);
}
frame->margin_width = (Dimension)_XmHTMLTagGetNumber(attributes,
"marginwidth", 5);
frame->margin_height = (Dimension)_XmHTMLTagGetNumber(attributes,
"marginheight", 5);
if(!frame->margin_width)
frame->margin_width = html->html.margin_width;
if(!frame->margin_height)
frame->margin_height = html->html.margin_height;
/*
* This is useless as we don't support frame resizing. I think this is
* a thing the caller must be able to do. A possible way could be to
* overlay the render area with a PanedWidget and store these HTML widgets
* as childs of this paned widget.
*/
frame->resize = !_XmHTMLTagCheck(attributes, "noresize");
frame->scroll_type = FRAME_SCROLL_AUTO;
if((chPtr = _XmHTMLTagGetValue(attributes, "scrolling")) != NULL)
{
if(!(strcasecmp(chPtr, "yes")))
frame->scroll_type = FRAME_SCROLL_YES;
else if(!(strcasecmp(chPtr, "no")))
frame->scroll_type = FRAME_SCROLL_NONE;
free(chPtr);
}
_XmHTMLDebug(11, ("frames.c: doFrame, frame %i created\n"
"\tname: %s\n"
"\tsrc : %s\n"
"\tmargin width : %i\n"
"\tmargin height: %i\n"
"\tresize : %s\n"
"\tscrolling : %s\n", current_frame, frame->name,
frame->src ? frame->src : "", frame->margin_width,
frame->margin_height, frame->resize ? "yes" : "no",
frame->scroll_type == FRAME_SCROLL_AUTO ? "auto" :
(frame->scroll_type == FRAME_SCROLL_YES ? "always" : "none")));
/*
* Actual widget creation is postponed until the very last moment
* of _XmHTMLCreateFrames
*/
/* increment running frame counter */
current_frame++;
return(frame);
}
/*****
* Name: insertFrameSetChild
* Return Type: void
* Description: inserts a child frameset in it's parent list
* In:
* parent: parent of this frameset
* child: obvious
* Returns:
* nothing
*****/
static void
insertFrameSetChild(frameSet *parent, frameSet *child)
{
if(parent && parent->childs_done < parent->nchilds)
{
int idx = parent->childs_done;
child->parent = parent;
child->insert_pos = idx;
parent->childs[parent->childs_done] = *child;
parent->childs_done++;
}
}
/*****
* Name: insertFrameChild
* Return Type: void
* Description: sets the geometry constraints on a HTML frame
* In:
* frame_set: frameset parent of this frame;
* frame: frame for which to set the constraints
* Returns:
* nothing, but frame is updated.
*****/
static void
insertFrameChild(frameSet *frame_set, XmHTMLFrameWidget *frame)
{
frameSet *parent = frame_set->parent;
int insert_pos = frame_set->childs_done;
if(frame_set->type == COL)
{
frame->width_s = frame_set->sizes[insert_pos];
frame->width_type = frame_set->size_types[insert_pos];
frame->xs = frame_set->curr_x;
/* advance to next x position */
frame_set->curr_x += frame->width_s;
if(parent)
{
frame->height_s = parent->sizes[frame_set->insert_pos];
frame->height_type = parent->size_types[frame_set->insert_pos];
frame->ys = parent->curr_y;
}
}
else
{
frame->height_s = frame_set->sizes[insert_pos];
frame->height_type = frame_set->size_types[insert_pos];
frame->ys = frame_set->curr_y;
/* advance to next y position */
frame_set->curr_y += frame->height_s;
if(parent)
{
frame->width_s = parent->sizes[frame_set->insert_pos];
frame->width_type = parent->size_types[frame_set->insert_pos];
frame->xs = parent->curr_x;
}
}
if(frame->height_s == 0)
frame->height_type = FRAME_SIZE_OPTIONAL;
if(frame->width_s == 0)
frame->width_type = FRAME_SIZE_OPTIONAL;
/* set additional constraints for this frame */
frame->border = frame_set->border;
/* disable resizing if we don't have a border */
if(!frame->border)
frame->resize = False;
frame_set->childs_done++;
}
/*****
* Name: pushFrameSet
* Return Type: void
* Description: pushes a frameset on the stack
* In:
* frame_set: frameset to push
* Returns:
* nothing
*****/
static void
pushFrameSet(frameSet *frame_set)
{
frameStack *tmp;
tmp = (frameStack*)malloc(sizeof(frameStack));
tmp->frame_set = frame_set;
tmp->next = frame_stack;
frame_stack = tmp;
}
/*****
* Name: popFrameSet
* Return Type: frameSet*
* Description: pops a frameset of the stack
* In:
* nothing
* Returns:
* the next frameset on the stack, or NULL when stack is empty
*****/
static frameSet*
popFrameSet(void)
{
frameStack *tmp;
frameSet *frame_set;
if(frame_stack->next)
{
tmp = frame_stack;
frame_stack = frame_stack->next;
frame_set = tmp->frame_set;
free(tmp);
return(frame_set);
}
return(NULL);
}
/*****
* Name: makeFrameSets
* Return Type: void
* Description: creates all HTML framesets and sets the geometry constraints
* on each frame.
* In:
* html: XmHTMLWidget id;
* frameset: XmHTMLObject data;
* Returns:
* nothing
* Note:
* This routine was *very* difficult to conceive, so don't let the simplicity
* of it deceive you.
*****/
static void
makeFrameSets(XmHTMLWidget html, XmHTMLObject *frameset)
{
XmHTMLObject *tmp;
XmHTMLFrameWidget *frame;
frameSet *current_set = NULL, *parent_set = NULL;
int idx = 0, i;
for(tmp = frameset; tmp != NULL; tmp = tmp->next)
{
switch(tmp->id)
{
case HT_FRAMESET:
if(tmp->is_end)
{
/* frameset terminated, pop from stack */
current_set = popFrameSet();
/*
* no more sets left on the stack: we've reached the
* end of the outermost frameset and are done here.
*/
if(current_set == NULL)
return;
/*
* if this test evaluates to False, it's a bad frameset
* spec: it has more childs than originally specified
* with the ROWS or COLS attribute. We just ignore
* these things. This normally shouldn't happen as
* unspecified framesets are skipped when a new
* frameset is encountered.
*/
if((i = current_set->childs_done) < current_set->nchilds)
{
if(current_set->type == ROW)
current_set->curr_y += current_set->sizes[i];
else
current_set->curr_x += current_set->sizes[i];
}
}
else
{
/* A new frameset, push the current frameset on the stack */
pushFrameSet(current_set);
parent_set = frame_stack->frame_set;
/* Check if we still have room for this thing. */
if(!parent_set ||
parent_set->childs_done < parent_set->nchilds)
{
/* create a new frameset */
current_set = doFrameSet(tmp->attributes);
insertFrameSetChild(parent_set, current_set);
idx = 0;
}
else
{
/*
* No more room available, this is an unspecified
* frameset, kill it and all childs it might have.
*/
int depth = 1;
int start_line = tmp->line;
for(tmp = tmp->next; tmp != NULL; tmp = tmp->next)
{
if(tmp->id == HT_FRAMESET)
{
if(tmp->is_end)
{
if(--depth == 0)
break;
}
else /* child frameset */
depth++;
}
}
_XmHTMLWarning(__WFUNC__(html, "doFrameSets"),
"Bad