/*
* Copyright (c) 2005-2015 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
RG_Animview *
RG_AnimviewNew(void *parent)
{
RG_Animview *av;
av = Malloc(sizeof(RG_Animview));
AG_ObjectInit(av, &rgAnimviewClass);
AG_ObjectAttach(parent, av);
return (av);
}
/* Timer for updating the current frame during playback. */
static Uint32
PlayUpdateTimeout(AG_Timer *to, AG_Event *event)
{
RG_Animview *av = AG_SELF();
if (av->anim->nframes == 0)
return (0);
AG_Redraw(av);
if (++av->frame < av->anim->nframes) {
RG_AnimFrame *fr = &av->anim->frames[av->frame];
return (fr->delay > 0 ? fr->delay*100/av->speed : 1);
} else {
RG_AnimFrame *fr = &av->anim->frames[0];
av->frame = 0;
return (fr->delay > 0 ? fr->delay*100/av->speed: 1);
}
}
static void
Play(AG_Event *event)
{
RG_Animview *av = AG_PTR(1);
AG_AddTimer(av, &av->timer, 1, PlayUpdateTimeout, NULL);
AG_WidgetDisable(av->btns.play);
AG_WidgetEnable(av->btns.pause);
AG_WidgetEnable(av->btns.stop);
}
static void
Pause(AG_Event *event)
{
RG_Animview *av = AG_PTR(1);
AG_DelTimer(av, &av->timer);
AG_WidgetEnable(av->btns.play);
AG_WidgetDisable(av->btns.pause);
AG_WidgetDisable(av->btns.stop);
}
static void
Stop(AG_Event *event)
{
RG_Animview *av = AG_PTR(1);
AG_DelTimer(av, &av->timer);
av->frame = 0;
AG_WidgetEnable(av->btns.play);
AG_WidgetDisable(av->btns.pause);
AG_WidgetDisable(av->btns.stop);
}
static void
SetSpeed(AG_Event *event)
{
RG_Animview *av = AG_PTR(1);
Uint factor = AG_INT(2);
av->speed = factor;
}
static void
PopupMenu(RG_Animview *av)
{
AG_PopupMenu *pm;
AG_MenuItem *mSpd;
if (av->pm != NULL) {
AG_PopupShow(av->pm);
return;
}
if ((av->pm = pm = AG_PopupNew(av)) == NULL)
return;
AG_MenuAction(pm->root, _("Play"), rgIconPlay.s, Play,"%p", av);
AG_MenuAction(pm->root, _("Pause"), rgIconPause.s, Pause,"%p", av);
AG_MenuAction(pm->root, _("Stop"), rgIconStop.s, Stop,"%p", av);
AG_MenuSeparator(pm->root);
mSpd = AG_MenuNode(pm->root, _("Playback speed"), NULL);
{
AG_MenuAction(mSpd, "4x", NULL, SetSpeed,"%p,%i", av, 400);
AG_MenuAction(mSpd, "3x", NULL, SetSpeed,"%p,%i", av, 300);
AG_MenuAction(mSpd, "2x", NULL, SetSpeed,"%p,%i", av, 200);
AG_MenuAction(mSpd, "1x", NULL, SetSpeed,"%p,%i", av, 100);
AG_MenuAction(mSpd, "1/2", NULL, SetSpeed,"%p,%i", av, 50);
AG_MenuAction(mSpd, "1/3", NULL, SetSpeed,"%p,%i", av, 33);
AG_MenuAction(mSpd, "1/4", NULL, SetSpeed,"%p,%i", av, 25);
}
AG_PopupShow(pm);
}
static void
MouseButtonDown(AG_Event *event)
{
RG_Animview *av = AG_SELF();
int button = AG_INT(1);
/* int x = AG_INT(2); */
int y = AG_INT(3);
if (button == AG_MOUSE_RIGHT &&
y < WIDGET(av)->h - WIDGET(av->btns.play)->h)
PopupMenu(av);
}
static void
Init(void *obj)
{
RG_Animview *av = obj;
WIDGET(av)->flags |= AG_WIDGET_EXPAND;
av->pre_w = 64;
av->pre_h = 64;
av->ranim.x = 0;
av->ranim.y = 0;
av->ranim.w = av->pre_w;
av->ranim.h = av->pre_h;
av->speed = 100;
av->pm = NULL;
av->btns.play = AG_ButtonNewS(av, 0, NULL);
AG_ButtonSurfaceNODUP(av->btns.play, rgIconPlay.s);
AG_SetEvent(av->btns.play, "button-pushed", Play, "%p", av);
av->btns.pause = AG_ButtonNewS(av, 0, NULL);
AG_ButtonSurfaceNODUP(av->btns.pause, rgIconPause.s);
AG_SetEvent(av->btns.pause, "button-pushed", Pause, "%p", av);
av->btns.stop = AG_ButtonNewS(av, 0, NULL);
AG_ButtonSurfaceNODUP(av->btns.stop, rgIconStop.s);
AG_SetEvent(av->btns.stop, "button-pushed", Stop, "%p", av);
AG_WidgetEnable(av->btns.play);
AG_WidgetDisable(av->btns.pause);
AG_WidgetDisable(av->btns.stop);
AG_SetEvent(av, "mouse-button-down", MouseButtonDown, NULL);
}
static void
Destroy(void *obj)
{
RG_Animview *av = obj;
if (av->pm != NULL)
AG_PopupDestroy(av->pm);
}
void
RG_AnimviewSizeHint(RG_Animview *av, int w, int h)
{
av->pre_w = w;
av->pre_h = h;
}
static void
SizeRequest(void *p, AG_SizeReq *r)
{
RG_Animview *av = p;
AG_SizeReq rPlay, rPause, rStop;
AG_WidgetSizeReq(av->btns.play, &rPlay);
AG_WidgetSizeReq(av->btns.pause, &rPause);
AG_WidgetSizeReq(av->btns.stop, &rStop);
r->w = AG_MAX(av->pre_w, rPlay.w+rPause.w+rStop.w);
r->h = av->pre_h + AG_MAX3(rPlay.h, rPause.h, rStop.h);
}
static int
SizeAllocate(void *p, const AG_SizeAlloc *a)
{
RG_Animview *av = p;
AG_SizeReq rBtn;
AG_SizeAlloc aBtn;
int hBtn = 0;
AG_WidgetSizeReq(av->btns.play, &rBtn);
if (hBtn < rBtn.h) { hBtn = rBtn.h; }
AG_WidgetSizeReq(av->btns.pause, &rBtn);
if (hBtn < rBtn.h) { hBtn = rBtn.h; }
AG_WidgetSizeReq(av->btns.stop, &rBtn);
if (hBtn < rBtn.h) { hBtn = rBtn.h; }
aBtn.x = 0;
aBtn.y = a->h - hBtn;
aBtn.w = a->w/3;
aBtn.h = hBtn;
AG_WidgetSizeAlloc(av->btns.play, &aBtn);
aBtn.x += aBtn.w;
AG_WidgetSizeAlloc(av->btns.pause, &aBtn);
aBtn.x += aBtn.w;
if ((aBtn.x + aBtn.w) < a->w) { aBtn.w++; } /* For rounding */
AG_WidgetSizeAlloc(av->btns.stop, &aBtn);
av->ranim.x = a->w/2 - av->ranim.w/2;
av->ranim.y = (a->h - hBtn)/2 - av->ranim.h/2;
return (0);
}
static void
Draw(void *p)
{
RG_Animview *av = p;
RG_AnimFrame *fr;
if (av->anim == NULL || av->anim->nframes < 1) {
return;
}
fr = &av->anim->frames[av->frame];
if (fr->su != NULL) {
AG_Rect r;
r.x = 0;
r.y = 0;
r.w = WIDTH(av);
r.h = HEIGHT(av);
AG_PushClipRect(av, &r);
AG_WidgetBlit(av, fr->su, av->ranim.x, av->ranim.y);
AG_PopClipRect(av);
}
}
void
RG_AnimviewSetAnimation(RG_Animview *av, RG_Anim *anim)
{
AG_DelTimer(av, &av->timer);
av->anim = anim;
av->frame = 0;
av->ranim.x = WIDGET(av)->w/2 - anim->w/2;
av->ranim.y = (WIDGET(av)->h - WIDGET(av->btns.play)->h)/2 - anim->h/2;
av->ranim.w = anim->w;
av->ranim.h = anim->h;
AG_Redraw(av);
}
AG_WidgetClass rgAnimviewClass = {
{
"Agar(Widget):RG(Animview)",
sizeof(RG_Animview),
{ 0,0 },
Init,
Destroy,
NULL, /* destroy */
NULL, /* load */
NULL, /* save */
NULL /* edit */
},
Draw,
SizeRequest,
SizeAllocate
};