Add UI for entering pattern for device decryption

Change-Id: Ia2d3268a96423e9ca3846500c57e674c4f8fa60b
Signed-off-by: Vojtech Bocek <vbocek@gmail.com>
diff --git a/gui/Android.mk b/gui/Android.mk
index afaa5da..e5e5865 100644
--- a/gui/Android.mk
+++ b/gui/Android.mk
@@ -26,7 +26,8 @@
     blanktimer.cpp \
     partitionlist.cpp \
     mousecursor.cpp \
-    scrolllist.cpp
+    scrolllist.cpp \
+    patternpassword.cpp
 
 ifneq ($(TWRP_CUSTOM_KEYBOARD),)
     LOCAL_SRC_FILES += $(TWRP_CUSTOM_KEYBOARD)
diff --git a/gui/devices/1080x1920/res/ui.xml b/gui/devices/1080x1920/res/ui.xml
index 4661583..3135a03 100644
--- a/gui/devices/1080x1920/res/ui.xml
+++ b/gui/devices/1080x1920/res/ui.xml
@@ -199,6 +199,14 @@
 		<variable name="slidervalue_padding" value="30" />
 		<variable name="slidervalue_sliderw" value="15" />
 		<variable name="slidervalue_sliderh" value="90" />
+		<variable name="pattern_x" value="216" />
+		<variable name="pattern_y" value="508" />
+		<variable name="pattern_width" value="648" />
+		<variable name="pattern_dot_color" value="#33B5E5" />
+		<variable name="pattern_dot_color_active" value="#FFFFFF" />
+		<variable name="pattern_dot_radius" value="23" />
+		<variable name="pattern_line_color" value="#33B5E5" />
+		<variable name="pattern_line_width" value="18" />
 	</variables>
 
 	<mousecursor>
diff --git a/gui/devices/1920x1200/res/ui.xml b/gui/devices/1920x1200/res/ui.xml
index e8290ef..986c026 100644
--- a/gui/devices/1920x1200/res/ui.xml
+++ b/gui/devices/1920x1200/res/ui.xml
@@ -213,6 +213,14 @@
 		<variable name="slidervalue_padding" value="0" />
 		<variable name="slidervalue_sliderw" value="20" />
 		<variable name="slidervalue_sliderh" value="80" />
+		<variable name="pattern_x" value="480" />
+		<variable name="pattern_y" value="240" />
+		<variable name="pattern_width" value="720" />
+		<variable name="pattern_dot_color" value="#33B5E5" />
+		<variable name="pattern_dot_color_active" value="#FFFFFF" />
+		<variable name="pattern_dot_radius" value="23" />
+		<variable name="pattern_line_color" value="#33B5E5" />
+		<variable name="pattern_line_width" value="18" />
 	</variables>
 
 	<mousecursor>
diff --git a/gui/devices/320x320/res/ui.xml b/gui/devices/320x320/res/ui.xml
index 317e632..5495b77 100644
--- a/gui/devices/320x320/res/ui.xml
+++ b/gui/devices/320x320/res/ui.xml
@@ -202,6 +202,14 @@
 		<variable name="slidervalue_sliderh" value="20" />
 		<variable name="wipe_button_row1" value="240" />
 		<variable name="invalid_partition_y" value="218" />
+		<variable name="pattern_x" value="70" />
+		<variable name="pattern_y" value="22" />
+		<variable name="pattern_width" value="180" />
+		<variable name="pattern_dot_color" value="#33B5E5" />
+		<variable name="pattern_dot_color_active" value="#FFFFFF" />
+		<variable name="pattern_dot_radius" value="7" />
+		<variable name="pattern_line_color" value="#33B5E5" />
+		<variable name="pattern_line_width" value="4" />
 	</variables>
 
 	<templates>
diff --git a/gui/devices/480x800/res/ui.xml b/gui/devices/480x800/res/ui.xml
index 72183fe..3c320a7 100644
--- a/gui/devices/480x800/res/ui.xml
+++ b/gui/devices/480x800/res/ui.xml
@@ -194,6 +194,14 @@
 		<variable name="slidervalue_padding" value="13" />
 		<variable name="slidervalue_sliderw" value="7" />
 		<variable name="slidervalue_sliderh" value="40" />
