/* * Copyright (c) 2002-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. */ /* * Integer-only plotter widget. Used originally to implement a FPS meter. */ #include #ifdef AG_WIDGETS #include #include #include enum { NITEMS_INIT = 32, NITEMS_GROW = 16 }; AG_FixedPlotter * AG_FixedPlotterNew(void *parent, enum ag_fixed_plotter_type type, Uint flags) { AG_FixedPlotter *fpl; fpl = Malloc(sizeof(AG_FixedPlotter)); AG_ObjectInit(fpl, &agFixedPlotterClass); fpl->type = type; if (flags & AG_FIXED_PLOTTER_HFILL) { WIDGET(fpl)->flags |= AG_WIDGET_HFILL; } if (flags & AG_FIXED_PLOTTER_VFILL) { WIDGET(fpl)->flags |= AG_WIDGET_VFILL; } fpl->flags |= flags; AG_ObjectAttach(parent, fpl); return (fpl); } static void Init(void *obj) { AG_FixedPlotter *fpl = obj; WIDGET(fpl)->flags |= AG_WIDGET_FOCUSABLE; fpl->type = AG_FIXED_PLOTTER_LINES; fpl->flags = 0; fpl->xoffs = 0; fpl->yOrigin = 50; fpl->yrange = 100; TAILQ_INIT(&fpl->items); } void AG_FixedPlotterSetRange(AG_FixedPlotter *fpl, AG_FixedPlotterValue range) { AG_OBJECT_ISA(fpl, "AG_Widget:AG_FixedPlotter:*"); fpl->yrange = range; AG_Redraw(fpl); } static void KeyDown(void *obj, AG_KeySym ks, AG_KeyMod kmod, AG_Char ch) { AG_FixedPlotter *fpl = obj; switch (ks) { case AG_KEY_0: fpl->xoffs = 0; AG_Redraw(fpl); break; case AG_KEY_LEFT: if ((fpl->xoffs -= 10) < 0) { fpl->xoffs = 0; } AG_Redraw(fpl); break; case AG_KEY_RIGHT: fpl->xoffs += 10; AG_Redraw(fpl); break; default: break; } } static void MouseMotion(void *obj, int x, int y, int dx, int dy) { AG_FixedPlotter *fpl = obj; const AG_MouseButton btnState = WIDGET(fpl)->drv->mouse->btnState; const int h = WIDGET(fpl)->h; if ((btnState & AG_MOUSE_LMASK) == 0) return; if ((fpl->xoffs -= dx) < 0) fpl->xoffs = 0; fpl->yOrigin += dy; if (fpl->yOrigin < 0) fpl->yOrigin = 0; if (fpl->yOrigin > h) fpl->yOrigin = h; if (dx != 0 || dy != 0) AG_Redraw(fpl); } static void MouseButtonUp(void *obj, AG_MouseButton button, int x, int y) { AG_FixedPlotter *fpl = obj; fpl->flags |= AG_FIXED_PLOTTER_SCROLL; } static void MouseButtonDown(void *obj, AG_MouseButton button, int x, int y) { AG_FixedPlotter *fpl = obj; if (button != AG_MOUSE_LEFT) return; if (!AG_WidgetIsFocused(fpl)) AG_WidgetFocus(fpl); fpl->flags &= ~(AG_FIXED_PLOTTER_SCROLL); } static void SizeRequest(void *obj, AG_SizeReq *r) { r->w = 256; r->h = 128; } static int SizeAllocate(void *obj, const AG_SizeAlloc *a) { return (a->w > 2 && a->h > 4) ? 0 : -1; } static void Draw(void *obj) { AG_FixedPlotter *fpl = obj; AG_FixedPlotterItem *gi; AG_FixedPlotterValue oval; int x, y, ox = 0, oy; const int w = WIDTH(fpl); const int h = HEIGHT(fpl); const Uint32 yOrigin = h * fpl->yOrigin / 100; Uint32 i; AG_DrawBoxRaised(fpl, &WIDGET(fpl)->r, &WCOLOR(fpl,BG_COLOR)); if (fpl->flags & AG_FIXED_PLOTTER_XAXIS) AG_DrawLineH(fpl, 0, w-1, yOrigin+1, &WCOLOR(fpl,LINE_COLOR)); TAILQ_FOREACH(gi, &fpl->items, items) { if (fpl->xoffs > gi->nvals || fpl->xoffs < 0) continue; for (x = 2, ox = 0, i = fpl->xoffs; ++i < gi->nvals && x < w; ox = x, x += 2) { oval = gi->vals[i] * h / fpl->yrange; y = yOrigin - oval; if (i > 1) { oval = gi->vals[i-1] * h / fpl->yrange; oy = yOrigin - oval; } else { oy = yOrigin; } if (y < 0) { y = 0; } if (oy < 0) { oy = 0; } if (y > HEIGHT(fpl)) { y = HEIGHT(fpl); } if (oy > HEIGHT(fpl)) { oy = HEIGHT(fpl); } switch (fpl->type) { case AG_FIXED_PLOTTER_POINTS: AG_PutPixel(fpl, x,y, &gi->color); break; case AG_FIXED_PLOTTER_LINES: AG_DrawLine(fpl, ox,oy, x,y, &gi->color); break; } } } } AG_FixedPlotterItem * AG_FixedPlotterCurve(AG_FixedPlotter *fpl, const char *name, Uint8 r, Uint8 g, Uint8 b, Uint32 limit) { AG_FixedPlotterItem *gi; AG_OBJECT_ISA(fpl, "AG_Widget:AG_FixedPlotter:*"); gi = Malloc(sizeof(AG_FixedPlotterItem)); Strlcpy(gi->name, name, sizeof(gi->name)); AG_ColorRGB_8(&gi->color, r,g,b); gi->nvals = 0; gi->vals = Malloc(NITEMS_INIT*sizeof(AG_FixedPlotterValue)); gi->maxvals = NITEMS_INIT; gi->fpl = fpl; gi->limit = limit>0 ? limit : (0xffffffff-1); AG_ObjectLock(fpl); TAILQ_INSERT_HEAD(&fpl->items, gi, items); AG_Redraw(fpl); AG_ObjectUnlock(fpl); return (gi); } void AG_FixedPlotterDatum(AG_FixedPlotterItem *gi, AG_FixedPlotterValue val) { AG_FixedPlotter *fpl = gi->fpl; AG_OBJECT_ISA(fpl, "AG_Widget:AG_FixedPlotter:*"); AG_ObjectLock(fpl); gi->vals[gi->nvals] = val; if (gi->nvals+1 >= gi->limit) { memmove(gi->vals, gi->vals+1, gi->nvals*sizeof(AG_FixedPlotterValue)); fpl->flags &= ~(AG_FIXED_PLOTTER_SCROLL); } else { if (gi->nvals+1 >= gi->maxvals) { gi->vals = Realloc(gi->vals, (gi->maxvals+NITEMS_GROW) * sizeof(AG_FixedPlotterValue)); gi->maxvals += NITEMS_GROW; } gi->nvals++; } AG_Redraw(fpl); AG_ObjectUnlock(fpl); } void AG_FixedPlotterFreeItems(AG_FixedPlotter *fpl) { AG_FixedPlotterItem *git, *nextgit; AG_OBJECT_ISA(fpl, "AG_Widget:AG_FixedPlotter:*"); AG_ObjectLock(fpl); for (git = TAILQ_FIRST(&fpl->items); git != TAILQ_END(&fpl->items); git = nextgit) { nextgit = TAILQ_NEXT(git, items); Free(git->vals); Free(git); } TAILQ_INIT(&fpl->items); AG_Redraw(fpl); AG_ObjectUnlock(fpl); } static void Destroy(void *obj) { AG_FixedPlotter *fpl = obj; AG_FixedPlotterItem *git, *nextgit; for (git = TAILQ_FIRST(&fpl->items); git != TAILQ_END(&fpl->items); git = nextgit) { nextgit = TAILQ_NEXT(git, items); Free(git->vals); Free(git); } } void AG_FixedPlotterScroll(AG_FixedPlotter *_Nonnull fpl, int i) { AG_OBJECT_ISA(fpl, "AG_Widget:AG_FixedPlotter:*"); AG_ObjectLock(fpl); if (fpl->flags & AG_FIXED_PLOTTER_SCROLL) fpl->xoffs += i; AG_ObjectUnlock(fpl); } AG_WidgetClass agFixedPlotterClass = { { "Agar(Widget:FixedPlotter)", sizeof(AG_FixedPlotter), { 1,0, AGC_FIXED_PLOTTER, 0x223F }, Init, NULL, /* reset */ Destroy, NULL, /* load */ NULL, /* save */ NULL /* edit */ }, Draw, SizeRequest, SizeAllocate, MouseButtonDown, MouseButtonUp, MouseMotion, KeyDown, NULL, /* key_up */ NULL, /* touch */ NULL, /* ctrl */ NULL /* joy */ }; #endif /* AG_WIDGET */