Maelstrom: Beginnings of a data driven UI system

From c5cbf2bf7d8c6fc349ec776a85ec93afbc09ac62 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sat, 22 Oct 2011 17:15:45 -0400
Subject: [PATCH] Beginnings of a data driven UI system

---
 Makefile.am                |    4 +-
 UIElements.cpp             |    8 +
 UIElements.h               |    9 +
 init.cpp                   |    3 +
 screenlib/ErrorBase.h      |   66 +
 screenlib/Makefile.am      |   11 +-
 screenlib/SDL_FrameBuf.cpp |   12 +-
 screenlib/SDL_FrameBuf.h   |   37 +-
 screenlib/UIArea.cpp       |  170 +++
 screenlib/UIArea.h         |   95 ++
 screenlib/UIElement.cpp    |   52 +
 screenlib/UIElement.h      |   58 +
 screenlib/UIManager.cpp    |   55 +
 screenlib/UIManager.h      |   51 +
 screenlib/UIPanel.cpp      |  174 +++
 screenlib/UIPanel.h        |   81 ++
 utils/array.h              |   67 +
 utils/rapidxml.h           |   17 +
 utils/rapidxml.hpp         | 2617 ++++++++++++++++++++++++++++++++++++
 19 files changed, 3546 insertions(+), 41 deletions(-)
 create mode 100644 UIElements.cpp
 create mode 100644 UIElements.h
 create mode 100644 screenlib/ErrorBase.h
 create mode 100644 screenlib/UIArea.cpp
 create mode 100644 screenlib/UIArea.h
 create mode 100644 screenlib/UIElement.cpp
 create mode 100644 screenlib/UIElement.h
 create mode 100644 screenlib/UIManager.cpp
 create mode 100644 screenlib/UIManager.h
 create mode 100644 screenlib/UIPanel.cpp
 create mode 100644 screenlib/UIPanel.h
 create mode 100644 utils/array.h
 create mode 100644 utils/rapidxml.h
 create mode 100755 utils/rapidxml.hpp

diff --git a/Makefile.am b/Makefile.am
index 32691048..8c541214 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -28,7 +28,9 @@ Maelstrom_SOURCES =		\
 	rect.cpp		\
 	rect.h			\
 	scores.cpp		\
-	scores.h
+	scores.h		\
+	UIElements.cpp		\
+	UIElements.h
 
 LOGIC = netlogic
 
diff --git a/UIElements.cpp b/UIElements.cpp
new file mode 100644
index 00000000..7079d8a9
--- /dev/null
+++ b/UIElements.cpp
@@ -0,0 +1,8 @@
+
+#include "UIElements.h"
+
+UIElement *
+CreateMaelstromUIElement(UIPanel *panel, const char *name)
+{
+	return NULL;
+}
diff --git a/UIElements.h b/UIElements.h
new file mode 100644
index 00000000..7347f2f6
--- /dev/null
+++ b/UIElements.h
@@ -0,0 +1,9 @@
+#ifndef _UIElements_h
+#define _UIElements_h
+
+#include "screenlib/UIManager.h"
+#include "screenlib/UIPanel.h"
+
+UIElement *CreateMaelstromUIElement(UIPanel *panel, const char *name);
+
+#endif // _UIElements_h
diff --git a/init.cpp b/init.cpp
index c7a67c23..5e112af2 100644
--- a/init.cpp
+++ b/init.cpp
@@ -8,6 +8,7 @@
 #include "load.h"
 #include "colortable.h"
 #include "fastrand.h"
+#include "UIElements.h"
 
 
 // Global variables set in this file...
@@ -15,6 +16,7 @@ Sound    *sound = NULL;
 FontServ *fontserv = NULL;
 MFont    *fonts[NUM_FONTS];
 FrameBuf *screen = NULL;
+UIManager ui;
 
 Sint32	gLastHigh;
 Uint32	gLastDrawn;
@@ -782,6 +784,7 @@ int DoInitializations(Uint32 window_flags, Uint32 render_flags)
 			return(-1);
 		}
 	}
+	UIPanel::SetElementFactory(CreateMaelstromUIElement);
 
 	/* Load the Sound Server and initialize sound */
 	sound = new Sound("Maelstrom Sounds", gSoundLevel);
