GUIListBox::GUIListBox(xml_node<>* node) : GUIScrollList(node)
{
	xml_attribute<>* attr;
	xml_node<>* child;
	mIconSelected = mIconUnselected = NULL;
	mUpdate = 0;

	// Get the icons, if any
	child = FindNode(node, "icon");
	if (child) {
		mIconSelected = LoadAttrImage(child, "selected");
		mIconUnselected = LoadAttrImage(child, "unselected");
	}
	int iconWidth = std::max(mIconSelected->GetWidth(), mIconUnselected->GetWidth());
	int iconHeight = std::max(mIconSelected->GetHeight(), mIconUnselected->GetHeight());
	SetMaxIconSize(iconWidth, iconHeight);

	// Handle the result variable
	child = FindNode(node, "data");
	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);
	}
	else
		allowSelection = false;		// allows using listbox as a read-only list

	// Get the data for the list
	child = FindNode(node, "listitem");
	if (!child) return;
	while (child) {
		ListData data;

		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;
		xml_node<>* action = child->first_node("action");
		if (action) {
			data.action = new GUIAction(action);
			allowSelection = true;
		}

		mList.push_back(data);

		child = child->next_sibling("listitem");
	}
}
GUICheckbox::GUICheckbox(xml_node<>* node)
	: GUIObject(node)
{
	xml_attribute<>* attr;
	xml_node<>* child;

	mChecked = NULL;
	mUnchecked = NULL;
	mLabel = NULL;
	mRendered = false;

	mLastState = 0;

	if (!node)
		return;

	// The label can be loaded directly
	mLabel = new GUIText(node);

	// Read the check states
	child = FindNode(node, "image");
	if (child)
	{
		mChecked = LoadAttrImage(child, "checked");
		mUnchecked = LoadAttrImage(child, "unchecked");
	}

	// Get the variable data
	child = FindNode(node, "data");
	if (child)
	{
		attr = child->first_attribute("variable");
		if (attr)
			mVarName = attr->value();
		attr = child->first_attribute("default");
		if (attr)
			DataManager::SetValue(mVarName, attr->value());
	}

	mCheckW = mChecked->GetWidth();
	mCheckH = mChecked->GetHeight();
	if (mCheckW == 0)
	{
		mCheckW = mUnchecked->GetWidth();
		mCheckH = mUnchecked->GetHeight();
	}

	int x, y, w, h;
	mLabel->GetRenderPos(x, y, w, h);
	SetRenderPos(x, y, 0, 0);
}
Example #3
0
GUIProgressBar::GUIProgressBar(xml_node<>* node) : GUIObject(node)
{
	xml_attribute<>* attr;
	xml_node<>* child;

	mEmptyBar = NULL;
	mFullBar = NULL;
	mLastPos = 0;
	mSlide = 0.0;
	mSlideInc = 0.0;

	if (!node)
	{
		LOGERR("GUIProgressBar created without XML node\n");
		return;
	}

	child = FindNode(node, "resource");
	if (child)
	{
		mEmptyBar = LoadAttrImage(child, "empty");
		mFullBar = LoadAttrImage(child, "full");
	}

	// Load the placement
	LoadPlacement(FindNode(node, "placement"), &mRenderX, &mRenderY);

	// Load the data
	child = FindNode(node, "data");
	if (child)
	{
		mMinValVar = LoadAttrString(child, "min");
		mMaxValVar = LoadAttrString(child, "max");
		mCurValVar = LoadAttrString(child, "name");
	}

	mRenderW = mEmptyBar->GetWidth();
	mRenderH = mEmptyBar->GetHeight();
}
Example #4
0
GUIConsole::GUIConsole(xml_node<>* node) : GUIScrollList(node)
{
	xml_node<>* child;

	mLastCount = 0;
	scrollToEnd = true;
	mSlideoutX = mSlideoutY = mSlideoutW = mSlideoutH = 0;
	mSlideout = 0;
	mSlideoutState = visible;

	allowSelection = false;	// console doesn't support list item selections

	if (!node)
	{
		mRenderX = 0; mRenderY = 0; mRenderW = gr_fb_width(); mRenderH = gr_fb_height();
	}
	else
	{
		child = FindNode(node, "color");
		if (child)
		{
			mFontColor = LoadAttrColor(child, "foreground", mFontColor);
			mBackgroundColor = LoadAttrColor(child, "background", mBackgroundColor);
			//mScrollColor = LoadAttrColor(child, "scroll", mScrollColor);
		}

		child = FindNode(node, "slideout");
		if (child)
		{
			mSlideout = 1;
			mSlideoutState = hidden;
			LoadPlacement(child, &mSlideoutX, &mSlideoutY, &mSlideoutW, &mSlideoutH, &mPlacement);

			mSlideoutImage = LoadAttrImage(child, "resource");

			if (mSlideoutImage && mSlideoutImage->GetResource())
			{
				mSlideoutW = mSlideoutImage->GetWidth();
				mSlideoutH = mSlideoutImage->GetHeight();
				if (mPlacement == CENTER || mPlacement == CENTER_X_ONLY) {
					mSlideoutX = mSlideoutX - (mSlideoutW / 2);
					if (mPlacement == CENTER) {
						mSlideoutY = mSlideoutY - (mSlideoutH / 2);
					}
				}
			}
		}
	}
}
void GUIKeyboard::LoadKeyLabels(xml_node<>* parent, int layout)
{
	for (xml_node<>* child = parent->first_node(); child; child = child->next_sibling()) {
		std::string name = child->name();
		if (name == "keylabel") {
			std::string keydef = LoadAttrString(child, "key", "");
			Key tempkey;
			int dummyX;
			if (ParseKey(keydef.c_str(), tempkey, dummyX, 0, false) == 0) {
				KeyLabel keylabel;
				keylabel.key = tempkey.key;
				keylabel.layout_from = layout;
				keylabel.layout_to = tempkey.layout;
				keylabel.text = LoadAttrString(child, "text", "");
				keylabel.image = LoadAttrImage(child, "resource");
				mKeyLabels.push_back(keylabel);
			} else {
				LOGERR("Ignoring invalid keylabel in layout %d: '%s'.\n", layout, keydef.c_str());
			}
		}
	}
}
GUIButton::GUIButton(xml_node<>* node)
	: GUIObject(node)
{
	xml_attribute<>* attr;
	xml_node<>* child;

	mButtonImg = NULL;
	mButtonIcon = NULL;
	mButtonLabel = NULL;
	mAction = NULL;
	mRendered = false;
	hasHighlightColor = false;
	renderHighlight = false;
	hasFill = false;

	if (!node)  return;

	// These can be loaded directly from the node
	mButtonLabel = new GUIText(node);
	mAction = new GUIAction(node);

	mButtonImg = new GUIImage(node);
	if (mButtonImg->Render() < 0)
	{
		delete mButtonImg;
		mButtonImg = NULL;
	}
	if (mButtonLabel->Render() < 0)
	{
		delete mButtonLabel;
		mButtonLabel = NULL;
	}
	// Load fill if it exists
	mFillColor = LoadAttrColor(FindNode(node, "fill"), "color", &hasFill);
	if (!hasFill && mButtonImg == NULL) {
		LOGERR("No image resource or fill specified for button.\n");
	}

	// The icon is a special case
	mButtonIcon = LoadAttrImage(FindNode(node, "icon"), "resource");

	mHighlightColor = LoadAttrColor(FindNode(node, "highlight"), "color", &hasHighlightColor);

	int x, y, w, h;
	TextPlacement = TOP_LEFT;
	if (mButtonImg) {
		mButtonImg->GetRenderPos(x, y, w, h);
	} else if (hasFill) {
		LoadPlacement(FindNode(node, "placement"), &x, &y, &w, &h, &TextPlacement);
	}
	SetRenderPos(x, y, w, h);
	if (mButtonLabel) {
		TextPlacement = (Placement)LoadAttrInt(FindNode(node, "placement"), "textplacement", TOP_LEFT);
		if (TextPlacement != TEXT_ONLY_RIGHT) {
			mButtonLabel->scaleWidth = 1;
			mButtonLabel->SetMaxWidth(w);
			mButtonLabel->SetPlacement(CENTER);
			mTextX = ((mRenderW / 2) + mRenderX);
			mTextY = mRenderY + (mRenderH / 2);
			mButtonLabel->SetRenderPos(mTextX, mTextY);
		} else {
			mTextX = mRenderW + mRenderX + 5;
			mButtonLabel->GetCurrentBounds(mTextW, mTextH);
			mRenderW += mTextW + 5;
			mTextY = mRenderY + (mRenderH / 2) - (mTextH / 2);
			mButtonLabel->SetRenderPos(mTextX, mTextY);
			if (mAction)
				mAction->SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
			SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
		}
	}
}
Example #7
0
GUIInput::GUIInput(xml_node<>* node)
	: GUIObject(node)
{
	xml_attribute<>* attr;
	xml_node<>* child;

	mInputText = NULL;
	mAction = NULL;
	mBackground = NULL;
	mCursor = NULL;
	mFont = NULL;
	mRendered = false;
	HasMask = false;
	DrawCursor = false;
	isLocalChange = true;
	HasAllowed = false;
	HasDisabled = false;
	skipChars = scrollingX = mFontHeight = mFontY = lastX = 0;
	mBackgroundX = mBackgroundY = mBackgroundW = mBackgroundH = MinLen = MaxLen = 0;
	mCursorLocation = -1; // -1 is always the end of the string
	CursorWidth = 3;
	ConvertStrToColor("black", &mBackgroundColor);
	ConvertStrToColor("white", &mCursorColor);

	if (!node)
		return;

	// Load text directly from the node
	mInputText = new GUIText(node);
	// Load action directly from the node
	mAction = new GUIAction(node);

	if (mInputText->Render() < 0)
	{
		delete mInputText;
		mInputText = NULL;
	}

	// Load the background
	child = FindNode(node, "background");
	if (child)
	{
		mBackground = LoadAttrImage(child, "resource");
		mBackgroundColor = LoadAttrColor(child, "color", mBackgroundColor);
	}
	if (mBackground && mBackground->GetResource())
	{
		mBackgroundW = mBackground->GetWidth();
		mBackgroundH = mBackground->GetHeight();
	}

	// Load the cursor color
	child = FindNode(node, "cursor");
	if (child)
	{
		mCursor = LoadAttrImage(child, "resource");
		mCursorColor = LoadAttrColor(child, "color", mCursorColor);
		attr = child->first_attribute("hasfocus");
		if (attr)
		{
			std::string focus = attr->value();
			SetInputFocus(atoi(focus.c_str()));
		}
		CursorWidth = LoadAttrIntScaleX(child, "width", CursorWidth);
	}
	DrawCursor = HasInputFocus;

	// Load the font
	child = FindNode(node, "font");
	if (child)
	{
		mFont = LoadAttrFont(child, "resource");
		mFontHeight = mFont->GetHeight();
	}

	child = FindNode(node, "text");
	if (child)  mText = child->value();
	mLastValue = gui_parse_text(mText);

	child = FindNode(node, "data");
	if (child)
	{
		attr = child->first_attribute("name");
		if (attr)
			mVariable = attr->value();
		attr = child->first_attribute("default");
		if (attr)
			DataManager::SetValue(mVariable, attr->value());
		mMask = LoadAttrString(child, "mask");
		HasMask = !mMask.empty();
		attr = child->first_attribute("maskvariable");
		if (attr)
			mMaskVariable = attr->value();
		else
			mMaskVariable = mVariable;
	}

	// Load input restrictions
	child = FindNode(node, "restrict");
	if (child)
	{
		MinLen = LoadAttrInt(child, "minlen", MinLen);
		MaxLen = LoadAttrInt(child, "maxlen", MaxLen);
		AllowedList = LoadAttrString(child, "allow");
		HasAllowed = !AllowedList.empty();
		DisabledList = LoadAttrString(child, "disable");
		HasDisabled = !DisabledList.empty();
	}

	// Load the placement
	LoadPlacement(FindNode(node, "placement"), &mRenderX, &mRenderY, &mRenderW, &mRenderH);
	SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);

	if (mInputText && mFontHeight && mFontHeight < (unsigned)mRenderH) {
		mFontY = ((mRenderH - mFontHeight) / 2) + mRenderY;
		mInputText->SetRenderPos(mRenderX, mFontY);
	} else
		mFontY = mRenderY;

	if (mInputText)
		mInputText->SetMaxWidth(mRenderW);

	isLocalChange = false;
	HandleTextLocation(-3);
}
GUIKeyboard::GUIKeyboard(xml_node<>* node)
	: GUIObject(node)
{
	int layoutindex, rowindex, keyindex, Xindex, Yindex, keyHeight = 0, keyWidth = 0;
	currentKey = NULL;
	highlightRenderCount = 0;
	hasHighlight = hasCapsHighlight = false;
	char resource[10], layout[8], row[5], key[6], longpress[7];
	xml_attribute<>* attr;
	xml_node<>* child;
	xml_node<>* keylayout;
	xml_node<>* keyrow;

	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;

	if (!node)  return;

	mHighlightColor = LoadAttrColor(FindNode(node, "highlight"), "color", &hasHighlight);
	mCapsHighlightColor = LoadAttrColor(FindNode(node, "capshighlight"), "color", &hasCapsHighlight);

	child = FindNode(node, "keymargin");
	mKeyMarginX = LoadAttrIntScaleX(child, "x", 0);
	mKeyMarginY = LoadAttrIntScaleY(child, "y", 0);

	child = FindNode(node, "background");
	mBackgroundColor = LoadAttrColor(child, "color", COLOR(32,32,32,255));

	child = FindNode(node, "key-alphanumeric");
	mFont = PageManager::GetResources()->FindFont(LoadAttrString(child, "font", "keylabel"));
	mFontColor = LoadAttrColor(child, "textcolor", COLOR(255,255,255,255));
	mKeyColorAlphanumeric = LoadAttrColor(child, "color", COLOR(0,0,0,0));

	child = FindNode(node, "key-other");
	mSmallFont = PageManager::GetResources()->FindFont(LoadAttrString(child, "font", "keylabel-small"));
	mFontColorSmall = LoadAttrColor(child, "textcolor", COLOR(192,192,192,255));
	mKeyColorOther = LoadAttrColor(child, "color", COLOR(0,0,0,0));

	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

	// compatibility ugliness: resources should be specified in the layouts themselves instead
	// Load the images for the different layouts
	child = FindNode(node, "layout");
	if (child)
	{
		layoutindex = 1;
		strcpy(resource, "resource1");
		attr = child->first_attribute(resource);
		while (attr && layoutindex < (MAX_KEYBOARD_LAYOUTS + 1)) {
			layouts[layoutindex - 1].keyboardImg = LoadAttrImage(child, resource);

			layoutindex++;
			resource[8] = (char)(layoutindex + 48);
			attr = child->first_attribute(resource);
		}
	}

	// Check the first image to get height and width
	if (layouts[0].keyboardImg && layouts[0].keyboardImg->GetResource())
	{
		mRenderW = layouts[0].keyboardImg->GetWidth();
		mRenderH = layouts[0].keyboardImg->GetHeight();
	}

	// Load all of the layout maps
	layoutindex = 1;
	strcpy(layout, "layout1");
	keylayout = FindNode(node, layout);
	while (keylayout)
	{
		if (layoutindex > MAX_KEYBOARD_LAYOUTS) {
			LOGERR("Too many layouts defined in keyboard.\n");
			return;
		}

		LoadKeyLabels(keylayout, layoutindex); // load per-layout key labels

		Layout& lay = layouts[layoutindex - 1];

		child = keylayout->first_node("keysize");
		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;
		strcpy(row, "row1");
		keyrow = keylayout->first_node(row);
		while (keyrow)
		{
			if (rowindex > MAX_KEYBOARD_ROWS) {
				LOGERR("Too many rows defined in keyboard.\n");
				return;
			}

			Yindex += keyHeight;
			lay.row_end_y[rowindex - 1] = Yindex;

			keyindex = 1;
			Xindex = 0;
			strcpy(key, "key01");
			attr = keyrow->first_attribute(key);

			while (attr) {
				if (keyindex > MAX_KEYBOARD_KEYS) {
					LOGERR("Too many keys defined in a keyboard row.\n");
					return;
				}

				const char* keyinfo = attr->value();

				if (strlen(keyinfo) == 0) {
					LOGERR("No key info on layout%i, row%i, key%dd.\n", layoutindex, rowindex, keyindex);
					return;
				}

				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);


				// PROCESS LONG PRESS INFO IF EXISTS
				sprintf(longpress, "long%02i", keyindex);
				attr = keyrow->first_attribute(longpress);
				if (attr) {
					const char* keyinfo = attr->value();

					if (strlen(keyinfo) == 0) {
						LOGERR("No long press info on layout%i, row%i, long%dd.\n", layoutindex, rowindex, keyindex);
						return;
					}

					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++;
				sprintf(key, "key%02i", keyindex);
				attr = keyrow->first_attribute(key);
			}
			rowindex++;
			row[3] = (char)(rowindex + 48);
			keyrow = keylayout->first_node(row);
		}
		layoutindex++;
		layout[6] = (char)(layoutindex + 48);
		keylayout = FindNode(node, layout);
	}

	int x, y;
	// Load the placement
	LoadPlacement(FindNode(node, "placement"), &x, &y, &mRenderW, &mRenderH);
	SetRenderPos(x, y, mRenderW, mRenderH);
	return;
}
GUISlider::GUISlider(xml_node<>* node) : GUIObject(node)
{
	xml_attribute<>* attr;
	xml_node<>* child;

	sAction = NULL;
	sSliderLabel = NULL;
	sSlider = NULL;
	sSliderUsed = NULL;
	sTouch = NULL;
	sTouchW = 20;

	if (!node)
	{
		LOGERR("GUISlider created without XML node\n");
		return;
	}

	// Load the resources
	child = FindNode(node, "resource");
	if (child)
	{
		sSlider = LoadAttrImage(child, "base");
		sSliderUsed = LoadAttrImage(child, "used");
		sTouch = LoadAttrImage(child, "touch");
	}

	// Load the text label
	sSliderLabel = new GUIText(node);
	if (sSliderLabel->Render() < 0)
	{
		delete sSliderLabel;
		sSliderLabel = NULL;
	}

	// Load the placement
	Placement TextPlacement = CENTER;
	LoadPlacement(FindNode(node, "placement"), &mRenderX, &mRenderY, &mRenderW, &mRenderH, &TextPlacement);

	mRenderW = sSlider->GetWidth();
	mRenderH = sSlider->GetHeight();
	if (TextPlacement == CENTER || TextPlacement == CENTER_X_ONLY) {
		mRenderX = mRenderX - (mRenderW / 2);
		if (TextPlacement == CENTER) {
			mRenderY = mRenderY - (mRenderH / 2);
		}
	}
	if (sSliderLabel) {
		int sTextX = mRenderX + (mRenderW / 2);
		int w, h;
		sSliderLabel->GetCurrentBounds(w, h);
		int sTextY = mRenderY + ((mRenderH - h) / 2);
		sSliderLabel->SetRenderPos(sTextX, sTextY);
		sSliderLabel->SetMaxWidth(mRenderW);
	}
	if (sTouch && sTouch->GetResource())
	{
		sTouchW = sTouch->GetWidth();  // Width of the "touch image" that follows the touch (arrow)
		sTouchH = sTouch->GetHeight(); // Height of the "touch image" that follows the touch (arrow)
	}

	//LOGINFO("mRenderW: %i mTouchW: %i\n", mRenderW, mTouchW);
	mActionX = mRenderX;
	mActionY = mRenderY;
	mActionW = mRenderW;
	mActionH = mRenderH;

	sAction = new GUIAction(node);

	sCurTouchX = mRenderX;
	sUpdate = 1;
}
Example #10
0
GUIKeyboard::GUIKeyboard(xml_node<>* node)
	: GUIObject(node)
{
	int layoutindex, rowindex, keyindex, Xindex, Yindex, keyHeight = 0, keyWidth = 0;
	rowY = colX = -1;
	highlightRenderCount = 0;
	hasHighlight = hasCapsHighlight = false;
	char resource[10], layout[8], row[5], key[6], longpress[7];
	xml_attribute<>* attr;
	xml_node<>* child;
	xml_node<>* keylayout;
	xml_node<>* keyrow;

	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;

	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)
	{
		layoutindex = 1;
		strcpy(resource, "resource1");
		attr = child->first_attribute(resource);
		while (attr && layoutindex < (MAX_KEYBOARD_LAYOUTS + 1)) {
			layouts[layoutindex - 1].keyboardImg = LoadAttrImage(child, resource);

			layoutindex++;
			resource[8] = (char)(layoutindex + 48);
			attr = child->first_attribute(resource);
		}
	}

	// Check the first image to get height and width
	if (layouts[0].keyboardImg && layouts[0].keyboardImg->GetResource())
	{
		KeyboardWidth = layouts[0].keyboardImg->GetWidth();
		KeyboardHeight = layouts[0].keyboardImg->GetHeight();
	}

	// Load all of the layout maps
	layoutindex = 1;
	strcpy(layout, "layout1");
	keylayout = FindNode(node, layout);
	while (keylayout)
	{
		if (layoutindex > MAX_KEYBOARD_LAYOUTS) {
			LOGERR("Too many layouts defined in keyboard.\n");
			return;
		}

		Layout& lay = layouts[layoutindex - 1];

		child = keylayout->first_node("keysize");
		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;
		strcpy(row, "row1");
		keyrow = keylayout->first_node(row);
		while (keyrow)
		{
			if (rowindex > MAX_KEYBOARD_ROWS) {
				LOGERR("Too many rows defined in keyboard.\n");
				return;
			}

			Yindex += keyHeight;
			lay.row_end_y[rowindex - 1] = Yindex;

			keyindex = 1;
			Xindex = 0;
			strcpy(key, "key01");
			attr = keyrow->first_attribute(key);

			while (attr) {
				if (keyindex > MAX_KEYBOARD_KEYS) {
					LOGERR("Too many keys defined in a keyboard row.\n");
					return;
				}

				const char* keyinfo = attr->value();

				if (strlen(keyinfo) == 0) {
					LOGERR("No key info on layout%i, row%i, key%dd.\n", layoutindex, rowindex, keyindex);
					return;
				}

				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);


				// PROCESS LONG PRESS INFO IF EXISTS
				sprintf(longpress, "long%02i", keyindex);
				attr = keyrow->first_attribute(longpress);
				if (attr) {
					const char* keyinfo = attr->value();

					if (strlen(keyinfo) == 0) {
						LOGERR("No long press info on layout%i, row%i, long%dd.\n", layoutindex, rowindex, keyindex);
						return;
					}

					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++;
				sprintf(key, "key%02i", keyindex);
				attr = keyrow->first_attribute(key);
			}
			rowindex++;
			row[3] = (char)(rowindex + 48);
			keyrow = keylayout->first_node(row);
		}
		layoutindex++;
		layout[6] = (char)(layoutindex + 48);
		keylayout = FindNode(node, layout);
	}

	int x, y;
	// Load the placement
	LoadPlacement(FindNode(node, "placement"), &x, &y);
	SetRenderPos(x, y, KeyboardWidth, KeyboardHeight);
	return;
}