gui: keyboard: support longpress label offset, code cleanup

To position the longpress label:
<longpress font="..." textcolor="..." x="5" y="0"/>
x and y is the distance from the upper right corner.

- extract method GUIKeyboard::DrawKey
- clean up handling of currently pressed key
- integrate key highlighting into main key drawing loops

Change-Id: I80ccf2975d30cffa2a48627a74ab3693c84e4fa4
diff --git a/gui/keyboard.cpp b/gui/keyboard.cpp
index 8ab2280..f35a5d3 100644
--- a/gui/keyboard.cpp
+++ b/gui/keyboard.cpp
@@ -35,7 +35,7 @@
 	: GUIObject(node)
 {
 	int layoutindex, rowindex, keyindex, Xindex, Yindex, keyHeight = 0, keyWidth = 0;
-	rowY = colX = -1;
+	currentKey = NULL;
 	highlightRenderCount = 0;
 	hasHighlight = hasCapsHighlight = false;
 	char resource[10], layout[8], row[5], key[6], longpress[7];
@@ -79,6 +79,7 @@
 	child = FindNode(node, "longpress");
 	mLongpressFont = PageManager::GetResources()->FindFont(LoadAttrString(child, "font", "keylabel-longpress"));
 	mLongpressFontColor = LoadAttrColor(child, "textcolor", COLOR(128,128,128,255));
+	LoadPlacement(child, &longpressOffsetX, &longpressOffsetY);
 
 	LoadKeyLabels(node, 0); // load global key labels
 
@@ -270,6 +271,78 @@
 	}
 }
 