+		<variable name="pattern_x" value="90" />
+		<variable name="pattern_y" value="250" />
+		<variable name="pattern_width" value="300" />
+		<variable name="pattern_dot_color" value="#33B5E5" />
+		<variable name="pattern_dot_color_active" value="#FFFFFF" />
+		<variable name="pattern_dot_radius" value="12" />
+		<variable name="pattern_line_color" value="#33B5E5" />
+		<variable name="pattern_line_width" value="9" />
 	</variables>
 
 	<mousecursor>
diff --git a/gui/devices/800x480/res/ui.xml b/gui/devices/800x480/res/ui.xml
index f35d1d4..12e0ee7 100644
--- a/gui/devices/800x480/res/ui.xml
+++ b/gui/devices/800x480/res/ui.xml
@@ -214,6 +214,14 @@
 		<variable name="slidervalue_padding" value="0" />
 		<variable name="slidervalue_sliderw" value="7" />
 		<variable name="slidervalue_sliderh" value="40" />
+		<variable name="pattern_x" value="200" />
+		<variable name="pattern_y" value="110" />
+		<variable name="pattern_width" value="300" />
+		<variable name="pattern_dot_color" value="#33B5E5" />
+		<variable name="pattern_dot_color_active" value="#FFFFFF" />
+		<variable name="pattern_dot_radius" value="10" />
+		<variable name="pattern_line_color" value="#33B5E5" />
+		<variable name="pattern_line_width" value="7" />
 	</variables>
 
 	<mousecursor>
diff --git a/gui/devices/landscape/res/landscape.xml b/gui/devices/landscape/res/landscape.xml
index 0a907cb..432e9fb 100644
--- a/gui/devices/landscape/res/landscape.xml
+++ b/gui/devices/landscape/res/landscape.xml
@@ -3241,6 +3241,11 @@
 		<page name="decrypt">
 			<object type="template" name="header" />
 
+			<object type="action">
+				<condition var1="tw_crypto_pwtype" var2="2" />
+				<action function="page">decrypt_pattern</action>
+			</object>
+
 			<object type="text">
 				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
 				<text>Please Enter Your Password</text>
@@ -3277,6 +3282,41 @@
 			<object type="template" name="footer" />
 		</page>
 
+		<page name="decrypt_pattern">
+			<object type="template" name="header" />
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Please Enter Your Pattern</text>
+			</object>
+
+			<object type="text">
+				<condition var1="tw_password_fail" var2="1" />
+				<font resource="font" color="%text_fail_color%" />
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Pattern Failed, Please Try Again</text>
+			</object>
+
+			<object type="patternpassword">
+				<placement x="%pattern_x%" y="%pattern_y%" w="%pattern_width%" h="%pattern_width%" />
+				<dot color="%pattern_dot_color%" activecolor="%pattern_dot_color_active%" radius="%pattern_dot_radius%" />
+				<line color="%pattern_line_color%" width="%pattern_line_width%" />
+				<data name="tw_crypto_password"/>
+				<action function="page">trydecrypt</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col4_x%" y="%cancel_button_y%" />
+				<text>Cancel</text>
+				<actions>
+					<action function="set">tw_page_done=1</action>
+					<action function="page">main</action>
+				</actions>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
 		<page name="trydecrypt">
 			<object type="template" name="header" />
 
diff --git a/gui/devices/portrait/res/portrait.xml b/gui/devices/portrait/res/portrait.xml
index 02e3a5a..76a6dc7 100644
--- a/gui/devices/portrait/res/portrait.xml
+++ b/gui/devices/portrait/res/portrait.xml
@@ -3268,6 +3268,11 @@
 		<page name="decrypt">
 			<object type="template" name="header" />
 
+			<object type="action">
+				<condition var1="tw_crypto_pwtype" var2="2" />
+				<action function="page">decrypt_pattern</action>
+			</object>
+
 			<object type="text">
 				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
 				<text>Please Enter Your Password</text>
@@ -3304,6 +3309,41 @@
 			<object type="template" name="footer" />
 		</page>
 
