PageRenderTime 87ms CodeModel.GetById 15ms app.highlight 60ms RepoModel.GetById 1ms app.codeStats 1ms

/indra/llui/lltimectrl.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 432 lines | 329 code | 65 blank | 38 comment | 41 complexity | eb24a6e55a7476a6f81e1b8a59a2073a MD5 | raw file
  1/**
  2 * @file lltimectrl.cpp
  3 * @brief LLTimeCtrl base class
  4 *
  5 * $LicenseInfo:firstyear=2001&license=viewerlgpl$
  6 * Second Life Viewer Source Code
  7 * Copyright (C) 2011, Linden Research, Inc.
  8 *
  9 * This library is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU Lesser General Public
 11 * License as published by the Free Software Foundation;
 12 * version 2.1 of the License only.
 13 *
 14 * This library is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17 * Lesser General Public License for more details.
 18 *
 19 * You should have received a copy of the GNU Lesser General Public
 20 * License along with this library; if not, write to the Free Software
 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 22 *
 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 24 * $/LicenseInfo$
 25 */
 26
 27#include "linden_common.h"
 28
 29#include "lltimectrl.h"
 30
 31#include "llui.h"
 32#include "lluiconstants.h"
 33
 34#include "llbutton.h"
 35#include "llfontgl.h"
 36#include "lllineeditor.h"
 37#include "llkeyboard.h"
 38#include "llstring.h"
 39#include "lltextbox.h"
 40#include "lluictrlfactory.h"
 41
 42static LLDefaultChildRegistry::Register<LLTimeCtrl> time_r("time");
 43
 44const U32 AMPM_LEN = 3;
 45const U32 MINUTES_MIN = 0;
 46const U32 MINUTES_MAX = 59;
 47const U32 HOURS_MIN = 1;
 48const U32 HOURS_MAX = 12;
 49const U32 MINUTES_PER_HOUR = 60;
 50const U32 MINUTES_PER_DAY = 24 * MINUTES_PER_HOUR;
 51
 52
 53LLTimeCtrl::Params::Params()
 54:	label_width("label_width"),
 55	snap_to("snap_to"),
 56	allow_text_entry("allow_text_entry", true),
 57	text_enabled_color("text_enabled_color"),
 58	text_disabled_color("text_disabled_color"),
 59	up_button("up_button"),
 60	down_button("down_button")
 61{}
 62
 63LLTimeCtrl::LLTimeCtrl(const LLTimeCtrl::Params& p)
 64:	LLUICtrl(p),
 65	mLabelBox(NULL),
 66	mTextEnabledColor(p.text_enabled_color()),
 67	mTextDisabledColor(p.text_disabled_color()),
 68	mTime(0),
 69	mSnapToMin(5)
 70{
 71	static LLUICachedControl<S32> spinctrl_spacing ("UISpinctrlSpacing", 0);
 72	static LLUICachedControl<S32> spinctrl_btn_width ("UISpinctrlBtnWidth", 0);
 73	static LLUICachedControl<S32> spinctrl_btn_height ("UISpinctrlBtnHeight", 0);
 74	S32 centered_top = getRect().getHeight();
 75	S32 centered_bottom = getRect().getHeight() - 2 * spinctrl_btn_height;
 76	S32 label_width = llclamp(p.label_width(), 0, llmax(0, getRect().getWidth() - 40));
 77	S32 editor_left = label_width + spinctrl_spacing;
 78
 79	//================= Label =================//
 80	if( !p.label().empty() )
 81	{
 82		LLRect label_rect( 0, centered_top, label_width, centered_bottom );
 83		LLTextBox::Params params;
 84		params.name("TimeCtrl Label");
 85		params.rect(label_rect);
 86		params.initial_value(p.label());
 87		if (p.font.isProvided())
 88		{
 89			params.font(p.font);
 90		}
 91		mLabelBox = LLUICtrlFactory::create<LLTextBox> (params);
 92		addChild(mLabelBox);
 93
 94		editor_left = label_rect.mRight + spinctrl_spacing;
 95	}
 96
 97	S32 editor_right = getRect().getWidth() - spinctrl_btn_width - spinctrl_spacing;
 98
 99	//================= Editor ================//
100	LLRect editor_rect( editor_left, centered_top, editor_right, centered_bottom );
101	LLLineEditor::Params params;
102	params.name("SpinCtrl Editor");
103	params.rect(editor_rect);
104	if (p.font.isProvided())
105	{
106		params.font(p.font);
107	}
108
109	params.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM);
110	params.max_length.chars(8);
111	params.keystroke_callback(boost::bind(&LLTimeCtrl::onTextEntry, this, _1));
112	mEditor = LLUICtrlFactory::create<LLLineEditor> (params);
113	mEditor->setPrevalidateInput(LLTextValidate::validateNonNegativeS32NoSpace);
114	mEditor->setPrevalidate(boost::bind(&LLTimeCtrl::isTimeStringValid, this, _1));
115	mEditor->setText(LLStringExplicit("12:00 AM"));
116	addChild(mEditor);
117
118	//================= Spin Buttons ==========//
119	LLButton::Params up_button_params(p.up_button);
120	up_button_params.rect = LLRect(editor_right + 1, getRect().getHeight(), editor_right + spinctrl_btn_width, getRect().getHeight() - spinctrl_btn_height);
121
122	up_button_params.click_callback.function(boost::bind(&LLTimeCtrl::onUpBtn, this));
123	up_button_params.mouse_held_callback.function(boost::bind(&LLTimeCtrl::onUpBtn, this));
124	mUpBtn = LLUICtrlFactory::create<LLButton>(up_button_params);
125	addChild(mUpBtn);
126
127	LLButton::Params down_button_params(p.down_button);
128	down_button_params.rect = LLRect(editor_right + 1, getRect().getHeight() - spinctrl_btn_height, editor_right + spinctrl_btn_width, getRect().getHeight() - 2 * spinctrl_btn_height);
129	down_button_params.click_callback.function(boost::bind(&LLTimeCtrl::onDownBtn, this));
130	down_button_params.mouse_held_callback.function(boost::bind(&LLTimeCtrl::onDownBtn, this));
131	mDownBtn = LLUICtrlFactory::create<LLButton>(down_button_params);
132	addChild(mDownBtn);
133
134	setUseBoundingRect( TRUE );
135}
136
137F32 LLTimeCtrl::getTime24() const
138{
139	// 0.0 - 23.99;
140	return mTime / 60.0f;
141}
142
143U32 LLTimeCtrl::getHours24() const
144{
145	return (U32) getTime24();
146}
147
148U32 LLTimeCtrl::getMinutes() const
149{
150	return mTime % MINUTES_PER_HOUR;
151}
152
153void LLTimeCtrl::setTime24(F32 time)
154{
155	time = llclamp(time, 0.0f, 23.99f); // fix out of range values
156	mTime = llround(time * MINUTES_PER_HOUR); // fixes values like 4.99999
157
158	updateText();
159}
160
161BOOL LLTimeCtrl::handleKeyHere(KEY key, MASK mask)
162{
163	if (mEditor->hasFocus())
164	{
165		if(key == KEY_UP)
166		{
167			onUpBtn();
168			return TRUE;
169		}
170		if(key == KEY_DOWN)
171		{
172			onDownBtn();
173			return TRUE;
174		}
175		if (key == KEY_RETURN)
176		{
177			onCommit();
178			return TRUE;
179		}
180	}
181	return FALSE;
182}
183
184void LLTimeCtrl::onUpBtn()
185{
186	switch(getEditingPart())
187	{
188	case HOURS:
189		increaseHours();
190		break;
191	case MINUTES:
192		increaseMinutes();
193		break;
194	case DAYPART:
195		switchDayPeriod();
196		break;
197	default:
198		break;
199	}
200
201	updateText();
202	onCommit();
203}
204
205void LLTimeCtrl::onDownBtn()
206{
207	switch(getEditingPart())
208	{
209	case HOURS:
210		decreaseHours();
211		break;
212	case MINUTES:
213		decreaseMinutes();
214		break;
215	case DAYPART:
216		switchDayPeriod();
217		break;
218	default:
219		break;
220	}
221
222	updateText();
223	onCommit();
224}
225
226void LLTimeCtrl::onFocusLost()
227{
228	updateText();
229	onCommit();
230	LLUICtrl::onFocusLost();
231}
232
233void LLTimeCtrl::onTextEntry(LLLineEditor* line_editor)
234{
235	std::string time_str = line_editor->getText();
236	U32 h12 = parseHours(getHoursString(time_str));
237	U32 m = parseMinutes(getMinutesString(time_str));
238	bool pm = parseAMPM(getAMPMString(time_str));
239
240	if (h12 == 12)
241	{
242		h12 = 0;
243	}
244
245	U32 h24 = pm ? h12 + 12 : h12;
246
247	mTime = h24 * MINUTES_PER_HOUR + m;
248}
249
250bool LLTimeCtrl::isTimeStringValid(const LLWString &wstr)
251{
252	std::string str = wstring_to_utf8str(wstr);
253
254	return isHoursStringValid(getHoursString(str)) &&
255		isMinutesStringValid(getMinutesString(str)) &&
256		isPMAMStringValid(getAMPMString(str));
257}
258
259void LLTimeCtrl::increaseMinutes()
260{
261	mTime = (mTime + mSnapToMin) % MINUTES_PER_DAY - (mTime % mSnapToMin);
262}
263
264void LLTimeCtrl::increaseHours()
265{
266	mTime = (mTime + MINUTES_PER_HOUR) % MINUTES_PER_DAY;
267}
268
269void LLTimeCtrl::decreaseMinutes()
270{
271	if (mTime < mSnapToMin)
272	{
273		mTime = MINUTES_PER_DAY - mTime;
274	}
275
276	mTime -= (mTime % mSnapToMin) ? mTime % mSnapToMin : mSnapToMin;
277}
278
279void LLTimeCtrl::decreaseHours()
280{
281	if (mTime < MINUTES_PER_HOUR)
282	{
283		mTime = 23 * MINUTES_PER_HOUR + mTime;
284	}
285	else
286	{
287		mTime -= MINUTES_PER_HOUR;
288	}
289}
290
291bool LLTimeCtrl::isPM() const
292{
293	return mTime >= (MINUTES_PER_DAY / 2);
294}
295
296void LLTimeCtrl::switchDayPeriod()
297{
298	if (isPM())
299	{
300		mTime -= MINUTES_PER_DAY / 2;
301	}
302	else
303	{
304		mTime += MINUTES_PER_DAY / 2;
305	}
306}
307
308void LLTimeCtrl::updateText()
309{
310	U32 h24 = getHours24();
311	U32 m = getMinutes();
312	U32 h12 = h24 > 12 ? h24 - 12 : h24;
313
314	if (h12 == 0)
315		h12 = 12;
316
317	mEditor->setText(llformat("%d:%02d %s", h12, m, isPM() ? "PM":"AM"));
318}
319
320LLTimeCtrl::EEditingPart LLTimeCtrl::getEditingPart()
321{
322	S32 cur_pos = mEditor->getCursor();
323	std::string time_str = mEditor->getText();
324
325	S32 colon_index = time_str.find_first_of(':');
326
327	if (cur_pos <= colon_index)
328	{
329		return HOURS;
330	}
331	else if (cur_pos > colon_index && cur_pos <= (S32)(time_str.length() - AMPM_LEN))
332	{
333		return MINUTES;
334	}
335	else if (cur_pos > (S32)(time_str.length() - AMPM_LEN))
336	{
337		return DAYPART;
338	}
339
340	return NONE;
341}
342
343// static
344std::string LLTimeCtrl::getHoursString(const std::string& str)
345{
346	size_t colon_index = str.find_first_of(':');
347	std::string hours_str = str.substr(0, colon_index);
348
349	return hours_str;
350}
351
352// static
353std::string LLTimeCtrl::getMinutesString(const std::string& str)
354{
355	size_t colon_index = str.find_first_of(':');
356	++colon_index;
357
358	int minutes_len = str.length() - colon_index - AMPM_LEN;
359	std::string minutes_str = str.substr(colon_index, minutes_len);
360
361	return minutes_str;
362}
363
364// static
365std::string LLTimeCtrl::getAMPMString(const std::string& str)
366{
367	return str.substr(str.size() - 2, 2); // returns last two characters
368}
369
370// static
371bool LLTimeCtrl::isHoursStringValid(const std::string& str)
372{
373	U32 hours;
374	if ((!LLStringUtil::convertToU32(str, hours) || (hours <= HOURS_MAX)) && str.length() < 3)
375		return true;
376
377	return false;
378}
379
380// static
381bool LLTimeCtrl::isMinutesStringValid(const std::string& str)
382{
383	U32 minutes;
384	if (!LLStringUtil::convertToU32(str, minutes) || (minutes <= MINUTES_MAX) && str.length() < 3)
385		return true;
386
387	return false;
388}
389
390// static
391bool LLTimeCtrl::isPMAMStringValid(const std::string& str)
392{
393	S32 len = str.length();
394
395	bool valid = (str[--len] == 'M') && (str[--len] == 'P' || str[len] == 'A');
396
397	return valid;
398}
399
400// static
401U32 LLTimeCtrl::parseHours(const std::string& str)
402{
403	U32 hours;
404	if (LLStringUtil::convertToU32(str, hours) && (hours >= HOURS_MIN) && (hours <= HOURS_MAX))
405	{
406		return hours;
407	}
408	else
409	{
410		return HOURS_MIN;
411	}
412}
413
414// static
415U32 LLTimeCtrl::parseMinutes(const std::string& str)
416{
417	U32 minutes;
418	if (LLStringUtil::convertToU32(str, minutes) && (minutes >= MINUTES_MIN) && (minutes <= MINUTES_MAX))
419	{
420		return minutes;
421	}
422	else
423	{
424		return MINUTES_MIN;
425	}
426}
427
428// static
429bool LLTimeCtrl::parseAMPM(const std::string& str)
430{
431	return str == "PM";
432}