+void GUIKeyboard::DrawKey(Key& key, int keyX, int keyY, int keyW, int keyH)
+{
+	unsigned char keychar = key.key;
+	if (!keychar)
+		return;
+
+	// key background
+	COLOR& c = (keychar >= 32 && keychar < 127) ? mKeyColorAlphanumeric : mKeyColorOther;
+	gr_color(c.red, c.green, c.blue, c.alpha);
+	keyX += mKeyMarginX;
+	keyY += mKeyMarginY;
+	keyW -= mKeyMarginX * 2;
+	keyH -= mKeyMarginY * 2;
+	gr_fill(keyX, keyY, keyW, keyH);
+
+	// key label
+	FontResource* labelFont = mFont;
+	string labelText;
+	ImageResource* labelImage = NULL;
+	if (keychar > 32 && keychar < 127) {
+		labelText = (char) keychar;
+		gr_color(mFontColor.red, mFontColor.green, mFontColor.blue, mFontColor.alpha);
+	}
+	else {
+		// search for a special key label
+		for (std::vector<KeyLabel>::iterator it = mKeyLabels.begin(); it != mKeyLabels.end(); ++it) {
+			if (it->layout_from > 0 && it->layout_from != currentLayout)
+				continue; // this label is for another layout
+			if (it->key == key.key && it->layout_to == key.layout)
+			{
+				// found a label
+				labelText = it->text;
+				labelImage = it->image;
+				break;
+			}
+		}
+		labelFont = mSmallFont;
+		gr_color(mFontColorSmall.red, mFontColorSmall.green, mFontColorSmall.blue, mFontColorSmall.alpha);
+	}
+
+	if (labelImage)
+	{
+		int w = labelImage->GetWidth();
+		int h = labelImage->GetHeight();
+		int x = keyX + (keyW - w) / 2;
+		int y = keyY + (keyH - h) / 2;
+		gr_blit(labelImage->GetResource(), 0, 0, w, h, x, y);
+	}
+	else if (!labelText.empty())
+	{
+		void* fontResource = labelFont->GetResource();
+		int textW = gr_measureEx(labelText.c_str(), fontResource);
+		int textH = labelFont->GetHeight();
+		int textX = keyX + (keyW - textW) / 2;
+		int textY = keyY + (keyH - textH) / 2;
+		gr_textEx(textX, textY, labelText.c_str(), fontResource);
+	}
+
+	// longpress key label (only if font is defined)
+	keychar = key.longpresskey;
+	if (keychar > 32 && keychar < 127 && mLongpressFont->GetResource()) {
+		void* fontResource = mLongpressFont->GetResource();
+		gr_color(mLongpressFontColor.red, mLongpressFontColor.green, mLongpressFontColor.blue, mLongpressFontColor.alpha);
+		string text(1, keychar);
+		int textH = mLongpressFont->GetHeight();
+		int textW = gr_measureEx(text.c_str(), fontResource);
+		int textX = keyX + keyW - longpressOffsetX - textW;
+		int textY = keyY + longpressOffsetY;
+		gr_textEx(textX, textY, text.c_str(), fontResource);
+	}
+}
+
 int GUIKeyboard::Render(void)
 {
 	if (!isConditionTrue())
@@ -280,159 +353,55 @@
 
 	Layout& lay = layouts[currentLayout - 1];
 
+	bool drawKeys = false;
 	if (lay.keyboardImg && lay.keyboardImg->GetResource())
+		// keyboard is image based
 		gr_blit(lay.keyboardImg->GetResource(), 0, 0, mRenderW, mRenderH, mRenderX, mRenderY);
 	else {
+		// keyboard is software drawn
+		// fill background
 		gr_color(mBackgroundColor.red, mBackgroundColor.green, mBackgroundColor.blue, mBackgroundColor.alpha);
 		gr_fill(mRenderX, mRenderY, mRenderW, mRenderH);
-		// draw keys
-		int y1 = 0;
-		for (int row = 0; row < MAX_KEYBOARD_ROWS; ++row) {
-			int rowY = mRenderY + y1;
-			int rowH = lay.row_end_y[row] - y1;
-			y1 = lay.row_end_y[row];
-			int x1 = 0;
-			for (int col = 0; col < MAX_KEYBOARD_KEYS; ++col) {
-				Key& key = lay.keys[row][col];
-				int keyY = rowY;
-				int keyH = rowH;
-				int keyX = mRenderX + x1;
-				int keyW = key.end_x - x1;
-				x1 = key.end_x;
+		drawKeys = true;
+	}
 
-				unsigned char keychar = key.key;
-				if (!keychar)
-					continue;
+	// draw keys
+	int y1 = 0;
+	for (int row = 0; row < MAX_KEYBOARD_ROWS; ++row) {
+		int rowY = mRenderY + y1;
+		int rowH = lay.row_end_y[row] - y1;
+		y1 = lay.row_end_y[row];
+		int x1 = 0;
+		for (int col = 0; col < MAX_KEYBOARD_KEYS; ++col) {
+			Key& key = lay.keys[row][col];
+			int keyY = rowY;
+			int keyH = rowH;
+			int keyX = mRenderX + x1;
+			int keyW = key.end_x - x1;
+			x1 = key.end_x;
 
-				// draw a single key
-				// key background
-				COLOR& c = (keychar >= 32 && keychar < 127) ? mKeyColorAlphanumeric : mKeyColorOther;
-				gr_color(c.red, c.green, c.blue, c.alpha);
-				keyX += mKeyMarginX;
-				keyY += mKeyMarginY;
-				keyW -= mKeyMarginX * 2;
-				keyH -= mKeyMarginY * 2;
+			// Draw key for software drawn keyboard
+			if (drawKeys)
+				DrawKey(key, keyX, keyY, keyW, keyH);
+
+			// Draw highlight for capslock
+			if (hasCapsHighlight && lay.is_caps && CapsLockOn && (int)key.key == KEYBOARD_LAYOUT && key.layout == lay.revert_layout) {
+				gr_color(mCapsHighlightColor.red, mCapsHighlightColor.green, mCapsHighlightColor.blue, mCapsHighlightColor.alpha);
 				gr_fill(keyX, keyY, keyW, keyH);
+			}
 
-				// key label
-				FontResource* labelFont = mFont;
-				string labelText;
-				ImageResource* labelImage = NULL;
-				if (keychar > 32 && keychar < 127) {
-					labelText = (char) keychar;
-					gr_color(mFontColor.red, mFontColor.green, mFontColor.blue, mFontColor.alpha);
-				}
-				else {
-					/*
-					static const char* shiftLabels[] = {"abc", "ABC", "?123", "~\\{"};
-					switch (keychar) {
-						case KEYBOARD_LAYOUT:
-							labelText = shiftLabels[key.layout - 1];
-							break;
-						case KEYBOARD_BACKSPACE:
-							labelText = "Bksp";
-							break;
-						case KEYBOARD_ACTION:
-							labelText = "Enter";
-							break;
-					}
-					*/
-					// search for a special key label
-					for (std::vector<KeyLabel>::iterator it = mKeyLabels.begin(); it != mKeyLabels.end(); ++it) {
-						if (it->layout_from > 0 && it->layout_from != currentLayout)
-							continue; // this label is for another layout
-						if (it->key == key.key && it->layout_to == key.layout)
-						{
-							// found a label
-							labelText = it->text;
-							labelImage = it->image;
-							break;
-						}
-					}
-					labelFont = mSmallFont;
-					gr_color(mFontColorSmall.red, mFontColorSmall.green, mFontColorSmall.blue, mFontColorSmall.alpha);
-				}
-
-				if (labelImage)
-				{
-					int w = labelImage->GetWidth();
-					int h = labelImage->GetHeight();
-					int x = keyX + (keyW - w) / 2;
-					int y = keyY + (keyH - h) / 2;
-					gr_blit(labelImage->GetResource(), 0, 0, w, h, x, y);
-				}
-				else if (!labelText.empty())
-				{
-					void* fontResource = labelFont->GetResource();
-					int textW = gr_measureEx(labelText.c_str(), fontResource);
-					int textH = labelFont->GetHeight();
-					int textX = keyX + (keyW - textW) / 2;
-					int textY = keyY + (keyH - textH) / 2;
-					gr_textEx(textX, textY, labelText.c_str(), fontResource);
-				}
-
-				// longpress key label (only if font is defined)
-				keychar = key.longpresskey;
-				if (keychar > 32 && keychar < 127 && mLongpressFont->GetResource()) {
-					void* fontResource = mLongpressFont->GetResource();
-					gr_color(mLongpressFontColor.red, mLongpressFontColor.green, mLongpressFontColor.blue, mLongpressFontColor.alpha);
-					string text(1, keychar);
-					int textH = mLongpressFont->GetHeight();
-					int textW = gr_measureEx(text.c_str(), fontResource);
-					int textX = keyX + keyW - 5 - textW;	// TODO: configure these offsets
-					int textY = keyY + 0;
-					gr_textEx(textX, textY, text.c_str(), fontResource);
-				}
+			// Highlight current key
+			if (hasHighlight && &key == currentKey && highlightRenderCount != 0) {
+				gr_color(mHighlightColor.red, mHighlightColor.green, mHighlightColor.blue, mHighlightColor.alpha);
+				gr_fill(keyX, keyY, keyW, keyH);
 			}
 		}
 	}
 
-	// Draw highlight for capslock
-	// TODO: integrate with key drawing
-	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)lay.keys[indexy][indexx].key == KEYBOARD_LAYOUT && (int)lay.keys[indexy][indexx].layout == lay.revert_layout) {
-					int boxheight, boxwidth, x;
-					if (indexy == 0)
-						boxheight = lay.row_end_y[indexy];
-					else
-						boxheight = lay.row_end_y[indexy] - lay.row_end_y[indexy - 1];
-					if (indexx == 0) {
-						x = mRenderX;
-						boxwidth = lay.keys[indexy][indexx].end_x;
-					} else {
-						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 + lay.row_end_y[indexy - 1], boxwidth, boxheight);
-				}
-			}
-		}
-	}
-
-	// TODO: integrate with key drawing
-	if (hasHighlight && highlightRenderCount != 0) {
-		int boxheight, boxwidth, x;
-		if (rowY == 0)
-			boxheight = lay.row_end_y[rowY];
-		else
-			boxheight = lay.row_end_y[rowY] - lay.row_end_y[rowY - 1];
-		if (colX == 0) {
-			x = mRenderX;
-			boxwidth = lay.keys[rowY][colX].end_x;
-		} else {
-			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 + lay.row_end_y[rowY - 1], boxwidth, boxheight);
-		if (highlightRenderCount > 0)
-			highlightRenderCount--;
-	} else
+	if (!hasHighlight || highlightRenderCount == 0)
 		mRendered = true;
