gui: keyboard: introduce struct Layout and simplify caps handling

Also minor cleanups:
- move array limits from #defines to a protected enum
- zero fill layouts in ctor

Change-Id: I8fe0f8465ebc646ad3bf3cc3f8490dbdd384f43d
diff --git a/gui/keyboard.cpp b/gui/keyboard.cpp
index 306422f..914c944 100644
--- a/gui/keyboard.cpp
+++ b/gui/keyboard.cpp
@@ -44,11 +44,15 @@
 	xml_node<>* keylayout;
 	xml_node<>* keyrow;
 
-	for (layoutindex=0; layoutindex<MAX_KEYBOARD_LAYOUTS; layoutindex++)
-		keyboardImg[layoutindex] = NULL;
+	for (layoutindex=0; layoutindex<MAX_KEYBOARD_LAYOUTS; layoutindex++) {
+		layouts[layoutindex].keyboardImg = NULL;
+		memset(layouts[layoutindex].keys, 0, sizeof(Layout::keys));
+		memset(layouts[layoutindex].row_end_y, 0, sizeof(Layout::row_end_y));
+	}
 
 	mRendered = false;
 	currentLayout = 1;
+	CapsLockOn = false;
 	KeyboardHeight = KeyboardWidth = 0;
 
 	if (!node)  return;
@@ -56,6 +60,7 @@
 	mHighlightColor = LoadAttrColor(FindNode(node, "highlight"), "color", &hasHighlight);
 	mCapsHighlightColor = LoadAttrColor(FindNode(node, "capshighlight"), "color", &hasCapsHighlight);
 
+	// compatibility ugliness: resources should be specified in the layouts themselves instead
 	// Load the images for the different layouts
 	child = FindNode(node, "layout");
 	if (child)
@@ -64,7 +69,7 @@
 		strcpy(resource, "resource1");
 		attr = child->first_attribute(resource);
 		while (attr && layoutindex < (MAX_KEYBOARD_LAYOUTS + 1)) {
-			keyboardImg[layoutindex - 1] = LoadAttrImage(child, resource);
+			layouts[layoutindex - 1].keyboardImg = LoadAttrImage(child, resource);
 
 			layoutindex++;
 			resource[8] = (char)(layoutindex + 48);
@@ -73,10 +78,10 @@
 	}
 
 	// Check the first image to get height and width
-	if (keyboardImg[0] && keyboardImg[0]->GetResource())
+	if (layouts[0].keyboardImg && layouts[0].keyboardImg->GetResource())
 	{
-		KeyboardWidth = keyboardImg[0]->GetWidth();
-		KeyboardHeight = keyboardImg[0]->GetHeight();
+		KeyboardWidth = layouts[0].keyboardImg->GetWidth();
+		KeyboardHeight = layouts[0].keyboardImg->GetHeight();
 	}
 
 	// Load all of the layout maps
@@ -90,29 +95,15 @@
 			return;
 		}
 
+		Layout& lay = layouts[layoutindex - 1];
+
 		child = keylayout->first_node("keysize");
-		if (child) {
-			attr = child->first_attribute("height");
-			if (attr)
-				keyHeight = scale_theme_y(atoi(attr->value()));
-			else
-				keyHeight = 0;
-			attr = child->first_attribute("width");
-			if (attr)
-				keyWidth = scale_theme_x(atoi(attr->value()));
-			else
-				keyWidth = 0;
-			attr = child->first_attribute("capslock");
-			if (attr)
-				caps_tracking[layoutindex - 1].capslock = atoi(attr->value());
-			else
-				caps_tracking[layoutindex - 1].capslock = 1;
-			attr = child->first_attribute("revert_layout");
-			if (attr)
-				caps_tracking[layoutindex - 1].revert_layout = atoi(attr->value());
-			else
-				caps_tracking[layoutindex - 1].revert_layout = -1;
-		}
+		keyHeight = LoadAttrIntScaleY(child, "height", 0);
+		keyWidth = LoadAttrIntScaleX(child, "width", 0);
+		// compatibility ugliness: capslock="0" means that this is the caps layout. Also it has nothing to do with keysize.
+		lay.is_caps = (LoadAttrInt(child, "capslock", 1) == 0);
+		// compatibility ugliness: revert_layout has nothing to do with keysize.
+		lay.revert_layout = LoadAttrInt(child, "revert_layout", -1);
 
 		rowindex = 1;
 		Yindex = 0;
