/* * stack.c * * Copyright (c) 1995-1997, John Kilburg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "port_before.h" #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include #include #include "port_after.h" #include "ChimeraP.h" typedef enum { StackNewAction, StackBackAction, StackReloadAction, StackReplaceAction, StackRedrawAction, StackHomeAction } StackOpAction; typedef struct { char *url; void *state; /* * this is the state of the renderer (mostly meant for * scrollbar position) to be used when the document is * redisplayed. */ } URLInfo; struct ChimeraStackP { MemPool mp, tmp; ChimeraGUI wd; ChimeraContext wc; ChimeraStack parent; StackOpAction action; ChimeraRender wn; ChimeraSink wp; ChimeraSink pending; ChimeraRenderActionProc rap; void *rapc; ChimeraRenderHooks *rh; bool toplevel; /* list for URL stack */ GList urls; GList ourls; }; static void StackGUIResize _ArgProto((ChimeraGUI, void *, unsigned int, unsigned int)); static bool StackPop _ArgProto((ChimeraStack)); static void StackOp _ArgProto((ChimeraStack, ChimeraRequest *, StackOpAction)); int StackSinkInit _ArgProto((ChimeraSink, void *)); static void StackSinkAdd _ArgProto((void *)); static void StackSinkEnd _ArgProto((void *)); static void StackSinkMessage _ArgProto((void *, char *)); static void StackRenderAction _ArgProto((void *, ChimeraRender, ChimeraRequest *, char *)); /* * StackPop */ static bool StackPop(cs) ChimeraStack cs; { void *muck; if ((muck = GListPop(cs->urls)) != NULL) { GListAddHead(cs->ourls, muck); return(true); } return(false); } /* * StackMessage */ static void StackSinkMessage(closure, message) void *closure; char *message; { ChimeraStack cs = (ChimeraStack)closure; HeadPrintMessage(cs->wc, message); return; } /* * StackEnd */ static void StackSinkEnd(closure) void *closure; { ChimeraStack cs = (ChimeraStack)closure; RenderEnd(cs->wn); return; } /* * StackSinkAdd */ static void StackSinkAdd(closure) void *closure; { ChimeraStack cs = (ChimeraStack)closure; RenderAdd(cs->wn); return; } /* * StackOpen */ void StackOpen(cs, wr) ChimeraStack cs; ChimeraRequest *wr; { StackOp(cs, wr, StackNewAction); return; } /* * StackHome */ void StackHome(cs) ChimeraStack cs; { URLInfo *u; if (GListGetTail(cs->urls) == GListGetHead(cs->urls)) return; if ((u = (URLInfo *)GListGetTail(cs->urls)) == NULL) return; StackOp(cs, RequestCreate(cs->wc->cres, u->url, NULL), StackHomeAction); return; } /* * StackBack */ void StackBack(cs) ChimeraStack cs; { URLInfo *u; if (GListGetHead(cs->urls) == NULL) return; if ((u = (URLInfo *)GListGetNext(cs->urls)) == NULL) return; StackOp(cs, RequestCreate(cs->wc->cres, u->url, NULL), StackBackAction); return; } /* * StackReload */ void StackReload(cs) ChimeraStack cs; { URLInfo *u; ChimeraRequest *wr; if ((u = GListGetHead(cs->urls)) == NULL) return; wr = RequestCreate(cs->wc->cres, u->url, NULL); RequestReload(wr, true); StackOp(cs, wr, StackReloadAction); return; } /* * StackRedraw */ void StackRedraw(cs) ChimeraStack cs; { URLInfo *u; if ((u = GListGetHead(cs->urls)) == NULL) return; StackOp(cs, RequestCreate(cs->wc->cres, u->url, NULL), StackReplaceAction); return; } /* * StackCancel */ void StackCancel(cs) ChimeraStack cs; { if (cs->wp != NULL) SinkCancel(cs->wp); if (cs->pending != NULL) SinkCancel(cs->pending); if (cs->wn != NULL) RenderCancel(cs->wn); return; } /* * StackAction */ void StackAction(cs, wr, action) ChimeraStack cs; ChimeraRequest *wr; char *action; { MemPool mp; char *rstr; char *rname; size_t len; bool newhead; /* * Should probably give an error message here. */ if (wr == NULL) return; if (strcmp("open", action) == 0) { mp = MPCreate(); len = strlen(wr->scheme) + strlen(".newhead"); rname = (char *)MPGet(mp, len + 1); strncpy(rname, wr->scheme, len); strncat(rname, ".newhead", len); rname[len] = '\0'; if ((rstr = ResourceGetBool(cs->wc->cres, rname, &newhead)) == NULL || !newhead) { StackOpen(cs, wr); } else { HeadCreate(cs->wc->cres, wr, NULL); } MPDestroy(mp); } else if (strcmp("download", action) == 0) DownloadOpen(cs->wc->cres, wr); else if (strcmp("external", action) == 0) ViewOpen(cs->wc->cres, wr); else fprintf (stderr, "Action cannot be taken. Bug.\n"); return; } /* * StackRenderAction */ static void StackRenderAction(closure, wn, wr, action) void *closure; ChimeraRender wn; ChimeraRequest *wr; char *action; { StackAction((ChimeraStack)closure, wr, action); return; } /* * StackSinkInit */ int StackSinkInit(wp, closure) ChimeraSink wp; void *closure; { ChimeraStack cs = (ChimeraStack)closure; URLInfo *u; void *newstate, *curstate; char *url; char *ctype; ChimeraRenderHooks *rh; ChimeraRequest *wr; myassert(wp == cs->pending, "Data init is not the same as data pending."); url = SinkGetInfo(wp, "x-url"); ctype = SinkGetInfo(wp, "content-type"); if (cs->rh != NULL) rh = cs->rh; else { if ((rh = RenderGetHooks(cs->wc->cres, ctype)) == NULL) { if ((wr = RequestCreate(cs->wc->cres, url, NULL)) == NULL) { HeadPrintMessage(cs->wc, "Invalid URL."); } else ViewOpen(cs->wc->cres, wr); SinkDestroy(wp); cs->pending = NULL; return(-1); } } if (cs->wn != NULL) { curstate = RenderGetState(cs->wn); GUIReset(cs->wd); RenderDestroy(cs->wn); cs->wn = NULL; } else curstate = NULL; if (cs->wp != NULL) SinkDestroy(cs->wp); cs->wp = cs->pending; cs->pending = NULL; if (cs->action == StackBackAction) { StackPop(cs); u = (URLInfo *)GListGetHead(cs->urls); newstate = u->state; StackPop(cs); } else if (cs->action == StackReloadAction || cs->action == StackReplaceAction) { u = (URLInfo *)GListGetHead(cs->urls); newstate = u->state; StackPop(cs); } else if (cs->action == StackHomeAction) { u = (URLInfo *)GListGetTail(cs->urls); newstate = u->state; while (StackPop(cs)) ; MPDestroy(cs->tmp); cs->tmp = MPCreate(); } else { if ((u = (URLInfo *)GListGetHead(cs->urls)) != NULL) { u->state = curstate; } newstate = NULL; } if ((u = (URLInfo *)GListPop(cs->ourls)) == NULL) { u = (URLInfo *)MPCGet(cs->mp, sizeof(URLInfo)); } u->url = MPStrDup(cs->tmp, url); u->state = newstate; GListAddHead(cs->urls, u); if ((cs->wn = RenderCreate(cs->wc, cs->wd, cs->wp, rh, NULL, NULL, u->state, cs->rap, cs->rapc)) == NULL) { DownloadOpen(cs->wc->cres, wr); SinkDestroy(wp); return(-1); } if (cs->toplevel) HeadPrintURL(cs->wc, url); return(0); } /* * StackGetCurrentURL */ char * StackGetCurrentURL(cs) ChimeraStack cs; { URLInfo *u; if ((u = (URLInfo *)GListGetHead(cs->urls)) == NULL) return(NULL); return(u->url); } /* * StackOp */ static void StackOp(cs, wr, action) ChimeraStack cs; ChimeraRequest *wr; StackOpAction action; { ChimeraSinkHooks hooks; if (wr == NULL) { HeadPrintMessage(cs->wc, "Invalid URL"); return; } StackCancel(cs); if (cs->pending != NULL) { SinkDestroy(cs->pending); cs->pending = NULL; } cs->action = action; memset(&hooks, 0, sizeof(hooks)); hooks.init = StackSinkInit; hooks.add = StackSinkAdd; hooks.end = StackSinkEnd; hooks.message = StackSinkMessage; /* First, pick all of the contents we can deal with directly */ RequestAddRegexContent(cs->wc->cres, wr, "*/*"); /* Add whatever we can get onto the end */ RequestAddContent(wr, "*/*"); if (wr->url != NULL && cs->wc->cres->logfp != NULL) { fprintf (cs->wc->cres->logfp, "%s\n", wr->url); } cs->pending = SinkCreate(cs->wc->cres, wr); if (cs->pending != NULL) SinkSetHooks(cs->pending, &hooks, cs); return; } /* * StackGUIResize */ static void StackGUIResize(wd, closure, width, height) ChimeraGUI wd; void *closure; unsigned int width, height; { StackRedraw((ChimeraStack)closure); return; } /* * StackCreateToplevel */ ChimeraStack StackCreateToplevel(wc, widget) ChimeraContext wc; Widget widget; { ChimeraStack cs; MemPool mp; mp = MPCreate(); cs = (ChimeraStack)MPCGet(mp, sizeof(struct ChimeraStackP)); cs->wc = wc; cs->mp = mp; cs->tmp = MPCreate(); cs->urls = GListCreateX(mp); cs->ourls = GListCreateX(mp); cs->rap = StackRenderAction; cs->rapc = cs; GListAddHead(wc->cres->stacks, cs); cs->wd = GUICreateToplevel(wc, widget, StackGUIResize, cs); cs->toplevel = true; return(cs); } /* * StackCreate */ ChimeraStack StackCreate(parent, x, y, width, height, rap, rapc) ChimeraStack parent; int x, y; unsigned int width, height; ChimeraRenderActionProc rap; void *rapc; { ChimeraStack cs; MemPool mp; mp = MPCreate(); cs = (ChimeraStack)MPCGet(mp, sizeof(struct ChimeraStackP)); cs->parent = parent; cs->wc = parent->wc; cs->mp = mp; cs->tmp = MPCreate(); cs->urls = GListCreateX(mp); cs->ourls = GListCreateX(mp); if (cs->rap == NULL || cs->rapc == NULL) { cs->rap = StackRenderAction; cs->rapc = cs; } GListAddHead(parent->wc->cres->stacks, cs); cs->wd = GUICreate(parent->wc, parent->wd, StackGUIResize, cs); GUISetInitialDimensions(cs->wd, width, height); GUIMap(cs->wd, x, y); return(cs); } /* * StackToRender */ ChimeraRender StackToRender(cs) ChimeraStack cs; { return(cs->wn); } /* * StackToGUI */ ChimeraGUI StackToGUI(cs) ChimeraStack cs; { return(cs->wd); } /* * StackSetRender */ void StackSetRender(cs, rh) ChimeraStack cs; ChimeraRenderHooks *rh; { cs->rh = rh; return; } /* * StackDestroy */ void StackDestroy(cs) ChimeraStack cs; { if (cs->pending != NULL) SinkDestroy(cs->pending); if (cs->wp != NULL) SinkDestroy(cs->wp); if (cs->wn != NULL) RenderDestroy(cs->wn); if (cs->wd != NULL) GUIDestroy(cs->wd); if (cs->tmp != NULL) MPDestroy(cs->tmp); GListRemoveItem(cs->wc->cres->stacks, cs); MPDestroy(cs->mp); return; } /* * StackFromGUI */ ChimeraStack StackFromGUI(wc, wd) ChimeraContext wc; ChimeraGUI wd; { ChimeraStack cs; for (cs = (ChimeraStack)GListGetHead(wc->cres->stacks); cs != NULL; cs = (ChimeraStack)GListGetNext(wc->cres->stacks)) { if (cs->wd == wd) return(cs); } return(NULL); } /* * StackGetParent */ ChimeraStack StackGetParent(cs) ChimeraStack cs; { return(cs->parent); }