Maelstrom: Added a UI template system to reduce the amount of duplicated XML in the UI.

https://github.com/libsdl-org/Maelstrom/commit/16dc59b09e8078273bed62297999a7cab97ef63e

From 16dc59b09e8078273bed62297999a7cab97ef63e Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Thu, 27 Oct 2011 22:08:32 -0400
Subject: [PATCH] Added a UI template system to reduce the amount of duplicated
 XML in the UI.

---
 UI/about_credits.xml       |  37 +-------
 UI/about_game.xml          |  37 +-------
 UI/about_story.xml         |  37 +-------
 UI/loading.xml             |  37 +-------
 UI/main.xml                |  39 +--------
 init.cpp                   |   1 +
 screenlib/Makefile.am      |   4 +-
 screenlib/SDL_FrameBuf.cpp |   2 +-
 screenlib/UIElement.h      |   1 +
 screenlib/UIManager.cpp    |  17 +++-
 screenlib/UIManager.h      |   6 ++
 screenlib/UIPanel.cpp      |  15 +++-
 screenlib/UIPanel.h        |   3 +-
 screenlib/UITemplates.cpp  | 167 +++++++++++++++++++++++++++++++++++++
 screenlib/UITemplates.h    |  55 ++++++++++++
 15 files changed, 271 insertions(+), 187 deletions(-)
 create mode 100644 screenlib/UITemplates.cpp
 create mode 100644 screenlib/UITemplates.h

diff --git a/UI/about_credits.xml b/UI/about_credits.xml
index e0eb06f3..8cf624b2 100644
--- a/UI/about_credits.xml
+++ b/UI/about_credits.xml
@@ -1,43 +1,8 @@
-<UIPanel>
+<UIPanel template="FramedPanel">
 	<Elements>
 		<Title name="image" id="135">
 			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
 		</Title>
-		<Rectangle>
-			<Color r="0x75" g="0x75" b="0xFF"/>
-			<Size w="514" h="386"/>
-			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
-		</Rectangle>
-		<Rectangle>
-			<Color r="0x75" g="0x75" b="0xFF"/>
-			<Size w="516" h="388"/>
-			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
-		</Rectangle>
-		<Rectangle>
-			<Color r="0x9C" g="0x9C" b="0xFF"/>
-			<Size w="518" h="390"/>
-			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
-		</Rectangle>
-		<Rectangle>
-			<Color r="0x9C" g="0x9C" b="0xFF"/>
-			<Size w="520" h="392"/>
-			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
-		</Rectangle>
-		<Rectangle>
-			<Color r="0xC3" g="0xC3" b="0xFF"/>
-			<Size w="522" h="394"/>
-			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
-		</Rectangle>
-		<Rectangle>
-			<Color r="0x9C" g="0x9C" b="0xFF"/>
-			<Size w="524" h="396"/>
-			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
-		</Rectangle>
-		<Rectangle>
-			<Color r="0x75" g="0x75" b="0xFF"/>
-			<Size w="526" h="398"/>
-			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
-		</Rectangle>
 
 		<!-- Porting credits -->
 		<Rectangle fill="true">
diff --git a/UI/about_game.xml b/UI/about_game.xml
index 71d74682..249ae89c 100644
--- a/UI/about_game.xml
+++ b/UI/about_game.xml
@@ -1,43 +1,8 @@
-<UIPanel delegate="AboutPanel">
+<UIPanel template="FramedPanel" delegate="AboutPanel">
 	<Elements>
 		<Title name="image" id="134">
 			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
 		</Title>