+		<page name="decrypt_pattern">
+			<object type="template" name="header" />
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Please Enter Your Pattern</text>
+			</object>
+
+			<object type="text">
+				<condition var1="tw_password_fail" var2="1" />
+				<font resource="font" color="%text_fail_color%"/>
+				<placement x="%center_x%" y="%row2_text_y%" placement="5" />
+				<text>Pattern Failed, Please Try Again</text>
+			</object>
+
+			<object type="patternpassword">
+				<placement x="%pattern_x%" y="%pattern_y%" w="%pattern_width%" h="%pattern_width%" />
+				<dot color="%pattern_dot_color%" activecolor="%pattern_dot_color_active%" radius="%pattern_dot_radius%" />
+				<line color="%pattern_line_color%" width="%pattern_line_width%" />
+				<data name="tw_crypto_password"/>
+				<action function="page">trydecrypt</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col_center_x%" y="%row4_y%" />
+				<text>Cancel</text>
+				<actions>
+					<action function="set">tw_page_done=1</action>
+					<action function="page">main</action>
+				</actions>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
 		<page name="trydecrypt">
 			<object type="template" name="header" />
 
diff --git a/gui/devices/watch/res/watch.xml b/gui/devices/watch/res/watch.xml
index c40e981..d6a3fa8 100644
--- a/gui/devices/watch/res/watch.xml
+++ b/gui/devices/watch/res/watch.xml
@@ -3261,6 +3261,11 @@
 		<page name="decrypt">
 			<object type="template" name="header" />
 
+			<object type="action">
+				<condition var1="tw_crypto_pwtype" var2="2" />
+				<action function="page">decrypt_pattern</action>
+			</object>
+
 			<object type="text">
 				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
 				<text>Please Enter Your Password</text>
@@ -3297,6 +3302,41 @@
 			<object type="template" name="footer" />
 		</page>
 
+		<page name="decrypt_pattern">
+			<object type="template" name="header" />
+
+			<object type="text">
+				<placement x="%center_x%" y="%row1_header_y%" placement="5" />
+				<text>Please Enter Your Pattern</text>
+			</object>
+
+			<object type="text">
+				<condition var1="tw_password_fail" var2="1" />
+				<font resource="font" color="%text_fail_color%"/>
+				<placement x="%center_x%" y="%row1_text_y%" placement="5" />
+				<text>Pattern Failed, Please Try Again</text>
+			</object>
+
+			<object type="patternpassword">
+				<placement x="%pattern_x%" y="%pattern_y%" w="%pattern_width%" h="%pattern_width%" />
+				<dot color="%pattern_dot_color%" activecolor="%pattern_dot_color_active%" radius="%pattern_dot_radius%" />
+				<line color="%pattern_line_color%" width="%pattern_line_width%" />
+				<data name="tw_crypto_password"/>
+				<action function="page">trydecrypt</action>
+			</object>
+
+			<object type="button">
+				<placement x="%col_center_x%" y="%row4_y%" />
+				<text>Cancel</text>
+				<actions>
+					<action function="set">tw_page_done=1</action>
+					<action function="page">main</action>
+				</actions>
+			</object>
+
+			<object type="template" name="footer" />
+		</page>
+
 		<page name="trydecrypt">
 			<object type="template" name="header" />
 
diff --git a/gui/objects.hpp b/gui/objects.hpp
index 2611411..d26b0c2 100644
--- a/gui/objects.hpp
+++ b/gui/objects.hpp
@@ -1058,6 +1058,58 @@
 	bool m_present;
 };
 
