/*	Public domain	*/
/*
 * This application displays a set of standard Agar-GUI widgets.
 */

#include "agartest.h"

#include <stdio.h>
#include <math.h>
#include <string.h>

#include <agar/core/types.h>

#include <agar/config/ag_debug.h>
#include <agar/config/version.h>

typedef struct {
	AG_TestInstance _inherit;
	char textBuffer[128];
	char *someText;
	AG_TextElement *textElement;
} MyTestInstance;

static void
ComboSelectedItem(AG_Event *event)
{
	AG_TlistItem *ti = AG_TLISTITEM_PTR(1);

	AG_TextTmsg(AG_MSG_INFO, 1000,
	    "Selected item: [ " AGSI_ITALIC "%s" AGSI_RST " ]", ti->text);
}

static void
ComboEnteredText(AG_Event *event)
{
	const char *text = AG_STRING(1);

	AG_TextTmsg(AG_MSG_INFO, 1000,
	    "Entered text: \" " AGSI_ITALIC "%s" AGSI_RST " \"", text);
}

/* Suggested "GUI Preferences" dialog. */
static void
Preferences(AG_Event *event)
{
	AG_DEV_ConfigShow();
}

static void
SetWordWrap(AG_Event *event)
{
	AG_Textbox *textbox = AG_TEXTBOX_PTR(1);
	int flag = AG_INT(2);

	AG_TextboxSetWordWrap(textbox, flag);
}

static void
TestMenuFn(AG_Event *event)
{
	const char *text = AG_STRING(1);

	AG_TextMsgS(AG_MSG_INFO, text);
}

static void
TableKeyDown(AG_Event *event)
{
	AG_Table *t = AG_TABLE_SELF();
	const int keysym = AG_INT(1);
	int m;

	switch (keysym) {
	case AG_KEY_DELETE:
		for (m = 0; m < t->m; m++) {
			if (AG_TableRowSelected(t,m))
				break;
		}
		if (m < t->m) {
			AG_Debug(t, "Deleting row # %d\n", m);
		}
		break;
	}
}

static void
SayHello(AG_Event *event)
{
	AG_Textbox *tb = AG_TEXTBOX_SELF();
	char *who = AG_TextboxDupString(tb);

	AG_TextTmsg(AG_MSG_INFO, 2000, "Hello, %s!", who);

	Free(who);
}

/*
 * Sample autocomplete routine for Textbox. Parse "First Last" or "First,Last"
 * and provide different suggestions for first and last names based on two
 * separate dictionaries.
 */
