/*
* 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 "notebook.h"
#include "window.h"
#include "primitive.h"
#define SPACING 8
AG_Notebook *
AG_NotebookNew(void *parent, Uint flags)
{
AG_Notebook *nb;
nb = Malloc(sizeof(AG_Notebook));
AG_ObjectInit(nb, &agNotebookClass);
nb->flags |= flags;
if (flags & AG_NOTEBOOK_HFILL) { AG_ExpandHoriz(nb); }
if (flags & AG_NOTEBOOK_VFILL) { AG_ExpandVert(nb); }
AG_ObjectAttach(parent, nb);
return (nb);
}
static void
MouseButtonDown(AG_Event *event)
{
AG_Notebook *nb = AG_SELF();
int x = AG_INT(2);
int y = AG_INT(3);
if ((nb->flags & AG_NOTEBOOK_HIDE_TABS) == 0 &&
y <= nb->bar_h) {
AG_NotebookTab *tab;
int tx = SPACING;
TAILQ_FOREACH(tab, &nb->tabs, tabs) {
AG_Surface *label = WSURFACE(nb,tab->label);
#if 0
if (tx+label->w+SPACING*2 > WIDGET(nb)->w)
break;
#endif
if (x >= tx && x < tx+(label->w + SPACING*2)) {
AG_NotebookSelectTab(nb, tab);
break;
}
tx += label->w + SPACING*2;
}
}
}
static void
Shown(AG_Event *event)
{
AG_Notebook *nb = AG_SELF();
if (nb->sel_tab != NULL) {
AG_NotebookSelectTab(nb, nb->sel_tab);
} else if (!TAILQ_EMPTY(&nb->tabs)) {
AG_NotebookSelectTab(nb, TAILQ_FIRST(&nb->tabs));
}
}
static void
Init(void *obj)
{
AG_Notebook *nb = obj;
nb->flags = 0;
nb->tab_align = AG_NOTEBOOK_TABS_TOP;
nb->sel_tab = NULL;
nb->bar_w = -1;
nb->bar_h = -1;
nb->cont_w = -1;
nb->cont_h = -1;
nb->spacing = -1;
nb->padding = -1;
nb->lblPartial = -1;
nb->tabFont = NULL;
nb->r = AG_RECT(0,0,0,0);
TAILQ_INIT(&nb->tabs);
AG_NotebookSetTabFont(nb, AG_FetchFont(NULL, agDefaultFont->size-1, 0));
AG_SetEvent(nb, "window-mousebuttondown", MouseButtonDown, NULL);
AG_SetEvent(nb, "widget-shown", Shown, NULL);
}
static void
Draw(void *obj)
{
AG_Notebook *nb = obj;
AG_NotebookTab *tab;
int x = SPACING;
int y = SPACING;
int idx = 0;
AG_Rect r;
STYLE(nb)->NotebookBackground(nb, nb->r);
if (nb->sel_tab != NULL) {
AG_PushClipRect(nb, nb->r);
AG_WidgetDraw(&nb->sel_tab->box);
AG_PopClipRect();
}
if (nb->flags & AG_NOTEBOOK_HIDE_TABS) {
return;
}
TAILQ_FOREACH(tab, &nb->tabs, tabs) {
if (tab->label == -1) {
AG_PushTextState();
AG_TextFont(nb->tabFont);
AG_TextColor(NOTEBOOK_TXT_COLOR);
tab->label = AG_WidgetMapSurface(nb,
AG_TextRender(tab->labelText));
AG_PopTextState();
}
r.x = x;
r.y = y;
r.w = WSURFACE(nb,tab->label)->w + SPACING*2;
r.h = nb->bar_h - SPACING;
if (r.x+r.w > WIDTH(nb)) {
if ((r.w = WIDTH(nb) - r.x) <=
nb->lblPartialWidth + SPACING*2) {
break;
}
if (nb->lblPartial == -1) {
AG_PushTextState();
AG_TextFont(nb->tabFont);
AG_TextColor(NOTEBOOK_TXT_COLOR);
nb->lblPartial = AG_WidgetMapSurface(nb,
AG_TextRender("..."));
AG_PopTextState();
}
STYLE(nb)->NotebookTabBackground(nb, r, idx,
(nb->sel_tab == tab));
AG_WidgetBlitSurface(nb, nb->lblPartial, x+SPACING,
y+(r.h/2 - WSURFACE(nb,nb->lblPartial)->h/2));
break;
}
STYLE(nb)->NotebookTabBackground(nb, r, idx++,
(nb->sel_tab == tab));
AG_WidgetBlitSurface(nb, tab->label, x+SPACING,
y+(r.h/2 - WSURFACE(nb,tab->label)->h/2));
x += r.w;
}
}
static void
SizeRequest(void *obj, AG_SizeReq *r)
{
AG_Notebook *nb = obj;
AG_NotebookTab *tab;
AG_SizeReq rTab;
if ((nb->flags & AG_NOTEBOOK_HIDE_TABS) == 0) {
nb->bar_h = agTextFontHeight + SPACING*2;
nb->bar_w = SPACING*2;
} else {
nb->bar_h = 0;
nb->bar_w = 0;
}
nb->cont_w = 0;
nb->cont_h = 0;
TAILQ_FOREACH(tab, &nb->tabs, tabs) {
AG_WidgetSizeReq(tab, &rTab);
nb->cont_w = MAX(nb->cont_w,rTab.w);
nb->cont_h = MAX(nb->cont_h,rTab.h);
if ((nb->flags & AG_NOTEBOOK_HIDE_TABS) == 0)
nb->bar_w += SPACING*2;
/* + WSURFACE(nb,tab->label)->w */
}
r->h = nb->cont_h + nb->bar_h;
r->w = MAX(nb->cont_w, nb->bar_w);
}
static int
SizeAllocate(void *obj, const AG_SizeAlloc *a)
{
AG_Notebook *nb = obj;
AG_NotebookTab *tab;
AG_SizeAlloc aTab;
if (a->h < nb->bar_h) {
return (-1);
}
if ((tab = nb->sel_tab) != NULL) {
aTab.x = 0;
aTab.y = nb->bar_h;
aTab.w = a->w;
aTab.h = a->h - nb->bar_h;
AG_WidgetSizeAlloc(tab, &aTab);
}
nb->r.x = 0;
nb->r.y = nb->bar_h;
nb->r.w = a->w;
nb->r.h = a->h - nb->bar_h;
return (0);
}
void
AG_NotebookSetTabAlignment(AG_Notebook *nb, enum ag_notebook_tab_alignment ta)
{
AG_ObjectLock(nb);
nb->tab_align = ta;
AG_ObjectUnlock(nb);
}
void
AG_NotebookSetSpacing(AG_Notebook *nb, int spacing)
{
AG_ObjectLock(nb);
nb->spacing = spacing;
AG_ObjectUnlock(nb);
}
void
AG_NotebookSetTabFont(AG_Notebook *nb, AG_Font *font)
{
AG_ObjectLock(nb);
nb->tabFont = font;
AG_TextSize("...", &nb->lblPartialWidth, NULL);
AG_ObjectUnlock(nb);
}
void
AG_NotebookSetPadding(AG_Notebook *nb, int padding)
{
AG_ObjectLock(nb);
nb->padding = padding;
AG_ObjectUnlock(nb);
}
AG_NotebookTab *
AG_NotebookAddTab(AG_Notebook *nb, const char *label, enum ag_box_type btype)
{
AG_NotebookTab *tab;
tab = Malloc(sizeof(AG_NotebookTab));
AG_ObjectInit(tab, &agNotebookTabClass);
AG_BoxSetType(&tab->box, btype);
AG_Expand(tab);
AG_ObjectLock(nb);
if (nb->padding >= 0)
AG_BoxSetPadding(&tab->box, nb->padding);
if (nb->spacing >= 0)
AG_BoxSetSpacing(&tab->box, nb->spacing);
Strlcpy(tab->labelText, label, sizeof(tab->labelText));
tab->label = -1;
TAILQ_INSERT_TAIL(&nb->tabs, tab, tabs);
if (TAILQ_FIRST(&nb->tabs) == tab)
AG_NotebookSelectTab(nb, tab);
AG_ObjectUnlock(nb);
return (tab);
}
void
AG_NotebookDelTab(AG_Notebook *nb, AG_NotebookTab *tab)
{
AG_ObjectLock(nb);
TAILQ_REMOVE(&nb->tabs, tab, tabs);
AG_WidgetUnmapSurface(nb, tab->label);
AG_ObjectDestroy(tab);
AG_ObjectUnlock(nb);
}
void
AG_NotebookSelectTab(AG_Notebook *nb, AG_NotebookTab *tab)
{
AG_Window *pwin;
AG_SizeReq rTab;
AG_SizeAlloc aTab;
AG_LockVFS(agView);
AG_ObjectLock(nb);
if ((pwin = AG_WidgetParentWindow(nb)) == NULL) {
/* AG_FatalError("AG_Notebook: No window is attached"); */
goto out;
}
if (nb->sel_tab != NULL) {
AG_WidgetHiddenRecursive(nb->sel_tab);
AG_ObjectDetach(nb->sel_tab);
OBJECT(nb->sel_tab)->flags &= ~(AG_OBJECT_NAME_ONATTACH);
}
if (tab == NULL) {
nb->sel_tab = NULL;
goto out;
}
AG_ObjectAttach(nb, tab);
AG_SetStyle(nb, WIDGET(pwin)->style);
nb->sel_tab = tab;
AG_WidgetSizeReq(tab, &rTab);
aTab.x = 0;
aTab.y = nb->bar_h;
aTab.w = WIDGET(nb)->w;
aTab.h = WIDGET(nb)->h - nb->bar_h;
AG_WidgetSizeAlloc(tab, &aTab);
AG_WidgetShownRecursive(tab);
AG_WindowUpdate(pwin);
/* AG_WidgetFocus(tab); */
out:
AG_ObjectUnlock(nb);
AG_UnlockVFS(agView);
}
void
AG_NotebookSetTabVisibility(AG_Notebook *nb, int flag)
{
AG_ObjectLock(nb);
AG_SETFLAGS(nb->flags, AG_NOTEBOOK_HIDE_TABS, flag);
AG_ObjectUnlock(nb);
}
AG_WidgetClass agNotebookClass = {
{
"Agar(Widget:Notebook)",
sizeof(AG_Notebook),
{ 0,0 },
Init,
NULL, /* free */
NULL, /* destroy */
NULL, /* load */
NULL, /* save */
NULL /* edit */
},
Draw,
SizeRequest,
SizeAllocate
};
AG_WidgetClass agNotebookTabClass = {
{
"AG_Widget:AG_Box:AG_NotebookTab",
sizeof(AG_NotebookTab),
{ 0,0 },
NULL, /* init */
NULL, /* free */
NULL, /* destroy */
NULL, /* load */
NULL, /* save */
NULL /* edit */
},
AG_WidgetInheritDraw,
AG_WidgetInheritSizeRequest,
AG_WidgetInheritSizeAllocate
};