/*
* 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 "radio.h"
#include "window.h"
#include "primitive.h"
#include "text.h"
AG_Radio *
AG_RadioNew(void *parent, Uint flags, const char **itemText)
{
AG_Radio *rad;
rad = Malloc(sizeof(AG_Radio));
AG_ObjectInit(rad, &agRadioClass);
rad->flags |= flags;
if (flags & AG_RADIO_HFILL) { AG_ExpandHoriz(rad); }
if (flags & AG_RADIO_VFILL) { AG_ExpandVert(rad); }
if (itemText != NULL) {
AG_RadioItemsFromArray(rad, itemText);
}
AG_ObjectAttach(parent, rad);
return (rad);
}
AG_Radio *
AG_RadioNewInt(void *parent, Uint flags, const char **itemText, int *v)
{
AG_Radio *rad;
rad = AG_RadioNew(parent, flags, itemText);
AG_BindInt(rad, "value", v);
return (rad);
}
AG_Radio *
AG_RadioNewUint(void *parent, Uint flags, const char **itemText, Uint *v)
{
AG_Radio *rad;
rad = AG_RadioNew(parent, flags, itemText);
AG_BindUint(rad, "value", v);
return (rad);
}
AG_Radio *
AG_RadioNewFn(void *parent, Uint flags, const char **itemText, AG_EventFn fn,
const char *fmt, ...)
{
AG_Radio *rad;
AG_Event *ev;
rad = AG_RadioNew(parent, flags, itemText);
ev = AG_SetEvent(rad, "radio-changed", fn, NULL);
AG_EVENT_GET_ARGS(ev, fmt);
return (rad);
}
/* Create a set of items from an array of strings. */
void
AG_RadioItemsFromArray(AG_Radio *rad, const char **itemText)
{
const char *s, **pItems;
AG_RadioItem *ri;
int i, w;
AG_ObjectLock(rad);
for (i = 0, pItems = itemText;
(s = *pItems++) != NULL;
i++) {
rad->items = Realloc(rad->items, (rad->nItems+1) *
sizeof(AG_RadioItem));
ri = &rad->items[rad->nItems++];
Strlcpy(ri->text, s, sizeof(ri->text));
ri->surface = -1;
ri->hotkey = SDLK_UNKNOWN;
AG_TextSize(s, &w, NULL);
if (w > rad->max_w) { rad->max_w = w; }
}
AG_ObjectUnlock(rad);
}
/* Create a radio item and return its index. */
int
AG_RadioAddItem(AG_Radio *rad, const char *fmt, ...)
{
AG_RadioItem *ri;
va_list ap;
int w, rv;
AG_ObjectLock(rad);
rad->items = Realloc(rad->items, (rad->nItems+1)*sizeof(AG_RadioItem));
ri = &rad->items[rad->nItems];
ri->surface = -1;
ri->hotkey = SDLK_UNKNOWN;
va_start(ap, fmt);
Vsnprintf(ri->text, sizeof(ri->text), fmt, ap);
va_end(ap);
AG_TextSize(ri->text, &w, NULL);
if (w > rad->max_w) { rad->max_w = w; }
rv = rad->nItems++;
AG_ObjectUnlock(rad);
return (rv);
}
/* Create a radio item and return its index (with hotkey). */
int
AG_RadioAddItemHK(AG_Radio *rad, SDLKey hotkey, const char *fmt, ...)
{
AG_RadioItem *ri;
va_list ap;
int w, rv;
AG_ObjectLock(rad);
rad->items = Realloc(rad->items, (rad->nItems+1)*sizeof(AG_RadioItem));
ri = &rad->items[rad->nItems];
ri->surface = -1;
ri->hotkey = hotkey;
va_start(ap, fmt);
Vsnprintf(ri->text, sizeof(ri->text), fmt, ap);
va_end(ap);
AG_TextSize(ri->text, &w, NULL);
if (w > rad->max_w) { rad->max_w = w; }
rv = rad->nItems++;
AG_ObjectUnlock(rad);
return (rv);
}
/* Remove all radio items */
void
AG_RadioClearItems(AG_Radio *rad)
{
int i;
AG_ObjectLock(rad);
for (i = 0; i < rad->nItems; i++) {
if (rad->items[i].surface != -1)
AG_WidgetUnmapSurface(rad, rad->items[i].surface);
}
Free(rad->items);
rad->items = Malloc(sizeof(AG_RadioItem));
rad->nItems = 0;
rad->max_w = 0;
AG_ObjectUnlock(rad);
}
static void
Draw(void *obj)
{
AG_Radio *rad = obj;
int i, val;
int x = rad->xPadding + rad->radius*2 + rad->xSpacing;
int y = rad->yPadding;
STYLE(rad)->RadioGroupBackground(rad,
AG_RECT(0, 0, WIDTH(rad), HEIGHT(rad)));
val = AG_GetInt(rad, "value");
AG_PushTextState();
AG_PushClipRect(rad, rad->r);
for (i = 0; i < rad->nItems;
i++, y += (rad->radius*2 + rad->ySpacing)) {
AG_RadioItem *ri = &rad->items[i];
STYLE(rad)->RadioButton(rad, x, y,
(i == val),
(i == rad->oversel));
if (ri->surface == -1) {
AG_TextColor(RADIO_TXT_COLOR);
ri->surface = AG_WidgetMapSurface(rad,
AG_TextRender(ri->text));
}
AG_WidgetBlitSurface(rad, ri->surface, x, y);
}
AG_PopClipRect();
AG_PopTextState();
}
static void
Destroy(void *p)
{
AG_Radio *rad = p;
Free(rad->items);
}
static void
SizeRequest(void *obj, AG_SizeReq *r)
{
AG_Radio *rad = obj;
if (rad->nItems == 0) {
r->w = 0;
r->h = 0;
} else {
r->w = rad->xPadding*2 + rad->xSpacing*2 + rad->radius*2 +
rad->max_w;
r->h = rad->yPadding*2 + rad->nItems*rad->radius*2 +
(rad->nItems-1)*rad->ySpacing;
}
}
static int
SizeAllocate(void *obj, const AG_SizeAlloc *a)
{
AG_Radio *rad = obj;
if (a->w < rad->xPadding*2 ||
a->h < rad->yPadding*2) {
return (-1);
}
rad->r.x = rad->xPadding;
rad->r.y = rad->yPadding;
rad->r.w = a->w - rad->xPadding;
rad->r.h = a->h - rad->yPadding;
return (0);
}
static void
MouseMotion(AG_Event *event)
{
AG_Radio *rad = AG_SELF();
int y = AG_INT(2) - rad->yPadding;
rad->oversel = (y/(rad->radius*2 + rad->ySpacing));
}
static void
MouseButtonDown(AG_Event *event)
{
AG_Radio *rad = AG_SELF();
AG_Variable *value;
int button = AG_INT(1);
int y = AG_INT(3);
int *sel, selNew = -1;
value = AG_GetVariable(rad, "value", &sel);
switch (button) {
case SDL_BUTTON_LEFT:
selNew = ((y - rad->yPadding)/(rad->radius*2 + rad->ySpacing));
if (selNew >= rad->nItems) {
selNew = rad->nItems - 1;
} else if (selNew < 0) {
selNew = 0;
}
AG_WidgetFocus(rad);
break;
default:
break;
}
if (selNew != -1 && selNew != *sel) {
*sel = selNew;
AG_PostEvent(NULL, rad, "radio-changed", "%i", *sel);
}
AG_UnlockVariable(value);
}
static void
KeyDown(AG_Event *event)
{
AG_Radio *rad = AG_SELF();
AG_Variable *value;
int keysym = AG_INT(1);
int *sel, selNew = -1;
int i;
value = AG_GetVariable(rad, "value", &sel);
switch ((SDLKey)keysym) {
case SDLK_DOWN:
selNew = *sel;
if (++selNew >= rad->nItems)
selNew = rad->nItems-1;
break;
case SDLK_UP:
selNew = *sel;
if (--selNew < 0)
selNew = 0;
break;
default:
for (i = 0; i < rad->nItems; i++) {
if (rad->items[i].hotkey != SDLK_UNKNOWN &&
rad->items[i].hotkey == keysym) {
selNew = i;
break;
}
}
break;
}
if (selNew != -1 && selNew != *sel) {
*sel = selNew;
AG_PostEvent(NULL, rad, "radio-changed", "%i", *sel);
}
AG_UnlockVariable(value);
}
static void
Init(void *obj)
{
AG_Radio *rad = obj;
WIDGET(rad)->flags |= AG_WIDGET_FOCUSABLE|AG_WIDGET_UNFOCUSED_MOTION;
AG_BindInt(rad, "value", &rad->value);
rad->flags = 0;
rad->value = -1;
rad->max_w = 0;
rad->oversel = -1;
rad->xPadding = 3;
rad->yPadding = 4;
rad->xSpacing = 7;
rad->ySpacing = 2;
rad->radius = 6;
rad->items = NULL;
rad->nItems = 0;
rad->r = AG_RECT(0,0,0,0);
AG_SetEvent(rad, "window-mousebuttondown", MouseButtonDown, NULL);
AG_SetEvent(rad, "window-keydown", KeyDown, NULL);
AG_SetEvent(rad, "window-mousemotion", MouseMotion, NULL);
}
AG_WidgetClass agRadioClass = {
{
"Agar(Widget:Radio)",
sizeof(AG_Radio),
{ 0,0, },
Init,
NULL, /* free */
Destroy,
NULL, /* load */
NULL, /* save */
NULL /* edit */
},
Draw,
SizeRequest,
SizeAllocate
};