diff options
Diffstat (limited to 'indra/llui')
30 files changed, 1673 insertions, 238 deletions
diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index 684e393cba..0bbdcfd6ff 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -28,6 +28,8 @@ include_directories( set(llui_SOURCE_FILES llaccordionctrl.cpp llaccordionctrltab.cpp + llbadge.cpp + llbadgeowner.cpp llbutton.cpp llcheckboxctrl.cpp llclipboard.cpp @@ -93,6 +95,7 @@ set(llui_SOURCE_FILES lltextparser.cpp lltextutil.cpp lltextvalidate.cpp + lltimectrl.cpp lltransutil.cpp lltoggleablemenu.cpp lltooltip.cpp @@ -119,6 +122,8 @@ set(llui_HEADER_FILES llaccordionctrl.h llaccordionctrltab.h + llbadge.h + llbadgeowner.h llbutton.h llcallbackmap.h llcheckboxctrl.h @@ -191,6 +196,7 @@ set(llui_HEADER_FILES lltextparser.h lltextutil.h lltextvalidate.h + lltimectrl.h lltoggleablemenu.h lltooltip.h lltransutil.h @@ -245,11 +251,11 @@ target_link_libraries(llui ) # Add tests -if (LL_TESTS) - include(LLAddBuildTest) - SET(llui_TEST_SOURCE_FILES - llurlmatch.cpp - llurlentry.cpp - ) - LL_ADD_PROJECT_UNIT_TESTS(llui "${llui_TEST_SOURCE_FILES}") -endif (LL_TESTS)
\ No newline at end of file +if(LL_TESTS) + include(LLAddBuildTest) + SET(llui_TEST_SOURCE_FILES + llurlmatch.cpp + llurlentry.cpp + ) + LL_ADD_PROJECT_UNIT_TESTS(llui "${llui_TEST_SOURCE_FILES}") +endif(LL_TESTS) diff --git a/indra/llui/llaccordionctrltab.cpp b/indra/llui/llaccordionctrltab.cpp index 9e4849c58b..6afe276379 100644 --- a/indra/llui/llaccordionctrltab.cpp +++ b/indra/llui/llaccordionctrltab.cpp @@ -1022,7 +1022,7 @@ void LLAccordionCtrlTab::updateLayout ( const LLRect& child_rect ) S32 panel_width = child_rect.getWidth(); static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0); - if(mScrollbar->getVisible() != false) + if(mScrollbar && mScrollbar->getVisible() != false) { panel_top+=mScrollbar->getDocPos(); panel_width-=scrollbar_size; diff --git a/indra/llui/llbadge.cpp b/indra/llui/llbadge.cpp new file mode 100644 index 0000000000..c28a947a7f --- /dev/null +++ b/indra/llui/llbadge.cpp @@ -0,0 +1,274 @@ +/** + * @file llbadge.cpp + * @brief Implementation for badges + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, 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$ + */ + +#define LLBADGE_CPP +#include "llbadge.h" + +#include "lluictrlfactory.h" + + +static LLDefaultChildRegistry::Register<LLBadge> r("badge"); + +// Compiler optimization, generate extern template +template class LLBadge* LLView::getChild<class LLBadge>(const std::string& name, BOOL recurse) const; + + +LLBadge::Params::Params() + : image("image") + , border_image("border_image") + , border_color("border_color") + , image_color("image_color") + , label("label") + , label_color("label_color") + , location("location", LLRelPos::TOP_LEFT) + , location_percent_hcenter("location_percent_hcenter") + , location_percent_vcenter("location_percent_vcenter") + , padding_horiz("padding_horiz") + , padding_vert("padding_vert") +{ + // We set a name here so the name isn't necessary in any xml files that use badges + name = "badge"; +} + +bool LLBadge::Params::equals(const Params& a) const +{ + bool comp = true; + + // skip owner in comparison on purpose + + comp &= (border_image() == a.border_image()); + comp &= (border_color() == a.border_color()); + comp &= (image() == a.image()); + comp &= (image_color() == a.image_color()); + comp &= (label() == a.label()); + comp &= (label_color() == a.label_color()); + comp &= (location() == a.location()); + comp &= (location_percent_hcenter() == a.location_percent_hcenter()); + comp &= (location_percent_vcenter() == a.location_percent_vcenter()); + comp &= (padding_horiz() == a.padding_horiz()); + comp &= (padding_vert() == a.padding_vert()); + + return comp; +} + +LLBadge::LLBadge(const LLBadge::Params& p) + : LLUICtrl(p) + , mOwner(p.owner) + , mBorderImage(p.border_image) + , mBorderColor(p.border_color) + , mGLFont(p.font) + , mImage(p.image) + , mImageColor(p.image_color) + , mLabel(p.label) + , mLabelColor(p.label_color) + , mLocation(p.location) + , mLocationPercentHCenter(0.5f) + , mLocationPercentVCenter(0.5f) + , mPaddingHoriz(p.padding_horiz) + , mPaddingVert(p.padding_vert) +{ + if (mImage.isNull()) + { + llwarns << "Badge: " << getName() << " with no image!" << llendl; + } + + // + // The following logic is to set the mLocationPercentHCenter and mLocationPercentVCenter + // based on the Location enum and our horizontal and vertical location percentages. The + // draw code then uses this on the owner rectangle to compute the screen location for + // the badge. + // + + if (!LLRelPos::IsCenter(mLocation)) + { + F32 h_center = p.location_percent_hcenter * 0.01f; + F32 v_center = p.location_percent_vcenter * 0.01f; + + if (LLRelPos::IsRight(mLocation)) + { + mLocationPercentHCenter = 0.5f * (1.0f + h_center); + } + else if (LLRelPos::IsLeft(mLocation)) + { + mLocationPercentHCenter = 0.5f * (1.0f - h_center); + } + + if (LLRelPos::IsTop(mLocation)) + { + mLocationPercentVCenter = 0.5f * (1.0f + v_center); + } + else if (LLRelPos::IsBottom(mLocation)) + { + mLocationPercentVCenter = 0.5f * (1.0f - v_center); + } + } +} + +LLBadge::~LLBadge() +{ +} + +void LLBadge::setLabel(const LLStringExplicit& label) +{ + mLabel = label; +} + +// +// This is a fallback function to render a rectangle for badges without a valid image +// +void renderBadgeBackground(F32 centerX, F32 centerY, F32 width, F32 height, const LLColor4U &color) +{ + gGL.pushUIMatrix(); + gGL.loadUIIdentity(); + gGL.setSceneBlendType(LLRender::BT_REPLACE); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + gGL.color4ubv(color.mV); + gGL.texCoord2i(0, 0); + + F32 x = LLFontGL::sCurOrigin.mX + centerX - width * 0.5f; + F32 y = LLFontGL::sCurOrigin.mY + centerY - height * 0.5f; + + LLRectf screen_rect(llround(x), + llround(y), + llround(x) + width, + llround(y) + height); + + LLVector3 vertices[4]; + vertices[0] = LLVector3(screen_rect.mRight, screen_rect.mTop, 1.0f); + vertices[1] = LLVector3(screen_rect.mLeft, screen_rect.mTop, 1.0f); + vertices[2] = LLVector3(screen_rect.mLeft, screen_rect.mBottom, 1.0f); + vertices[3] = LLVector3(screen_rect.mRight, screen_rect.mBottom, 1.0f); + + gGL.begin(LLRender::QUADS); + { + gGL.vertexBatchPreTransformed(vertices, 4); + } + gGL.end(); + + gGL.popUIMatrix(); +} + + +// virtual +void LLBadge::draw() +{ + if (!mLabel.empty()) + { + LLView* owner_view = mOwner.get(); + + if (owner_view) + { + // + // Calculate badge position based on owner + // + + LLRect owner_rect; + owner_view->localRectToOtherView(owner_view->getLocalRect(), & owner_rect, this); + + F32 badge_center_x = owner_rect.mLeft + owner_rect.getWidth() * mLocationPercentHCenter; + F32 badge_center_y = owner_rect.mBottom + owner_rect.getHeight() * mLocationPercentVCenter; + + // + // Calculate badge size based on label text + // + + LLWString badge_label_wstring = mLabel; + + S32 badge_label_begin_offset = 0; + S32 badge_char_length = S32_MAX; + S32 badge_pixel_length = S32_MAX; + F32 *right_position_out = NULL; + BOOL do_not_use_ellipses = false; + + F32 badge_width = (2.0f * mPaddingHoriz) + + mGLFont->getWidthF32(badge_label_wstring.c_str(), badge_label_begin_offset, badge_char_length); + + F32 badge_height = (2.0f * mPaddingVert) + mGLFont->getLineHeight(); + + // + // Draw button image, if available. + // Otherwise draw basic rectangular button. + // + + F32 alpha = getDrawContext().mAlpha; + + if (!mImage.isNull()) + { + F32 badge_x = badge_center_x - badge_width * 0.5f; + F32 badge_y = badge_center_y - badge_height * 0.5f; + + mImage->drawSolid((S32) badge_x, (S32) badge_y, (S32) badge_width, (S32) badge_height, mImageColor % alpha); + + if (!mBorderImage.isNull()) + { + mBorderImage->drawSolid((S32) badge_x, (S32) badge_y, (S32) badge_width, (S32) badge_height, mBorderColor % alpha); + } + } + else + { + lldebugs << "No image for badge " << getName() << " on owner " << owner_view->getName() << llendl; + + renderBadgeBackground(badge_center_x, badge_center_y, + badge_width, badge_height, + mImageColor % alpha); + } + + // + // Draw the label + // + + mGLFont->render(badge_label_wstring, badge_label_begin_offset, + badge_center_x, badge_center_y, + mLabelColor % alpha, + LLFontGL::HCENTER, LLFontGL::VCENTER, // centered around the position + LLFontGL::NORMAL, // normal text (not bold, italics, etc.) + LLFontGL::DROP_SHADOW_SOFT, + badge_char_length, badge_pixel_length, + right_position_out, do_not_use_ellipses); + } + } +} + + +namespace LLInitParam +{ + void TypeValues<LLRelPos::Location>::declareValues() + { + declare("bottom", LLRelPos::BOTTOM); + declare("bottom_left", LLRelPos::BOTTOM_LEFT); + declare("bottom_right", LLRelPos::BOTTOM_RIGHT); + declare("center", LLRelPos::CENTER); + declare("left", LLRelPos::LEFT); + declare("right", LLRelPos::RIGHT); + declare("top", LLRelPos::TOP); + declare("top_left", LLRelPos::TOP_LEFT); + declare("top_right", LLRelPos::TOP_RIGHT); + } +} + + +// eof diff --git a/indra/llui/llbadge.h b/indra/llui/llbadge.h new file mode 100644 index 0000000000..0f923ef01b --- /dev/null +++ b/indra/llui/llbadge.h @@ -0,0 +1,159 @@ +/** + * @file llbadge.h + * @brief Header for badges + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, 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 LL_LLBADGE_H +#define LL_LLBADGE_H + +#include <string> + +#include "lluicolor.h" +#include "lluictrl.h" +#include "llstring.h" +#include "lluiimage.h" +#include "llview.h" + +// +// Declarations +// + +class LLUICtrlFactory; +class LLFontGL; + +// +// Relative Position Alignment +// + +namespace LLRelPos +{ + enum Location + { + CENTER = 0, + + LEFT = (1 << 0), + RIGHT = (1 << 1), + + TOP = (1 << 2), + BOTTOM = (1 << 3), + + BOTTOM_LEFT = (BOTTOM | LEFT), + BOTTOM_RIGHT = (BOTTOM | RIGHT), + + TOP_LEFT = (TOP | LEFT), + TOP_RIGHT = (TOP | RIGHT), + }; + + inline bool IsBottom(Location relPos) { return (relPos & BOTTOM) == BOTTOM; } + inline bool IsCenter(Location relPos) { return (relPos == CENTER); } + inline bool IsLeft(Location relPos) { return (relPos & LEFT) == LEFT; } + inline bool IsRight(Location relPos) { return (relPos & RIGHT) == RIGHT; } + inline bool IsTop(Location relPos) { return (relPos & TOP) == TOP; } +} + +// NOTE: This needs to occur before Optional<LLRelPos::Location> declaration for proper compilation. +namespace LLInitParam +{ + template<> + struct TypeValues<LLRelPos::Location> : public TypeValuesHelper<LLRelPos::Location> + { + static void declareValues(); + }; +} + +// +// Classes +// + +class LLBadge +: public LLUICtrl +{ +public: + struct Params + : public LLInitParam::Block<Params, LLUICtrl::Params> + { + Optional< LLHandle<LLView> > owner; // Mandatory in code but not in xml + + Optional< LLUIImage* > border_image; + Optional< LLUIColor > border_color; + + Optional< LLUIImage* > image; + Optional< LLUIColor > image_color; + + Optional< std::string > label; + Optional< LLUIColor > label_color; + + Optional< LLRelPos::Location > location; + Optional< U32 > location_percent_hcenter; + Optional< U32 > location_percent_vcenter; + + Optional< F32 > padding_horiz; + Optional< F32 > padding_vert; + + Params(); + + bool equals(const Params&) const; + }; + +protected: + friend class LLUICtrlFactory; + LLBadge(const Params& p); + +public: + + ~LLBadge(); + + virtual void draw(); + + const std::string getLabel() const { return wstring_to_utf8str(mLabel); } + void setLabel( const LLStringExplicit& label); + +private: + LLPointer< LLUIImage > mBorderImage; + LLUIColor mBorderColor; + + const LLFontGL* mGLFont; + + LLPointer< LLUIImage > mImage; + LLUIColor mImageColor; + + LLUIString mLabel; + LLUIColor mLabelColor; + + LLRelPos::Location mLocation; + F32 mLocationPercentHCenter; + F32 mLocationPercentVCenter; + + LLHandle< LLView > mOwner; + + F32 mPaddingHoriz; + F32 mPaddingVert; +}; + +// Build time optimization, generate once in .cpp file +#ifndef LLBADGE_CPP +extern template class LLBadge* LLView::getChild<class LLBadge>(const std::string& name, BOOL recurse) const; +#endif + +#endif // LL_LLBADGE_H diff --git a/indra/llui/llbadgeowner.cpp b/indra/llui/llbadgeowner.cpp new file mode 100644 index 0000000000..77f15567bf --- /dev/null +++ b/indra/llui/llbadgeowner.cpp @@ -0,0 +1,126 @@ +/** + * @file llbadgeowner.cpp + * @brief Class to manage badges attached to a UI control + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, 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 "llbadgeowner.h" +#include "llpanel.h" + +// +// Classes +// + +LLBadgeOwner::LLBadgeOwner(LLHandle< LLView > viewHandle) + : mBadge(NULL) + , mBadgeOwnerView(viewHandle) +{ +} + +void LLBadgeOwner::initBadgeParams(const LLBadge::Params& p) +{ + if (!p.equals(LLUICtrlFactory::getDefaultParams<LLBadge>())) + { + mBadge = createBadge(p); + } +} + +void LLBadgeOwner::setBadgeLabel(const LLStringExplicit& label) +{ + if (mBadge == NULL) + { + mBadge = createBadge(LLUICtrlFactory::getDefaultParams<LLBadge>()); + + addBadgeToParentPanel(); + } + + if (mBadge) + { + mBadge->setLabel(label); + + // + // Push the badge to the front so it renders on top + // + + LLView * parent = mBadge->getParent(); + + if (parent) + { + parent->sendChildToFront(mBadge); + } + } +} + +void LLBadgeOwner::setBadgeVisibility(bool visible) +{ + if (mBadge) + { + mBadge->setVisible(visible); + } +} + +void LLBadgeOwner::addBadgeToParentPanel() +{ + LLView * owner_view = mBadgeOwnerView.get(); + + if (mBadge && owner_view) + { + // Badge parent is badge owner by default + LLView * badge_parent = owner_view; + + // Find the appropriate parent for the badge + LLView * parent = owner_view->getParent(); + + while (parent) + { + LLPanel * parent_panel = dynamic_cast<LLPanel *>(parent); + + if (parent_panel && parent_panel->acceptsBadge()) + { + badge_parent = parent; + break; + } + + parent = parent->getParent(); + } + + if (badge_parent) + { + badge_parent->addChild(mBadge); + } + else + { + llwarns << "Unable to find parent panel for badge " << mBadge->getName() << " on " << owner_view->getName() << llendl; + } + } +} + +LLBadge* LLBadgeOwner::createBadge(const LLBadge::Params& p) +{ + LLBadge::Params badge_params(p); + badge_params.owner = mBadgeOwnerView; + + return LLUICtrlFactory::create<LLBadge>(badge_params); +} diff --git a/indra/llui/llbadgeowner.h b/indra/llui/llbadgeowner.h new file mode 100644 index 0000000000..a2399189a5 --- /dev/null +++ b/indra/llui/llbadgeowner.h @@ -0,0 +1,61 @@ +/** + * @file llbadgeowner.h + * @brief Header for badge owners + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, 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 LL_LLBADGEOWNER_H +#define LL_LLBADGEOWNER_H + +#include "llbadge.h" +#include "llview.h" + +// +// Classes +// + +class LLBadgeOwner +{ +public: + + LLBadgeOwner(LLHandle< LLView > viewHandle); + + void initBadgeParams(const LLBadge::Params& p); + void addBadgeToParentPanel(); + + bool badgeHasParent() const { return (mBadge && mBadge->getParent()); } + + void setBadgeLabel(const LLStringExplicit& label); + void setBadgeVisibility(bool visible); + +private: + + LLBadge* createBadge(const LLBadge::Params& p); + +private: + + LLBadge* mBadge; + LLHandle< LLView > mBadgeOwnerView; +}; + +#endif // LL_LLBADGEOWNER_H diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp index 45ceaff696..7b015bd576 100644 --- a/indra/llui/llbutton.cpp +++ b/indra/llui/llbutton.cpp @@ -99,7 +99,9 @@ LLButton::Params::Params() scale_image("scale_image", true), hover_glow_amount("hover_glow_amount"), commit_on_return("commit_on_return", true), - use_draw_context_alpha("use_draw_context_alpha", true) + use_draw_context_alpha("use_draw_context_alpha", true), + badge("badge"), + handle_right_mouse("handle_right_mouse") { addSynonym(is_toggle, "toggle"); held_down_delay.seconds = 0.5f; @@ -109,6 +111,7 @@ LLButton::Params::Params() LLButton::LLButton(const LLButton::Params& p) : LLUICtrl(p), + LLBadgeOwner(LLView::getHandle()), mMouseDownFrame(0), mMouseHeldDownCount(0), mBorderEnabled( FALSE ), @@ -160,8 +163,8 @@ LLButton::LLButton(const LLButton::Params& p) mMouseDownSignal(NULL), mMouseUpSignal(NULL), mHeldDownSignal(NULL), - mUseDrawContextAlpha(p.use_draw_context_alpha) - + mUseDrawContextAlpha(p.use_draw_context_alpha), + mHandleRightMouse(p.handle_right_mouse) { static LLUICachedControl<S32> llbutton_orig_h_pad ("UIButtonOrigHPad", 0); static Params default_params(LLUICtrlFactory::getDefaultParams<LLButton>()); @@ -244,6 +247,11 @@ LLButton::LLButton(const LLButton::Params& p) { setHeldDownCallback(initCommitCallback(p.mouse_held_callback)); } + + if (p.badge.isProvided()) + { + LLBadgeOwner::initBadgeParams(p.badge()); + } } LLButton::~LLButton() @@ -327,8 +335,12 @@ boost::signals2::connection LLButton::setHeldDownCallback( button_callback_t cb, BOOL LLButton::postBuild() { autoResize(); - return TRUE; + + addBadgeToParentPanel(); + + return LLUICtrl::postBuild(); } + BOOL LLButton::handleUnicodeCharHere(llwchar uni_char) { BOOL handled = FALSE; @@ -447,7 +459,7 @@ BOOL LLButton::handleMouseUp(S32 x, S32 y, MASK mask) BOOL LLButton::handleRightMouseDown(S32 x, S32 y, MASK mask) { - if (!childrenHandleRightMouseDown(x, y, mask)) + if (mHandleRightMouse && !childrenHandleRightMouseDown(x, y, mask)) { // Route future Mouse messages here preemptively. (Release on mouse up.) gFocusMgr.setMouseCapture( this ); @@ -460,37 +472,42 @@ BOOL LLButton::handleRightMouseDown(S32 x, S32 y, MASK mask) // if (pointInView(x, y)) // { // } + // send the mouse down signal + LLUICtrl::handleRightMouseDown(x,y,mask); + // *TODO: Return result of LLUICtrl call above? Should defer to base class + // but this might change the mouse handling of existing buttons in a bad way + // if they are not mouse opaque. } - // send the mouse down signal - LLUICtrl::handleRightMouseDown(x,y,mask); - // *TODO: Return result of LLUICtrl call above? Should defer to base class - // but this might change the mouse handling of existing buttons in a bad way - // if they are not mouse opaque. + return TRUE; } BOOL LLButton::handleRightMouseUp(S32 x, S32 y, MASK mask) { - // We only handle the click if the click both started and ended within us - if( hasMouseCapture() ) + if (mHandleRightMouse) { - // Always release the mouse - gFocusMgr.setMouseCapture( NULL ); + // We only handle the click if the click both started and ended within us + if( hasMouseCapture() ) + { + // Always release the mouse + gFocusMgr.setMouseCapture( NULL ); -// if (pointInView(x, y)) -// { -// mRightMouseUpSignal(this, x,y,mask); -// } - } - else - { - childrenHandleRightMouseUp(x, y, mask); + // if (pointInView(x, y)) + // { + // mRightMouseUpSignal(this, x,y,mask); + // } + } + else + { + childrenHandleRightMouseUp(x, y, mask); + } + + // send the mouse up signal + LLUICtrl::handleRightMouseUp(x,y,mask); + // *TODO: Return result of LLUICtrl call above? Should defer to base class + // but this might change the mouse handling of existing buttons in a bad way. + // if they are not mouse opaque. } - // send the mouse up signal - LLUICtrl::handleRightMouseUp(x,y,mask); - // *TODO: Return result of LLUICtrl call above? Should defer to base class - // but this might change the mouse handling of existing buttons in a bad way. - // if they are not mouse opaque. return TRUE; } diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h index 0cfc393e05..5968916006 100644 --- a/indra/llui/llbutton.h +++ b/indra/llui/llbutton.h @@ -27,6 +27,8 @@ #ifndef LL_LLBUTTON_H #define LL_LLBUTTON_H +#include "lluuid.h" +#include "llbadgeowner.h" #include "llcontrol.h" #include "lluictrl.h" #include "v4color.h" @@ -52,15 +54,13 @@ S32 round_up(S32 grid, S32 value); class LLUICtrlFactory; -class LLUIImage; -class LLUUID; // // Classes // class LLButton -: public LLUICtrl +: public LLUICtrl, public LLBadgeOwner { public: struct Params @@ -125,7 +125,11 @@ public: Optional<F32> hover_glow_amount; Optional<TimeIntervalParam> held_down_delay; - Optional<bool> use_draw_context_alpha; + Optional<bool> use_draw_context_alpha; + + Optional<LLBadge::Params> badge; + + Optional<bool> handle_right_mouse; Params(); }; @@ -249,7 +253,7 @@ public: void setImageDisabledSelected(LLPointer<LLUIImage> image); void setImageFlash(LLPointer<LLUIImage> image); void setImagePressed(LLPointer<LLUIImage> image); - + void setCommitOnReturn(BOOL commit) { mCommitOnReturn = commit; } BOOL getCommitOnReturn() const { return mCommitOnReturn; } @@ -357,6 +361,8 @@ private: bool mForcePressedState; LLFrameTimer mFlashingTimer; + + bool mHandleRightMouse; }; // Build time optimization, generate once in .cpp file diff --git a/indra/llui/llconsole.cpp b/indra/llui/llconsole.cpp index 06bad1f371..04040200d0 100644 --- a/indra/llui/llconsole.cpp +++ b/indra/llui/llconsole.cpp @@ -132,6 +132,9 @@ void LLConsole::setFontSize(S32 size_index) void LLConsole::draw() { + // Units in pixels + static const F32 padding_horizontal = 10; + static const F32 padding_vertical = 3; LLGLSUIDefault gls_ui; // skip lines added more than mLinePersistTime ago @@ -176,11 +179,9 @@ void LLConsole::draw() // draw remaining lines F32 y_pos = 0.f; - LLUIImagePtr imagep = LLUI::getUIImage("Rounded_Square"); + LLUIImagePtr imagep = LLUI::getUIImage("transparent"); -// F32 console_opacity = llclamp(gSavedSettings.getF32("ConsoleBackgroundOpacity"), 0.f, 1.f); F32 console_opacity = llclamp(LLUI::sSettingGroups["config"]->getF32("ConsoleBackgroundOpacity"), 0.f, 1.f); -// LLColor4 color = LLUIColorTable::instance().getColor("ConsoleBackground"); LLColor4 color = LLUIColorTable::instance().getColor("ConsoleBackground"); color.mV[VALPHA] *= console_opacity; @@ -188,8 +189,8 @@ void LLConsole::draw() for(paragraph_it = mParagraphs.rbegin(); paragraph_it != mParagraphs.rend(); paragraph_it++) { - S32 target_height = llfloor( (*paragraph_it).mLines.size() * line_height + 8); - S32 target_width = llfloor( (*paragraph_it).mMaxWidth +15); + S32 target_height = llfloor( (*paragraph_it).mLines.size() * line_height + padding_vertical); + S32 target_width = llfloor( (*paragraph_it).mMaxWidth + padding_horizontal); y_pos += ((*paragraph_it).mLines.size()) * line_height; imagep->drawSolid(-14, (S32)(y_pos + line_height - target_height), target_width, target_height, color); @@ -234,7 +235,7 @@ void LLConsole::draw() y_off += line_height; } } - y_pos += 8; + y_pos += padding_vertical; } } diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp index 9b6830a816..6a91ec56e4 100644 --- a/indra/llui/lllayoutstack.cpp +++ b/indra/llui/lllayoutstack.cpp @@ -49,6 +49,8 @@ void LLLayoutStack::OrientationNames::declareValues() // LLLayoutPanel::LLLayoutPanel(const Params& p) : LLPanel(p), + mExpandedMinDimSpecified(false), + mExpandedMinDim(p.min_dim), mMinDim(p.min_dim), mMaxDim(p.max_dim), mAutoResize(p.auto_resize), @@ -58,6 +60,13 @@ LLLayoutPanel::LLLayoutPanel(const Params& p) mVisibleAmt(1.f), // default to fully visible mResizeBar(NULL) { + // Set the expanded min dim if it is provided, otherwise it gets the p.min_dim value + if (p.expanded_min_dim.isProvided()) + { + mExpandedMinDimSpecified = true; + mExpandedMinDim = p.expanded_min_dim(); + } + // panels initialized as hidden should not start out partially visible if (!getVisible()) { @@ -78,20 +87,20 @@ LLLayoutPanel::~LLLayoutPanel() delete mResizeBar; mResizeBar = NULL; } - + F32 LLLayoutPanel::getCollapseFactor(LLLayoutStack::ELayoutOrientation orientation) { if (orientation == LLLayoutStack::HORIZONTAL) { F32 collapse_amt = - clamp_rescale(mCollapseAmt, 0.f, 1.f, 1.f, (F32)mMinDim / (F32)llmax(1, getRect().getWidth())); + clamp_rescale(mCollapseAmt, 0.f, 1.f, 1.f, (F32)getRelevantMinDim() / (F32)llmax(1, getRect().getWidth())); return mVisibleAmt * collapse_amt; } else { - F32 collapse_amt = - clamp_rescale(mCollapseAmt, 0.f, 1.f, 1.f, llmin(1.f, (F32)mMinDim / (F32)llmax(1, getRect().getHeight()))); - return mVisibleAmt * collapse_amt; + F32 collapse_amt = + clamp_rescale(mCollapseAmt, 0.f, 1.f, 1.f, llmin(1.f, (F32)getRelevantMinDim() / (F32)llmax(1, getRect().getHeight()))); + return mVisibleAmt * collapse_amt; } } @@ -182,14 +191,14 @@ BOOL LLLayoutStack::postBuild() } bool LLLayoutStack::addChild(LLView* child, S32 tab_group) - { +{ LLLayoutPanel* panelp = dynamic_cast<LLLayoutPanel*>(child); - if (panelp) - { + if (panelp) + { mPanels.push_back(panelp); - } + } return LLView::addChild(child, tab_group); - } +} S32 LLLayoutStack::getDefaultHeight(S32 cur_height) @@ -281,9 +290,9 @@ bool LLLayoutStack::getPanelMinSize(const std::string& panel_name, S32* min_dimp { LLLayoutPanel* panel = findEmbeddedPanelByName(panel_name); - if (panel) + if (panel && min_dimp) { - if (min_dimp) *min_dimp = panel->mMinDim; + *min_dimp = panel->getRelevantMinDim(); } return NULL != panel; @@ -316,23 +325,23 @@ void LLLayoutStack::updateLayout(BOOL force_resize) e_panel_list_t::iterator panel_it; for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) { - LLPanel* panelp = (*panel_it); + LLLayoutPanel* panelp = (*panel_it); if (panelp->getVisible()) { if (mAnimate) { if (!mAnimatedThisFrame) { - (*panel_it)->mVisibleAmt = lerp((*panel_it)->mVisibleAmt, 1.f, LLCriticalDamp::getInterpolant(mOpenTimeConstant)); - if ((*panel_it)->mVisibleAmt > 0.99f) + panelp->mVisibleAmt = lerp(panelp->mVisibleAmt, 1.f, LLCriticalDamp::getInterpolant(mOpenTimeConstant)); + if (panelp->mVisibleAmt > 0.99f) { - (*panel_it)->mVisibleAmt = 1.f; + panelp->mVisibleAmt = 1.f; } } } else { - (*panel_it)->mVisibleAmt = 1.f; + panelp->mVisibleAmt = 1.f; } } else // not visible @@ -341,36 +350,36 @@ void LLLayoutStack::updateLayout(BOOL force_resize) { if (!mAnimatedThisFrame) { - (*panel_it)->mVisibleAmt = lerp((*panel_it)->mVisibleAmt, 0.f, LLCriticalDamp::getInterpolant(mCloseTimeConstant)); - if ((*panel_it)->mVisibleAmt < 0.001f) + panelp->mVisibleAmt = lerp(panelp->mVisibleAmt, 0.f, LLCriticalDamp::getInterpolant(mCloseTimeConstant)); + if (panelp->mVisibleAmt < 0.001f) { - (*panel_it)->mVisibleAmt = 0.f; + panelp->mVisibleAmt = 0.f; } } } else { - (*panel_it)->mVisibleAmt = 0.f; + panelp->mVisibleAmt = 0.f; } } - if ((*panel_it)->mCollapsed) + if (panelp->mCollapsed) { - (*panel_it)->mCollapseAmt = lerp((*panel_it)->mCollapseAmt, 1.f, LLCriticalDamp::getInterpolant(mCloseTimeConstant)); + panelp->mCollapseAmt = lerp(panelp->mCollapseAmt, 1.f, LLCriticalDamp::getInterpolant(mCloseTimeConstant)); } else { - (*panel_it)->mCollapseAmt = lerp((*panel_it)->mCollapseAmt, 0.f, LLCriticalDamp::getInterpolant(mCloseTimeConstant)); + panelp->mCollapseAmt = lerp(panelp->mCollapseAmt, 0.f, LLCriticalDamp::getInterpolant(mCloseTimeConstant)); } if (mOrientation == HORIZONTAL) { // enforce minimize size constraint by default - if (panelp->getRect().getWidth() < (*panel_it)->mMinDim) + if (panelp->getRect().getWidth() < panelp->getRelevantMinDim()) { - panelp->reshape((*panel_it)->mMinDim, panelp->getRect().getHeight()); + panelp->reshape(panelp->getRelevantMinDim(), panelp->getRect().getHeight()); } - total_width += llround(panelp->getRect().getWidth() * (*panel_it)->getCollapseFactor(mOrientation)); + total_width += llround(panelp->getRect().getWidth() * panelp->getCollapseFactor(mOrientation)); // want n-1 panel gaps for n panels if (panel_it != mPanels.begin()) { @@ -380,11 +389,11 @@ void LLLayoutStack::updateLayout(BOOL force_resize) else //VERTICAL { // enforce minimize size constraint by default - if (panelp->getRect().getHeight() < (*panel_it)->mMinDim) + if (panelp->getRect().getHeight() < panelp->getRelevantMinDim()) { - panelp->reshape(panelp->getRect().getWidth(), (*panel_it)->mMinDim); + panelp->reshape(panelp->getRect().getWidth(), panelp->getRelevantMinDim()); } - total_height += llround(panelp->getRect().getHeight() * (*panel_it)->getCollapseFactor(mOrientation)); + total_height += llround(panelp->getRect().getHeight() * panelp->getCollapseFactor(mOrientation)); if (panel_it != mPanels.begin()) { total_height += mPanelSpacing; @@ -403,34 +412,23 @@ void LLLayoutStack::updateLayout(BOOL force_resize) continue; } + S32 relevant_dimension = (mOrientation == HORIZONTAL) ? (*panel_it)->getRect().getWidth() : (*panel_it)->getRect().getHeight(); + S32 relevant_min = (*panel_it)->getRelevantMinDim(); + // if currently resizing a panel or the panel is flagged as not automatically resizing // only track total available headroom, but don't use it for automatic resize logic if ((*panel_it)->mResizeBar->hasMouseCapture() || (!(*panel_it)->mAutoResize && !force_resize)) { - if (mOrientation == HORIZONTAL) - { - shrink_headroom_total += (*panel_it)->getRect().getWidth() - (*panel_it)->mMinDim; - } - else //VERTICAL - { - shrink_headroom_total += (*panel_it)->getRect().getHeight() - (*panel_it)->mMinDim; - } + shrink_headroom_total += relevant_dimension - relevant_min; } else { num_resizable_panels++; - if (mOrientation == HORIZONTAL) - { - shrink_headroom_available += (*panel_it)->getRect().getWidth() - (*panel_it)->mMinDim; - shrink_headroom_total += (*panel_it)->getRect().getWidth() - (*panel_it)->mMinDim; - } - else //VERTICAL - { - shrink_headroom_available += (*panel_it)->getRect().getHeight() - (*panel_it)->mMinDim; - shrink_headroom_total += (*panel_it)->getRect().getHeight() - (*panel_it)->mMinDim; - } + + shrink_headroom_available += relevant_dimension - relevant_min; + shrink_headroom_total += relevant_dimension - relevant_min; } } @@ -452,27 +450,28 @@ void LLLayoutStack::updateLayout(BOOL force_resize) for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) { - LLPanel* panelp = (*panel_it); + LLLayoutPanel* panelp = (*panel_it); S32 cur_width = panelp->getRect().getWidth(); S32 cur_height = panelp->getRect().getHeight(); S32 new_width = cur_width; - S32 new_height = cur_height; + S32 new_height = cur_height; + S32 relevant_min = panelp->getRelevantMinDim(); if (mOrientation == HORIZONTAL) { - new_width = llmax((*panel_it)->mMinDim, new_width); + new_width = llmax(relevant_min, new_width); } else { - new_height = llmax((*panel_it)->mMinDim, new_height); + new_height = llmax(relevant_min, new_height); } S32 delta_size = 0; // if panel can automatically resize (not animating, and resize flag set)... - if ((*panel_it)->getCollapseFactor(mOrientation) == 1.f - && (force_resize || (*panel_it)->mAutoResize) - && !(*panel_it)->mResizeBar->hasMouseCapture()) + if (panelp->getCollapseFactor(mOrientation) == 1.f + && (force_resize || panelp->mAutoResize) + && !panelp->mResizeBar->hasMouseCapture()) { if (mOrientation == HORIZONTAL) { @@ -481,8 +480,8 @@ void LLLayoutStack::updateLayout(BOOL force_resize) { // shrink proportionally to amount over minimum // so we can do this in one pass - delta_size = (shrink_headroom_available > 0) ? llround((F32)pixels_to_distribute * ((F32)(cur_width - (*panel_it)->mMinDim) / (F32)shrink_headroom_available)) : 0; - shrink_headroom_available -= (cur_width - (*panel_it)->mMinDim); + delta_size = (shrink_headroom_available > 0) ? llround((F32)pixels_to_distribute * ((F32)(cur_width - relevant_min) / (F32)shrink_headroom_available)) : 0; + shrink_headroom_available -= (cur_width - relevant_min); } else { @@ -491,7 +490,7 @@ void LLLayoutStack::updateLayout(BOOL force_resize) num_resizable_panels--; } pixels_to_distribute -= delta_size; - new_width = llmax((*panel_it)->mMinDim, cur_width + delta_size); + new_width = llmax(relevant_min, cur_width + delta_size); } else { @@ -504,8 +503,8 @@ void LLLayoutStack::updateLayout(BOOL force_resize) { // shrink proportionally to amount over minimum // so we can do this in one pass - delta_size = (shrink_headroom_available > 0) ? llround((F32)pixels_to_distribute * ((F32)(cur_height - (*panel_it)->mMinDim) / (F32)shrink_headroom_available)) : 0; - shrink_headroom_available -= (cur_height - (*panel_it)->mMinDim); + delta_size = (shrink_headroom_available > 0) ? llround((F32)pixels_to_distribute * ((F32)(cur_height - relevant_min) / (F32)shrink_headroom_available)) : 0; + shrink_headroom_available -= (cur_height - relevant_min); } else { @@ -513,7 +512,7 @@ void LLLayoutStack::updateLayout(BOOL force_resize) num_resizable_panels--; } pixels_to_distribute -= delta_size; - new_height = llmax((*panel_it)->mMinDim, cur_height + delta_size); + new_height = llmax(relevant_min, cur_height + delta_size); } else { @@ -566,19 +565,20 @@ void LLLayoutStack::updateLayout(BOOL force_resize) LLLayoutPanel* last_resizeable_panel = NULL; for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) { - LLPanel* panelp = (*panel_it); + LLLayoutPanel* panelp = (*panel_it); + S32 relevant_min = panelp->getRelevantMinDim(); if (mOrientation == HORIZONTAL) { (*panel_it)->mResizeBar->setResizeLimits( - (*panel_it)->mMinDim, - (*panel_it)->mMinDim + shrink_headroom_total); + relevant_min, + relevant_min + shrink_headroom_total); } else //VERTICAL { (*panel_it)->mResizeBar->setResizeLimits( - (*panel_it)->mMinDim, - (*panel_it)->mMinDim + shrink_headroom_total); + relevant_min, + relevant_min + shrink_headroom_total); } // toggle resize bars based on panel visibility, resizability, etc @@ -658,7 +658,7 @@ void LLLayoutStack::calcMinExtents() { if (mOrientation == HORIZONTAL) { - mMinWidth += (*panel_it)->mMinDim; + mMinWidth += (*panel_it)->getRelevantMinDim(); if (panel_it != mPanels.begin()) { mMinWidth += mPanelSpacing; @@ -666,7 +666,7 @@ void LLLayoutStack::calcMinExtents() } else //VERTICAL { - mMinHeight += (*panel_it)->mMinDim; + mMinHeight += (*panel_it)->getRelevantMinDim(); if (panel_it != mPanels.begin()) { mMinHeight += mPanelSpacing; @@ -688,7 +688,7 @@ void LLLayoutStack::createResizeBars() LLResizeBar::Params resize_params; resize_params.name("resize"); resize_params.resizing_view(lp); - resize_params.min_size(lp->mMinDim); + resize_params.min_size(lp->getRelevantMinDim()); resize_params.side(side); resize_params.snapping_enabled(false); LLResizeBar* resize_bar = LLUICtrlFactory::create<LLResizeBar>(resize_params); diff --git a/indra/llui/lllayoutstack.h b/indra/llui/lllayoutstack.h index 4ac8ef0ee9..d8ef0aeaca 100644 --- a/indra/llui/lllayoutstack.h +++ b/indra/llui/lllayoutstack.h @@ -30,10 +30,10 @@ #include "llpanel.h" -class LLPanel; class LLLayoutPanel; + class LLLayoutStack : public LLView, public LLInstanceTracker<LLLayoutStack> { public: @@ -149,6 +149,7 @@ private: F32 mCloseTimeConstant; }; // end class LLLayoutStack + class LLLayoutPanel : public LLPanel { friend class LLLayoutStack; @@ -156,13 +157,15 @@ friend class LLUICtrlFactory; public: struct Params : public LLInitParam::Block<Params, LLPanel::Params> { - Optional<S32> min_dim, + Optional<S32> expanded_min_dim, + min_dim, max_dim; Optional<bool> user_resize, auto_resize; Params() - : min_dim("min_dim", 0), + : expanded_min_dim("expanded_min_dim", 0), + min_dim("min_dim", 0), max_dim("max_dim", 0), user_resize("user_resize", true), auto_resize("auto_resize", true) @@ -177,15 +180,36 @@ public: ~LLLayoutPanel(); void initFromParams(const Params& p); - void setMinDim(S32 value) { mMinDim = value; } + + S32 getMinDim() const { return mMinDim; } + void setMinDim(S32 value) { mMinDim = value; if (!mExpandedMinDimSpecified) mExpandedMinDim = value; } + + S32 getMaxDim() const { return mMaxDim; } void setMaxDim(S32 value) { mMaxDim = value; } -protected: - LLLayoutPanel(const Params& p) ; + S32 getExpandedMinDim() const { return mExpandedMinDim; } + void setExpandedMinDim(S32 value) { mExpandedMinDim = value; mExpandedMinDimSpecified = true; } + + S32 getRelevantMinDim() const + { + S32 min_dim = mMinDim; + + if (!mCollapsed) + { + min_dim = mExpandedMinDim; + } + + return min_dim; + } +protected: + LLLayoutPanel(const Params& p); F32 getCollapseFactor(LLLayoutStack::ELayoutOrientation orientation); + bool mExpandedMinDimSpecified; + S32 mExpandedMinDim; + S32 mMinDim; S32 mMaxDim; BOOL mAutoResize; diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index 0196080d90..06fbc0f234 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -37,6 +37,7 @@ #include "llgl.h" #include "lltimer.h" +#include "llcalc.h" //#include "llclipboard.h" #include "llcontrol.h" #include "llbutton.h" @@ -81,6 +82,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"), @@ -132,6 +134,7 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p) mIgnoreTab( p.ignore_tab ), mDrawAsterixes( p.is_password ), mSelectAllonFocusReceived( p.select_on_focus ), + mSelectAllonCommit( TRUE ), mPassDelete(FALSE), mReadOnly(FALSE), mBgImage( p.background_image ), @@ -173,6 +176,7 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p) updateTextPadding(); setCursor(mText.length()); + setPrevalidateInput(p.prevalidate_input_callback()); setPrevalidate(p.prevalidate_callback()); LLContextMenu* menu = LLUICtrlFactory::instance().createFromFile<LLContextMenu> @@ -228,7 +232,10 @@ void LLLineEditor::onCommit() setControlValue(getValue()); LLUICtrl::onCommit(); - selectAll(); + + // Selection on commit needs to be turned off when evaluating maths + // expressions, to allow indication of the error position + if (mSelectAllonCommit) selectAll(); } // Returns TRUE if user changed value at all @@ -405,23 +412,15 @@ 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 length = llabs( mSelectionStart - cursor_pos ); + const LLWString& substr = mText.getWString().substr(left_pos, length); + + if (mIsSelecting && !prevalidateInput(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); } @@ -505,6 +504,11 @@ BOOL LLLineEditor::canSelectAll() const void LLLineEditor::selectAll() { + if (!prevalidateInput(mText.getWString())) + { + return; + } + mSelectionStart = mText.length(); mSelectionEnd = 0; setCursor(mSelectionEnd); @@ -590,6 +594,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); @@ -624,8 +631,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 { @@ -796,6 +801,9 @@ void LLLineEditor::removeChar() { if( getCursor() > 0 ) { + if (!prevalidateInput(mText.getWString().substr(getCursor()-1, 1))) + return; + mText.erase(getCursor() - 1, 1); setCursor(getCursor() - 1); @@ -816,6 +824,9 @@ void LLLineEditor::addChar(const llwchar uni_char) } else if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode()) { + if (!prevalidateInput(mText.getWString().substr(getCursor(), 1))) + return; + mText.erase(getCursor(), 1); } @@ -864,6 +875,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().substr(left_pos, selection_length); + + if (!prevalidateInput(selection)) + return; + setCursor(new_cursor_pos); mSelectionEnd = getCursor(); } @@ -994,8 +1012,12 @@ void LLLineEditor::deleteSelection() { if( !mReadOnly && hasSelection() ) { - S32 left_pos = llmin( mSelectionStart, mSelectionEnd ); - S32 selection_length = llabs( mSelectionStart - mSelectionEnd ); + S32 left_pos, selection_length; + getSelectionRange(&left_pos, &selection_length); + const LLWString& selection = mText.getWString().substr(left_pos, selection_length); + + if (!prevalidateInput(selection)) + return; mText.erase(left_pos, selection_length); deselect(); @@ -1013,12 +1035,16 @@ void LLLineEditor::cut() { if( canCut() ) { + S32 left_pos, length; + getSelectionRange(&left_pos, &length); + const LLWString& selection = mText.getWString().substr(left_pos, length); + + if (!prevalidateInput(selection)) + 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(); @@ -1098,6 +1124,9 @@ void LLLineEditor::pasteHelper(bool is_primary) if (!paste.empty()) { + if (!prevalidateInput(paste)) + return; + // Prepare for possible rollback LLLineEditorRollback rollback(this); @@ -1445,6 +1474,13 @@ BOOL LLLineEditor::handleUnicodeCharHere(llwchar uni_char) LLLineEditorRollback rollback( this ); + { + LLWString u_char; + u_char.assign(1, uni_char); + if (!prevalidateInput(u_char)) + return handled; + } + addChar(uni_char); mKeystrokeTimer.reset(); @@ -1496,6 +1532,15 @@ void LLLineEditor::doDelete() } else if ( getCursor() < mText.length()) { + const LLWString& text_to_delete = mText.getWString().substr(getCursor(), 1); + + if (!prevalidateInput(text_to_delete)) + { + if( mKeystrokeCallback ) + mKeystrokeCallback( this ); + + return; + } setCursor(getCursor() + 1); removeChar(); } @@ -1843,6 +1888,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() { @@ -1936,6 +2002,22 @@ void LLLineEditor::setPrevalidate(LLTextValidate::validate_func_t func) updateAllowingLanguageInput(); } +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) { @@ -1995,6 +2077,32 @@ BOOL LLLineEditor::postvalidateFloat(const std::string &str) return success; } +BOOL LLLineEditor::evaluateFloat() +{ + bool success; + F32 result = 0.f; + std::string expr = getText(); + LLStringUtil::toUpper(expr); + + success = LLCalc::getInstance()->evalString(expr, result); + + if (!success) + { + // Move the cursor to near the error on failure + setCursor(LLCalc::getInstance()->getLastErrorPos()); + // *TODO: Translated error message indicating the type of error? Select error text? + } + else + { + // Replace the expression with the result + std::string result_str = llformat("%f",result); + setText(result_str); + selectAll(); + } + + return success; +} + void LLLineEditor::onMouseCaptureLost() { endSelection(); diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h index fe191e5971..583bde360a 100644 --- a/indra/llui/lllineeditor.h +++ b/indra/llui/lllineeditor.h @@ -76,6 +76,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; @@ -220,6 +221,7 @@ public: void deleteSelection(); void setSelectAllonFocusReceived(BOOL b); + void setSelectAllonCommit(BOOL b) { mSelectAllonCommit = b; } typedef boost::function<void (LLLineEditor* caller, void* user_data)> callback_t; void setKeystrokeCallback(callback_t callback, void* user_data); @@ -232,8 +234,16 @@ public: // Prevalidation controls which keystrokes can affect the editor void setPrevalidate( 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); + BOOL evaluateFloat(); + // line history support: void setEnableLineHistory( BOOL enabled ) { mHaveHistory = enabled; } // switches line history on or off void updateHistory(); // stores current line in history @@ -251,6 +261,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); @@ -312,6 +323,7 @@ protected: S32 mLastSelectionEnd; LLTextValidate::validate_func_t mPrevalidateFunc; + LLTextValidate::validate_func_t mPrevalidateInputFunc; LLFrameTimer mKeystrokeTimer; LLTimer mTripleClickTimer; @@ -330,6 +342,7 @@ protected: BOOL mDrawAsterixes; BOOL mSelectAllonFocusReceived; + BOOL mSelectAllonCommit; BOOL mPassDelete; BOOL mReadOnly; diff --git a/indra/llui/llloadingindicator.h b/indra/llui/llloadingindicator.h index 4c47cc267c..7c44478848 100644 --- a/indra/llui/llloadingindicator.h +++ b/indra/llui/llloadingindicator.h @@ -86,6 +86,8 @@ public: */ void start(); + void reset() { mCurImageIdx = 0; } + private: LLLoadingIndicator(const Params&); void initFromParams(const Params&); diff --git a/indra/llui/llmultislider.cpp b/indra/llui/llmultislider.cpp index f744e9db41..9052bc7d1d 100644 --- a/indra/llui/llmultislider.cpp +++ b/indra/llui/llmultislider.cpp @@ -511,7 +511,7 @@ void LLMultiSlider::draw() mIt->second.mTop + extra_triangle_height, mIt->second.mLeft + mIt->second.getWidth() / 2, mIt->second.mBottom - extra_triangle_height, - mTriangleColor.get(), TRUE); + mTriangleColor.get() % opacity, TRUE); } } else if (!thumb_imagep) diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp index bdac125eb0..6085c61f9a 100644 --- a/indra/llui/llnotifications.cpp +++ b/indra/llui/llnotifications.cpp @@ -1532,7 +1532,7 @@ bool LLNotifications::loadVisibilityRules() // Add a simple notification (from XUI) void LLNotifications::addFromCallback(const LLSD& name) { - add(LLNotification::Params().name(name.asString())); + add(name.asString(), LLSD(), LLSD()); } LLNotificationPtr LLNotifications::add(const std::string& name, diff --git a/indra/llui/llpanel.cpp b/indra/llui/llpanel.cpp index b2383106a8..1dcdd79efa 100644 --- a/indra/llui/llpanel.cpp +++ b/indra/llui/llpanel.cpp @@ -87,7 +87,8 @@ LLPanel::Params::Params() filename("filename"), class_name("class"), help_topic("help_topic"), - visible_callback("visible_callback") + visible_callback("visible_callback"), + accepts_badge("accepts_badge") { name = "panel"; addSynonym(background_visible, "bg_visible"); @@ -113,7 +114,8 @@ LLPanel::LLPanel(const LLPanel::Params& p) mCommitCallbackRegistrar(false), mEnableCallbackRegistrar(false), mXMLFilename(p.filename), - mVisibleSignal(NULL) + mVisibleSignal(NULL), + mAcceptsBadge(p.accepts_badge) // *NOTE: Be sure to also change LLPanel::initFromParams(). We have too // many classes derived from LLPanel to retrofit them all to pass in params. { @@ -485,6 +487,8 @@ void LLPanel::initFromParams(const LLPanel::Params& p) mBgAlphaImage = p.bg_alpha_image(); mBgOpaqueImageOverlay = p.bg_opaque_image_overlay; mBgAlphaImageOverlay = p.bg_alpha_image_overlay; + + mAcceptsBadge = p.accepts_badge; } static LLFastTimer::DeclareTimer FTM_PANEL_SETUP("Panel Setup"); diff --git a/indra/llui/llpanel.h b/indra/llui/llpanel.h index 7bbbeaf709..67674fab7e 100644 --- a/indra/llui/llpanel.h +++ b/indra/llui/llpanel.h @@ -89,6 +89,8 @@ public: Multiple<LocalizedString> strings; Optional<CommitCallbackParam> visible_callback; + + Optional<bool> accepts_badge; Params(); }; @@ -250,6 +252,8 @@ public: boost::signals2::connection setVisibleCallback( const commit_signal_t::slot_type& cb ); + bool acceptsBadge() const { return mAcceptsBadge; } + protected: // Override to set not found list LLButton* getDefaultButton() { return mDefaultBtn; } @@ -264,6 +268,7 @@ protected: static factory_stack_t sFactoryStack; private: + bool mAcceptsBadge; BOOL mBgVisible; // any background at all? BOOL mBgOpaque; // use opaque color or image LLUIColor mBgOpaqueColor; diff --git a/indra/llui/llresmgr.cpp b/indra/llui/llresmgr.cpp index 39385786bc..820e7cb26a 100644 --- a/indra/llui/llresmgr.cpp +++ b/indra/llui/llresmgr.cpp @@ -337,7 +337,7 @@ LLLocale::LLLocale(const std::string& locale_string) char* new_locale_string = setlocale( LC_ALL, locale_string.c_str()); if ( new_locale_string == NULL) { - llwarns << "Failed to set locale " << locale_string << llendl; + LL_WARNS_ONCE("LLLocale") << "Failed to set locale " << locale_string << LL_ENDL; setlocale(LC_ALL, SYSTEM_LOCALE.c_str()); } //else diff --git a/indra/llui/llspinctrl.cpp b/indra/llui/llspinctrl.cpp index 6b4e9cf923..934879cdfd 100644 --- a/indra/llui/llspinctrl.cpp +++ b/indra/llui/llspinctrl.cpp @@ -44,7 +44,7 @@ #include "llresmgr.h" #include "lluictrlfactory.h" -const U32 MAX_STRING_LENGTH = 32; +const U32 MAX_STRING_LENGTH = 255; static LLDefaultChildRegistry::Register<LLSpinCtrl> r2("spinner"); @@ -52,6 +52,7 @@ LLSpinCtrl::Params::Params() : label_width("label_width"), decimal_digits("decimal_digits"), allow_text_entry("allow_text_entry", true), + label_wrap("label_wrap", false), text_enabled_color("text_enabled_color"), text_disabled_color("text_disabled_color"), up_button("up_button"), @@ -80,6 +81,7 @@ LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p) { LLRect label_rect( 0, centered_top, label_width, centered_bottom ); LLTextBox::Params params; + params.wrap(p.label_wrap); params.name("SpinCtrl Label"); params.rect(label_rect); params.initial_value(p.label()); @@ -122,14 +124,7 @@ LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p) params.max_length.bytes(MAX_STRING_LENGTH); params.commit_callback.function((boost::bind(&LLSpinCtrl::onEditorCommit, this, _2))); - if( mPrecision>0 )//should accept float numbers - { - params.prevalidate_callback(&LLTextValidate::validateFloat); - } - else //should accept int numbers - { - params.prevalidate_callback(&LLTextValidate::validateInt); - } + //*NOTE: allow entering of any chars for LLCalc, proper input will be evaluated on commit params.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM); mEditor = LLUICtrlFactory::create<LLLineEditor> (params); @@ -138,6 +133,7 @@ LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p) // than when it doesn't. Instead, if you always have to double click to select all the text, // it's easier to understand //mEditor->setSelectAllonFocusReceived(TRUE); + mEditor->setSelectAllonCommit(FALSE); addChild(mEditor); updateEditor(); @@ -302,9 +298,10 @@ void LLSpinCtrl::onEditorCommit( const LLSD& data ) { BOOL success = FALSE; - std::string text = mEditor->getText(); - if( LLLineEditor::postvalidateFloat( text ) ) + if( mEditor->evaluateFloat() ) { + std::string text = mEditor->getText(); + LLLocale locale(LLLocale::USER_LOCALE); F32 val = (F32) atof(text.c_str()); @@ -325,7 +322,11 @@ void LLSpinCtrl::onEditorCommit( const LLSD& data ) } updateEditor(); - if( !success ) + if( success ) + { + updateEditor(); + } + else { reportInvalidData(); } diff --git a/indra/llui/llspinctrl.h b/indra/llui/llspinctrl.h index 8960971594..d197084e38 100644 --- a/indra/llui/llspinctrl.h +++ b/indra/llui/llspinctrl.h @@ -44,6 +44,7 @@ public: Optional<S32> label_width; Optional<U32> decimal_digits; Optional<bool> allow_text_entry; + Optional<bool> label_wrap; Optional<LLUIColor> text_enabled_color; Optional<LLUIColor> text_disabled_color; diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 1cc3cc04d6..349dbc3405 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -280,7 +280,7 @@ bool LLTextBase::truncate() if (getLength() >= S32(mMaxTextByteLength / 4)) { // Have to check actual byte size - LLWString text(getWText()); + LLWString text(getWText()); S32 utf8_byte_size = wstring_utf8_length(text); if ( utf8_byte_size > mMaxTextByteLength ) { @@ -547,8 +547,7 @@ void LLTextBase::drawText() } LLRect text_rect(line.mRect); - text_rect.mRight = llmin(mDocumentView->getRect().getWidth(), text_rect.mRight); // clamp right edge to document extents - text_rect.translate(mVisibleTextRect.mLeft, mVisibleTextRect.mBottom); // translate into display region of text widget + text_rect.mRight = mDocumentView->getRect().getWidth(); // clamp right edge to document extents text_rect.translate(mDocumentView->getRect().mLeft, mDocumentView->getRect().mBottom); // adjust by scroll position // draw a single line of text @@ -655,7 +654,7 @@ S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::s } text.insert(pos, wstr); - getViewModel()->setDisplay(text); + getViewModel()->setDisplay(text); if ( truncate() ) { @@ -670,7 +669,7 @@ S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::s S32 LLTextBase::removeStringNoUndo(S32 pos, S32 length) { - LLWString text(getWText()); + LLWString text(getWText()); segment_set_t::iterator seg_iter = getSegIterContaining(pos); while(seg_iter != mSegments.end()) { @@ -717,7 +716,7 @@ S32 LLTextBase::removeStringNoUndo(S32 pos, S32 length) } text.erase(pos, length); - getViewModel()->setDisplay(text); + getViewModel()->setDisplay(text); // recreate default segment in case we erased everything createDefaultSegment(); @@ -734,9 +733,9 @@ S32 LLTextBase::overwriteCharNoUndo(S32 pos, llwchar wc) { return 0; } - LLWString text(getWText()); + LLWString text(getWText()); text[pos] = wc; - getViewModel()->setDisplay(text); + getViewModel()->setDisplay(text); onValueChange(pos, pos + 1); needsReflow(pos); @@ -857,7 +856,7 @@ BOOL LLTextBase::handleMouseUp(S32 x, S32 y, MASK mask) // Did we just click on a link? if (mURLClickSignal && cur_segment->getStyle() - && cur_segment->getStyle()->isLink()) + && cur_segment->getStyle()->isLink()) { // *TODO: send URL here? (*mURLClickSignal)(this, LLSD() ); @@ -1035,27 +1034,27 @@ void LLTextBase::draw() gl_rect_2d(text_rect, bg_color % alpha, TRUE); } - bool should_clip = mClip || mScroller != NULL; - { LLLocalClipRect clip(text_rect, should_clip); + bool should_clip = mClip || mScroller != NULL; + { LLLocalClipRect clip(text_rect, should_clip); - // draw document view - if (mScroller) + // draw document view + if (mScroller) + { + drawChild(mScroller); + } + else { - drawChild(mScroller); - } - else - { - drawChild(mDocumentView); - } + drawChild(mDocumentView); + } drawSelectionBackground(); drawText(); drawCursor(); } - mDocumentView->setVisible(FALSE); - LLUICtrl::draw(); - mDocumentView->setVisible(TRUE); + mDocumentView->setVisible(FALSE); + LLUICtrl::draw(); + mDocumentView->setVisible(TRUE); } @@ -1119,8 +1118,7 @@ void LLTextBase::updateScrollFromCursor() // scroll so that the cursor is at the top of the page LLRect scroller_doc_window = getVisibleDocumentRect(); - LLRect cursor_rect_doc = getLocalRectFromDocIndex(mCursorPos); - cursor_rect_doc.translate(scroller_doc_window.mLeft, scroller_doc_window.mBottom); + LLRect cursor_rect_doc = getDocRectFromDocIndex(mCursorPos); mScroller->scrollToShowRect(cursor_rect_doc, LLRect(0, scroller_doc_window.getHeight() - 5, scroller_doc_window.getWidth(), 5)); } @@ -1366,9 +1364,9 @@ S32 LLTextBase::getLineStart( S32 line ) const { S32 num_lines = getLineCount(); if (num_lines == 0) - { + { return 0; - } + } line = llclamp(line, 0, num_lines-1); return mLineInfoList[line].mDocIndexStart; @@ -1378,9 +1376,9 @@ S32 LLTextBase::getLineEnd( S32 line ) const { S32 num_lines = getLineCount(); if (num_lines == 0) - { + { return 0; - } + } line = llclamp(line, 0, num_lines-1); return mLineInfoList[line].mDocIndexEnd; @@ -1656,7 +1654,7 @@ void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Para LLUrlMatch match; std::string text = new_text; while ( LLUrlRegistry::instance().findUrl(text, match, - boost::bind(&LLTextBase::replaceUrl, this, _1, _2, _3)) ) + boost::bind(&LLTextBase::replaceUrl, this, _1, _2, _3)) ) { LLTextUtil::processUrlMatch(&match,this); @@ -1949,7 +1947,7 @@ void LLTextBase::setWText(const LLWString& text) const LLWString& LLTextBase::getWText() const { - return getViewModel()->getDisplay(); + return getViewModel()->getDisplay(); } // If round is true, if the position is on the right half of a character, the cursor @@ -1960,9 +1958,12 @@ S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round, { // Figure out which line we're nearest to. LLRect visible_region = getVisibleDocumentRect(); + LLRect doc_rect = mDocumentView->getRect(); + + S32 doc_y = local_y - doc_rect.mBottom; // binary search for line that starts before local_y - line_list_t::const_iterator line_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), local_y - mVisibleTextRect.mBottom + visible_region.mBottom, compare_bottom()); + line_list_t::const_iterator line_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), doc_y, compare_bottom()); if (line_iter == mLineInfoList.end()) { @@ -1970,7 +1971,7 @@ S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round, } S32 pos = getLength(); - S32 start_x = mVisibleTextRect.mLeft + line_iter->mRect.mLeft - visible_region.mLeft; + S32 start_x = line_iter->mRect.mLeft + doc_rect.mLeft; segment_set_t::iterator line_seg_iter; S32 line_seg_offset; @@ -1992,7 +1993,7 @@ S32 LLTextBase::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round, } // if we've reached a line of text *below* the mouse cursor, doc index is first character on that line - if (hit_past_end_of_line && local_y - mVisibleTextRect.mBottom + visible_region.mBottom > line_iter->mRect.mTop) + if (hit_past_end_of_line && doc_y > line_iter->mRect.mTop) { pos = segment_line_start; break; @@ -2461,7 +2462,7 @@ LLRect LLTextBase::getVisibleDocumentRect() const LLRect doc_rect = mDocumentView->getLocalRect(); doc_rect.mLeft -= mDocumentView->getRect().mLeft; // adjust for height of text above widget baseline - doc_rect.mBottom = llmin(0, doc_rect.getHeight() - mVisibleTextRect.getHeight()); + doc_rect.mBottom = doc_rect.getHeight() - mVisibleTextRect.getHeight(); return doc_rect; } } @@ -2575,21 +2576,21 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele LLColor4 color = (mEditor.getReadOnly() ? mStyle->getReadOnlyColor() : mStyle->getColor()) % alpha; - if( selection_start > seg_start ) + if( selection_start > seg_start ) { // Draw normally S32 start = seg_start; S32 end = llmin( selection_start, seg_end ); S32 length = end - start; font->render(text, start, - rect, - color, - LLFontGL::LEFT, mEditor.mVAlign, - LLFontGL::NORMAL, - mStyle->getShadowType(), - length, - &right_x, - mEditor.getUseEllipses()); + rect, + color, + LLFontGL::LEFT, mEditor.mVAlign, + LLFontGL::NORMAL, + mStyle->getShadowType(), + length, + &right_x, + mEditor.getUseEllipses()); } rect.mLeft = (S32)ceil(right_x); @@ -2601,14 +2602,14 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele S32 length = end - start; font->render(text, start, - rect, - mStyle->getSelectedColor().get(), - LLFontGL::LEFT, mEditor.mVAlign, - LLFontGL::NORMAL, - LLFontGL::NO_SHADOW, - length, - &right_x, - mEditor.getUseEllipses()); + rect, + mStyle->getSelectedColor().get(), + LLFontGL::LEFT, mEditor.mVAlign, + LLFontGL::NORMAL, + LLFontGL::NO_SHADOW, + length, + &right_x, + mEditor.getUseEllipses()); } rect.mLeft = (S32)ceil(right_x); if( selection_end < seg_end ) @@ -2618,14 +2619,14 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele S32 end = seg_end; S32 length = end - start; font->render(text, start, - rect, - color, - LLFontGL::LEFT, mEditor.mVAlign, - LLFontGL::NORMAL, - mStyle->getShadowType(), - length, - &right_x, - mEditor.getUseEllipses()); + rect, + color, + LLFontGL::LEFT, mEditor.mVAlign, + LLFontGL::NORMAL, + mStyle->getShadowType(), + length, + &right_x, + mEditor.getUseEllipses()); } return right_x; } 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..9ea1e8815e --- /dev/null +++ b/indra/llui/lltimectrl.cpp @@ -0,0 +1,432 @@ +/** + * @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; +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"), + 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()), + mTime(0), + mSnapToMin(5) +{ + 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->setPrevalidateInput(LLTextValidate::validateNonNegativeS32NoSpace); + mEditor->setPrevalidate(boost::bind(&LLTimeCtrl::isTimeStringValid, this, _1)); + mEditor->setText(LLStringExplicit("12: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 ); +} + +F32 LLTimeCtrl::getTime24() const +{ + // 0.0 - 23.99; + return mTime / 60.0f; +} + +U32 LLTimeCtrl::getHours24() const +{ + return (U32) getTime24(); +} + +U32 LLTimeCtrl::getMinutes() const +{ + return mTime % MINUTES_PER_HOUR; +} + +void LLTimeCtrl::setTime24(F32 time) +{ + time = llclamp(time, 0.0f, 23.99f); // fix out of range values + mTime = llround(time * MINUTES_PER_HOUR); // fixes values like 4.99999 + + updateText(); +} + +BOOL LLTimeCtrl::handleKeyHere(KEY key, MASK mask) +{ + if (mEditor->hasFocus()) + { + if(key == KEY_UP) + { + onUpBtn(); + return TRUE; + } + if(key == KEY_DOWN) + { + onDownBtn(); + return TRUE; + } + if (key == KEY_RETURN) + { + onCommit(); + return TRUE; + } + } + return FALSE; +} + +void LLTimeCtrl::onUpBtn() +{ + switch(getEditingPart()) + { + case HOURS: + increaseHours(); + break; + case MINUTES: + increaseMinutes(); + break; + case DAYPART: + switchDayPeriod(); + break; + default: + break; + } + + updateText(); + onCommit(); +} + +void LLTimeCtrl::onDownBtn() +{ + switch(getEditingPart()) + { + case HOURS: + decreaseHours(); + break; + case MINUTES: + decreaseMinutes(); + break; + case DAYPART: + switchDayPeriod(); + break; + default: + break; + } + + updateText(); + onCommit(); +} + +void LLTimeCtrl::onFocusLost() +{ + updateText(); + onCommit(); + LLUICtrl::onFocusLost(); +} + +void LLTimeCtrl::onTextEntry(LLLineEditor* line_editor) +{ + 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) + { + h12 = 0; + } + + U32 h24 = pm ? h12 + 12 : h12; + + mTime = h24 * MINUTES_PER_HOUR + m; +} + +bool LLTimeCtrl::isTimeStringValid(const LLWString &wstr) +{ + std::string str = wstring_to_utf8str(wstr); + + return isHoursStringValid(getHoursString(str)) && + isMinutesStringValid(getMinutesString(str)) && + isPMAMStringValid(getAMPMString(str)); +} + +void LLTimeCtrl::increaseMinutes() +{ + mTime = (mTime + mSnapToMin) % MINUTES_PER_DAY - (mTime % mSnapToMin); +} + +void LLTimeCtrl::increaseHours() +{ + mTime = (mTime + MINUTES_PER_HOUR) % MINUTES_PER_DAY; +} + +void LLTimeCtrl::decreaseMinutes() +{ + if (mTime < mSnapToMin) + { + mTime = MINUTES_PER_DAY - mTime; + } + + mTime -= (mTime % mSnapToMin) ? mTime % mSnapToMin : mSnapToMin; +} + +void LLTimeCtrl::decreaseHours() +{ + if (mTime < MINUTES_PER_HOUR) + { + mTime = 23 * MINUTES_PER_HOUR + mTime; + } + else + { + mTime -= MINUTES_PER_HOUR; + } +} + +bool LLTimeCtrl::isPM() const +{ + return mTime >= (MINUTES_PER_DAY / 2); +} + +void LLTimeCtrl::switchDayPeriod() +{ + if (isPM()) + { + mTime -= MINUTES_PER_DAY / 2; + } + else + { + mTime += MINUTES_PER_DAY / 2; + } +} + +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 cur_pos = mEditor->getCursor(); + std::string time_str = mEditor->getText(); + + 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)) + { + return MINUTES; + } + else if (cur_pos > (S32)(time_str.length() - AMPM_LEN)) + { + return DAYPART; + } + + return NONE; +} + +// static +std::string LLTimeCtrl::getHoursString(const std::string& str) +{ + size_t colon_index = str.find_first_of(':'); + std::string hours_str = str.substr(0, colon_index); + + return hours_str; +} + +// static +std::string LLTimeCtrl::getMinutesString(const std::string& str) +{ + size_t colon_index = str.find_first_of(':'); + ++colon_index; + + int minutes_len = str.length() - colon_index - AMPM_LEN; + std::string minutes_str = str.substr(colon_index, minutes_len); + + return minutes_str; +} + +// static +std::string LLTimeCtrl::getAMPMString(const std::string& str) +{ + return str.substr(str.size() - 2, 2); // returns last two characters +} + +// static +bool LLTimeCtrl::isHoursStringValid(const std::string& str) +{ + U32 hours; + if ((!LLStringUtil::convertToU32(str, hours) || (hours <= HOURS_MAX)) && str.length() < 3) + return true; + + return false; +} + +// static +bool LLTimeCtrl::isMinutesStringValid(const std::string& str) +{ + U32 minutes; + if (!LLStringUtil::convertToU32(str, minutes) || (minutes <= MINUTES_MAX) && str.length() < 3) + return true; + + return false; +} + +// static +bool LLTimeCtrl::isPMAMStringValid(const std::string& str) +{ + S32 len = str.length(); + + bool valid = (str[--len] == 'M') && (str[--len] == 'P' || str[len] == 'A'); + + return valid; +} + +// static +U32 LLTimeCtrl::parseHours(const std::string& str) +{ + U32 hours; + if (LLStringUtil::convertToU32(str, hours) && (hours >= HOURS_MIN) && (hours <= HOURS_MAX)) + { + return hours; + } + else + { + return HOURS_MIN; + } +} + +// static +U32 LLTimeCtrl::parseMinutes(const std::string& str) +{ + U32 minutes; + if (LLStringUtil::convertToU32(str, minutes) && (minutes >= MINUTES_MIN) && (minutes <= MINUTES_MAX)) + { + return minutes; + } + else + { + return MINUTES_MIN; + } +} + +// static +bool LLTimeCtrl::parseAMPM(const std::string& str) +{ + return str == "PM"; +} diff --git a/indra/llui/lltimectrl.h b/indra/llui/lltimectrl.h new file mode 100644 index 0000000000..b5f268c76a --- /dev/null +++ b/indra/llui/lltimectrl.h @@ -0,0 +1,131 @@ +/** + * @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 +{ + LOG_CLASS(LLTimeCtrl); +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; + Optional<LLUIColor> text_disabled_color; + + Optional<LLButton::Params> up_button; + Optional<LLButton::Params> down_button; + + 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; + +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); + + bool isTimeStringValid(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; + LLUIColor mTextEnabledColor; + LLUIColor mTextDisabledColor; + + class LLButton* mUpBtn; + class LLButton* mDownBtn; + + U32 mTime; // minutes since midnight: 0 - 1439 + U32 mSnapToMin; // interval in minutes to snap to + + BOOL mAllowEdit; +}; +#endif /* LLTIMECTRL_H_ */ diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp index 0a06b5e74f..d58df5801b 100644 --- a/indra/llui/lluictrl.cpp +++ b/indra/llui/lluictrl.cpp @@ -68,6 +68,7 @@ LLUICtrl::ControlVisibility::ControlVisibility() LLUICtrl::Params::Params() : tab_stop("tab_stop", true), chrome("chrome", false), + requests_front("requests_front", false), label("label"), initial_value("value"), init_callback("init_callback"), @@ -96,9 +97,10 @@ const LLUICtrl::Params& LLUICtrl::getDefaultParams() LLUICtrl::LLUICtrl(const LLUICtrl::Params& p, const LLViewModelPtr& viewmodel) : LLView(p), - mTentative(FALSE), mIsChrome(FALSE), + mRequestsFront(p.requests_front), mTabStop(FALSE), + mTentative(FALSE), mViewModel(viewmodel), mControlVariable(NULL), mEnabledControlVariable(NULL), @@ -123,6 +125,8 @@ void LLUICtrl::initFromParams(const Params& p) { LLView::initFromParams(p); + mRequestsFront = p.requests_front; + setIsChrome(p.chrome); setControlName(p.control_name); if(p.enabled_controls.isProvided()) @@ -403,6 +407,36 @@ LLViewModel* LLUICtrl::getViewModel() const return mViewModel; } +//virtual +BOOL LLUICtrl::postBuild() +{ + // + // Find all of the children that want to be in front and move them to the front + // + + if (getChildCount() > 0) + { + std::vector<LLUICtrl*> childrenToMoveToFront; + + for (LLView::child_list_const_iter_t child_it = beginChild(); child_it != endChild(); ++child_it) + { + LLUICtrl* uictrl = dynamic_cast<LLUICtrl*>(*child_it); + + if (uictrl && uictrl->mRequestsFront) + { + childrenToMoveToFront.push_back(uictrl); + } + } + + for (std::vector<LLUICtrl*>::iterator it = childrenToMoveToFront.begin(); it != childrenToMoveToFront.end(); ++it) + { + sendChildToFront(*it); + } + } + + return LLView::postBuild(); +} + bool LLUICtrl::setControlValue(const LLSD& value) { if (mControlVariable) diff --git a/indra/llui/lluictrl.h b/indra/llui/lluictrl.h index b37e9f6b1b..09bed9b958 100644 --- a/indra/llui/lluictrl.h +++ b/indra/llui/lluictrl.h @@ -94,7 +94,8 @@ public: { Optional<std::string> label; Optional<bool> tab_stop, - chrome; + chrome, + requests_front; Optional<LLSD> initial_value; Optional<CommitCallbackParam> init_callback, @@ -143,6 +144,8 @@ protected: virtual LLViewModel* getViewModel() const; // We shouldn't ever need to set this directly //virtual void setViewModel(const LLViewModelPtr&); + + virtual BOOL postBuild(); public: // LLView interface @@ -301,8 +304,9 @@ protected: private: - BOOL mTabStop; BOOL mIsChrome; + BOOL mRequestsFront; + BOOL mTabStop; BOOL mTentative; LLRootHandle<LLUICtrl> mUICtrlHandle; diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index 245126d178..8803d106ba 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -1299,15 +1299,7 @@ void LLView::drawChildren() { if (!mChildList.empty()) { - static const LLRect* rootRect = NULL; - - if (!mParentView) - { - rootRect = &mRect; - } - - LLRect screenRect; - + LLView* rootp = LLUI::getRootView(); ++sDepth; for (child_list_reverse_iter_t child_iter = mChildList.rbegin(); child_iter != mChildList.rend();) // ++child_iter) @@ -1317,9 +1309,8 @@ void LLView::drawChildren() if (viewp->getVisible() && viewp->getRect().isValid()) { - // Only draw views that are within the root view - localRectToScreen(viewp->getRect(),&screenRect); - if ( rootRect->overlaps(screenRect) && LLUI::sDirtyRect.overlaps(screenRect)) + LLRect screen_rect = viewp->calcScreenRect(); + if ( rootp->getLocalRect().overlaps(screen_rect) && LLUI::sDirtyRect.overlaps(screen_rect)) { LLUI::pushMatrix(); { diff --git a/indra/llui/tests/llurlmatch_test.cpp b/indra/llui/tests/llurlmatch_test.cpp index e09ef33d49..3cd61e574e 100644 --- a/indra/llui/tests/llurlmatch_test.cpp +++ b/indra/llui/tests/llurlmatch_test.cpp @@ -95,7 +95,7 @@ namespace LLInitParam { const U8* my_addr = reinterpret_cast<const U8*>(this); const U8* block_addr = reinterpret_cast<const U8*>(enclosing_block); - mEnclosingBlockOffset = (U16)(my_addr - block_addr); + mEnclosingBlockOffset = 0x7FFFffff & ((U32)(my_addr - block_addr)); } bool BaseBlock::deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack, S32 generation){ return true; } |