-		<Rectangle>
-			<Color r="0x75" g="0x75" b="0xFF"/>
-			<Size w="514" h="386"/>
-			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
-		</Rectangle>
-		<Rectangle>
-			<Color r="0x75" g="0x75" b="0xFF"/>
-			<Size w="516" h="388"/>
-			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
-		</Rectangle>
-		<Rectangle>
-			<Color r="0x9C" g="0x9C" b="0xFF"/>
-			<Size w="518" h="390"/>
-			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
-		</Rectangle>
-		<Rectangle>
-			<Color r="0x9C" g="0x9C" b="0xFF"/>
-			<Size w="520" h="392"/>
-			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
-		</Rectangle>
-		<Rectangle>
-			<Color r="0xC3" g="0xC3" b="0xFF"/>
-			<Size w="522" h="394"/>
-			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
-		</Rectangle>
-		<Rectangle>
-			<Color r="0x9C" g="0x9C" b="0xFF"/>
-			<Size w="524" h="396"/>
-			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
-		</Rectangle>
-		<Rectangle>
-			<Color r="0x75" g="0x75" b="0xFF"/>
-			<Size w="526" h="398"/>
-			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
-		</Rectangle>
 
 		<!-- Shield icon -->
 		<Icon id="137">
diff --git a/UI/about_story.xml b/UI/about_story.xml
index 7b7c59a0..e6ef9ea0 100644
--- a/UI/about_story.xml
+++ b/UI/about_story.xml
@@ -1,43 +1,8 @@
-<UIPanel>
+<UIPanel template="FramedPanel">
 	<Elements>
 		<Title name="image" id="133">
 			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
 		</Title>
-		<Rectangle>
-			<Color r="0x75" g="0x75" b="0xFF"/>
-			<Size w="514" h="386"/>
-			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
-		</Rectangle>
-		<Rectangle>
-			<Color r="0x75" g="0x75" b="0xFF"/>
-			<Size w="516" h="388"/>
-			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
-		</Rectangle>
-		<Rectangle>
-			<Color r="0x9C" g="0x9C" b="0xFF"/>
-			<Size w="518" h="390"/>
-			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
-		</Rectangle>
-		<Rectangle>
-			<Color r="0x9C" g="0x9C" b="0xFF"/>
-			<Size w="520" h="392"/>
-			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
-		</Rectangle>
-		<Rectangle>
-			<Color r="0xC3" g="0xC3" b="0xFF"/>
-			<Size w="522" h="394"/>
-			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
-		</Rectangle>
-		<Rectangle>
-			<Color r="0x9C" g="0x9C" b="0xFF"/>
-			<Size w="524" h="396"/>
-			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
-		</Rectangle>
-		<Rectangle>
-			<Color r="0x75" g="0x75" b="0xFF"/>
-			<Size w="526" h="398"/>
-			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
-		</Rectangle>
 
 		<!-- Buttons -->
 		<Button hotkey="any" clickPanel="main" clickSound="106"/>
diff --git a/UI/loading.xml b/UI/loading.xml
index 902f5c77..f5e46d4a 100644
--- a/UI/loading.xml
+++ b/UI/loading.xml
@@ -1,43 +1,8 @@
-<UIPanel enterSound="111" leaveSound="123">
+<UIPanel template="FramedPanel" enterSound="111" leaveSound="123">
 	<Elements>
 		<Title name="image" id="130">
 			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
 		</Title>
-		<Rectangle>
-			<Color r="0x75" g="0x75" b="0xFF"/>
-			<Size w="514" h="386"/>
-			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
-		</Rectangle>
-		<Rectangle>
-			<Color r="0x75" g="0x75" b="0xFF"/>
-			<Size w="516" h="388"/>
-			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
-		</Rectangle>
-		<Rectangle>
-			<Color r="0x9C" g="0x9C" b="0xFF"/>
-			<Size w="518" h="390"/>
-			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
-		</Rectangle>
-		<Rectangle>
-			<Color r="0x9C" g="0x9C" b="0xFF"/>
-			<Size w="520" h="392"/>
-			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
-		</Rectangle>
-		<Rectangle>
-			<Color r="0xC3" g="0xC3" b="0xFF"/>
-			<Size w="522" h="394"/>
-			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
-		</Rectangle>
-		<Rectangle>
-			<Color r="0x9C" g="0x9C" b="0xFF"/>
-			<Size w="524" h="396"/>
-			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
-		</Rectangle>
-		<Rectangle>
-			<Color r="0x75" g="0x75" b="0xFF"/>
-			<Size w="526" h="398"/>
-			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
-		</Rectangle>
 		<Label fontName="Geneva" fontSize="9" fontStyle="BOLD" text="Loading...">
 			<Color r="0xFF" g="0xFF" b="0x00"/>
 			<Anchor anchorFrom="CENTER" anchorTo="BOTTOM" anchor="image" y="20"/>
