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