From 2d19b297a98d8be87c487f6b93e098dce9ca908c Mon Sep 17 00:00:00 2001
From: Paul ProductEngine <pguslisty@productengine.com>
Date: Fri, 13 May 2011 18:23:11 +0300
Subject: STORM-1202 FIXED Time spin control

Implemented time spin control which is like spin control, but shows and allows to edit time string in "hh:mm PM/AM" format.
Implemented according to the WLRS spec.
---
 indra/llui/CMakeLists.txt     |   2 +
 indra/llui/lllineeditor.cpp   | 106 +++++++++---
 indra/llui/lllineeditor.h     |   4 +
 indra/llui/lltextvalidate.cpp |  33 ++++
 indra/llui/lltextvalidate.h   |   1 +
 indra/llui/lltimectrl.cpp     | 390 ++++++++++++++++++++++++++++++++++++++++++
 indra/llui/lltimectrl.h       | 125 ++++++++++++++
 7 files changed, 640 insertions(+), 21 deletions(-)
 create mode 100644 indra/llui/lltimectrl.cpp
 create mode 100644 indra/llui/lltimectrl.h

(limited to 'indra/llui')

diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt
index 33ab2e93b5..72329c70fe 100644
--- a/indra/llui/CMakeLists.txt
+++ b/indra/llui/CMakeLists.txt
@@ -93,6 +93,7 @@ set(llui_SOURCE_FILES
     lltextparser.cpp
     lltextutil.cpp
     lltextvalidate.cpp
+    lltimectrl.cpp
     lltransutil.cpp
     lltoggleablemenu.cpp
     lltooltip.cpp
@@ -191,6 +192,7 @@ set(llui_HEADER_FILES
     lltextparser.h
     lltextutil.h
     lltextvalidate.h
+    lltimectrl.h
     lltoggleablemenu.h
     lltooltip.h
     lltransutil.h
diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp
index d99ee5a545..66c607e988 100644
--- a/indra/llui/lllineeditor.cpp
+++ b/indra/llui/lllineeditor.cpp
@@ -81,6 +81,7 @@ LLLineEditor::Params::Params()
 :	max_length(""),
     keystroke_callback("keystroke_callback"),
 	prevalidate_callback("prevalidate_callback"),
+	prevalidate_input_callback("prevalidate_input_callback"),
 	background_image("background_image"),
 	background_image_disabled("background_image_disabled"),
 	background_image_focused("background_image_focused"),
@@ -173,6 +174,7 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p)
 	updateTextPadding();
 	setCursor(mText.length());
 
+	setPrevalidate(p.prevalidate_input_callback());
 	setPrevalidate(p.prevalidate_callback());
 
 	LLContextMenu* menu = LLUICtrlFactory::instance().createFromFile<LLContextMenu>
@@ -401,23 +403,16 @@ void LLLineEditor::setText(const LLStringExplicit &new_text)
 // Picks a new cursor position based on the actual screen size of text being drawn.
 void LLLineEditor::setCursorAtLocalPos( S32 local_mouse_x )
 {
-	const llwchar* wtext = mText.getWString().c_str();
-	LLWString asterix_text;
-	if (mDrawAsterixes)
-	{
-		for (S32 i = 0; i < mText.length(); i++)
-		{
-			asterix_text += utf8str_to_wstring(PASSWORD_ASTERISK);
-		}
-		wtext = asterix_text.c_str();
-	}
+	S32 cursor_pos = calcCursorPos(local_mouse_x);
+
+	S32 left_pos = llmin( mSelectionStart, cursor_pos );
+	S32 selection_length = llabs( mSelectionStart - cursor_pos );
+	const LLWString& text = mText.getWString();
+	const LLWString& substr = text.substr(left_pos, selection_length);
+
+	if (mPrevalidateInputFunc && mIsSelecting && !mPrevalidateInputFunc(substr))
+		return;
 
-	S32 cursor_pos =
-		mScrollHPos + 
-		mGLFont->charFromPixelOffset(
-			wtext, mScrollHPos,
-			(F32)(local_mouse_x - mTextLeftEdge),
-			(F32)(mTextRightEdge - mTextLeftEdge + 1)); // min-max range is inclusive
 	setCursor(cursor_pos);
 }
 
@@ -501,6 +496,9 @@ BOOL LLLineEditor::canSelectAll() const
 
 void LLLineEditor::selectAll()
 {
+	if (mPrevalidateInputFunc && !mPrevalidateInputFunc(mText.getWString()))
+		return;
+
 	mSelectionStart = mText.length();
 	mSelectionEnd = 0;
 	setCursor(mSelectionEnd);
@@ -586,6 +584,9 @@ BOOL LLLineEditor::handleMouseDown(S32 x, S32 y, MASK mask)
 
 		if (mask & MASK_SHIFT)
 		{
+			// assume we're starting a drag select
+			mIsSelecting = TRUE;
+
 			// Handle selection extension
 			S32 old_cursor_pos = getCursor();
 			setCursorAtLocalPos(x);
@@ -620,8 +621,6 @@ BOOL LLLineEditor::handleMouseDown(S32 x, S32 y, MASK mask)
 				mSelectionStart = old_cursor_pos;
 				mSelectionEnd = getCursor();
 			}
-			// assume we're starting a drag select
-			mIsSelecting = TRUE;
 		}
 		else
 		{
@@ -792,6 +791,9 @@ void LLLineEditor::removeChar()
 {
 	if( getCursor() > 0 )
 	{
+		if (mPrevalidateInputFunc && !mPrevalidateInputFunc(mText.getWString().substr(getCursor()-1, 1)))
+			return;
+
 		mText.erase(getCursor() - 1, 1);
 
 		setCursor(getCursor() - 1);
@@ -812,6 +814,9 @@ void LLLineEditor::addChar(const llwchar uni_char)
 	}
 	else if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode())
 	{
+		if (mPrevalidateInputFunc && !mPrevalidateInputFunc(mText.getWString().substr(getCursor(), 1)))
+			return;
+
 		mText.erase(getCursor(), 1);
 	}
 
@@ -860,6 +865,13 @@ void LLLineEditor::extendSelection( S32 new_cursor_pos )
 		startSelection();
 	}
 	
+	S32 left_pos = llmin( mSelectionStart, new_cursor_pos );
+	S32 selection_length = llabs( mSelectionStart - new_cursor_pos );
+	const LLWString& selection = mText.getWString();
+
+	if ( mPrevalidateInputFunc && !mPrevalidateInputFunc(selection.substr(left_pos, selection_length)))
+		return;
+
 	setCursor(new_cursor_pos);
 	mSelectionEnd = getCursor();
 }