+class GUIPatternPassword : public GUIObject, public RenderObject, public ActionObject
+{
+public:
+	GUIPatternPassword(xml_node<>* node);
+	virtual ~GUIPatternPassword();
+
+public:
+	virtual int Render(void);
+	virtual int Update(void);
+	virtual int NotifyTouch(TOUCH_STATE state, int x, int y);
+	virtual int SetRenderPos(int x, int y, int w = 0, int h = 0);
+
+protected:
+	void CalculateDotPositions();
+	void ResetActiveDots();
+	void ConnectDot(int dot_idx);
+	void ConnectIntermediateDots(int dot_idx);
+	int InDot(int x, int y);
+	bool DotUsed(int dot_idx);
+	static bool IsInRect(int x, int y, int rx, int ry, int rw, int rh);
+	void PatternDrawn();
+
+	struct Dot {
+		int x;
+		int y;
+		bool active;
+	};
+
+	Dot mDots[9];
+	int mConnectedDots[9];
+	size_t mConnectedDotsLen;
+	int mCurLineX;
+	int mCurLineY;
+	bool mTrackingTouch;
+	bool mNeedRender;
+
+	COLOR mDotColor;
+	COLOR mActiveDotColor;
+	COLOR mLineColor;
+	ImageResource *mDotImage;
+	ImageResource *mActiveDotImage;
+	gr_surface mDotCircle;
+	gr_surface mActiveDotCircle;
+	int mDotRadius;
+	int mLineWidth;
+
+	std::string mPassVar;
+	GUIAction *mAction;
+	int mUpdate;
+};
+
+
 // Helper APIs
 xml_node<>* FindNode(xml_node<>* parent, const char* nodename, int depth = 0);
 std::string LoadAttrString(xml_node<>* element, const char* attrname, const char* defaultvalue = "");
diff --git a/gui/pages.cpp b/gui/pages.cpp
index cd5e340..23cfcd8 100644
--- a/gui/pages.cpp
+++ b/gui/pages.cpp
@@ -445,6 +445,13 @@
 			mRenders.push_back(element);
 			mActions.push_back(element);
 		}