diff --git a/UI/main.xml b/UI/main.xml
index 6500104d..94f5a28e 100644
--- a/UI/main.xml
+++ b/UI/main.xml
@@ -1,4 +1,4 @@
-<UIPanel delegate="MainPanel">
+<UIPanel template="FramedPanel" delegate="MainPanel">
 	<Elements>
 		<Title name="image" id="129">
 			<Anchor anchorFrom="TOPLEFT" anchorTo="CENTER" x="-251" y="-187"/>
@@ -7,43 +7,6 @@
 		<!-- Catch-all button to bonk when a key is pressed -->
 		<Button hotkey="any" clickSound="108"/>
 
-		<!-- screen frame -->
-		<Rectangle>
-			<Color r="0x75" g="0x75" b="0xFF"/>
-			<Size w="514" h="386"/>
-			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
-		</Rectangle>
-		<Rectangle>
-			<Color r="0x75" g="0x75" b="0xFF"/>
-			<Size w="516" h="388"/>
-			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
-		</Rectangle>
-		<Rectangle>
-			<Color r="0x9C" g="0x9C" b="0xFF"/>
-			<Size w="518" h="390"/>
-			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
-		</Rectangle>
-		<Rectangle>
-			<Color r="0x9C" g="0x9C" b="0xFF"/>
-			<Size w="520" h="392"/>
-			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
-		</Rectangle>
-		<Rectangle>
-			<Color r="0xC3" g="0xC3" b="0xFF"/>
-			<Size w="522" h="394"/>
-			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
-		</Rectangle>
-		<Rectangle>
-			<Color r="0x9C" g="0x9C" b="0xFF"/>
-			<Size w="524" h="396"/>
-			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
-		</Rectangle>
-		<Rectangle>
-			<Color r="0x75" g="0x75" b="0xFF"/>
-			<Size w="526" h="398"/>
-			<Anchor anchorFrom="CENTER" anchorTo="CENTER"/>
-		</Rectangle>
-
 		<!-- dividers -->
 		<Line name="vertical_divider">
 			<Color r="0x9C" g="0x9C" b="0xFF"/>
diff --git a/init.cpp b/init.cpp
index f92abb98..cc70ce2a 100644
--- a/init.cpp
+++ b/init.cpp
@@ -730,6 +730,7 @@ int DoInitializations(Uint32 window_flags, Uint32 render_flags)
 	ui = new UIManager(screen, CreateMaelstromUIPanel, CreateMaelstromUIElement);
 	ui->SetSoundCallback(PlayUISound, NULL);
 	ui->SetLoadPath("UI");
+	ui->LoadTemplates("UITemplates.xml");
 
 	/* Load the Sound Server and initialize sound */
 	sound = new Sound("Maelstrom Sounds", gSoundLevel);
diff --git a/screenlib/Makefile.am b/screenlib/Makefile.am
index 61028b82..3eb44741 100644
--- a/screenlib/Makefile.am
+++ b/screenlib/Makefile.am
@@ -20,4 +20,6 @@ libSDLscreen_a_SOURCES =	\
 	UIManager.cpp		\
 	UIManager.h		\
 	UIPanel.cpp		\
