Unify scrollable list code

The goal of this patch set is to eliminate the code duplication
caused by copy/paste of the code in the file selector, listbox,
and partition list GUI elements. Those classes will now utilize a
single GUIScrollList class that will handle rendering and
scrolling.

Change-Id: I0cb98ab36cf47178296034293435225658c779cd
diff --git a/gui/Android.mk b/gui/Android.mk
index fca5e07..93913f5 100644
--- a/gui/Android.mk
+++ b/gui/Android.mk
@@ -25,7 +25,8 @@
     input.cpp \
     blanktimer.cpp \
     partitionlist.cpp \
-    mousecursor.cpp
+    mousecursor.cpp \
+    scrolllist.cpp
 
 ifneq ($(TWRP_CUSTOM_KEYBOARD),)
   LOCAL_SRC_FILES += $(TWRP_CUSTOM_KEYBOARD)
diff --git a/gui/fileselector.cpp b/gui/fileselector.cpp
index cf7a9a9..c7805c4 100644
--- a/gui/fileselector.cpp
+++ b/gui/fileselector.cpp
@@ -16,25 +16,9 @@
 	along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-#include <linux/input.h>
-#include <pthread.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
-#include <fcntl.h>
-#include <sys/reboot.h>
 #include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/mman.h>
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <time.h>
-#include <unistd.h>
-#include <stdlib.h>
 #include <dirent.h>