static void
AutocompleteName(AG_Event *event)
{
	const char *dictFirst[] = {
		"Agnes", "Apu", "Artie", "Barbara", "Barry", "Bart", "Bernice",
		"Brandine", "Carl", "Cecil", "Cletus", "Count", "Disco",
		"Dondelinger", "Doris", "Drederick", "Eleanor", "Gerald", "Gil",
		"Gino", "Herman", "Homer", "Jasper", "Jebediah", "Jimbo",
		"Julius", "Kent", "Kirk", "Lenny", "Lisa", "Maggie", "Marge",
		"Martha", "Martin", "Manjula", "Marvin", "Maude", "Moe", "Murphy",
		"Ned", "Rainier", "Robert", "Rod", "Sarah", "Sideshow", "Todd",
		"\xC3\x9Cter", NULL
	}, **dp;
	const char *dictLast[] = {
		"Abernathy", "Beardly", "Bouvier", "Brockman", "Carlson",
		"Dracula", "Duffman", "Flanders", "Gunderson", "Harlan",
		"Hibbert", "Hermann", "Jones", "Krustofsky", "Moleman",
		"Monroe", "Nahasapeemapetilon", "Leonard", "Mel", "Prince",
		"Rollolinski", "Samson", "Simpson", "Skinner", "Springfield",
		"Spuckler", "Szyslak", "Stu", "Tatum", "Terwilliger",
		"Z\xC3\xB6rker", "Van Houten", "Wiggum", "Wolfcastle", "Ziff",
		NULL
	};
	AG_Editable *ed = AG_EDITABLE_SELF();
	AG_Tlist *tl = AG_TLIST_PTR(1);
	char *s = AG_EditableDupString(ed), *sp = s;
	const char *sFirst, *sLast;
	AG_TlistItem *it;
	int nSpaces=0;

	while (*sp == ' ' || *sp == '\t') {
		sp++;
	}
	sFirst = AG_Strsep(&sp, " ,");
	do {
		sLast = AG_Strsep(&sp, " ,");
		nSpaces++;
	} while (sLast != NULL && *sLast == '\0');

	AG_TlistBegin(tl);

	if (sFirst[0] == '\0' || sFirst[0] == '*') {
		for (dp = dictFirst; *dp != NULL; dp++)
			AG_TlistAddPtr(tl, NULL, *dp, (void *)*dp);
	} else if (sLast != NULL) {
		if (sLast[0] == '\0' || sLast[0] == '*') {
			for (dp = dictLast; *dp != NULL; dp++) {
				it = AG_TlistAdd(tl, NULL, "%s %s", sFirst, *dp);
				it->p1 = (void *)*dp;
			}
		} else {
			for (dp = dictLast; *dp != NULL; dp++) {
				if (AG_Strncasecmp(*dp, sLast, (AG_Size)strlen(sLast)) == 0) {
					it = AG_TlistAdd(tl, NULL, "%s %s", sFirst, *dp);
					it->p1 = (void *)*dp;
				}
			}
		}
	} else {
		if (nSpaces > 1) {
			for (dp = dictLast; *dp != NULL; dp++) {
				it = AG_TlistAdd(tl, NULL, "%s %s", sFirst, *dp);
				it->p1 = (void *)*dp;
			}
		} else {
			for (dp = dictFirst; *dp != NULL; dp++)
				if (AG_Strncasecmp(*dp, sFirst, (AG_Size)strlen(sFirst)) == 0)
					AG_TlistAddPtr(tl, NULL, *dp, (void *)*dp);
		}
	}

	AG_TlistEnd(tl);

	if (tl->nItems == 0) {
		AG_EditableCloseAutocomplete(ed);
	} else if (tl->nItems == 1) {
		char *sOrig = AG_EditableDupString(ed);

		if (AG_TlistFindText(tl, sOrig)) {
			AG_EditableCloseAutocomplete(ed);
		}
		Free(sOrig);
	}
	Free(s);
}

/* Create more checkboxes under "Some checkboxes" */
static void
CreateMoreCheckboxes(AG_Event *_Nonnull event)
{
	AG_Button *bu = AG_BUTTON_SELF();
	AG_Box *box = AG_BOX_PTR(1);
	AG_Pane *pane = AG_PANE_PTR(2);

	AG_WidgetDisable(bu);

	AG_CheckboxNew(box, 0, "George Liquor");
	AG_CheckboxNew(box, 0, "Haggis McHaggis");
	AG_CheckboxNew(box, 0, "Kowalski, Bubba\n"
	                       "and Jiminy Lummox");
	AG_CheckboxNew(box, 0, "Wilbern Cobb");

	AG_PaneMoveDivider(pane, pane->dx + 50);
	AG_SetFontSize(box, "80%");
}

static void
ComboExpanded(AG_Event *_Nonnull event)
{
	AG_Combo *com = AG_COMBO_SELF();
	AG_Tlist *tl = com->list;
	int i;

	for (i = 0; i < 50; i++) {
		char text[32];

		AG_Strlcpy(text, "Item #", sizeof(text));
		AG_StrlcatInt(text, i, sizeof(text));

		AG_TlistAddS(tl, NULL, text);
	}

	AG_TlistSelectIdx(tl, 1);
}

static void
UComboExpanded(AG_Event *_Nonnull event)
{
	AG_UCombo *com = AG_UCOMBO_SELF();
	AG_Tlist *tl = com->list;

	AG_TlistAddS(tl, NULL, "George Liquor");
	AG_TlistAddS(tl, NULL, "Haggis McHaggis");
	AG_TlistAddS(tl, NULL, "Kowalski (Lummox)");
	AG_TlistAddS(tl, NULL, "Bubba (Lummox)");
	AG_TlistAddS(tl, NULL, "Jiminy (Lummox)");
	AG_TlistAddS(tl, NULL, "Wilbern Cobb");

	AG_TlistSizeHintLargest(tl, 6);
}