-	UIPanel.h
+	UIPanel.h		\
+	UITemplates.cpp		\
+	UITemplates.h
diff --git a/screenlib/SDL_FrameBuf.cpp b/screenlib/SDL_FrameBuf.cpp
index a190e6c9..9e6f1b05 100644
--- a/screenlib/SDL_FrameBuf.cpp
+++ b/screenlib/SDL_FrameBuf.cpp
@@ -131,7 +131,7 @@ void
 FrameBuf:: Fade(void)
 {
 // Temporary for development
-//return;
+return;
 	const int max = 32;
 	Uint16 ramp[256];   
 
diff --git a/screenlib/UIElement.h b/screenlib/UIElement.h
index 6e1a8672..9c2a2bb0 100644
--- a/screenlib/UIElement.h
+++ b/screenlib/UIElement.h
@@ -50,6 +50,7 @@ class UIElement : public UIArea
 	}
 
 	virtual bool Load(rapidxml::xml_node<> *node);
+	virtual bool FinishLoading() { return true; }
 
 	virtual UIArea *GetAnchorElement(const char *name);
 
diff --git a/screenlib/UIManager.cpp b/screenlib/UIManager.cpp
index 500105d4..c2a35f46 100644
--- a/screenlib/UIManager.cpp
+++ b/screenlib/UIManager.cpp
@@ -43,6 +43,7 @@ UIManager::~UIManager()
 	while (m_panels.length() > 0) {
 		delete m_panels[m_panels.length()-1];
 	}
+	delete[] m_loadPath;
 }
 
 void
@@ -53,6 +54,15 @@ UIManager::SetLoadPath(const char *path)
 	strcpy(m_loadPath, path);
 }
 