@@ -126,7 +117,7 @@
 			}
 
 			Yindex += keyHeight;
-			row_heights[layoutindex - 1][rowindex - 1] = Yindex;
+			lay.row_end_y[rowindex - 1] = Yindex;
 
 			keyindex = 1;
 			Xindex = 0;
@@ -146,7 +137,7 @@
 					return;
 				}
 
-				if (ParseKey(keyinfo, keyboard_keys[layoutindex - 1][rowindex - 1][keyindex - 1], Xindex, keyWidth, false))
+				if (ParseKey(keyinfo, lay.keys[rowindex - 1][keyindex - 1], Xindex, keyWidth, false))
 					LOGERR("Invalid key info on layout%i, row%i, key%02i.\n", layoutindex, rowindex, keyindex);
 
 
@@ -161,7 +152,7 @@
 						return;
 					}
 
-					if (ParseKey(keyinfo, keyboard_keys[layoutindex - 1][rowindex - 1][keyindex - 1], Xindex, keyWidth, true))
+					if (ParseKey(keyinfo, lay.keys[rowindex - 1][keyindex - 1], Xindex, keyWidth, true))
 						LOGERR("Invalid long press key info on layout%i, row%i, long%02i.\n", layoutindex, rowindex, keyindex);
 				}
 				keyindex++;
@@ -239,30 +230,30 @@
 		return 0;
 	}
 
-	int ret = 0;
+	Layout& lay = layouts[currentLayout - 1];
 
-	if (keyboardImg[currentLayout - 1] && keyboardImg[currentLayout - 1]->GetResource())
-		gr_blit(keyboardImg[currentLayout - 1]->GetResource(), 0, 0, KeyboardWidth, KeyboardHeight, mRenderX, mRenderY);
+	if (lay.keyboardImg && lay.keyboardImg->GetResource())
+		gr_blit(lay.keyboardImg->GetResource(), 0, 0, KeyboardWidth, KeyboardHeight, mRenderX, mRenderY);
 
 	// Draw highlight for capslock