static int
TestGUI(void *obj, AG_Window *win)
{
	char path[AG_PATHNAME_MAX];
	MyTestInstance *ti = obj;
	AG_Box *hBox, *vBox, *div, *divRight;
	AG_Pane *hPane, *vPane;
	AG_Combo *com;
	AG_UCombo *ucom;
	AG_Textbox *tbox;
	AG_Surface *S;
	int i;

	/*
	 * Pane provides two Box containers which can be resized using
	 * a control placed in the middle.
	 */
	hPane = AG_PaneNewHoriz(win, AG_PANE_EXPAND);
	AG_PaneSetDivisionMin(hPane, 0, 50, 100);
/*	AG_PaneMoveDividerPct(hPane, 45); */
	div = hPane->div[0];
	divRight = hPane->div[1];

	hBox = AG_BoxNewHoriz(div, AG_BOX_HFILL);
	{
		/* Load an Indexed PNG file and display it in an AG_Pixmap(3). */
		if (AG_ConfigFind(AG_CONFIG_PATH_DATA, "parrot8.png",
		    path, sizeof(path)) == 0) {
			if ((S = AG_SurfaceFromPNG(path)) == NULL) {
				S = AG_TextRender(AG_GetError());
				AG_PixmapFromSurface(hBox, 0, S);
				AG_SurfaceFree(S);
			}
			AG_PixmapFromSurface(hBox, AG_PIXMAP_RESCALE, S);
			AG_SurfaceFree(S);
		}

		/* Load a 32bpp BMP file and display it in an AG_Pixmap(3). */
		if (AG_ConfigFind(AG_CONFIG_PATH_DATA, "agar-1.bmp",
		    path, sizeof(path)) == 0) {
			if ((S = AG_SurfaceFromBMP(path)) == NULL) {
				S = AG_TextRender(AG_GetError());
			}
			AG_PixmapFromSurface(hBox, AG_PIXMAP_RESCALE, S);
			AG_SurfaceFree(S);
		} else {
			S = AG_TextRender(AG_GetError());
			AG_PixmapFromSurface(hBox, 0, S);
			AG_SurfaceFree(S);
		}
#if AG_MODEL == AG_LARGE
		/* Load a 16bpc PNG file and display it in an AG_Pixmap(3). */
		if (AG_ConfigFind(AG_CONFIG_PATH_DATA, "agar64.png",
		    path, sizeof(path)) == 0) {
			if ((S = AG_SurfaceFromPNG(path)) == NULL) {
				S = AG_TextRender(AG_GetError());
				AG_PixmapFromSurface(hBox, 0, S);
				AG_SurfaceFree(S);
			}
			AG_PixmapFromSurface(hBox, AG_PIXMAP_RESCALE, S);
			AG_SurfaceFree(S);
		}
#endif
	}

	AG_SeparatorNewHoriz(div);

	/*
	 * AG_Label(3) displays either a static text label, or a dynamically
	 * updated one. Polled (dynamic) labels use a special format documented
	 * in AG_String(3).
	 */
	{
		AG_AgarVersion av;
		AG_Label *lbl;

		AG_GetVersion(&av);

		/* A static label */
		lbl = AG_LabelNew(div, AG_LABEL_HFILL,
		    "Agar v%d.%d.%d ("
		    AGSI_LEAGUE_SPARTAN AGSI_CYAN "%s" AGSI_RST ")",
		    av.major, av.minor, av.patch,
		    av.release ? av.release : "dev");
		AG_SetFontSize(lbl, "120%");
		AG_LabelJustify(lbl, AG_TEXT_CENTER);

		/* A dynamically-updated label. */
		lbl = AG_LabelNewPolled(div, AG_LABEL_HFILL,
		    "This "
		    AGSI_ALGUE AGSI_BLACK_AGAR AGSI_RST
		    " Window is at %i,%i (%u x %u).",
		    &AGWIDGET(win)->x,
		    &AGWIDGET(win)->y,
		    &AGWIDGET(win)->w,
		    &AGWIDGET(win)->h);
		AG_LabelSizeHint(lbl, 1, "<This [XXXX] Window is at 0000,0000 (0000 x 0000). >");
		AG_SetFontSize(lbl, "110%");

		/* Show an example of a bitmap font (agar-minimal.agbf) */
		AG_LabelNew(div, AG_LABEL_HFILL,
		    "Here is a bitmap font: " 
		    AGSI_AGAR_MINIMAL "Agar Minimal Hello!" AGSI_RST
		    " (12px)");
	}

	/*
	 * Pane provides two Box containers which can be resized using
	 * a control placed in the middle.
	 */
	vPane = AG_PaneNewVert(div, AG_PANE_EXPAND);
	div = vPane->div[0];

	/*
	 * Box is a general-purpose widget container. AG_BoxNewHoriz() creates
	 * a container which packs its widgets horizontally.
	 */
	hBox = AG_BoxNewHoriz(div, AG_BOX_HOMOGENOUS | AG_BOX_HFILL);
	{
		/*
		 * The Button widget is a simple push-button. It is typically
		 * used to trigger events, but it can also bind its state to
		 * an boolean (integer) value or a bitmask.
		 */
		for (i = 0; i < 5; i++)
			AG_ButtonNewS(hBox, 0, AG_Printf("%c", 0x41+i));
	}

	hBox = AG_BoxNewHoriz(div, AG_BOX_HFILL);
	AG_BoxSetHorizAlign(hBox, AG_BOX_CENTER);
	AG_BoxSetVertAlign(hBox, AG_BOX_CENTER);
	{
		/* Radio button group */
		vBox = AG_BoxNewVert(hBox, 0);
		AG_BoxSetLabel(vBox, "Radio group:");
		{
			const char *radioItems[] = {
				"Homer\n"
				"(" AGSI_ITALIC "Simpson" AGSI_RST ")",
				"Marge",
				"Bart",
				"Lisa",
				"Maggie",
				NULL
			};
			AG_RadioNew(vBox, 0, radioItems);
		}
	
		vBox = AG_BoxNewVert(hBox, 0);
		AG_BoxSetLabel(vBox, "Checkboxes:");
		{
			AG_Button *btn;

			/*
			 * The Checkbox widget can bind to boolean values
			 * and bitmasks.
			 */
			AG_CheckboxNew(vBox, 0, "Ren H" "\xC3\xB6" "ek");
			AG_CheckboxNew(vBox, 0, "Stimpson "
			                        AGSI_ITALIC "J." AGSI_RST
						" Cat");
			AG_CheckboxNew(vBox, 0, "Mr. " AGSI_BOLD "Horse" AGSI_RST);
			AG_CheckboxNew(vBox, 0, AGSI_YEL AGSI_BOLD "P" AGSI_RST
			                        "owdered "
						AGSI_YEL AGSI_BOLD "T" AGSI_RST
						"oast "
						AGSI_YEL AGSI_BOLD "M" AGSI_RST
						"an");

			btn = AG_ButtonNewFn(vBox, 0, "Create More ...",
			    CreateMoreCheckboxes, "%p,%p", vBox, vPane);

			AG_SetPadding(btn, "2");
		}
	}

	div = vPane->div[1];

	/*
	 * The Combo widget is a textbox widget with a expander button next
	 * to it. The button triggers a popup window which displays a list
	 * (using the AG_Tlist(3) widget).
	 */
	com = AG_ComboNew(div, AG_COMBO_ANY_TEXT | AG_COMBO_HFILL, "Combo: ");
	AG_ComboSizeHint(com, "<Item #000>", 10);
	AG_SetEvent(com, "combo-expanded", ComboExpanded, NULL);
	AG_SetEvent(com, "combo-selected", ComboSelectedItem, NULL);
	AG_SetEvent(com, "combo-text-entry", ComboEnteredText, NULL);

	/* UCombo is a variant of Combo which looks like a single button. */
	ucom = AG_UComboNew(div, AG_UCOMBO_HFILL);
	AG_UComboSizeHint(ucom, "<Item #1234>", 5);
	AG_SetEvent(ucom, "ucombo-expanded", UComboExpanded, NULL);

	/* Create a horizontal separator */
	AG_SeparatorNewHoriz(div);

	/*
	 * Textbox is a single or multiline text edition widget. It can bind
	 * to fixed-size or dynamically-sized buffers in any character set
	 * encoding.
	 */
	ti->textBuffer[0] = '\0';
	ti->textElement = AG_TextNew(100);

	/* Create a textbox bound to a fixed-size buffer */
	tbox = AG_TextboxNew(div, AG_TEXTBOX_EXCL | AG_TEXTBOX_HFILL, "Textbox: ");
	AG_TextboxSetPlaceholderS(tbox, "First & Last Name");
	AG_TextboxAutocomplete(tbox, AutocompleteName, NULL);
	AG_TextboxBindUTF8(tbox, ti->textBuffer, sizeof(ti->textBuffer));
	AG_SetEvent(tbox, "textbox-return", SayHello, "%p", tbox);

	/*
	 * Create a textbox connected to a dynamically-sized, multilingual
	 * text element.
	 */
	tbox = AG_TextboxNew(div, AG_TEXTBOX_EXCL | AG_TEXTBOX_MULTILINGUAL |
	                          AG_TEXTBOX_HFILL, "TextElement: ");
	AG_TextboxSetString(tbox, "Hello");
	AG_TextSetEntS(ti->textElement, AG_LANG_EN, "Hello");
	AG_TextSetEntS(ti->textElement, AG_LANG_FR, "Bonjour");
	AG_TextSetEntS(ti->textElement, AG_LANG_DE, "Guten tag");
	AG_TextboxBindText(tbox, ti->textElement);

	/* Create a horizontal separator */
	AG_SeparatorNewHoriz(div);

	/*
	 * Numerical binds to an integral or floating-point number.
	 * It can also provides built-in unit conversion (see AG_Units(3)).
	 */
	{
		AG_Numerical *num;
		static float myFloat = 1.0f;
		static int myInt = 50;

		num = AG_NumericalNewS(div,
		    AG_NUMERICAL_EXCL | AG_NUMERICAL_HFILL,
		    "cm", "Numerical (flt): ");
		AG_BindFloat(num, "value", &myFloat);
		AG_SetFloat(num, "inc", 1.0f);

		num = AG_NumericalNewS(div,
		    AG_NUMERICAL_EXCL | AG_NUMERICAL_HFILL,
		    NULL, "Numerical (int): ");
		AG_BindInt(num, "value", &myInt);
		AG_SetInt(num, "min", -100);
		AG_SetInt(num, "max", +100);
	}

	/* Create a horizontal separator */
	AG_SeparatorNewHoriz(div);

	/*
	 * Scrollbar provides "value", "min" and "max" bindings which can be
	 * connected to integers or floating-point variables. Progressbar and
	 * Slider offer similar interfaces.
	 */
	{
		static int myVal = 50, myMin = -100, myMax = 100, myVisible = 0;
		AG_Scrollbar *sb;
		AG_ProgressBar *pb;
		AG_Numerical *num;

		num = AG_NumericalNewS(div, AG_NUMERICAL_HFILL, NULL,
		    "Bound Integer: ");
		AG_BindInt(num, "value", &myVal);
		AG_SetInt(num, "min", -100);
		AG_SetInt(num, "max", +100);

		AG_LabelNewS(div, 0, "Scrollbar:");
		sb = AG_ScrollbarNewHoriz(div, AG_SCROLLBAR_EXCL |
		                               AG_SCROLLBAR_HFILL);
		AG_BindInt(sb, "value", &myVal);
		AG_BindInt(sb, "min", &myMin);
		AG_BindInt(sb, "max", &myMax);
		AG_BindInt(sb, "visible", &myVisible);
		AG_SetInt(sb, "inc", 10);

		AG_LabelNewS(div, 0, "ProgressBar (Horiz | Vert):");

		hBox = AG_BoxNewHoriz(div, AG_BOX_HFILL);

		pb = AG_ProgressBarNewHoriz(hBox, AG_PROGRESS_BAR_EXCL |
		                                  AG_PROGRESS_BAR_SHOW_PCT |
		                                  AG_PROGRESS_BAR_HFILL);
		AG_BindInt(pb, "value", &myVal);
		AG_BindInt(pb, "min", &myMin);
		AG_BindInt(pb, "max", &myMax);

		AG_SeparatorNewVert(hBox);

		pb = AG_ProgressBarNewVert(hBox, AG_PROGRESS_BAR_EXCL |
		                                 AG_PROGRESS_BAR_SHOW_PCT);
		AG_ProgressBarSetLength(pb, 50);
		AG_BindInt(pb, "value", &myVal);
		AG_BindInt(pb, "min", &myMin);
		AG_BindInt(pb, "max", &myMax);
	}

	/*
	 * Load the square agar logo, perform pixel manipulation on the loaded
	 * surface (divide alpha of red-dominant pixels) and display the results
	 * in AG_Pixmap(3) widgets.
	 */
	if (AG_ConfigFind(AG_CONFIG_PATH_DATA, "sq-agar.bmp", path, sizeof(path)) == 0) {
		AG_Surface *S;
		int i, x,y;

		S = AG_SurfaceFromFile(path);

		hBox = AG_BoxNewHoriz(div, AG_BOX_HOMOGENOUS | AG_BOX_HFILL);
		for (i = 0; i < 5; i++) {
			for (y = 0; y < (int)S->h; y++) {
				for (x = 0; x < (int)S->w; x++) {
					AG_Pixel px = AG_SurfaceGet(S, x,y);
					AG_Color c ;
						
					AG_GetColor(&c, px, &S->format);
					if (c.r > AG_MAX(c.g, c.b)) {
						c.a /= (1+i);
					}
					AG_SurfacePut(S, x,y,
					    AG_MapPixel(&S->format, &c));
				}
			}
			AG_PixmapFromSurface(hBox, 0, S);
		}
		AG_SurfaceFree(S);
	}

	/*
	 * Notebook provides multiple containers which can be selected by
	 * the user.
	 */
	{
		AG_Notebook *nb;
		AG_NotebookTab *nt;
		AG_Table *table;
		AG_Menu *menu;
		AG_MenuItem *m, *mSub;
		static int myInt[2];
	
		/* Create a test menu */
		menu = AG_MenuNew(divRight, AG_MENU_HFILL);
		m = AG_MenuNode(menu->root, "File", NULL);
		{
			AG_MenuActionKb(m,
			    AGSI_IDEOGRAM AGSI_CLOSE_X AGSI_RST
			    " Close Test", NULL,
			    AG_KEY_W, AGSI_WINCMD,
			    AGWINCLOSE(win));
		}

		m = AG_MenuNode(menu->root, "Edit", NULL);
		{
			AG_MenuActionKb(m,
			    AGSI_IDEOGRAM AGSI_GEAR AGSI_RST
			    " Preferences...", NULL,
			    AG_KEY_P, AGSI_WINCMD,
			    Preferences, NULL);
		}

		m = AG_MenuNode(menu->root, "Test Menu", NULL);
		{
			AG_MenuState(m, 0);
			AG_MenuNode(m,
			    AGSI_IDEOGRAM AGSI_DIP_CHIP AGSI_RST
			    " Disabled Submenu",
			    NULL);
			AG_MenuState(m, 1);
			AG_MenuSeparator(m);

			/* Menu item with child entries */
			mSub = AG_MenuNode(m,
			    AGSI_IDEOGRAM AGSI_GAME_CONTROLLER AGSI_RST
			    " Try Me!",
			    NULL);

			AG_MenuAction(mSub,
			    AGSI_IDEOGRAM AGSI_BELL_JAR_W_VACUUM AGSI_RST
			    " Bell Jar",
			    NULL,
			    TestMenuFn, "%s", "Bell Jar "
			    AGSI_IDEOGRAM AGSI_BELL_JAR_W_VACUUM);

			AG_MenuAction(mSub,
			    AGSI_IDEOGRAM AGSI_EDGAR_ALLAN_POE AGSI_RST
			    " Edgar Allan Poe",
			    NULL,
			    TestMenuFn, "%s", "Edgar Allan Poe "
			    AGSI_IDEOGRAM AGSI_EDGAR_ALLAN_POE);

			AG_MenuSeparator(m);

			AG_MenuIntBool(mSub, "Togglable Binding #1 ",
			    NULL, &myInt[0], 0);
			AG_MenuIntBool(mSub, "Inverted Binding #1 ",
			    NULL, &myInt[0], 1);

			AG_MenuSectionS(m,
			    AGSI_IDEOGRAM AGSI_MAMISMOKE AGSI_RST
			    AGSI_ITALIC " Non Selectable Text");
			AG_MenuSeparator(m);

			AG_MenuIntBool(m,
			    "Togglable Binding #1\t"
			    AGSI_IDEOGRAM AGSI_AGAR_AG AGSI_RST,
			    NULL, &myInt[0], 0);
			AG_MenuIntBool(m,
			    "Togglable Binding #2\t"
			    AGSI_IDEOGRAM AGSI_AGAR_AR AGSI_RST,
			    NULL, &myInt[1], 0);

			AG_MenuIntBool(m, "Inverted Binding #2", NULL,
			    &myInt[1], 1);
		}

		AG_LabelNewS(divRight, 0, "Here is a Notebook:");

		nb = AG_NotebookNew(divRight, AG_NOTEBOOK_EXPAND);

		nt = AG_NotebookAdd(nb,
		    "A Table\n"
		    AGSI_IDEOGRAM AGSI_MATH_X_EQUALS AGSI_RST
		    AGSI_ITALIC "sin" AGSI_RST "(x)",
		    AG_BOX_VERT);
		{
			float f;

			/*
			 * AG_Table displays a set of cells organized in
			 * rows and columns. It is optimized for cases where
			 * the table is static or needs to be repopulated
			 * periodically.
			 */
			table = AG_TableNew(nt, AG_TABLE_EXPAND | AG_TABLE_MULTI);

			AG_TableAddCol(table, "x", "33%", NULL);
			AG_TableAddCol(table, "sin(x)", "33%", NULL);
			AG_TableAddCol(table, "cos(x)", "33%", NULL);

			AG_SetFontFamily(table, "charter");
			AG_SetFontSize(table, "120%");
			AG_SetColor(table, "#666");

			for (f = 0.0f; f < 60.0f; f += 0.3f) {
				/*
				 * Insert a Table row for sin(f) and cos(f).
				 * The directives of the format string are
				 * documented in AG_Table(3).
				 */
				AG_TableAddRow(table, "%.02f:%.02f:%.02f",
				    f, sin(f), cos(f));
			}
			{
				const char *selModes[] = {
				    "Select\nby\nRow",
				    "Select\nby\nCell",
				    "Select\nby\nColumn",
				    NULL
				};
				AG_Radio *rad;

				rad = AG_RadioNewUint(nt, 0, selModes,
				    (Uint *)&table->selMode);
				AG_RadioSetDisposition(rad, AG_RADIO_HORIZ);
				AG_SetFontSize(rad, "80%");
			}
			

			vBox = AG_BoxNewVert(nt, 0);
			AG_SetFontSize(vBox, "80%");
			{
				AG_CheckboxNewFlag(vBox, 0, "Select multiple\n(with ctrl/shift)",
				    &table->flags, AG_TABLE_MULTI);

				AG_CheckboxNewFlag(vBox, 0, "Select multiple always",
				    &table->flags, AG_TABLE_MULTITOGGLE);

				AG_CheckboxNewFlag(vBox, 0, "Highlight columns",
				    &table->flags, AG_TABLE_HIGHLIGHT_COLS);
			}

			/*
			 * Append an event handler for the DELETE function.
			 */
			AG_AddEvent(table, "key-down", TableKeyDown, NULL);
		}
		
		nt = AG_NotebookAdd(nb,
		    AGSI_WHT AGSI_ITALIC "\"Loss of Breath\"" AGSI_RST
		    AGSI_LEAGUE_GOTHIC "\nBy Edgar Allan Poe  " AGSI_RST
		    AGSI_IDEOGRAM AGSI_EDGAR_ALLAN_POE AGSI_RST,
		    AG_BOX_VERT);
		{
			char path[AG_PATHNAME_MAX];
			AG_Size size, bufSize;
			FILE *f;

			/*
			 * Textboxes with the MULTILINE flag provide basic
			 * text edition functionality. The CATCH_TAB flag
			 * causes the widget to receive TAB key events
			 * (normally used to focus other widgets).
			 */
			tbox = AG_TextboxNewS(nt,
			    AG_TEXTBOX_MULTILINE | AG_TEXTBOX_CATCH_TAB |
			    AG_TEXTBOX_EXPAND | AG_TEXTBOX_EXCL, NULL);
			AG_WidgetSetFocusable(tbox, 1);

			/*
			 * Load the contents of this file into a buffer. Make
			 * the buffer a bit larger so the user can try
			 * entering text.
			 */
			if (AG_ConfigFind(AG_CONFIG_PATH_DATA, "loss.txt",
			    path, sizeof(path)) == 0) {
				if ((f = fopen(path, "r")) != NULL) {
					fseek(f, 0, SEEK_END);
					size = ftell(f);
					fseek(f, 0, SEEK_SET);
					bufSize = size + 4096;
					ti->someText = AG_Malloc(bufSize);
					(void)fread(ti->someText, size, 1, f);
					fclose(f);
					ti->someText[size] = '\0';
				} else {
					ti->someText = Strdup(path);
					bufSize = (AG_Size)strlen(ti->someText)+1;
				}
			} else {
				ti->someText = AG_Strdup("loss.txt not found");
				bufSize = (AG_Size)strlen(ti->someText)+1;
			}
	
			/*
			 * Bind the buffer's contents to the Textbox. The
			 * size argument to AG_TextboxBindUTF8() must include
			 * space for the terminating NUL.
			 */
			AG_TextboxBindUTF8(tbox, ti->someText, bufSize);
	
			/* Add a word wrapping control */
			AG_CheckboxNewFn(nt, 0, "Word wrapping",
			    SetWordWrap, "%p", tbox);
		}
		AG_NotebookSelect(nb, nt);

		nt = AG_NotebookAdd(nb, "Colors\n"
		    AGSI_RED AGSI_BOLD "R" AGSI_RST
		    AGSI_GRN AGSI_BOLD "G" AGSI_RST
		    AGSI_BLU AGSI_BOLD "B" AGSI_RST,
		    AG_BOX_VERT);
		AGBOX(nt)->flags |= AG_BOX_HOMOGENOUS;
		AG_SetPadding(nt, "10");
		{
			AG_HSVPal *pal;
			const Uint flags = AG_HSVPAL_HFILL | AG_HSVPAL_SHOW_RGB;

			pal = AG_HSVPalNew(nt, flags);
			pal->h = 0.0f;
			pal->s = 1.0f;
			pal->v = 1.0f;
			pal = AG_HSVPalNew(nt, flags);
			pal->h = 120.0f;
			pal->s = 1.0f;
			pal->v = 1.0f;
			pal = AG_HSVPalNew(nt, flags);
			pal->h = 240.0f;
			pal->s = 1.0f;
			pal->v = 1.0f;
		}
	}
	return (0);
}

static int
Init(void *obj)
{
	MyTestInstance *ti = obj;

	ti->textBuffer[0] = '\0';
	ti->someText = NULL;
	return (0);
}

static void
Destroy(void *obj)
{
	MyTestInstance *ti = obj;

	Free(ti->someText);
	AG_TextFree(ti->textElement);
}

const AG_TestCase widgetsTest = {
	AGSI_IDEOGRAM AGSI_POPULATED_WINDOW AGSI_RST,
	"widgets",
	N_("Display various Agar-GUI widgets"),
	"1.6.0",
	0,
	sizeof(MyTestInstance),
	Init,
	Destroy,
	NULL,		/* test */
	TestGUI,
	NULL		/* bench */
};