+bool
+UIManager::LoadTemplates(const char *file)
+{
+	char path[1024];
+
+	sprintf(path, "%s/%s", m_loadPath, file);
+	return m_templates.Load(path);
+}
+
 static const char *GetLine(char *&text)
 {
 	while (*text == '\r' || *text == '\n') {
@@ -128,7 +138,12 @@ UIManager::LoadPanel(const char *name)
 		attr = node->first_attribute("delegate", 0, false);
 		panel = (GetPanelFactory())(this, node->name(), name, attr ? attr->value() : NULL);
 		if (panel) {
-			if (!panel->Load(node)) {
+			rapidxml::xml_node<> *templateNode;
+
+			templateNode = GetTemplateFor(node);
+			if ((templateNode && !panel->Load(templateNode)) ||
+			    !panel->Load(node) ||
+			    !panel->FinishLoading()) {
 				fprintf(stderr, "Warning: Couldn't load %s: %s\n",
 							file, panel->Error());
 				delete[] buffer;
diff --git a/screenlib/UIManager.h b/screenlib/UIManager.h
index ff28c601..d891a389 100644
--- a/screenlib/UIManager.h
+++ b/screenlib/UIManager.h
@@ -26,6 +26,7 @@
 #include "SDL.h"
 #include "../utils/array.h"
 #include "UIArea.h"
+#include "UITemplates.h"
 
 class FrameBuf;
 class UIManager;
@@ -51,8 +52,12 @@ class UIManager : public UIArea
 	UIElementFactory GetElementFactory() const {
 		return m_elementFactory;
 	}
+	rapidxml::xml_node<> *GetTemplateFor(rapidxml::xml_node<> *node) const {
+		return m_templates.GetTemplateFor(node);
+	}
 
 	void SetLoadPath(const char *path);
+	bool LoadTemplates(const char *file);
 	UIPanel *LoadPanel(const char *name);
 	UIPanel *GetPanel(const char *name, bool allowLoad = true);
 	UIPanel *GetCurrentPanel();
@@ -100,6 +105,7 @@ class UIManager : public UIArea
 	UISoundCallback m_soundCallback;
 	void *m_soundCallbackParam;
 	char *m_loadPath;
+	UITemplates m_templates;
 	array<UIPanel *> m_panels;
 	array<UIPanel *> m_visible;
 };
diff --git a/screenlib/UIPanel.cpp b/screenlib/UIPanel.cpp
index 9136f00c..09a27bab 100644
--- a/screenlib/UIPanel.cpp
+++ b/screenlib/UIPanel.cpp
@@ -96,6 +96,12 @@ UIPanel::Load(rapidxml::xml_node<> *node)
 		}
 	}
 
+	return true;
+}
+
+bool
+UIPanel::FinishLoading()
+{
 	if (m_delegate) {
 		return m_delegate->OnLoad();
 	} else {
@@ -106,12 +112,19 @@ UIPanel::Load(rapidxml::xml_node<> *node)
 bool
 UIPanel::LoadElements(rapidxml::xml_node<> *node)
 {
+	rapidxml::xml_node<> *templateNode;
+
 	for (node = node->first_node(); node; node = node->next_sibling()) {
 		UIElement *element = (m_ui->GetElementFactory())(this, node->name());
 		if (!element) {
 			fprintf(stderr, "Warning: Couldn't find handler for element %s\n", node->name());
 		}
-		if (!element->Load(node)) {
+
+
+		templateNode = m_ui->GetTemplateFor(node);
+		if ((templateNode && !element->Load(templateNode)) ||
+		    !element->Load(node) ||
+		    !element->FinishLoading()) {
 			fprintf(stderr, "Warning: Couldn't load element %s: %s\n", node->name(), element->Error());
 			delete element;
 		}
diff --git a/screenlib/UIPanel.h b/screenlib/UIPanel.h
index 301ad18f..b060456b 100644
--- a/screenlib/UIPanel.h
+++ b/screenlib/UIPanel.h
@@ -69,7 +69,8 @@ class UIPanel : public UIArea
 		return m_cursorVisible;
 	}
 
-	bool Load(rapidxml::xml_node<> *node);
+	virtual bool Load(rapidxml::xml_node<> *node);
+	virtual bool FinishLoading();
 
 	virtual UIArea *GetAnchorElement(const char *name);
 
diff --git a/screenlib/UITemplates.cpp b/screenlib/UITemplates.cpp
new file mode 100644
index 00000000..de6226a2
--- /dev/null
+++ b/screenlib/UITemplates.cpp
@@ -0,0 +1,167 @@
+/*
+    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 <stdio.h>
+#include <strings.h>
+#include <physfs.h>
+
+#include "UITemplates.h"
+
+
+UITemplates::UITemplates()
+{
+	m_data = NULL;
+	m_hashTable = NULL;
+}
+
+UITemplates::~UITemplates()
+{
+	if (m_hashTable) {
+		hash_destroy(m_hashTable);
+	}
+	if (m_data) {
+		delete[] m_data;
+	}
+}
+
+bool
+UITemplates::Load(const char *file)
+{
+	PHYSFS_File *fp;
+	PHYSFS_sint64 size;
+	rapidxml::xml_node<> *node;
+	rapidxml::xml_attribute<> *attr;
+
+	fp = PHYSFS_openRead(file);
+	if (!fp) {
+		fprintf(stderr, "Warning: Couldn't open %s: %s\n",
+					file, PHYSFS_getLastError());
+		return false;
+	}
+
+	size = PHYSFS_fileLength(fp);
+	m_data = new char[size+1];
+	if (PHYSFS_readBytes(fp, m_data, size) != size) {
+		fprintf(stderr, "Warning: Couldn't read from %s: %s\n",
+					file, PHYSFS_getLastError());
+		PHYSFS_close(fp);
+		delete[] m_data;
+		m_data = NULL;
+		return false;
+	}
+	m_data[size] = '\0';
+	PHYSFS_close(fp);
+
+	try {
+		m_doc.parse<0>(m_data);
+	} catch (rapidxml::parse_error e) {
+		fprintf(stderr, "Warning: Couldn't parse %s: error: %s\n",
+					file, e.what());
+		delete[] m_data;
+		m_data = NULL;
+		return false;
+	}
+
+
+	m_hashTable = hash_create(NULL, HashTable_Hash,
+					HashTable_KeyMatch,
+					HashTable_Nuke);
+
+	node = m_doc.first_node();
+	for (node = node->first_node(); node; node = node->next_sibling()) {
+		attr = node->first_attribute("templateName", 0, false);
+		if (attr) {
+			HashKey *key = new HashKey;
+			key->type = node->name();
+			key->name = attr->value();
+			hash_insert(m_hashTable, key, node);
+		} else {
+			fprintf(stderr, "Warning: UITemplate %s missing 'templateName'\n", node->name());
+		}
+	}
+	return true;
+}
+
+rapidxml::xml_node<> *
+UITemplates::GetTemplateFor(rapidxml::xml_node<> *node) const
+{
+	rapidxml::xml_attribute<> *attr;
+
+	attr = node->first_attribute("template", 0, false);
+	if (!attr) {
+		return NULL;
+	}
+	return GetTemplate(node->name(), attr->value());
+}
+
+rapidxml::xml_node<> *
+UITemplates::GetTemplate(const char *type, const char *name) const
+{
+	HashKey key;
+	rapidxml::xml_node<> *templateNode;
+
+	if (!m_hashTable) {
+		return NULL;
+	}
+
+	key.type = type;
+	key.name = name;
+	if (hash_find(m_hashTable, &key, (const void **)&templateNode)) {
+		return templateNode;
+	}
+	return NULL;
+}
+
+// this is djb's xor hashing function.
+unsigned
+UITemplates::HashTable_Hash(const void *_key, void *data)
+{
+	const HashKey *key = static_cast<const HashKey *>(_key);
+	const char *p;
+	register unsigned hash = 5381;
+
+	p = key->type;
+	while (*p) {
+		hash = ((hash << 5) + hash) ^ *(p++);
+	}
+	p = key->name;
+	while (*p) {
+		hash = ((hash << 5) + hash) ^ *(p++);
+	}
+	return hash;
+}
+
+int
+UITemplates::HashTable_KeyMatch(const void *_a, const void *_b, void *data)
+{
+	const HashKey *a = static_cast<const HashKey *>(_a);
+	const HashKey *b = static_cast<const HashKey *>(_a);
+
+	return strcmp(a->type, b->type) == 0 && strcmp(a->name, b->name) == 0;
+}
+
+void
+UITemplates::HashTable_Nuke(const void *_key, const void *value, void *data)
+{
+	HashKey *key = (HashKey *)_key;
+	delete key;
+}
diff --git a/screenlib/UITemplates.h b/screenlib/UITemplates.h
new file mode 100644
index 00000000..6e251ba2
--- /dev/null
+++ b/screenlib/UITemplates.h
@@ -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
+*/
+
+#ifndef _UITemplates_h
+#define _UITemplates_h
+
+#include "../utils/hashtable.h"
+#include "../utils/rapidxml.h"
+
+class UITemplates
+{
+public:
+	UITemplates();
+	~UITemplates();
+
+	bool Load(const char *file);
+
+	rapidxml::xml_node<> *GetTemplateFor(rapidxml::xml_node<> *node) const;
+	rapidxml::xml_node<> *GetTemplate(const char *type, const char *name) const;
+
+protected:
+	char *m_data;
+	rapidxml::xml_document<> m_doc;
+	struct HashKey {
+		const char *type;
+		const char *name;
+	};
+	HashTable *m_hashTable;
+
+protected:
+	static unsigned HashTable_Hash(const void *key, void *data);
+	static int HashTable_KeyMatch(const void *a, const void *b, void *data);
+	static void HashTable_Nuke(const void *key, const void *value, void *data);
+};
+
+#endif // _UITemplates_h