@@ -992,6 +1004,10 @@ void LLLineEditor::deleteSelection()
 	{
 		S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
 		S32 selection_length = llabs( mSelectionStart - mSelectionEnd );
+		const LLWString& selection = mText.getWString();
+
+		if ( mPrevalidateInputFunc && !mPrevalidateInputFunc(selection.substr(left_pos, selection_length)))
+			return;
 
 		mText.erase(left_pos, selection_length);
 		deselect();
@@ -1009,12 +1025,16 @@ void LLLineEditor::cut()
 {
 	if( canCut() )
 	{
+		S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
+		S32 length = llabs( mSelectionStart - mSelectionEnd );
+		const LLWString& selection = mText.getWString();
+
+		if ( mPrevalidateInputFunc && !mPrevalidateInputFunc(selection.substr(left_pos, length)))
+			return;
+
 		// Prepare for possible rollback
 		LLLineEditorRollback rollback( this );
 
-
-		S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
-		S32 length = llabs( mSelectionStart - mSelectionEnd );
 		gClipboard.copyFromSubstring( mText.getWString(), left_pos, length );
 		deleteSelection();
 
@@ -1094,6 +1114,9 @@ void LLLineEditor::pasteHelper(bool is_primary)
 
 		if (!paste.empty())
 		{
+			if ( mPrevalidateInputFunc && !mPrevalidateInputFunc(paste) )
+				return;
+
 			// Prepare for possible rollback
 			LLLineEditorRollback rollback(this);
 			
@@ -1441,6 +1464,11 @@ BOOL LLLineEditor::handleUnicodeCharHere(llwchar uni_char)
 
 		LLLineEditorRollback rollback( this );
 
+		LLWString u_char;
+		u_char.assign(1, uni_char);
+		if (mPrevalidateInputFunc && !mPrevalidateInputFunc(u_char))
+			return handled;
+
 		addChar(uni_char);
 
 		mKeystrokeTimer.reset();
@@ -1492,6 +1520,15 @@ void LLLineEditor::doDelete()
 		}
 		else if ( getCursor() < mText.length())
 		{	
+			const LLWString& selection = mText.getWString();
+
+			if ( mPrevalidateInputFunc && !mPrevalidateInputFunc(selection.substr(getCursor(), 1)))
+			{
+				if( mKeystrokeCallback )
+					mKeystrokeCallback( this );
+
+				return;
+			}
 			setCursor(getCursor() + 1);
 			removeChar();
 		}
@@ -1839,6 +1876,27 @@ S32 LLLineEditor::findPixelNearestPos(const S32 cursor_offset) const
 	return result;
 }
 
+S32 LLLineEditor::calcCursorPos(S32 mouse_x)
+{
+	const llwchar* wtext = mText.getWString().c_str();
+	LLWString asterix_text;
+	if (mDrawAsterixes)
+	{
+		for (S32 i = 0; i < mText.length(); i++)
+		{
+			asterix_text += utf8str_to_wstring(PASSWORD_ASTERISK);
+		}
+		wtext = asterix_text.c_str();
+	}
+
+	S32 cur_pos = mScrollHPos +
+			mGLFont->charFromPixelOffset(
+				wtext, mScrollHPos,
+				(F32)(mouse_x - mTextLeftEdge),
+				(F32)(mTextRightEdge - mTextLeftEdge + 1)); // min-max range is inclusive
+
+	return cur_pos;
+}
 //virtual
 void LLLineEditor::clear()
 {
@@ -1932,6 +1990,12 @@ void LLLineEditor::setPrevalidate(LLTextValidate::validate_func_t func)
 	updateAllowingLanguageInput();
 }
 
+void LLLineEditor::setPrevalidateInputText(LLTextValidate::validate_func_t func)
+{
+	mPrevalidateInputFunc = func;
+	updateAllowingLanguageInput();
+}
+
 // static
 BOOL LLLineEditor::postvalidateFloat(const std::string &str)
 {
diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h
index 7b5fa218f2..1e29fd0dbf 100644
--- a/indra/llui/lllineeditor.h
+++ b/indra/llui/lllineeditor.h
@@ -75,6 +75,7 @@ public:
 		Optional<keystroke_callback_t>	keystroke_callback;
 
 		Optional<LLTextValidate::validate_func_t, LLTextValidate::ValidateTextNamedFuncs>	prevalidate_callback;
+		Optional<LLTextValidate::validate_func_t, LLTextValidate::ValidateTextNamedFuncs>	prevalidate_input_callback;
 		
 		Optional<LLViewBorder::Params>	border;
 
@@ -231,6 +232,7 @@ public:
 
 	// Prevalidation controls which keystrokes can affect the editor
 	void			setPrevalidate( LLTextValidate::validate_func_t func );
+	void			setPrevalidateInputText( LLTextValidate::validate_func_t func );
 	static BOOL		postvalidateFloat(const std::string &str);
 
 	// line history support:
@@ -250,6 +252,7 @@ private:
 	void			addChar(const llwchar c);
 	void			setCursorAtLocalPos(S32 local_mouse_x);
 	S32				findPixelNearestPos(S32 cursor_offset = 0) const;
+	S32				calcCursorPos(S32 mouse_x);
 	BOOL			handleSpecialKey(KEY key, MASK mask);
 	BOOL			handleSelectionKey(KEY key, MASK mask);
 	BOOL			handleControlKey(KEY key, MASK mask);
@@ -311,6 +314,7 @@ protected:
 	S32			mLastSelectionEnd;
 
 	LLTextValidate::validate_func_t mPrevalidateFunc;
+	LLTextValidate::validate_func_t mPrevalidateInputFunc;
 
 	LLFrameTimer mKeystrokeTimer;
 	LLTimer		mTripleClickTimer;
diff --git a/indra/llui/lltextvalidate.cpp b/indra/llui/lltextvalidate.cpp
index 4b9faa0560..234e600ccd 100644
--- a/indra/llui/lltextvalidate.cpp
+++ b/indra/llui/lltextvalidate.cpp
@@ -188,6 +188,39 @@ namespace LLTextValidate
 		return success;
 	}
 
+	bool validateNonNegativeS32NoSpace(const LLWString &str)
+	{
+		LLLocale locale(LLLocale::USER_LOCALE);
+
+		LLWString test_str = str;
+		S32 len = test_str.length();
+		bool success = TRUE;
+		if(0 < len)
+		{
+			if('-' == test_str[0])
+			{
+				success = FALSE;
+			}
+			S32 i = 0;
+			while(success && (i < len))
+			{
+				if(!LLStringOps::isDigit(test_str[i]) || LLStringOps::isSpace(test_str[i++]))
+				{
+					success = FALSE;
+				}
+			}
+		}
+		if (success)
+		{
+			S32 val = strtol(wstring_to_utf8str(test_str).c_str(), NULL, 10);
+			if (val < 0)
+			{
+				success = FALSE;
+			}
+		}
+		return success;
+	}
+
 	bool validateAlphaNum(const LLWString &str)
 	{
 		LLLocale locale(LLLocale::USER_LOCALE);
diff --git a/indra/llui/lltextvalidate.h b/indra/llui/lltextvalidate.h
index 84644be30c..5c830d7db3 100644
--- a/indra/llui/lltextvalidate.h
+++ b/indra/llui/lltextvalidate.h
@@ -46,6 +46,7 @@ namespace LLTextValidate
 	bool	validateInt(const LLWString &str );
 	bool	validatePositiveS32(const LLWString &str);
 	bool	validateNonNegativeS32(const LLWString &str);
+	bool 	validateNonNegativeS32NoSpace(const LLWString &str);
 	bool	validateAlphaNum(const LLWString &str );
 	bool	validateAlphaNumSpace(const LLWString &str );
 	bool	validateASCIIPrintableNoPipe(const LLWString &str); 
diff --git a/indra/llui/lltimectrl.cpp b/indra/llui/lltimectrl.cpp
new file mode 100644
index 0000000000..33e8db432f
--- /dev/null
+++ b/indra/llui/lltimectrl.cpp
@@ -0,0 +1,390 @@
+/**
+ * @file lltimectrl.cpp
+ * @brief LLTimeCtrl base class
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2011, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "lltimectrl.h"
+
+#include "llui.h"
+#include "lluiconstants.h"
+
+#include "llbutton.h"
+#include "llfontgl.h"
+#include "lllineeditor.h"
+#include "llkeyboard.h"
+#include "llstring.h"
+#include "lltextbox.h"
+#include "lluictrlfactory.h"
+
+static LLDefaultChildRegistry::Register<LLTimeCtrl> time_r("time");
+
+const U32 AMPM_LEN = 3;
+const U32 MINUTES_MIN = 0;
+const U32 MINUTES_MAX = 59;
+const U32 HOURS_MIN = 1;
+const U32 HOURS_MAX = 12;
+
+LLTimeCtrl::Params::Params()
+:	label_width("label_width"),
+	allow_text_entry("allow_text_entry", true),
+	text_enabled_color("text_enabled_color"),
+	text_disabled_color("text_disabled_color"),
+	up_button("up_button"),
+	down_button("down_button")
+{}
+
+LLTimeCtrl::LLTimeCtrl(const LLTimeCtrl::Params& p)
+:	LLUICtrl(p),
+	mLabelBox(NULL),
+	mTextEnabledColor(p.text_enabled_color()),
+	mTextDisabledColor(p.text_disabled_color()),
+	mHours(HOURS_MIN),
+	mMinutes(MINUTES_MIN)
+{
+	static LLUICachedControl<S32> spinctrl_spacing ("UISpinctrlSpacing", 0);
+	static LLUICachedControl<S32> spinctrl_btn_width ("UISpinctrlBtnWidth", 0);
+	static LLUICachedControl<S32> spinctrl_btn_height ("UISpinctrlBtnHeight", 0);
+	S32 centered_top = getRect().getHeight();
+	S32 centered_bottom = getRect().getHeight() - 2 * spinctrl_btn_height;
+	S32 label_width = llclamp(p.label_width(), 0, llmax(0, getRect().getWidth() - 40));
+	S32 editor_left = label_width + spinctrl_spacing;
+
+	//================= Label =================//
+	if( !p.label().empty() )
+	{
+		LLRect label_rect( 0, centered_top, label_width, centered_bottom );
+		LLTextBox::Params params;
+		params.name("TimeCtrl Label");
+		params.rect(label_rect);
+		params.initial_value(p.label());
+		if (p.font.isProvided())
+		{
+			params.font(p.font);
+		}
+		mLabelBox = LLUICtrlFactory::create<LLTextBox> (params);
+		addChild(mLabelBox);
+
+		editor_left = label_rect.mRight + spinctrl_spacing;
+	}
+
+	S32 editor_right = getRect().getWidth() - spinctrl_btn_width - spinctrl_spacing;
+
+	//================= Editor ================//
+	LLRect editor_rect( editor_left, centered_top, editor_right, centered_bottom );
+	LLLineEditor::Params params;
+	params.name("SpinCtrl Editor");
+	params.rect(editor_rect);
+	if (p.font.isProvided())
+	{
+		params.font(p.font);
+	}
+
+	params.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM);
+	params.max_length.chars(8);
+	params.keystroke_callback(boost::bind(&LLTimeCtrl::onTextEntry, this, _1));
+	mEditor = LLUICtrlFactory::create<LLLineEditor> (params);
+	mEditor->setPrevalidateInputText(LLTextValidate::validateNonNegativeS32NoSpace);
+	mEditor->setPrevalidate(boost::bind(&LLTimeCtrl::isTimeStringValid, this, _1));
+	mEditor->setText(LLStringExplicit("0:00 AM"));
+	addChild(mEditor);
+
+	//================= Spin Buttons ==========//
+	LLButton::Params up_button_params(p.up_button);
+	up_button_params.rect = LLRect(editor_right + 1, getRect().getHeight(), editor_right + spinctrl_btn_width, getRect().getHeight() - spinctrl_btn_height);
+
+	up_button_params.click_callback.function(boost::bind(&LLTimeCtrl::onUpBtn, this));
+	up_button_params.mouse_held_callback.function(boost::bind(&LLTimeCtrl::onUpBtn, this));
+	mUpBtn = LLUICtrlFactory::create<LLButton>(up_button_params);
+	addChild(mUpBtn);
+
+	LLButton::Params down_button_params(p.down_button);
+	down_button_params.rect = LLRect(editor_right + 1, getRect().getHeight() - spinctrl_btn_height, editor_right + spinctrl_btn_width, getRect().getHeight() - 2 * spinctrl_btn_height);
+	down_button_params.click_callback.function(boost::bind(&LLTimeCtrl::onDownBtn, this));
+	down_button_params.mouse_held_callback.function(boost::bind(&LLTimeCtrl::onDownBtn, this));
+	mDownBtn = LLUICtrlFactory::create<LLButton>(down_button_params);
+	addChild(mDownBtn);
+
+	setUseBoundingRect( TRUE );
+}
+
+BOOL LLTimeCtrl::handleKeyHere(KEY key, MASK mask)
+{
+	if (mEditor->hasFocus())
+	{
+		if(key == KEY_UP)
+		{
+			onUpBtn();
+			return TRUE;
+		}
+		if(key == KEY_DOWN)
+		{
+			onDownBtn();
+			return TRUE;
+		}
+	}
+	return FALSE;
+}
+
+void LLTimeCtrl::onUpBtn()
+{
+	switch(getEditingPart())
+	{
+	case HOURS:
+		increaseHours();
+		break;
+	case MINUTES:
+		increaseMinutes();
+		break;
+	case DAYPART:
+		switchDayPeriod();
+		break;
+	default:
+		break;
+	}
+
+	buildTimeString();
+	mEditor->setText(mTimeString);
+}
+
+void LLTimeCtrl::onDownBtn()
+{
+	switch(getEditingPart())
+	{
+	case HOURS:
+		decreaseHours();
+		break;
+	case MINUTES:
+		decreaseMinutes();
+		break;
+	case DAYPART:
+		switchDayPeriod();
+		break;
+	default:
+		break;
+	}
+
+	buildTimeString();
+	mEditor->setText(mTimeString);
+}
+
+void LLTimeCtrl::onFocusLost()
+{
+	buildTimeString();
+	mEditor->setText(mTimeString);
+
+	LLUICtrl::onFocusLost();
+}
+
+void LLTimeCtrl::onTextEntry(LLLineEditor* line_editor)
+{
+	LLWString time_str = line_editor->getWText();
+	switch(getEditingPart())
+	{
+	case HOURS:
+		validateHours(getHoursWString(time_str));
+		break;
+	case MINUTES:
+		validateMinutes(getMinutesWString(time_str));
+		break;
+	default:
+		break;
+	}
+}
+
+bool LLTimeCtrl::isTimeStringValid(const LLWString &wstr)
+{
+	if (!isHoursStringValid(getHoursWString(wstr)) || !isMinutesStringValid(getMinutesWString(wstr)) || !isPMAMStringValid(wstr))
+		return false;
+
+	return true;
+}
+
+bool LLTimeCtrl::isHoursStringValid(const LLWString& wstr)
+{
+	U32 hours;
+	if ((!LLWStringUtil::convertToU32(wstr, hours) || (hours <= HOURS_MAX)) && wstr.length() < 3)
+		return true;
+
+	return false;
+}
+
+bool LLTimeCtrl::isMinutesStringValid(const LLWString& wstr)
+{
+	U32 minutes;
+	if (!LLWStringUtil::convertToU32(wstr, minutes) || (minutes <= MINUTES_MAX) && wstr.length() < 3)
+		return true;
+
+	return false;
+}
+
+void LLTimeCtrl::validateHours(const LLWString& wstr)
+{
+	U32 hours;
+	if (LLWStringUtil::convertToU32(wstr, hours) && (hours >= HOURS_MIN) && (hours <= HOURS_MAX))
+	{
+		mHours = hours;
+	}
+	else
+	{
+		mHours = HOURS_MIN;
+	}
+}
+
+void LLTimeCtrl::validateMinutes(const LLWString& wstr)
+{
+	U32 minutes;
+	if (LLWStringUtil::convertToU32(wstr, minutes) && (minutes >= MINUTES_MIN) && (minutes <= MINUTES_MAX))
+	{
+		mMinutes = minutes;
+	}
+	else
+	{
+		mMinutes = MINUTES_MIN;
+	}
+}
+
+bool LLTimeCtrl::isPMAMStringValid(const LLWString &wstr)
+{
+	S32 len = wstr.length();
+
+	bool valid = (wstr[--len] == 'M') && (wstr[--len] == 'P' || wstr[len] == 'A');
+
+	return valid;
+}
+
+LLWString LLTimeCtrl::getHoursWString(const LLWString& wstr)
+{
+	size_t colon_index = wstr.find_first_of(':');
+	LLWString hours_str = wstr.substr(0, colon_index);
+
+	return hours_str;
+}
+
+LLWString LLTimeCtrl::getMinutesWString(const LLWString& wstr)
+{
+	size_t colon_index = wstr.find_first_of(':');
+	++colon_index;
+
+	int minutes_len = wstr.length() - colon_index - AMPM_LEN;
+	LLWString minutes_str = wstr.substr(colon_index, minutes_len);
+
+	return minutes_str;
+}
+
+void LLTimeCtrl::increaseMinutes()
+{
+	if (++mMinutes > MINUTES_MAX)
+	{
+		mMinutes = MINUTES_MIN;
+	}
+}
+
+void LLTimeCtrl::increaseHours()
+{
+	if (++mHours > HOURS_MAX)
+	{
+		mHours = HOURS_MIN;
+	}
+}
+
+void LLTimeCtrl::decreaseMinutes()
+{
+	if (mMinutes-- == MINUTES_MIN)
+	{
+		mMinutes = MINUTES_MAX;
+	}
+}
+
+void LLTimeCtrl::decreaseHours()
+{
+	if (mHours-- == HOURS_MIN)
+	{
+		mHours = HOURS_MAX;
+		switchDayPeriod();
+	}
+}
+
+void LLTimeCtrl::switchDayPeriod()
+{
+	switch (mCurrentDayPeriod)
+	{
+	case AM:
+		mCurrentDayPeriod = PM;
+		break;
+	case PM:
+		mCurrentDayPeriod = AM;
+		break;
+	}
+}
+
+void LLTimeCtrl::buildTimeString()
+{
+	std::stringstream time_buf;
+	time_buf << mHours << ":";
+
+	if (mMinutes < 10)
+	{
+		time_buf << "0";
+	}
+
+	time_buf << mMinutes;
+	time_buf << " ";
+
+	switch (mCurrentDayPeriod)
+	{
+	case AM:
+		time_buf << "AM";
+		break;
+	case PM:
+		time_buf << "PM";
+		break;
+	}
+
+	mTimeString = time_buf.str();
+}
+
+LLTimeCtrl::EEditingPart LLTimeCtrl::getEditingPart()
+{
+	S32 cur_pos = mEditor->getCursor();
+	LLWString time_str = mEditor->getWText();
+
+	size_t colon_index = time_str.find_first_of(':');
+
+	if (cur_pos <= colon_index)
+	{
+		return HOURS;
+	}
+	else if (cur_pos > colon_index && cur_pos <= (time_str.length() - AMPM_LEN))
+	{
+		return MINUTES;
+	}
+	else if (cur_pos > (time_str.length() - AMPM_LEN))
+	{
+		return DAYPART;
+	}
+
+	return NONE;
+}
diff --git a/indra/llui/lltimectrl.h b/indra/llui/lltimectrl.h
new file mode 100644
index 0000000000..81d4477da4
--- /dev/null
+++ b/indra/llui/lltimectrl.h
@@ -0,0 +1,125 @@
+/**
+ * @file lltimectrl.h
+ * @brief Time control
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2011, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LLTIMECTRL_H_
+#define LLTIMECTRL_H_
+
+#include "stdtypes.h"
+#include "llbutton.h"
+#include "v4color.h"
+#include "llrect.h"
+
+class LLLineEditor;
+
+class LLTimeCtrl
+: public LLUICtrl
+{
+public:
+	struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
+	{
+		Optional<S32> label_width;
+		Optional<bool> allow_text_entry;
+
+		Optional<LLUIColor> text_enabled_color;
+		Optional<LLUIColor> text_disabled_color;
+
+		Optional<LLButton::Params> up_button;
+		Optional<LLButton::Params> down_button;
+
+		Params();
+	};
+protected:
+	LLTimeCtrl(const Params&);
+	friend class LLUICtrlFactory;
+
+	U32 getHours() const	{ return mHours; }
+	U32 getMinutes() const { return mMinutes; }
+
+private:
+
+	enum EDayPeriod
+	{
+		AM,
+		PM
+	};
+
+	enum EEditingPart
+	{
+		HOURS,
+		MINUTES,
+		DAYPART,
+		NONE
+	};
+
+	virtual void	onFocusLost();
+	virtual BOOL	handleKeyHere(KEY key, MASK mask);
+
+	void	onUpBtn();
+	void	onDownBtn();
+
+	void	onTextEntry(LLLineEditor* line_editor);
+
+	void	validateHours(const LLWString& wstr);
+	void	validateMinutes(const LLWString& wstr);
+	bool	isTimeStringValid(const LLWString& wstr);
+
+	bool	isPMAMStringValid(const LLWString& wstr);
+	bool	isHoursStringValid(const LLWString& wstr);
+	bool 	isMinutesStringValid(const LLWString& wstr);
+
+	LLWString getHoursWString(const LLWString& wstr);
+	LLWString getMinutesWString(const LLWString& wstr);
+
+	void increaseMinutes();
+	void increaseHours();
+
+	void decreaseMinutes();
+	void decreaseHours();
+
+	void switchDayPeriod();
+
+	void buildTimeString();
+
+	EEditingPart getEditingPart();
+
+	class LLTextBox*	mLabelBox;
+
+	class LLLineEditor*	mEditor;
+	LLUIColor			mTextEnabledColor;
+	LLUIColor			mTextDisabledColor;
+
+	class LLButton*		mUpBtn;
+	class LLButton*		mDownBtn;
+
+	U32 			mHours;
+	U32 			mMinutes;
+	EDayPeriod 		mCurrentDayPeriod;
+
+	std::string	mTimeString;
+
+	BOOL			mAllowEdit;
+};
+#endif /* LLTIMECTRL_H_ */
-- 
cgit v1.2.3


From 5daeefd35e1e105f403f99184ad866e8823767d4 Mon Sep 17 00:00:00 2001
From: Paul ProductEngine <pguslisty@productengine.com>
Date: Mon, 16 May 2011 17:59:05 +0300
Subject: STORM-1202 Code cleanup for LLLineEditor

- Removed code duplication

- Renamed prevalidateInputText to prevalidateInput
---
 indra/llui/lllineeditor.cpp | 65 +++++++++++++++++++++++++++------------------
 indra/llui/lllineeditor.h   |  8 +++++-
 indra/llui/lltimectrl.cpp   |  2 +-
 3 files changed, 47 insertions(+), 28 deletions(-)

(limited to 'indra/llui')

diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp
index 66c607e988..2527a608bf 100644
--- a/indra/llui/lllineeditor.cpp
+++ b/indra/llui/lllineeditor.cpp
@@ -174,7 +174,7 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p)
 	updateTextPadding();
 	setCursor(mText.length());
 
-	setPrevalidate(p.prevalidate_input_callback());
+	setPrevalidateInput(p.prevalidate_input_callback());
 	setPrevalidate(p.prevalidate_callback());
 
 	LLContextMenu* menu = LLUICtrlFactory::instance().createFromFile<LLContextMenu>
@@ -406,11 +406,10 @@ void LLLineEditor::setCursorAtLocalPos( S32 local_mouse_x )
 	S32 cursor_pos = calcCursorPos(local_mouse_x);
 
 	S32 left_pos = llmin( mSelectionStart, cursor_pos );
-	S32 selection_length = llabs( mSelectionStart - cursor_pos );
-	const LLWString& text = mText.getWString();
-	const LLWString& substr = text.substr(left_pos, selection_length);
+	S32 length = llabs( mSelectionStart - cursor_pos );
+	const LLWString& substr = mText.getWString().substr(left_pos, length);
 
-	if (mPrevalidateInputFunc && mIsSelecting && !mPrevalidateInputFunc(substr))
+	if (mIsSelecting && !prevalidateInput(substr))
 		return;
 
 	setCursor(cursor_pos);
@@ -496,8 +495,10 @@ BOOL LLLineEditor::canSelectAll() const
 
 void LLLineEditor::selectAll()
 {
-	if (mPrevalidateInputFunc && !mPrevalidateInputFunc(mText.getWString()))
+	if (!prevalidateInput(mText.getWString()))
+	{
 		return;
+	}
 
 	mSelectionStart = mText.length();
 	mSelectionEnd = 0;
@@ -791,7 +792,7 @@ void LLLineEditor::removeChar()
 {
 	if( getCursor() > 0 )
 	{
-		if (mPrevalidateInputFunc && !mPrevalidateInputFunc(mText.getWString().substr(getCursor()-1, 1)))
+		if (!prevalidateInput(mText.getWString().substr(getCursor()-1, 1)))
 			return;
 
 		mText.erase(getCursor() - 1, 1);
@@ -814,7 +815,7 @@ void LLLineEditor::addChar(const llwchar uni_char)
 	}
 	else if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode())
 	{
-		if (mPrevalidateInputFunc && !mPrevalidateInputFunc(mText.getWString().substr(getCursor(), 1)))
+		if (!prevalidateInput(mText.getWString().substr(getCursor(), 1)))
 			return;
 
 		mText.erase(getCursor(), 1);
@@ -867,9 +868,9 @@ void LLLineEditor::extendSelection( S32 new_cursor_pos )
 	
 	S32 left_pos = llmin( mSelectionStart, new_cursor_pos );
 	S32 selection_length = llabs( mSelectionStart - new_cursor_pos );
-	const LLWString& selection = mText.getWString();
+	const LLWString& selection = mText.getWString().substr(left_pos, selection_length);
 
-	if ( mPrevalidateInputFunc && !mPrevalidateInputFunc(selection.substr(left_pos, selection_length)))
+	if (!prevalidateInput(selection))
 		return;
 
 	setCursor(new_cursor_pos);
@@ -1002,11 +1003,11 @@ void LLLineEditor::deleteSelection()
 {
 	if( !mReadOnly && hasSelection() )
 	{
-		S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
-		S32 selection_length = llabs( mSelectionStart - mSelectionEnd );
-		const LLWString& selection = mText.getWString();
+		S32 left_pos, selection_length;
+		getSelectionRange(&left_pos, &selection_length);
+		const LLWString& selection = mText.getWString().substr(left_pos, selection_length);
 
-		if ( mPrevalidateInputFunc && !mPrevalidateInputFunc(selection.substr(left_pos, selection_length)))
+		if (!prevalidateInput(selection))
 			return;
 
 		mText.erase(left_pos, selection_length);
@@ -1025,11 +1026,11 @@ void LLLineEditor::cut()
 {
 	if( canCut() )
 	{
-		S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
-		S32 length = llabs( mSelectionStart - mSelectionEnd );
-		const LLWString& selection = mText.getWString();
+		S32 left_pos, length;
+		getSelectionRange(&left_pos, &length);
+		const LLWString& selection = mText.getWString().substr(left_pos, length);
 
-		if ( mPrevalidateInputFunc && !mPrevalidateInputFunc(selection.substr(left_pos, length)))
+		if (!prevalidateInput(selection))
 			return;
 
 		// Prepare for possible rollback
@@ -1114,7 +1115,7 @@ void LLLineEditor::pasteHelper(bool is_primary)
 
 		if (!paste.empty())
 		{
-			if ( mPrevalidateInputFunc && !mPrevalidateInputFunc(paste) )
+			if (!prevalidateInput(paste))
 				return;
 
 			// Prepare for possible rollback
@@ -1464,10 +1465,12 @@ BOOL LLLineEditor::handleUnicodeCharHere(llwchar uni_char)
 
 		LLLineEditorRollback rollback( this );
 
-		LLWString u_char;
-		u_char.assign(1, uni_char);
-		if (mPrevalidateInputFunc && !mPrevalidateInputFunc(u_char))
-			return handled;
+		{
+			LLWString u_char;
+			u_char.assign(1, uni_char);
+			if (!prevalidateInput(u_char))
+				return handled;
+		}
 
 		addChar(uni_char);
 
@@ -1520,9 +1523,9 @@ void LLLineEditor::doDelete()
 		}
 		else if ( getCursor() < mText.length())
 		{	
-			const LLWString& selection = mText.getWString();
+			const LLWString& text_to_delete = mText.getWString().substr(getCursor(), 1);
 
-			if ( mPrevalidateInputFunc && !mPrevalidateInputFunc(selection.substr(getCursor(), 1)))
+			if (!prevalidateInput(text_to_delete))
 			{
 				if( mKeystrokeCallback )
 					mKeystrokeCallback( this );
@@ -1990,12 +1993,22 @@ void LLLineEditor::setPrevalidate(LLTextValidate::validate_func_t func)
 	updateAllowingLanguageInput();
 }
 
-void LLLineEditor::setPrevalidateInputText(LLTextValidate::validate_func_t func)
+void LLLineEditor::setPrevalidateInput(LLTextValidate::validate_func_t func)
 {
 	mPrevalidateInputFunc = func;
 	updateAllowingLanguageInput();
 }
 
+bool LLLineEditor::prevalidateInput(const LLWString& wstr)
+{
+	if (mPrevalidateInputFunc && !mPrevalidateInputFunc(wstr))
+	{
+		return false;
+	}
+
+	return true;
+}
+
 // static
 BOOL LLLineEditor::postvalidateFloat(const std::string &str)
 {
diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h
index 1e29fd0dbf..1588f59b46 100644
--- a/indra/llui/lllineeditor.h
+++ b/indra/llui/lllineeditor.h
@@ -232,9 +232,15 @@ public:
 
 	// Prevalidation controls which keystrokes can affect the editor
 	void			setPrevalidate( LLTextValidate::validate_func_t func );
-	void			setPrevalidateInputText( LLTextValidate::validate_func_t func );
+	// This method sets callback that prevents from:
+	// - deleting, selecting, typing, cutting, pasting characters that are not valid.
+	// Also callback that this method sets differs from setPrevalidate in a way that it validates just inputed
+	// symbols, before existing text is modified, but setPrevalidate validates line after it was modified.
+	void			setPrevalidateInput(LLTextValidate::validate_func_t func);
 	static BOOL		postvalidateFloat(const std::string &str);
 
+	bool			prevalidateInput(const LLWString& wstr);
+
 	// line history support:
 	void			setEnableLineHistory( BOOL enabled ) { mHaveHistory = enabled; } // switches line history on or off 
 	void			updateHistory(); // stores current line in history
diff --git a/indra/llui/lltimectrl.cpp b/indra/llui/lltimectrl.cpp
index 33e8db432f..08d24a29a8 100644
--- a/indra/llui/lltimectrl.cpp
+++ b/indra/llui/lltimectrl.cpp
@@ -106,7 +106,7 @@ LLTimeCtrl::LLTimeCtrl(const LLTimeCtrl::Params& p)
 	params.max_length.chars(8);
 	params.keystroke_callback(boost::bind(&LLTimeCtrl::onTextEntry, this, _1));
 	mEditor = LLUICtrlFactory::create<LLLineEditor> (params);
-	mEditor->setPrevalidateInputText(LLTextValidate::validateNonNegativeS32NoSpace);
+	mEditor->setPrevalidateInput(LLTextValidate::validateNonNegativeS32NoSpace);
 	mEditor->setPrevalidate(boost::bind(&LLTimeCtrl::isTimeStringValid, this, _1));
 	mEditor->setText(LLStringExplicit("0:00 AM"));
 	addChild(mEditor);
-- 
cgit v1.2.3


From a9d7ee879216857fd61b36c70ffea257021c869a Mon Sep 17 00:00:00 2001
From: Vadim ProductEngine <vsavchuk@productengine.com>
Date: Wed, 18 May 2011 16:07:36 +0300
Subject: STORM-1202 ADDITIONAL_FIX Fixing Windows build.

---
 indra/llui/lltimectrl.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

(limited to 'indra/llui')

diff --git a/indra/llui/lltimectrl.cpp b/indra/llui/lltimectrl.cpp
index 08d24a29a8..4b49c45006 100644
--- a/indra/llui/lltimectrl.cpp
+++ b/indra/llui/lltimectrl.cpp
@@ -371,17 +371,17 @@ LLTimeCtrl::EEditingPart LLTimeCtrl::getEditingPart()
 	S32 cur_pos = mEditor->getCursor();
 	LLWString time_str = mEditor->getWText();
 
-	size_t colon_index = time_str.find_first_of(':');
+	S32 colon_index = time_str.find_first_of(':');
 
 	if (cur_pos <= colon_index)
 	{
 		return HOURS;
 	}
-	else if (cur_pos > colon_index && cur_pos <= (time_str.length() - AMPM_LEN))
+	else if (cur_pos > colon_index && cur_pos <= (S32)(time_str.length() - AMPM_LEN))
 	{
 		return MINUTES;
 	}
-	else if (cur_pos > (time_str.length() - AMPM_LEN))
+	else if (cur_pos > (S32)(time_str.length() - AMPM_LEN))
 	{
 		return DAYPART;
 	}
-- 
cgit v1.2.3


From f02d6e7095d526bc7abc75cc796dc4f07c9b18c7 Mon Sep 17 00:00:00 2001
From: Paul ProductEngine <pguslisty@productengine.com>
Date: Wed, 18 May 2011 21:07:06 +0300
Subject: STORM-1202 ADDITIONAL_FIX Workaround for another Windows build issue.

---
 indra/llui/lltimectrl.cpp | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

(limited to 'indra/llui')

diff --git a/indra/llui/lltimectrl.cpp b/indra/llui/lltimectrl.cpp
index 4b49c45006..a77842a392 100644
--- a/indra/llui/lltimectrl.cpp
+++ b/indra/llui/lltimectrl.cpp
@@ -225,7 +225,8 @@ bool LLTimeCtrl::isTimeStringValid(const LLWString &wstr)
 bool LLTimeCtrl::isHoursStringValid(const LLWString& wstr)
 {
 	U32 hours;
-	if ((!LLWStringUtil::convertToU32(wstr, hours) || (hours <= HOURS_MAX)) && wstr.length() < 3)
+	std::string utf8time = wstring_to_utf8str(wstr);
+	if ((!LLStringUtil::convertToU32(utf8time, hours) || (hours <= HOURS_MAX)) && wstr.length() < 3)
 		return true;
 
 	return false;
@@ -234,7 +235,8 @@ bool LLTimeCtrl::isHoursStringValid(const LLWString& wstr)
 bool LLTimeCtrl::isMinutesStringValid(const LLWString& wstr)
 {
 	U32 minutes;
-	if (!LLWStringUtil::convertToU32(wstr, minutes) || (minutes <= MINUTES_MAX) && wstr.length() < 3)
+	std::string utf8time = wstring_to_utf8str(wstr);
+	if (!LLStringUtil::convertToU32(utf8time, minutes) || (minutes <= MINUTES_MAX) && wstr.length() < 3)
 		return true;
 
 	return false;
@@ -243,7 +245,8 @@ bool LLTimeCtrl::isMinutesStringValid(const LLWString& wstr)
 void LLTimeCtrl::validateHours(const LLWString& wstr)
 {
 	U32 hours;
-	if (LLWStringUtil::convertToU32(wstr, hours) && (hours >= HOURS_MIN) && (hours <= HOURS_MAX))
+	std::string utf8time = wstring_to_utf8str(wstr);
+	if (LLStringUtil::convertToU32(utf8time, hours) && (hours >= HOURS_MIN) && (hours <= HOURS_MAX))
 	{
 		mHours = hours;
 	}
@@ -256,7 +259,8 @@ void LLTimeCtrl::validateHours(const LLWString& wstr)
 void LLTimeCtrl::validateMinutes(const LLWString& wstr)
 {
 	U32 minutes;
-	if (LLWStringUtil::convertToU32(wstr, minutes) && (minutes >= MINUTES_MIN) && (minutes <= MINUTES_MAX))
+	std::string utf8time = wstring_to_utf8str(wstr);
+	if (LLStringUtil::convertToU32(utf8time, minutes) && (minutes >= MINUTES_MIN) && (minutes <= MINUTES_MAX))
 	{
 		mMinutes = minutes;
 	}
-- 
cgit v1.2.3


From 657e434fd59139436e8b97e5ecd01ca686e82269 Mon Sep 17 00:00:00 2001
From: Vadim ProductEngine <vsavchuk@productengine.com>
Date: Mon, 30 May 2011 22:34:56 +0300
Subject: STORM-1253 WIP New day cycle editor.

Done:
* Creating new local day cycles.
* Editing existing local day cycles.
* Deleting day cycles.

To do:
* Editing region day cycle, dealing with skies in region scope.
* Handle teleport while editing a day cycle.
* Update UI when a day cycle or sky preset gets deleted.
* Make the time ctrl increase/decrease consistently.
---
 indra/llui/lltimectrl.cpp | 86 +++++++++++++++++++++++++++++++++++++++++------
 indra/llui/lltimectrl.h   | 21 +++++++-----
 2 files changed, 88 insertions(+), 19 deletions(-)

(limited to 'indra/llui')

diff --git a/indra/llui/lltimectrl.cpp b/indra/llui/lltimectrl.cpp
index a77842a392..ce376f001d 100644
--- a/indra/llui/lltimectrl.cpp
+++ b/indra/llui/lltimectrl.cpp
@@ -108,7 +108,7 @@ LLTimeCtrl::LLTimeCtrl(const LLTimeCtrl::Params& p)
 	mEditor = LLUICtrlFactory::create<LLLineEditor> (params);
 	mEditor->setPrevalidateInput(LLTextValidate::validateNonNegativeS32NoSpace);
 	mEditor->setPrevalidate(boost::bind(&LLTimeCtrl::isTimeStringValid, this, _1));
-	mEditor->setText(LLStringExplicit("0:00 AM"));
+	mEditor->setText(LLStringExplicit("12:00 AM"));
 	addChild(mEditor);
 
 	//================= Spin Buttons ==========//
@@ -130,6 +130,66 @@ LLTimeCtrl::LLTimeCtrl(const LLTimeCtrl::Params& p)
 	setUseBoundingRect( TRUE );
 }
 
+F32 LLTimeCtrl::getTime24() const
+{
+	return getHours24() + getMinutes() / 60.0f;
+}
+
+U32 LLTimeCtrl::getHours24() const
+{
+	U32 h = mHours;
+
+	if (h == 12)
+    {
+        h = 0;
+    }
+
+    if (mCurrentDayPeriod == PM)
+    {
+        h += 12;
+    }
+
+    return h;
+}
+
+U32 LLTimeCtrl::getMinutes() const
+{
+	return mMinutes;
+}
+
+void LLTimeCtrl::setTime24(F32 time)
+{
+	time = llclamp(time, 0.0f, 23.99f); // fix out of range values
+
+	U32 h = time;
+	U32 m = llround((time - h) * 60); // fixes values like 4.99999
+
+	// fix rounding error
+	if (m == 60)
+	{
+		m = 0;
+		++h;
+	}
+
+	mCurrentDayPeriod = (h >= 12 ? PM : AM);
+
+	if (h >= 12)
+	{
+		h -= 12;
+	}
+
+	if (h == 0)
+	{
+		h = 12;
+	}
+
+
+	mHours = h;
+	mMinutes = m;
+
+	updateText();
+}
+
 BOOL LLTimeCtrl::handleKeyHere(KEY key, MASK mask)
 {
 	if (mEditor->hasFocus())
@@ -144,6 +204,11 @@ BOOL LLTimeCtrl::handleKeyHere(KEY key, MASK mask)
 			onDownBtn();
 			return TRUE;
 		}
+		if (key == KEY_RETURN)
+		{
+			onCommit();
+			return TRUE;
+		}
 	}
 	return FALSE;
 }
@@ -165,8 +230,8 @@ void LLTimeCtrl::onUpBtn()
 		break;
 	}
 
-	buildTimeString();
-	mEditor->setText(mTimeString);
+	updateText();
+	onCommit();
 }
 
 void LLTimeCtrl::onDownBtn()
@@ -186,15 +251,14 @@ void LLTimeCtrl::onDownBtn()
 		break;
 	}
 
-	buildTimeString();
-	mEditor->setText(mTimeString);
+	updateText();
+	onCommit();
 }
 
 void LLTimeCtrl::onFocusLost()
 {
-	buildTimeString();
-	mEditor->setText(mTimeString);
-
+	updateText();
+	onCommit();
 	LLUICtrl::onFocusLost();
 }
 
@@ -300,6 +364,7 @@ LLWString LLTimeCtrl::getMinutesWString(const LLWString& wstr)
 
 void LLTimeCtrl::increaseMinutes()
 {
+	// *TODO: snap to 5 min
 	if (++mMinutes > MINUTES_MAX)
 	{
 		mMinutes = MINUTES_MIN;
@@ -316,6 +381,7 @@ void LLTimeCtrl::increaseHours()
 
 void LLTimeCtrl::decreaseMinutes()
 {
+	// *TODO: snap to 5 min
 	if (mMinutes-- == MINUTES_MIN)
 	{
 		mMinutes = MINUTES_MAX;
@@ -344,7 +410,7 @@ void LLTimeCtrl::switchDayPeriod()
 	}
 }
 
-void LLTimeCtrl::buildTimeString()
+void LLTimeCtrl::updateText()
 {
 	std::stringstream time_buf;
 	time_buf << mHours << ":";
@@ -367,7 +433,7 @@ void LLTimeCtrl::buildTimeString()
 		break;
 	}
 
-	mTimeString = time_buf.str();
+	mEditor->setText(time_buf.str());
 }
 
 LLTimeCtrl::EEditingPart LLTimeCtrl::getEditingPart()
diff --git a/indra/llui/lltimectrl.h b/indra/llui/lltimectrl.h
index 81d4477da4..aebc5b6eab 100644
--- a/indra/llui/lltimectrl.h
+++ b/indra/llui/lltimectrl.h
@@ -37,6 +37,7 @@ class LLLineEditor;
 class LLTimeCtrl
 : public LLUICtrl
 {
+	LOG_CLASS(LLTimeCtrl);
 public:
 	struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
 	{
@@ -51,13 +52,17 @@ public:
 
 		Params();
 	};
+
+	F32 getTime24() const;		// 0.0 - 24.0
+	U32 getHours24() const;		// 0 - 23
+	U32 getMinutes() const;		// 0 - 59
+
+	void setTime24(F32 time);	// 0.0 - 23.98(3)
+
 protected:
 	LLTimeCtrl(const Params&);
 	friend class LLUICtrlFactory;
 
-	U32 getHours() const	{ return mHours; }
-	U32 getMinutes() const { return mMinutes; }
-
 private:
 
 	enum EDayPeriod
@@ -101,7 +106,7 @@ private:
 
 	void switchDayPeriod();
 
-	void buildTimeString();
+	void updateText();
 
 	EEditingPart getEditingPart();
 
@@ -114,11 +119,9 @@ private:
 	class LLButton*		mUpBtn;
 	class LLButton*		mDownBtn;
 
-	U32 			mHours;
-	U32 			mMinutes;
-	EDayPeriod 		mCurrentDayPeriod;
-
-	std::string	mTimeString;
+	U32 			mHours;				// 1 - 12
+	U32 			mMinutes;			// 0 - 59
+	EDayPeriod 		mCurrentDayPeriod;	// AM/PM
 
 	BOOL			mAllowEdit;
 };
-- 
cgit v1.2.3


From 345a72f47996f9e3ccd7cb1a28060250339be3c8 Mon Sep 17 00:00:00 2001
From: Vadim ProductEngine <vsavchuk@productengine.com>
Date: Mon, 30 May 2011 23:20:04 +0300
Subject: STORM-1253 WIP Fixed a compiler warning.

---
 indra/llui/lltimectrl.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'indra/llui')

diff --git a/indra/llui/lltimectrl.cpp b/indra/llui/lltimectrl.cpp
index ce376f001d..88b11d6c0f 100644
--- a/indra/llui/lltimectrl.cpp
+++ b/indra/llui/lltimectrl.cpp
@@ -161,7 +161,7 @@ void LLTimeCtrl::setTime24(F32 time)
 {
 	time = llclamp(time, 0.0f, 23.99f); // fix out of range values
 
-	U32 h = time;
+	U32 h = (U32) time;
 	U32 m = llround((time - h) * 60); // fixes values like 4.99999
 
 	// fix rounding error
-- 
cgit v1.2.3


From af1b7a4ac87f81b56fe1ff49d29c6bd7c3bffa9c Mon Sep 17 00:00:00 2001
From: Vadim ProductEngine <vsavchuk@productengine.com>
Date: Wed, 1 Jun 2011 16:53:19 +0300
Subject: STORM-1253 WIP Time control: the up/down buttons now work
 consistently across the whole day; time values are snapped to 5 minutes.

---
 indra/llui/lltimectrl.cpp | 292 +++++++++++++++++++++-------------------------
 indra/llui/lltimectrl.h   |  29 ++---
 2 files changed, 148 insertions(+), 173 deletions(-)

(limited to 'indra/llui')

diff --git a/indra/llui/lltimectrl.cpp b/indra/llui/lltimectrl.cpp
index 88b11d6c0f..9ea1e8815e 100644
--- a/indra/llui/lltimectrl.cpp
+++ b/indra/llui/lltimectrl.cpp
@@ -46,9 +46,13 @@ const U32 MINUTES_MIN = 0;
 const U32 MINUTES_MAX = 59;
 const U32 HOURS_MIN = 1;
 const U32 HOURS_MAX = 12;
+const U32 MINUTES_PER_HOUR = 60;
+const U32 MINUTES_PER_DAY = 24 * MINUTES_PER_HOUR;
+
 
 LLTimeCtrl::Params::Params()
 :	label_width("label_width"),
+	snap_to("snap_to"),
 	allow_text_entry("allow_text_entry", true),
 	text_enabled_color("text_enabled_color"),
 	text_disabled_color("text_disabled_color"),
@@ -61,8 +65,8 @@ LLTimeCtrl::LLTimeCtrl(const LLTimeCtrl::Params& p)
 	mLabelBox(NULL),
 	mTextEnabledColor(p.text_enabled_color()),
 	mTextDisabledColor(p.text_disabled_color()),
-	mHours(HOURS_MIN),
-	mMinutes(MINUTES_MIN)
+	mTime(0),
+	mSnapToMin(5)
 {
 	static LLUICachedControl<S32> spinctrl_spacing ("UISpinctrlSpacing", 0);
 	static LLUICachedControl<S32> spinctrl_btn_width ("UISpinctrlBtnWidth", 0);
@@ -132,60 +136,24 @@ LLTimeCtrl::LLTimeCtrl(const LLTimeCtrl::Params& p)
 
 F32 LLTimeCtrl::getTime24() const
 {
-	return getHours24() + getMinutes() / 60.0f;
+	// 0.0 - 23.99;
+	return mTime / 60.0f;
 }
 
 U32 LLTimeCtrl::getHours24() const
 {
-	U32 h = mHours;
-
-	if (h == 12)
-    {
-        h = 0;
-    }
-
-    if (mCurrentDayPeriod == PM)
-    {
-        h += 12;
-    }
-
-    return h;
+	return (U32) getTime24();
 }
 
 U32 LLTimeCtrl::getMinutes() const
 {
-	return mMinutes;
+	return mTime % MINUTES_PER_HOUR;
 }
 
 void LLTimeCtrl::setTime24(F32 time)
 {
 	time = llclamp(time, 0.0f, 23.99f); // fix out of range values
-
-	U32 h = (U32) time;
-	U32 m = llround((time - h) * 60); // fixes values like 4.99999
-
-	// fix rounding error
-	if (m == 60)
-	{
-		m = 0;
-		++h;
-	}
-
-	mCurrentDayPeriod = (h >= 12 ? PM : AM);
-
-	if (h >= 12)
-	{
-		h -= 12;
-	}
-
-	if (h == 0)
-	{
-		h = 12;
-	}
-
-
-	mHours = h;
-	mMinutes = m;
+	mTime = llround(time * MINUTES_PER_HOUR); // fixes values like 4.99999
 
 	updateText();
 }
@@ -264,197 +232,201 @@ void LLTimeCtrl::onFocusLost()
 
 void LLTimeCtrl::onTextEntry(LLLineEditor* line_editor)
 {
-	LLWString time_str = line_editor->getWText();
-	switch(getEditingPart())
+	std::string time_str = line_editor->getText();
+	U32 h12 = parseHours(getHoursString(time_str));
+	U32 m = parseMinutes(getMinutesString(time_str));
+	bool pm = parseAMPM(getAMPMString(time_str));
+
+	if (h12 == 12)
 	{
-	case HOURS:
-		validateHours(getHoursWString(time_str));
-		break;
-	case MINUTES:
-		validateMinutes(getMinutesWString(time_str));
-		break;
-	default:
-		break;
+		h12 = 0;
 	}
+
+	U32 h24 = pm ? h12 + 12 : h12;
+
+	mTime = h24 * MINUTES_PER_HOUR + m;
 }
 
 bool LLTimeCtrl::isTimeStringValid(const LLWString &wstr)
 {
-	if (!isHoursStringValid(getHoursWString(wstr)) || !isMinutesStringValid(getMinutesWString(wstr)) || !isPMAMStringValid(wstr))
-		return false;
+	std::string str = wstring_to_utf8str(wstr);
 
-	return true;
+	return isHoursStringValid(getHoursString(str)) &&
+		isMinutesStringValid(getMinutesString(str)) &&
+		isPMAMStringValid(getAMPMString(str));
 }
 
-bool LLTimeCtrl::isHoursStringValid(const LLWString& wstr)
+void LLTimeCtrl::increaseMinutes()
 {
-	U32 hours;
-	std::string utf8time = wstring_to_utf8str(wstr);
-	if ((!LLStringUtil::convertToU32(utf8time, hours) || (hours <= HOURS_MAX)) && wstr.length() < 3)
-		return true;
+	mTime = (mTime + mSnapToMin) % MINUTES_PER_DAY - (mTime % mSnapToMin);
+}
 
-	return false;
+void LLTimeCtrl::increaseHours()
+{
+	mTime = (mTime + MINUTES_PER_HOUR) % MINUTES_PER_DAY;
 }
 
-bool LLTimeCtrl::isMinutesStringValid(const LLWString& wstr)
+void LLTimeCtrl::decreaseMinutes()
 {
-	U32 minutes;
-	std::string utf8time = wstring_to_utf8str(wstr);
-	if (!LLStringUtil::convertToU32(utf8time, minutes) || (minutes <= MINUTES_MAX) && wstr.length() < 3)
-		return true;
+	if (mTime < mSnapToMin)
+	{
+		mTime = MINUTES_PER_DAY - mTime;
+	}
 
-	return false;
+	mTime -= (mTime % mSnapToMin) ? mTime % mSnapToMin : mSnapToMin;
 }
 
-void LLTimeCtrl::validateHours(const LLWString& wstr)
+void LLTimeCtrl::decreaseHours()
 {
-	U32 hours;
-	std::string utf8time = wstring_to_utf8str(wstr);
-	if (LLStringUtil::convertToU32(utf8time, hours) && (hours >= HOURS_MIN) && (hours <= HOURS_MAX))
+	if (mTime < MINUTES_PER_HOUR)
 	{
-		mHours = hours;
+		mTime = 23 * MINUTES_PER_HOUR + mTime;
 	}
 	else
 	{
-		mHours = HOURS_MIN;
+		mTime -= MINUTES_PER_HOUR;
 	}
 }
 
-void LLTimeCtrl::validateMinutes(const LLWString& wstr)
+bool LLTimeCtrl::isPM() const
 {
-	U32 minutes;
-	std::string utf8time = wstring_to_utf8str(wstr);
-	if (LLStringUtil::convertToU32(utf8time, minutes) && (minutes >= MINUTES_MIN) && (minutes <= MINUTES_MAX))
+	return mTime >= (MINUTES_PER_DAY / 2);
+}
+
+void LLTimeCtrl::switchDayPeriod()
+{
+	if (isPM())
 	{
-		mMinutes = minutes;
+		mTime -= MINUTES_PER_DAY / 2;
 	}
 	else
 	{
-		mMinutes = MINUTES_MIN;
+		mTime += MINUTES_PER_DAY / 2;
 	}
 }
 
-bool LLTimeCtrl::isPMAMStringValid(const LLWString &wstr)
+void LLTimeCtrl::updateText()
+{
+	U32 h24 = getHours24();
+	U32 m = getMinutes();
+	U32 h12 = h24 > 12 ? h24 - 12 : h24;
+
+	if (h12 == 0)
+		h12 = 12;
+
+	mEditor->setText(llformat("%d:%02d %s", h12, m, isPM() ? "PM":"AM"));
+}
+
+LLTimeCtrl::EEditingPart LLTimeCtrl::getEditingPart()
 {
-	S32 len = wstr.length();
+	S32 cur_pos = mEditor->getCursor();
+	std::string time_str = mEditor->getText();
 
-	bool valid = (wstr[--len] == 'M') && (wstr[--len] == 'P' || wstr[len] == 'A');
+	S32 colon_index = time_str.find_first_of(':');
 
-	return valid;
+	if (cur_pos <= colon_index)
+	{
+		return HOURS;
+	}
+	else if (cur_pos > colon_index && cur_pos <= (S32)(time_str.length() - AMPM_LEN))
+	{
+		return MINUTES;
+	}
+	else if (cur_pos > (S32)(time_str.length() - AMPM_LEN))
+	{
+		return DAYPART;
+	}
+
+	return NONE;
 }
 
-LLWString LLTimeCtrl::getHoursWString(const LLWString& wstr)
+// static
+std::string LLTimeCtrl::getHoursString(const std::string& str)
 {
-	size_t colon_index = wstr.find_first_of(':');
-	LLWString hours_str = wstr.substr(0, colon_index);
+	size_t colon_index = str.find_first_of(':');
+	std::string hours_str = str.substr(0, colon_index);
 
 	return hours_str;
 }
 
-LLWString LLTimeCtrl::getMinutesWString(const LLWString& wstr)
+// static
+std::string LLTimeCtrl::getMinutesString(const std::string& str)
 {
-	size_t colon_index = wstr.find_first_of(':');
+	size_t colon_index = str.find_first_of(':');
 	++colon_index;
 
-	int minutes_len = wstr.length() - colon_index - AMPM_LEN;
-	LLWString minutes_str = wstr.substr(colon_index, minutes_len);
+	int minutes_len = str.length() - colon_index - AMPM_LEN;
+	std::string minutes_str = str.substr(colon_index, minutes_len);
 
 	return minutes_str;
 }
 
-void LLTimeCtrl::increaseMinutes()
+// static
+std::string LLTimeCtrl::getAMPMString(const std::string& str)
 {
-	// *TODO: snap to 5 min
-	if (++mMinutes > MINUTES_MAX)
-	{
-		mMinutes = MINUTES_MIN;
-	}
+	return str.substr(str.size() - 2, 2); // returns last two characters
 }
 
-void LLTimeCtrl::increaseHours()
+// static
+bool LLTimeCtrl::isHoursStringValid(const std::string& str)
 {
-	if (++mHours > HOURS_MAX)
-	{
-		mHours = HOURS_MIN;
-	}
-}
+	U32 hours;
+	if ((!LLStringUtil::convertToU32(str, hours) || (hours <= HOURS_MAX)) && str.length() < 3)
+		return true;
 
-void LLTimeCtrl::decreaseMinutes()
-{
-	// *TODO: snap to 5 min
-	if (mMinutes-- == MINUTES_MIN)
-	{
-		mMinutes = MINUTES_MAX;
-	}
+	return false;
 }
 
-void LLTimeCtrl::decreaseHours()
+// static
+bool LLTimeCtrl::isMinutesStringValid(const std::string& str)
 {
-	if (mHours-- == HOURS_MIN)
-	{
-		mHours = HOURS_MAX;
-		switchDayPeriod();
-	}
+	U32 minutes;
+	if (!LLStringUtil::convertToU32(str, minutes) || (minutes <= MINUTES_MAX) && str.length() < 3)
+		return true;
+
+	return false;
 }
 
-void LLTimeCtrl::switchDayPeriod()
+// static
+bool LLTimeCtrl::isPMAMStringValid(const std::string& str)
 {
-	switch (mCurrentDayPeriod)
-	{
-	case AM:
-		mCurrentDayPeriod = PM;
-		break;
-	case PM:
-		mCurrentDayPeriod = AM;
-		break;
-	}
+	S32 len = str.length();
+
+	bool valid = (str[--len] == 'M') && (str[--len] == 'P' || str[len] == 'A');
+
+	return valid;
 }
 
-void LLTimeCtrl::updateText()
+// static
+U32 LLTimeCtrl::parseHours(const std::string& str)
 {
-	std::stringstream time_buf;
-	time_buf << mHours << ":";
-
-	if (mMinutes < 10)
+	U32 hours;
+	if (LLStringUtil::convertToU32(str, hours) && (hours >= HOURS_MIN) && (hours <= HOURS_MAX))
 	{
-		time_buf << "0";
+		return hours;
 	}
-
-	time_buf << mMinutes;
-	time_buf << " ";
-
-	switch (mCurrentDayPeriod)
+	else
 	{
-	case AM:
-		time_buf << "AM";
-		break;
-	case PM:
-		time_buf << "PM";
-		break;
+		return HOURS_MIN;
 	}
-
-	mEditor->setText(time_buf.str());
 }
 
-LLTimeCtrl::EEditingPart LLTimeCtrl::getEditingPart()
+// static
+U32 LLTimeCtrl::parseMinutes(const std::string& str)
 {
-	S32 cur_pos = mEditor->getCursor();
-	LLWString time_str = mEditor->getWText();
-
-	S32 colon_index = time_str.find_first_of(':');
-
-	if (cur_pos <= colon_index)
-	{
-		return HOURS;
-	}
-	else if (cur_pos > colon_index && cur_pos <= (S32)(time_str.length() - AMPM_LEN))
+	U32 minutes;
+	if (LLStringUtil::convertToU32(str, minutes) && (minutes >= MINUTES_MIN) && (minutes <= MINUTES_MAX))
 	{
-		return MINUTES;
+		return minutes;
 	}
-	else if (cur_pos > (S32)(time_str.length() - AMPM_LEN))
+	else
 	{
-		return DAYPART;
+		return MINUTES_MIN;
 	}
+}
 
-	return NONE;
+// static
+bool LLTimeCtrl::parseAMPM(const std::string& str)
+{
+	return str == "PM";
 }
diff --git a/indra/llui/lltimectrl.h b/indra/llui/lltimectrl.h
index aebc5b6eab..b5f268c76a 100644
--- a/indra/llui/lltimectrl.h
+++ b/indra/llui/lltimectrl.h
@@ -42,6 +42,7 @@ public:
 	struct Params : public LLInitParam::Block<Params, LLUICtrl::Params>
 	{
 		Optional<S32> label_width;
+		Optional<S32> snap_to;
 		Optional<bool> allow_text_entry;
 
 		Optional<LLUIColor> text_enabled_color;
@@ -84,32 +85,35 @@ private:
 
 	void	onUpBtn();
 	void	onDownBtn();
-
 	void	onTextEntry(LLLineEditor* line_editor);
 
-	void	validateHours(const LLWString& wstr);
-	void	validateMinutes(const LLWString& wstr);
 	bool	isTimeStringValid(const LLWString& wstr);
 
-	bool	isPMAMStringValid(const LLWString& wstr);
-	bool	isHoursStringValid(const LLWString& wstr);
-	bool 	isMinutesStringValid(const LLWString& wstr);
-
-	LLWString getHoursWString(const LLWString& wstr);
-	LLWString getMinutesWString(const LLWString& wstr);
-
 	void increaseMinutes();
 	void increaseHours();
 
 	void decreaseMinutes();
 	void decreaseHours();
 
+	bool isPM() const;
 	void switchDayPeriod();
 
 	void updateText();
 
 	EEditingPart getEditingPart();
 
+	static std::string getHoursString(const std::string& str);
+	static std::string getMinutesString(const std::string& str);
+	static std::string getAMPMString(const std::string& str);
+
+	static bool isHoursStringValid(const std::string& str);
+	static bool isMinutesStringValid(const std::string& str);
+	static bool isPMAMStringValid(const std::string& str);
+
+	static U32		parseHours(const std::string& str);
+	static U32		parseMinutes(const std::string& str);
+	static bool		parseAMPM(const std::string& str);
+
 	class LLTextBox*	mLabelBox;
 
 	class LLLineEditor*	mEditor;
@@ -119,9 +123,8 @@ private:
 	class LLButton*		mUpBtn;
 	class LLButton*		mDownBtn;
 
-	U32 			mHours;				// 1 - 12
-	U32 			mMinutes;			// 0 - 59
-	EDayPeriod 		mCurrentDayPeriod;	// AM/PM
+	U32				mTime;				// minutes since midnight: 0 - 1439
+	U32				mSnapToMin;			// interval in minutes to snap to
 
 	BOOL			mAllowEdit;
 };
-- 
cgit v1.2.3