/* * Copyright (c) 2005-2019 Julien Nadeau Carriere * 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. */ /* * Tile display and editor widget. */ #include #include #include #include #include #include #include #include #include #include #include #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); } /* Timeout for keyboard zoom control. */ static Uint32 ZoomTimeout(AG_Timer *_Nonnull to, AG_Event *_Nonnull event) { RG_Tileview *tv = RG_TILEVIEW_SELF(); const int dir = AG_INT(1); if (dir == +1) { /* Zoom in */ if (tv->zoom > 1600) { return (0); } RG_TileviewSetZoom(tv, tv->zoom+20, 1); } else { /* Zoom out */ if (tv->zoom < 100) { return (0); } RG_TileviewSetZoom(tv, tv->zoom-20, 1); } return (10); } static __inline__ void MoveCursor(RG_Tileview *_Nonnull 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; AG_Redraw(tv); } static __inline__ int CursorOver(RG_TileviewHandle *_Nonnull 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(void *obj, AG_KeySym ks, AG_KeyMod kmod, AG_Char ch) { RG_Tileview *tv = obj; switch (tv->state) { case RG_TILEVIEW_PIXMAP_EDIT: RG_PixmapKeydown(tv, ks); break; #if 0 case RG_TILEVIEW_SKETCH_EDIT: RG_SketchKeyDown(tv, tv->sketch.tel, ks, kmod); break; #endif default: break; } switch (ks) { case AG_KEY_Z: if (kmod & AG_KEYMOD_CTRL) { switch (tv->state) { case RG_TILEVIEW_PIXMAP_EDIT: RG_PixmapUndo(tv, tv->pixmap.tel); break; #if 0 case RG_TILEVIEW_SKETCH_EDIT: RG_SketchUndo(tv, tv->sketch.tel); break; #endif default: break; } } break; case AG_KEY_R: if (kmod & AG_KEYMOD_CTRL) { switch (tv->state) { case RG_TILEVIEW_PIXMAP_EDIT: RG_PixmapRedo(tv, tv->pixmap.tel); break; #if 0 case RG_TILEVIEW_SKETCH_EDIT: RG_SketchRedo(tv, tv->sketch.tel); break; #endif default: break; } } else { tv->tile->flags |= RG_TILE_DIRTY; } break; case AG_KEY_EQUALS: AG_AddTimer(tv, &tv->zoomTo, 1, ZoomTimeout, "%i", +1); break; case AG_KEY_MINUS: AG_AddTimer(tv, &tv->zoomTo, 1, ZoomTimeout, "%i", -1); break; case AG_KEY_0: case AG_KEY_1: RG_TileviewSetZoom(tv, 100, 1); break; } } static void KeyUp(void *obj, AG_KeySym ks, AG_KeyMod kmod, AG_Char ch) { RG_Tileview *tv = obj; switch (tv->state) { case RG_TILEVIEW_PIXMAP_EDIT: RG_PixmapKeyup(tv); break; #if 0 case RG_TILEVIEW_SKETCH_EDIT: RG_SketchKeyUp(tv, tv->sketch.tel, ks, kmod); break; #endif default: break; } switch (ks) { case AG_KEY_EQUALS: case AG_KEY_MINUS: AG_DelTimer(tv, &tv->zoomTo); break; } } static __inline__ int OverPixmap(RG_TileElement *_Nonnull 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 *_Nonnull 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 *_Nonnull 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->attrs.nx == nx && 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->attrs.nx = nx; tv->attrs.ny = ny; t->flags |= RG_TILE_DIRTY; AG_Redraw(tv); } static void IncrementLayer(RG_Tileview *_Nonnull 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; AG_Redraw(tv); } static void MouseButtonDown(void *obj, AG_MouseButton button, int x, int y) { RG_Tileview *tv = obj; RG_TileviewCtrl *ctrl; RG_TileElement *tel = tv->pixmap.tel; const int sx = (x - tv->xoffs) / tv->pxsz; const int sy = (y - tv->yoffs) / tv->pxsz; int i; if (!AG_WidgetIsFocused(tv)) AG_WidgetFocus(tv); switch (button) { case AG_MOUSE_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); return; case AG_MOUSE_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); break; default: break; } if (button == AG_MOUSE_LEFT && !(tv->flags & RG_TILEVIEW_HIDE_CONTROLS)) { 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_PostEventByPtr(tv, ctrl->buttondown, "%i,%i", sx, sy); } AG_Redraw(tv); break; } } if (i < ctrl->nHandles) return; } } switch (tv->state) { case RG_TILEVIEW_PIXMAP_EDIT: if (OverPixmap(tel, sx, sy)) { tv->pixmap.xorig = sx - tel->tel_pixmap.x; tv->pixmap.yorig = sy - tel->tel_pixmap.y; RG_PixmapButtondown(tv, tel, tv->pixmap.xorig, tv->pixmap.yorig, x, y, button); return; } else { if (button == AG_MOUSE_RIGHT) tv->scrolling = 1; } break; #if 0 case RG_TILEVIEW_SKETCH_EDIT: if (button == AG_MOUSE_RIGHT && (tv->flags & RG_TILEVIEW_NO_SCROLLING) == 0) { tv->scrolling = 1; } if (button == AG_MOUSE_MIDDLE || button == AG_MOUSE_RIGHT || (button == AG_MOUSE_LEFT && OverSketch(tel, sx, sy))) { RG_Sketch *sk = 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 == AG_MOUSE_MIDDLE) { if (tv->feature.ft->ops->menu != NULL) { RG_FeatureOpenMenu(tv, x+5, y+5); } } else if (button == AG_MOUSE_RIGHT) { tv->scrolling = 1; } break; case RG_TILEVIEW_TILE_EDIT: if (button == AG_MOUSE_RIGHT) { tv->scrolling = 1; } break; case RG_TILEVIEW_ATTRIB_EDIT: if (button == AG_MOUSE_RIGHT) { tv->scrolling = 1; } else if (button == AG_MOUSE_LEFT) { tv->flags |= RG_TILEVIEW_SET_ATTRIBS; tv->attrs.nx = -1; tv->attrs.ny = -1; ToggleAttrib(tv, sx, sy); } break; case RG_TILEVIEW_LAYERS_EDIT: if (button == AG_MOUSE_LEFT) { IncrementLayer(tv, sx, sy, +1); } else if (button == AG_MOUSE_RIGHT) { IncrementLayer(tv, sx, sy, -1); tv->scrolling = 1; } break; } if (button == AG_MOUSE_MIDDLE && tv->state == RG_TILEVIEW_TILE_EDIT) { RG_TileOpenMenu(tv, x+5, y+5); } } static void MouseButtonUp(void *obj, AG_MouseButton button, int x, int y) { RG_Tileview *tv = obj; RG_TileviewCtrl *ctrl; int i; if (button == AG_MOUSE_RIGHT || button == AG_MOUSE_MIDDLE) tv->scrolling = 0; switch (button) { case AG_MOUSE_RIGHT: case AG_MOUSE_MIDDLE: tv->scrolling = 0; break; case AG_MOUSE_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_PostEventByPtr(tv, ctrl->buttonup, NULL); } break; } } if (i < ctrl->nHandles) break; } if (ctrl == NULL) { switch (tv->state) { case RG_TILEVIEW_PIXMAP_EDIT: { RG_TileElement *tel = tv->pixmap.tel; RG_PixmapButtonup(tv, tel, tv->xms - tel->tel_pixmap.x, tv->yms - tel->tel_pixmap.y, x, y, button); } break; #if 0 case RG_TILEVIEW_SKETCH_EDIT: { RG_TileElement *tel = 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 *_Nonnull tv) { int lim; if (tv->xoffs > (lim = (WIDTH(tv) - RG_TILEVIEW_MIN_W))) { tv->xoffs = lim; } else if (tv->xoffs < (lim = (-((int)tv->scaled->w) + RG_TILEVIEW_MIN_W))) { tv->xoffs = lim; } if (tv->yoffs > (lim = (HEIGHT(tv) - RG_TILEVIEW_MIN_H))) { tv->yoffs = lim; } else if (tv->yoffs < (lim = (-((int)tv->scaled->h) + RG_TILEVIEW_MIN_H))) { tv->yoffs = lim; } } static void MoveHandle(RG_Tileview *_Nonnull tv, RG_TileviewCtrl *_Nonnull ctrl, int which, 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 (which) { 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 (which) { 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_PostEventByPtr(tv, ctrl->motion, "%i,%i", xoffs, yoffs); AG_Redraw(tv); } static void MouseMotion(void *obj, int x, int y, int dx, int dy) { RG_Tileview *tv = obj; RG_TileviewCtrl *ctrl; int sx, sy, i; if (tv->scrolling) { tv->xoffs += dx; tv->yoffs += dy; 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->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); } } break; #if 0 case RG_TILEVIEW_SKETCH_EDIT: { RG_TileElement *tel = 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); } 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; } 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); } t->flags |= RG_TILE_DIRTY; AG_Redraw(tv); } static void Init(void *_Nonnull obj) { RG_Tileview *tv = obj; WIDGET(tv)->flags |= AG_WIDGET_HFILL | AG_WIDGET_VFILL | AG_WIDGET_FOCUSABLE | AG_WIDGET_USE_TEXT; tv->flags = RG_TILEVIEW_NO_EXTENT | RG_TILEVIEW_NO_TILING; tv->ts = NULL; tv->tile = NULL; tv->su = -1; 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->tile_ctrl.geo_ctrl = NULL; tv->tile_ctrl.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->tCache = AG_TextCacheNew(tv, 64, 2); TAILQ_INIT(&tv->tools); TAILQ_INIT(&tv->ctrls); AG_InitTimer(&tv->redrawTo, "redraw", 0); } #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; /* XXX TODO switch to agar's new style system */ AG_ColorWhite(&ctrl->c); ctrl->a = 255; AG_ColorRGB_8(&ctrl->cIna, 128,128,128); ctrl->aIna = 255; AG_ColorRGB_8(&ctrl->cEna, 250,250,250); ctrl->aEna = 255; AG_ColorRGB_8(&ctrl->cOver, 200,200,200); ctrl->aOver = 255; AG_ColorRGB_8(&ctrl->cHigh, 200,200,200); AG_ColorRGB_8(&ctrl->cLow, 60,60,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; } AG_Redraw(tv); return (ctrl); missingvals: AG_FatalError("RG_TileviewAddCtrl: Missing values"); } static void FreeCtrl(RG_TileviewCtrl *_Nonnull ctrl) { free(ctrl->valtypes); free(ctrl->vals); free(ctrl); } static void FreeTool(RG_TileviewTool *_Nonnull 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); AG_Redraw(tv); } /* Timer for auto refresh updates. */ static Uint32 RedrawTimeout(AG_Timer *_Nonnull to, AG_Event *_Nonnull event) { RG_Tileview *tv = RG_TILEVIEW_SELF(); tv->tile->flags |= RG_TILE_DIRTY; AG_Redraw(tv); return (to->ival); } void RG_TileviewSetAutoRefresh(RG_Tileview *tv, int ena, int rate) { if (ena) { AG_AddTimer(tv, &tv->redrawTo, rate, RedrawTimeout, NULL); } else { AG_DelTimer(tv, &tv->redrawTo); } } void RG_TileviewSetZoom(RG_Tileview *tv, int z2, int adj_offs) { RG_Tile *t = tv->tile; AG_Surface *S = t->su; int pxsz = tv->pxsz; int pxsz1 = pxsz; if (z2 < 100 || z2 > 1600) return; tv->zoom = z2; pxsz = z2/100; if (pxsz < 1) { pxsz = 1; } tv->pxsz = pxsz; tv->scaled = AG_SurfaceRGBA( (z2 >= 100) ? S->w * pxsz : S->w * z2 / 100, (z2 >= 100) ? S->h * pxsz : S->h * z2 / 100, S->format.BitsPerPixel, (S->flags & AG_SURFACE_COLORKEY), S->format.Rmask, S->format.Gmask, S->format.Bmask, S->format.Amask); if (tv->scaled == NULL) { AG_FatalError(NULL); } tv->scaled->alpha = S->alpha; tv->scaled->colorkey = S->colorkey; tv->pxlen = pxsz * tv->scaled->format.BytesPerPixel; if (0) { tv->xoffs += (S->w*pxsz1 - S->w*pxsz)/2; tv->yoffs += (S->h*pxsz1 - S->h*pxsz)/2; ClampOffsets(tv); } if (tv->su != -1) { AG_WidgetUnmapSurface(tv, tv->su); tv->su = -1; } t->flags |= RG_TILE_DIRTY; AG_Redraw(tv); } static void SizeRequest(void *_Nonnull obj, AG_SizeReq *_Nonnull 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 *_Nonnull obj, const AG_SizeAlloc *_Nonnull 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); } /* * Render a scaled pixel at x,y. * Must be called from widget draw() context. */ void RG_TileviewPixel(RG_Tileview *tv, int x, int y) { AG_Driver *drv = WIDGET(tv)->drv; int dx, dy; if (!(AGDRIVER_CLASS(drv)->flags & AG_DRIVER_OPENGL)) { int x1 = RG_TILEVIEW_MAPPED_X(tv,x); int y1 = RG_TILEVIEW_MAPPED_Y(tv,y); for (dy = 0; dy < tv->pxsz; dy++) { for (dx = 0; dx < tv->pxsz; dx++) { int xView = x1+dx; int yView = y1+dy; if (xView < 0 || yView < 0 || xView > WIDTH(tv) || yView > HEIGHT(tv)) continue; if (tv->c.a < 255) { AG_BlendPixelRGBA(tv, xView, yView, (Uint8 *)&tv->c, AG_ALPHA_OVERLAY); } else { AG_PutPixel32(tv, xView, yView, tv->c.pc); } } } } else { #ifdef HAVE_OPENGL int x1 = RG_TILEVIEW_MAPPED_X_ABS(tv,x); int y1 = RG_TILEVIEW_MAPPED_Y_ABS(tv,y); int x2 = x1 + tv->pxsz; int y2 = y1 + tv->pxsz; GLboolean svBlendBit; GLint svBlendSrc, svBlendDst; 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 *_Nonnull tv, const char *_Nonnull label) { AG_Rect r; int su, w,h, wSu,hSu; AG_PushTextState(); AG_TextColor(&WCOLOR(tv, TEXT_COLOR)); su = AG_TextCacheGet(tv->tCache, label); AG_PopTextState(); if (su == -1) return; wSu = WSURFACE(tv,su)->w; hSu = WSURFACE(tv,su)->h; w = WIDTH(tv); h = HEIGHT(tv); r.x = (wSu >= w) ? 0 : (w - wSu - 2); r.y = h - hSu - 2; r.w = w; r.h = h; AG_DrawRect(tv, &r, &WCOLOR(tv, BG_COLOR)); AG_PushBlendingMode(tv, AG_ALPHA_SRC, AG_ALPHA_ONE_MINUS_SRC); AG_WidgetBlitSurface(tv, su, w-wSu-1, h-hSu-1); AG_PopBlendingMode(tv); } 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_MapPixel32_RGB8(WIDGET(tv)->drv->videoFmt, 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_MapPixel32_RGBA8(WIDGET(tv)->drv->videoFmt, r,g,b,a); } void RG_TileviewColor(RG_Tileview *tv, const 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_MapPixel32_RGBA8(WIDGET(tv)->drv->videoFmt, c->r, c->g, c->b, a); } void RG_TileviewAlpha(RG_Tileview *tv, Uint8 a) { tv->c.a = a; tv->c.pc = AG_MapPixel32_RGBA8(WIDGET(tv)->drv->videoFmt, tv->c.r, tv->c.g, tv->c.b, a); } /* Must be called from widget draw context. */ void RG_TileviewRect(RG_Tileview *tv, int x, int y, int w, int h) { AG_Driver *drv = WIDGET(tv)->drv; if (!(AGDRIVER_CLASS(drv)->flags & AG_DRIVER_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_TileviewPixel(tv, xi, yi); } else { #ifdef HAVE_OPENGL int x1 = RG_TILEVIEW_MAPPED_X_ABS(tv,x); int y1 = RG_TILEVIEW_MAPPED_Y_ABS(tv,y); int x2 = x1 + w*tv->pxsz; int y2 = y1 + h*tv->pxsz; GLboolean svBlendBit; GLint svBlendSrc, svBlendDst; 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_TileviewCircle(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_TileviewPixel(tv, x0+x, y0+y); RG_TileviewPixel(tv, x0+x, y0-y); RG_TileviewPixel(tv, x0-x, y0+y); RG_TileviewPixel(tv, x0-x, y0-y); e += u; u += 2; if (v < 2*e) { y--; e -= v; v -= 2; } x++; RG_TileviewPixel(tv, x0+y, y0+x); RG_TileviewPixel(tv, x0+y, y0-x); RG_TileviewPixel(tv, x0-y, y0+x); RG_TileviewPixel(tv, x0-y, y0-x); } RG_TileviewPixel(tv, x0-r, y0); RG_TileviewPixel(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_TileviewPixel(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_TileviewPixel(tv, x, y); } /* Must be called from widget draw context. */ void RG_TileviewRectOut(RG_Tileview *tv, int x, int y, int w, int h) { AG_Driver *drv = WIDGET(tv)->drv; if (!(AGDRIVER_CLASS(drv)->flags & AG_DRIVER_OPENGL)) { int xi, yi; for (yi = y+1; yi < y+h; yi++) { RG_TileviewPixel(tv, x, yi); RG_TileviewPixel(tv, x+w, yi); } for (xi = x; xi < x+w+1; xi++) { RG_TileviewPixel(tv, xi, y); RG_TileviewPixel(tv, xi, y+h); } } else { #ifdef HAVE_OPENGL int x1 = RG_TILEVIEW_MAPPED_X_ABS(tv,x); int y1 = RG_TILEVIEW_MAPPED_Y_ABS(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_LINE_LOOP); 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(); glLineWidth(saved_width); #endif /* HAVE_OPENGL */ } } static void DrawHandle(RG_Tileview *_Nonnull tv, RG_TileviewCtrl *_Nonnull ctrl, RG_TileviewHandle *_Nonnull th) { int x = th->x; int y = th->y; if (th->over && !th->enable) { RG_TileviewColor(tv, &ctrl->cOver, ctrl->aOver); } else { RG_TileviewColor(tv, th->enable ? &ctrl->cEna : &ctrl->cIna, th->enable ? ctrl->aEna : ctrl->aIna); } /* Draw the inner circle. */ RG_TileviewRect(tv, x-1, y-1, 3, 3); /* Draw the central point. */ RG_TileviewColor4i(tv, 255, 255, 255, th->enable ? ctrl->aEna : ctrl->aIna); RG_TileviewPixel(tv, x, y); /* Draw the highlights. */ RG_TileviewColor(tv, th->enable ? &ctrl->cLow : &ctrl->cHigh, 255); RG_TileviewPixel(tv, x-2, y+1); /* Highlight left */ RG_TileviewPixel(tv, x-2, y); RG_TileviewPixel(tv, x-2, y-1); RG_TileviewPixel(tv, x-1, y-2); /* Highlight top */ RG_TileviewPixel(tv, x, y-2); RG_TileviewPixel(tv, x+1, y-2); RG_TileviewColor(tv, th->enable ? &ctrl->cHigh : &ctrl->cLow, 255); RG_TileviewPixel(tv, x-1, y+2); /* Occlusion bottom */ RG_TileviewPixel(tv, x, y+2); RG_TileviewPixel(tv, x+1, y+2); RG_TileviewPixel(tv, x+2, y-1); RG_TileviewPixel(tv, x+2, y); RG_TileviewPixel(tv, x+2, y+1); } static void DrawControl(RG_Tileview *_Nonnull tv, RG_TileviewCtrl *_Nonnull ctrl) { int i; RG_TileviewColor(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_TileviewRectOut(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_TileviewCircle(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_TileviewCircle(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_TileviewCircle(tv, x, y, r); RG_TileviewPixel(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 GetAttrColor(Uint flag, int state, AG_Color *c) { switch (flag) { case RG_TILE_BLOCK: if (state) { AG_ColorRGBA_8(c, 255,0,0,64); } else { AG_ColorRGBA_8(c, 0,255,0,32); } break; case RG_TILE_CLIMBABLE: if (state) { AG_ColorRGBA_8(c, 255,255,0,64); } else { AG_ColorRGBA_8(c, 255,0,0,32); } break; case RG_TILE_SLIPPERY: if (state) { AG_ColorRGBA_8(c, 0,0,255,64); } else { AG_ColorRGBA_8(c, 0,0,0,0); } break; case RG_TILE_JUMPABLE: if (state) { AG_ColorRGBA_8(c, 255,0,255,64); } else { AG_ColorRGBA_8(c, 0,0,0,0); } break; } } /* Render a gimp-style background tiling. */ static void DrawTiling(void *obj, const AG_Rect *r, int tsz, int offs, const AG_Color *c1, const AG_Color *c2) { AG_Widget *wid = (AG_Widget *)obj; AG_Driver *drv = wid->drv; AG_Rect rd; int alt1 = 0, alt2 = 0; int x = wid->rView.x1 + r->x; int y = wid->rView.y1 + r->y; int x2 = x + r->w; int y2 = y + r->h; int tsz_offs = tsz+offs; rd.w = tsz; rd.h = tsz; /* XXX inelegant */ for (rd.y = y-tsz_offs; rd.y < y2; rd.y += tsz) { for (rd.x = x-tsz_offs; rd.x < x2; rd.x += tsz) { if (alt1++ == 1) { wid->drvOps->drawRectFilled(drv, &rd, c1); alt1 = 0; } else { wid->drvOps->drawRectFilled(drv, &rd, c2); } } if (alt2++ == 1) { alt2 = 0; } alt1 = alt2; } } static void Draw(void *_Nonnull obj) { char status[64]; RG_Tileview *tv = obj; #ifdef HAVE_OPENGL AG_Driver *drv = WIDGET(tv)->drv; #endif RG_Tile *t = tv->tile; AG_Rect rs, rd; int x, y; if (t == NULL) return; rs.x = 0; rs.y = 0; rs.w = WIDTH(tv); rs.h = HEIGHT(tv); AG_PushClipRect(tv, &rs); #if 0 if (tv->state == RG_TILEVIEW_SKETCH_EDIT) { VG_Rasterize(rv->sketch.sk->vg); t->flags |= RG_TILE_DIRTY; } #endif if ((t->flags & RG_TILE_DIRTY) || tv->su == -1) { AG_Surface *Ss; t->flags &= ~RG_TILE_DIRTY; RG_TileGenerate(t); Ss = AG_SurfaceScale(t->su, tv->scaled->w, tv->scaled->h, 0); if (Ss == NULL) { AG_FatalError(NULL); } tv->scaled = Ss; if (tv->su == -1) { tv->su = AG_WidgetMapSurface(tv, tv->scaled); } else { AG_WidgetUpdateSurface(tv, tv->su); } } if ((tv->flags & RG_TILEVIEW_NO_TILING) == 0) { AG_Color c1,c2; AG_ColorRGB_8(&c1, 140,140,140); AG_ColorRGB_8(&c2, 80,80,80); DrawTiling(tv, &rs, 9, 0, &c1,&c2); } rs.x = 0; rs.y = 0; rs.w = tv->scaled->w; rs.h = tv->scaled->h; rd.x = tv->xoffs; rd.y = tv->yoffs; if (tv->xoffs > 0 && tv->xoffs + tv->scaled->w > WIDTH(tv)) { rs.w = WIDTH(tv) - tv->xoffs; } else if (tv->xoffs < 0 && -tv->xoffs < tv->scaled->w) { rd.x = 0; rs.x = -tv->xoffs; rs.w = tv->scaled->w - (-tv->xoffs); if (rs.w > WIDTH(tv)) rs.w = WIDTH(tv); } if (tv->yoffs > 0 && tv->yoffs + tv->scaled->h > HEIGHT(tv)) { rs.h = HEIGHT(tv) - tv->yoffs; } else if (tv->yoffs < 0 && -tv->yoffs < tv->scaled->h) { rd.y = 0; rs.y = -tv->yoffs; rs.h = tv->scaled->h - (-tv->yoffs); if (rs.h > HEIGHT(tv)) rs.h = HEIGHT(tv); } AG_WidgetBlitFrom(tv, tv->su, &rs, rd.x, rd.y); #ifdef HAVE_OPENGL if (AGDRIVER_CLASS(drv)->flags & AG_DRIVER_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_TileviewRectOut(tv, 0, 0, t->su->w-1, t->su->h-1); } RG_TileviewPixel(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; for (y=0, ny=0; y < th; y+=tsz, ny++) { for (x=0, nx=0; x < tw; x+=tsz, nx++) { AG_Rect r; AG_Color c; int w = tsz; int h = tsz; int d; AG_ColorNone(&c); GetAttrColor(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; } r.x = tv->xoffs + x; r.y = tv->yoffs + y; r.w = w; r.h = h; AG_DrawRectBlended(tv, &r, &c, AG_ALPHA_SRC, AG_ALPHA_ONE_MINUS_SRC); } } 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(); for (y=0, ny=0; y < th; y+=tsz, ny++) { for (x=0, nx=0; x < tw; x+=tsz, nx++) { AG_Color c; AG_Rect r; AG_Surface *tsu; int l = RG_TILE_LAYER2(t,nx,ny); Strlcpy(text, (l > 0) ? "+" : "", sizeof(text)); StrlcatInt(text, l, sizeof(text)); AG_TextColorRGB(0,0,0); tsu = AG_TextRender(text); r.x = tv->xoffs + x; r.y = tv->yoffs + y; r.w = tsu->w; r.h = tsu->h; AG_ColorRGBA_8(&c, 255,255,255,128); AG_DrawRectBlended(tv, &r, &c, AG_ALPHA_SRC, AG_ALPHA_ONE_MINUS_SRC); AG_WidgetBlit(tv, tsu, r.x, r.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->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->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->pixmap.px->name, sizeof(status)); Strlcat(status, pixmap_state_names[tv->pixmap.state], sizeof(status)); DrawStatusText(tv, status); } break; default: break; } if (!(tv->flags & RG_TILEVIEW_HIDE_CONTROLS)) { RG_TileviewCtrl *ctrl; TAILQ_FOREACH(ctrl, &tv->ctrls, ctrls) DrawControl(tv, ctrl); } #ifdef HAVE_OPENGL if (AGDRIVER_CLASS(drv)->flags & AG_DRIVER_OPENGL) glDisable(GL_BLEND); #endif AG_PopClipRect(tv); } static void Destroy(void *_Nonnull 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); } AG_TextCacheDestroy(tv->tCache); } static void CloseToolWindow(AG_Event *_Nonnull event) { RG_Tileview *tv = RG_TILEVIEW_PTR(1); AG_ObjectDetach(tv->cur_tool->win); tv->cur_tool->win = NULL; } void RG_TileviewSelectTool(RG_Tileview *tv, RG_TileviewTool *tvt) { AG_Window *pwin; pwin = AG_ParentWindow(tv); #ifdef AG_DEBUG if (pwin == NULL) { AG_Verbose("RG_TileviewSelectTool: %s has no parent window\n", OBJECT(tv)->name); return; } #endif if (tv->cur_tool != NULL && tv->cur_tool->win != NULL) { AG_ObjectDetach(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_WindowSetCaptionS(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; AG_Redraw(tv); } void RG_TileviewUnselectTool(RG_Tileview *tv) { if (tv->cur_tool != NULL) { if (tv->cur_tool->win != NULL) { AG_ObjectDetach(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; AG_Redraw(tv); } 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, MouseButtonDown, MouseButtonUp, MouseMotion, KeyDown, KeyUp, NULL, /* touch */ NULL, /* ctrl */ NULL /* joy */ };