-	if (hasCapsHighlight && caps_tracking[currentLayout - 1].capslock == 0 && caps_tracking[currentLayout - 1].set_capslock) {
-		int boxheight, boxwidth, x;
+	if (hasCapsHighlight && lay.is_caps && CapsLockOn) {
 		gr_color(mCapsHighlightColor.red, mCapsHighlightColor.green, mCapsHighlightColor.blue, mCapsHighlightColor.alpha);
 		for (int indexy=0; indexy<MAX_KEYBOARD_ROWS; indexy++) {
 			for (int indexx=0; indexx<MAX_KEYBOARD_KEYS; indexx++) {
-				if ((int)keyboard_keys[currentLayout - 1][indexy][indexx].key == KEYBOARD_LAYOUT && (int)keyboard_keys[currentLayout - 1][indexy][indexx].layout == caps_tracking[currentLayout - 1].revert_layout) {
+				if ((int)lay.keys[indexy][indexx].key == KEYBOARD_LAYOUT && (int)lay.keys[indexy][indexx].layout == lay.revert_layout) {
+					int boxheight, boxwidth, x;
 					if (indexy == 0)
-						boxheight = row_heights[currentLayout - 1][indexy];
+						boxheight = lay.row_end_y[indexy];
 					else
-						boxheight = row_heights[currentLayout - 1][indexy] - row_heights[currentLayout - 1][indexy - 1];
+						boxheight = lay.row_end_y[indexy] - lay.row_end_y[indexy - 1];
 					if (indexx == 0) {
 						x = mRenderX;
-						boxwidth = keyboard_keys[currentLayout - 1][indexy][indexx].end_x;
+						boxwidth = lay.keys[indexy][indexx].end_x;
 					} else {
-						x = mRenderX + keyboard_keys[currentLayout - 1][indexy][indexx - 1].end_x;
-						boxwidth = keyboard_keys[currentLayout - 1][indexy][indexx].end_x - keyboard_keys[currentLayout - 1][indexy][indexx - 1].end_x;
+						x = mRenderX + lay.keys[indexy][indexx - 1].end_x;
+						boxwidth = lay.keys[indexy][indexx].end_x - lay.keys[indexy][indexx - 1].end_x;
 					}
-					gr_fill(x, mRenderY + row_heights[currentLayout - 1][indexy - 1], boxwidth, boxheight);
+					gr_fill(x, mRenderY + lay.row_end_y[indexy - 1], boxwidth, boxheight);
 				}
 			}
 		}
@@ -271,24 +262,24 @@
 	if (hasHighlight && highlightRenderCount != 0) {
 		int boxheight, boxwidth, x;
 		if (rowY == 0)
-			boxheight = row_heights[currentLayout - 1][rowY];
+			boxheight = lay.row_end_y[rowY];
 		else
-			boxheight = row_heights[currentLayout - 1][rowY] - row_heights[currentLayout - 1][rowY - 1];
+			boxheight = lay.row_end_y[rowY] - lay.row_end_y[rowY - 1];
 		if (colX == 0) {
 			x = mRenderX;
-			boxwidth = keyboard_keys[currentLayout - 1][rowY][colX].end_x;
+			boxwidth = lay.keys[rowY][colX].end_x;
 		} else {
-			x = mRenderX + keyboard_keys[currentLayout - 1][rowY][colX - 1].end_x;
-			boxwidth = keyboard_keys[currentLayout - 1][rowY][colX].end_x - keyboard_keys[currentLayout - 1][rowY][colX - 1].end_x;
+			x = mRenderX + lay.keys[rowY][colX - 1].end_x;
+			boxwidth = lay.keys[rowY][colX].end_x - lay.keys[rowY][colX - 1].end_x;
 		}
 		gr_color(mHighlightColor.red, mHighlightColor.green, mHighlightColor.blue, mHighlightColor.alpha);
-		gr_fill(x, mRenderY + row_heights[currentLayout - 1][rowY - 1], boxwidth, boxheight);
+		gr_fill(x, mRenderY + lay.row_end_y[rowY - 1], boxwidth, boxheight);
 		if (highlightRenderCount > 0)
 			highlightRenderCount--;
 	} else
 		mRendered = true;
 
-	return ret;
+	return 0;
 }
 
 int GUIKeyboard::Update(void)
@@ -317,10 +308,12 @@
 	int rely = y - mRenderY;
 	int relx = x - mRenderX;
 
+	Layout& lay = layouts[currentLayout - 1];
+
 	// Find the correct row
 	int row;
 	for (row = 0; row < MAX_KEYBOARD_ROWS; ++row) {
-		if (row_heights[currentLayout - 1][row] > rely)
+		if (lay.row_end_y[row] > rely)
 			break;
 	}
 	if (row == MAX_KEYBOARD_ROWS)
@@ -330,7 +323,7 @@
 	int col;
 	int x1 = 0;
 	for (col = 0; col < MAX_KEYBOARD_KEYS; ++col) {
-		Key& key = keyboard_keys[currentLayout - 1][row][col];
+		Key& key = lay.keys[row][col];
 		if (x1 <= relx && relx < key.end_x && key.key != 0) {
 			// This is the key that was pressed!
 			rowY = row;
@@ -409,26 +402,15 @@
 			return 0;
 		} else {
 			Key& key = *initial_key;
+			Layout& lay = layouts[currentLayout - 1];
 			if (state == TOUCH_RELEASE && was_held == 0) {
 				DataManager::Vibrate("tw_keyboard_vibrate");
-				if ((int)key.key < KEYBOARD_SPECIAL_KEYS && (int)key.key > 0) {
-					// Regular key
-					PageManager::NotifyKeyboard(key.key);
-					if (caps_tracking[currentLayout - 1].capslock == 0 && !caps_tracking[currentLayout - 1].set_capslock) {
-						// caps lock was not set, change layouts
-						currentLayout = caps_tracking[currentLayout - 1].revert_layout;
-						mRendered = false;
-					}
-				} else if ((int)key.key == KEYBOARD_LAYOUT) {
+				if ((int)key.key == KEYBOARD_LAYOUT) {
 					// Switch layouts
-					if (caps_tracking[currentLayout - 1].capslock == 0 && key.layout == caps_tracking[currentLayout - 1].revert_layout) {
-						if (!caps_tracking[currentLayout - 1].set_capslock) {
-							caps_tracking[currentLayout - 1].set_capslock = 1; // Set the caps lock
-						} else {
-							caps_tracking[currentLayout - 1].set_capslock = 0; // Unset the caps lock and change layouts
-							currentLayout = key.layout;
-						}
+					if (lay.is_caps && key.layout == lay.revert_layout && !CapsLockOn) {
+						CapsLockOn = true; // Set the caps lock
 					} else {
+						CapsLockOn = false; // Unset the caps lock and change layouts
 						currentLayout = key.layout;
 					}
 					mRendered = false;
@@ -437,6 +419,14 @@
 					highlightRenderCount = 0;
 					// Send action notification
 					PageManager::NotifyKeyboard(key.key);
+				} else if ((int)key.key < KEYBOARD_SPECIAL_KEYS && (int)key.key > 0) {
+					// Regular key
+					PageManager::NotifyKeyboard(key.key);
+					if (!CapsLockOn && lay.is_caps) {
+						// caps lock was not set, change layouts
+						currentLayout = lay.revert_layout;
+						mRendered = false;
+					}
 				}
 			} else if (state == TOUCH_HOLD) {
 				was_held = 1;
diff --git a/gui/objects.hpp b/gui/objects.hpp
index 6eeefc2..e70053e 100644
--- a/gui/objects.hpp
+++ b/gui/objects.hpp
@@ -815,9 +815,6 @@
 	int sUpdate;
 };
 
-#define MAX_KEYBOARD_LAYOUTS 5
-#define MAX_KEYBOARD_ROWS 9
-#define MAX_KEYBOARD_KEYS 20
 #define KEYBOARD_ACTION 253
 #define KEYBOARD_LAYOUT 254
 #define KEYBOARD_SWIPE_LEFT 252
@@ -852,23 +849,29 @@
 		int layout;
 	};
 	int ParseKey(const char* keyinfo, Key& key, int& Xindex, int keyWidth, bool longpress);
-	struct capslock_tracking_struct
+
+	enum {
+		MAX_KEYBOARD_LAYOUTS = 5,
+		MAX_KEYBOARD_ROWS = 9,
+		MAX_KEYBOARD_KEYS = 20
+	};
+	struct Layout
 	{
-		int capslock;
-		int set_capslock;
+		ImageResource* keyboardImg;
+		struct Key keys[MAX_KEYBOARD_ROWS][MAX_KEYBOARD_KEYS];
+		int row_end_y[MAX_KEYBOARD_ROWS];
+		bool is_caps;
 		int revert_layout;
 	};
+	Layout layouts[MAX_KEYBOARD_LAYOUTS];
 
 	// Find key at screen coordinates
 	Key* HitTestKey(int x, int y);
 
-	ImageResource* keyboardImg[MAX_KEYBOARD_LAYOUTS];
-	struct Key keyboard_keys[MAX_KEYBOARD_LAYOUTS][MAX_KEYBOARD_ROWS][MAX_KEYBOARD_KEYS];
-	struct capslock_tracking_struct caps_tracking[MAX_KEYBOARD_LAYOUTS];
 	bool mRendered;
 	std::string mVariable;
 	int currentLayout;
-	int row_heights[MAX_KEYBOARD_LAYOUTS][MAX_KEYBOARD_ROWS];
+	bool CapsLockOn;
 	unsigned int KeyboardWidth, KeyboardHeight;
 	int rowY, colX, highlightRenderCount;
 	bool hasHighlight, hasCapsHighlight;