/*
* Copyright (c) 2005-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 "pane.h"
#include "window.h"
#include "primitive.h"
#include "cursors.h"
AG_Pane *
AG_PaneNew(void *parent, enum ag_pane_type type, Uint flags)
{
AG_Pane *pa;
pa = Malloc(sizeof(AG_Pane));
AG_ObjectInit(pa, &agPaneClass);
pa->type = type;
pa->flags |= flags;
if (flags & AG_PANE_HFILL) { AG_ExpandHoriz(pa); }
if (flags & AG_PANE_VFILL) { AG_ExpandVert(pa); }
AG_ObjectAttach(parent, pa);
return (pa);
}
static __inline__ int
OverDivControl(AG_Pane *pa, int pos)
{
return (pos >= pa->dx &&
pos <= (pa->dx + pa->wDiv));
}
static void
MouseButtonDown(AG_Event *event)
{
AG_Pane *pa = AG_SELF();
int button = AG_INT(1);
if (button == SDL_BUTTON_LEFT) {
pa->dmoving = OverDivControl(pa,
pa->type == AG_PANE_HORIZ ? AG_INT(2) : AG_INT(3));
if (pa->dmoving)
WIDGET(pa)->flags |= AG_WIDGET_PRIO_MOTION;
}
}
int
AG_PaneMoveDivider(AG_Pane *pa, int dx)
{
AG_Window *pwin;
AG_SizeAlloc a;
int dxNew;
AG_ObjectLock(pa);
pa->dx = dx;
a.x = WIDGET(pa)->x;
a.y = WIDGET(pa)->y;
a.w = WIDTH(pa);
a.h = HEIGHT(pa);
AG_WidgetSizeAlloc(pa, &a);
dxNew = pa->dx;
AG_ObjectUnlock(pa);
if ((pwin = AG_WidgetParentWindow(pa)) != NULL) {
AG_WidgetUpdateCoords(pwin, WIDGET(pwin)->x,
WIDGET(pwin)->y);
}
return (dxNew);
}
static void
MouseMotion(AG_Event *event)
{
AG_Pane *pa = AG_SELF();
int x = AG_INT(1);
int y = AG_INT(2);
int dx = AG_INT(3);
int dy = AG_INT(4);
switch (pa->type) {
case AG_PANE_HORIZ:
if (y < 0 || y > HEIGHT(pa)) {
return;
}
if (pa->dmoving) {
if (pa->rx < 0) { pa->rx = pa->dx; }
pa->rx += dx;
if (pa->rx < 2) { pa->rx = 2; }
pa->rx = AG_PaneMoveDivider(pa, pa->rx);
if (OverDivControl(pa, x)) {
AG_SetCursor(AG_HRESIZE_CURSOR);
}
break;
} else if (OverDivControl(pa, x)) {
AG_SetCursor(AG_HRESIZE_CURSOR);
}
break;
case AG_PANE_VERT:
if (x < 0 || x > WIDTH(pa)) {
return;
}
if (pa->dmoving) {
if (pa->rx < 0) { pa->rx = pa->dx; }
pa->rx += dy;
pa->rx = AG_PaneMoveDivider(pa, pa->rx);
if (pa->rx < 2) { pa->rx = 2; }
if (OverDivControl(pa, y)) {
AG_SetCursor(AG_VRESIZE_CURSOR);
}
break;
} else if (OverDivControl(pa, y)) {
AG_SetCursor(AG_VRESIZE_CURSOR);
}
break;
}
}
static void
MouseButtonUp(AG_Event *event)
{
AG_Pane *pa = AG_SELF();
if (pa->dmoving) {
WIDGET(pa)->flags &= ~(AG_WIDGET_PRIO_MOTION);
pa->dmoving = 0;
}
}
static void
Init(void *obj)
{
AG_Pane *pa = obj;
int i;
WIDGET(pa)->flags |= AG_WIDGET_UNFOCUSED_BUTTONUP|
AG_WIDGET_UNFOCUSED_MOTION;
pa->type = AG_PANE_VERT;
pa->flags = AG_PANE_INITSCALE;
pa->div[0] = AG_BoxNew(pa, AG_BOX_VERT, AG_BOX_FRAME);
pa->div[1] = AG_BoxNew(pa, AG_BOX_VERT, AG_BOX_FRAME);
pa->dx = 0;
pa->rx = -1;
pa->dmoving = 0;
pa->wDiv = 8;
for (i = 0; i < 2; i++) {
pa->minw[i] = -1;
pa->minh[i] = -1;
#if 0
AG_BoxSetPadding(pa->div[i], 0);
AG_BoxSetSpacing(pa->div[i], 0);
#endif
}
AG_SetEvent(pa, "window-mousebuttondown", MouseButtonDown, NULL);
AG_SetEvent(pa, "window-mousebuttonup", MouseButtonUp, NULL);
AG_SetEvent(pa, "window-mousemotion", MouseMotion, NULL);
}
void
AG_PaneSetDividerWidth(AG_Pane *pa, int wDiv)
{
AG_ObjectLock(pa);
pa->wDiv = wDiv;
AG_ObjectUnlock(pa);
}
void
AG_PaneSetDivisionPacking(AG_Pane *pa, int which, enum ag_box_type packing)
{
AG_ObjectLock(pa);
AG_BoxSetType(pa->div[which], packing);
AG_ObjectUnlock(pa);
}
void
AG_PaneSetDivisionMin(AG_Pane *pa, int which, int minw, int minh)
{
AG_ObjectLock(pa);
pa->minw[which] = minw;
pa->minh[which] = minh;
AG_ObjectUnlock(pa);
}
void
AG_PaneAttachBox(AG_Pane *pa, int which, AG_Box *box)
{
AG_ObjectLock(pa);
AG_ObjectLock(box);
/* XXX */
#if 0
if (pa->div[which] != NULL) {
AG_ObjectDetach(pa->div[which]);
AG_ObjectDestroy(pa->div[which]);
}
#endif
AG_ObjectAttach(pa->div[which], box);
WIDGET(box)->flags |= AG_WIDGET_EXPAND;
AG_ObjectUnlock(box);
AG_ObjectUnlock(pa);
}
void
AG_PaneAttachBoxes(AG_Pane *pa, AG_Box *box1, AG_Box *box2)
{
AG_ObjectLock(pa);
AG_PaneAttachBox(pa, 0, box1);
AG_PaneAttachBox(pa, 1, box2);
AG_ObjectUnlock(pa);
}
static void
Draw(void *obj)
{
AG_Pane *pa = obj;
AG_WidgetDraw(pa->div[0]);
AG_WidgetDraw(pa->div[1]);
switch (pa->type) {
case AG_PANE_HORIZ:
STYLE(pa)->PaneHorizDivider(pa, pa->dx, HEIGHT(pa)/2,
pa->wDiv, pa->dmoving);
break;
case AG_PANE_VERT:
STYLE(pa)->PaneVertDivider(pa, WIDTH(pa)/2, pa->dx,
pa->wDiv, pa->dmoving);
break;
}
}
static void
SizeRequest(void *obj, AG_SizeReq *r)
{
AG_Pane *pa = obj;
AG_SizeReq rDiv;
int wMax = 0, hMax = 0;
int i;
r->w = 0;
r->h = 0;
for (i = 0; i < 2; i++) {
AG_Widget *div = WIDGET(pa->div[i]);
AG_WidgetSizeReq(div, &rDiv);
if (pa->minw[i] == -1) { pa->minw[i] = rDiv.w; }
if (pa->minh[i] == -1) { pa->minh[i] = rDiv.h; }
if (rDiv.w > wMax) { wMax = rDiv.w; }
if (rDiv.h > hMax) { hMax = rDiv.h; }
switch (pa->type) {
case AG_PANE_HORIZ:
r->h = MAX(r->h, hMax);
r->w += rDiv.w;
break;
case AG_PANE_VERT:
r->w = MAX(r->w, wMax);
r->h += rDiv.h;
break;
}
}
switch (pa->type) {
case AG_PANE_HORIZ:
r->w += pa->wDiv + 2;
break;
case AG_PANE_VERT:
r->h += pa->wDiv + 2;
break;
}
}
static int
SizeAllocate(void *obj, const AG_SizeAlloc *a)
{
AG_Pane *pa = obj;
AG_SizeReq r1, r2;
AG_SizeAlloc a1, a2;
a1.x = 0;
a1.y = 0;
AG_WidgetSizeReq(pa->div[0], &r1);
AG_WidgetSizeReq(pa->div[1], &r2);
switch (pa->type) {
case AG_PANE_HORIZ:
if (pa->rx == -1) {
if (pa->flags & AG_PANE_DIV) {
pa->dx = a->w/2;
} else if (pa->flags & AG_PANE_DIV1FILL) {
pa->dx = a->w - pa->minw[1];
} else {
pa->dx = pa->minw[0];
}
pa->rx = pa->dx;
} else {
if (pa->flags & AG_PANE_FORCE_DIV) {
pa->dx = a->w/2;
} else if (pa->flags & AG_PANE_FORCE_DIV1FILL) {
pa->dx = a->w - pa->minw[1];
} else if (pa->flags & AG_PANE_FORCE_DIV2FILL) {
pa->dx = pa->minw[0];
}
}
if (pa->dx < pa->minw[0]) {
pa->rx = pa->dx = pa->minw[0];
} else if (pa->dx > (a->w - pa->wDiv)) {
pa->rx = pa->dx = a->w - pa->wDiv;
} else if (pa->dx > (a->w - pa->minw[1])) {
pa->rx = pa->dx = a->w - pa->minw[1];
}
a1.w = pa->dx;
a1.h = a->h;
a2.x = pa->dx + pa->wDiv;
a2.y = 0;
a2.w = a->w - pa->dx - pa->wDiv;
a2.h = a->h;
break;
case AG_PANE_VERT:
if (pa->rx == -1) {
if (pa->flags & AG_PANE_DIV) {
pa->dx = a->h/2;
} else if (pa->flags & AG_PANE_DIV1FILL) {
pa->dx = a->h - pa->minh[1];
} else {
pa->dx = pa->minh[0];
}
pa->rx = pa->dx;
} else {
if (pa->flags & AG_PANE_FORCE_DIV) {
pa->dx = a->h/2;
} else if (pa->flags & AG_PANE_FORCE_DIV1FILL) {
pa->dx = a->h - pa->minh[1];
} else if (pa->flags & AG_PANE_FORCE_DIV2FILL) {
pa->dx = pa->minh[0];
}
}
if (pa->dx < pa->minh[0]) {
pa->rx = pa->dx = pa->minh[0];
} else if (pa->dx > (a->h - pa->wDiv)) {
pa->rx = pa->dx = a->h - pa->wDiv;
} else if (pa->dx > (a->h - pa->minh[1])) {
pa->rx = pa->dx = a->h - pa->minh[1];
}
a1.w = a->w;
a1.h = pa->dx;
a2.x = 0;
a2.y = pa->dx + pa->wDiv;
a2.w = a->w;
a2.h = a->h - pa->dx - pa->wDiv;
break;
}
AG_WidgetSizeAlloc(pa->div[0], &a1);
AG_WidgetSizeAlloc(pa->div[1], &a2);
if (pa->flags & AG_PANE_INITSCALE) {
pa->minw[0] = 0;
pa->minh[0] = 0;
pa->minw[1] = 0;
pa->minh[1] = 0;
}
return (0);
}
AG_WidgetClass agPaneClass = {
{
"Agar(Widget:Pane)",
sizeof(AG_Pane),
{ 0,0 },
Init,
NULL, /* free */
NULL, /* destroy */
NULL, /* load */
NULL, /* save */
NULL /* edit */
},
Draw,
SizeRequest,
SizeAllocate
};