diff --git a/screenlib/ErrorBase.h b/screenlib/ErrorBase.h
new file mode 100644
index 00000000..6491eb55
--- /dev/null
+++ b/screenlib/ErrorBase.h
@@ -0,0 +1,66 @@
+/*
+    SCREENLIB:  A framebuffer library based on the SDL library
+    Copyright (C) 1997  Sam Lantinga
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Sam Lantinga
+    slouken@libsdl.org
+*/
+
+#ifndef _ErrorBase_h
+#define _ErrorBase_h
+
+#include <string.h>
+#include <stdarg.h>
+
+#define ERRORBASE_ERRSIZ	1024
+
+class ErrorBase {
+
+public:
+	ErrorBase() : errstr(NULL) { }
+	virtual ~ErrorBase() {
+		ClearError();
+	}
+
+	/* Error message routine */
+	char *Error(void) {
+		return(errstr);
+	}
+
+	void ClearError() {
+		if (errstr) {
+			delete[] errstr;
+		}
+		errstr = NULL;
+	}
+
+protected:
+	/* Error message */
+	void SetError(const char *fmt, ...) {
+		va_list ap;
+
+		va_start(ap, fmt);
+		if (!errstr) {
+			errstr = new char[ERRORBASE_ERRSIZ];
+		}
+		vsnprintf(errstr, ERRORBASE_ERRSIZ, fmt, ap);
+		va_end(ap);
+        }
+	char *errstr;
+};
+
+#endif /* _ErrorBase_h */
diff --git a/screenlib/Makefile.am b/screenlib/Makefile.am
index 3c5ce620..55b629de 100644
--- a/screenlib/Makefile.am
+++ b/screenlib/Makefile.am
@@ -2,5 +2,14 @@
 noinst_LIBRARIES = libSDLscreen.a
 
 libSDLscreen_a_SOURCES =	\
+	ErrorBase.h		\
 	SDL_FrameBuf.cpp	\
-	SDL_FrameBuf.h
+	SDL_FrameBuf.h		\
+	UIArea.cpp		\
+	UIArea.h		\
+	UIElement.cpp		\
+	UIElement.h		\
+	UIManager.cpp		\
+	UIManager.h		\
+	UIPanel.cpp		\
+	UIPanel.h
diff --git a/screenlib/SDL_FrameBuf.cpp b/screenlib/SDL_FrameBuf.cpp
index b5e3d9a5..9e97ba20 100644
--- a/screenlib/SDL_FrameBuf.cpp
+++ b/screenlib/SDL_FrameBuf.cpp
@@ -33,12 +33,11 @@
 #define MAX(A, B)	((A > B) ? A : B)
 
 /* Constructors cannot fail. :-/ */
-FrameBuf:: FrameBuf()
+FrameBuf:: FrameBuf() : UIArea()
 {
 	/* Initialize various variables to null state */
 	window = NULL;
 	renderer = NULL;
-	errstr = NULL;
 	faded = 0;
 }
 
@@ -64,11 +63,12 @@ FrameBuf:: Init(int width, int height, Uint32 window_flags, Uint32 render_flags,
 		SDL_SetWindowIcon(window, icon);
 	}
 
+	/* Set the UI area */
+	m_rect.w = width;
+	m_rect.h = height;
+
 	/* Set the blit clipping rectangle */
