gui: support conditions for listbox items

Change-Id: Iea189ac53ec3d7c1bea83da7b77684778b1c0f46
diff --git a/gui/listbox.cpp b/gui/listbox.cpp
index d82736e..be2b486 100644
--- a/gui/listbox.cpp
+++ b/gui/listbox.cpp
@@ -58,45 +58,41 @@
 		DataManager::GetValue(mVariable, currentValue);
 	}
 	else
-		allowSelection = false;		// allows using listbox as a read-only list
+		allowSelection = false;		// allows using listbox as a read-only list or menu
 
 	// Get the data for the list
 	child = FindNode(node, "listitem");
 	if (!child) return;
 	while (child) {
-		ListData data;
+		ListItem item;
 
 		attr = child->first_attribute("name");
 		if (!attr)
 			continue;
-		data.displayName = gui_parse_text(attr->value());
-		data.variableValue = gui_parse_text(child->value());
-		if (child->value() == currentValue) {
-			data.selected = 1;
-		} else {
-			data.selected = 0;
-		}
-		data.action = NULL;
+		item.displayName = gui_parse_text(attr->value());
+		item.variableValue = gui_parse_text(child->value());
+		item.selected = (child->value() == currentValue);
+		item.action = NULL;
 		xml_node<>* action = child->first_node("action");
 		if (action) {
-			data.action = new GUIAction(action);
+			item.action = new GUIAction(action);
 			allowSelection = true;
 		}
 		xml_node<>* variable_name = child->first_node("data");
 		if (variable_name) {
 			attr = variable_name->first_attribute("variable");
 			if (attr) {
-				data.variableName = attr->value();
-				if (DataManager::GetIntValue(data.variableName) == 0)
-					data.selected = 0;
-				else
-					data.selected = 1;
+				item.variableName = attr->value();
+				item.selected = (DataManager::GetIntValue(item.variableName) != 0);
 				allowSelection = true;
 				isCheckList = true;
 			}
 		}
 
-		mList.push_back(data);
+		LoadConditions(child, item.mConditions);
+
+		mListItems.push_back(item);
+		mVisibleItems.push_back(mListItems.size()-1);
 
 		child = child->next_sibling("listitem");
 	}
@@ -128,37 +124,46 @@
 	if(!isConditionTrue())
 		return 0;
 
-	if (isCheckList) {
-		int i, listSize = mList.size();
-
-		for (i = 0; i < listSize; i++) {
-			if (mList.at(i).variableName == varName) {
-				if (value == "0") {
-					mList.at(i).selected = 0;
-				} else {
-					mList.at(i).selected = 1;
-				}
-			}
-		}
-		mUpdate = 1;
-		return 0;
-	}
 	// 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++) {
-			if (mList.at(i).variableValue == currentValue) {
-				mList.at(i).selected = 1;
-				SetVisibleListLocation(i);
-			} else
-				mList.at(i).selected = 0;
-		}
 		mUpdate = 1;
-		return 0;
 	}
+
+	std::vector<size_t> mVisibleItemsOld;
+	std::swap(mVisibleItemsOld, mVisibleItems);
+	for (size_t i = 0; i < mListItems.size(); i++) {
+		ListItem& item = mListItems[i];
+		// update per-item visibility condition
+		bool itemVisible = UpdateConditions(item.mConditions, varName);
+		if (itemVisible)
+			mVisibleItems.push_back(i);
+
+		if (isCheckList)
+		{
+			if (item.variableName == varName) {
+				item.selected = (value != "0");
+				mUpdate = 1;
+			}
+		}
+		else if (varName == mVariable) {
+			if (item.variableValue == currentValue) {
+				item.selected = 1;
+				SetVisibleListLocation(mVisibleItems.empty() ? 0 : mVisibleItems.size()-1);
+			} else {
+				item.selected = 0;
+			}
+		}
+	}
+
+	if (mVisibleItemsOld != mVisibleItems) {
+		mUpdate = 1; // some item's visibility has changed
+		if (firstDisplayedItem >= mVisibleItems.size()) {
+			// all items in the view area were removed - make last item visible
+			SetVisibleListLocation(mVisibleItems.empty() ? 0 : mVisibleItems.size()-1);
+		}
+	}
+
 	return 0;
 }
 
@@ -173,15 +178,16 @@
 
 size_t GUIListBox::GetItemCount()
 {
-	return mList.size();
+	return mVisibleItems.size();
 }
 
 void GUIListBox::RenderItem(size_t itemindex, int yPos, bool selected)
 {
 	// note: the "selected" parameter above is for the currently touched item
 	// don't confuse it with the more persistent "selected" flag per list item used below
-	ImageResource* icon = mList.at(itemindex).selected ? mIconSelected : mIconUnselected;
-	const std::string& text = mList.at(itemindex).displayName;
+	ListItem& item = mListItems[mVisibleItems[itemindex]];
+	ImageResource* icon = item.selected ? mIconSelected : mIconUnselected;
+	const std::string& text = item.displayName;
 
 	RenderStdItem(yPos, selected, icon, text.c_str());
 }
@@ -189,27 +195,24 @@
 void GUIListBox::NotifySelect(size_t item_selected)
 {
 	if (!isCheckList) {
-		for (size_t i = 0; i < mList.size(); i++) {
-			mList.at(i).selected = 0;
+		// deselect all items, even invisible ones
+		for (size_t i = 0; i < mListItems.size(); i++) {
+			mListItems[i].selected = 0;
 		}
 	}
-	if (item_selected < mList.size()) {
-		ListData& data = mList.at(item_selected);
-		if (isCheckList) {
-			if (data.selected) {
-				data.selected = 0;
-				DataManager::SetValue(data.variableName, "0");
-			} else {
-				data.selected = 1;
-				DataManager::SetValue(data.variableName, "1");
-			}
-		} else {
-			data.selected = 1;
-			string str = data.variableValue;	// [check] should this set currentValue instead?
-			DataManager::SetValue(mVariable, str);
-		}
-		if (data.action)
-			data.action->doActions();
+
+	ListItem& item = mListItems[mVisibleItems[item_selected]];
+
+	if (isCheckList) {
+		int selected = 1 - item.selected;
+		item.selected = selected;
+		DataManager::SetValue(item.variableName, selected ? "1" : "0");
+	} else {
+		item.selected = 1;
+		string str = item.variableValue;	// [check] should this set currentValue instead?
+		DataManager::SetValue(mVariable, str);
 	}
+	if (item.action)
+		item.action->doActions();
 	mUpdate = 1;
 }