Permalink
Browse files

Merge pull request #3088 from Montellese/gui_input_validation

CGUIEditControl: add input validation
  • Loading branch information...
Montellese committed Sep 9, 2013
2 parents 51ffc37 + 2dc6a07 commit 95bfa124617f661444fa5a1fe898ca82e07c8b62
@@ -536,6 +536,9 @@
DFD882F617DD1A5B001516FE /* AddonPythonInvoker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFD882F417DD1A5B001516FE /* AddonPythonInvoker.cpp */; };
DFD882F717DD1A5B001516FE /* AddonPythonInvoker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFD882F417DD1A5B001516FE /* AddonPythonInvoker.cpp */; };
DFD882F817DD1A5B001516FE /* AddonPythonInvoker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFD882F417DD1A5B001516FE /* AddonPythonInvoker.cpp */; };
+ DFD882E717DD189E001516FE /* StringValidation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFD882E517DD189E001516FE /* StringValidation.cpp */; };
+ DFD882E817DD189E001516FE /* StringValidation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFD882E517DD189E001516FE /* StringValidation.cpp */; };
+ DFD882E917DD189E001516FE /* StringValidation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFD882E517DD189E001516FE /* StringValidation.cpp */; };
DFD928F316384B6800709DAE /* Timer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFD928F116384B6800709DAE /* Timer.cpp */; };
DFDA3153160E34230047A626 /* DVDOverlayCodec.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFDA3152160E34230047A626 /* DVDOverlayCodec.cpp */; };
DFE4095B17417FDF00473BD9 /* LegacyPathTranslation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DFE4095917417FDF00473BD9 /* LegacyPathTranslation.cpp */; };
@@ -4240,6 +4243,8 @@
DFD5812416C828500008EEA0 /* DAVFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DAVFile.h; sourceTree = "<group>"; };
DFD882F417DD1A5B001516FE /* AddonPythonInvoker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AddonPythonInvoker.cpp; path = python/AddonPythonInvoker.cpp; sourceTree = "<group>"; };
DFD882F517DD1A5B001516FE /* AddonPythonInvoker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AddonPythonInvoker.h; path = python/AddonPythonInvoker.h; sourceTree = "<group>"; };
+ DFD882E517DD189E001516FE /* StringValidation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StringValidation.cpp; sourceTree = "<group>"; };
+ DFD882E617DD189E001516FE /* StringValidation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StringValidation.h; sourceTree = "<group>"; };
DFD928F116384B6800709DAE /* Timer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Timer.cpp; sourceTree = "<group>"; };
DFD928F216384B6800709DAE /* Timer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Timer.h; sourceTree = "<group>"; };
DFDA3152160E34230047A626 /* DVDOverlayCodec.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DVDOverlayCodec.cpp; sourceTree = "<group>"; };
@@ -8675,6 +8680,8 @@
18ECC96113CF178D00A9ED6C /* StreamUtils.h */,
18B7C8F11294261F009E7A26 /* StringUtils.cpp */,
18B7C8F21294261F009E7A26 /* StringUtils.h */,
+ DFD882E517DD189E001516FE /* StringValidation.cpp */,
+ DFD882E617DD189E001516FE /* StringValidation.h */,
E38E1E830D25F9FD00618676 /* SystemInfo.cpp */,
E38E1E840D25F9FD00618676 /* SystemInfo.h */,
C848291D156D003E005A996F /* TextSearch.cpp */,
@@ -10608,6 +10615,7 @@
DF28DF4D17B8379E0077F41A /* ProfilesOperations.cpp in Sources */,
180F6C8117CE9A5700127892 /* smc.c in Sources */,
DFD882F817DD1A5B001516FE /* AddonPythonInvoker.cpp in Sources */,
+ DFD882E917DD189E001516FE /* StringValidation.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -11636,6 +11644,7 @@
DF29668217B2B04300DF10F9 /* SettingRequirement.cpp in Sources */,
DF28DF4F17B8379E0077F41A /* ProfilesOperations.cpp in Sources */,
DFD882F617DD1A5B001516FE /* AddonPythonInvoker.cpp in Sources */,
+ DFD882E717DD189E001516FE /* StringValidation.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -12666,6 +12675,7 @@
DF29668117B2B04300DF10F9 /* SettingRequirement.cpp in Sources */,
DF28DF4E17B8379E0077F41A /* ProfilesOperations.cpp in Sources */,
DFD882F717DD1A5B001516FE /* AddonPythonInvoker.cpp in Sources */,
+ DFD882E817DD189E001516FE /* StringValidation.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -168,6 +168,7 @@
<font>font13</font>
<textcolor>grey2</textcolor>
<focusedcolor>white</focusedcolor>
+ <invalidcolor>invalid</invalidcolor>
<texturefocus border="0,2,0,2">MenuItemFO.png</texturefocus>
<texturenofocus border="0,2,0,2">MenuItemNF.png</texturenofocus>
</control>
@@ -132,6 +132,7 @@
<height>40</height>
<font>font13</font>
<aligny>center</aligny>
+ <invalidcolor>invalid</invalidcolor>
<texturefocus border="3">button-focus2.png</texturefocus>
<texturenofocus border="3">button-nofocus.png</texturenofocus>
<label>-</label>
@@ -188,6 +188,7 @@
<font>font13</font>
<textcolor>white</textcolor>
<disabledcolor>grey3</disabledcolor>
+ <invalidcolor>invalid</invalidcolor>
<textoffsetx>7</textoffsetx>
<aligny>center</aligny>
<pulseonselect>no</pulseonselect>
@@ -6,4 +6,5 @@
<color name="black">FF000000</color>
<color name="blue">FF0084ff</color>
<color name="selected">FFEB9E17</color>
+ <color name="invalid">FFFF0000</color>
</colors>
@@ -1209,6 +1209,7 @@
<ClInclude Include="..\..\xbmc\utils\IXmlDeserializable.h" />
<ClInclude Include="..\..\xbmc\utils\LegacyPathTranslation.h" />
<ClInclude Include="..\..\xbmc\utils\RssManager.h" />
+ <ClInclude Include="..\..\xbmc\utils\StringValidation.h" />
<ClInclude Include="..\..\xbmc\utils\Vector.h" />
<ClInclude Include="..\..\xbmc\video\FFmpegVideoDecoder.h" />
<ClInclude Include="..\..\xbmc\interfaces\python\swig.h" />
@@ -1350,6 +1351,7 @@
<ClCompile Include="..\..\xbmc\utils\BooleanLogic.cpp" />
<ClCompile Include="..\..\xbmc\utils\LegacyPathTranslation.cpp" />
<ClCompile Include="..\..\xbmc\utils\RssManager.cpp" />
+ <ClCompile Include="..\..\xbmc\utils\StringValidation.cpp" />
<ClCompile Include="..\..\xbmc\utils\test\TestUrlOptions.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug (DirectX)|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug (OpenGL)|Win32'">true</ExcludedFromBuild>
@@ -3070,6 +3070,9 @@
<ClCompile Include="..\..\xbmc\interfaces\python\AddonPythonInvoker.cpp">
<Filter>interfaces\python</Filter>
</ClCompile>
+ <ClCompile Include="..\..\xbmc\utils\StringValidation.cpp">
+ <Filter>utils</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\xbmc\win32\pch.h">
@@ -6028,6 +6031,9 @@
<ClInclude Include="..\..\xbmc\interfaces\python\AddonPythonInvoker.h">
<Filter>interfaces\python</Filter>
</ClInclude>
+ <ClInclude Include="..\..\xbmc\utils\StringValidation.h">
+ <Filter>utils</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\..\xbmc\win32\XBMC_PC.rc">
@@ -84,6 +84,10 @@ bool CGUIDialogSmartPlaylistRule::OnMessage(CGUIMessage& message)
return true;
}
break;
+
+ case GUI_MSG_VALIDITY_CHANGED:
+ CONTROL_ENABLE_ON_CONDITION(CONTROL_OK, message.GetParam1());
+ break;
}
return CGUIDialog::OnMessage(message);
}
@@ -496,6 +500,10 @@ void CGUIDialogSmartPlaylistRule::OnInitWindow()
OnMessage(msg);
}
UpdateButtons();
+
+ CGUIEditControl *editControl = (CGUIEditControl*)GetControl(CONTROL_VALUE);
+ if (editControl != NULL)
+ editControl->SetInputValidation(CSmartPlaylistRule::Validate, &m_rule);
}
void CGUIDialogSmartPlaylistRule::OnDeinitWindow(int nextWindowID)
@@ -86,7 +86,7 @@ class CGUIButtonControl : public CGUIControl
void OnUnFocus();
virtual void ProcessText(unsigned int currentTime);
virtual void RenderText();
- CGUILabel::COLOR GetTextColor() const;
+ virtual CGUILabel::COLOR GetTextColor() const;
CGUITexture m_imgFocus;
CGUITexture m_imgNoFocus;
@@ -757,6 +757,7 @@ CGUIControl* CGUIControlFactory::Create(int parentID, const CRect &rect, TiXmlEl
GetInfoColor(pControlNode, "disabledcolor", labelInfo.disabledColor, parentID);
GetInfoColor(pControlNode, "shadowcolor", labelInfo.shadowColor, parentID);
GetInfoColor(pControlNode, "selectedcolor", labelInfo.selectedColor, parentID);
+ GetInfoColor(pControlNode, "invalidcolor", labelInfo.invalidColor, parentID);
XMLUtils::GetFloat(pControlNode, "textoffsetx", labelInfo.offsetX);
XMLUtils::GetFloat(pControlNode, "textoffsety", labelInfo.offsetY);
int angle = 0; // use the negative angle to compensate for our vertically flipped cartesian plane
@@ -66,6 +66,9 @@ void CGUIEditControl::DefaultConstructor()
m_label.SetAlign(m_label.GetLabelInfo().align & XBFONT_CENTER_Y); // left align
m_label2.GetLabelInfo().offsetX = 0;
m_isMD5 = false;
+ m_invalidInput = false;
+ m_inputValidator = NULL;
+ m_inputValidatorData = NULL;
}
CGUIEditControl::CGUIEditControl(const CGUIButtonControl &button)
@@ -335,6 +338,8 @@ void CGUIEditControl::UpdateText(bool sendUpdate)
m_smsTimer.Stop();
if (sendUpdate)
{
+ ValidateInput();
+
SEND_CLICK_MESSAGE(GetID(), GetParentID(), 0);
m_textChangeActions.ExecuteActions(GetID(), GetParentID());
@@ -471,6 +476,15 @@ void CGUIEditControl::RenderText()
}
}
+CGUILabel::COLOR CGUIEditControl::GetTextColor() const
+{
+ CGUILabel::COLOR color = CGUIButtonControl::GetTextColor();
+ if (color != CGUILabel::COLOR_DISABLED && HasInvalidInput())
+ return CGUILabel::COLOR_INVALID;
+
+ return color;
+}
+
void CGUIEditControl::SetHint(const CGUIInfoLabel& hint)
{
m_hintInfo = hint;
@@ -508,6 +522,7 @@ void CGUIEditControl::SetLabel2(const std::string &text)
m_isMD5 = (m_inputType == INPUT_TYPE_PASSWORD_MD5 || m_inputType == INPUT_TYPE_PASSWORD_NUMBER_VERIFY_NEW);
m_text2 = newText;
m_cursorPos = m_text2.size();
+ ValidateInput();
SetInvalid();
}
}
@@ -598,3 +613,40 @@ void CGUIEditControl::OnPasteClipboard()
UpdateText();
}
}
+
+void CGUIEditControl::SetInputValidation(StringValidation::Validator inputValidator, void *data /* = NULL */)
+{
+ if (m_inputValidator == inputValidator)
+ return;
+
+ m_inputValidator = inputValidator;
+ m_inputValidatorData = data;
+ // the input validator has changed, so re-validate the current data
+ ValidateInput();
+}
+
+bool CGUIEditControl::ValidateInput(const CStdStringW &data) const
+{
+ if (m_inputValidator == NULL)
+ return true;
+
+ return m_inputValidator(GetLabel2(), (void*)(m_inputValidatorData != NULL ? m_inputValidatorData : this));
+}
+
+void CGUIEditControl::ValidateInput()
+{
+ // validate the input
+ bool invalid = !ValidateInput(m_text2);
+ // nothing to do if still valid/invalid
+ if (invalid != m_invalidInput)
+ {
+ // the validity state has changed so we need to update the control
+ m_invalidInput = invalid;
+
+ // let the window/dialog know that the validity has changed
+ CGUIMessage msg(GUI_MSG_VALIDITY_CHANGED, GetID(), GetID(), m_invalidInput ? 0 : 1);
+ SendWindowMessage(msg);
+
+ SetInvalid();
+ }
+}
@@ -30,6 +30,7 @@
#include "GUIButtonControl.h"
#include "utils/Stopwatch.h"
+#include "utils/StringValidation.h"
/*!
\ingroup controls
@@ -80,9 +81,13 @@ class CGUIEditControl : public CGUIButtonControl
bool HasTextChangeActions() const { return m_textChangeActions.HasActionsMeetingCondition(); };
+ virtual bool HasInvalidInput() const { return m_invalidInput; }
+ virtual void SetInputValidation(StringValidation::Validator inputValidator, void *data = NULL);
+
protected:
virtual void ProcessText(unsigned int currentTime);
virtual void RenderText();
+ virtual CGUILabel::COLOR GetTextColor() const;
CStdStringW GetDisplayedText() const;
void RecalcLabelPosition();
void ValidateCursor();
@@ -91,6 +96,9 @@ class CGUIEditControl : public CGUIButtonControl
void OnSMSCharacter(unsigned int key);
void DefaultConstructor();
+ virtual bool ValidateInput(const CStdStringW &data) const;
+ void ValidateInput();
+
/*! \brief Clear out the current text input if it's an MD5 password.
\return true if the password is cleared, false otherwise.
*/
@@ -114,6 +122,10 @@ class CGUIEditControl : public CGUIButtonControl
CGUIAction m_textChangeActions;
+ bool m_invalidInput;
+ StringValidation::Validator m_inputValidator;
+ void *m_inputValidatorData;
+
unsigned int m_smsKeyIndex;
unsigned int m_smsLastKey;
CStopWatch m_smsTimer;
View
@@ -78,6 +78,8 @@ color_t CGUILabel::GetColor() const
return m_label.disabledColor;
case COLOR_FOCUSED:
return m_label.focusedColor ? m_label.focusedColor : m_label.textColor;
+ case COLOR_INVALID:
+ return m_label.invalidColor ? m_label.invalidColor : m_label.textColor;
default:
break;
}
View
@@ -52,6 +52,7 @@ class CLabelInfo
changed |= selectedColor.Update();
changed |= disabledColor.Update();
changed |= focusedColor.Update();
+ changed |= invalidColor.Update();
return changed;
};
@@ -61,6 +62,7 @@ class CLabelInfo
CGUIInfoColor selectedColor;
CGUIInfoColor disabledColor;
CGUIInfoColor focusedColor;
+ CGUIInfoColor invalidColor;
uint32_t align;
float offsetX;
float offsetY;
@@ -83,7 +85,8 @@ class CGUILabel
enum COLOR { COLOR_TEXT = 0,
COLOR_SELECTED,
COLOR_FOCUSED,
- COLOR_DISABLED };
+ COLOR_DISABLED,
+ COLOR_INVALID };
/*! \brief allowed overflow handling techniques for labels, as defined by the skin
*/
View
@@ -133,6 +133,8 @@
#define GUI_MSG_WINDOW_LOAD 43
+#define GUI_MSG_VALIDITY_CHANGED 44
+
#define GUI_MSG_USER 1000
/*!
Oops, something went wrong.

0 comments on commit 95bfa12

Please sign in to comment.