-	clip.x = 0;
-	clip.y = 0;
-	clip.w = width;
-	clip.h = height;
+	clip = m_rect;
 
 	/* Copy the image colormap */
 	if ( colors ) {
diff --git a/screenlib/SDL_FrameBuf.h b/screenlib/SDL_FrameBuf.h
index 6f2d2bf5..4b2667d4 100644
--- a/screenlib/SDL_FrameBuf.h
+++ b/screenlib/SDL_FrameBuf.h
@@ -30,23 +30,23 @@
 */
 
 #include <stdio.h>
-#include <string.h>
-#include <stdarg.h>
 
 #include "SDL.h"
+#include "ErrorBase.h"
+#include "UIArea.h"
 
 typedef enum {
 	DOCLIP,
 	NOCLIP
 } clipval;
 
-class FrameBuf {
+class FrameBuf : public UIArea {
 
 public:
 	FrameBuf();
 	int Init(int width, int height, Uint32 window_flags, Uint32 render_flags,
 			SDL_Color *colors = NULL, SDL_Surface *icon = NULL);
-	~FrameBuf();
+	virtual ~FrameBuf();
 
 	/* Setup routines */
 	/* Set the image palette -- 256 entries */
@@ -88,18 +88,6 @@ class FrameBuf {
 	}
 	void Fade(void);		/* Fade screen out, then in */
 
-	/* Informational routines */
-	int Width(void) {
-		int w, h;
-		SDL_GetWindowSize(window, &w, &h);
-		return w;
-	}
-	int Height(void) {
-		int w, h;
-		SDL_GetWindowSize(window, &w, &h);
-		return h;
-	}
-
 	/* Drawing routines */
 	void Clear(int x, int y, int w, int h) {
 		FillRect(x, y, w, h, 0);
@@ -166,11 +154,6 @@ class FrameBuf {
 		SDL_SetWindowTitle(window, caption);
 	}
 
-	/* Error message routine */
-	char *Error(void) {
-		return(errstr);
-	}
-
 private:
 	/* The current display */
 	SDL_Window *window;
@@ -186,18 +169,6 @@ class FrameBuf {
 		b = (color >>  0) & 0xFF;
 		SDL_SetRenderDrawColor(renderer, r, g, b, 0xFF);
 	}
-
-	/* Error message */
-	void SetError(const char *fmt, ...) {
-		va_list ap;
-
-		va_start(ap, fmt);
-		vsprintf(errbuf, fmt, ap);
-		va_end(ap);
-		errstr = errbuf;
-        }
-	char *errstr;
-	char  errbuf[1024];
 };
 
 #endif /* _SDL_FrameBuf_h */
diff --git a/screenlib/UIArea.cpp b/screenlib/UIArea.cpp
new file mode 100644
index 00000000..bae27d11
--- /dev/null
+++ b/screenlib/UIArea.cpp
@@ -0,0 +1,170 @@
+/*
+    SCREENLIB:  A framebuffer library based on the SDL library
+    Copyright (C) 1997  Sam Lantinga
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Sam Lantinga
+    slouken@libsdl.org
+*/
+
+#include "UIArea.h"
+
+
+static AnchorLocation ParseAnchorLocation(const char *text)
+{
+	AnchorLocation value = TOPLEFT;
+
+	if (strcasecmp(text, "TOPLEFT") == 0) {
+		value = TOPLEFT;
+	} else if (strcasecmp(text, "TOP") == 0) {
+		value = TOP;
+	} else if (strcasecmp(text, "TOPRIGHT") == 0) {
+		value = TOPRIGHT;
+	} else if (strcasecmp(text, "LEFT") == 0) {
+		value = LEFT;
+	} else if (strcasecmp(text, "CENTER") == 0) {
+		value = CENTER;
+	} else if (strcasecmp(text, "RIGHT") == 0) {
+		value = RIGHT;
+	} else if (strcasecmp(text, "BOTTOMLEFT") == 0) {
+		value = BOTTOMLEFT;
+	} else if (strcasecmp(text, "BOTTOM") == 0) {
+		value = BOTTOM;
+	} else if (strcasecmp(text, "BOTTOMRIGHT") == 0) {
+		value = BOTTOMRIGHT;
+	}
+	return value;
+
+}
+
+UIArea::UIArea() : ErrorBase()
+{
+	m_rect.x = 0;
+	m_rect.y = 0;
+	m_rect.w = 0;
+	m_rect.h = 0;
+}
+
+bool
+UIArea::Load(rapidxml::xml_node<> *node)
+{
+	rapidxml::xml_node<> *child;
+	rapidxml::xml_attribute<> *attr;
+
+	child = node->first_node("size", 0, false);
+	if (child) {
+		attr = child->first_attribute("w", 0, false);
+		if (attr) {
+			m_rect.w = atoi(attr->value());
+		}
+		attr = child->first_attribute("h", 0, false);
+		if (attr) {
+			m_rect.h = atoi(attr->value());
+		}
+	}
+
+	child = node->first_node("anchor", 0, false);
+	if (child) {
+		UIArea *anchorElement;
+		AnchorLocation anchorFrom = TOPLEFT;
+		AnchorLocation anchorTo = TOPLEFT;
+		int x, y;
+
+		attr = node->first_attribute("anchorElement", 0, false);
+		anchorElement = GetAnchorElement(attr ? attr->value() : NULL);
+		if (!anchorElement) {
+			SetError("Element 'anchor' couldn't find anchor element %s",
+				attr ? attr->value() : "NULL");
+			return false;
+		}
+
+		attr = node->first_attribute("anchorFrom", 0, false);
+		if (attr) {
+			anchorFrom = ParseAnchorLocation(attr->value());
+		}
+		attr = node->first_attribute("anchorTo", 0, false);
+		if (attr) {
+			anchorTo = ParseAnchorLocation(attr->value());
+		}
+		anchorElement->GetAnchorLocation(anchorTo, &x, &y);
+
+		switch (anchorFrom & X_MASK) {
+			case X_CENTER:
+				x -= Width() / 2;
+				break;
+			case X_RIGHT:
+				x -= Width();
+				break;
+			default:
+				break;
+		}
+		switch (anchorFrom & Y_MASK) {
+			case Y_CENTER:
+				y -= Height() / 2;
+				break;
+			case Y_BOTTOM:
+				y -= Height();
+				break;
+			default:
+				break;
+		}
+
+		attr = child->first_attribute("x", 0, false);
+		if (attr) {
+			x += atoi(attr->value());
+		}
+		attr = child->first_attribute("y", 0, false);
+		if (attr) {
+			y += atoi(attr->value());
+		}
+
+		m_rect.x = x;
+		m_rect.y = y;
+	}
+
+	return true;
+}
+
+void
+UIArea::GetAnchorLocation(AnchorLocation spot, int *x, int *y) const
+{
+	switch (spot & X_MASK) {
+		case X_LEFT:
+			*x = m_rect.x;
+			break;
+		case X_CENTER:
+			*x = m_rect.x + m_rect.w/2;
+			break;
+		case X_RIGHT:
+			*x = m_rect.x + m_rect.w;
+			break;
+		default:
+			assert(0);
+	}
+	switch (spot & Y_MASK) {
+		case Y_TOP:
+			*y = m_rect.y;
+			break;
+		case Y_CENTER:
+			*y = m_rect.y + m_rect.h/2;
+			break;
+		case Y_BOTTOM:
+			*y = m_rect.y + m_rect.h;
+			break;
+		default:
+			assert(0);
+	}
+}
diff --git a/screenlib/UIArea.h b/screenlib/UIArea.h
new file mode 100644
index 00000000..93a55d8c
--- /dev/null
+++ b/screenlib/UIArea.h
@@ -0,0 +1,95 @@
+/*
+    SCREENLIB:  A framebuffer library based on the SDL library
+    Copyright (C) 1997  Sam Lantinga
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Sam Lantinga
+    slouken@libsdl.org
+*/
+
+#ifndef _UIArea_h
+#define _UIArea_h
+
+#include "SDL_rect.h"
+
+#include "../utils/rapidxml.h"
+
+#include "ErrorBase.h"
+
+enum {
+	X_LEFT = 0x01,
+	X_CENTER = 0x02,
+	X_RIGHT = 0x4,
+	X_MASK = (X_LEFT|X_CENTER|X_RIGHT),
+	Y_TOP = 0x10,
+	Y_CENTER = 0x20,
+	Y_BOTTOM = 0x40,
+	Y_MASK = (Y_TOP|Y_CENTER|Y_BOTTOM)
+};
+enum AnchorLocation {
+	TOPLEFT = (Y_TOP|X_LEFT),
+	TOP = (Y_TOP|X_CENTER),
+	TOPRIGHT = (Y_TOP|X_RIGHT),
+	LEFT = (Y_CENTER|X_LEFT),
+	CENTER = (Y_CENTER|X_CENTER),
+	RIGHT = (Y_CENTER|X_RIGHT),
+	BOTTOMLEFT = (Y_BOTTOM|X_LEFT),
+	BOTTOM = (Y_BOTTOM|X_CENTER),
+	BOTTOMRIGHT = (Y_BOTTOM|X_RIGHT)
+};
+
+class UIArea : public ErrorBase
+{
+public:
+	UIArea();
+
+	bool Load(rapidxml::xml_node<> *node);
+
+	// This function returns anchor areas by name
+	virtual UIArea *GetAnchorElement(const char *name) {
+		return NULL;
+	}
+
+	void SetPosition(int x, int y) {
+		m_rect.x = x;
+		m_rect.y = y;
+	}
+	void SetSize(int w, int h) {
+		m_rect.w = w;
+		m_rect.h = h;
+	}
+	const SDL_Rect *GetRect() const {
+		return &m_rect;
+	}
+	int X() const {
+		return m_rect.x;
+	}
+	int Y() const {
+		return m_rect.y;
+	}
+	int Width() const {
+		return m_rect.w;
+	}
+	int Height() const {
+		return m_rect.h;
+	}
+	void GetAnchorLocation(AnchorLocation spot, int *x, int *y) const;
+
+protected:
+	SDL_Rect m_rect;
+};
+
+#endif // _UIArea_h
diff --git a/screenlib/UIElement.cpp b/screenlib/UIElement.cpp
new file mode 100644
index 00000000..9417d8f1
--- /dev/null
+++ b/screenlib/UIElement.cpp
@@ -0,0 +1,52 @@
+/*
+    SCREENLIB:  A framebuffer library based on the SDL library
+    Copyright (C) 1997  Sam Lantinga
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Sam Lantinga
+    slouken@libsdl.org
+*/
+
+#include "UIPanel.h"
+#include "UIElement.h"
+
+UIElement::UIElement(UIPanel *panel, const char *name) : UIArea()
+{
+	m_name = new char[strlen(name)+1];
+	strcpy(m_name, name);
+
+	m_panel = panel;
+}
+
+UIElement::~UIElement()
+{
+	delete[] m_name;
+}
+
+bool
+UIElement::Load(rapidxml::xml_node<> *node)
+{
+	return UIArea::Load(node);
+}
+
+UIArea *
+UIElement::GetAnchorElement(const char *name)
+{
+	if (!name) {
+		return m_panel;
+	}
+	return m_panel->GetElement(name);
+}
diff --git a/screenlib/UIElement.h b/screenlib/UIElement.h
new file mode 100644
index 00000000..6719c7e8
--- /dev/null
+++ b/screenlib/UIElement.h
@@ -0,0 +1,58 @@
+/*
+    SCREENLIB:  A framebuffer library based on the SDL library
+    Copyright (C) 1997  Sam Lantinga
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Sam Lantinga
+    slouken@libsdl.org
+*/
+
+#ifndef _UIElement_h
+#define _UIElement_h
+
+#include "SDL.h"
+
+#include "../utils/rapidxml.h"
+
+#include "ErrorBase.h"
+#include "UIArea.h"
+
+class FrameBuf;
+class UIPanel;
+
+class UIElement : public UIArea
+{
+public:
+	UIElement(UIPanel *panel, const char *name = "");
+	virtual ~UIElement();
+
+	const char *GetName() const {
+		return m_name;
+	}
+
+	bool Load(rapidxml::xml_node<> *node);
+
+	virtual UIArea *GetAnchorElement(const char *name);
+
+	virtual void Draw(FrameBuf *) { }
+	virtual bool HandleEvent(const SDL_Event &event) { return false; }
+
+protected:
+	char *m_name;
+	UIPanel *m_panel;
+};
+
+#endif // _UIElement_h
diff --git a/screenlib/UIManager.cpp b/screenlib/UIManager.cpp
new file mode 100644
index 00000000..ab6068f6
--- /dev/null
+++ b/screenlib/UIManager.cpp
@@ -0,0 +1,55 @@
+/*
+    SCREENLIB:  A framebuffer library based on the SDL library
+    Copyright (C) 1997  Sam Lantinga
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Sam Lantinga
+    slouken@libsdl.org
+*/
+
+#include "UIManager.h"
+#include "UIPanel.h"
+
+
+UIPanel *
+UIManager::GetPanel(const char *name)
+{
+	for (unsigned i = 0; i < m_panels.length(); ++i) {
+		if (strcmp(name, m_panels[i]->GetName()) == 0) {
+			return m_panels[i];
+		}
+	}
+	return NULL;
+}
+
+void
+UIManager::Draw()
+{
+	for (unsigned i = 0; i < m_panels.length(); ++i) {
+		m_panels[i]->Draw();
+	}
+}
+
+bool
+UIManager::HandleEvent(const SDL_Event &event)
+{
+	for (unsigned i = m_panels.length(); i--; ) {
+		if (m_panels[i]->HandleEvent(event)) {
+			return true;
+		}
+	}
+	return false;
+}
diff --git a/screenlib/UIManager.h b/screenlib/UIManager.h
new file mode 100644
index 00000000..40567352
--- /dev/null
+++ b/screenlib/UIManager.h
@@ -0,0 +1,51 @@
+/*
+    SCREENLIB:  A framebuffer library based on the SDL library
+    Copyright (C) 1997  Sam Lantinga
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Sam Lantinga
+    slouken@libsdl.org
+*/
+
+#ifndef _UIManager_h
+#define _UIManager_h
+
+#include "SDL.h"
+#include "array.h"
+
+class UIPanel;
+
+class UIManager
+{
+public:
+	UIManager() { }
+
+	void AddPanel(UIPanel *panel) {
+		m_panels.add(panel);
+	}
+	UIPanel *GetPanel(const char *name);
+	void RemovePanel(UIPanel *panel) {
+		m_panels.remove(panel);
+	}
+
+	void Draw();
+	bool HandleEvent(const SDL_Event &event);
+
+protected:
+	array<UIPanel *> m_panels;
+};
+
+#endif // _UIManager_h
diff --git a/screenlib/UIPanel.cpp b/screenlib/UIPanel.cpp
new file mode 100644
index 00000000..395a6a6b
--- /dev/null
+++ b/screenlib/UIPanel.cpp
@@ -0,0 +1,174 @@
+/*
+    SCREENLIB:  A framebuffer library based on the SDL library
+    Copyright (C) 1997  Sam Lantinga
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Sam Lantinga
+    slouken@libsdl.org
+*/
+
+#include <physfs.h>
+
+#include "SDL_FrameBuf.h"
+#include "UIPanel.h"
+#include "UIElement.h"
+
+UIElementFactory UIPanel::s_elementFactory;
+
+UIPanel::UIPanel(FrameBuf *screen, const char *name) : UIArea()
+{
+	m_screen = screen;
+	m_name = new char[strlen(name)+1];
+	strcpy(m_name, name);
+
+	m_rect.w = screen->Width();
+	m_rect.h = screen->Height();
+}
+
+UIPanel::~UIPanel()
+{
+	delete[] m_name;
+
+	for (unsigned i = 0; i < m_elements.length(); ++i) {
+		delete m_elements[i];
+	}
+}
+
+bool
+UIPanel::Load(const char *file)
+{
+	PHYSFS_File *fp;
+	PHYSFS_sint64 size;
+	char *buffer;
+
+	ClearError();
+
+	if (!s_elementFactory) {
+		SetError("No panel element factory set");
+		return false;
+	}
+
+	fp = PHYSFS_openRead(file);
+	if (!fp) {
+		SetError("Couldn't open %s: %s", file, PHYSFS_getLastError());
+		return false;
+	}
+
+	size = PHYSFS_fileLength(fp);
+	buffer = new char[size+1];
+	if (PHYSFS_readBytes(fp, buffer, size) != size) {
+		SetError("Couldn't read from %s: %s", file, PHYSFS_getLastError());
+		PHYSFS_close(fp);
+		delete[] buffer;
+		return false;
+	}
+	buffer[size] = '\0';
+	PHYSFS_close(fp);
+
+	rapidxml::xml_document<> doc;
+	try {
+		doc.parse<0>(buffer);
+	} catch (rapidxml::parse_error e) {
+		SetError("Parse error: %s", e.what());
+		delete[] buffer;
+		return false;
+	}
+
+	rapidxml::xml_node<> *node = doc.first_node();
+	rapidxml::xml_node<> *child;
+	if (strcmp(node->name(), "UIPanel") != 0) {
+		SetError("Parse error: UIPanel root element expected");
+		delete[] buffer;
+		return false;
+	}
+	child = node->first_node("name", 0, false);
+	if (child) {
+		const char *name = node->value();
+		delete[] m_name;
+		m_name = new char[strlen(name)+1];
+		strcpy(m_name, name);
+	}
+	if (!UIArea::Load(node)) {
+		delete[] buffer;
+		return false;
+	}
+	child = node->first_node("elements", 0, false);
+	if (child) {
+		if (!LoadElements(child)) {
+			delete[] buffer;
+			return false;
+		}
+	}
+	delete[] buffer;
+	return true;
+}
+
+bool
+UIPanel::LoadElements(rapidxml::xml_node<> *node)
+{
+	for (node = node->first_node(); node; node = node->next_sibling()) {
+		UIElement *element = s_elementFactory(this, node->name());
+		if (!element) {
+			SetError("Couldn't find handler for element %s", node->name());
+			return false;
+		}
+		if (!element->Load(node)) {
+			SetError("Couldn't load element %s: %s", node->name(), element->Error());
+			delete element;
+			return false;
+		}
+		AddElement(element);
+	}
+}
+
+UIArea *
+UIPanel::GetAnchorElement(const char *name)
+{
+	if (!name) {
+		return m_screen;
+	}
+	return NULL;
+}
+
+UIElement *
+UIPanel::GetElement(const char *name)
+{
+	for (unsigned i = 0; i < m_elements.length(); ++i) {
+		if (strcmp(name, m_elements[i]->GetName()) == 0) {
+			return m_elements[i];
+		}
+	}
+	return NULL;
+}
+
+void
+UIPanel::Draw()
+{
+	for (unsigned i = 0; i < m_elements.length(); ++i) {
+		m_elements[i]->Draw(m_screen);
+	}
+}
+
+bool
+UIPanel::HandleEvent(const SDL_Event &event)
+{
+	for (unsigned i = m_elements.length(); i--; ) {
+		if (m_elements[i]->HandleEvent(event)) {
+			return true;
+		}
+	}
+	return false;
+}
diff --git a/screenlib/UIPanel.h b/screenlib/UIPanel.h
new file mode 100644
index 00000000..9624c22a
--- /dev/null
+++ b/screenlib/UIPanel.h
@@ -0,0 +1,81 @@
+/*
+    SCREENLIB:  A framebuffer library based on the SDL library
+    Copyright (C) 1997  Sam Lantinga
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Sam Lantinga
+    slouken@libsdl.org
+*/
+
+#ifndef _UIPanel_h
+#define _UIPanel_h
+
+#include "SDL.h"
+
+#include "../utils/rapidxml.h"
+#include "../utils/array.h"
+
+#include "ErrorBase.h"
+#include "UIArea.h"
+
+class FrameBuf;
+class UIElement;
+class UIPanel;
+
+typedef UIElement *(*UIElementFactory)(UIPanel *panel, const char *name);
+
+class UIPanel : public UIArea
+{
+public:
+	static void SetElementFactory(UIElementFactory function) {
+		s_elementFactory = function;
+	}
+
+public:
+	UIPanel(FrameBuf *screen, const char *name = "");
+	virtual ~UIPanel();
+
+	const char *GetName() const {
+		return m_name;
+	}
+
+	bool Load(const char *file);
+
+	virtual UIArea *GetAnchorElement(const char *name);
+
+	void AddElement(UIElement *element) {
+		m_elements.add(element);
+	}
+	UIElement *GetElement(const char *name);
+	void RemoveElement(UIElement *element) {
+		m_elements.remove(element);
+	}
+
+	void Draw();
+	bool HandleEvent(const SDL_Event &event);
+
+protected:
+	FrameBuf *m_screen;
+	char *m_name;
+	array<UIElement *> m_elements;
+
+	bool LoadElements(rapidxml::xml_node<> *node);
+
+protected:
+	static UIElementFactory s_elementFactory;
+};
+
+#endif // _UIPanel_h
diff --git a/utils/array.h b/utils/array.h
new file mode 100644
index 00000000..0d273307
--- /dev/null
+++ b/utils/array.h
@@ -0,0 +1,67 @@
+
+/* Simple array class for use with Maelstrom.
+ *
+ * The types in the array are POD types, without constructors or destructors.
+ */
+#ifndef _array_h
+#define _array_h
+
+#include <stdlib.h>
+#include <assert.h>
+
+template <typename T>
+class array
+{
+public:
+	array() : m_len(0), m_max(1), m_data((T*)malloc(sizeof(T))) { }
+	~array() { free(m_data); }
+
+	void add(const T& item) {
+		resize(m_len+1);
+		m_data[m_len++] = item;
+	}
+	void insert(const T& item, unsigned index) {
+		if (index == m_len) {
+			add(item);
+			return;
+		}
+		resize(m_len+1);
+		for (unsigned i = m_len; i >= index; --i) {
+			m_data[i] = m_data[i-1];
+		}
+		m_data[index] = item;
+		++m_len;
+	}
+	bool remove(const T& item) {
+		for (unsigned i = 0; i < m_len; ++i) {
+			if (m_data[i] == item) {
+				memcpy(&m_data[i], &m_data[i+1], (m_len-i-1)*sizeof(T));
+				--m_len;
+				return true;
+			}
+		}
+		return false;
+	}
+	int length() const {
+		return m_len;
+	}
+	T operator[](unsigned index) {
+		assert(index < m_len);
+		return m_data[index];
+	}
+
+protected:
+	unsigned m_len;
+	unsigned m_max;
+	T *m_data;
+
+	void resize(unsigned len) {
+		if (len > m_max) {
+			while (m_max < len)
+				m_max *= 2;
+			m_data = (T*)realloc(m_data, m_max*sizeof(T));
+		}
+	}
+};
+
+#endif // _array_h
diff --git a/utils/rapidxml.h b/utils/rapidxml.h
new file mode 100644
index 00000000..fe6e23de
--- /dev/null
+++ b/utils/rapidxml.h
@@ -0,0 +1,17 @@
+#ifndef _rapidxml_h
+#define _rapidxml_h
+
+#define RAPIDXML_NO_STDLIB
+
+#include <assert.h>
+
+namespace std
+{
+	typedef ::size_t size_t;
+}
+
+extern inline void * operator new (size_t, void * p) throw() { return p ; }
+
+#include "rapidxml.hpp"
+
+#endif // _rapidxml_h
diff --git a/utils/rapidxml.hpp b/utils/rapidxml.hpp
new file mode 100755
index 00000000..4f840ce0
--- /dev/null
+++ b/utils/rapidxml.hpp
@@ -0,0 +1,2617 @@
+#ifndef RAPIDXML_HPP_INCLUDED
+#define RAPIDXML_HPP_INCLUDED
+
+/*
+Copyright (c) 2006, 2007 Marcin Kalicinski
+
+Permission is hereby granted, free of charge, to any person obtaining a copy 
+of this software and associated documentation files (the "Software"), to deal 
+in the Software without restriction, including without limitation the rights 
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+of the Software, and to permit persons to whom the Software is furnished to do so, 
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all 
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 
+IN THE SOFTWARE.
+*/
+// Copyright (C) 2006, 2009 Marcin Kalicinski
+// Version 1.13
+// Revision $DateTime: 2009/05/13 01:46:17 $
+//! \file rapidxml.hpp This file contains rapidxml parser and DOM implementation
+
+// If standard library is disabled, user must provide implementations of required functions and typedefs
+#if !defined(RAPIDXML_NO_STDLIB)
+    #include <cstdlib>      // For std::size_t
+    #include <cassert>      // For assert
+    #include <new>          // For placement new
+#endif
+
+// On MSVC, disable "conditional expression is constant" warning (level 4). 
+// This warning is almost impossible to avoid with certain types of templated code
+#ifdef _MSC_VER
+    #pragma warning(push)
+    #pragma warning(disable:4127)   // Conditional expression is constant
+#endif
+
+///////////////////////////////////////////////////////////////////////////
+// RAPIDXML_PARSE_ERROR
+    
+#if defined(RAPIDXML_NO_EXCEPTIONS)
+
+#define RAPIDXML_PARSE_ERROR(what, where) { parse_error_handler(what, where); assert(0); }
+
+namespace rapidxml
+{
+    //! When exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, 
+    //! this function is called to notify user about the error.
+    //! It must be defined by the user.
+    //! <br><br>
+    //! This function cannot

(Patch may be truncated, please check the link at the top of this post.)