+		else if (type == "patternpassword")
+		{
+			GUIPatternPassword* element = new GUIPatternPassword(child);
+			mObjects.push_back(element);
+			mRenders.push_back(element);
+			mActions.push_back(element);
+		}
 		else if (type == "template")
 		{
 			if (!templates || !child->first_attribute("name"))
diff --git a/gui/patternpassword.cpp b/gui/patternpassword.cpp
new file mode 100644
index 0000000..4fc7812
--- /dev/null
+++ b/gui/patternpassword.cpp
@@ -0,0 +1,313 @@
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <string>
+
+extern "C" {
+#include "../twcommon.h"
+#include "../minuitwrp/minui.h"
+}
+
+#include "rapidxml.hpp"
+#include "objects.hpp"
+
+GUIPatternPassword::GUIPatternPassword(xml_node<>* node)
+	: GUIObject(node)
+{
+	xml_attribute<>* attr;
+	xml_node<>* child;
+
+	ResetActiveDots();
+	mTrackingTouch = false;
+	mNeedRender = true;
+
+	ConvertStrToColor("blue", &mDotColor);
+	ConvertStrToColor("white", &mActiveDotColor);
+	ConvertStrToColor("blue", &mLineColor);
+
+	mDotImage = mActiveDotImage = NULL;
+	mDotCircle = mActiveDotCircle = NULL;
+	mDotRadius = 50;
+	mLineWidth = 35;
+
+	mAction = NULL;
+	mUpdate = 0;
+
+	if (!node)
+		return;
+
+	LoadPlacement(FindNode(node, "placement"), &mRenderX, &mRenderY, &mRenderW, &mRenderH, &mPlacement);
+
+	mAction = new GUIAction(node);
+
+	child = FindNode(node, "dot");
+	if(child)
+	{
+		mDotColor = LoadAttrColor(child, "color", mDotColor);
+		mActiveDotColor = LoadAttrColor(child, "activecolor", mActiveDotColor);
+		mDotRadius = LoadAttrIntScaleX(child, "radius", mDotRadius);
+
+		mDotImage = LoadAttrImage(child, "image");
+		mActiveDotImage = LoadAttrImage(child, "activeimage");
+	}
+
+	child = FindNode(node, "line");
+	if(child)
+	{
+		mLineColor = LoadAttrColor(child, "color", mLineColor);
+		mLineWidth = LoadAttrIntScaleX(child, "width", mLineWidth);
+	}
+
+	child = FindNode(node, "data");
+	if(child)
+		mPassVar = LoadAttrString(child, "name", "");
+
+
+	if(!mDotImage || !mDotImage->GetResource() || !mActiveDotImage || !mActiveDotImage->GetResource())
+	{
+		mDotCircle = gr_render_circle(mDotRadius, mDotColor.red, mDotColor.green, mDotColor.blue, mDotColor.alpha);
+		mActiveDotCircle = gr_render_circle(mDotRadius/2, mActiveDotColor.red, mActiveDotColor.green, mActiveDotColor.blue, mActiveDotColor.alpha);
+	}
+	else
+		mDotRadius = mDotImage->GetWidth()/2;
+
+	SetRenderPos(mRenderX, mRenderY, mRenderW, mRenderH);
+}
+
+GUIPatternPassword::~GUIPatternPassword()
+{
+	delete mDotImage;
+	delete mActiveDotImage;
+	delete mAction;
+
+	if(mDotCircle)
+		gr_free_surface(mDotCircle);
+
+	if(mActiveDotCircle)
+		gr_free_surface(mActiveDotCircle);
+}
+
+void GUIPatternPassword::ResetActiveDots()
+{
+	mConnectedDotsLen = 0;
+	mCurLineX = mCurLineY = -1;
+	for(int i = 0; i < 9; ++i)
+		mDots[i].active = false;
+}
+
+int GUIPatternPassword::SetRenderPos(int x, int y, int w, int h)
+{
+	mRenderX = x;
+	mRenderY = y;
+
+	if (w || h)
+	{
+		mRenderW = w;
+		mRenderH = h;
+
+		mAction->SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
+		SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
+	}
+
+	CalculateDotPositions();
+	return 0;
+}
+
+void GUIPatternPassword::CalculateDotPositions(void)
+{
+	const int step_x = (mRenderW - mDotRadius*2) / 2;
+	const int step_y = (mRenderH - mDotRadius*2) / 2;
+	int x = mRenderX;
+	int y = mRenderY;
+
+	for(int r = 0; r < 3; ++r)
+	{
+		for(int c = 0; c < 3; ++c)
+		{
+			mDots[3*r+c].x = x;
+			mDots[3*r+c].y = y;
+			x += step_x;
+		}
+		x = mRenderX;
+		y += step_y;
+	}
+}
+
+int GUIPatternPassword::Render(void)
+{
+	if(!isConditionTrue())
+		return 0;
+
+	gr_color(mLineColor.red, mLineColor.green, mLineColor.blue, mLineColor.alpha);
+	for(size_t i = 1; i < mConnectedDotsLen; ++i) {
+		const Dot& dp = mDots[mConnectedDots[i-1]];
+		const Dot& dc = mDots[mConnectedDots[i]];
+		gr_line(dp.x + mDotRadius, dp.y + mDotRadius, dc.x + mDotRadius, dc.y + mDotRadius, mLineWidth);
+	}
+
+	if(mConnectedDotsLen > 0 && mTrackingTouch) {
+		const Dot& dc = mDots[mConnectedDots[mConnectedDotsLen-1]];
+		gr_line(dc.x + mDotRadius, dc.y + mDotRadius, mCurLineX, mCurLineY, mLineWidth);
+	}
+
+	for(int i = 0; i < 9; ++i) {
+		if(mDotCircle) {
+			gr_blit(mDotCircle, 0, 0, gr_get_width(mDotCircle), gr_get_height(mDotCircle), mDots[i].x, mDots[i].y);
+			if(mDots[i].active) {
+				gr_blit(mActiveDotCircle, 0, 0, gr_get_width(mActiveDotCircle), gr_get_height(mActiveDotCircle), mDots[i].x + mDotRadius/2, mDots[i].y + mDotRadius/2);
+			}
+		} else {
+			if(mDots[i].active) {
+				gr_blit(mActiveDotImage->GetResource(), 0, 0, mActiveDotImage->GetWidth(), mActiveDotImage->GetHeight(),
+						mDots[i].x + (mDotRadius - mActiveDotImage->GetWidth()/2), mDots[i].y + (mDotRadius - mActiveDotImage->GetHeight()/2));
+			} else {
+				gr_blit(mDotImage->GetResource(), 0, 0, mDotImage->GetWidth(), mDotImage->GetHeight(), mDots[i].x, mDots[i].y);
+			}
+		}
+	}
+	return 0;
+}
+
+int GUIPatternPassword::Update(void)
+{
+	if(!isConditionTrue())
+		return 0;
+
+	int res = mNeedRender ? 2 : 1;
+	mNeedRender = false;
+	return res;
+}
+
+bool GUIPatternPassword::IsInRect(int x, int y, int rx, int ry, int rw, int rh)
+{
+	return x >= rx && y >= ry && x <= rx+rw && y <= ry+rh;
+}
+
+int GUIPatternPassword::InDot(int x, int y)
+{
+	for(int i = 0; i < 9; ++i) {
+		if(IsInRect(x, y, mDots[i].x - mDotRadius*1.5, mDots[i].y - mDotRadius*1.5, mDotRadius*6, mDotRadius*6))
+			return i;
+	}
+	return -1;
+}
+
+bool GUIPatternPassword::DotUsed(int dot_idx)
+{
+	for(size_t i = 0; i < mConnectedDotsLen; ++i) {
+		if(mConnectedDots[i] == dot_idx)
+			return true;
+	}
+	return false;
+}
+
+void GUIPatternPassword::ConnectDot(int dot_idx)
+{
+	if(mConnectedDotsLen >= 9)
+	{
+		LOGERR("mConnectedDots in GUIPatternPassword has overflown!\n");
+		return;
+	}
+
+	mConnectedDots[mConnectedDotsLen++] = dot_idx;
+	mDots[dot_idx].active = true;
+}
+
+void GUIPatternPassword::ConnectIntermediateDots(int dot_idx)
+{
+	if(mConnectedDotsLen == 0)
+		return;
+
+	const int last_dot = mConnectedDots[mConnectedDotsLen-1];
+
+	// The line is vertical and has crossed a point in the middle
+	if(dot_idx%3 == last_dot%3 && abs(dot_idx - last_dot) > 3)
+		ConnectDot(3 + dot_idx%3);
+	// the line is horizontal and has crossed a point in the middle
+	else if(dot_idx/3 == last_dot/3 && abs(dot_idx - last_dot) > 1)
+		ConnectDot((dot_idx/3)*3 + 1);
+	// the line is diagonal and has crossed the middle point
+	else if((dot_idx == 0 && last_dot == 8) || (dot_idx == 8 && last_dot == 0) ||
+			(dot_idx == 2 && last_dot == 6) || (dot_idx == 6 && last_dot == 2))
+	{
+		ConnectDot(4);
+	}
+}
+
+int GUIPatternPassword::NotifyTouch(TOUCH_STATE state, int x, int y)
+{
+	if(!isConditionTrue())
+		return -1;
+
+	switch (state)
+	{
+		case TOUCH_START:
+		{
+			const int dot_idx = InDot(x, y);
+			if(dot_idx == -1)
+				break;
+
+			mTrackingTouch = true;
+			ResetActiveDots();
+			ConnectDot(dot_idx);
+			DataManager::Vibrate("tw_button_vibrate");
+			mCurLineX = x;
+			mCurLineY = y;
+			mNeedRender = true;
+			break;
+		}
+		case TOUCH_DRAG:
+		{
+			if(!mTrackingTouch)
+				break;
+
+			const int dot_idx = InDot(x, y);
+			if(dot_idx != -1 && !DotUsed(dot_idx))
+			{
+				ConnectIntermediateDots(dot_idx);
+				ConnectDot(dot_idx);
+				DataManager::Vibrate("tw_button_vibrate");
+			}
+
+			mCurLineX = x;
+			mCurLineY = y;
+			mNeedRender = true;
+			break;
+		}
+		case TOUCH_RELEASE:
+		{
+			if(!mTrackingTouch)
+				break;
+
+			mNeedRender = true;
+			mTrackingTouch = false;
+			PatternDrawn();
+			ResetActiveDots();
+			break;
+		}
+		default:
+			break;
+	}
+	return 0;
+}
+
+void GUIPatternPassword::PatternDrawn()
+{
+	if(!mPassVar.empty() && mConnectedDotsLen > 0)
+	{
+		std::string pass;
+		for(size_t i = 0; i < mConnectedDotsLen; ++i)
+			pass += '1' + mConnectedDots[i];
+		DataManager::SetValue(mPassVar, pass);
+	}
+
+	if(mAction)
+		mAction->doActions();
+}