/*
* Copyright (c) 2005-2008 Hypertriton, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Visualization widget.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include "vg.h"
#include "vg_view.h"
#include "vg_tools.h"
#include "tools.h"
static const float scaleFactors[] = {
1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f,
12.0f, 14.0f, 16.0f, 18.0f, 20.0f, 22.0f, 24.0f, 26.0f, 28.0f,
30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.0f,
200.0f, 300.0f, 400.0f, 600.0f, 700.0f, 800.0f, 900.0f
};
const int nScaleFactors = sizeof(scaleFactors)/sizeof(scaleFactors[0]);
VG_View *
VG_ViewNew(void *parent, VG *vg, Uint flags)
{
VG_View *vv;
vv = Malloc(sizeof(VG_View));
AG_ObjectInit(vv, &vgViewClass);
vv->flags |= flags;
vv->vg = vg;
if (flags & VG_VIEW_HFILL) { AG_ExpandHoriz(vv); }
if (flags & VG_VIEW_VFILL) { AG_ExpandVert(vv); }
AG_ObjectAttach(parent, vv);
return (vv);
}
static void
MouseMotion(AG_Event *event)
{
VG_View *vv = AG_SELF();
VG_Tool *tool = VG_CURTOOL(vv);
int xCurs = AG_INT(1);
int yCurs = AG_INT(2);
float xRel = (float)AG_INT(3);
float yRel = (float)AG_INT(4);
int state = AG_INT(5);
float x, y;
VG_Vector vCt;
if (vv->mouse.panning) {
vv->x += (float)xRel;
vv->y += (float)yRel;
return;
}
VG_GetVGCoords(vv, xCurs,yCurs, &vCt);
x = vCt.x;
y = vCt.y;
if (vv->vg == NULL) {
return;
}
if (tool != NULL && tool->ops->mousemotion != NULL) {
if (!(tool->ops->flags & VG_MOUSEMOTION_NOSNAP)) {
VG_ApplyConstraints(vv, &vCt);
}
tool->vCursor = vCt;
if (tool->ops->mousemotion(tool, vCt,
VG_ScaleVector(1.0f/vv->scale,VGVECTOR(xRel,yRel)),
state) == 1) {
vv->mouse.x = x;
vv->mouse.y = y;
return;
}
} else {
vv->mouse.x = x;
vv->mouse.y = y;
}
}
static void
MouseButtonDown(AG_Event *event)
{
VG_View *vv = AG_SELF();
VG_Tool *tool = VG_CURTOOL(vv);
int button = AG_INT(1);
int xCurs = AG_INT(2);
int yCurs = AG_INT(3);
float x, y;
VG_Vector vCt;
VG_GetVGCoords(vv, xCurs,yCurs, &vCt);
x = vCt.x;
y = vCt.y;
AG_WidgetFocus(vv);
vv->mouse.x = x;
vv->mouse.y = y;
switch (button) {
case SDL_BUTTON_MIDDLE:
vv->mouse.panning = 1;
break;
case SDL_BUTTON_WHEELDOWN:
if (vv->scaleIdx-1 >= 0) {
VG_ViewSetScale(vv, --vv->scaleIdx);
VG_Status(vv, _("Scale: 1:%.0f"), vv->scale);
}
return;
case SDL_BUTTON_WHEELUP:
if (vv->scaleIdx+1 < nScaleFactors) {
VG_ViewSetScale(vv, ++vv->scaleIdx);
VG_Status(vv, _("Scale: 1:%.0f"), vv->scale);
}
return;
default:
break;
}
if (vv->vg == NULL) {
return;
}
if (tool != NULL && tool->ops->mousebuttondown != NULL) {
if (!(tool->ops->flags & VG_BUTTONDOWN_NOSNAP)) {
VG_ApplyConstraints(vv, &vCt);
}
if (tool->ops->mousebuttondown(tool, vCt, button) == 1)
return;
}
if (vv->btndown_ev != NULL)
AG_PostEvent(NULL, vv, vv->btndown_ev->name,
"%i,%f,%f", button, x, y);
}
static void
MouseButtonUp(AG_Event *event)
{
VG_View *vv = AG_SELF();
VG_Tool *tool = VG_CURTOOL(vv);
int button = AG_INT(1);
int xCurs = AG_INT(2);
int yCurs = AG_INT(3);
float x, y;
VG_Vector vCt;
if (vv->vg == NULL)
return;
VG_GetVGCoords(vv, xCurs,yCurs, &vCt);
x = vCt.x;
y = vCt.y;
if (tool != NULL && tool->ops->mousebuttonup != NULL) {
if (!(tool->ops->flags & VG_BUTTONUP_NOSNAP)) {
VG_ApplyConstraints(vv, &vCt);
}
if (tool->ops->mousebuttonup(tool, vCt, button) == 1)
return;
}
switch (button) {
case SDL_BUTTON_MIDDLE:
vv->mouse.panning = 0;
return;
default:
break;
}
if (vv->btnup_ev != NULL)
AG_PostEvent(NULL, vv, vv->btnup_ev->name, "%i,%f,%f",
button, x, y);
}
static void
KeyDown(AG_Event *event)
{
VG_View *vv = AG_SELF();
VG_Tool *tool = VG_CURTOOL(vv);
int keysym = AG_INT(1);
int keymod = AG_INT(2);
int unicode = AG_INT(3);
VG_ToolCommand *cmd;
if (vv->vg == NULL || tool == NULL)
return;
if (tool->ops->keydown != NULL &&
tool->ops->keydown(tool, keysym, keymod, unicode) == 1) {
return;
}
TAILQ_FOREACH(cmd, &tool->cmds, cmds) {
if (cmd->kSym == keysym &&
(cmd->kMod == KMOD_NONE || keymod & cmd->kMod)) {
Debug(tool->vgv, "%s: KBD: <%s>\n", tool->ops->name, cmd->name);
AG_PostEvent(NULL, tool->vgv, cmd->fn->name, "%p", tool);
}
}
/* TODO panning, etc... */
}
static void
KeyUp(AG_Event *event)
{
VG_View *vv = AG_SELF();
VG_Tool *tool = VG_CURTOOL(vv);
int keysym = AG_INT(1);
int keymod = AG_INT(2);
int unicode = AG_INT(3);
if (vv->vg == NULL)
return;
if (tool != NULL && tool->ops->keyup != NULL) {
if (tool->ops->keyup(tool, keysym, keymod, unicode) == 1)
return;
}
/* TODO panning, etc... */
}
static void
OnShow(AG_Event *event)
{
VG_View *vv = AG_SELF();
vv->x = AGWIDGET(vv)->w/2.0f;
vv->y = AGWIDGET(vv)->h/2.0f;
}
static void
UpdateGridIntervals(VG_View *vv)
{
int i;
for (i = 0; i < vv->nGrids; i++) {
VG_Grid *grid = &vv->grid[i];
grid->ivalView = (int)VG_Rint(((float)grid->ival)/vv->wPixel);
if (grid->ivalView < 6) {
grid->flags |= VG_GRID_UNDERSIZE;
} else {
grid->flags &= ~(VG_GRID_UNDERSIZE);
}
}
}
static void
Init(void *obj)
{
VG_View *vv = obj;
WIDGET(vv)->flags |= AG_WIDGET_FOCUSABLE;
vv->flags = 0;
vv->vg = NULL;
vv->draw_ev = NULL;
vv->scale_ev = NULL;
vv->keydown_ev = NULL;
vv->btndown_ev = NULL;
vv->keyup_ev = NULL;
vv->btnup_ev = NULL;
vv->motion_ev = NULL;
vv->x = 0.0f;
vv->y = 0.0f;
vv->scaleIdx = 0;
vv->scale = scaleFactors[0];
vv->scaleMin = 1.0f;
vv->scaleMax = 1e6f;
vv->wPixel = 1.0f;
vv->snap_mode = VG_GRID;
vv->ortho_mode = VG_NO_ORTHO;
vv->mouse.x = 0.0f;
vv->mouse.y = 0.0f;
vv->mouse.panning = 0;
vv->curtool = NULL;
vv->deftool = NULL;
vv->status[0] = '\0';
vv->tCache = AG_TextCacheNew(vv, 128, 16);
vv->editAreas = NULL;
vv->nEditAreas = 0;
vv->r = AG_RECT(0,0,0,0);
TAILQ_INIT(&vv->tools);
vv->nGrids = 0;
VG_ViewSetGrid(vv, 0, VG_GRID_POINTS, 8, VG_GetColorRGB(100,100,100));
VG_ViewSetScale(vv, 0);
AG_SetEvent(vv, "window-mousemotion", MouseMotion, NULL);
AG_SetEvent(vv, "window-mousebuttondown", MouseButtonDown, NULL);
AG_SetEvent(vv, "window-mousebuttonup", MouseButtonUp, NULL);
AG_SetEvent(vv, "window-keydown", KeyDown, NULL);
AG_SetEvent(vv, "window-keyup", KeyUp, NULL);
AG_SetEvent(vv, "widget-shown", OnShow, NULL);
}
static void
Destroy(void *obj)
{
VG_View *vv = obj;
if (vv->tCache != NULL)
AG_TextCacheDestroy(vv->tCache);
}
/* Change the VG being displayed. */
void
VG_ViewSetVG(VG_View *vv, VG *vg)
{
AG_ObjectLock(vv);
if (vv->vg != vg) {
vv->vg = vg;
VG_ViewSelectTool(vv, NULL, NULL);
}
AG_ObjectUnlock(vv);
}
/* Set the snapping constraint. */
void
VG_ViewSetSnapMode(VG_View *vv, enum vg_snap_mode mode)
{
AG_ObjectLock(vv);
vv->snap_mode = mode;
AG_ObjectUnlock(vv);
}
/* Set the orthogonal constraint. */
void
VG_ViewSetOrthoMode(VG_View *vv, enum vg_ortho_mode mode)
{
AG_ObjectLock(vv);
vv->ortho_mode = mode;
AG_ObjectUnlock(vv);
}
/* Set the parameters of the specified grid. */
void
VG_ViewSetGrid(VG_View *vv, int idx, enum vg_grid_type type, int ival,
VG_Color color)
{
if (idx+1 > VG_GRIDS_MAX)
AG_FatalError("Too many grids");
AG_ObjectLock(vv);
vv->grid[idx].type = type;
vv->grid[idx].ival = ival;
vv->grid[idx].ivalView = 0;
vv->grid[idx].color = color;
vv->grid[idx].flags = 0;
if (idx == 0) {
vv->pointSelRadius = vv->grid[0].ival/2;
}
if (idx >= vv->nGrids) {
vv->nGrids = idx+1;
}
UpdateGridIntervals(vv);
AG_ObjectUnlock(vv);
}
/* Register a "draw" callback. */
void
VG_ViewDrawFn(VG_View *vv, AG_EventFn fn, const char *fmt, ...)
{
AG_ObjectLock(vv);
vv->draw_ev = AG_SetEvent(vv, NULL, fn, NULL);
AG_EVENT_GET_ARGS(vv->draw_ev, fmt);
AG_ObjectUnlock(vv);
}
/* Register a "resize" callback. */
void
VG_ViewScaleFn(VG_View *vv, AG_EventFn fn, const char *fmt, ...)
{
AG_ObjectLock(vv);
vv->scale_ev = AG_SetEvent(vv, NULL, fn, NULL);
AG_EVENT_GET_ARGS(vv->scale_ev, fmt);
AG_ObjectUnlock(vv);
}
/* Register a keydown callback. */
void
VG_ViewKeydownFn(VG_View *vv, AG_EventFn fn, const char *fmt, ...)
{
AG_ObjectLock(vv);
vv->keydown_ev = AG_SetEvent(vv, "window-keydown", fn, NULL);
AG_EVENT_GET_ARGS(vv->keydown_ev, fmt);
AG_ObjectUnlock(vv);
}
/* Register a keyup callback. */
void
VG_ViewKeyupFn(VG_View *vv, AG_EventFn fn, const char *fmt, ...)
{
AG_ObjectLock(vv);
vv->keyup_ev = AG_SetEvent(vv, "window-keyup", fn, NULL);
AG_EVENT_GET_ARGS(vv->keyup_ev, fmt);
AG_ObjectUnlock(vv);
}
/* Register a mousebuttondown callback. */
void
VG_ViewButtondownFn(VG_View *vv, AG_EventFn fn, const char *fmt, ...)
{
AG_ObjectLock(vv);
vv->btndown_ev = AG_SetEvent(vv, NULL, fn, NULL);
AG_EVENT_GET_ARGS(vv->btndown_ev, fmt);
AG_ObjectUnlock(vv);
}
/* Register a mousebuttonup callback. */
void
VG_ViewButtonupFn(VG_View *vv, AG_EventFn fn, const char *fmt, ...)
{
AG_ObjectLock(vv);
vv->btnup_ev = AG_SetEvent(vv, "window-mousebuttonup", fn, NULL);
AG_EVENT_GET_ARGS(vv->btnup_ev, fmt);
AG_ObjectUnlock(vv);
}
/* Register a mousemotion callback. */
void
VG_ViewMotionFn(VG_View *vv, AG_EventFn fn, const char *fmt, ...)
{
AG_ObjectLock(vv);
vv->motion_ev = AG_SetEvent(vv, "window-mousemotion", fn, NULL);
AG_EVENT_GET_ARGS(vv->motion_ev, fmt);
AG_ObjectUnlock(vv);
}
static void
SizeRequest(void *obj, AG_SizeReq *r)
{
r->w = 16;
r->h = 16;
}
static int
SizeAllocate(void *obj, const AG_SizeAlloc *a)
{
VG_View *vv = obj;
if (a->w < 16 || a->h < 16) {
return (-1);
}
vv->r.w = a->w;
vv->r.h = a->h;
return (0);
}
static __inline__ void
DrawGrid(VG_View *vv, const VG_Grid *grid)
{
int x, x0, y, ival;
if (grid->flags & (VG_GRID_HIDE|VG_GRID_UNDERSIZE))
return;
ival = grid->ivalView;
#ifdef HAVE_OPENGL
if (agView->opengl) {
x0 = WIDGET(vv)->rView.x1 + (int)(vv->x)%ival;
y = WIDGET(vv)->rView.y1 + (int)(vv->y)%ival;
glBegin(GL_POINTS);
glColor3ub(grid->color.r, grid->color.g, grid->color.b);
for (; y < WIDGET(vv)->rView.y2; y += ival) {
for (x = x0; x < WIDGET(vv)->rView.x2; x += ival)
glVertex2s(x, y);
}
glEnd();
} else
#endif
{
Uint32 c32;
x0 = (int)(vv->x)%ival;
y = (int)(vv->y)%ival;
c32 = VG_MapColorRGB(grid->color);
for (; y < WIDGET(vv)->rView.y2; y += ival) {
for (x = x0; x < WIDGET(vv)->rView.x2; x += ival)
AG_DrawPixel(vv, x, y, c32);
}
}
}
#ifdef AG_DEBUG
static void
DrawNodeExtent(VG_Node *vn, VG_View *vv)
{
AG_Rect rExt;
VG_Vector a, b;
if (vn->ops->extent == NULL) {
return;
}
vn->ops->extent(vn, vv, &a, &b);
VG_GetViewCoords(vv, a, &rExt.x, &rExt.y);
rExt.w = (int)((b.x - a.x)*vv->scale);
rExt.h = (int)((b.y - a.y)*vv->scale);
AG_DrawRectOutline(vv, rExt, AG_MapRGB(agVideoFmt, 250,0,0));
}
#endif /* AG_DEBUG */
static void
DrawNode(VG *vg, VG_Node *vn, VG_View *vv)
{
VG_Node *vnChld;
VG_Color colorSave;
VG_PushMatrix(vg);
VG_MultMatrix(&vg->T[vg->nT-1], &vn->T);
VG_FOREACH_CHLD(vnChld, vn, vg_node)
DrawNode(vg, vnChld, vv);
#ifdef AG_DEBUG
if (vv->flags & VG_VIEW_EXTENTS)
DrawNodeExtent(vn, vv);
#endif
colorSave = vn->color;
if (vn->flags & VG_NODE_SELECTED) {
VG_BlendColors(&vn->color, vg->selectionColor);
}
if (vn->flags & VG_NODE_MOUSEOVER) {
VG_BlendColors(&vn->color, vg->mouseoverColor);
}
vn->ops->draw(vn, vv);
vn->color = colorSave;
VG_PopMatrix(vg);
}
static void
Draw(void *obj)
{
VG_View *vv = obj;
VG *vg = vv->vg;
int su, i;
if (vg == NULL)
return;
if (!(vv->flags & VG_VIEW_DISABLE_BG))
AG_DrawRectFilled(vv, vv->r, VG_MapColorRGB(vg->fillColor));
AG_PushClipRect(vv, vv->r);
if (vv->flags & VG_VIEW_GRID)
for (i = 0; i < vv->nGrids; i++)
DrawGrid(vv, &vv->grid[i]);
VG_Lock(vg);
if (vv->curtool != NULL && vv->curtool->ops->predraw != NULL) {
vv->curtool->ops->predraw(vv->curtool, vv);
}
if (vv->draw_ev != NULL) {
vv->draw_ev->handler(vv->draw_ev);
}
if (vv->curtool != NULL && vv->curtool->ops->postdraw != NULL) {
vv->curtool->ops->postdraw(vv->curtool, vv);
}
DrawNode(vg, vg->root, vv);
VG_Unlock(vg);
if (vv->status[0] != '\0') {
AG_TextColor(TEXT_COLOR);
su = AG_TextCacheGet(vv->tCache, vv->status);
AG_WidgetBlitSurface(vv, su,
0,
HEIGHT(vv) - WSURFACE(vv,su)->h);
}
AG_PopClipRect();
}
/* Select a new tool to use. */
void
VG_ViewSelectTool(VG_View *vv, void *pTool, void *p)
{
VG_Tool *ntool = pTool;
int i;
AG_ObjectLock(vv);
if (ntool == NULL || !(ntool->ops->flags & VG_NOEDITCLEAR)) {
for (i = 0; i < vv->nEditAreas; i++) {
AG_ObjectFreeChildren(vv->editAreas[i]);
AG_WindowUpdate(AG_ParentWindow(vv->editAreas[i]));
}
}
if (vv->curtool != NULL) {
if (ntool != NULL && ntool->ops == vv->curtool->ops) {
goto out;
}
if (vv->curtool->editWin != NULL) {
AG_WindowHide(vv->curtool->editWin);
}
if (vv->curtool->ops->deselected != NULL) {
vv->curtool->ops->deselected(ntool, vv);
}
vv->curtool->selected = 0;
}
vv->curtool = ntool;
if (ntool != NULL) {
ntool->selected = 1;
ntool->p = p;
ntool->vgv = vv;
if (ntool->editWin != NULL) {
AG_WindowShow(ntool->editWin);
}
if (ntool->ops->edit != NULL && vv->nEditAreas > 0) {
AG_ObjectAttach(vv->editAreas[0],
ntool->ops->edit(ntool,vv));
AG_WindowUpdate(AG_ParentWindow(vv->editAreas[0]));
}
VG_Status(vv, _("Tool: %s"), ntool->ops->name);
if (ntool->ops->selected != NULL)
ntool->ops->selected(ntool, vv);
} else {
VG_Status(vv, NULL);
}
out:
AG_ObjectUnlock(vv);
}
/* Generic event handler for tool selection. */
void
VG_ViewSelectToolEv(AG_Event *event)
{
VG_View *vv = AG_PTR(1);
AG_WidgetFocus(vv);
VG_ViewSelectTool(vv, AG_PTR(2), AG_PTR(3));
}
/* VG_View must be locked */
VG_Tool *
VG_ViewFindTool(VG_View *vv, const char *name)
{
VG_Tool *tool;
TAILQ_FOREACH(tool, &vv->tools, tools) {
if (strcmp(tool->ops->name, name) == 0)
return (tool);
}
AG_SetError("No such tool: %s", name);
return (NULL);
}
/* VG_View must be locked */
VG_Tool *
VG_ViewFindToolByOps(VG_View *vv, const VG_ToolOps *ops)
{
VG_Tool *tool;
TAILQ_FOREACH(tool, &vv->tools, tools) {
if (tool->ops == ops)
return (tool);
}
AG_SetError("No such tool: %p", ops);
return (NULL);
}
/* Register a new VG_Tool class. */
VG_Tool *
VG_ViewRegTool(VG_View *vv, const VG_ToolOps *ops, void *p)
{
VG_Tool *t;
t = Malloc(ops->len);
t->ops = ops;
t->vgv = vv;
t->p = p;
AG_ObjectLock(vv);
VG_ToolInit(t);
TAILQ_INSERT_TAIL(&vv->tools, t, tools);
AG_ObjectUnlock(vv);
return (t);
}
/* Set the minimum allowed display scale factor. */
void
VG_ViewSetScaleMin(VG_View *vv, float scaleMin)
{
AG_ObjectLock(vv);
vv->scaleMin = MAX(scaleMin,1e-6f);
AG_ObjectUnlock(vv);
}
/* Set the maximum allowed display scale factor. */
void
VG_ViewSetScaleMax(VG_View *vv, float scaleMax)
{
AG_ObjectLock(vv);
vv->scaleMax = scaleMax;
AG_ObjectUnlock(vv);
}
/* Set the display scale factor explicitely. */
void
VG_ViewSetScale(VG_View *vv, float c)
{
float scalePrev;
AG_ObjectLock(vv);
scalePrev = vv->scale;
/* Set the specified scaling factor. */
/* vv->scaleIdx = idx; XXX */
vv->scale = c;
if (vv->scale < vv->scaleMin) { vv->scale = vv->scaleMin; }
if (vv->scale > vv->scaleMax) { vv->scale = vv->scaleMax; }
/* Update all values dependent on VG's representation of a pixel. */
vv->wPixel = 1.0/vv->scale;
vv->x *= (vv->scale/scalePrev);
vv->y *= (vv->scale/scalePrev);
vv->pointSelRadius = vv->grid[0].ival/2;
UpdateGridIntervals(vv);
AG_ObjectUnlock(vv);
}
/* Set the display scale factor by preset index. */
void
VG_ViewSetScalePreset(VG_View *vv, int idx)
{
if (idx < 0) { idx = 0; }
else if (idx >= nScaleFactors) { idx = nScaleFactors-1; }
VG_ViewSetScale(vv, scaleFactors[idx]);
}
/* Set the specified tool as default. */
void
VG_ViewSetDefaultTool(VG_View *vv, VG_Tool *tool)
{
AG_ObjectLock(vv);
vv->deftool = tool;
AG_ObjectUnlock(vv);
}
/* Set the status line text. */
void
VG_Status(VG_View *vv, const char *fmt, ...)
{
va_list ap;
AG_ObjectLock(vv);
if (fmt != NULL) {
va_start(ap, fmt);
Vsnprintf(vv->status, sizeof(vv->status), fmt, ap);
va_end(ap);
} else {
vv->status[0] = '\0';
}
AG_ObjectUnlock(vv);
}
/* Register a new container widget to associate with the tool. */
Uint
VG_AddEditArea(VG_View *vv, void *widget)
{
Uint name;
AG_ObjectLock(vv);
vv->editAreas = Realloc(vv->editAreas, (vv->nEditAreas+1) *
sizeof(AG_Widget *));
name = vv->nEditAreas++;
vv->editAreas[name] = widget;
AG_ObjectUnlock(vv);
return (name);
}
/* Destroy widgets attached to all edit areas. */
void
VG_ClearEditAreas(VG_View *vv)
{
Uint i;
AG_ObjectLock(vv);
for (i = 0; i < vv->nEditAreas; i++) {
AG_Widget *editArea = vv->editAreas[i];
AG_ObjectFreeChildren(editArea);
AG_WindowUpdate(AG_ParentWindow(editArea));
AG_WidgetHiddenRecursive(editArea);
}
AG_ObjectUnlock(vv);
}
/* Create GUI elements for editing the parameters of vn. */
void
VG_EditNode(VG_View *vv, Uint editArea, VG_Node *vn)
{
void *wEdit;
if (vv->nEditAreas > editArea &&
vn->ops->edit != NULL &&
(wEdit = vn->ops->edit(vn, vv)) != NULL) {
AG_ObjectFreeChildren(vv->editAreas[editArea]);
AG_ObjectAttach(vv->editAreas[editArea], wEdit);
AG_WindowUpdate(AG_ParentWindow(vv->editAreas[editArea]));
AG_WidgetShownRecursive(vv->editAreas[editArea]);
}
}
/* Render a mapped surface at the specified coordinates and rotation. */
void
VG_DrawSurface(VG_View *vv, int x, int y, float degs, int su)
{
#ifdef HAVE_OPENGL
if (agView->opengl) {
glPushMatrix();
glTranslatef((float)(AGWIDGET(vv)->rView.x1 + x),
(float)(AGWIDGET(vv)->rView.y1 + y),
0.0f);
if (degs != 0.0f) {
glRotatef(degs, 0.0f, 0.0f, 1.0f);
}
AG_WidgetBlitSurfaceGL(vv, su,
WSURFACE(vv,su)->w,
WSURFACE(vv,su)->h);
glPopMatrix();
} else
#endif /* HAVE_OPENGL */
{
AG_WidgetBlitSurface(vv, su, x, y);
}
}
AG_WidgetClass vgViewClass = {
{
"Agar(Widget):VG(View)",
sizeof(VG_View),
{ 0,0 },
Init,
NULL, /* free */
Destroy,
NULL, /* load */
NULL, /* save */
NULL /* edit */
},
Draw,
SizeRequest,
SizeAllocate
};