-#include <ctype.h>
-
 #include <algorithm>
 
 extern "C" {
@@ -49,188 +33,23 @@
 
 #define TW_FILESELECTOR_UP_A_LEVEL "(Up A Level)"
 
-#define SCROLLING_SPEED_DECREMENT 6
-#define SCROLLING_FLOOR 10
-#define SCROLLING_MULTIPLIER 6
-
 int GUIFileSelector::mSortOrder = 0;
 
-GUIFileSelector::GUIFileSelector(xml_node<>* node) : GUIObject(node)
+GUIFileSelector::GUIFileSelector(xml_node<>* node) : GUIScrollList(node)
 {
 	xml_attribute<>* attr;
 	xml_node<>* child;
-	int header_separator_color_specified = 0, header_separator_height_specified = 0, header_text_color_specified = 0, header_background_color_specified = 0;
 
-	mStart = mLineSpacing = startY = mFontHeight = mSeparatorH = scrollingY = scrollingSpeed = 0;
-	mIconWidth = mIconHeight = mFolderIconHeight = mFileIconHeight = mFolderIconWidth = mFileIconWidth = mHeaderIconHeight = mHeaderIconWidth = 0;
-	mHeaderSeparatorH = mLineHeight = mHeaderIsStatic = mHeaderH = actualLineHeight = 0;
-	mFolderIcon = mFileIcon = mBackground = mFont = mHeaderIcon = NULL;
-	mBackgroundX = mBackgroundY = mBackgroundW = mBackgroundH = 0;
+	int mIconWidth = 0, mIconHeight = 0, mFolderIconHeight = 0, mFileIconHeight = 0, mFolderIconWidth = 0, mFileIconWidth = 0;
+	mFolderIcon = mFileIcon = NULL;
 	mShowFolders = mShowFiles = mShowNavFolders = 1;
-	mFastScrollW = mFastScrollLineW = mFastScrollRectW = mFastScrollRectH = 0;
-	mFastScrollRectX = mFastScrollRectY = -1;
 	mUpdate = 0;
-	touchDebounce = 6;
 	mPathVar = "cwd";
-	ConvertStrToColor("black", &mBackgroundColor);
-	ConvertStrToColor("black", &mHeaderBackgroundColor);
-	ConvertStrToColor("black", &mSeparatorColor);
-	ConvertStrToColor("black", &mHeaderSeparatorColor);
-	ConvertStrToColor("white", &mFontColor);
-	ConvertStrToColor("white", &mHeaderFontColor);
-	ConvertStrToColor("white", &mFastScrollLineColor);
-	ConvertStrToColor("white", &mFastScrollRectColor);
-	hasHighlightColor = false;
-	hasFontHighlightColor = false;
-	isHighlighted = false;
 	updateFileList = false;
-	startSelection = -1;
 
-	// Load header text
-	child = node->first_node("header");
-	if (child)
-	{
-		attr = child->first_attribute("icon");
-		if (attr)
-			mHeaderIcon = PageManager::FindResource(attr->value());
-
-		attr = child->first_attribute("background");
-		if (attr)
-		{
-			std::string color = attr->value();
-			ConvertStrToColor(color, &mHeaderBackgroundColor);
-			header_background_color_specified = -1;
-		}
-		attr = child->first_attribute("textcolor");
-		if (attr)
-		{
-			std::string color = attr->value();
-			ConvertStrToColor(color, &mHeaderFontColor);
-			header_text_color_specified = -1;
-		}
-		attr = child->first_attribute("separatorcolor");
-		if (attr)
-		{
-			std::string color = attr->value();
-			ConvertStrToColor(color, &mHeaderSeparatorColor);
-			header_separator_color_specified = -1;
-		}
-		attr = child->first_attribute("separatorheight");
-		if (attr) {
-			string parsevalue = gui_parse_text(attr->value());
-			mHeaderSeparatorH = atoi(parsevalue.c_str());
-			header_separator_height_specified = -1;
-		}
-	}
-	child = node->first_node("text");
-	if (child)  mHeaderText = child->value();
-
-	memset(&mHighlightColor, 0, sizeof(COLOR));
-	child = node->first_node("highlight");
-	if (child) {
-		attr = child->first_attribute("color");
-		if (attr) {
-			hasHighlightColor = true;
-			std::string color = attr->value();
-			ConvertStrToColor(color, &mHighlightColor);
-		}
-	}
-
-	// Simple way to check for static state
-	mLastValue = gui_parse_text(mHeaderText);
-	if (mLastValue != mHeaderText)
-		mHeaderIsStatic = 0;
-	else
-		mHeaderIsStatic = -1;
-
-	child = node->first_node("icon");
-	if (child)
-	{
-		attr = child->first_attribute("folder");
-		if (attr)
-			mFolderIcon = PageManager::FindResource(attr->value());
-		attr = child->first_attribute("file");
-		if (attr)
-			mFileIcon = PageManager::FindResource(attr->value());
-	}
-	child = node->first_node("background");
-	if (child)
-	{
-		attr = child->first_attribute("resource");
-		if (attr)
-			mBackground = PageManager::FindResource(attr->value());
-		attr = child->first_attribute("color");
-		if (attr)
-		{
-			std::string color = attr->value();
-			ConvertStrToColor(color, &mBackgroundColor);
-			if (!header_background_color_specified)
-				ConvertStrToColor(color, &mHeaderBackgroundColor);
-		}
-	}
-
-	// Load the placement
-	LoadPlacement(node->first_node("placement"), &mRenderX, &mRenderY, &mRenderW, &mRenderH);
-	SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
-
-	// Load the font, and possibly override the color
-	child = node->first_node("font");
-	if (child)
-	{
-		attr = child->first_attribute("resource");
-		if (attr)
-			mFont = PageManager::FindResource(attr->value());
-
-		attr = child->first_attribute("color");
-		if (attr)
-		{
-			std::string color = attr->value();
-			ConvertStrToColor(color, &mFontColor);
-			if (!header_text_color_specified)
-				ConvertStrToColor(color, &mHeaderFontColor);
-		}
-
-		attr = child->first_attribute("spacing");
-		if (attr) {
-			string parsevalue = gui_parse_text(attr->value());
-			mLineSpacing = atoi(parsevalue.c_str());
-		}
-
-		attr = child->first_attribute("highlightcolor");
-		memset(&mFontHighlightColor, 0, sizeof(COLOR));
-		if (attr)
-		{
-			std::string color = attr->value();
-			ConvertStrToColor(color, &mFontHighlightColor);
-			hasFontHighlightColor = true;
-		}
-	}
-
-	// Load the separator if it exists
-	child = node->first_node("separator");
-	if (child)
-	{
-		attr = child->first_attribute("color");
-		if (attr)
-		{
-			std::string color = attr->value();
-			ConvertStrToColor(color, &mSeparatorColor);
-			if (!header_separator_color_specified)
-				ConvertStrToColor(color, &mHeaderSeparatorColor);
-		}
-
-		attr = child->first_attribute("height");
-		if (attr) {
-			string parsevalue = gui_parse_text(attr->value());
-			mSeparatorH = atoi(parsevalue.c_str());
-			if (!header_separator_height_specified)
-				mHeaderSeparatorH = mSeparatorH;
-		}
-	}
-
+	// Load filter for filtering files (e.g. *.zip for only zips)
 	child = node->first_node("filter");
-	if (child)
-	{
+	if (child) {
 		attr = child->first_attribute("extn");
 		if (attr)
 			mExtn = attr->value();
@@ -247,8 +66,7 @@
 
 	// Handle the path variable
 	child = node->first_node("path");
-	if (child)
-	{
+	if (child) {
 		attr = child->first_attribute("name");
 		if (attr)
 			mPathVar = attr->value();
@@ -259,8 +77,7 @@
 
 	// Handle the result variable
 	child = node->first_node("data");
-	if (child)
-	{
+	if (child) {
 		attr = child->first_attribute("name");
 		if (attr)
 			mVariable = attr->value();
@@ -271,8 +88,7 @@
 
 	// Handle the sort variable
 	child = node->first_node("sort");
-	if (child)
-	{
+	if (child) {
 		attr = child->first_attribute("name");
 		if (attr)
 			mSortVariable = attr->value();
@@ -285,100 +101,37 @@
 
 	// Handle the selection variable
 	child = node->first_node("selection");
-	if (child)
-	{
-		attr = child->first_attribute("name");
-		if (attr)
-			mSelection = attr->value();
-		else
-			mSelection = "0";
-	} else
+	if (child && (attr = child->first_attribute("name")))
+		mSelection = attr->value();
+	else
 		mSelection = "0";
 
-	// Fast scroll colors
-	child = node->first_node("fastscroll");
-	if (child)
-	{
-		attr = child->first_attribute("linecolor");
-		if(attr)
-			ConvertStrToColor(attr->value(), &mFastScrollLineColor);
-
-		attr = child->first_attribute("rectcolor");
-		if(attr)
-			ConvertStrToColor(attr->value(), &mFastScrollRectColor);
-
-		attr = child->first_attribute("w");
-		if (attr) {
-			string parsevalue = gui_parse_text(attr->value());
-			mFastScrollW = atoi(parsevalue.c_str());
-		}
-
-		attr = child->first_attribute("linew");
-		if (attr) {
-			string parsevalue = gui_parse_text(attr->value());
-			mFastScrollLineW = atoi(parsevalue.c_str());
-		}
-
-		attr = child->first_attribute("rectw");
-		if (attr) {
-			string parsevalue = gui_parse_text(attr->value());
-			mFastScrollRectW = atoi(parsevalue.c_str());
-		}
-
-		attr = child->first_attribute("recth");
-		if (attr) {
-			string parsevalue = gui_parse_text(attr->value());
-			mFastScrollRectH = atoi(parsevalue.c_str());
-		}
+	// Get folder and file icons if present
+	child = node->first_node("icon");
+	if (child) {
+		attr = child->first_attribute("folder");
+		if (attr)
+			mFolderIcon = PageManager::FindResource(attr->value());
+		attr = child->first_attribute("file");
+		if (attr)
+			mFileIcon = PageManager::FindResource(attr->value());
 	}
-
-	// Retrieve the line height
-	mFontHeight = gr_getMaxFontHeight(mFont ? mFont->GetResource() : NULL);
-	mLineHeight = mFontHeight;
-	mHeaderH = mFontHeight;
-
-	if (mFolderIcon && mFolderIcon->GetResource())
-	{
+	if (mFolderIcon && mFolderIcon->GetResource()) {
 		mFolderIconWidth = gr_get_width(mFolderIcon->GetResource());
 		mFolderIconHeight = gr_get_height(mFolderIcon->GetResource());
-		if (mFolderIconHeight > (int)mLineHeight)
-			mLineHeight = mFolderIconHeight;
+		if (mFolderIconHeight > mIconHeight)
+			mIconHeight = mFolderIconHeight;
 		mIconWidth = mFolderIconWidth;
 	}
-
-	if (mFileIcon && mFileIcon->GetResource())
-	{
+	if (mFileIcon && mFileIcon->GetResource()) {
 		mFileIconWidth = gr_get_width(mFileIcon->GetResource());
 		mFileIconHeight = gr_get_height(mFileIcon->GetResource());
-		if (mFileIconHeight > (int)mLineHeight)
-			mLineHeight = mFileIconHeight;
+		if (mFileIconHeight > mIconHeight)
+			mIconHeight = mFileIconHeight;
 		if (mFileIconWidth > mIconWidth)
 			mIconWidth = mFileIconWidth;
 	}
-
-	if (mHeaderIcon && mHeaderIcon->GetResource())
-	{
-		mHeaderIconWidth = gr_get_width(mHeaderIcon->GetResource());
-		mHeaderIconHeight = gr_get_height(mHeaderIcon->GetResource());
-		if (mHeaderIconHeight > mHeaderH)
-			mHeaderH = mHeaderIconHeight;
-		if (mHeaderIconWidth > mIconWidth)
-			mIconWidth = mHeaderIconWidth;
-	}
-
-	mHeaderH += mLineSpacing + mHeaderSeparatorH;
-	actualLineHeight = mLineHeight + mLineSpacing + mSeparatorH;
-	if (mHeaderH < actualLineHeight)
-		mHeaderH = actualLineHeight;
-
-	if (actualLineHeight / 3 > 6)
-		touchDebounce = actualLineHeight / 3;
-
-	if (mBackground && mBackground->GetResource())
-	{
-		mBackgroundW = gr_get_width(mBackground->GetResource());
-		mBackgroundH = gr_get_height(mBackground->GetResource());
-	}
+	SetMaxIconSize(mIconWidth, mIconHeight);
 
 	// Fetch the file/folder list
 	std::string value;
@@ -388,194 +141,8 @@
 
 GUIFileSelector::~GUIFileSelector()
 {
-}
-
-int GUIFileSelector::Render(void)
-{
-	if(!isConditionTrue())
-		return 0;
-
-	// First step, fill background
-	gr_color(mBackgroundColor.red, mBackgroundColor.green, mBackgroundColor.blue, 255);
-	gr_fill(mRenderX, mRenderY + mHeaderH, mRenderW, mRenderH - mHeaderH);
-
-	// Next, render the background resource (if it exists)
-	if (mBackground && mBackground->GetResource())
-	{
-		mBackgroundX = mRenderX + ((mRenderW - mBackgroundW) / 2);
-		mBackgroundY = mRenderY + ((mRenderH - mBackgroundH) / 2);
-		gr_blit(mBackground->GetResource(), 0, 0, mBackgroundW, mBackgroundH, mBackgroundX, mBackgroundY);
-	}
-
-	// Update the file list if needed
-	if (updateFileList) {
-		string value;
-		DataManager::GetValue(mPathVar, value);
-		if (GetFileList(value) == 0)
-			updateFileList = false;
-		else
-			return 0;
-	}
-
-	// This tells us how many lines we can actually render
-	int lines = (mRenderH - mHeaderH) / (actualLineHeight);
-	int line;
-
-	int folderSize = mShowFolders ? mFolderList.size() : 0;
-	int fileSize = mShowFiles ? mFileList.size() : 0;
-
-	int listW = mRenderW;
-
-	if (folderSize + fileSize < lines) {
-		lines = folderSize + fileSize;
-		scrollingY = 0;
-		mFastScrollRectX = mFastScrollRectY = -1;
-	} else {
-		listW -= mFastScrollW; // space for fast scroll
-		lines++;
-		if (lines < folderSize + fileSize)
-			lines++;
-	}
-
-	void* fontResource = NULL;
-	if (mFont)  fontResource = mFont->GetResource();
-
-	int yPos = mRenderY + mHeaderH + scrollingY;
-	int fontOffsetY = (int)((actualLineHeight - mFontHeight) / 2);
-	int currentIconHeight = 0, currentIconWidth = 0;
-	int currentIconOffsetY = 0, currentIconOffsetX = 0;
-	int folderIconOffsetY = (int)((actualLineHeight - mFolderIconHeight) / 2), fileIconOffsetY = (int)((actualLineHeight - mFileIconHeight) / 2);
-	int folderIconOffsetX = (mIconWidth - mFolderIconWidth) / 2, fileIconOffsetX = (mIconWidth - mFileIconWidth) / 2;
-	int actualSelection = mStart;
-
-	if (isHighlighted) {
-		int selectY = scrollingY;
-
-		// Locate the correct line for highlighting
-		while (selectY + actualLineHeight < startSelection) {
-			selectY += actualLineHeight;
-			actualSelection++;
-		}
-		if (hasHighlightColor) {
-			// Highlight the area
-			gr_color(mHighlightColor.red, mHighlightColor.green, mHighlightColor.blue, 255);
-			int HighlightHeight = actualLineHeight;
-			if (mRenderY + mHeaderH + selectY + actualLineHeight > mRenderH + mRenderY) {
-				HighlightHeight = actualLineHeight - (mRenderY + mHeaderH + selectY + actualLineHeight - mRenderH - mRenderY);
-			}
-			gr_fill(mRenderX, mRenderY + mHeaderH + selectY, mRenderW, HighlightHeight);
-		}
-	}
-
-	for (line = 0; line < lines; line++)
-	{
-		Resource* icon;
-		std::string label;
-
-		if (isHighlighted && hasFontHighlightColor && line + mStart == actualSelection) {
-			// Use the highlight color for the font
-			gr_color(mFontHighlightColor.red, mFontHighlightColor.green, mFontHighlightColor.blue, 255);
-		} else {
-			// Set the color for the font
-			gr_color(mFontColor.red, mFontColor.green, mFontColor.blue, 255);
-		}
-
-		if (line + mStart < folderSize)
-		{
-			icon = mFolderIcon;
-			label = mFolderList.at(line + mStart).fileName;
-			currentIconHeight = mFolderIconHeight;
-			currentIconWidth = mFolderIconWidth;
-			currentIconOffsetY = folderIconOffsetY;
-			currentIconOffsetX = folderIconOffsetX;
-		}
-		else if (line + mStart < folderSize + fileSize)
-		{
-			icon = mFileIcon;
-			label = mFileList.at((line + mStart) - folderSize).fileName;
-			currentIconHeight = mFileIconHeight;
-			currentIconWidth = mFileIconWidth;
-			currentIconOffsetY = fileIconOffsetY;
-			currentIconOffsetX = fileIconOffsetX;
-		} else {
-			continue;
-		}
-
-		if (icon && icon->GetResource())
-		{
-			int rect_y = 0, image_y = (yPos + currentIconOffsetY);
-			if (image_y + currentIconHeight > mRenderY + mRenderH)
-				rect_y = mRenderY + mRenderH - image_y;
-			else
-				rect_y = currentIconHeight;
-			gr_blit(icon->GetResource(), 0, 0, currentIconWidth, rect_y, mRenderX + currentIconOffsetX, image_y);
-		}
-		gr_textExWH(mRenderX + mIconWidth + 5, yPos + fontOffsetY, label.c_str(), fontResource, mRenderX + listW, mRenderY + mRenderH);
-
-		// Add the separator
-		if (yPos + actualLineHeight < mRenderH + mRenderY) {
-			gr_color(mSeparatorColor.red, mSeparatorColor.green, mSeparatorColor.blue, 255);
-			gr_fill(mRenderX, yPos + actualLineHeight - mSeparatorH, listW, mSeparatorH);
-		}
-
-		// Move the yPos
-		yPos += actualLineHeight;
-	}
-
-	// Render the Header (last so that it overwrites the top most row for per pixel scrolling)
-	// First step, fill background
-	gr_color(mHeaderBackgroundColor.red, mHeaderBackgroundColor.green, mHeaderBackgroundColor.blue, 255);
-	gr_fill(mRenderX, mRenderY, mRenderW, mHeaderH);
-
-	// Now, we need the header (icon + text)
-	yPos = mRenderY;
-	{
-		Resource* headerIcon;
-		int mIconOffsetX = 0;
-
-		// render the icon if it exists
-		headerIcon = mHeaderIcon;
-		if (headerIcon && headerIcon->GetResource())
-		{
-			gr_blit(headerIcon->GetResource(), 0, 0, mHeaderIconWidth, mHeaderIconHeight, mRenderX + ((mHeaderIconWidth - mIconWidth) / 2), (yPos + (int)((mHeaderH - mHeaderIconHeight) / 2)));
-			mIconOffsetX = mIconWidth;
-		}
-
-		// render the text
-		gr_color(mHeaderFontColor.red, mHeaderFontColor.green, mHeaderFontColor.blue, 255);
-		gr_textExWH(mRenderX + mIconOffsetX + 5, yPos + (int)((mHeaderH - mFontHeight) / 2), mLastValue.c_str(), fontResource, mRenderX + mRenderW, mRenderY + mRenderH);
-
-		// Add the separator
-		gr_color(mHeaderSeparatorColor.red, mHeaderSeparatorColor.green, mHeaderSeparatorColor.blue, 255);
-		gr_fill(mRenderX, yPos + mHeaderH - mHeaderSeparatorH, mRenderW, mHeaderSeparatorH);
-	}
-
-	// render fast scroll
-	lines = (mRenderH - mHeaderH) / (actualLineHeight);
-	if(mFastScrollW > 0 && folderSize + fileSize > lines)
-	{
-		int startX = listW + mRenderX;
-		int fWidth = mRenderW - listW;
-		int fHeight = mRenderH - mHeaderH;
-
-		// line
-		gr_color(mFastScrollLineColor.red, mFastScrollLineColor.green, mFastScrollLineColor.blue, 255);
-		gr_fill(startX + fWidth/2, mRenderY + mHeaderH, mFastScrollLineW, mRenderH - mHeaderH);
-
-		// rect
-		int pct = ((mStart*actualLineHeight - scrollingY)*100)/((folderSize + fileSize)*actualLineHeight-lines*actualLineHeight);
-		mFastScrollRectX = startX + (fWidth - mFastScrollRectW)/2;
-		mFastScrollRectY = mRenderY+mHeaderH + ((fHeight - mFastScrollRectH)*pct)/100;
-
-		gr_color(mFastScrollRectColor.red, mFastScrollRectColor.green, mFastScrollRectColor.blue, 255);
-		gr_fill(mFastScrollRectX, mFastScrollRectY, mFastScrollRectW, mFastScrollRectH);
-	}
-
-	// If a change came in during the render then we need to do another redraw so leave mUpdate alone if updateFileList is true.
-	if (!updateFileList) {
-		mUpdate = 0;
-	}
-	return 0;
+	delete mFileIcon;
+	delete mFolderIcon;
 }
 
 int GUIFileSelector::Update(void)
@@ -583,338 +150,52 @@
 	if(!isConditionTrue())
 		return 0;
 
-	if (!mHeaderIsStatic) {
-		std::string newValue = gui_parse_text(mHeaderText);
-		if (mLastValue != newValue) {
-			mLastValue = newValue;
+	GUIScrollList::Update();
+
+	// Update the file list if needed
+	if (updateFileList) {
+		string value;
+		DataManager::GetValue(mPathVar, value);
+		if (GetFileList(value) == 0) {
+			updateFileList = false;
 			mUpdate = 1;
-		}
+		} else
+			return 0;
 	}
 
-	if (mUpdate)
-	{
+	if (mUpdate) {
 		mUpdate = 0;
 		if (Render() == 0)
 			return 2;
 	}
-
-	// Handle kinetic scrolling
-	if (scrollingSpeed == 0) {
-		// Do nothing
-	} else if (scrollingSpeed > 0) {
-		if (scrollingSpeed < ((int) (actualLineHeight * 2.5))) {
-			scrollingY += scrollingSpeed;
-			scrollingSpeed -= SCROLLING_SPEED_DECREMENT;
-		} else {
-			scrollingY += ((int) (actualLineHeight * 2.5));
-			scrollingSpeed -= SCROLLING_SPEED_DECREMENT;
-		}
-		while (mStart && scrollingY > 0) {
-			mStart--;
-			scrollingY -= actualLineHeight;
-		}
-		if (mStart == 0 && scrollingY > 0) {
-			scrollingY = 0;
-			scrollingSpeed = 0;
-		} else if (scrollingSpeed < SCROLLING_FLOOR)
-			scrollingSpeed = 0;
-		mUpdate = 1;
-	} else if (scrollingSpeed < 0) {
-		int totalSize = (mShowFolders ? mFolderList.size() : 0) + (mShowFiles ? mFileList.size() : 0);
-		int lines = (mRenderH - mHeaderH) / (actualLineHeight);
-
-		if (totalSize > lines) {
-			int bottom_offset = ((int)(mRenderH) - mHeaderH) - (lines * actualLineHeight);
-
-			bottom_offset -= actualLineHeight;
-
-			if (abs(scrollingSpeed) < ((int) (actualLineHeight * 2.5))) {
-				scrollingY += scrollingSpeed;
-				scrollingSpeed += SCROLLING_SPEED_DECREMENT;
-			} else {
-				scrollingY -= ((int) (actualLineHeight * 2.5));
-				scrollingSpeed += SCROLLING_SPEED_DECREMENT;
-			}
-			while (mStart + lines + (bottom_offset ? 1 : 0) < totalSize && abs(scrollingY) > actualLineHeight) {
-				mStart++;
-				scrollingY += actualLineHeight;
-			}
-			if (bottom_offset != 0 && mStart + lines + 1 >= totalSize && scrollingY <= bottom_offset) {
-				mStart = totalSize - lines - 1;
-				scrollingY = bottom_offset;
-			} else if (mStart + lines >= totalSize && scrollingY < 0) {
-				mStart = totalSize - lines;
-				scrollingY = 0;
-			} else if (scrollingSpeed * -1 < SCROLLING_FLOOR)
-				scrollingSpeed = 0;
-			mUpdate = 1;
-		}
-	}
-
-	return 0;
-}
-
-int GUIFileSelector::GetSelection(int x, int y)
-{
-	// We only care about y position
-	if (y < mRenderY || y - mRenderY <= mHeaderH || y - mRenderY > mRenderH)
-		return -1;
-
-	return (y - mRenderY - mHeaderH);
-}
-
-int GUIFileSelector::NotifyTouch(TOUCH_STATE state, int x, int y)
-{
-	if(!isConditionTrue())
-		return -1;
-
-	static int lastY = 0, last2Y = 0, fastScroll = 0;
-	int selection = 0;
-
-	switch (state)
-	{
-	case TOUCH_START:
-		if (scrollingSpeed != 0)
-			startSelection = -1;
-		else
-			startSelection = GetSelection(x,y);
-		isHighlighted = (startSelection > -1);
-		if (isHighlighted)
-			mUpdate = 1;
-		startY = lastY = last2Y = y;
-		scrollingSpeed = 0;
-
-		if(mFastScrollRectX != -1 && x >= mRenderX + mRenderW - mFastScrollW)
-			fastScroll = 1;
-		break;
-	case TOUCH_DRAG:
-		// Check if we dragged out of the selection window
-		if (GetSelection(x, y) == -1) {
-			last2Y = lastY = 0;
-			if (isHighlighted) {
-				isHighlighted = false;
-				mUpdate = 1;
-			}
-			break;
-		}
-
-		// Fast scroll
-		if(fastScroll)
-		{
-			int pct = ((y-mRenderY-mHeaderH)*100)/(mRenderH-mHeaderH);
-			int totalSize = (mShowFolders ? mFolderList.size() : 0) + (mShowFiles ? mFileList.size() : 0);
-			int lines = (mRenderH - mHeaderH) / (actualLineHeight);
-
-			float l = float((totalSize-lines)*pct)/100;
-			if(l + lines >= totalSize)
-			{
-				mStart = totalSize - lines;
-				scrollingY = 0;
-			}
-			else
-			{
-				mStart = l;
-				scrollingY = -(l - int(l))*actualLineHeight;
-			}
-
-			startSelection = -1;
-			mUpdate = 1;
-			scrollingSpeed = 0;
-			isHighlighted = false;
-			break;
-		}
-
-		// Provide some debounce on initial touches
-		if (startSelection != -1 && abs(y - startY) < touchDebounce) {
-			isHighlighted = true;
-			mUpdate = 1;
-			break;
-		}
-
-		isHighlighted = false;
-		last2Y = lastY;
-		lastY = y;
-		startSelection = -1;
-
-		// Handle scrolling
-		scrollingY += y - startY;
-		startY = y;
-		while(mStart && scrollingY > 0) {
-			mStart--;
-			scrollingY -= actualLineHeight;
-		}
-		if (mStart == 0 && scrollingY > 0)
-			scrollingY = 0;
-		{
-			int totalSize = (mShowFolders ? mFolderList.size() : 0) + (mShowFiles ? mFileList.size() : 0);
-			int lines = (mRenderH - mHeaderH) / (actualLineHeight);
-
-			if (totalSize > lines) {
-				int bottom_offset = ((int)(mRenderH) - mHeaderH) - (lines * actualLineHeight);
-
-				bottom_offset -= actualLineHeight;
-
-				while (mStart + lines + (bottom_offset ? 1 : 0) < totalSize && abs(scrollingY) > actualLineHeight) {
-					mStart++;
-					scrollingY += actualLineHeight;
-				}
-				if (bottom_offset != 0 && mStart + lines + 1 >= totalSize && scrollingY <= bottom_offset) {
-					mStart = totalSize - lines - 1;
-					scrollingY = bottom_offset;
-				} else if (mStart + lines >= totalSize && scrollingY < 0) {
-					mStart = totalSize - lines;
-					scrollingY = 0;
-				}
-			} else
-				scrollingY = 0;
-		}
-		mUpdate = 1;
-		break;
-
-	case TOUCH_RELEASE:
-		isHighlighted = false;
-		fastScroll = 0;
-		if (startSelection >= 0)
-		{
-			// We've selected an item!
-			std::string str;
-
-			int folderSize = mShowFolders ? mFolderList.size() : 0;
-			int fileSize = mShowFiles ? mFileList.size() : 0;
-			int selectY = scrollingY, actualSelection = mStart;
-
-			// Move the selection to the proper place in the array
-			while (selectY + actualLineHeight < startSelection) {
-				selectY += actualLineHeight;
-				actualSelection++;
-			}
-			startSelection = actualSelection;
-
-			if (startSelection < folderSize + fileSize)
-			{
-				DataManager::Vibrate("tw_button_vibrate");
-
-				if (startSelection < folderSize)
-				{
-					std::string oldcwd;
-					std::string cwd;
-
-					str = mFolderList.at(startSelection).fileName;
-					if (mSelection != "0")
-						DataManager::SetValue(mSelection, str);
-					DataManager::GetValue(mPathVar, cwd);
-
-					oldcwd = cwd;
-					// Ignore requests to do nothing
-					if (str == ".")	 return 0;
-					if (str == TW_FILESELECTOR_UP_A_LEVEL)
-					{
-						if (cwd != "/")
-						{
-							size_t found;
-							found = cwd.find_last_of('/');
-							cwd = cwd.substr(0,found);
-
-							if (cwd.length() < 2)   cwd = "/";
-						}
-					}
-					else
-					{
-						// Add a slash if we're not the root folder
-						if (cwd != "/")	 cwd += "/";
-						cwd += str;
-					}
-
-					if (mShowNavFolders == 0 && mShowFiles == 0)
-					{
-						// This is a "folder" selection
-						DataManager::SetValue(mVariable, cwd);
-					}
-					else
-					{
-						DataManager::SetValue(mPathVar, cwd);
-						mStart = 0;
-						scrollingY = 0;
-						mUpdate = 1;
-					}
-				}
-				else if (!mVariable.empty())
-				{
-					str = mFileList.at(startSelection - folderSize).fileName;
-					if (mSelection != "0")
-						DataManager::SetValue(mSelection, str);
-
-					std::string cwd;
-					DataManager::GetValue(mPathVar, cwd);
-					if (cwd != "/")	 cwd += "/";
-					DataManager::SetValue(mVariable, cwd + str);
-				}
-			}
-		} else {
-			// This is for kinetic scrolling
-			scrollingSpeed = lastY - last2Y;
-			if (abs(scrollingSpeed) > SCROLLING_FLOOR)
-				scrollingSpeed *= SCROLLING_MULTIPLIER;
-			else
-				scrollingSpeed = 0;
-		}
-	case TOUCH_REPEAT:
-	case TOUCH_HOLD:
-		break;
-	}
 	return 0;
 }
 
 int GUIFileSelector::NotifyVarChange(const std::string& varName, const std::string& value)
 {
-	GUIObject::NotifyVarChange(varName, value);
-
 	if(!isConditionTrue())
 		return 0;
 
+	GUIScrollList::NotifyVarChange(varName, value);
+
 	if (varName.empty()) {
 		// Always clear the data variable so we know to use it
 		DataManager::SetValue(mVariable, "");
 	}
-	if (!mHeaderIsStatic) {
-		std::string newValue = gui_parse_text(mHeaderText);
-		if (mLastValue != newValue) {
-			mLastValue = newValue;
-			mStart = 0;
-			scrollingY = 0;
-			scrollingSpeed = 0;
-			mUpdate = 1;
-		}
-	}
-	if (varName == mPathVar || varName == mSortVariable)
-	{
+	if (varName == mPathVar || varName == mSortVariable) {
 		if (varName == mSortVariable) {
 			DataManager::GetValue(mSortVariable, mSortOrder);
+		} else {
+			// Reset the list to the top
+			SetVisibleListLocation(0);
 		}
 		updateFileList = true;
-		mStart = 0;
-		scrollingY = 0;
-		scrollingSpeed = 0;
 		mUpdate = 1;
 		return 0;
 	}
 	return 0;
 }
 
-int GUIFileSelector::SetRenderPos(int x, int y, int w /* = 0 */, int h /* = 0 */)
-{
-	mRenderX = x;
-	mRenderY = y;
-	if (w || h)
-	{
-		mRenderW = w;
-		mRenderH = h;
-	}
-	SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
-	mUpdate = 1;
-	return 0;
-}
-
 bool GUIFileSelector::fileSort(FileData d1, FileData d2)
 {
 	if (d1.fileName == ".")
@@ -948,6 +229,7 @@
 		default: // should be a 1 - sort by name ascending
 			return (strcasecmp(d1.fileName.c_str(), d2.fileName.c_str()) < 0);
 	}
+	return 0;
 }
 
 int GUIFileSelector::GetFileList(const std::string folder)
@@ -961,8 +243,7 @@
 	mFileList.clear();
 
 	d = opendir(folder.c_str());
-	if (d == NULL)
-	{
+	if (d == NULL) {
 		LOGINFO("Unable to open '%s'\n", folder.c_str());
 		if (folder != "/" && (mShowNavFolders != 0 || mShowFiles != 0)) {
 			size_t found;
@@ -978,8 +259,7 @@
 		return -1;
 	}
 
-	while ((de = readdir(d)) != NULL)
-	{
+	while ((de = readdir(d)) != NULL) {
 		FileData data;
 
 		data.fileName = de->d_name;
@@ -1007,15 +287,11 @@
 		if (data.fileType == DT_UNKNOWN) {
 			data.fileType = TWFunc::Get_D_Type_From_Stat(path);
 		}
-		if (data.fileType == DT_DIR)
-		{
+		if (data.fileType == DT_DIR) {
 			if (mShowNavFolders || (data.fileName != "." && data.fileName != TW_FILESELECTOR_UP_A_LEVEL))
 				mFolderList.push_back(data);
-		}
-		else if (data.fileType == DT_REG || data.fileType == DT_LNK || data.fileType == DT_BLK)
-		{
-			if (mExtn.empty() || (data.fileName.length() > mExtn.length() && data.fileName.substr(data.fileName.length() - mExtn.length()) == mExtn))
-			{
+		} else if (data.fileType == DT_REG || data.fileType == DT_LNK || data.fileType == DT_BLK) {
+			if (mExtn.empty() || (data.fileName.length() > mExtn.length() && data.fileName.substr(data.fileName.length() - mExtn.length()) == mExtn)) {
 				mFileList.push_back(data);
 			}
 		}
@@ -1030,11 +306,85 @@
 
 void GUIFileSelector::SetPageFocus(int inFocus)
 {
-	if (inFocus)
-	{
+	GUIScrollList::SetPageFocus(inFocus);
+	if (inFocus) {
 		updateFileList = true;
-		scrollingY = 0;
-		scrollingSpeed = 0;
 		mUpdate = 1;
 	}
 }
+
+size_t GUIFileSelector::GetItemCount()
+{
+	size_t folderSize = mShowFolders ? mFolderList.size() : 0;
+	size_t fileSize = mShowFiles ? mFileList.size() : 0;
+	return folderSize + fileSize;
+}
+
+int GUIFileSelector::GetListItem(size_t item_index, Resource*& icon, std::string &text)
+{
+	size_t folderSize = mShowFolders ? mFolderList.size() : 0;
+	size_t fileSize = mShowFiles ? mFileList.size() : 0;
+
+	if (item_index < folderSize) {
+		text = mFolderList.at(item_index).fileName;
+		icon = mFolderIcon;
+	} else {
+		text = mFileList.at(item_index - folderSize).fileName;
+		icon = mFileIcon;
+	}
+	return 0;
+}
+
+void GUIFileSelector::NotifySelect(size_t item_selected)
+{
+	size_t folderSize = mShowFolders ? mFolderList.size() : 0;
+	size_t fileSize = mShowFiles ? mFileList.size() : 0;
+
+	if (item_selected < folderSize + fileSize) {
+		// We've selected an item!
+		std::string str;
+		if (item_selected < folderSize) {
+			std::string cwd;
+
+			str = mFolderList.at(item_selected).fileName;
+			if (mSelection != "0")
+				DataManager::SetValue(mSelection, str);
+			DataManager::GetValue(mPathVar, cwd);
+
+			// Ignore requests to do nothing
+			if (str == ".")	 return;
+			if (str == TW_FILESELECTOR_UP_A_LEVEL) {
+				if (cwd != "/") {
+					size_t found;
+					found = cwd.find_last_of('/');
+					cwd = cwd.substr(0,found);
+
+					if (cwd.length() < 2)   cwd = "/";
+				}
+			} else {
+				// Add a slash if we're not the root folder
+				if (cwd != "/")	 cwd += "/";
+				cwd += str;
+			}
+
+			if (mShowNavFolders == 0 && mShowFiles == 0) {
+				// nav folders and files are disabled, this is probably the restore list and we need to save chosen location to mVariable instead of mPathVar
+				DataManager::SetValue(mVariable, cwd);
+			} else {
+				// We are changing paths, so we need to set mPathVar
+				DataManager::SetValue(mPathVar, cwd);
+			}
+		} else if (!mVariable.empty()) {
+			str = mFileList.at(item_selected - folderSize).fileName;
+			if (mSelection != "0")
+				DataManager::SetValue(mSelection, str);
+
+			std::string cwd;
+			DataManager::GetValue(mPathVar, cwd);
+			if (cwd != "/")
+				cwd += "/";
+			DataManager::SetValue(mVariable, cwd + str);
+		}
+	}
+	mUpdate = 1;
+}
diff --git a/gui/listbox.cpp b/gui/listbox.cpp
index 851b373..545b7a2 100644
--- a/gui/listbox.cpp
+++ b/gui/listbox.cpp
@@ -16,26 +16,9 @@
 	along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-#include <linux/input.h>
-#include <pthread.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
-#include <fcntl.h>
-#include <sys/reboot.h>
 #include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/mman.h>
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <time.h>
-#include <unistd.h>
-#include <stdlib.h>
 #include <dirent.h>
-#include <ctype.h>
-
-#include <algorithm>
 
 extern "C" {
 #include "../twcommon.h"
@@ -45,100 +28,18 @@
 #include "rapidxml.hpp"
 #include "objects.hpp"
 #include "../data.hpp"
-#include "../twrp-functions.hpp"
 
-#define SCROLLING_SPEED_DECREMENT 6
-#define SCROLLING_FLOOR 10
-#define SCROLLING_MULTIPLIER 6
-
-GUIListBox::GUIListBox(xml_node<>* node) : GUIObject(node)
+GUIListBox::GUIListBox(xml_node<>* node) : GUIScrollList(node)
 {
 	xml_attribute<>* attr;
 	xml_node<>* child;
-	int header_separator_color_specified = 0, header_separator_height_specified = 0, header_text_color_specified = 0, header_background_color_specified = 0;
-
-	mStart = mLineSpacing = startY = mFontHeight = mSeparatorH = scrollingY = scrollingSpeed = 0;
-	mIconWidth = mIconHeight = mSelectedIconHeight = mSelectedIconWidth = mUnselectedIconHeight = mUnselectedIconWidth = mHeaderIconHeight = mHeaderIconWidth = 0;
-	mHeaderSeparatorH = mLineHeight = mHeaderIsStatic = mHeaderH = actualLineHeight = 0;
-	mIconSelected = mIconUnselected = mBackground = mFont = mHeaderIcon = NULL;
-	mBackgroundX = mBackgroundY = mBackgroundW = mBackgroundH = 0;
-	mFastScrollW = mFastScrollLineW = mFastScrollRectW = mFastScrollRectH = 0;
-	mFastScrollRectX = mFastScrollRectY = -1;
+	mIconSelected = mIconUnselected = NULL;
+	int mSelectedIconWidth = 0, mSelectedIconHeight = 0, mUnselectedIconWidth = 0, mUnselectedIconHeight = 0, mIconWidth = 0, mIconHeight = 0;
 	mUpdate = 0;
-	touchDebounce = 6;
-	ConvertStrToColor("black", &mBackgroundColor);
-	ConvertStrToColor("black", &mHeaderBackgroundColor);
-	ConvertStrToColor("black", &mSeparatorColor);
-	ConvertStrToColor("black", &mHeaderSeparatorColor);
-	ConvertStrToColor("white", &mFontColor);
-	ConvertStrToColor("white", &mHeaderFontColor);
-	ConvertStrToColor("white", &mFastScrollLineColor);
-	ConvertStrToColor("white", &mFastScrollRectColor);
-	hasHighlightColor = false;
-	hasFontHighlightColor = false;
-	isHighlighted = false;
-	startSelection = -1;
 
-	// Load header text
-	child = node->first_node("header");
-	if (child)
-	{
-		attr = child->first_attribute("icon");
-		if (attr)
-			mHeaderIcon = PageManager::FindResource(attr->value());
-
-		attr = child->first_attribute("background");
-		if (attr)
-		{
-			std::string color = attr->value();
-			ConvertStrToColor(color, &mHeaderBackgroundColor);
-			header_background_color_specified = -1;
-		}
-		attr = child->first_attribute("textcolor");
-		if (attr)
-		{
-			std::string color = attr->value();
-			ConvertStrToColor(color, &mHeaderFontColor);
-			header_text_color_specified = -1;
-		}
-		attr = child->first_attribute("separatorcolor");
-		if (attr)
-		{
-			std::string color = attr->value();
-			ConvertStrToColor(color, &mHeaderSeparatorColor);
-			header_separator_color_specified = -1;
-		}
-		attr = child->first_attribute("separatorheight");
-		if (attr) {
-			string parsevalue = gui_parse_text(attr->value());
-			mHeaderSeparatorH = atoi(parsevalue.c_str());
-			header_separator_height_specified = -1;
-		}
-	}
-	child = node->first_node("text");
-	if (child)  mHeaderText = child->value();
-
-	memset(&mHighlightColor, 0, sizeof(COLOR));
-	child = node->first_node("highlight");
-	if (child) {
-		attr = child->first_attribute("color");
-		if (attr) {
-			hasHighlightColor = true;
-			std::string color = attr->value();
-			ConvertStrToColor(color, &mHighlightColor);
-		}
-	}
-
-	// Simple way to check for static state
-	mLastValue = gui_parse_text(mHeaderText);
-	if (mLastValue != mHeaderText)
-		mHeaderIsStatic = 0;
-	else
-		mHeaderIsStatic = -1;
-
+	// Get the icons, if any
 	child = node->first_node("icon");
-	if (child)
-	{
+	if (child) {
 		attr = child->first_attribute("selected");
 		if (attr)
 			mIconSelected = PageManager::FindResource(attr->value());
@@ -146,187 +47,41 @@
 		if (attr)
 			mIconUnselected = PageManager::FindResource(attr->value());
 	}
-	child = node->first_node("background");
-	if (child)
-	{
-		attr = child->first_attribute("resource");
-		if (attr)
-			mBackground = PageManager::FindResource(attr->value());
-		attr = child->first_attribute("color");
-		if (attr)
-		{
-			std::string color = attr->value();
-			ConvertStrToColor(color, &mBackgroundColor);
-			if (!header_background_color_specified)
-				ConvertStrToColor(color, &mHeaderBackgroundColor);
-		}
+	if (mIconSelected && mIconSelected->GetResource()) {
+		mSelectedIconWidth = gr_get_width(mIconSelected->GetResource());
+		mSelectedIconHeight = gr_get_height(mIconSelected->GetResource());
+		if (mSelectedIconHeight > mIconHeight)
+			mIconHeight = mSelectedIconHeight;
+		mIconWidth = mSelectedIconWidth;
 	}
 
-	// Load the placement
-	LoadPlacement(node->first_node("placement"), &mRenderX, &mRenderY, &mRenderW, &mRenderH);
-	SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
-
-	// Load the font, and possibly override the color
-	child = node->first_node("font");
-	if (child)
-	{
-		attr = child->first_attribute("resource");
-		if (attr)
-			mFont = PageManager::FindResource(attr->value());
-
-		attr = child->first_attribute("color");
-		if (attr)
-		{
-			std::string color = attr->value();
-			ConvertStrToColor(color, &mFontColor);
-			if (!header_text_color_specified)
-				ConvertStrToColor(color, &mHeaderFontColor);
-		}
-
-		attr = child->first_attribute("spacing");
-		if (attr) {
-			string parsevalue = gui_parse_text(attr->value());
-			mLineSpacing = atoi(parsevalue.c_str());
-		}
-
-		attr = child->first_attribute("highlightcolor");
-		memset(&mFontHighlightColor, 0, sizeof(COLOR));
-		if (attr)
-		{
-			std::string color = attr->value();
-			ConvertStrToColor(color, &mFontHighlightColor);
-			hasFontHighlightColor = true;
-		}
+	if (mIconUnselected && mIconUnselected->GetResource()) {
+		mUnselectedIconWidth = gr_get_width(mIconUnselected->GetResource());
+		mUnselectedIconHeight = gr_get_height(mIconUnselected->GetResource());
+		if (mUnselectedIconHeight > mIconHeight)
+			mIconHeight = mUnselectedIconHeight;
+		if (mUnselectedIconWidth > mIconWidth)
+			mIconWidth = mUnselectedIconWidth;
 	}
-
-	// Load the separator if it exists
-	child = node->first_node("separator");
-	if (child)
-	{
-		attr = child->first_attribute("color");
-		if (attr)
-		{
-			std::string color = attr->value();
-			ConvertStrToColor(color, &mSeparatorColor);
-			if (!header_separator_color_specified)
-				ConvertStrToColor(color, &mHeaderSeparatorColor);
-		}
-
-		attr = child->first_attribute("height");
-		if (attr) {
-			string parsevalue = gui_parse_text(attr->value());
-			mSeparatorH = atoi(parsevalue.c_str());
-			if (!header_separator_height_specified)
-				mHeaderSeparatorH = mSeparatorH;
-		}
-	}
+	SetMaxIconSize(mIconWidth, mIconHeight);
 
 	// Handle the result variable
 	child = node->first_node("data");
-	if (child)
-	{
+	if (child) {
 		attr = child->first_attribute("name");
 		if (attr)
 			mVariable = attr->value();
 		attr = child->first_attribute("default");
 		if (attr)
 			DataManager::SetValue(mVariable, attr->value());
+		// Get the currently selected value for the list
+		DataManager::GetValue(mVariable, currentValue);
 	}
 
-	// Fast scroll colors
-	child = node->first_node("fastscroll");
-	if (child)
-	{
-		attr = child->first_attribute("linecolor");
-		if(attr)
-			ConvertStrToColor(attr->value(), &mFastScrollLineColor);
-
-		attr = child->first_attribute("rectcolor");
-		if(attr)
-			ConvertStrToColor(attr->value(), &mFastScrollRectColor);
-
-		attr = child->first_attribute("w");
-		if (attr) {
-			string parsevalue = gui_parse_text(attr->value());
-			mFastScrollW = atoi(parsevalue.c_str());
-		}
-
-		attr = child->first_attribute("linew");
-		if (attr) {
-			string parsevalue = gui_parse_text(attr->value());
-			mFastScrollLineW = atoi(parsevalue.c_str());
-		}
-
-		attr = child->first_attribute("rectw");
-		if (attr) {
-			string parsevalue = gui_parse_text(attr->value());
-			mFastScrollRectW = atoi(parsevalue.c_str());
-		}
-
-		attr = child->first_attribute("recth");
-		if (attr) {
-			string parsevalue = gui_parse_text(attr->value());
-			mFastScrollRectH = atoi(parsevalue.c_str());
-		}
-	}
-
-	// Retrieve the line height
-	mFontHeight = gr_getMaxFontHeight(mFont ? mFont->GetResource() : NULL);
-	mLineHeight = mFontHeight;
-	mHeaderH = mFontHeight;
-
-	if (mIconSelected && mIconSelected->GetResource())
-	{
-		mSelectedIconWidth = gr_get_width(mIconSelected->GetResource());
-		mSelectedIconHeight = gr_get_height(mIconSelected->GetResource());
-		if (mSelectedIconHeight > (int)mLineHeight)
-			mLineHeight = mSelectedIconHeight;
-		mIconWidth = mSelectedIconWidth;
-	}
-
-	if (mIconUnselected && mIconUnselected->GetResource())
-	{
-		mUnselectedIconWidth = gr_get_width(mIconUnselected->GetResource());
-		mUnselectedIconHeight = gr_get_height(mIconUnselected->GetResource());
-		if (mUnselectedIconHeight > (int)mLineHeight)
-			mLineHeight = mUnselectedIconHeight;
-		if (mUnselectedIconWidth > mIconWidth)
-			mIconWidth = mUnselectedIconWidth;
-	}
-
-	if (mHeaderIcon && mHeaderIcon->GetResource())
-	{
-		mHeaderIconWidth = gr_get_width(mHeaderIcon->GetResource());
-		mHeaderIconHeight = gr_get_height(mHeaderIcon->GetResource());
-		if (mHeaderIconHeight > mHeaderH)
-			mHeaderH = mHeaderIconHeight;
-		if (mHeaderIconWidth > mIconWidth)
-			mIconWidth = mHeaderIconWidth;
-	}
-
-	mHeaderH += mLineSpacing + mHeaderSeparatorH;
-	actualLineHeight = mLineHeight + mLineSpacing + mSeparatorH;
-	if (mHeaderH < actualLineHeight)
-		mHeaderH = actualLineHeight;
-
-	if (actualLineHeight / 3 > 6)
-		touchDebounce = actualLineHeight / 3;
-
-	if (mBackground && mBackground->GetResource())
-	{
-		mBackgroundW = gr_get_width(mBackground->GetResource());
-		mBackgroundH = gr_get_height(mBackground->GetResource());
-	}
-
-	// Get the currently selected value for the list
-	DataManager::GetValue(mVariable, currentValue);
-
 	// Get the data for the list
 	child = node->first_node("listitem");
 	if (!child) return;
-
-	while (child)
-	{
+	while (child) {
 		ListData data;
 
 		attr = child->first_attribute("name");
@@ -348,179 +103,8 @@
 
 GUIListBox::~GUIListBox()
 {
-}
-
-int GUIListBox::Render(void)
-{
-	if(!isConditionTrue())
-		return 0;
-
-	// First step, fill background
-	gr_color(mBackgroundColor.red, mBackgroundColor.green, mBackgroundColor.blue, 255);
-	gr_fill(mRenderX, mRenderY + mHeaderH, mRenderW, mRenderH - mHeaderH);
-
-	// Next, render the background resource (if it exists)
-	if (mBackground && mBackground->GetResource())
-	{
-		mBackgroundX = mRenderX + ((mRenderW - mBackgroundW) / 2);
-		mBackgroundY = mRenderY + ((mRenderH - mBackgroundH) / 2);
-		gr_blit(mBackground->GetResource(), 0, 0, mBackgroundW, mBackgroundH, mBackgroundX, mBackgroundY);
-	}
-
-	// This tells us how many lines we can actually render
-	int lines = (mRenderH - mHeaderH) / (actualLineHeight);
-	int line;
-
-	int listSize = mList.size();
-	int listW = mRenderW;
-
-	if (listSize < lines) {
-		lines = listSize;
-		scrollingY = 0;
-		mFastScrollRectX = mFastScrollRectY = -1;
-	} else {
-		listW -= mFastScrollW; // space for fast scroll
-		lines++;
-		if (lines < listSize)
-			lines++;
-	}
-
-	void* fontResource = NULL;
-	if (mFont)  fontResource = mFont->GetResource();
-
-	int yPos = mRenderY + mHeaderH + scrollingY;
-	int fontOffsetY = (int)((actualLineHeight - mFontHeight) / 2);
-	int currentIconHeight = 0, currentIconWidth = 0;
-	int currentIconOffsetY = 0, currentIconOffsetX = 0;
-	int UnselectedIconOffsetY = (int)((actualLineHeight - mUnselectedIconHeight) / 2), SelectedIconOffsetY = (int)((actualLineHeight - mSelectedIconHeight) / 2);
-	int UnselectedIconOffsetX = (mIconWidth - mUnselectedIconWidth) / 2, SelectedIconOffsetX = (mIconWidth - mSelectedIconWidth) / 2;
-	int actualSelection = mStart;
-
-	if (isHighlighted) {
-		int selectY = scrollingY;
-
-		// Locate the correct line for highlighting
-		while (selectY + actualLineHeight < startSelection) {
-			selectY += actualLineHeight;
-			actualSelection++;
-		}
-		if (hasHighlightColor) {
-			// Highlight the area
-			gr_color(mHighlightColor.red, mHighlightColor.green, mHighlightColor.blue, 255);
-			int HighlightHeight = actualLineHeight;
-			if (mRenderY + mHeaderH + selectY + actualLineHeight > mRenderH + mRenderY) {
-				HighlightHeight = actualLineHeight - (mRenderY + mHeaderH + selectY + actualLineHeight - mRenderH - mRenderY);
-			}
-			gr_fill(mRenderX, mRenderY + mHeaderH + selectY, mRenderW, HighlightHeight);
-		}
-	}
-
-	for (line = 0; line < lines; line++)
-	{
-		Resource* icon;
-		std::string label;
-
-		if (line + mStart >= listSize)
-			continue;
-
-		label = mList.at(line + mStart).displayName;
-		if (isHighlighted && hasFontHighlightColor && line + mStart == actualSelection) {
-			// Use the highlight color for the font
-			gr_color(mFontHighlightColor.red, mFontHighlightColor.green, mFontHighlightColor.blue, 255);
-		} else {
-			// Set the color for the font
-			gr_color(mFontColor.red, mFontColor.green, mFontColor.blue, 255);
-		}
-
-		if (mList.at(line + mStart).selected != 0)
-		{
-			icon = mIconSelected;
-			currentIconHeight = mSelectedIconHeight;
-			currentIconWidth = mSelectedIconWidth;
-			currentIconOffsetY = SelectedIconOffsetY;
-			currentIconOffsetX = SelectedIconOffsetX;
-		}
-		else
-		{
-			icon = mIconUnselected;
-			currentIconHeight = mSelectedIconHeight;
-			currentIconWidth = mSelectedIconWidth;
-			currentIconOffsetY = SelectedIconOffsetY;
-			currentIconOffsetX = SelectedIconOffsetX;
-		}
-
-		if (icon && icon->GetResource())
-		{
-			int rect_y = 0, image_y = (yPos + currentIconOffsetY);
-			if (image_y + currentIconHeight > mRenderY + mRenderH)
-				rect_y = mRenderY + mRenderH - image_y;
-			else
-				rect_y = currentIconHeight;
-			gr_blit(icon->GetResource(), 0, 0, currentIconWidth, rect_y, mRenderX + currentIconOffsetX, image_y);
-		}
-		gr_textExWH(mRenderX + mIconWidth + 5, yPos + fontOffsetY, label.c_str(), fontResource, mRenderX + listW, mRenderY + mRenderH);
-
-		// Add the separator
-		if (yPos + actualLineHeight < mRenderH + mRenderY) {
-			gr_color(mSeparatorColor.red, mSeparatorColor.green, mSeparatorColor.blue, 255);
-			gr_fill(mRenderX, yPos + actualLineHeight - mSeparatorH, listW, mSeparatorH);
-		}
-
-		// Move the yPos
-		yPos += actualLineHeight;
-	}
-
-	// Render the Header (last so that it overwrites the top most row for per pixel scrolling)
-	// First step, fill background
-	gr_color(mHeaderBackgroundColor.red, mHeaderBackgroundColor.green, mHeaderBackgroundColor.blue, 255);
-	gr_fill(mRenderX, mRenderY, mRenderW, mHeaderH);
-
-	// Now, we need the header (icon + text)
-	yPos = mRenderY;
-	{
-		Resource* headerIcon;
-		int mIconOffsetX = 0;
-
-		// render the icon if it exists
-		headerIcon = mHeaderIcon;
-		if (headerIcon && headerIcon->GetResource())
-		{
-			gr_blit(headerIcon->GetResource(), 0, 0, mHeaderIconWidth, mHeaderIconHeight, mRenderX + ((mHeaderIconWidth - mIconWidth) / 2), (yPos + (int)((mHeaderH - mHeaderIconHeight) / 2)));
-			mIconOffsetX = mIconWidth;
-		}
-
-		// render the text
-		gr_color(mHeaderFontColor.red, mHeaderFontColor.green, mHeaderFontColor.blue, 255);
-		gr_textExWH(mRenderX + mIconOffsetX + 5, yPos + (int)((mHeaderH - mFontHeight) / 2), mLastValue.c_str(), fontResource, mRenderX + mRenderW, mRenderY + mRenderH);
-
-		// Add the separator
-		gr_color(mHeaderSeparatorColor.red, mHeaderSeparatorColor.green, mHeaderSeparatorColor.blue, 255);
-		gr_fill(mRenderX, yPos + mHeaderH - mHeaderSeparatorH, mRenderW, mHeaderSeparatorH);
-	}
-
-	// render fast scroll
-	lines = (mRenderH - mHeaderH) / (actualLineHeight);
-	if(mFastScrollW > 0 &&  listSize > lines)
-	{
-		int startX = listW + mRenderX;
-		int fWidth = mRenderW - listW;
-		int fHeight = mRenderH - mHeaderH;
-
-		// line
-		gr_color(mFastScrollLineColor.red, mFastScrollLineColor.green, mFastScrollLineColor.blue, 255);
-		gr_fill(startX + fWidth/2, mRenderY + mHeaderH, mFastScrollLineW, mRenderH - mHeaderH);
-
-		// rect
-		int pct = ((mStart*actualLineHeight - scrollingY)*100)/((listSize)*actualLineHeight-lines*actualLineHeight);
-		mFastScrollRectX = startX + (fWidth - mFastScrollRectW)/2;
-		mFastScrollRectY = mRenderY+mHeaderH + ((fHeight - mFastScrollRectH)*pct)/100;
-
-		gr_color(mFastScrollRectColor.red, mFastScrollRectColor.green, mFastScrollRectColor.blue, 255);
-		gr_fill(mFastScrollRectX, mFastScrollRectY, mFastScrollRectW, mFastScrollRectH);
-	}
-
-	mUpdate = 0;
-	return 0;
+	delete mIconSelected;
+	delete mIconUnselected;
 }
 
 int GUIListBox::Update(void)
@@ -528,306 +112,75 @@
 	if(!isConditionTrue())
 		return 0;
 
-	if (!mHeaderIsStatic) {
-		std::string newValue = gui_parse_text(mHeaderText);
-		if (mLastValue != newValue) {
-			mLastValue = newValue;
-			mUpdate = 1;
-		}
-	}
+	GUIScrollList::Update();
 
-	if (mUpdate)
-	{
+	if (mUpdate) {
 		mUpdate = 0;
 		if (Render() == 0)
 			return 2;
 	}
-
-	// Handle kinetic scrolling
-	if (scrollingSpeed == 0) {
-		// Do nothing
-	} else if (scrollingSpeed > 0) {
-		if (scrollingSpeed < ((int) (actualLineHeight * 2.5))) {
-			scrollingY += scrollingSpeed;
-			scrollingSpeed -= SCROLLING_SPEED_DECREMENT;
-		} else {
-			scrollingY += ((int) (actualLineHeight * 2.5));
-			scrollingSpeed -= SCROLLING_SPEED_DECREMENT;
-		}
-		while (mStart && scrollingY > 0) {
-			mStart--;
-			scrollingY -= actualLineHeight;
-		}
-		if (mStart == 0 && scrollingY > 0) {
-			scrollingY = 0;
-			scrollingSpeed = 0;
-		} else if (scrollingSpeed < SCROLLING_FLOOR)
-			scrollingSpeed = 0;
-		mUpdate = 1;
-	} else if (scrollingSpeed < 0) {
-		int totalSize = mList.size();
-		int lines = (mRenderH - mHeaderH) / (actualLineHeight);
-
-		if (totalSize > lines) {
-			int bottom_offset = ((int)(mRenderH) - mHeaderH) - (lines * actualLineHeight);
-
-			bottom_offset -= actualLineHeight;
-
-			if (abs(scrollingSpeed) < ((int) (actualLineHeight * 2.5))) {
-				scrollingY += scrollingSpeed;
-				scrollingSpeed += SCROLLING_SPEED_DECREMENT;
-			} else {
-				scrollingY -= ((int) (actualLineHeight * 2.5));
-				scrollingSpeed += SCROLLING_SPEED_DECREMENT;
-			}
-			while (mStart + lines + (bottom_offset ? 1 : 0) < totalSize && abs(scrollingY) > actualLineHeight) {
-				mStart++;
-				scrollingY += actualLineHeight;
-			}
-			if (bottom_offset != 0 && mStart + lines + 1 >= totalSize && scrollingY <= bottom_offset) {
-				mStart = totalSize - lines - 1;
-				scrollingY = bottom_offset;
-			} else if (mStart + lines >= totalSize && scrollingY < 0) {
-				mStart = totalSize - lines;
-				scrollingY = 0;
-			} else if (scrollingSpeed * -1 < SCROLLING_FLOOR)
-				scrollingSpeed = 0;
-			mUpdate = 1;
-		}
-	}
-
-	return 0;
-}
-
-int GUIListBox::GetSelection(int x, int y)
-{
-	// We only care about y position
-	if (y < mRenderY || y - mRenderY <= mHeaderH || y - mRenderY > mRenderH) return -1;
-	return (y - mRenderY - mHeaderH);
-}
-
-int GUIListBox::NotifyTouch(TOUCH_STATE state, int x, int y)
-{
-	if(!isConditionTrue())
-		return -1;
-
-	static int lastY = 0, last2Y = 0, fastScroll = 0;
-	int selection = 0;
-
-	switch (state)
-	{
-	case TOUCH_START:
-		if (scrollingSpeed != 0)
-			startSelection = -1;
-		else
-			startSelection = GetSelection(x,y);
-		isHighlighted = (startSelection > -1);
-		if (isHighlighted)
-			mUpdate = 1;
-		startY = lastY = last2Y = y;
-		scrollingSpeed = 0;
-
-		if(mFastScrollRectX != -1 && x >= mRenderX + mRenderW - mFastScrollW)
-			fastScroll = 1;
-		break;
-
-	case TOUCH_DRAG:
-		// Check if we dragged out of the selection window
-		if (GetSelection(x, y) == -1) {
-			last2Y = lastY = 0;
-			if (isHighlighted) {
-				isHighlighted = false;
-				mUpdate = 1;
-			}
-			break;
-		}
-
-		// Fast scroll
-		if(fastScroll)
-		{
-			int pct = ((y-mRenderY-mHeaderH)*100)/(mRenderH-mHeaderH);
-			int totalSize = mList.size();
-			int lines = (mRenderH - mHeaderH) / (actualLineHeight);
-
-			float l = float((totalSize-lines)*pct)/100;
-			if(l + lines >= totalSize)
-			{
-				mStart = totalSize - lines;
-				scrollingY = 0;
-			}
-			else
-			{
-				mStart = l;
-				scrollingY = -(l - int(l))*actualLineHeight;
-			}
-
-			startSelection = -1;
-			mUpdate = 1;
-			scrollingSpeed = 0;
-			isHighlighted = false;
-			break;
-		}
-
-		// Provide some debounce on initial touches
-		if (startSelection != -1 && abs(y - startY) < touchDebounce) {
-			isHighlighted = true;
-			mUpdate = 1;
-			break;
-		}
-
-		isHighlighted = false;
-		last2Y = lastY;
-		lastY = y;
-		startSelection = -1;
-
-		// Handle scrolling
-		scrollingY += y - startY;
-		startY = y;
-		while(mStart && scrollingY > 0) {
-			mStart--;
-			scrollingY -= actualLineHeight;
-		}
-		if (mStart == 0 && scrollingY > 0)
-			scrollingY = 0;
-		{
-			int totalSize = mList.size();
-			int lines = (mRenderH - mHeaderH) / (actualLineHeight);
-
-			if (totalSize > lines) {
-				int bottom_offset = ((int)(mRenderH) - mHeaderH) - (lines * actualLineHeight);
-
-				bottom_offset -= actualLineHeight;
-
-				while (mStart + lines + (bottom_offset ? 1 : 0) < totalSize && abs(scrollingY) > actualLineHeight) {
-					mStart++;
-					scrollingY += actualLineHeight;
-				}
-				if (bottom_offset != 0 && mStart + lines + 1 >= totalSize && scrollingY <= bottom_offset) {
-					mStart = totalSize - lines - 1;
-					scrollingY = bottom_offset;
-				} else if (mStart + lines >= totalSize && scrollingY < 0) {
-					mStart = totalSize - lines;
-					scrollingY = 0;
-				}
-			} else
-				scrollingY = 0;
-		}
-		mUpdate = 1;
-		break;
-
-	case TOUCH_RELEASE:
-		isHighlighted = false;
-		fastScroll = 0;
-		if (startSelection >= 0)
-		{
-			// We've selected an item!
-			std::string str;
-
-			int listSize = mList.size();
-			int selectY = scrollingY, actualSelection = mStart;
-
-			// Move the selection to the proper place in the array
-			while (selectY + actualLineHeight < startSelection) {
-				selectY += actualLineHeight;
-				actualSelection++;
-			}
-
-			if (actualSelection < listSize && !mVariable.empty())
-			{
-				int i;
-				for (i=0; i<listSize; i++)
-					mList.at(i).selected = 0;
-
-				str = mList.at(actualSelection).variableValue;
-				mList.at(actualSelection).selected = 1;
-				DataManager::SetValue(mVariable, str);
-				mUpdate = 1;
-
-				DataManager::Vibrate("tw_button_vibrate");
-			}
-		} else {
-			// This is for kinetic scrolling
-			scrollingSpeed = lastY - last2Y;
-			if (abs(scrollingSpeed) > SCROLLING_FLOOR)
-				scrollingSpeed *= SCROLLING_MULTIPLIER;
-			else
-				scrollingSpeed = 0;
-		}
-	case TOUCH_REPEAT:
-	case TOUCH_HOLD:
-		break;
-	}
 	return 0;
 }
 
 int GUIListBox::NotifyVarChange(const std::string& varName, const std::string& value)
 {
-	GUIObject::NotifyVarChange(varName, value);
-
 	if(!isConditionTrue())
 		return 0;
 
-	if (!mHeaderIsStatic) {
-		std::string newValue = gui_parse_text(mHeaderText);
-		if (mLastValue != newValue) {
-			mLastValue = newValue;
-			mStart = 0;
-			scrollingY = 0;
-			scrollingSpeed = 0;
-			mUpdate = 1;
-		}
-	}
-	if (varName == mVariable)
-	{
-		int i, listSize = mList.size(), selected_index = 0;
+	GUIScrollList::NotifyVarChange(varName, value);
+
+	// Check to see if the variable that we are using to store the list selected value has been updated
+	if (varName == mVariable) {
+		int i, listSize = mList.size();
 
 		currentValue = value;
 
-		for (i=0; i<listSize; i++) {
+		for (i = 0; i < listSize; i++) {
 			if (mList.at(i).variableValue == currentValue) {
 				mList.at(i).selected = 1;
-				selected_index = i;
+				SetVisibleListLocation(i);
 			} else
 				mList.at(i).selected = 0;
 		}
-
-		int lines = mRenderH / (mLineHeight + mLineSpacing);
-		int line;
-
-		if (selected_index > mStart + lines - 1)
-			mStart = selected_index;
-		if (mStart > listSize - lines) {
-			mStart = listSize - lines;
-		} else if (selected_index < mStart) {
-			mStart = selected_index;
-		}
-
 		mUpdate = 1;
 		return 0;
 	}
 	return 0;
 }
 
-int GUIListBox::SetRenderPos(int x, int y, int w /* = 0 */, int h /* = 0 */)
-{
-	mRenderX = x;
-	mRenderY = y;
-	if (w || h)
-	{
-		mRenderW = w;
-		mRenderH = h;
-	}
-	SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
-	mUpdate = 1;
-	return 0;
-}
-
 void GUIListBox::SetPageFocus(int inFocus)
 {
-	if (inFocus)
-	{
+	GUIScrollList::SetPageFocus(inFocus);
+	if (inFocus) {
 		DataManager::GetValue(mVariable, currentValue);
 		NotifyVarChange(mVariable, currentValue);
-		mUpdate = 1;
 	}
 }
+
+size_t GUIListBox::GetItemCount()
+{
+	return mList.size();
+}
+
+int GUIListBox::GetListItem(size_t item_index, Resource*& icon, std::string &text)
+{
+	text = mList.at(item_index).displayName;
+	if (mList.at(item_index).selected)
+		icon = mIconSelected;
+	else
+		icon = mIconUnselected;
+	return 0;
+}
+
+void GUIListBox::NotifySelect(size_t item_selected)
+{
+	for (size_t i = 0; i < mList.size(); i++) {
+		mList.at(i).selected = 0;
+	}
+	if (item_selected < mList.size()) {
+		mList.at(item_selected).selected = 1;
+		string str = mList.at(item_selected).variableValue;
+		DataManager::SetValue(mVariable, str);
+	}
+	mUpdate = 1;
+}
diff --git a/gui/objects.hpp b/gui/objects.hpp
index 3e33561..0928f30 100644
--- a/gui/objects.hpp
+++ b/gui/objects.hpp
@@ -501,11 +501,17 @@
 	std::string mVarName;
 };
 
-class GUIFileSelector : public GUIObject, public RenderObject, public ActionObject
+struct ScrollListData {
+	Resource* displayResource;
+	std::string displayName;
+	int list_index;
+};
+
+class GUIScrollList : public GUIObject, public RenderObject, public ActionObject
 {
 public:
-	GUIFileSelector(xml_node<>* node);
-	virtual ~GUIFileSelector();
+	GUIScrollList(xml_node<>* node);
+	virtual ~GUIScrollList();
 
 public:
 	// Render - Render the full object to the GL surface
@@ -531,6 +537,106 @@
 	virtual void SetPageFocus(int inFocus);
 
 protected:
+	// derived classes need to implement these
+	// get number of items
+	virtual size_t GetItemCount() { return 0; }
+	// get data for one item
+	virtual int GetListItem(size_t item_index, Resource*& icon, std::string &text)
+		{ icon = NULL; text = ""; return -1; }
+	// an item was selected
+	virtual void NotifySelect(size_t item_selected) {}
+
+	enum { NO_ITEM = (size_t)-1 };
+	// returns item index at coordinates or NO_ITEM if there is no item there
+	size_t HitTestItem(int x, int y);
+
+	// Called by the derived class to set the max icon size so we can calculate the proper actualItemHeight and center smaller icons if the icon size varies
+	void SetMaxIconSize(int w, int h);
+
+	// This will make sure that the item indicated by list_index is visible on the screen
+	void SetVisibleListLocation(size_t list_index);
+
+	// Handle scrolling changes for drags and kinetic scrolling
+	void HandleScrolling();
+
+	// Returns many rows the list is capable of displaying
+	int GetDisplayItemCount();
+
+	// Returns the size in pixels of a partial item or row size
+	int GetDisplayRemainder();
+
+protected:
+	// Background
+	COLOR mBackgroundColor;
+	Resource* mBackground; // background image, if any, automatically centered
+	int mBackgroundW, mBackgroundH; // background width and height if using an image for the background
+
+	// Header
+	COLOR mHeaderBackgroundColor;
+	COLOR mHeaderFontColor;
+	std::string mHeaderText; // Original header text without parsing any variables
+	std::string mLastHeaderValue; // Header text after parsing variables
+	int mHeaderIsStatic; // indicates if the header is static (no need to check for changes in NotifyVarChange)
+	int mHeaderH; // actual header height including font, icon, padding, and separator heights
+	Resource* mHeaderIcon;
+	int mHeaderIconHeight, mHeaderIconWidth; // width and height of the header icon if present
+	int mHeaderSeparatorH; // Height of the separator between header and list items
+	COLOR mHeaderSeparatorColor; // color of the header separator
+
+	// Per-item layout
+	Resource* mFont;
+	COLOR mFontColor;
+	bool hasHighlightColor; // indicates if a hightlight color was set
+	bool hasFontHighlightColor; // indicates if the font hightlight color is set
+	COLOR mHighlightColor; // background row hightlight color
+	COLOR mFontHighlightColor;
+	int mFontHeight;
+	int actualItemHeight; // Actual height of each item in pixels including max icon size, font size, and padding
+	int maxIconWidth, maxIconHeight; // max icon width and height for the list, set by derived class in SetMaxIconSize
+	int mItemSpacing; // stores the spacing or padding on the y axis, part of the actualItemHeight
+	int mSeparatorH; // Height of the separator between items
+	COLOR mSeparatorColor; // color of the separator that is between items
+
+	// Scrolling and dynamic state
+	int mFastScrollW; // width of the fastscroll area
+	int mFastScrollLineW; // width of the line for fastscroll rendering
+	int mFastScrollRectW; // width of the rectangle for fastscroll
+	int mFastScrollRectH; // height of the rectangle for fastscroll
+	COLOR mFastScrollLineColor;
+	COLOR mFastScrollRectColor;
+	bool hasScroll; // indicates that we have enough items in the list to scroll
+	int firstDisplayedItem; // this item goes at the top of the display list - may only be partially visible
+	int scrollingSpeed; // on a touch release, this is set based on the difference in the y-axis between the last 2 touches and indicates how fast the kinetic scrolling will go
+	int y_offset; // this is how many pixels offset in the y axis for per pixel scrolling, is always <= 0 and should never be < -actualItemHeight
+	size_t selectedItem; // selected item index after the initial touch, set to -1 if we are scrolling
+	int touchDebounce; // debounce for touches, minimum of 6 pixels but may be larger calculated based actualItemHeight / 3
+	int lastY, last2Y; // last 2 touch locations, used for tracking kinetic scroll speed
+	int fastScroll; // indicates that the inital touch was inside the fastscroll region - makes for easier fast scrolling as the touches don't have to stay within the fast scroll region and you drag your finger
+	int mUpdate; // indicates that a change took place and we need to re-render
+};
+
+class GUIFileSelector : public GUIScrollList
+{
+public:
+	GUIFileSelector(xml_node<>* node);
+	virtual ~GUIFileSelector();
+
+public:
+	// Update - Update any UI component animations (called <= 30 FPS)
+	//  Return 0 if nothing to update, 1 on success and contiue, >1 if full render required, and <0 on error
+	virtual int Update(void);
+
+	// NotifyVarChange - Notify of a variable change
+	virtual int NotifyVarChange(const std::string& varName, const std::string& value);
+
+	// SetPageFocus - Notify when a page gains or loses focus
+	virtual void SetPageFocus(int inFocus);
+
+	virtual size_t GetItemCount();
+	virtual int GetListItem(size_t item_index, Resource*& icon, std::string &text);
+	virtual void NotifySelect(size_t item_selected);
+
+protected:
 	struct FileData {
 		std::string fileName;
 		unsigned char fileType;	 // Uses d_type format from struct dirent
@@ -544,96 +650,46 @@
 	};
 
 protected:
-	virtual int GetSelection(int x, int y);
-
 	virtual int GetFileList(const std::string folder);
 	static bool fileSort(FileData d1, FileData d2);
 
 protected:
 	std::vector<FileData> mFolderList;
 	std::vector<FileData> mFileList;
-	std::string mPathVar;
-	std::string mExtn;
-	std::string mVariable;
-	std::string mSortVariable;
-	std::string mSelection;
-	std::string mHeaderText;
-	std::string mLastValue;
-	int actualLineHeight;
-	int mStart;
-	int mLineSpacing;
-	int mSeparatorH;
-	int mHeaderSeparatorH;
-	int mShowFolders, mShowFiles, mShowNavFolders;
-	int mUpdate;
-	int mBackgroundX, mBackgroundY, mBackgroundW, mBackgroundH;
-	int mHeaderH;
-	int mFastScrollW;
-	int mFastScrollLineW;
-	int mFastScrollRectW;
-	int mFastScrollRectH;
-	int mFastScrollRectX;
-	int mFastScrollRectY;
-	static int mSortOrder;
-	int startY;
-	int scrollingSpeed;
-	int scrollingY;
-	int mHeaderIsStatic;
-	int touchDebounce;
-	unsigned mFontHeight;
-	unsigned mLineHeight;
-	int mIconWidth, mIconHeight, mFolderIconHeight, mFileIconHeight, mFolderIconWidth, mFileIconWidth, mHeaderIconHeight, mHeaderIconWidth;
-	Resource* mHeaderIcon;
+	std::string mPathVar; // current path displayed, saved in the data manager
+	std::string mExtn; // used for filtering the file list, for example, *.zip
+	std::string mVariable; // set when the user selects an item, pull path like /path/to/foo
+	std::string mSortVariable; // data manager variable used to change the sorting of files
+	std::string mSelection; // set when the user selects an item without the full path like selecting /path/to/foo would just be set to foo
+	int mShowFolders, mShowFiles; // indicates if the list should show folders and/or files
+	int mShowNavFolders; // indicates if the list should include the "up a level" item and allow you to traverse folders (nav folders are disabled for the restore list, for instance)
+	static int mSortOrder; // must be static because it is used by the static function fileSort
 	Resource* mFolderIcon;
 	Resource* mFileIcon;
-	Resource* mBackground;
-	Resource* mFont;
-	COLOR mBackgroundColor;
-	COLOR mFontColor;
-	COLOR mHeaderBackgroundColor;
-	COLOR mHeaderFontColor;
-	COLOR mSeparatorColor;
-	COLOR mHeaderSeparatorColor;
-	COLOR mFastScrollLineColor;
-	COLOR mFastScrollRectColor;
-	bool hasHighlightColor;
-	bool hasFontHighlightColor;
-	bool isHighlighted;
-	COLOR mHighlightColor;
-	COLOR mFontHighlightColor;
-	int startSelection;
 	bool updateFileList;
 };
 
-class GUIListBox : public GUIObject, public RenderObject, public ActionObject
+class GUIListBox : public GUIScrollList
 {
 public:
 	GUIListBox(xml_node<>* node);
 	virtual ~GUIListBox();
 
 public:
-	// Render - Render the full object to the GL surface
-	//  Return 0 on success, <0 on error
-	virtual int Render(void);
-
 	// Update - Update any UI component animations (called <= 30 FPS)
 	//  Return 0 if nothing to update, 1 on success and contiue, >1 if full render required, and <0 on error
 	virtual int Update(void);
 
-	// NotifyTouch - Notify of a touch event
-	//  Return 0 on success, >0 to ignore remainder of touch, and <0 on error
-	virtual int NotifyTouch(TOUCH_STATE state, int x, int y);
-
 	// NotifyVarChange - Notify of a variable change
 	virtual int NotifyVarChange(const std::string& varName, const std::string& value);
 
-	// SetPos - Update the position of the render object
-	//  Return 0 on success, <0 on error
-	virtual int SetRenderPos(int x, int y, int w = 0, int h = 0);
-
 	// SetPageFocus - Notify when a page gains or loses focus
 	virtual void SetPageFocus(int inFocus);
 
+	virtual size_t GetItemCount();
+	virtual int GetListItem(size_t item_index, Resource*& icon, std::string &text);
+	virtual void NotifySelect(size_t item_selected);
+
 protected:
 	struct ListData {
 		std::string displayName;
@@ -642,89 +698,37 @@
 	};
 
 protected:
-	virtual int GetSelection(int x, int y);
-
-protected:
 	std::vector<ListData> mList;
 	std::string mVariable;
-	std::string mSelection;
 	std::string currentValue;
-	std::string mHeaderText;
-	std::string mLastValue;
-	int actualLineHeight;
-	int mStart;
-	int startY;
-	int mSeparatorH, mHeaderSeparatorH;
-	int mLineSpacing;
-	int mUpdate;
-	int mBackgroundX, mBackgroundY, mBackgroundW, mBackgroundH, mHeaderH;
-	int mFastScrollW;
-	int mFastScrollLineW;
-	int mFastScrollRectW;
-	int mFastScrollRectH;
-	int mFastScrollRectX;
-	int mFastScrollRectY;
-	int mIconWidth, mIconHeight, mSelectedIconWidth, mSelectedIconHeight, mUnselectedIconWidth, mUnselectedIconHeight, mHeaderIconHeight, mHeaderIconWidth;
-	int scrollingSpeed;
-	int scrollingY;
-	static int mSortOrder;
-	unsigned mFontHeight;
-	unsigned mLineHeight;
-	Resource* mHeaderIcon;
 	Resource* mIconSelected;
 	Resource* mIconUnselected;
-	Resource* mBackground;
-	Resource* mFont;
-	COLOR mBackgroundColor;
-	COLOR mFontColor;
-	COLOR mHeaderBackgroundColor;
-	COLOR mHeaderFontColor;
-	COLOR mSeparatorColor;
-	COLOR mHeaderSeparatorColor;
-	COLOR mFastScrollLineColor;
-	COLOR mFastScrollRectColor;
-	bool hasHighlightColor;
-	bool hasFontHighlightColor;
-	bool isHighlighted;
-	COLOR mHighlightColor;
-	COLOR mFontHighlightColor;
-	int mHeaderIsStatic;
-	int startSelection;
-	int touchDebounce;
 };
 
-class GUIPartitionList : public GUIObject, public RenderObject, public ActionObject
+class GUIPartitionList : public GUIScrollList
 {
 public:
 	GUIPartitionList(xml_node<>* node);
 	virtual ~GUIPartitionList();
 
 public:
-	// Render - Render the full object to the GL surface
-	//  Return 0 on success, <0 on error
-	virtual int Render(void);
-
 	// Update - Update any UI component animations (called <= 30 FPS)
 	//  Return 0 if nothing to update, 1 on success and contiue, >1 if full render required, and <0 on error
-	virtual int Update(void);
-
-	// NotifyTouch - Notify of a touch event
-	//  Return 0 on success, >0 to ignore remainder of touch, and <0 on error
-	virtual int NotifyTouch(TOUCH_STATE state, int x, int y);
+	virtual int Update();
 
 	// NotifyVarChange - Notify of a variable change
 	virtual int NotifyVarChange(const std::string& varName, const std::string& value);
 
-	// SetPos - Update the position of the render object
-	//  Return 0 on success, <0 on error
-	virtual int SetRenderPos(int x, int y, int w = 0, int h = 0);
-
 	// SetPageFocus - Notify when a page gains or loses focus
 	virtual void SetPageFocus(int inFocus);
 
+	virtual size_t GetItemCount();
+	virtual int GetListItem(size_t item_index, Resource*& icon, std::string &text);
+	virtual void NotifySelect(size_t item_selected);
+
 protected:
-	virtual int GetSelection(int x, int y);
-	virtual void MatchList(void);
+	void MatchList();
+	void SetStoragePosition();
 
 protected:
 	std::vector<PartitionList> mList;
@@ -732,48 +736,9 @@
 	std::string mVariable;
 	std::string selectedList;
 	std::string currentValue;
-	std::string mHeaderText;
 	std::string mLastValue;
-	int actualLineHeight;
-	int mStart;
-	int startY;
-	int mSeparatorH, mHeaderSeparatorH;
-	int mLineSpacing;
-	int mUpdate;
-	int mBackgroundX, mBackgroundY, mBackgroundW, mBackgroundH, mHeaderH;
-	int mFastScrollW;
-	int mFastScrollLineW;
-	int mFastScrollRectW;
-	int mFastScrollRectH;
-	int mFastScrollRectX;
-	int mFastScrollRectY;
-	int mIconWidth, mIconHeight, mSelectedIconWidth, mSelectedIconHeight, mUnselectedIconWidth, mUnselectedIconHeight, mHeaderIconHeight, mHeaderIconWidth;
-	int scrollingSpeed;
-	int scrollingY;
-	static int mSortOrder;
-	unsigned mFontHeight;
-	unsigned mLineHeight;
-	Resource* mHeaderIcon;
 	Resource* mIconSelected;
 	Resource* mIconUnselected;
-	Resource* mBackground;
-	Resource* mFont;
-	COLOR mBackgroundColor;
-	COLOR mFontColor;
-	COLOR mHeaderBackgroundColor;
-	COLOR mHeaderFontColor;
-	COLOR mSeparatorColor;
-	COLOR mHeaderSeparatorColor;
-	COLOR mFastScrollLineColor;
-	COLOR mFastScrollRectColor;
-	bool hasHighlightColor;
-	bool hasFontHighlightColor;
-	bool isHighlighted;
-	COLOR mHighlightColor;
-	COLOR mFontHighlightColor;
-	int mHeaderIsStatic;
-	int startSelection;
-	int touchDebounce;
 	bool updateList;
 };
 
diff --git a/gui/partitionlist.cpp b/gui/partitionlist.cpp
index 3ec1548..48bd295 100644
--- a/gui/partitionlist.cpp
+++ b/gui/partitionlist.cpp
@@ -16,26 +16,9 @@
 	along with TWRP.  If not, see <http://www.gnu.org/licenses/>.
 */
 
-#include <linux/input.h>
-#include <pthread.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
-#include <fcntl.h>
-#include <sys/reboot.h>
 #include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/mman.h>
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <time.h>
-#include <unistd.h>
-#include <stdlib.h>
 #include <dirent.h>
-#include <ctype.h>
-
-#include <algorithm>
 
 extern "C" {
 #include "../twcommon.h"
@@ -45,98 +28,17 @@
 #include "rapidxml.hpp"
 #include "objects.hpp"
 #include "../data.hpp"
-#include "../twrp-functions.hpp"
 #include "../partitions.hpp"
 
-#define SCROLLING_SPEED_DECREMENT 6
-#define SCROLLING_FLOOR 10
-#define SCROLLING_MULTIPLIER 6
-
-GUIPartitionList::GUIPartitionList(xml_node<>* node) : GUIObject(node)
+GUIPartitionList::GUIPartitionList(xml_node<>* node) : GUIScrollList(node)
 {
 	xml_attribute<>* attr;
 	xml_node<>* child;
-	int header_separator_color_specified = 0, header_separator_height_specified = 0, header_text_color_specified = 0, header_background_color_specified = 0;
 
-	mStart = mLineSpacing = startY = mFontHeight = mSeparatorH = scrollingY = scrollingSpeed = 0;
-	mIconWidth = mIconHeight = mSelectedIconHeight = mSelectedIconWidth = mUnselectedIconHeight = mUnselectedIconWidth = mHeaderIconHeight = mHeaderIconWidth = 0;
-	mHeaderSeparatorH = mLineHeight = mHeaderIsStatic = mHeaderH = actualLineHeight = 0;
-	mIconSelected = mIconUnselected = mBackground = mFont = mHeaderIcon = NULL;
-	mBackgroundX = mBackgroundY = mBackgroundW = mBackgroundH = 0;
-	mFastScrollW = mFastScrollLineW = mFastScrollRectW = mFastScrollRectH = 0;
-	mFastScrollRectX = mFastScrollRectY = -1;
+	int mIconWidth = 0, mIconHeight = 0, mSelectedIconHeight = 0, mSelectedIconWidth = 0, mUnselectedIconHeight = 0, mUnselectedIconWidth = 0;
+	mIconSelected = mIconUnselected = NULL;
 	mUpdate = 0;
-	touchDebounce = 6;
-	ConvertStrToColor("black", &mBackgroundColor);
-	ConvertStrToColor("black", &mHeaderBackgroundColor);
-	ConvertStrToColor("black", &mSeparatorColor);
-	ConvertStrToColor("black", &mHeaderSeparatorColor);
-	ConvertStrToColor("white", &mFontColor);
-	ConvertStrToColor("white", &mHeaderFontColor);
-	ConvertStrToColor("white", &mFastScrollLineColor);
-	ConvertStrToColor("white", &mFastScrollRectColor);
-	hasHighlightColor = false;
-	hasFontHighlightColor = false;
-	isHighlighted = false;
 	updateList = false;
-	startSelection = -1;
-
-	// Load header text
-	child = node->first_node("header");
-	if (child)
-	{
-		attr = child->first_attribute("icon");
-		if (attr)
-			mHeaderIcon = PageManager::FindResource(attr->value());
-
-		attr = child->first_attribute("background");
-		if (attr)
-		{
-			std::string color = attr->value();
-			ConvertStrToColor(color, &mHeaderBackgroundColor);
-			header_background_color_specified = -1;
-		}
-		attr = child->first_attribute("textcolor");
-		if (attr)
-		{
-			std::string color = attr->value();
-			ConvertStrToColor(color, &mHeaderFontColor);
-			header_text_color_specified = -1;
-		}
-		attr = child->first_attribute("separatorcolor");
-		if (attr)
-		{
-			std::string color = attr->value();
-			ConvertStrToColor(color, &mHeaderSeparatorColor);
-			header_separator_color_specified = -1;
-		}
-		attr = child->first_attribute("separatorheight");
-		if (attr) {
-			string parsevalue = gui_parse_text(attr->value());
-			mHeaderSeparatorH = atoi(parsevalue.c_str());
-			header_separator_height_specified = -1;
-		}
-	}
-	child = node->first_node("text");
-	if (child)  mHeaderText = child->value();
-
-	memset(&mHighlightColor, 0, sizeof(COLOR));
-	child = node->first_node("highlight");
-	if (child) {
-		attr = child->first_attribute("color");
-		if (attr) {
-			hasHighlightColor = true;
-			std::string color = attr->value();
-			ConvertStrToColor(color, &mHighlightColor);
-		}
-	}
-
-	// Simple way to check for static state
-	mLastValue = gui_parse_text(mHeaderText);
-	if (mLastValue != mHeaderText)
-		mHeaderIsStatic = 0;
-	else
-		mHeaderIsStatic = -1;
 
 	child = node->first_node("icon");
 	if (child)
@@ -148,80 +50,6 @@
 		if (attr)
 			mIconUnselected = PageManager::FindResource(attr->value());
 	}
-	child = node->first_node("background");
-	if (child)
-	{
-		attr = child->first_attribute("resource");
-		if (attr)
-			mBackground = PageManager::FindResource(attr->value());
-		attr = child->first_attribute("color");
-		if (attr)
-		{
-			std::string color = attr->value();
-			ConvertStrToColor(color, &mBackgroundColor);
-			if (!header_background_color_specified)
-				ConvertStrToColor(color, &mHeaderBackgroundColor);
-		}
-	}
-
-	// Load the placement
-	LoadPlacement(node->first_node("placement"), &mRenderX, &mRenderY, &mRenderW, &mRenderH);
-	SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
-
-	// Load the font, and possibly override the color
-	child = node->first_node("font");
-	if (child)
-	{
-		attr = child->first_attribute("resource");
-		if (attr)
-			mFont = PageManager::FindResource(attr->value());
-
-		attr = child->first_attribute("color");
-		if (attr)
-		{
-			std::string color = attr->value();
-			ConvertStrToColor(color, &mFontColor);
-			if (!header_text_color_specified)
-				ConvertStrToColor(color, &mHeaderFontColor);
-		}
-
-		attr = child->first_attribute("spacing");
-		if (attr) {
-			string parsevalue = gui_parse_text(attr->value());
-			mLineSpacing = atoi(parsevalue.c_str());
-		}
-
-		attr = child->first_attribute("highlightcolor");
-		memset(&mFontHighlightColor, 0, sizeof(COLOR));
-		if (attr)
-		{
-			std::string color = attr->value();
-			ConvertStrToColor(color, &mFontHighlightColor);
-			hasFontHighlightColor = true;
-		}
-	}
-
-	// Load the separator if it exists
-	child = node->first_node("separator");
-	if (child)
-	{
-		attr = child->first_attribute("color");
-		if (attr)
-		{
-			std::string color = attr->value();
-			ConvertStrToColor(color, &mSeparatorColor);
-			if (!header_separator_color_specified)
-				ConvertStrToColor(color, &mHeaderSeparatorColor);
-		}
-
-		attr = child->first_attribute("height");
-		if (attr) {
-			string parsevalue = gui_parse_text(attr->value());
-			mSeparatorH = atoi(parsevalue.c_str());
-			if (!header_separator_height_specified)
-				mHeaderSeparatorH = mSeparatorH;
-		}
-	}
 
 	// Handle the result variable
 	child = node->first_node("data");
@@ -235,54 +63,12 @@
 			selectedList = attr->value();
 	}
 
-	// Fast scroll colors
-	child = node->first_node("fastscroll");
-	if (child)
-	{
-		attr = child->first_attribute("linecolor");
-		if(attr)
-			ConvertStrToColor(attr->value(), &mFastScrollLineColor);
-
-		attr = child->first_attribute("rectcolor");
-		if(attr)
-			ConvertStrToColor(attr->value(), &mFastScrollRectColor);
-
-		attr = child->first_attribute("w");
-		if (attr) {
-			string parsevalue = gui_parse_text(attr->value());
-			mFastScrollW = atoi(parsevalue.c_str());
-		}
-
-		attr = child->first_attribute("linew");
-		if (attr) {
-			string parsevalue = gui_parse_text(attr->value());
-			mFastScrollLineW = atoi(parsevalue.c_str());
-		}
-
-		attr = child->first_attribute("rectw");
-		if (attr) {
-			string parsevalue = gui_parse_text(attr->value());
-			mFastScrollRectW = atoi(parsevalue.c_str());
-		}
-
-		attr = child->first_attribute("recth");
-		if (attr) {
-			string parsevalue = gui_parse_text(attr->value());
-			mFastScrollRectH = atoi(parsevalue.c_str());
-		}
-	}
-
-	// Retrieve the line height
-	mFontHeight = gr_getMaxFontHeight(mFont ? mFont->GetResource() : NULL);
-	mLineHeight = mFontHeight;
-	mHeaderH = mFontHeight;
-
 	if (mIconSelected && mIconSelected->GetResource())
 	{
 		mSelectedIconWidth = gr_get_width(mIconSelected->GetResource());
 		mSelectedIconHeight = gr_get_height(mIconSelected->GetResource());
-		if (mSelectedIconHeight > (int)mLineHeight)
-			mLineHeight = mSelectedIconHeight;
+		if (mSelectedIconHeight > mIconHeight)
+			mIconHeight = mSelectedIconHeight;
 		mIconWidth = mSelectedIconWidth;
 	}
 
@@ -290,47 +76,17 @@
 	{
 		mUnselectedIconWidth = gr_get_width(mIconUnselected->GetResource());
 		mUnselectedIconHeight = gr_get_height(mIconUnselected->GetResource());
-		if (mUnselectedIconHeight > (int)mLineHeight)
-			mLineHeight = mUnselectedIconHeight;
+		if (mUnselectedIconHeight > mIconHeight)
+			mIconHeight = mUnselectedIconHeight;
 		if (mUnselectedIconWidth > mIconWidth)
 			mIconWidth = mUnselectedIconWidth;
 	}
-
-	if (mHeaderIcon && mHeaderIcon->GetResource())
-	{
-		mHeaderIconWidth = gr_get_width(mHeaderIcon->GetResource());
-		mHeaderIconHeight = gr_get_height(mHeaderIcon->GetResource());
-		if (mHeaderIconHeight > mHeaderH)
-			mHeaderH = mHeaderIconHeight;
-		if (mHeaderIconWidth > mIconWidth)
-			mIconWidth = mHeaderIconWidth;
-	}
-
-	mHeaderH += mLineSpacing + mHeaderSeparatorH;
-	actualLineHeight = mLineHeight + mLineSpacing + mSeparatorH;
-	if (mHeaderH < actualLineHeight)
-		mHeaderH = actualLineHeight;
-
-	if (actualLineHeight / 3 > 6)
-		touchDebounce = actualLineHeight / 3;
-
-	if (mBackground && mBackground->GetResource())
-	{
-		mBackgroundW = gr_get_width(mBackground->GetResource());
-		mBackgroundH = gr_get_height(mBackground->GetResource());
-	}
+	SetMaxIconSize(mIconWidth, mIconHeight);
 
 	child = node->first_node("listtype");
-	if (child) {
-		attr = child->first_attribute("name");
-		if (attr) {
-			ListType = attr->value();
-			PartitionManager.Get_Partition_List(ListType, &mList);
-		} else {
-			mList.clear();
-			LOGERR("No partition listtype name specified for partitionlist GUI element\n");
-			return;
-		}
+	if (child && (attr = child->first_attribute("name"))) {
+		ListType = attr->value();
+		updateList = true;
 	} else {
 		mList.clear();
 		LOGERR("No partition listtype specified for partitionlist GUI element\n");
@@ -340,190 +96,8 @@
 
 GUIPartitionList::~GUIPartitionList()
 {
-}
-
-int GUIPartitionList::Render(void)
-{
-	if(!isConditionTrue())
-		return 0;
-
-	// First step, fill background
-	gr_color(mBackgroundColor.red, mBackgroundColor.green, mBackgroundColor.blue, 255);
-	gr_fill(mRenderX, mRenderY + mHeaderH, mRenderW, mRenderH - mHeaderH);
-
-	// Next, render the background resource (if it exists)
-	if (mBackground && mBackground->GetResource())
-	{
-		mBackgroundX = mRenderX + ((mRenderW - mBackgroundW) / 2);
-		mBackgroundY = mRenderY + ((mRenderH - mBackgroundH) / 2);
-		gr_blit(mBackground->GetResource(), 0, 0, mBackgroundW, mBackgroundH, mBackgroundX, mBackgroundY);
-	}
-
-	// This tells us how many lines we can actually render
-	int lines = (mRenderH - mHeaderH) / (actualLineHeight);
-	int line;
-
-	if (updateList) {
-		mList.clear();
-		PartitionManager.Get_Partition_List(ListType, &mList);
-		updateList = false;
-		if (ListType == "backup")
-			MatchList();
-	}
-
-	int listSize = mList.size();
-	int listW = mRenderW;
-
-	if (listSize < lines) {
-		lines = listSize;
-		scrollingY = 0;
-		mFastScrollRectX = mFastScrollRectY = -1;
-	} else {
-		lines++;
-		if (lines < listSize)
-			lines++;
-		if (listSize >= lines)
-			listW -= mFastScrollW; // space for fast scrollbar
-		else
-			mFastScrollRectX = mFastScrollRectY = -1; // no fast scrollbar
-	}
-
-	void* fontResource = NULL;
-	if (mFont)  fontResource = mFont->GetResource();
-
-	int yPos = mRenderY + mHeaderH + scrollingY;
-	int fontOffsetY = (int)((actualLineHeight - mFontHeight) / 2);
-	int currentIconHeight = 0, currentIconWidth = 0;
-	int currentIconOffsetY = 0, currentIconOffsetX = 0;
-	int UnselectedIconOffsetY = (int)((actualLineHeight - mUnselectedIconHeight) / 2), SelectedIconOffsetY = (int)((actualLineHeight - mSelectedIconHeight) / 2);
-	int UnselectedIconOffsetX = (mIconWidth - mUnselectedIconWidth) / 2, SelectedIconOffsetX = (mIconWidth - mSelectedIconWidth) / 2;
-	int actualSelection = mStart;
-
-	if (isHighlighted) {
-		int selectY = scrollingY;
-
-		// Locate the correct line for highlighting
-		while (selectY + actualLineHeight < startSelection) {
-			selectY += actualLineHeight;
-			actualSelection++;
-		}
-		if (hasHighlightColor) {
-			// Highlight the area
-			gr_color(mHighlightColor.red, mHighlightColor.green, mHighlightColor.blue, 255);
-			int HighlightHeight = actualLineHeight;
-			if (mRenderY + mHeaderH + selectY + actualLineHeight > mRenderH + mRenderY) {
-				HighlightHeight = actualLineHeight - (mRenderY + mHeaderH + selectY + actualLineHeight - mRenderH - mRenderY);
-			}
-			gr_fill(mRenderX, mRenderY + mHeaderH + selectY, mRenderW, HighlightHeight);
-		}
-	}
-
-	for (line = 0; line < lines; line++)
-	{
-		Resource* icon;
-		std::string label;
-
-		if (line + mStart >= listSize)
-			continue;
-
-		label = mList.at(line + mStart).Display_Name;
-		if (isHighlighted && hasFontHighlightColor && line + mStart == actualSelection) {
-			// Use the highlight color for the font
-			gr_color(mFontHighlightColor.red, mFontHighlightColor.green, mFontHighlightColor.blue, 255);
-		} else {
-			// Set the color for the font
-			gr_color(mFontColor.red, mFontColor.green, mFontColor.blue, 255);
-		}
-
-		if (mList.at(line + mStart).selected != 0)
-		{
-			icon = mIconSelected;
-			currentIconHeight = mSelectedIconHeight;
-			currentIconWidth = mSelectedIconWidth;
-			currentIconOffsetY = SelectedIconOffsetY;
-			currentIconOffsetX = SelectedIconOffsetX;
-		}
-		else
-		{
-			icon = mIconUnselected;
-			currentIconHeight = mSelectedIconHeight;
-			currentIconWidth = mSelectedIconWidth;
-			currentIconOffsetY = SelectedIconOffsetY;
-			currentIconOffsetX = SelectedIconOffsetX;
-		}
-
-		if (icon && icon->GetResource())
-		{
-			int rect_y = 0, image_y = (yPos + currentIconOffsetY);
-			if (image_y + currentIconHeight > mRenderY + mRenderH)
-				rect_y = mRenderY + mRenderH - image_y;
-			else
-				rect_y = currentIconHeight;
-			gr_blit(icon->GetResource(), 0, 0, currentIconWidth, rect_y, mRenderX + currentIconOffsetX, image_y);
-		}
-		gr_textExWH(mRenderX + mIconWidth + 5, yPos + fontOffsetY, label.c_str(), fontResource, mRenderX + listW, mRenderY + mRenderH);
-
-		// Add the separator
-		if (yPos + actualLineHeight < mRenderH + mRenderY) {
-			gr_color(mSeparatorColor.red, mSeparatorColor.green, mSeparatorColor.blue, 255);
-			gr_fill(mRenderX, yPos + actualLineHeight - mSeparatorH, listW, mSeparatorH);
-		}
-
-		// Move the yPos
-		yPos += actualLineHeight;
-	}
-
-	// Render the Header (last so that it overwrites the top most row for per pixel scrolling)
-	// First step, fill background
-	gr_color(mHeaderBackgroundColor.red, mHeaderBackgroundColor.green, mHeaderBackgroundColor.blue, 255);
-	gr_fill(mRenderX, mRenderY, mRenderW, mHeaderH);
-
-	// Now, we need the header (icon + text)
-	yPos = mRenderY;
-	{
-		Resource* headerIcon;
-		int mIconOffsetX = 0;
-
-		// render the icon if it exists
-		headerIcon = mHeaderIcon;
-		if (headerIcon && headerIcon->GetResource())
-		{
-			gr_blit(headerIcon->GetResource(), 0, 0, mHeaderIconWidth, mHeaderIconHeight, mRenderX + ((mHeaderIconWidth - mIconWidth) / 2), (yPos + (int)((mHeaderH - mHeaderIconHeight) / 2)));
-			mIconOffsetX = mIconWidth;
-		}
-
-		// render the text
-		gr_color(mHeaderFontColor.red, mHeaderFontColor.green, mHeaderFontColor.blue, 255);
-		gr_textExWH(mRenderX + mIconOffsetX + 5, yPos + (int)((mHeaderH - mFontHeight) / 2), mLastValue.c_str(), fontResource, mRenderX + mRenderW, mRenderY + mRenderH);
-
-		// Add the separator
-		gr_color(mHeaderSeparatorColor.red, mHeaderSeparatorColor.green, mHeaderSeparatorColor.blue, 255);
-		gr_fill(mRenderX, yPos + mHeaderH - mHeaderSeparatorH, mRenderW, mHeaderSeparatorH);
-	}
-
-	// render fast scroll
-	lines = (mRenderH - mHeaderH) / (actualLineHeight);
-	if(mFastScrollW > 0 && listSize > lines)
-	{
-		int startX = listW + mRenderX;
-		int fWidth = mRenderW - listW;
-		int fHeight = mRenderH - mHeaderH;
-
-		// line
-		gr_color(mFastScrollLineColor.red, mFastScrollLineColor.green, mFastScrollLineColor.blue, 255);
-		gr_fill(startX + fWidth/2, mRenderY + mHeaderH, mFastScrollLineW, mRenderH - mHeaderH);
-
-		// rect
-		int pct = ((mStart*actualLineHeight - scrollingY)*100)/((listSize)*actualLineHeight-lines*actualLineHeight);
-		mFastScrollRectX = startX + (fWidth - mFastScrollRectW)/2;
-		mFastScrollRectY = mRenderY+mHeaderH + ((fHeight - mFastScrollRectH)*pct)/100;
-
-		gr_color(mFastScrollRectColor.red, mFastScrollRectColor.green, mFastScrollRectColor.blue, 255);
-		gr_fill(mFastScrollRectX, mFastScrollRectY, mFastScrollRectW, mFastScrollRectH);
-	}
-
-	mUpdate = 0;
-	return 0;
+	delete mIconSelected;
+	delete mIconUnselected;
 }
 
 int GUIPartitionList::Update(void)
@@ -531,14 +105,6 @@
 	if(!isConditionTrue())
 		return 0;
 
-	if (!mHeaderIsStatic) {
-		std::string newValue = gui_parse_text(mHeaderText);
-		if (mLastValue != newValue) {
-			mLastValue = newValue;
-			mUpdate = 1;
-		}
-	}
-
 	// Check for changes in mount points if the list type is mount and update the list and render if needed
 	if (ListType == "mount") {
 		int listSize = mList.size();
@@ -553,329 +119,49 @@
 		}
 	}
 
-	if (mUpdate)
-	{
+	GUIScrollList::Update();
+
+	if (updateList) {
+		int listSize = 0;
+
+		// Completely update the list if needed -- Used primarily for 
+		// restore as the list for restore will change depending on what 
+		// partitions were backed up
+		mList.clear();
+		PartitionManager.Get_Partition_List(ListType, &mList);
+		SetVisibleListLocation(0);
+		updateList = false;
+		mUpdate = 1;
+		if (ListType == "backup")
+			MatchList();
+	}
+
+	if (mUpdate) {
 		mUpdate = 0;
 		if (Render() == 0)
 			return 2;
 	}
 
-	// Handle kinetic scrolling
-	if (scrollingSpeed == 0) {
-		// Do nothing
-	} else if (scrollingSpeed > 0) {
-		if (scrollingSpeed < ((int) (actualLineHeight * 2.5))) {
-			scrollingY += scrollingSpeed;
-			scrollingSpeed -= SCROLLING_SPEED_DECREMENT;
-		} else {
-			scrollingY += ((int) (actualLineHeight * 2.5));
-			scrollingSpeed -= SCROLLING_SPEED_DECREMENT;
-		}
-		while (mStart && scrollingY > 0) {
-			mStart--;
-			scrollingY -= actualLineHeight;
-		}
-		if (mStart == 0 && scrollingY > 0) {
-			scrollingY = 0;
-			scrollingSpeed = 0;
-		} else if (scrollingSpeed < SCROLLING_FLOOR)
-			scrollingSpeed = 0;
-		mUpdate = 1;
-	} else if (scrollingSpeed < 0) {
-		int totalSize = mList.size();
-		int lines = (mRenderH - mHeaderH) / (actualLineHeight);
-
-		if (totalSize > lines) {
-			int bottom_offset = ((int)(mRenderH) - mHeaderH) - (lines * actualLineHeight);
-
-			bottom_offset -= actualLineHeight;
-
-			if (abs(scrollingSpeed) < ((int) (actualLineHeight * 2.5))) {
-				scrollingY += scrollingSpeed;
-				scrollingSpeed += SCROLLING_SPEED_DECREMENT;
-			} else {
-				scrollingY -= ((int) (actualLineHeight * 2.5));
-				scrollingSpeed += SCROLLING_SPEED_DECREMENT;
-			}
-			while (mStart + lines + (bottom_offset ? 1 : 0) < totalSize && abs(scrollingY) > actualLineHeight) {
-				mStart++;
-				scrollingY += actualLineHeight;
-			}
-			if (bottom_offset != 0 && mStart + lines + 1 >= totalSize && scrollingY <= bottom_offset) {
-				mStart = totalSize - lines - 1;
-				scrollingY = bottom_offset;
-			} else if (mStart + lines >= totalSize && scrollingY < 0) {
-				mStart = totalSize - lines;
-				scrollingY = 0;
-			} else if (scrollingSpeed * -1 < SCROLLING_FLOOR)
-				scrollingSpeed = 0;
-			mUpdate = 1;
-		}
-	}
-
-	return 0;
-}
-
-int GUIPartitionList::GetSelection(int x, int y)
-{
-	// We only care about y position
-	if (y < mRenderY || y - mRenderY <= mHeaderH || y - mRenderY > mRenderH) return -1;
-	return (y - mRenderY - mHeaderH);
-}
-
-int GUIPartitionList::NotifyTouch(TOUCH_STATE state, int x, int y)
-{
-	if(!isConditionTrue())
-		return -1;
-
-	static int lastY = 0, last2Y = 0;
-	int selection = 0;
-
-	switch (state)
-	{
-	case TOUCH_START:
-		if (scrollingSpeed != 0)
-			startSelection = -1;
-		else
-			startSelection = GetSelection(x,y);
-		isHighlighted = (startSelection > -1);
-		if (isHighlighted)
-			mUpdate = 1;
-		startY = lastY = last2Y = y;
-		scrollingSpeed = 0;
-		break;
-
-	case TOUCH_DRAG:
-		// Check if we dragged out of the selection window
-		if (GetSelection(x, y) == -1) {
-			last2Y = lastY = 0;
-			if (isHighlighted) {
-				isHighlighted = false;
-				mUpdate = 1;
-			}
-			break;
-		}
-
-		// Fast scroll
-		if(mFastScrollRectX != -1 && x >= mRenderX + mRenderW - mFastScrollW)
-		{
-			int pct = ((y-mRenderY-mHeaderH)*100)/(mRenderH-mHeaderH);
-			int totalSize = mList.size();
-			int lines = (mRenderH - mHeaderH) / (actualLineHeight);
-
-			float l = float((totalSize-lines)*pct)/100;
-			if(l + lines >= totalSize)
-			{
-				mStart = totalSize - lines;
-				scrollingY = 0;
-			}
-			else
-			{
-				mStart = l;
-				scrollingY = -(l - int(l))*actualLineHeight;
-			}
-
-			startSelection = -1;
-			mUpdate = 1;
-			scrollingSpeed = 0;
-			isHighlighted = false;
-			break;
-		}
-
-		// Provide some debounce on initial touches
-		if (startSelection != -1 && abs(y - startY) < touchDebounce) {
-			isHighlighted = true;
-			mUpdate = 1;
-			break;
-		}
-
-		isHighlighted = false;
-		last2Y = lastY;
-		lastY = y;
-		startSelection = -1;
-
-		// Handle scrolling
-		scrollingY += y - startY;
-		startY = y;
-		while(mStart && scrollingY > 0) {
-			mStart--;
-			scrollingY -= actualLineHeight;
-		}
-		if (mStart == 0 && scrollingY > 0)
-			scrollingY = 0;
-		{
-			int totalSize = mList.size();
-			int lines = (mRenderH - mHeaderH) / (actualLineHeight);
-
-			if (totalSize > lines) {
-				int bottom_offset = ((int)(mRenderH) - mHeaderH) - (lines * actualLineHeight);
-
-				bottom_offset -= actualLineHeight;
-
-				while (mStart + lines + (bottom_offset ? 1 : 0) < totalSize && abs(scrollingY) > actualLineHeight) {
-					mStart++;
-					scrollingY += actualLineHeight;
-				}
-				if (bottom_offset != 0 && mStart + lines + 1 >= totalSize && scrollingY <= bottom_offset) {
-					mStart = totalSize - lines - 1;
-					scrollingY = bottom_offset;
-				} else if (mStart + lines >= totalSize && scrollingY < 0) {
-					mStart = totalSize - lines;
-					scrollingY = 0;
-				}
-			} else
-				scrollingY = 0;
-		}
-		mUpdate = 1;
-		break;
-
-	case TOUCH_RELEASE:
-		isHighlighted = false;
-		if (startSelection >= 0)
-		{
-			// We've selected an item!
-			int listSize = mList.size();
-			int selectY = scrollingY, actualSelection = mStart;
-
-			// Move the selection to the proper place in the array
-			while (selectY + actualLineHeight < startSelection) {
-				selectY += actualLineHeight;
-				actualSelection++;
-			}
-
-			if (actualSelection < listSize && ListType == "mount") {
-				DataManager::Vibrate("tw_button_vibrate");
-
-				if (!mList.at(actualSelection).selected) {
-					if (PartitionManager.Mount_By_Path(mList.at(actualSelection).Mount_Point, true)) {
-						mList.at(actualSelection).selected = 1;
-						PartitionManager.Add_MTP_Storage(mList.at(actualSelection).Mount_Point);
-						mUpdate = 1;
-					}
-				} else {
-					if (PartitionManager.UnMount_By_Path(mList.at(actualSelection).Mount_Point, true)) {
-						mList.at(actualSelection).selected = 0;
-						mUpdate = 1;
-					}
-				}
-			} else if (actualSelection < listSize && !mVariable.empty()) {
-				DataManager::Vibrate("tw_button_vibrate");
-
-				if (ListType == "storage") {
-					int i;
-					std::string str = mList.at(actualSelection).Mount_Point;
-					bool update_size = false;
-					TWPartition* Part = PartitionManager.Find_Partition_By_Path(str);
-					if (Part == NULL) {
-						LOGERR("Unable to locate partition for '%s'\n", str.c_str());
-						return 0;
-					}
-					if (!Part->Is_Mounted() && Part->Removable)
-						update_size = true;
-					if (!Part->Mount(true)) {
-						// Do Nothing
-					} else if (update_size && !Part->Update_Size(true)) {
-						// Do Nothing
-					} else {
-						for (i=0; i<listSize; i++)
-							mList.at(i).selected = 0;
-
-						if (update_size) {
-							char free_space[255];
-							sprintf(free_space, "%llu", Part->Free / 1024 / 1024);
-							mList.at(actualSelection).Display_Name = Part->Storage_Name + " (";
-							mList.at(actualSelection).Display_Name += free_space;
-							mList.at(actualSelection).Display_Name += "MB)";
-						}
-						mList.at(actualSelection).selected = 1;
-						mUpdate = 1;
-
-						DataManager::SetValue(mVariable, str);
-					}
-				} else {
-					if (ListType == "flashimg") { // only one item can be selected for flashing images
-						for (int i=0; i<listSize; i++)
-							mList.at(i).selected = 0;
-					}
-					if (mList.at(actualSelection).selected)
-						mList.at(actualSelection).selected = 0;
-					else
-						mList.at(actualSelection).selected = 1;
-
-					int i;
-					string variablelist;
-					for (i=0; i<listSize; i++) {
-						if (mList.at(i).selected) {
-							variablelist += mList.at(i).Mount_Point + ";";
-						}
-					}
-
-					mUpdate = 1;
-					if (selectedList.empty())
-						DataManager::SetValue(mVariable, variablelist);
-					else
-						DataManager::SetValue(selectedList, variablelist);
-				}
-			}
-		} else {
-			// This is for kinetic scrolling
-			scrollingSpeed = lastY - last2Y;
-			if (abs(scrollingSpeed) > SCROLLING_FLOOR)
-				scrollingSpeed *= SCROLLING_MULTIPLIER;
-			else
-				scrollingSpeed = 0;
-		}
-	case TOUCH_REPEAT:
-	case TOUCH_HOLD:
-		break;
-	}
 	return 0;
 }
 
 int GUIPartitionList::NotifyVarChange(const std::string& varName, const std::string& value)
 {
-	GUIObject::NotifyVarChange(varName, value);
-
 	if(!isConditionTrue())
 		return 0;
 
-	if (!mHeaderIsStatic) {
-		std::string newValue = gui_parse_text(mHeaderText);
-		if (mLastValue != newValue) {
-			mLastValue = newValue;
-			mStart = 0;
-			scrollingY = 0;
-			scrollingSpeed = 0;
-			mUpdate = 1;
-		}
-	}
+	GUIScrollList::NotifyVarChange(varName, value);
+
 	if (varName == mVariable && !mUpdate)
 	{
 		if (ListType == "storage") {
-			int i, listSize = mList.size(), selected_index = 0;
-
 			currentValue = value;
-
-			for (i=0; i<listSize; i++) {
-				if (mList.at(i).Mount_Point == currentValue) {
-					mList.at(i).selected = 1;
-					selected_index = i;
-				} else
-					mList.at(i).selected = 0;
-			}
-
-			int lines = mRenderH / (mLineHeight + mLineSpacing);
-			int line;
-
-			if (selected_index > mStart + lines - 1) {
-				mStart = selected_index;
-			} else if (selected_index < mStart) {
-				mStart = selected_index;
-			}
+			SetStoragePosition();
 		} else if (ListType == "backup") {
 			MatchList();
 		} else if (ListType == "restore") {
 			updateList = true;
+			SetVisibleListLocation(0);
 		}
 
 		mUpdate = 1;
@@ -884,44 +170,13 @@
 	return 0;
 }
 
-int GUIPartitionList::SetRenderPos(int x, int y, int w /* = 0 */, int h /* = 0 */)
-{
-	mRenderX = x;
-	mRenderY = y;
-	if (w || h)
-	{
-		mRenderW = w;
-		mRenderH = h;
-	}
-	SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
-	mUpdate = 1;
-	return 0;
-}
-
 void GUIPartitionList::SetPageFocus(int inFocus)
 {
+	GUIScrollList::SetPageFocus(inFocus);
 	if (inFocus) {
 		if (ListType == "storage") {
-			int i, listSize = mList.size(), selected_index = 0;
-
 			DataManager::GetValue(mVariable, currentValue);
-
-			for (i=0; i<listSize; i++) {
-				if (mList.at(i).Mount_Point == currentValue) {
-					mList.at(i).selected = 1;
-					selected_index = i;
-				} else
-					mList.at(i).selected = 0;
-			}
-
-			int lines = mRenderH / (mLineHeight + mLineSpacing);
-			int line;
-
-			if (selected_index > mStart + lines - 1) {
-				mStart = selected_index;
-			} else if (selected_index < mStart) {
-				mStart = selected_index;
-			}
+			SetStoragePosition();
 		}
 		updateList = true;
 		mUpdate = 1;
@@ -935,7 +190,7 @@
 
 	DataManager::GetValue(mVariable, variablelist);
 
-	for (i=0; i<listSize; i++) {
+	for (i = 0; i < listSize; i++) {
 		searchvalue = mList.at(i).Mount_Point + ";";
 		pos = variablelist.find(searchvalue);
 		if (pos != string::npos) {
@@ -945,3 +200,109 @@
 		}
 	}
 }
+
+void GUIPartitionList::SetStoragePosition() {
+	int listSize = mList.size();
+
+	for (int i = 0; i < listSize; i++) {
+		if (mList.at(i).Mount_Point == currentValue) {
+			mList.at(i).selected = 1;
+			SetVisibleListLocation(i);
+		} else {
+			mList.at(i).selected = 0;
+			SetVisibleListLocation(0);
+		}
+	}
+}
+
+size_t GUIPartitionList::GetItemCount()
+{
+	return mList.size();
+}
+
+int GUIPartitionList::GetListItem(size_t item_index, Resource*& icon, std::string &text)
+{
+	text = mList.at(item_index).Display_Name;
+	if (mList.at(item_index).selected)
+		icon = mIconSelected;
+	else
+		icon = mIconUnselected;
+	return 0;
+}
+
+void GUIPartitionList::NotifySelect(size_t item_selected)
+{
+	if (item_selected < mList.size()) {
+		int listSize = mList.size();
+		if (ListType == "mount") {
+			if (!mList.at(item_selected).selected) {
+				if (PartitionManager.Mount_By_Path(mList.at(item_selected).Mount_Point, true)) {
+					mList.at(item_selected).selected = 1;
+					PartitionManager.Add_MTP_Storage(mList.at(item_selected).Mount_Point);
+					mUpdate = 1;
+				}
+			} else {
+				if (PartitionManager.UnMount_By_Path(mList.at(item_selected).Mount_Point, true)) {
+					mList.at(item_selected).selected = 0;
+					mUpdate = 1;
+				}
+			}
+		} else if (!mVariable.empty()) {
+			if (ListType == "storage") {
+				int i;
+				std::string str = mList.at(item_selected).Mount_Point;
+				bool update_size = false;
+				TWPartition* Part = PartitionManager.Find_Partition_By_Path(str);
+				if (Part == NULL) {
+					LOGERR("Unable to locate partition for '%s'\n", str.c_str());
+					return;
+				}
+				if (!Part->Is_Mounted() && Part->Removable)
+					update_size = true;
+				if (!Part->Mount(true)) {
+					// Do Nothing
+				} else if (update_size && !Part->Update_Size(true)) {
+					// Do Nothing
+				} else {
+					for (i=0; i<listSize; i++)
+						mList.at(i).selected = 0;
+
+					if (update_size) {
+						char free_space[255];
+						sprintf(free_space, "%llu", Part->Free / 1024 / 1024);
+						mList.at(item_selected).Display_Name = Part->Storage_Name + " (";
+						mList.at(item_selected).Display_Name += free_space;
+						mList.at(item_selected).Display_Name += "MB)";
+					}
+					mList.at(item_selected).selected = 1;
+					mUpdate = 1;
+
+					DataManager::SetValue(mVariable, str);
+				}
+			} else {
+				if (ListType == "flashimg") { // only one item can be selected for flashing images
+					for (int i=0; i<listSize; i++)
+						mList.at(i).selected = 0;
+				}
+				if (mList.at(item_selected).selected)
+					mList.at(item_selected).selected = 0;
+				else
+					mList.at(item_selected).selected = 1;
+
+				int i;
+				string variablelist;
+				for (i=0; i<listSize; i++) {
+					if (mList.at(i).selected) {
+						variablelist += mList.at(i).Mount_Point + ";";
+					}
+				}
+
+				mUpdate = 1;
+				if (selectedList.empty())
+					DataManager::SetValue(mVariable, variablelist);
+				else
+					DataManager::SetValue(selectedList, variablelist);
+			}
+		}
+	}
+}
diff --git a/gui/scrolllist.cpp b/gui/scrolllist.cpp
new file mode 100644
index 0000000..9e8db4c
--- /dev/null
+++ b/gui/scrolllist.cpp
@@ -0,0 +1,709 @@
+/*
+	Copyright 2013 bigbiff/Dees_Troy TeamWin
+	This file is part of TWRP/TeamWin Recovery Project.
+
+	TWRP 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 3 of the License, or
+	(at your option) any later version.
+
+	TWRP 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 TWRP.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <string.h>
+
+extern "C" {
+#include "../twcommon.h"
+#include "../minuitwrp/minui.h"
+}
+
+#include "rapidxml.hpp"
+#include "objects.hpp"
+#include "../data.hpp"
+
+const int SCROLLING_SPEED_DECREMENT = 12; // friction
+const int SCROLLING_FLOOR = 10;	// minimum pixels for scrolling to start or stop
+const int SCROLLING_MULTIPLIER = 2; // initial speed of kinetic scrolling
+const float SCROLLING_SPEED_LIMIT = 2.5; // maximum number of items to scroll per update
+
+GUIScrollList::GUIScrollList(xml_node<>* node) : GUIObject(node)
+{
+	xml_attribute<>* attr;
+	xml_node<>* child;
+	int header_separator_color_specified = 0, header_separator_height_specified = 0, header_text_color_specified = 0, header_background_color_specified = 0;
+
+	firstDisplayedItem = mItemSpacing = mFontHeight = mSeparatorH = y_offset = scrollingSpeed = 0;
+	maxIconWidth = maxIconHeight =  mHeaderIconHeight = mHeaderIconWidth = 0;
+	mHeaderSeparatorH = mHeaderIsStatic = mHeaderH = actualItemHeight = 0;
+	mBackground = mFont = mHeaderIcon = NULL;
+	mBackgroundW = mBackgroundH = 0;
+	mFastScrollW = mFastScrollLineW = mFastScrollRectW = mFastScrollRectH = 0;
+	lastY = last2Y = fastScroll = 0;
+	mUpdate = 0;
+	touchDebounce = 6;
+	ConvertStrToColor("black", &mBackgroundColor);
+	ConvertStrToColor("black", &mHeaderBackgroundColor);
+	ConvertStrToColor("black", &mSeparatorColor);
+	ConvertStrToColor("black", &mHeaderSeparatorColor);
+	ConvertStrToColor("white", &mFontColor);
+	ConvertStrToColor("white", &mHeaderFontColor);
+	ConvertStrToColor("white", &mFastScrollLineColor);
+	ConvertStrToColor("white", &mFastScrollRectColor);
+	hasHighlightColor = false;
+	hasFontHighlightColor = false;
+	selectedItem = NO_ITEM;
+
+	// Load header text
+	child = node->first_node("header");
+	if (child)
+	{
+		attr = child->first_attribute("icon");
+		if (attr)
+			mHeaderIcon = PageManager::FindResource(attr->value());
+
+		attr = child->first_attribute("background");
+		if (attr)
+		{
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mHeaderBackgroundColor);
+			header_background_color_specified = -1;
+		}
+		attr = child->first_attribute("textcolor");
+		if (attr)
+		{
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mHeaderFontColor);
+			header_text_color_specified = -1;
+		}
+		attr = child->first_attribute("separatorcolor");
+		if (attr)
+		{
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mHeaderSeparatorColor);
+			header_separator_color_specified = -1;
+		}
+		attr = child->first_attribute("separatorheight");
+		if (attr) {
+			string parsevalue = gui_parse_text(attr->value());
+			mHeaderSeparatorH = atoi(parsevalue.c_str());
+			header_separator_height_specified = -1;
+		}
+	}
+	child = node->first_node("text");
+	if (child)  mHeaderText = child->value();
+
+	memset(&mHighlightColor, 0, sizeof(COLOR));
+	child = node->first_node("highlight");
+	if (child) {
+		attr = child->first_attribute("color");
+		if (attr) {
+			hasHighlightColor = true;
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mHighlightColor);
+		}
+	}
+
+	// Simple way to check for static state
+	mLastHeaderValue = gui_parse_text(mHeaderText);
+	if (mLastHeaderValue != mHeaderText)
+		mHeaderIsStatic = 0;
+	else
+		mHeaderIsStatic = -1;
+
+	child = node->first_node("background");
+	if (child)
+	{
+		attr = child->first_attribute("resource");
+		if (attr)
+			mBackground = PageManager::FindResource(attr->value());
+		attr = child->first_attribute("color");
+		if (attr)
+		{
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mBackgroundColor);
+			if (!header_background_color_specified)
+				ConvertStrToColor(color, &mHeaderBackgroundColor);
+		}
+	}
+
+	// Load the placement
+	LoadPlacement(node->first_node("placement"), &mRenderX, &mRenderY, &mRenderW, &mRenderH);
+	SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
+
+	// Load the font, and possibly override the color
+	child = node->first_node("font");
+	if (child)
+	{
+		attr = child->first_attribute("resource");
+		if (attr)
+			mFont = PageManager::FindResource(attr->value());
+
+		attr = child->first_attribute("color");
+		if (attr)
+		{
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mFontColor);
+			if (!header_text_color_specified)
+				ConvertStrToColor(color, &mHeaderFontColor);
+		}
+
+		attr = child->first_attribute("spacing");
+		if (attr) {
+			string parsevalue = gui_parse_text(attr->value());
+			mItemSpacing = atoi(parsevalue.c_str());
+		}
+
+		attr = child->first_attribute("highlightcolor");
+		memset(&mFontHighlightColor, 0, sizeof(COLOR));
+		if (attr)
+		{
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mFontHighlightColor);
+			hasFontHighlightColor = true;
+		}
+	}
+
+	// Load the separator if it exists
+	child = node->first_node("separator");
+	if (child)
+	{
+		attr = child->first_attribute("color");
+		if (attr)
+		{
+			std::string color = attr->value();
+			ConvertStrToColor(color, &mSeparatorColor);
+			if (!header_separator_color_specified)
+				ConvertStrToColor(color, &mHeaderSeparatorColor);
+		}
+
+		attr = child->first_attribute("height");
+		if (attr) {
+			string parsevalue = gui_parse_text(attr->value());
+			mSeparatorH = atoi(parsevalue.c_str());
+			if (!header_separator_height_specified)
+				mHeaderSeparatorH = mSeparatorH;
+		}
+	}
+
+	// Fast scroll colors
+	child = node->first_node("fastscroll");
+	if (child)
+	{
+		attr = child->first_attribute("linecolor");
+		if(attr)
+			ConvertStrToColor(attr->value(), &mFastScrollLineColor);
+
+		attr = child->first_attribute("rectcolor");
+		if(attr)
+			ConvertStrToColor(attr->value(), &mFastScrollRectColor);
+
+		attr = child->first_attribute("w");
+		if (attr) {
+			string parsevalue = gui_parse_text(attr->value());
+			mFastScrollW = atoi(parsevalue.c_str());
+		}
+
+		attr = child->first_attribute("linew");
+		if (attr) {
+			string parsevalue = gui_parse_text(attr->value());
+			mFastScrollLineW = atoi(parsevalue.c_str());
+		}
+
+		attr = child->first_attribute("rectw");
+		if (attr) {
+			string parsevalue = gui_parse_text(attr->value());
+			mFastScrollRectW = atoi(parsevalue.c_str());
+		}
+
+		attr = child->first_attribute("recth");
+		if (attr) {
+			string parsevalue = gui_parse_text(attr->value());
+			mFastScrollRectH = atoi(parsevalue.c_str());
+		}
+	}
+
+	// Retrieve the line height
+	mFontHeight = gr_getMaxFontHeight(mFont ? mFont->GetResource() : NULL);
+	mHeaderH = mFontHeight;
+
+	if (mHeaderIcon && mHeaderIcon->GetResource())
+	{
+		mHeaderIconWidth = gr_get_width(mHeaderIcon->GetResource());
+		mHeaderIconHeight = gr_get_height(mHeaderIcon->GetResource());
+		if (mHeaderIconHeight > mHeaderH)
+			mHeaderH = mHeaderIconHeight;
+		if (mHeaderIconWidth > maxIconWidth)
+			maxIconWidth = mHeaderIconWidth;
+	}
+
+	mHeaderH += mItemSpacing + mHeaderSeparatorH;
+	actualItemHeight = mFontHeight + mItemSpacing + mSeparatorH;
+	if (mHeaderH < actualItemHeight)
+		mHeaderH = actualItemHeight;
+
+	if (actualItemHeight / 3 > 6)
+		touchDebounce = actualItemHeight / 3;
+
+	if (mBackground && mBackground->GetResource())
+	{
+		mBackgroundW = gr_get_width(mBackground->GetResource());
+		mBackgroundH = gr_get_height(mBackground->GetResource());
+	}
+}
+
+GUIScrollList::~GUIScrollList()
+{
+	delete mHeaderIcon;
+	delete mBackground;
+	delete mFont;
+}
+
+void GUIScrollList::SetMaxIconSize(int w, int h)
+{
+	if (w > maxIconWidth)
+		maxIconWidth = w;
+	if (h > maxIconHeight)
+		maxIconHeight = h;
+	if (maxIconHeight > mFontHeight) {
+		actualItemHeight = maxIconHeight + mItemSpacing + mSeparatorH;
+		if (actualItemHeight > mHeaderH)
+			mHeaderH = actualItemHeight;
+	}
+}
+
+void GUIScrollList::SetVisibleListLocation(size_t list_index)
+{
+	// This will make sure that the item indicated by list_index is visible on the screen
+	size_t lines = GetDisplayItemCount(), listSize = GetItemCount();
+
+	if (list_index <= (unsigned)firstDisplayedItem) {
+		// list_index is above the currently displayed items, put the selected item at the very top
+		firstDisplayedItem = list_index;
+		y_offset = 0;
+	} else if (list_index >= firstDisplayedItem + lines) {
+		// list_index is below the currently displayed items, put the selected item at the very bottom
+		firstDisplayedItem = list_index - lines + 1;
+		if (GetDisplayRemainder() != 0) {
+			// There's a partial row displayed, set the scrolling offset so that the selected item really is at the very bottom
+			firstDisplayedItem--;
+			y_offset = GetDisplayRemainder() - actualItemHeight;
+		} else {
+			// There's no partial row so zero out the offset
+			y_offset = 0;
+		}
+	}
+	scrollingSpeed = 0; // stop kinetic scrolling on setting visible location
+	mUpdate = 1;
+}
+
+int GUIScrollList::Render(void)
+{
+	if(!isConditionTrue())
+		return 0;
+
+	// First step, fill background
+	gr_color(mBackgroundColor.red, mBackgroundColor.green, mBackgroundColor.blue, 255);
+	gr_fill(mRenderX, mRenderY + mHeaderH, mRenderW, mRenderH - mHeaderH);
+
+	// Next, render the background resource (if it exists)
+	if (mBackground && mBackground->GetResource())
+	{
+		int mBackgroundX = mRenderX + ((mRenderW - mBackgroundW) / 2);
+		int mBackgroundY = mRenderY + ((mRenderH - mBackgroundH) / 2);
+		gr_blit(mBackground->GetResource(), 0, 0, mBackgroundW, mBackgroundH, mBackgroundX, mBackgroundY);
+	}
+
+	// This tells us how many lines we can actually render
+	size_t lines = GetDisplayItemCount();
+
+	size_t listSize = GetItemCount();
+	int listW = mRenderW;
+
+	if (listSize <= lines) {
+		hasScroll = false;
+		scrollingSpeed = 0;
+		lines = listSize;
+		y_offset = 0;
+	} else {
+		hasScroll = true;
+		listW -= mFastScrollW; // space for fast scroll
+		lines++;
+		if (lines < listSize)
+			lines++;
+	}
+
+	void* fontResource = NULL;
+	if (mFont)  fontResource = mFont->GetResource();
+
+	int yPos = mRenderY + mHeaderH + y_offset;
+	int fontOffsetY = (int)((actualItemHeight - mFontHeight) / 2);
+
+	// render all visible items
+	for (size_t line = 0; line < lines; line++)
+	{
+		size_t itemindex = line + firstDisplayedItem;
+		if (itemindex >= listSize)
+			break;
+
+		// get item data
+		Resource* icon;
+		std::string label;
+		if (GetListItem(itemindex, icon, label))
+			break;
+
+		if (hasHighlightColor && itemindex == selectedItem) {
+			// Highlight the item background of the selected item
+			gr_color(mHighlightColor.red, mHighlightColor.green, mHighlightColor.blue, 255);
+			int HighlightHeight = actualItemHeight;
+			if (yPos + HighlightHeight > mRenderY + mRenderH) {
+				HighlightHeight = mRenderY + mRenderH - yPos;
+			}
+			gr_fill(mRenderX, yPos, mRenderW, HighlightHeight);
+		}
+
+		if (hasFontHighlightColor && itemindex == selectedItem) {
+			// Use the highlight color for the font
+			gr_color(mFontHighlightColor.red, mFontHighlightColor.green, mFontHighlightColor.blue, 255);
+		} else {
+			// Set the color for the font
+			gr_color(mFontColor.red, mFontColor.green, mFontColor.blue, 255);
+		}
+
+		if (icon && icon->GetResource()) {
+			int currentIconHeight = gr_get_height(icon->GetResource());
+			int currentIconWidth = gr_get_width(icon->GetResource());
+			int currentIconOffsetY = (int)((actualItemHeight - currentIconHeight) / 2);
+			int currentIconOffsetX = (maxIconWidth - currentIconWidth) / 2;
+			int rect_y = 0, image_y = (yPos + currentIconOffsetY);
+			if (image_y + currentIconHeight > mRenderY + mRenderH)
+				rect_y = mRenderY + mRenderH - image_y;
+			else
+				rect_y = currentIconHeight;
+			gr_blit(icon->GetResource(), 0, 0, currentIconWidth, rect_y, mRenderX + currentIconOffsetX, image_y);
+		}
+
+		gr_textExWH(mRenderX + maxIconWidth + 5, yPos + fontOffsetY, label.c_str(), fontResource, mRenderX + listW, mRenderY + mRenderH);
+
+		// Add the separator
+		if (yPos + actualItemHeight < mRenderH + mRenderY) {
+			gr_color(mSeparatorColor.red, mSeparatorColor.green, mSeparatorColor.blue, 255);
+			gr_fill(mRenderX, yPos + actualItemHeight - mSeparatorH, listW, mSeparatorH);
+		}
+
+		// Move the yPos
+		yPos += actualItemHeight;
+	}
+
+	// Render the Header (last so that it overwrites the top most row for per pixel scrolling)
+	// First step, fill background
+	gr_color(mHeaderBackgroundColor.red, mHeaderBackgroundColor.green, mHeaderBackgroundColor.blue, 255);
+	gr_fill(mRenderX, mRenderY, mRenderW, mHeaderH);
+
+	// Now, we need the header (icon + text)
+	yPos = mRenderY;
+	{
+		Resource* headerIcon;
+		int mIconOffsetX = 0;
+
+		// render the icon if it exists
+		headerIcon = mHeaderIcon;
+		if (headerIcon && headerIcon->GetResource())
+		{
+			gr_blit(headerIcon->GetResource(), 0, 0, mHeaderIconWidth, mHeaderIconHeight, mRenderX + ((mHeaderIconWidth - maxIconWidth) / 2), (yPos + (int)((mHeaderH - mHeaderIconHeight) / 2)));
+			mIconOffsetX = maxIconWidth;
+		}
+
+		// render the text
+		gr_color(mHeaderFontColor.red, mHeaderFontColor.green, mHeaderFontColor.blue, 255);
+		gr_textExWH(mRenderX + mIconOffsetX + 5, yPos + (int)((mHeaderH - mFontHeight) / 2), mLastHeaderValue.c_str(), fontResource, mRenderX + mRenderW, mRenderY + mRenderH);
+
+		// Add the separator
+		gr_color(mHeaderSeparatorColor.red, mHeaderSeparatorColor.green, mHeaderSeparatorColor.blue, 255);
+		gr_fill(mRenderX, yPos + mHeaderH - mHeaderSeparatorH, mRenderW, mHeaderSeparatorH);
+	}
+
+	// render fast scroll
+	lines = GetDisplayItemCount();
+	if (hasScroll) {
+		int startX = listW + mRenderX;
+		int fWidth = mRenderW - listW;
+		int fHeight = mRenderH - mHeaderH;
+
+		// line
+		gr_color(mFastScrollLineColor.red, mFastScrollLineColor.green, mFastScrollLineColor.blue, 255);
+		gr_fill(startX + fWidth/2, mRenderY + mHeaderH, mFastScrollLineW, mRenderH - mHeaderH);
+
+		// rect
+		int pct = 0;
+		if (GetDisplayRemainder() != 0) {
+			// Properly handle the percentage if a partial line is present
+			int partial_line_size = actualItemHeight - GetDisplayRemainder();
+			pct = ((firstDisplayedItem*actualItemHeight - y_offset)*100)/(listSize*actualItemHeight-((lines + 1)*actualItemHeight) + partial_line_size);
+		} else {
+			pct = ((firstDisplayedItem*actualItemHeight - y_offset)*100)/(listSize*actualItemHeight-lines*actualItemHeight);
+		}
+		int mFastScrollRectX = startX + (fWidth - mFastScrollRectW)/2;
+		int mFastScrollRectY = mRenderY+mHeaderH + ((fHeight - mFastScrollRectH)*pct)/100;
+
+		gr_color(mFastScrollRectColor.red, mFastScrollRectColor.green, mFastScrollRectColor.blue, 255);
+		gr_fill(mFastScrollRectX, mFastScrollRectY, mFastScrollRectW, mFastScrollRectH);
+	}
+	mUpdate = 0;
+	return 0;
+}
+
+int GUIScrollList::Update(void)
+{
+	if(!isConditionTrue())
+		return 0;
+
+	if (!mHeaderIsStatic) {
+		std::string newValue = gui_parse_text(mHeaderText);
+		if (mLastHeaderValue != newValue) {
+			mLastHeaderValue = newValue;
+			mUpdate = 1;
+		}
+	}
+
+	// Handle kinetic scrolling
+	int maxScrollDistance = actualItemHeight * SCROLLING_SPEED_LIMIT;
+	if (scrollingSpeed == 0) {
+		// Do nothing
+		return 0;
+	} else if (scrollingSpeed > 0) {
+		if (scrollingSpeed < maxScrollDistance)
+			y_offset += scrollingSpeed;
+		else
+			y_offset += maxScrollDistance;
+		scrollingSpeed -= SCROLLING_SPEED_DECREMENT;
+	} else if (scrollingSpeed < 0) {
+		if (abs(scrollingSpeed) < maxScrollDistance)
+			y_offset += scrollingSpeed;
+		else
+			y_offset -= maxScrollDistance;
+		scrollingSpeed += SCROLLING_SPEED_DECREMENT;
+	}
+	if (abs(scrollingSpeed) < SCROLLING_FLOOR)
+		scrollingSpeed = 0;
+	HandleScrolling();
+	mUpdate = 1;
+
+	return 0;
+}
+
+size_t GUIScrollList::HitTestItem(int x, int y)
+{
+	// We only care about y position
+	if (y < mRenderY || y - mRenderY <= mHeaderH || y - mRenderY > mRenderH)
+		return NO_ITEM;
+
+	int startSelection = (y - mRenderY - mHeaderH);
+
+	// Locate the correct item
+	size_t actualSelection = firstDisplayedItem;
+	int selectY = y_offset;
+	while (selectY + actualItemHeight < startSelection) {
+		selectY += actualItemHeight;
+		actualSelection++;
+	}
+
+	if (actualSelection < GetItemCount())
+		return actualSelection;
+
+	return NO_ITEM;
+}
+
+int GUIScrollList::NotifyTouch(TOUCH_STATE state, int x, int y)
+{
+	if(!isConditionTrue())
+		return -1;
+
+	switch (state)
+	{
+	case TOUCH_START:
+		if (hasScroll && x >= mRenderX + mRenderW - mFastScrollW)
+			fastScroll = 1; // Initial touch is in the fast scroll region
+		if (scrollingSpeed != 0) {
+			selectedItem = NO_ITEM; // this allows the user to tap the list to stop the scrolling without selecting the item they tap
+			scrollingSpeed = 0; // stop scrolling on a new touch
+		} else if (!fastScroll) {
+			// find out which item the user touched
+			selectedItem = HitTestItem(x, y);
+		}
+		if (selectedItem != NO_ITEM)
+			mUpdate = 1;
+		lastY = last2Y = y;
+		break;
+
+	case TOUCH_DRAG:
+		if (fastScroll)
+		{
+			int pct = ((y-mRenderY-mHeaderH)*100)/(mRenderH-mHeaderH);
+			int totalSize = GetItemCount();
+			int lines = GetDisplayItemCount();
+
+			float l = float((totalSize-lines)*pct)/100;
+			if(l + lines >= totalSize)
+			{
+				firstDisplayedItem = totalSize - lines;
+				if (GetDisplayRemainder() != 0) {
+					// There's a partial row displayed, set the scrolling offset so that the last item really is at the very bottom
+					firstDisplayedItem--;
+					y_offset = GetDisplayRemainder() - actualItemHeight;
+				} else {
+					// There's no partial row so zero out the offset
+					y_offset = 0;
+				}
+			}
+			else
+			{
+				if (l < 0)
+					l = 0;
+				firstDisplayedItem = l;
+				y_offset = -(l - int(l))*actualItemHeight;
+				if (GetDisplayRemainder() != 0) {
+					// There's a partial row displayed, make sure y_offset doesn't go past the max
+					if (firstDisplayedItem == totalSize - lines - 1 && y_offset < GetDisplayRemainder() - actualItemHeight)
+						y_offset = GetDisplayRemainder() - actualItemHeight;
+				} else if (firstDisplayedItem == totalSize - lines)
+					y_offset = 0;
+			}
+
+			selectedItem = NO_ITEM;
+			mUpdate = 1;
+			scrollingSpeed = 0; // prevent kinetic scrolling when using fast scroll
+			break;
+		}
+
+		// Provide some debounce on initial touches
+		if (selectedItem != NO_ITEM && abs(y - lastY) < touchDebounce) {
+			mUpdate = 1;
+			break;
+		}
+
+		selectedItem = NO_ITEM; // nothing is selected because we dragged too far
+		// Handle scrolling
+		if (hasScroll) {
+			y_offset += y - lastY; // adjust the scrolling offset based on the difference between the starting touch and the current touch
+			last2Y = lastY; // keep track of previous y locations so that we can tell how fast to scroll for kinetic scrolling
+			lastY = y; // update last touch to the current touch so we can tell how far and what direction we scroll for the next touch event
+
+			HandleScrolling();
+		} else
+			y_offset = 0;
+		mUpdate = 1;
+		break;
+
+	case TOUCH_RELEASE:
+		fastScroll = 0;
+		if (selectedItem != NO_ITEM) {
+			// We've selected an item!
+			NotifySelect(selectedItem);
+			mUpdate = 1;
+
+			DataManager::Vibrate("tw_button_vibrate");
+			selectedItem = NO_ITEM;
+		} else {
+			// Start kinetic scrolling
+			scrollingSpeed = lastY - last2Y;
+			if (abs(scrollingSpeed) > SCROLLING_FLOOR)
+				scrollingSpeed *= SCROLLING_MULTIPLIER;
+			else
+				scrollingSpeed = 0;
+		}
+	case TOUCH_REPEAT:
+	case TOUCH_HOLD:
+		break;
+	}
+	return 0;
+}
+
+void GUIScrollList::HandleScrolling()
+{
+	// handle dragging downward, scrolling upward
+	// the offset should always be <= 0 and > -actualItemHeight, adjust the first display row and offset as needed
+	while(firstDisplayedItem && y_offset > 0) {
+		firstDisplayedItem--;
+		y_offset -= actualItemHeight;
+	}
+	if (firstDisplayedItem == 0 && y_offset > 0)
+		y_offset = 0; // user kept dragging downward past the top of the list, so always reset the offset to 0 since we can't scroll any further in this direction
+
+	// handle dragging upward, scrolling downward
+	int totalSize = GetItemCount();
+	int lines = GetDisplayItemCount(); // number of full lines our list can display at once
+	int bottom_offset = GetDisplayRemainder() - actualItemHeight; // extra display area that can display a partial line for per pixel scrolling
+
+	// the offset should always be <= 0 and > -actualItemHeight, adjust the first display row and offset as needed
+	while (firstDisplayedItem + lines + (bottom_offset ? 1 : 0) < totalSize && abs(y_offset) > actualItemHeight) {
+		firstDisplayedItem++;
+		y_offset += actualItemHeight;
+	}
+	// Check if we dragged too far, set the list at the bottom and adjust offset as needed
+	if (bottom_offset != 0 && firstDisplayedItem + lines + 1 >= totalSize && y_offset <= bottom_offset) {
+		firstDisplayedItem = totalSize - lines - 1;
+		y_offset = bottom_offset;
+	} else if (firstDisplayedItem + lines >= totalSize && y_offset < 0) {
+		firstDisplayedItem = totalSize - lines;
+		y_offset = 0;
+	}
+}
+
+int GUIScrollList::GetDisplayItemCount()
+{
+	return (mRenderH - mHeaderH) / (actualItemHeight);
+}
+
+int GUIScrollList::GetDisplayRemainder()
+{
+	return (mRenderH - mHeaderH) % actualItemHeight;
+}
+
+int GUIScrollList::NotifyVarChange(const std::string& varName, const std::string& value)
+{
+	GUIObject::NotifyVarChange(varName, value);
+
+	if(!isConditionTrue())
+		return 0;
+
+	if (!mHeaderIsStatic) {
+		std::string newValue = gui_parse_text(mHeaderText);
+		if (mLastHeaderValue != newValue) {
+			mLastHeaderValue = newValue;
+			firstDisplayedItem = 0;
+			y_offset = 0;
+			scrollingSpeed = 0; // stop kinetic scrolling on variable changes
+			mUpdate = 1;
+		}
+	}
+	return 0;
+}
+
+int GUIScrollList::SetRenderPos(int x, int y, int w /* = 0 */, int h /* = 0 */)
+{
+	mRenderX = x;
+	mRenderY = y;
+	if (w || h)
+	{
+		mRenderW = w;
+		mRenderH = h;
+	}
+	SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
+	mUpdate = 1;
+	return 0;
+}
+
+void GUIScrollList::SetPageFocus(int inFocus)
+{
+	if (inFocus) {
+		NotifyVarChange("", ""); // This forces a check for the header text
+		scrollingSpeed = 0; // stop kinetic scrolling on page changes
+		mUpdate = 1;
+	}
+}