-
+	else if (highlightRenderCount > 0)
+		highlightRenderCount--;
 	return 0;
 }
 
@@ -477,8 +446,6 @@
 		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;
-			colX = col;
 			return &key;
 		}
 		x1 = key.end_x;
@@ -489,7 +456,6 @@
 int GUIKeyboard::NotifyTouch(TOUCH_STATE state, int x, int y)
 {
 	static int was_held = 0, startX = 0;
-	static Key* initial_key = 0;
 
 	if (!isConditionTrue())	 return -1;
 
@@ -498,8 +464,8 @@
 	case TOUCH_START:
 		was_held = 0;
 		startX = x;
-		initial_key = HitTestKey(x, y);
-		if (initial_key)
+		currentKey = HitTestKey(x, y);
+		if (currentKey)
 			highlightRenderCount = -1;
 		else
 			highlightRenderCount = 0;
@@ -528,7 +494,7 @@
 		// fall through
 	case TOUCH_HOLD:
 	case TOUCH_REPEAT:
-		if (!initial_key) {
+		if (!currentKey) {
 			if (highlightRenderCount != 0) {
 				highlightRenderCount = 0;
 				mRendered = false;
@@ -544,15 +510,16 @@
 			mRendered = false;
 		}
 
-		if (HitTestKey(x, y) != initial_key) {
+		if (HitTestKey(x, y) != currentKey) {
 			// We dragged off of the starting key
+			currentKey = NULL;
 			if (highlightRenderCount != 0) {
 				highlightRenderCount = 0;
 				mRendered = false;
 			}
 			return 0;
 		} else {
-			Key& key = *initial_key;
+			Key& key = *currentKey;
 			Layout& lay = layouts[currentLayout - 1];
 			if (state == TOUCH_RELEASE && was_held == 0) {
 				DataManager::Vibrate("tw_keyboard_vibrate");
diff --git a/gui/objects.hpp b/gui/objects.hpp
index 881b390..be1f973 100644
--- a/gui/objects.hpp
+++ b/gui/objects.hpp
@@ -853,6 +853,7 @@
 	};
 	int ParseKey(const char* keyinfo, Key& key, int& Xindex, int keyWidth, bool longpress);
 	void LoadKeyLabels(xml_node<>* parent, int layout);
+	void DrawKey(Key& key, int keyX, int keyY, int keyW, int keyH);
 
 	enum {
 		MAX_KEYBOARD_LAYOUTS = 5,
@@ -886,7 +887,8 @@
 	std::string mVariable;
 	int currentLayout;
 	bool CapsLockOn;
-	int rowY, colX, highlightRenderCount;
+	int highlightRenderCount;
+	Key* currentKey;
 	bool hasHighlight, hasCapsHighlight;
 	COLOR mHighlightColor;
 	COLOR mCapsHighlightColor;
@@ -895,6 +897,7 @@
 	FontResource* mFont; // for main key labels
 	FontResource* mSmallFont; // for key labels like "?123"
 	FontResource* mLongpressFont; // for the small longpress label in the upper right corner
+	int longpressOffsetX, longpressOffsetY; // distance of the longpress label from the key corner
 	COLOR mLongpressFontColor;
 	COLOR mBackgroundColor; // keyboard background color
 	COLOR mKeyColorAlphanumeric; // key background color