/*
* Copyright (c) 2002-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 "fixed_plotter.h"
#include "window.h"
#include "primitive.h"
enum {
NITEMS_INIT = 32,
NITEMS_GROW = 16
};
static void KeyDown(AG_Event *);
static void MouseMotion(AG_Event *);
static void MouseButtonDown(AG_Event *);
static void MouseButtonUp(AG_Event *);
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;
fpl->flags |= flags;
if (flags & AG_FIXED_PLOTTER_HFILL) { AG_ExpandHoriz(fpl); }
if (flags & AG_FIXED_PLOTTER_VFILL) { AG_ExpandVert(fpl); }
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);
AG_SetEvent(fpl, "window-mousemotion", MouseMotion, NULL);
AG_SetEvent(fpl, "window-keydown", KeyDown, NULL);
AG_SetEvent(fpl, "window-mousebuttonup", MouseButtonUp, NULL);
AG_SetEvent(fpl, "window-mousebuttondown", MouseButtonDown, NULL);
}
void
AG_FixedPlotterSetRange(AG_FixedPlotter *fpl, AG_FixedPlotterValue range)
{
AG_ObjectLock(fpl);
fpl->yrange = range;
AG_ObjectUnlock(fpl);
}
static void
KeyDown(AG_Event *event)
{
AG_FixedPlotter *fpl = AG_SELF();
SDLKey key = AG_SDLKEY(1);
switch (key) {
case SDLK_0:
fpl->xoffs = 0;
break;
case SDLK_LEFT:
if ((fpl->xoffs -= 10) < 0)
fpl->xoffs = 0;
break;
case SDLK_RIGHT:
fpl->xoffs += 10;
break;
default:
break;
}
}
static void
MouseMotion(AG_Event *event)
{
AG_FixedPlotter *fpl = AG_SELF();
int xrel = AG_INT(3);
int yrel = AG_INT(4);
int state = AG_INT(5);
if ((state & SDL_BUTTON_LMASK) == 0)
return;
if ((fpl->xoffs -= xrel) < 0)
fpl->xoffs = 0;
fpl->yOrigin += yrel;
if (fpl->yOrigin < 0)
fpl->yOrigin = 0;
if (fpl->yOrigin > WIDGET(fpl)->h)
fpl->yOrigin = WIDGET(fpl)->h;
}
static void
MouseButtonUp(AG_Event *event)
{
AG_FixedPlotter *fpl = AG_SELF();
fpl->flags |= AG_FIXED_PLOTTER_SCROLL;
}
static void
MouseButtonDown(AG_Event *event)
{
AG_FixedPlotter *fpl = AG_SELF();
int button = AG_INT(1);
if (button != SDL_BUTTON_LEFT)
return;
fpl->flags &= ~(AG_FIXED_PLOTTER_SCROLL);
AG_WidgetFocus(fpl);
}
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;
int x, y, ox = 0, oy;
Uint32 i, yOrigin;
AG_FixedPlotterValue oval;
yOrigin = WIDGET(fpl)->h * fpl->yOrigin / 100;
STYLE(fpl)->FixedPlotterBackground(fpl,
(fpl->flags & AG_FIXED_PLOTTER_XAXIS), yOrigin);
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 < WIDGET(fpl)->w;
ox = x, x += 2) {
oval = gi->vals[i] * WIDGET(fpl)->h / fpl->yrange;
y = yOrigin - oval;
if (i > 1) {
oval = gi->vals[i-1] * WIDGET(fpl)->h /
fpl->yrange;
oy = yOrigin - oval;
} else {
oy = yOrigin;
}
if (y < 0) { y = 0; }
if (oy < 0) { oy = 0; }
if (y > WIDGET(fpl)->h) { y = WIDGET(fpl)->h; }
if (oy > WIDGET(fpl)->h) { oy = WIDGET(fpl)->h; }
switch (fpl->type) {
case AG_FIXED_PLOTTER_POINTS:
AG_LockView();
AG_WidgetPutPixel(fpl, x, y, gi->color);
AG_UnlockView();
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;
gi = Malloc(sizeof(AG_FixedPlotterItem));
Strlcpy(gi->name, name, sizeof(gi->name));
gi->color = AG_MapRGB(agVideoFmt, r,g,b);
gi->vals = Malloc(NITEMS_INIT*sizeof(AG_FixedPlotterValue));
gi->maxvals = NITEMS_INIT;
gi->nvals = 0;
gi->fpl = fpl;
gi->limit = limit>0 ? limit : (0xffffffff-1);
AG_ObjectLock(fpl);
TAILQ_INSERT_HEAD(&fpl->items, gi, items);
AG_ObjectUnlock(fpl);
return (gi);
}
void
AG_FixedPlotterDatum(AG_FixedPlotterItem *gi, AG_FixedPlotterValue val)
{
AG_ObjectLock(gi->fpl);
gi->vals[gi->nvals] = val;
if (gi->nvals+1 >= gi->limit) {
memmove(gi->vals, gi->vals+1,
gi->nvals*sizeof(AG_FixedPlotterValue));
gi->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_ObjectUnlock(gi->fpl);
}
void
AG_FixedPlotterFreeItems(AG_FixedPlotter *fpl)
{
AG_FixedPlotterItem *git, *nextgit;
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_ObjectUnlock(fpl);
}
static void
Destroy(void *p)
{
AG_FixedPlotterFreeItems((AG_FixedPlotter *)p);
}
AG_WidgetClass agFixedPlotterClass = {
{
"Agar(Widget:FixedPlotter)",
sizeof(AG_FixedPlotter),
{ 0,0 },
Init,
NULL, /* free */
Destroy,
NULL, /* load */
NULL, /* save */
NULL /* edit */
},
Draw,
SizeRequest,
SizeAllocate
};