/*
* Copyright (c) 2005-2007 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.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include "tileview.h"
#include "icons.h"
#include
#include
RG_Tileview *
RG_TileviewNew(void *parent, RG_Tileset *ts, Uint flags)
{
RG_Tileview *tv;
tv = Malloc(sizeof(RG_Tileview));
AG_ObjectInit(tv, &rgTileviewClass);
tv->ts = ts;
tv->flags |= flags;
AG_ObjectAttach(parent, tv);
return (tv);
}
static Uint32
ZoomInTimeout(void *obj, Uint32 ival, void *arg)
{
RG_Tileview *tv = obj;
if (tv->zoom > 1600) {
return (0);
}
RG_TileviewSetZoom(tv, tv->zoom+20, 1);
return (ival);
}
static Uint32
ZoomOutTimeout(void *obj, Uint32 ival, void *arg)
{
RG_Tileview *tv = obj;
if (tv->zoom < 100) {
return (0);
}
RG_TileviewSetZoom(tv, tv->zoom-20, 1);
return (ival);
}
static __inline__ void
MoveCursor(RG_Tileview *tv, int x, int y)
{
tv->xms = x;
tv->yms = y;
tv->xsub = tv->xms%tv->pxsz;
tv->ysub = tv->yms%tv->pxsz;
tv->xms -= tv->xsub;
tv->yms -= tv->ysub;
tv->xms /= tv->pxsz;
tv->yms /= tv->pxsz;
}
static __inline__ int
CursorOver(RG_TileviewHandle *th, int sx, int sy)
{
return (sx >= th->x - 2 && sx <= th->x + 2 &&
sy >= th->y - 2 && sy <= th->y + 2);
}
static void
KeyDown(AG_Event *event)
{
RG_Tileview *tv = AG_SELF();
int keysym = AG_INT(1);
int keymod = AG_INT(2);
switch (tv->state) {
case RG_TILEVIEW_PIXMAP_EDIT:
RG_PixmapKeydown(keysym);
break;
#if 0
case RG_TILEVIEW_SKETCH_EDIT:
RG_SketchKeyDown(tv, tv->tv_sketch.tel, keysym, keymod);
break;
#endif
default:
break;
}
switch (keysym) {
case SDLK_z:
if (keymod & KMOD_CTRL) {
switch (tv->state) {
case RG_TILEVIEW_PIXMAP_EDIT:
RG_PixmapUndo(tv, tv->tv_pixmap.tel);
break;
#if 0
case RG_TILEVIEW_SKETCH_EDIT:
RG_SketchUndo(tv, tv->tv_sketch.tel);
break;
#endif
default:
break;
}
}
break;
case SDLK_r:
if (keymod & KMOD_CTRL) {
switch (tv->state) {
case RG_TILEVIEW_PIXMAP_EDIT:
RG_PixmapRedo(tv, tv->tv_pixmap.tel);
break;
#if 0
case RG_TILEVIEW_SKETCH_EDIT:
RG_SketchRedo(tv, tv->tv_sketch.tel);
break;
#endif
default:
break;
}
} else {
tv->tile->flags |= RG_TILE_DIRTY;
}
break;
case SDLK_EQUALS:
AG_SetTimeout(&tv->zoom_to, ZoomInTimeout, NULL, 0);
AG_ScheduleTimeout(tv, &tv->zoom_to, 10);
ZoomInTimeout(tv, 0, NULL);
break;
case SDLK_MINUS:
AG_SetTimeout(&tv->zoom_to, ZoomOutTimeout, NULL, 0);
AG_ScheduleTimeout(tv, &tv->zoom_to, 10);
ZoomOutTimeout(tv, 0, NULL);
break;
case SDLK_0:
case SDLK_1:
RG_TileviewSetZoom(tv, 100, 1);
break;
}
}
static void
KeyUp(AG_Event *event)
{
RG_Tileview *tv = AG_SELF();
int keysym = AG_INT(1);
/* int keymod = AG_INT(2); */
switch (tv->state) {
case RG_TILEVIEW_PIXMAP_EDIT:
RG_PixmapKeyup();
break;
#if 0
case RG_TILEVIEW_SKETCH_EDIT:
RG_SketchKeyUp(tv, tv->tv_sketch.tel, keysym, keymod);
break;
#endif
default:
break;
}
switch (keysym) {
case SDLK_EQUALS:
case SDLK_MINUS:
AG_DelTimeout(tv, &tv->zoom_to);
break;
}
}
static __inline__ int
OverPixmap(RG_TileElement *tel, int x, int y)
{
return (x >= tel->tel_pixmap.x &&
x < tel->tel_pixmap.x + tel->tel_pixmap.px->su->w &&
y >= tel->tel_pixmap.y &&
y < tel->tel_pixmap.y + tel->tel_pixmap.px->su->h);
}
#if 0
static __inline__ int
OverSketch(RG_TileElement *tel, int x, int y)
{
return (x >= tel->tel_sketch.x &&
x < tel->tel_sketch.x + tel->tel_sketch.sk->vg->su->w &&
y >= tel->tel_sketch.y &&
y < tel->tel_sketch.y + tel->tel_sketch.sk->vg->su->h);
}
#endif
static void
ToggleAttrib(RG_Tileview *tv, int sx, int sy)
{
RG_Tile *t = tv->tile;
int nx = sx/RG_TILESZ;
int ny = sy/RG_TILESZ;
Uint *a;
if (nx < 0 || nx >= t->nw ||
ny < 0 || ny >= t->nh ||
(tv->tv_attrs.nx == nx && tv->tv_attrs.ny == ny))
return;
a = &RG_TILE_ATTR2(t,nx,ny);
if (*a & tv->edit_attr) {
*a &= ~(tv->edit_attr);
} else {
*a |= tv->edit_attr;
}
tv->tv_attrs.nx = nx;
tv->tv_attrs.ny = ny;
t->flags |= RG_TILE_DIRTY;
}
static void
IncrementLayer(RG_Tileview *tv, int sx, int sy, int inc)
{
RG_Tile *t = tv->tile;
int nx = sx/RG_TILESZ;
int ny = sy/RG_TILESZ;
int *a;
if (nx < 0 || nx >= t->nw ||
ny < 0 || ny >= t->nh)
return;
a = &RG_TILE_LAYER2(t,nx,ny);
(*a) += inc;
t->flags |= RG_TILE_DIRTY;
}
static void
MouseButtonDown(AG_Event *event)
{
RG_Tileview *tv = AG_SELF();
int button = AG_INT(1);
int x = AG_INT(2);
int y = AG_INT(3);
RG_TileviewCtrl *ctrl;
RG_TileElement *tel = tv->tv_pixmap.tel;
int sx = (x - tv->xoffs)/tv->pxsz;
int sy = (y - tv->yoffs)/tv->pxsz;
int i;
AG_WidgetFocus(tv);
switch (button) {
case SDL_BUTTON_WHEELUP:
if (tv->state == RG_TILEVIEW_PIXMAP_EDIT) {
if (RG_PixmapWheel(tv, tel, 0) == 1)
return;
#if 0
} else if (tv->state == RG_TILEVIEW_SKETCH_EDIT) {
if (RG_SketchWheel(tv, tel, 0) == 1)
return;
#endif
}
RG_TileviewSetZoom(tv,
tv->zoom<100 ? tv->zoom+5 : tv->zoom+100, 1);
MoveCursor(tv, x - tv->xoffs, y - tv->yoffs);
return;
case SDL_BUTTON_WHEELDOWN:
if (tv->state == RG_TILEVIEW_PIXMAP_EDIT) {
if (RG_PixmapWheel(tv, tel, 1) == 1)
return;
#if 0
} else if (tv->state == RG_TILEVIEW_SKETCH_EDIT) {
if (RG_SketchWheel(tv, tel, 1) == 1)
return;
#endif
}
RG_TileviewSetZoom(tv,
tv->zoom<100 ? tv->zoom-5 : tv->zoom-100, 1);
MoveCursor(tv, x - tv->xoffs, y - tv->yoffs);
break;
default:
break;
}
if (button == SDL_BUTTON_LEFT &&
(tv->flags & RG_TILEVIEW_HIDE_CONTROLS) == 0) {
TAILQ_FOREACH(ctrl, &tv->ctrls, ctrls) {
for (i = 0; i < ctrl->nhandles; i++) {
RG_TileviewHandle *th = &ctrl->handles[i];
if (CursorOver(th, sx, sy)) {
th->enable = 1;
tv->xorig = sx;
tv->yorig = sy;
ctrl->xoffs = 0;
ctrl->yoffs = 0;
if (ctrl->buttondown != NULL) {
AG_PostEvent(NULL, tv,
ctrl->buttondown->name,
"%i,%i", sx, sy);
}
break;
}
}
if (i < ctrl->nhandles)
return;
}
}
switch (tv->state) {
case RG_TILEVIEW_PIXMAP_EDIT:
if (OverPixmap(tel, sx, sy)) {
tv->tv_pixmap.xorig = sx - tel->tel_pixmap.x;
tv->tv_pixmap.yorig = sy - tel->tel_pixmap.y;
RG_PixmapButtondown(tv, tel, tv->tv_pixmap.xorig,
tv->tv_pixmap.yorig, button);
return;
} else {
if (button == SDL_BUTTON_RIGHT)
tv->scrolling++;
}
break;
#if 0
case RG_TILEVIEW_SKETCH_EDIT:
if (button == SDL_BUTTON_RIGHT &&
(tv->flags & RG_TILEVIEW_NO_SCROLLING) == 0) {
tv->scrolling++;
}
if (button == SDL_BUTTON_MIDDLE || button == SDL_BUTTON_RIGHT ||
(button == SDL_BUTTON_LEFT &&
OverSketch(tel, sx, sy)))
{
RG_Sketch *sk = tv->tv_sketch.sk;
float vx, vy;
vx = VG_VECXF(sk->vg, sx - tel->tel_sketch.x);
vy = VG_VECYF(sk->vg, sy - tel->tel_sketch.y);
RG_SketchButtondown(tv, tel, vx, vy, button);
return;
}
break;
#endif
case RG_TILEVIEW_FEATURE_EDIT:
if (button == SDL_BUTTON_MIDDLE) {
if (tv->tv_feature.ft->ops->menu != NULL) {
RG_FeatureOpenMenu(tv,
WIDGET(tv)->rView.x1 + x,
WIDGET(tv)->rView.y1 + y);
}
} else if (button == SDL_BUTTON_RIGHT) {
tv->scrolling++;
}
break;
case RG_TILEVIEW_TILE_EDIT:
if (button == SDL_BUTTON_RIGHT) {
tv->scrolling++;
}
break;
case RG_TILEVIEW_ATTRIB_EDIT:
if (button == SDL_BUTTON_RIGHT) {
tv->scrolling++;
} else if (button == SDL_BUTTON_LEFT) {
tv->flags |= RG_TILEVIEW_SET_ATTRIBS;
tv->tv_attrs.nx = -1;
tv->tv_attrs.ny = -1;
ToggleAttrib(tv, sx, sy);
}
break;
case RG_TILEVIEW_LAYERS_EDIT:
if (button == SDL_BUTTON_LEFT) {
IncrementLayer(tv, sx, sy, +1);
} else if (button == SDL_BUTTON_RIGHT) {
IncrementLayer(tv, sx, sy, -1);
tv->scrolling++;
}
break;
}
if (button == SDL_BUTTON_MIDDLE &&
tv->state == RG_TILEVIEW_TILE_EDIT) {
RG_TileOpenMenu(tv,
WIDGET(tv)->rView.x1 + x,
WIDGET(tv)->rView.y1 + y);
}
}
static void
MouseButtonUp(AG_Event *event)
{
RG_Tileview *tv = AG_SELF();
int button = AG_INT(1);
RG_TileviewCtrl *ctrl;
int i;
if (button == SDL_BUTTON_RIGHT ||
button == SDL_BUTTON_MIDDLE)
tv->scrolling = 0;
switch (button) {
case SDL_BUTTON_RIGHT:
case SDL_BUTTON_MIDDLE:
tv->scrolling = 0;
break;
case SDL_BUTTON_LEFT:
TAILQ_FOREACH(ctrl, &tv->ctrls, ctrls) {
for (i = 0; i < ctrl->nhandles; i++) {
RG_TileviewHandle *th = &ctrl->handles[i];
if (th->enable) {
th->enable = 0;
if (ctrl->buttonup != NULL) {
AG_PostEvent(NULL, tv,
ctrl->buttonup->name, NULL);
}
break;
}
}
if (i < ctrl->nhandles)
break;
}
if (ctrl == NULL) {
switch (tv->state) {
case RG_TILEVIEW_PIXMAP_EDIT:
{
RG_TileElement *tel =
tv->tv_pixmap.tel;
RG_PixmapButtonup(tv, tel,
tv->xms - tel->tel_pixmap.x,
tv->yms - tel->tel_pixmap.y,
button);
}
break;
#if 0
case RG_TILEVIEW_SKETCH_EDIT:
{
RG_TileElement *tel =
tv->tv_sketch.tel;
float vx, vy;
VG_Vcoords2(tel->tel_sketch.sk->vg,
tv->xms - tel->tel_sketch.x,
tv->yms - tel->tel_sketch.y,
&vx, &vy);
RG_SketchButtonup(tv, tel, vx, vy,
button);
}
break;
#endif
case RG_TILEVIEW_ATTRIB_EDIT:
tv->flags &= ~(RG_TILEVIEW_SET_ATTRIBS);
break;
default:
break;
}
}
break;
}
}
static __inline__ void
ClampOffsets(RG_Tileview *tv)
{
int lim;
if (tv->xoffs >
(lim = (WIDGET(tv)->w - RG_TILEVIEW_MIN_W))) {
tv->xoffs = lim;
} else if (tv->xoffs <
(lim = (-tv->scaled->w + RG_TILEVIEW_MIN_W))) {
tv->xoffs = lim;
}
if (tv->yoffs >
(lim = (WIDGET(tv)->h - RG_TILEVIEW_MIN_H))) {
tv->yoffs = lim;
} else if (tv->yoffs <
(lim = (-tv->scaled->h + RG_TILEVIEW_MIN_H))) {
tv->yoffs = lim;
}
}
static void
MoveHandle(RG_Tileview *tv, RG_TileviewCtrl *ctrl, int nhandle,
int x2, int y2)
{
int dx = x2 - tv->xorig;
int dy = y2 - tv->yorig;
int xoffs = 0;
int yoffs = 0;
if (dx == 0 && dy == 0)
return;
switch (ctrl->type) {
case RG_TILEVIEW_RECTANGLE:
switch (nhandle) {
case 0:
RG_TileviewSetInt(ctrl, 0,
RG_TileviewInt(ctrl,0)+dx);
RG_TileviewSetInt(ctrl, 1,
RG_TileviewInt(ctrl,1)+dy);
break;
case 1: /* Top */
{
int ch = RG_TileviewInt(ctrl, 3);
int cy = RG_TileviewInt(ctrl, 1);
int nh = ch-dy;
RG_TileviewSetInt(ctrl, 1, cy+dy);
RG_TileviewSetInt(ctrl, 3, nh>=1 ? nh : 1);
if (dy < 0 || dy > 0)
yoffs = -dy;
}
break;
case 4: /* Left */
{
int cw = RG_TileviewInt(ctrl, 2);
int cx = RG_TileviewInt(ctrl, 0);
int nw = cw-dx;
RG_TileviewSetInt(ctrl, 0, cx+dx);
RG_TileviewSetInt(ctrl, 2, nw>=1 ? nw : 1);
if (dx < 0 || dx > 0)
xoffs = -dx;
}
break;
}
/* FALLTHROUGH */
case RG_TILEVIEW_RDIMENSIONS:
switch (nhandle) {
case 2: /* Bottom */
{
int ch = RG_TileviewInt(ctrl, 3);
int nh = ch+dy;
RG_TileviewSetInt(ctrl, 3, nh>=1 ? nh : 1);
}
break;
case 3: /* Right */
{
int cw = RG_TileviewInt(ctrl, 2);
int nw = cw+dx;
RG_TileviewSetInt(ctrl, 2, nw>=1 ? nw : 1);
}
break;
case 5: /* Bot right */
{
int cw = RG_TileviewInt(ctrl, 2);
int ch = RG_TileviewInt(ctrl, 3);
int nw = cw+dx;
int nh = ch+dy;
RG_TileviewSetInt(ctrl, 2, nw>=1 ? nw : 1);
RG_TileviewSetInt(ctrl, 3, nh>=1 ? nh : 1);
}
break;
}
break;
case RG_TILEVIEW_POINT:
RG_TileviewSetInt(ctrl, 0, RG_TileviewInt(ctrl, 0)+dx);
RG_TileviewSetInt(ctrl, 1, RG_TileviewInt(ctrl, 1)+dy);
break;
#if 0
case RG_TILEVIEW_VERTEX:
RG_TileviewSetDouble(ctrl, 0,
RG_TileviewDouble(ctrl, 0)+VG_VECXF(ctrl->vg,dx));
RG_TileviewSetDouble(ctrl, 1,
RG_TileviewDouble(ctrl, 1)+VG_VECYF(ctrl->vg,dy));
break;
#endif
default:
break;
}
ctrl->xoffs += xoffs;
ctrl->yoffs += yoffs;
if (ctrl->motion != NULL)
AG_PostEvent(NULL, tv, ctrl->motion->name, "%i,%i",
xoffs, yoffs);
}
static void
MouseMotion(AG_Event *event)
{
RG_Tileview *tv = AG_SELF();
int x = AG_INT(1);
int y = AG_INT(2);
int xrel = AG_INT(3);
int yrel = AG_INT(4);
int state = AG_INT(5);
RG_TileviewCtrl *ctrl;
int sx, sy, i;
if (tv->scrolling) {
tv->xoffs += xrel;
tv->yoffs += yrel;
ClampOffsets(tv);
MoveCursor(tv, x - tv->xoffs, y - tv->yoffs);
return;
}
sx = x - tv->xoffs;
sy = y - tv->yoffs;
MoveCursor(tv, sx, sy);
sx /= tv->pxsz;
sy /= tv->pxsz;
TAILQ_FOREACH(ctrl, &tv->ctrls, ctrls) {
for (i = 0; i < ctrl->nhandles; i++) {
RG_TileviewHandle *th = &ctrl->handles[i];
if (th->enable) {
MoveHandle(tv, ctrl, i, sx, sy);
break;
} else {
th->over = CursorOver(th, sx, sy);
}
}
if (i < ctrl->nhandles)
break;
}
if (ctrl == NULL) {
switch (tv->state) {
case RG_TILEVIEW_PIXMAP_EDIT:
{
RG_TileElement *tel = tv->tv_pixmap.tel;
if (OverPixmap(tel, sx, sy)) {
RG_PixmapMotion(tv, tel,
sx - tel->tel_pixmap.x,
sy - tel->tel_pixmap.y,
sx - tv->xorig,
sy - tv->yorig,
state);
}
}
break;
#if 0
case RG_TILEVIEW_SKETCH_EDIT:
{
RG_TileElement *tel = tv->tv_sketch.tel;
float vx, vy, vxrel, vyrel;
VG_Vcoords2(tel->tel_sketch.sk->vg,
sx - tel->tel_sketch.x,
sy - tel->tel_sketch.y,
&vx, &vy);
VG_Vcoords2(tel->tel_sketch.sk->vg,
sx - tv->xorig,
sy - tv->yorig,
&vxrel, &vyrel);
RG_SketchMotion(tv, tel,
vx/RG_TILESZ, vy/RG_TILESZ,
vxrel/RG_TILESZ, vyrel/RG_TILESZ,
state);
}
break;
#endif
case RG_TILEVIEW_ATTRIB_EDIT:
if (tv->flags & RG_TILEVIEW_SET_ATTRIBS) {
ToggleAttrib(tv, sx, sy);
}
break;
default:
break;
}
}
tv->xorig = sx;
tv->yorig = sy;
}
static Uint32
RedrawTimeout(void *obj, Uint32 ival, void *arg)
{
RG_Tileview *tv = obj;
tv->tile->flags |= RG_TILE_DIRTY;
return (ival);
}
RG_TileviewTool *
RG_TileviewRegTool(RG_Tileview *tv, const void *p)
{
const RG_TileviewToolOps *ops = p;
RG_TileviewTool *tvt;
tvt = Malloc(ops->len);
tvt->ops = ops;
tvt->tv = tv;
tvt->flags = ops->flags;
tvt->win = NULL;
if (ops->init != NULL) {
ops->init(tvt);
}
TAILQ_INSERT_TAIL(&tv->tools, tvt, tools);
return (tvt);
}
void
RG_TileviewSetTile(RG_Tileview *tv, RG_Tile *t)
{
if (tv->tile != NULL) {
tv->tile->nrefs--;
}
tv->tile = t;
if (t != NULL) {
t->nrefs++;
RG_TileviewSetZoom(tv, 100, 0);
}
}
static void
Init(void *obj)
{
RG_Tileview *tv = obj;
WIDGET(tv)->flags |= AG_WIDGET_HFILL|AG_WIDGET_VFILL|
AG_WIDGET_FOCUSABLE;
tv->flags = RG_TILEVIEW_NO_EXTENT|RG_TILEVIEW_NO_TILING;
tv->ts = NULL;
tv->tile = NULL;
tv->scaled = NULL;
tv->zoom = 100;
tv->pxsz = 1;
tv->pxlen = 0;
tv->xoffs = 0;
tv->yoffs = 0;
tv->xms = 0;
tv->yms = 0;
tv->scrolling = 0;
tv->state = RG_TILEVIEW_TILE_EDIT;
tv->tv_tile.geo_ctrl = NULL;
tv->tv_tile.orig_ctrl = NULL;
tv->edit_mode = 0;
tv->c.r = 255;
tv->c.g = 255;
tv->c.b = 255;
tv->c.a = 128;
tv->cur_tool = NULL;
tv->tel_box = NULL;
tv->tel_tbar = NULL;
tv->menu = NULL;
tv->menu_item = NULL;
tv->menu_win = NULL;
tv->tCache = AG_TextCacheNew(tv, 64, 16);
TAILQ_INIT(&tv->tools);
TAILQ_INIT(&tv->ctrls);
AG_WidgetMapSurface(tv, NULL);
AG_SetTimeout(&tv->redraw_to, RedrawTimeout, NULL, 0);
AG_SetEvent(tv, "window-keydown", KeyDown, NULL);
AG_SetEvent(tv, "window-keyup", KeyUp, NULL);
AG_SetEvent(tv, "window-mousebuttonup", MouseButtonUp, NULL);
AG_SetEvent(tv, "window-mousebuttondown", MouseButtonDown, NULL);
AG_SetEvent(tv, "window-mousemotion", MouseMotion, NULL);
}
#define INSERT_VALUE(vt,memb, type,arg) do { \
ctrl->valtypes[ctrl->nvals] = (vt); \
ctrl->vals[ctrl->nvals].memb = (type)va_arg(ap, arg); \
ctrl->nvals++; \
} while (/*CONSTCOND*/0)
/* Create a graphically editable geometric control. */
RG_TileviewCtrl *
RG_TileviewAddCtrl(RG_Tileview *tv, enum rg_tileview_ctrl_type type,
const char *fmt, ...)
{
RG_TileviewCtrl *ctrl;
va_list ap;
int i;
ctrl = Malloc(sizeof(RG_TileviewCtrl));
ctrl->type = type;
ctrl->vals = Malloc(sizeof(union rg_tileview_val) * strlen(fmt));
ctrl->valtypes = Malloc(sizeof(enum tileview_val_type) * strlen(fmt));
ctrl->nvals = 0;
#if 0
ctrl->vg = NULL;
ctrl->vge = NULL;
#endif
ctrl->motion = NULL;
ctrl->buttondown = NULL;
ctrl->buttonup = NULL;
ctrl->xoffs = 0;
ctrl->yoffs = 0;
ctrl->c.r = 255;
ctrl->c.g = 255;
ctrl->c.b = 255;
ctrl->a = 255;
ctrl->cIna.r = 128;
ctrl->cIna.g = 128;
ctrl->cIna.b = 128;
ctrl->aIna = 255;
ctrl->cEna.r = 250;
ctrl->cEna.g = 250;
ctrl->cEna.b = 250;
ctrl->aEna = 255;
ctrl->cOver.r = 200;
ctrl->cOver.g = 200;
ctrl->cOver.b = 200;
ctrl->aOver = 255;
ctrl->cHigh.r = 200;
ctrl->cHigh.g = 200;
ctrl->cHigh.b = 200;
ctrl->cLow.r = 60;
ctrl->cLow.g = 60;
ctrl->cLow.b = 60;
TAILQ_INSERT_TAIL(&tv->ctrls, ctrl, ctrls);
if (fmt == NULL)
goto out;
va_start(ap, fmt);
for (; *fmt != '\0'; fmt++) {
switch (*fmt) {
case '*':
switch (fmt[1]) {
case 'i':
INSERT_VALUE(RG_TILEVIEW_INT_PTR, p, int *,
void *);
break;
case 'u':
INSERT_VALUE(RG_TILEVIEW_UINT_PTR, p, Uint *,
void *);
break;
case 'f':
INSERT_VALUE(RG_TILEVIEW_FLOAT_PTR, p, float *,
void *);
break;
case 'd':
INSERT_VALUE(RG_TILEVIEW_DOUBLE_PTR, p,
double *, void *);
break;
default:
AG_FatalError("RG_TileviewAddCtrl: Bad format");
}
fmt++;
break;
case 'i':
INSERT_VALUE(RG_TILEVIEW_INT_VAL, i, int, int);
break;
case 'u':
INSERT_VALUE(RG_TILEVIEW_UINT_VAL, ui, Uint, int);
break;
case 'f':
INSERT_VALUE(RG_TILEVIEW_FLOAT_VAL, f, float, double);
break;
case 'd':
INSERT_VALUE(RG_TILEVIEW_DOUBLE_VAL, d, double, double);
break;
default:
break;
}
}
va_end(ap);
out:
switch (ctrl->type) {
case RG_TILEVIEW_POINT:
ctrl->nhandles = 1;
if (ctrl->nvals < 1) goto missingvals;
break;
#if 0
case RG_TILEVIEW_VERTEX:
ctrl->nhandles = 1;
if (ctrl->nvals < 1) goto missingvals;
break;
#endif
case RG_TILEVIEW_RECTANGLE:
case RG_TILEVIEW_RDIMENSIONS:
ctrl->nhandles = 6;
if (ctrl->nvals < 4) goto missingvals;
break;
case RG_TILEVIEW_CIRCLE:
ctrl->nhandles = 2;
if (ctrl->nvals < 2) goto missingvals;
break;
default:
ctrl->nhandles = 0;
break;
}
ctrl->handles = (ctrl->nhandles > 0) ?
Malloc(sizeof(RG_TileviewHandle)*ctrl->nhandles) : NULL;
for (i = 0; i < ctrl->nhandles; i++) {
RG_TileviewHandle *th = &ctrl->handles[i];
th->x = -1;
th->y = -1;
th->over = 0;
th->enable = 0;
}
return (ctrl);
missingvals:
AG_FatalError("RG_TileviewAddCtrl: Missing values");
return (NULL);
}
static void
FreeCtrl(RG_TileviewCtrl *ctrl)
{
Free(ctrl->valtypes);
Free(ctrl->vals);
Free(ctrl);
}
static void
FreeTool(RG_TileviewTool *t)
{
if (t->ops->destroy != NULL) {
t->ops->destroy(t);
}
Free(t);
}
void
RG_TileviewDelCtrl(RG_Tileview *tv, RG_TileviewCtrl *ctrl)
{
TAILQ_REMOVE(&tv->ctrls, ctrl, ctrls);
FreeCtrl(ctrl);
}
void
RG_TileviewSetAutoRefresh(RG_Tileview *tv, int ena, int rate)
{
if (ena) {
AG_ScheduleTimeout(tv, &tv->redraw_to, rate);
} else {
AG_DelTimeout(tv, &tv->redraw_to);
}
}
void
RG_TileviewSetZoom(RG_Tileview *tv, int z2, int adj_offs)
{
RG_Tile *t = tv->tile;
int pxsz1 = tv->pxsz;
if (z2 < 100 ||
z2 > 1600)
return;
tv->zoom = z2;
tv->pxsz = z2/100;
if (tv->pxsz < 1)
tv->pxsz = 1;
tv->scaled = AG_SurfaceRGBA(
z2>=100 ? t->su->w*tv->pxsz : t->su->w*z2/100,
z2>=100 ? t->su->h*tv->pxsz : t->su->h*z2/100,
t->su->format->BitsPerPixel,
(t->su->flags & (SDL_SRCALPHA|SDL_SRCCOLORKEY|SDL_RLEACCEL)),
t->su->format->Rmask, t->su->format->Gmask,
t->su->format->Bmask, t->su->format->Amask);
if (tv->scaled == NULL) {
AG_FatalError(NULL);
}
tv->scaled->format->alpha = t->su->format->alpha;
tv->scaled->format->colorkey = t->su->format->colorkey;
tv->pxlen = tv->pxsz*tv->scaled->format->BytesPerPixel;
AG_WidgetReplaceSurface(tv, 0, tv->scaled);
if (adj_offs) {
tv->xoffs += (t->su->w*pxsz1 - t->su->w*tv->pxsz)/2;
tv->yoffs += (t->su->h*pxsz1 - t->su->h*tv->pxsz)/2;
ClampOffsets(tv);
}
t->flags |= RG_TILE_DIRTY;
}
static void
SizeRequest(void *obj, AG_SizeReq *r)
{
RG_Tileview *tv = obj;
if (tv->tile != NULL) {
r->w = tv->tile->su->w + RG_TILEVIEW_MIN_W;
r->h = tv->tile->su->h + RG_TILEVIEW_MIN_H;
} else {
r->w = RG_TILEVIEW_MIN_W;
r->h = RG_TILEVIEW_MIN_H;
}
}
static int
SizeAllocate(void *obj, const AG_SizeAlloc *a)
{
RG_Tileview *tv = obj;
int lim;
if (a->w < RG_TILEVIEW_MIN_W ||
a->h < RG_TILEVIEW_MIN_H)
return (-1);
if (tv->xoffs > (lim = (a->w - RG_TILEVIEW_MIN_W)))
tv->xoffs = lim;
if (tv->yoffs > (lim = (a->h - RG_TILEVIEW_MIN_H)))
tv->yoffs = lim;
return (0);
}
/*
* Plot a "scaled pixel" at x,y.
* Must be called from widget draw context.
*/
void
RG_TileviewPixel2i(RG_Tileview *tv, int x, int y)
{
int x1 = RG_TILEVIEW_SCALED_X(tv,x);
int y1 = RG_TILEVIEW_SCALED_Y(tv,y);
int dx, dy;
if (!agView->opengl) {
for (dy = 0; dy < tv->pxsz; dy++) {
for (dx = 0; dx < tv->pxsz; dx++) {
int xView = x1+dx;
int yView = y1+dy;
if (xView < WIDGET(tv)->rView.x1 ||
yView < WIDGET(tv)->rView.y1 ||
xView > WIDGET(tv)->rView.x2 ||
yView > WIDGET(tv)->rView.y2)
continue;
if (tv->c.a < 255) {
AG_BLEND_RGBA2_CLIPPED(agView->v,
xView, yView,
tv->c.r, tv->c.g, tv->c.b, tv->c.a,
AG_ALPHA_OVERLAY);
} else {
AG_VIEW_PUT_PIXEL2_CLIPPED(xView, yView,
tv->c.pc);
}
}
}
} else {
#ifdef HAVE_OPENGL
int x2 = x1 + tv->pxsz;
int y2 = y1 + tv->pxsz;
GLboolean svBlendBit;
GLint svBlendSrc, svBlendDst;
if (x1 > WIDGET(tv)->rView.x2) return;
if (y1 > WIDGET(tv)->rView.y2) return;
if (x1 < WIDGET(tv)->rView.x1) x1 = WIDGET(tv)->rView.x1;
if (y1 < WIDGET(tv)->rView.y1) y1 = WIDGET(tv)->rView.y1;
if (x1 >= x2 || y1 >= y2) return;
if (x2 > WIDGET(tv)->rView.x2) x2 = WIDGET(tv)->rView.x2;
if (y2 > WIDGET(tv)->rView.y2) y2 = WIDGET(tv)->rView.y2;
if (tv->c.a < 255) {
glGetBooleanv(GL_BLEND, &svBlendBit);
glGetIntegerv(GL_BLEND_SRC, &svBlendSrc);
glGetIntegerv(GL_BLEND_DST, &svBlendDst);
glEnable(GL_BLEND);
glBlendFunc(GL_DST_ALPHA, GL_ZERO);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
}
glBegin(GL_QUADS);
glColor4ub(tv->c.r, tv->c.g, tv->c.b, tv->c.a);
glVertex2i(x1, y1);
glVertex2i(x2, y1);
glVertex2i(x2, y2);
glVertex2i(x1, y2);
glEnd();
if (tv->c.a < 255) {
if (!svBlendBit) {
glDisable(GL_BLEND);
}
glBlendFunc(GL_SRC_ALPHA, svBlendSrc);
glBlendFunc(GL_DST_ALPHA, svBlendDst);
}
#endif /* HAVE_OPENGL */
}
}
static void
DrawStatusText(RG_Tileview *tv, const char *label)
{
int su, wSu, hSu;
AG_PushTextState();
AG_TextColor(TILEVIEW_TEXT_COLOR);
su = AG_TextCacheGet(tv->tCache, label);
wSu = WSURFACE(tv,su)->w;
hSu = WSURFACE(tv,su)->h;
AG_PopTextState();
AG_DrawRectFilled(tv,
AG_RECT((wSu >= WIDTH(tv)) ? 0 : (WIDTH(tv)-wSu-2),
HEIGHT(tv)-hSu-2, WIDTH(tv), HEIGHT(tv)),
AG_COLOR(TILEVIEW_TEXTBG_COLOR));
AG_WidgetBlitSurface(tv, su, WIDTH(tv)-wSu-1, HEIGHT(tv)-hSu-1);
}
void
RG_TileviewColor3i(RG_Tileview *tv, Uint8 r, Uint8 g, Uint8 b)
{
tv->c.r = r;
tv->c.g = g;
tv->c.b = b;
tv->c.pc = AG_MapRGB(agVideoFmt, r,g,b);
}
void
RG_TileviewColor4i(RG_Tileview *tv, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
{
tv->c.r = r;
tv->c.g = g;
tv->c.b = b;
tv->c.a = a;
tv->c.pc = AG_MapRGBA(agVideoFmt, r,g,b,a);
}
void
RG_TileviewSDLColor(RG_Tileview *tv, AG_Color *c, Uint8 a)
{
tv->c.r = c->r;
tv->c.g = c->g;
tv->c.b = c->b;
tv->c.a = a;
tv->c.pc = AG_MapRGBA(agVideoFmt, c->r, c->g, c->b, a);
}
void
RG_TileviewAlpha(RG_Tileview *tv, Uint8 a)
{
tv->c.a = a;
tv->c.pc = AG_MapRGBA(agVideoFmt, tv->c.r, tv->c.g, tv->c.b, a);
}
/* Must be called from widget draw context. */
void
RG_TileviewRect2(RG_Tileview *tv, int x, int y, int w, int h)
{
if (!agView->opengl) {
int xi, yi;
int x2 = x+w;
int y2 = y+h;
for (yi = y; yi < y2; yi++)
for (xi = x; xi < x2; xi++)
RG_TileviewPixel2i(tv, xi, yi);
} else {
#ifdef HAVE_OPENGL
int x1 = RG_TILEVIEW_SCALED_X(tv,x);
int y1 = RG_TILEVIEW_SCALED_Y(tv,y);
int x2 = x1 + w*tv->pxsz;
int y2 = y1 + h*tv->pxsz;
GLboolean svBlendBit;
GLint svBlendSrc, svBlendDst;
if (x1 > WIDGET(tv)->rView.x2) return;
if (y1 > WIDGET(tv)->rView.y2) return;
if (x1 < WIDGET(tv)->rView.x1) x1 = WIDGET(tv)->rView.x1;
if (y1 < WIDGET(tv)->rView.y1) y1 = WIDGET(tv)->rView.y1;
if (x1 >= x2 || y1 >= y2) return;
if (x2 > WIDGET(tv)->rView.x2) x2 = WIDGET(tv)->rView.x2;
if (y2 > WIDGET(tv)->rView.y2) y2 = WIDGET(tv)->rView.y2;
if (tv->c.a < 255) {
glGetBooleanv(GL_BLEND, &svBlendBit);
glGetIntegerv(GL_BLEND_SRC, &svBlendSrc);
glGetIntegerv(GL_BLEND_DST, &svBlendDst);
glEnable(GL_BLEND);
glBlendFunc(GL_DST_ALPHA, GL_ZERO);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
}
glBegin(GL_QUADS);
glColor4ub(tv->c.r, tv->c.g, tv->c.b, tv->c.a);
glVertex2i(x1, y1);
glVertex2i(x2, y1);
glVertex2i(x2, y2);
glVertex2i(x1, y2);
glEnd();
if (tv->c.a < 255) {
if (!svBlendBit) {
glDisable(GL_BLEND);
}
glBlendFunc(GL_SRC_ALPHA, svBlendSrc);
glBlendFunc(GL_DST_ALPHA, svBlendDst);
}
#endif /* HAVE_OPENGL */
}
}
/* Must be called from widget draw context. */
void
RG_TileviewCircle2o(RG_Tileview *tv, int x0, int y0, int r)
{
int v = 2*r - 1;
int e = 0;
int u = 1;
int x = 0;
int y = r;
while (x < y) {
RG_TileviewPixel2i(tv, x0+x, y0+y);
RG_TileviewPixel2i(tv, x0+x, y0-y);
RG_TileviewPixel2i(tv, x0-x, y0+y);
RG_TileviewPixel2i(tv, x0-x, y0-y);
e += u;
u += 2;
if (v < 2*e) {
y--;
e -= v;
v -= 2;
}
x++;
RG_TileviewPixel2i(tv, x0+y, y0+x);
RG_TileviewPixel2i(tv, x0+y, y0-x);
RG_TileviewPixel2i(tv, x0-y, y0+x);
RG_TileviewPixel2i(tv, x0-y, y0-x);
}
RG_TileviewPixel2i(tv, x0-r, y0);
RG_TileviewPixel2i(tv, x0+r, y0);
}
/* Must be called from widget draw context. */
void
RG_TileviewVLine(RG_Tileview *tv, int x, int y1, int y2)
{
int y;
/* TODO opengl */
for (y = y1; y < y2; y++)
RG_TileviewPixel2i(tv, x, y);
}
/* Must be called from widget draw context. */
void
RG_TileviewHLine(RG_Tileview *tv, int x1, int x2, int y)
{
int x;
/* TODO opengl */
for (x = x1; x < x2; x++)
RG_TileviewPixel2i(tv, x, y);
}
/* Must be called from widget draw context. */
void
RG_TileviewRect2o(RG_Tileview *tv, int x, int y, int w, int h)
{
if (!agView->opengl) {
int xi, yi;
for (yi = y+1; yi < y+h; yi++) {
RG_TileviewPixel2i(tv, x, yi);
RG_TileviewPixel2i(tv, x+w, yi);
}
for (xi = x; xi < x+w+1; xi++) {
RG_TileviewPixel2i(tv, xi, y);
RG_TileviewPixel2i(tv, xi, y+h);
}
} else {
#ifdef HAVE_OPENGL
int x1 = RG_TILEVIEW_SCALED_X(tv,x);
int y1 = RG_TILEVIEW_SCALED_Y(tv,y);
int x2 = x1 + w*tv->pxsz;
int y2 = y1 + h*tv->pxsz;
GLfloat saved_width;
glGetFloatv(GL_LINE_WIDTH, &saved_width);
glLineWidth(tv->pxsz);
glBegin(GL_LINES);
if (tv->c.a < 255) {
glColor4ub(tv->c.r, tv->c.g, tv->c.b, tv->c.a);
} else {
glColor3ub(tv->c.r, tv->c.g, tv->c.b);
}
if (y1 > WIDGET(tv)->rView.y1 &&
y1 < WIDGET(tv)->rView.y2 &&
x1 < WIDGET(tv)->rView.x2 &&
x2 > WIDGET(tv)->rView.x1) {
glVertex2i(MAX(x1, WIDGET(tv)->rView.x1), y1);
glVertex2i(MIN(x2, WIDGET(tv)->rView.x2), y1);
}
if (x2 < WIDGET(tv)->rView.x2 && x2 > WIDGET(tv)->rView.x1 &&
y1 < WIDGET(tv)->rView.y2 && y2 > WIDGET(tv)->rView.y1) {
glVertex2i(x2, MAX(y1, WIDGET(tv)->rView.y1));
glVertex2i(x2, MIN(y2, WIDGET(tv)->rView.y2));
}
if (y2 > WIDGET(tv)->rView.y1 && y2 < WIDGET(tv)->rView.y2 &&
x1 < WIDGET(tv)->rView.x2 && x2 > WIDGET(tv)->rView.x1) {
glVertex2i(MIN(x2, WIDGET(tv)->rView.x2), y2);
glVertex2i(MAX(x1, WIDGET(tv)->rView.x1), y2);
}
if (x1 > WIDGET(tv)->rView.x1 && x1 < WIDGET(tv)->rView.x2 &&
y1 < WIDGET(tv)->rView.y2 && y2 > WIDGET(tv)->rView.y1) {
glVertex2i(x1, MIN(y2, WIDGET(tv)->rView.y2));
glVertex2i(x1, MAX(y1, WIDGET(tv)->rView.y1));
}
glEnd();
glLineWidth(saved_width);
#endif /* HAVE_OPENGL */
}
}
/* Must be called from widget draw context. */
static void
DrawHandle(RG_Tileview *tv, RG_TileviewCtrl *ctrl, RG_TileviewHandle *th)
{
int x = th->x;
int y = th->y;
if (th->over && !th->enable) {
RG_TileviewSDLColor(tv, &ctrl->cOver, ctrl->aOver);
} else {
RG_TileviewSDLColor(tv,
th->enable ? &ctrl->cEna : &ctrl->cIna,
th->enable ? ctrl->aEna : ctrl->aIna);
}
/* Draw the inner circle. */
RG_TileviewRect2(tv, x-1, y-1, 3, 3);
/* Draw the central point. */
RG_TileviewColor4i(tv, 255, 255, 255,
th->enable ? ctrl->aEna : ctrl->aIna);
RG_TileviewPixel2i(tv, x, y);
/* Draw the highlights. */
RG_TileviewSDLColor(tv,
th->enable ? &ctrl->cLow : &ctrl->cHigh,
255);
RG_TileviewPixel2i(tv, x-2, y+1); /* Highlight left */
RG_TileviewPixel2i(tv, x-2, y);
RG_TileviewPixel2i(tv, x-2, y-1);
RG_TileviewPixel2i(tv, x-1, y-2); /* Highlight top */
RG_TileviewPixel2i(tv, x, y-2);
RG_TileviewPixel2i(tv, x+1, y-2);
RG_TileviewSDLColor(tv,
th->enable ? &ctrl->cHigh : &ctrl->cLow,
255);
RG_TileviewPixel2i(tv, x-1, y+2); /* Occlusion bottom */
RG_TileviewPixel2i(tv, x, y+2);
RG_TileviewPixel2i(tv, x+1, y+2);
RG_TileviewPixel2i(tv, x+2, y-1);
RG_TileviewPixel2i(tv, x+2, y);
RG_TileviewPixel2i(tv, x+2, y+1);
}
/* Must be called from widget draw context. */
static void
DrawControl(RG_Tileview *tv, RG_TileviewCtrl *ctrl)
{
int i;
if (tv->flags & RG_TILEVIEW_HIDE_CONTROLS)
return;
RG_TileviewSDLColor(tv, &ctrl->c, ctrl->a);
switch (ctrl->type) {
case RG_TILEVIEW_RECTANGLE:
case RG_TILEVIEW_RDIMENSIONS:
{
int x = RG_TileviewInt(ctrl, 0);
int y = RG_TileviewInt(ctrl, 1);
Uint w = RG_TileviewUint(ctrl, 2);
Uint h = RG_TileviewUint(ctrl, 3);
RG_TileviewRect2o(tv, x-1, y-1, w+1, h+1);
ctrl->handles[0].x = x - 3; /* Position */
ctrl->handles[0].y = y - 3;
ctrl->handles[1].x = x + w/2; /* Top */
ctrl->handles[1].y = y - 3;
ctrl->handles[2].x = x + w/2; /* Bottom */
ctrl->handles[2].y = y + h + 2;
ctrl->handles[3].x = x + w + 2; /* Right */
ctrl->handles[3].y = y + h/2;
ctrl->handles[4].x = x - 3; /* Left */
ctrl->handles[4].y = y + h/2;
ctrl->handles[5].x = x + w + 2; /* Bot right */
ctrl->handles[5].y = y + h + 2;
}
break;
case RG_TILEVIEW_POINT:
{
int x = RG_TileviewInt(ctrl, 0);
int y = RG_TileviewInt(ctrl, 1);
RG_TileviewCircle2o(tv, x, y, 2);
ctrl->handles[0].x = x;
ctrl->handles[0].y = y;
}
break;
case RG_TILEVIEW_VERTEX:
#if 0
{
float x = RG_TileviewDouble(ctrl, 0);
float y = RG_TileviewDouble(ctrl, 1);
ctrl->handles[0].x = VG_RASXF(ctrl->vg,x);
ctrl->handles[0].y = VG_RASYF(ctrl->vg,y);
RG_TileviewCircle2o(tv,
ctrl->handles[0].x,
ctrl->handles[0].y,
2);
}
#endif
break;
case RG_TILEVIEW_CIRCLE:
{
int x = RG_TileviewInt(ctrl, 0);
int y = RG_TileviewInt(ctrl, 1);
Uint r = RG_TileviewUint(ctrl, 2);
RG_TileviewCircle2o(tv, x, y, r);
RG_TileviewPixel2i(tv, x, y);
ctrl->handles[0].x = x;
ctrl->handles[0].y = y;
ctrl->handles[1].x = x+r;
ctrl->handles[1].y = y;
}
break;
}
for (i = 0; i < ctrl->nhandles; i++)
DrawHandle(tv, ctrl, &ctrl->handles[i]);
}
static void
RG_AttrColor(Uint flag, int state, Uint8 *c)
{
switch (flag) {
case RG_TILE_BLOCK:
if (state) {
c[0] = 255;
c[1] = 0;
c[2] = 0;
c[3] = 64;
} else {
c[0] = 0;
c[1] = 255;
c[2] = 0;
c[3] = 32;
}
break;
case RG_TILE_CLIMBABLE:
if (state) {
c[0] = 255;
c[1] = 255;
c[2] = 0;
c[3] = 64;
} else {
c[0] = 255;
c[1] = 0;
c[2] = 0;
c[3] = 32;
}
break;
case RG_TILE_SLIPPERY:
if (state) {
c[0] = 0;
c[1] = 0;
c[2] = 255;
c[3] = 64;
} else {
c[0] = 0;
c[1] = 0;
c[2] = 0;
c[3] = 0;
}
break;
case RG_TILE_JUMPABLE:
if (state) {
c[0] = 255;
c[1] = 0;
c[2] = 255;
c[3] = 64;
} else {
c[0] = 0;
c[1] = 0;
c[2] = 0;
c[3] = 0;
}
break;
}
}
static void
Draw(void *obj)
{
RG_Tileview *tv = obj;
RG_Tile *t = tv->tile;
char status[64];
RG_TileviewCtrl *ctrl;
AG_Rect rsrc, rdst;
int x, y, n;
if (t == NULL)
return;
AG_PushClipRect(tv, AG_RECT(0, 0, WIDTH(tv), HEIGHT(tv)));
#if 0
if (tv->state == RG_TILEVIEW_SKETCH_EDIT) {
VG_Rasterize(rv->tv_sketch.sk->vg);
t->flags |= RG_TILE_DIRTY;
}
#endif
if (tv->flags & RG_TILEVIEW_READONLY) {
RG_TileGenerate(t);
if (AG_ScaleSurface(t->su, tv->scaled->w, tv->scaled->h,
&tv->scaled) == -1) {
AG_FatalError(NULL);
}
AG_WidgetUpdateSurface(tv, 0);
} else {
if (t->flags & RG_TILE_DIRTY) {
t->flags &= ~RG_TILE_DIRTY;
RG_TileGenerate(t);
if (AG_ScaleSurface(t->su, tv->scaled->w, tv->scaled->h,
&tv->scaled) == -1) {
AG_FatalError(NULL);
}
AG_WidgetUpdateSurface(tv, 0);
}
}
if ((tv->flags & RG_TILEVIEW_NO_TILING) == 0) {
AG_DrawTiling(tv,
AG_RECT(0, 0, WIDTH(tv), HEIGHT(tv)), 9, 0,
AG_COLOR(TILEVIEW_TILE1_COLOR),
AG_COLOR(TILEVIEW_TILE2_COLOR));
}
rsrc.x = 0;
rsrc.y = 0;
rsrc.w = tv->scaled->w;
rsrc.h = tv->scaled->h;
rdst.x = tv->xoffs;
rdst.y = tv->yoffs;
if (!agView->opengl) {
if (tv->xoffs > 0 &&
tv->xoffs + tv->scaled->w > WIDGET(tv)->w) {
rsrc.w = WIDGET(tv)->w - tv->xoffs;
} else if (tv->xoffs < 0 && -tv->xoffs < tv->scaled->w) {
rdst.x = 0;
rsrc.x = -tv->xoffs;
rsrc.w = tv->scaled->w - (-tv->xoffs);
if (rsrc.w > WIDGET(tv)->w)
rsrc.w = WIDGET(tv)->w;
}
if (tv->yoffs > 0 &&
tv->yoffs + tv->scaled->h > WIDGET(tv)->h) {
rsrc.h = WIDGET(tv)->h - tv->yoffs;
} else if (tv->yoffs < 0 && -tv->yoffs < tv->scaled->h) {
rdst.y = 0;
rsrc.y = -tv->yoffs;
rsrc.h = tv->scaled->h - (-tv->yoffs);
if (rsrc.h > WIDGET(tv)->h)
rsrc.h = WIDGET(tv)->h;
}
}
AG_WidgetBlitFrom(tv, tv, 0, &rsrc, rdst.x, rdst.y);
#ifdef HAVE_OPENGL
if (agView->opengl)
glEnable(GL_BLEND);
#endif
RG_TileviewColor4i(tv, 255, 255, 255, 32);
if ((tv->flags & RG_TILEVIEW_NO_GRID) == 0) {
for (y = 0; y < t->su->h; y += RG_TILESZ)
RG_TileviewHLine(tv, 0, t->su->w, y);
for (x = 0; x < t->su->w; x += RG_TILESZ)
RG_TileviewVLine(tv, x, 0, t->su->h);
}
RG_TileviewColor4i(tv, 255, 255, 255, 128);
if (tv->state != RG_TILEVIEW_TILE_EDIT &&
(tv->flags & RG_TILEVIEW_NO_EXTENT) == 0) {
RG_TileviewRect2o(tv, 0, 0, t->su->w-1, t->su->h-1);
}
RG_TileviewPixel2i(tv, tv->xms, tv->yms);
/* XXX inefficient */
switch (tv->state) {
case RG_TILEVIEW_ATTRIB_EDIT:
{
int tsz = RG_TILESZ*tv->pxsz;
int tw = t->su->w*tv->pxsz;
int th = t->su->h*tv->pxsz;
int nx, ny;
n = 0;
for (y = 0, ny = 0;
y < th;
y += tsz, ny++) {
for (x = 0, nx = 0;
x < tw;
x += tsz, nx++) {
Uint8 c[4];
int w = tsz;
int h = tsz;
int d;
RG_AttrColor(tv->edit_attr,
(RG_TILE_ATTR2(t,nx,ny) &
tv->edit_attr), c);
if ((d = (tsz - (tw - x))) > 0) {
w -= d;
}
if ((d = (tsz - (th - y))) > 0) {
h -= d;
}
AG_DrawRectBlended(tv,
AG_RECT(tv->xoffs+x,
tv->yoffs+y,
w, h),
c, AG_ALPHA_OVERLAY);
n++;
}
}
Strlcpy(status, _("Editing node attributes"),
sizeof(status));
DrawStatusText(tv, status);
}
break;
case RG_TILEVIEW_LAYERS_EDIT:
{
int tsz = RG_TILESZ*tv->pxsz;
int tw = t->su->w*tv->pxsz;
int th = t->su->h*tv->pxsz;
char text[16];
int nx, ny;
AG_PushTextState();
n = 0;
for (y = 0, ny = 0;
y < th;
y += tsz, ny++) {
for (x = 0, nx = 0;
x < tw;
x += tsz, nx++) {
AG_Surface *tsu;
int l = RG_TILE_LAYER2(t,nx,ny);
Uint8 c[4] = { 255, 255, 255, 128 };
Snprintf(text, sizeof(text), "%s%d",
(l > 0) ? "+" : "", l);
AG_TextColorRGB(0, 0, 0);
tsu = AG_TextRender(text);
AG_DrawRectBlended(tv,
AG_RECT(tv->xoffs+x,
tv->yoffs+y,
tsu->w, tsu->h),
c, AG_ALPHA_OVERLAY);
AG_WidgetBlit(tv, tsu,
tv->xoffs+x,
tv->yoffs+y);
AG_SurfaceFree(tsu);
}
}
Strlcpy(status, _("Editing node layers"),
sizeof(status));
DrawStatusText(tv, status);
AG_PopTextState();
}
break;
case RG_TILEVIEW_FEATURE_EDIT:
Strlcpy(status, _("Editing feature: "), sizeof(status));
Strlcat(status, tv->tv_feature.ft->name, sizeof(status));
DrawStatusText(tv, status);
break;
#if 0
case RG_TILEVIEW_SKETCH_EDIT:
Strlcpy(status, _("Editing sketch: "), sizeof(status));
Strlcat(status, tv->tv_sketch.sk->name, sizeof(status));
DrawStatusText(tv, status);
break;
#endif
case RG_TILEVIEW_PIXMAP_EDIT:
{
extern const char *pixmap_state_names[];
Strlcpy(status, _("Editing pixmap: "), sizeof(status));
Strlcat(status, tv->tv_pixmap.px->name, sizeof(status));
Strlcat(status, pixmap_state_names[tv->tv_pixmap.state],
sizeof(status));
DrawStatusText(tv, status);
}
break;
default:
break;
}
TAILQ_FOREACH(ctrl, &tv->ctrls, ctrls) {
DrawControl(tv, ctrl);
}
#ifdef HAVE_OPENGL
if (agView->opengl)
glDisable(GL_BLEND);
#endif
AG_PopClipRect();
}
static void
Destroy(void *p)
{
RG_Tileview *tv = p;
RG_TileviewCtrl *ctrl, *nctrl;
RG_TileviewTool *tool, *ntool;
if (tv->tile != NULL) {
tv->tile->nrefs--;
}
for (ctrl = TAILQ_FIRST(&tv->ctrls);
ctrl != TAILQ_END(&tv->ctrls);
ctrl = nctrl) {
nctrl = TAILQ_NEXT(ctrl, ctrls);
FreeCtrl(ctrl);
}
for (tool = TAILQ_FIRST(&tv->tools);
tool != TAILQ_END(&tv->tools);
tool = ntool) {
ntool = TAILQ_NEXT(tool, tools);
FreeTool(tool);
}
if (tv->tCache != NULL)
AG_TextCacheDestroy(tv->tCache);
}
static void
CloseToolWindow(AG_Event *event)
{
RG_Tileview *tv = AG_PTR(1);
AG_ViewDetach(tv->cur_tool->win);
tv->cur_tool->win = NULL;
}
void
RG_TileviewSelectTool(RG_Tileview *tv, RG_TileviewTool *tvt)
{
AG_Window *pwin = AG_WidgetParentWindow(tv);
#ifdef AG_DEBUG
if (pwin == NULL)
AG_FatalError("RG_TileviewSelectTool: %s has no parent window",
OBJECT(tv)->name);
#endif
if (tv->cur_tool != NULL && tv->cur_tool->win != NULL) {
AG_ViewDetach(tv->cur_tool->win);
tv->cur_tool->win = NULL;
}
if (tvt->ops->selected != NULL) {
tvt->ops->selected(tvt);
}
if (tvt->ops->edit != NULL) {
tvt->win = tvt->ops->edit(tvt);
AG_WindowSetCaption(tvt->win, _(tvt->ops->name));
AG_WindowSetPosition(tvt->win, AG_WINDOW_LOWER_LEFT, 0);
AG_WindowAttach(pwin, tvt->win);
AG_WindowShow(tvt->win);
AG_SetEvent(tvt->win, "window-close", CloseToolWindow,
"%p", tv);
}
tv->cur_tool = tvt;
}
void
RG_TileviewUnselectTool(RG_Tileview *tv)
{
if (tv->cur_tool != NULL) {
if (tv->cur_tool->win != NULL) {
AG_ViewDetach(tv->cur_tool->win);
tv->cur_tool->win = NULL;
}
if (tv->cur_tool->ops->unselected != NULL)
tv->cur_tool->ops->unselected(tv->cur_tool);
}
tv->cur_tool = NULL;
}
void
RG_TileviewGenericMenu(RG_Tileview *tv, AG_MenuItem *mi)
{
AG_MenuIntFlags(mi, _("Show tile grid"), rgIconControls.s,
&tv->flags, RG_TILEVIEW_NO_GRID, 1);
AG_MenuIntFlags(mi, _("Show tile extent"), rgIconControls.s,
&tv->flags, RG_TILEVIEW_NO_EXTENT, 1);
AG_MenuIntFlags(mi, _("Show controls"), rgIconControls.s,
&tv->flags, RG_TILEVIEW_HIDE_CONTROLS, 1);
AG_MenuIntFlags(mi, _("Show background"), rgIconTiling.s,
&tv->flags, RG_TILEVIEW_NO_TILING, 1);
}
AG_WidgetClass rgTileviewClass = {
{
"Agar(Widget):RG(Tileview)",
sizeof(RG_Tileview),
{ 0,0 },
Init,
NULL, /* free */
Destroy,
NULL, /* load */
NULL, /* save */
NULL /* edit */
},
Draw,
SizeRequest,
SizeAllocate
};