diff options
Diffstat (limited to 'indra/llui')
46 files changed, 934 insertions, 221 deletions
diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index 063e3906c7..33ab2e93b5 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -160,6 +160,7 @@ set(llui_HEADER_FILES llnotificationslistener.h llnotificationsutil.h llnotificationtemplate.h + llnotificationvisibilityrule.h llpanel.h llprogressbar.h llradiogroup.h diff --git a/indra/llui/llaccordionctrltab.cpp b/indra/llui/llaccordionctrltab.cpp index 9d49c1a831..9e4849c58b 100644 --- a/indra/llui/llaccordionctrltab.cpp +++ b/indra/llui/llaccordionctrltab.cpp @@ -203,7 +203,8 @@ void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::draw() S32 width = getRect().getWidth(); S32 height = getRect().getHeight(); - gl_rect_2d(0,0,width - 1 ,height - 1,mHeaderBGColor.get(),true); + F32 alpha = getCurrentTransparency(); + gl_rect_2d(0,0,width - 1 ,height - 1,mHeaderBGColor.get() % alpha,true); LLAccordionCtrlTab* parent = dynamic_cast<LLAccordionCtrlTab*>(getParent()); bool collapsible = (parent && parent->getCollapsible()); diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp index 65ef3e5f8f..45ceaff696 100644 --- a/indra/llui/llbutton.cpp +++ b/indra/llui/llbutton.cpp @@ -98,7 +98,8 @@ LLButton::Params::Params() is_toggle("is_toggle", false), scale_image("scale_image", true), hover_glow_amount("hover_glow_amount"), - commit_on_return("commit_on_return", true) + commit_on_return("commit_on_return", true), + use_draw_context_alpha("use_draw_context_alpha", true) { addSynonym(is_toggle, "toggle"); held_down_delay.seconds = 0.5f; @@ -158,7 +159,8 @@ LLButton::LLButton(const LLButton::Params& p) mLastDrawCharsCount(0), mMouseDownSignal(NULL), mMouseUpSignal(NULL), - mHeldDownSignal(NULL) + mHeldDownSignal(NULL), + mUseDrawContextAlpha(p.use_draw_context_alpha) { static LLUICachedControl<S32> llbutton_orig_h_pad ("UIButtonOrigHPad", 0); @@ -539,7 +541,7 @@ BOOL LLButton::handleHover(S32 x, S32 y, MASK mask) // virtual void LLButton::draw() { - F32 alpha = getDrawContext().mAlpha; + F32 alpha = mUseDrawContextAlpha ? getDrawContext().mAlpha : getCurrentTransparency(); bool flash = FALSE; static LLUICachedControl<F32> button_flash_rate("ButtonFlashRate", 0); static LLUICachedControl<S32> button_flash_count("ButtonFlashCount", 0); diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h index 2d5fefa78c..16aa49b653 100644 --- a/indra/llui/llbutton.h +++ b/indra/llui/llbutton.h @@ -124,6 +124,8 @@ public: Optional<F32> hover_glow_amount; Optional<TimeIntervalParam> held_down_delay; + Optional<bool> use_draw_context_alpha; + Params(); }; @@ -338,6 +340,8 @@ private: S32 mImageOverlayTopPad; S32 mImageOverlayBottomPad; + bool mUseDrawContextAlpha; + /* * Space between image_overlay and label */ diff --git a/indra/llui/llcheckboxctrl.cpp b/indra/llui/llcheckboxctrl.cpp index bbd8db2645..4fe444c1a4 100644 --- a/indra/llui/llcheckboxctrl.cpp +++ b/indra/llui/llcheckboxctrl.cpp @@ -88,27 +88,19 @@ LLCheckBoxCtrl::LLCheckBoxCtrl(const LLCheckBoxCtrl::Params& p) tbparams.font(p.font); } mLabel = LLUICtrlFactory::create<LLTextBox> (tbparams); + mLabel->reshapeToFitText(); addChild(mLabel); - S32 text_width = mLabel->getTextBoundingRect().getWidth(); - S32 text_height = llround(mFont->getLineHeight()); - LLRect label_rect; - label_rect.setOriginAndSize( - llcheckboxctrl_hpad + llcheckboxctrl_btn_size + llcheckboxctrl_spacing, - llcheckboxctrl_vpad + 1, // padding to get better alignment - text_width + llcheckboxctrl_hpad, - text_height ); - mLabel->setShape(label_rect); - + LLRect label_rect = mLabel->getRect(); // Button // Note: button cover the label by extending all the way to the right. - LLRect btn_rect; + LLRect btn_rect = p.check_button.rect(); btn_rect.setOriginAndSize( - llcheckboxctrl_hpad, - llcheckboxctrl_vpad, - llcheckboxctrl_btn_size + llcheckboxctrl_spacing + text_width + llcheckboxctrl_hpad, - llmax( text_height, llcheckboxctrl_btn_size() ) + llcheckboxctrl_vpad); + btn_rect.mLeft, + btn_rect.mBottom, + llmax(btn_rect.mRight, label_rect.mRight - btn_rect.mLeft), + llmax( label_rect.getHeight(), btn_rect.mTop)); std::string active_true_id, active_false_id; std::string inactive_true_id, inactive_false_id; @@ -174,31 +166,20 @@ void LLCheckBoxCtrl::clear() void LLCheckBoxCtrl::reshape(S32 width, S32 height, BOOL called_from_parent) { - //stretch or shrink bounding rectangle of label when rebuilding UI at new scale - static LLUICachedControl<S32> llcheckboxctrl_spacing ("UICheckboxctrlSpacing", 0); - static LLUICachedControl<S32> llcheckboxctrl_hpad ("UICheckboxctrlHPad", 0); - static LLUICachedControl<S32> llcheckboxctrl_vpad ("UICheckboxctrlVPad", 0); - static LLUICachedControl<S32> llcheckboxctrl_btn_size ("UICheckboxctrlBtnSize", 0); - S32 text_width = mLabel->getTextBoundingRect().getWidth(); - S32 text_height = llround(mFont->getLineHeight()); - LLRect label_rect; - label_rect.setOriginAndSize( - llcheckboxctrl_hpad + llcheckboxctrl_btn_size + llcheckboxctrl_spacing, - llcheckboxctrl_vpad, - text_width, - text_height ); - mLabel->setShape(label_rect); - - LLRect btn_rect; + mLabel->reshapeToFitText(); + + LLRect label_rect = mLabel->getRect(); + + // Button + // Note: button cover the label by extending all the way to the right. + LLRect btn_rect = mButton->getRect(); btn_rect.setOriginAndSize( - llcheckboxctrl_hpad, - llcheckboxctrl_vpad, - llcheckboxctrl_btn_size + llcheckboxctrl_spacing + text_width, - llmax( text_height, llcheckboxctrl_btn_size() ) ); - mButton->setShape( btn_rect ); - - LLUICtrl::reshape(width, height, called_from_parent); + btn_rect.mLeft, + btn_rect.mBottom, + llmax(btn_rect.getWidth(), label_rect.mRight - btn_rect.mLeft), + llmax(label_rect.mTop - btn_rect.mBottom, btn_rect.getHeight())); + mButton->setShape(btn_rect); } //virtual diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp index 70014fe4f5..6b06040b8a 100644 --- a/indra/llui/llcombobox.cpp +++ b/indra/llui/llcombobox.cpp @@ -769,7 +769,8 @@ BOOL LLComboBox::handleKeyHere(KEY key, MASK mask) return FALSE; } // if selection has changed, pop open list - else if ((mList->getLastSelectedItem() != last_selected_item) || (key == KEY_DOWN) || (key == KEY_UP)) + else if (mList->getLastSelectedItem() != last_selected_item || + (key == KEY_DOWN || key == KEY_UP) && !mList->isEmpty()) { showList(); } diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index 34d8e9c500..1265733bf5 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -1,4 +1,5 @@ /** + * @file llfloater.cpp * @brief LLFloater base class * @@ -61,10 +62,6 @@ // use this to control "jumping" behavior when Ctrl-Tabbing const S32 TABBED_FLOATER_OFFSET = 0; -// static -F32 LLFloater::sActiveFloaterTransparency = 0.0f; -F32 LLFloater::sInactiveFloaterTransparency = 0.0f; - std::string LLFloater::sButtonNames[BUTTON_COUNT] = { "llfloater_close_btn", //BUTTON_CLOSE @@ -208,14 +205,14 @@ void LLFloater::initClass() if (ctrl) { ctrl->getSignal()->connect(boost::bind(&LLFloater::updateActiveFloaterTransparency)); - sActiveFloaterTransparency = LLUI::sSettingGroups["config"]->getF32("ActiveFloaterTransparency"); + updateActiveFloaterTransparency(); } ctrl = LLUI::sSettingGroups["config"]->getControl("InactiveFloaterTransparency").get(); if (ctrl) { ctrl->getSignal()->connect(boost::bind(&LLFloater::updateInactiveFloaterTransparency)); - sInactiveFloaterTransparency = LLUI::sSettingGroups["config"]->getF32("InactiveFloaterTransparency"); + updateInactiveFloaterTransparency(); } } @@ -225,7 +222,7 @@ static LLWidgetNameRegistry::StaticRegistrar sRegisterFloaterParams(&typeid(LLFl LLFloater::LLFloater(const LLSD& key, const LLFloater::Params& p) : LLPanel(), // intentionally do not pass params here, see initFromParams - mDragHandle(NULL), + mDragHandle(NULL), mTitle(p.title), mShortTitle(p.short_title), mSingleInstance(p.single_instance), @@ -368,13 +365,13 @@ void LLFloater::layoutDragHandle() // static void LLFloater::updateActiveFloaterTransparency() { - sActiveFloaterTransparency = LLUI::sSettingGroups["config"]->getF32("ActiveFloaterTransparency"); + sActiveControlTransparency = LLUI::sSettingGroups["config"]->getF32("ActiveFloaterTransparency"); } // static void LLFloater::updateInactiveFloaterTransparency() { - sInactiveFloaterTransparency = LLUI::sSettingGroups["config"]->getF32("InactiveFloaterTransparency"); + sInactiveControlTransparency = LLUI::sSettingGroups["config"]->getF32("InactiveFloaterTransparency"); } void LLFloater::addResizeCtrls() @@ -1193,6 +1190,7 @@ void LLFloater::setFocus( BOOL b ) last_focus->setFocus(TRUE); } } + updateTransparency(b ? TT_ACTIVE : TT_INACTIVE); } // virtual @@ -1465,6 +1463,9 @@ void LLFloater::setFrontmost(BOOL take_focus) // there are more than one floater view // so we need to query our parent directly ((LLFloaterView*)getParent())->bringToFront(this, take_focus); + + // Make sure to set the appropriate transparency type (STORM-732). + updateTransparency(hasFocus() || getIsChrome() ? TT_ACTIVE : TT_INACTIVE); } } @@ -1652,7 +1653,7 @@ void LLFloater::onClickCloseBtn() // virtual void LLFloater::draw() { - mCurrentTransparency = hasFocus() ? sActiveFloaterTransparency : sInactiveFloaterTransparency; + const F32 alpha = getCurrentTransparency(); // draw background if( isBackgroundVisible() ) @@ -1684,12 +1685,12 @@ void LLFloater::draw() if (image) { // We're using images for this floater's backgrounds - image->draw(getLocalRect(), overlay_color % mCurrentTransparency); + image->draw(getLocalRect(), overlay_color % alpha); } else { // We're not using images, use old-school flat colors - gl_rect_2d( left, top, right, bottom, color % mCurrentTransparency ); + gl_rect_2d( left, top, right, bottom, color % alpha ); // draw highlight on title bar to indicate focus. RDW if(hasFocus() @@ -1701,7 +1702,7 @@ void LLFloater::draw() const LLFontGL* font = LLFontGL::getFontSansSerif(); LLRect r = getRect(); gl_rect_2d_offset_local(0, r.getHeight(), r.getWidth(), r.getHeight() - (S32)font->getLineHeight() - 1, - titlebar_focus_color % mCurrentTransparency, 0, TRUE); + titlebar_focus_color % alpha, 0, TRUE); } } } @@ -1767,10 +1768,32 @@ void LLFloater::drawShadow(LLPanel* panel) shadow_color.mV[VALPHA] *= 0.5f; } gl_drop_shadow(left, top, right, bottom, - shadow_color % mCurrentTransparency, + shadow_color % getCurrentTransparency(), llround(shadow_offset)); } +void LLFloater::updateTransparency(LLView* view, ETypeTransparency transparency_type) +{ + child_list_t children = *view->getChildList(); + child_list_t::iterator it = children.begin(); + + LLUICtrl* ctrl = dynamic_cast<LLUICtrl*>(view); + if (ctrl) + { + ctrl->setTransparencyType(transparency_type); + } + + for(; it != children.end(); ++it) + { + updateTransparency(*it, transparency_type); + } +} + +void LLFloater::updateTransparency(ETypeTransparency transparency_type) +{ + updateTransparency(this, transparency_type); +} + void LLFloater::setCanMinimize(BOOL can_minimize) { // if removing minimize/restore button programmatically, diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h index fa806bb632..bb96272d02 100644 --- a/indra/llui/llfloater.h +++ b/indra/llui/llfloater.h @@ -284,6 +284,8 @@ public: static void setFloaterHost(LLMultiFloater* hostp) {sHostp = hostp; } static LLMultiFloater* getFloaterHost() {return sHostp; } + + void updateTransparency(ETypeTransparency transparency_type); protected: @@ -343,6 +345,7 @@ private: static void updateActiveFloaterTransparency(); static void updateInactiveFloaterTransparency(); + void updateTransparency(LLView* view, ETypeTransparency transparency_type); public: // Called when floater is opened, passes mKey @@ -411,11 +414,6 @@ private: bool mDocked; bool mTornOff; - F32 mCurrentTransparency; - - static F32 sActiveFloaterTransparency; - static F32 sInactiveFloaterTransparency; - static LLMultiFloater* sHostp; static BOOL sQuitting; static std::string sButtonNames[BUTTON_COUNT]; diff --git a/indra/llui/llhandle.h b/indra/llui/llhandle.h index a43f095d67..8c000eee48 100644 --- a/indra/llui/llhandle.h +++ b/indra/llui/llhandle.h @@ -61,13 +61,6 @@ public: return *this; } - template<typename Subclass> - LLHandle<T>& operator =(const LLHandle<Subclass>& other) - { - mTombStone = other.mTombStone; - return *this; - } - bool isDead() const { return mTombStone->getTarget() == NULL; @@ -99,7 +92,6 @@ public: { return lhs.mTombStone > rhs.mTombStone; } -protected: protected: LLPointer<LLTombStone<T> > mTombStone; diff --git a/indra/llui/lliconctrl.cpp b/indra/llui/lliconctrl.cpp index 627957061d..47f2cfaf89 100644 --- a/indra/llui/lliconctrl.cpp +++ b/indra/llui/lliconctrl.cpp @@ -41,6 +41,7 @@ static LLDefaultChildRegistry::Register<LLIconCtrl> r("icon"); LLIconCtrl::Params::Params() : image("image_name"), color("color"), + use_draw_context_alpha("use_draw_context_alpha", true), scale_image("scale_image") { tab_stop = false; @@ -51,6 +52,7 @@ LLIconCtrl::LLIconCtrl(const LLIconCtrl::Params& p) : LLUICtrl(p), mColor(p.color()), mImagep(p.image), + mUseDrawContextAlpha(p.use_draw_context_alpha), mPriority(0), mDrawWidth(0), mDrawHeight(0) @@ -71,7 +73,8 @@ void LLIconCtrl::draw() { if( mImagep.notNull() ) { - mImagep->draw(getLocalRect(), mColor.get() % getDrawContext().mAlpha ); + const F32 alpha = mUseDrawContextAlpha ? getDrawContext().mAlpha : getCurrentTransparency(); + mImagep->draw(getLocalRect(), mColor.get() % alpha ); } LLUICtrl::draw(); diff --git a/indra/llui/lliconctrl.h b/indra/llui/lliconctrl.h index 79a8b0fb28..e9bdab2d47 100644 --- a/indra/llui/lliconctrl.h +++ b/indra/llui/lliconctrl.h @@ -48,6 +48,7 @@ public: { Optional<LLUIImage*> image; Optional<LLUIColor> color; + Optional<bool> use_draw_context_alpha; Ignored scale_image; Params(); }; @@ -79,6 +80,10 @@ protected: S32 mDrawWidth ; S32 mDrawHeight ; + // If set to true (default), use the draw context transparency. + // If false, will use transparency returned by getCurrentTransparency(). See STORM-698. + bool mUseDrawContextAlpha; + private: LLUIColor mColor; LLPointer<LLUIImage> mImagep; diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp index 6c0a2a9a10..19ac4c58a8 100644 --- a/indra/llui/lllayoutstack.cpp +++ b/indra/llui/lllayoutstack.cpp @@ -103,6 +103,8 @@ LLLayoutStack::Params::Params() : orientation("orientation"), animate("animate", true), clip("clip", true), + open_time_constant("open_time_constant", 0.02f), + close_time_constant("close_time_constant", 0.03f), border_size("border_size", LLCachedControl<S32>(*LLUI::sSettingGroups["config"], "UIResizeBarHeight", 0)) { name="stack"; @@ -116,7 +118,9 @@ LLLayoutStack::LLLayoutStack(const LLLayoutStack::Params& p) mOrientation(p.orientation), mAnimate(p.animate), mAnimatedThisFrame(false), - mClip(p.clip) + mClip(p.clip), + mOpenTimeConstant(p.open_time_constant), + mCloseTimeConstant(p.close_time_constant) {} LLLayoutStack::~LLLayoutStack() @@ -309,9 +313,6 @@ void LLLayoutStack::updateLayout(BOOL force_resize) S32 total_width = 0; S32 total_height = 0; - const F32 ANIM_OPEN_TIME = 0.02f; - const F32 ANIM_CLOSE_TIME = 0.03f; - e_panel_list_t::iterator panel_it; for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) { @@ -322,7 +323,7 @@ void LLLayoutStack::updateLayout(BOOL force_resize) { if (!mAnimatedThisFrame) { - (*panel_it)->mVisibleAmt = lerp((*panel_it)->mVisibleAmt, 1.f, LLCriticalDamp::getInterpolant(ANIM_OPEN_TIME)); + (*panel_it)->mVisibleAmt = lerp((*panel_it)->mVisibleAmt, 1.f, LLCriticalDamp::getInterpolant(mOpenTimeConstant)); if ((*panel_it)->mVisibleAmt > 0.99f) { (*panel_it)->mVisibleAmt = 1.f; @@ -340,7 +341,7 @@ void LLLayoutStack::updateLayout(BOOL force_resize) { if (!mAnimatedThisFrame) { - (*panel_it)->mVisibleAmt = lerp((*panel_it)->mVisibleAmt, 0.f, LLCriticalDamp::getInterpolant(ANIM_CLOSE_TIME)); + (*panel_it)->mVisibleAmt = lerp((*panel_it)->mVisibleAmt, 0.f, LLCriticalDamp::getInterpolant(mCloseTimeConstant)); if ((*panel_it)->mVisibleAmt < 0.001f) { (*panel_it)->mVisibleAmt = 0.f; @@ -355,11 +356,11 @@ void LLLayoutStack::updateLayout(BOOL force_resize) if ((*panel_it)->mCollapsed) { - (*panel_it)->mCollapseAmt = lerp((*panel_it)->mCollapseAmt, 1.f, LLCriticalDamp::getInterpolant(ANIM_CLOSE_TIME)); + (*panel_it)->mCollapseAmt = lerp((*panel_it)->mCollapseAmt, 1.f, LLCriticalDamp::getInterpolant(mCloseTimeConstant)); } else { - (*panel_it)->mCollapseAmt = lerp((*panel_it)->mCollapseAmt, 0.f, LLCriticalDamp::getInterpolant(ANIM_CLOSE_TIME)); + (*panel_it)->mCollapseAmt = lerp((*panel_it)->mCollapseAmt, 0.f, LLCriticalDamp::getInterpolant(mCloseTimeConstant)); } if (mOrientation == HORIZONTAL) diff --git a/indra/llui/lllayoutstack.h b/indra/llui/lllayoutstack.h index e43669d893..4ac8ef0ee9 100644 --- a/indra/llui/lllayoutstack.h +++ b/indra/llui/lllayoutstack.h @@ -58,6 +58,8 @@ public: Optional<S32> border_size; Optional<bool> animate, clip; + Optional<F32> open_time_constant, + close_time_constant; Params(); }; @@ -143,6 +145,8 @@ private: bool mAnimatedThisFrame; bool mAnimate; bool mClip; + F32 mOpenTimeConstant; + F32 mCloseTimeConstant; }; // end class LLLayoutStack class LLLayoutPanel : public LLPanel diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index 6ad1a0bac4..7e348656a9 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -1530,8 +1530,11 @@ void LLLineEditor::drawBackground() { image = mBgImage; } + + if (!image) return; - F32 alpha = getDrawContext().mAlpha; + F32 alpha = getCurrentTransparency(); + // optionally draw programmatic border if (has_focus) { diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h index d9d36b73a3..723423a5b9 100644 --- a/indra/llui/lllineeditor.h +++ b/indra/llui/lllineeditor.h @@ -337,7 +337,7 @@ protected: std::vector<S32> mPreeditPositions; LLPreeditor::standouts_t mPreeditStandouts; - LLHandle<LLView> mContextMenuHandle; + LLHandle<LLContextMenu> mContextMenuHandle; private: // Instances that by default point to the statics but can be overidden in XML. diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index a6cf86d9b8..6c0d47ef63 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -1462,7 +1462,7 @@ BOOL LLMenuItemBranchDownGL::handleAcceleratorKey(KEY key, MASK mask) { BOOL branch_visible = getBranch()->getVisible(); BOOL handled = getBranch()->handleAcceleratorKey(key, mask); - if (handled && !branch_visible && getVisible()) + if (handled && !branch_visible && isInVisibleChain()) { // flash this menu entry because we triggered an invisible menu item LLMenuHolderGL::setActivatedItem(this); @@ -2611,6 +2611,7 @@ LLMenuItemGL* LLMenuGL::getHighlightedItem() LLMenuItemGL* LLMenuGL::highlightNextItem(LLMenuItemGL* cur_item, BOOL skip_disabled) { + if (mItems.empty()) return NULL; // highlighting first item on a torn off menu is the // same as giving focus to it if (!cur_item && getTornOff()) @@ -2711,6 +2712,8 @@ LLMenuItemGL* LLMenuGL::highlightNextItem(LLMenuItemGL* cur_item, BOOL skip_disa LLMenuItemGL* LLMenuGL::highlightPrevItem(LLMenuItemGL* cur_item, BOOL skip_disabled) { + if (mItems.empty()) return NULL; + // highlighting first item on a torn off menu is the // same as giving focus to it if (!cur_item && getTornOff()) @@ -3045,6 +3048,11 @@ void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y) const S32 CURSOR_HEIGHT = 22; // Approximate "normal" cursor size const S32 CURSOR_WIDTH = 12; + if(menu->getChildList()->empty()) + { + return; + } + // Save click point for detecting cursor moves before mouse-up. // Must be in local coords to compare with mouseUp events. // If the mouse doesn't move, the menu will stay open ala the Mac. @@ -3125,7 +3133,10 @@ BOOL LLMenuBarGL::handleAcceleratorKey(KEY key, MASK mask) mAltKeyTrigger = FALSE; } - if(!result && (key == KEY_F10 && mask == MASK_CONTROL) && !gKeyboard->getKeyRepeated(key)) + if(!result + && (key == KEY_F10 && mask == MASK_CONTROL) + && !gKeyboard->getKeyRepeated(key) + && isInVisibleChain()) { if (getHighlightedItem()) { @@ -3508,8 +3519,10 @@ BOOL LLMenuHolderGL::handleKey(KEY key, MASK mask, BOOL called_from_parent) else { //highlight first enabled one - pMenu->highlightNextItem(NULL); - handled = true; + if(pMenu->highlightNextItem(NULL)) + { + handled = true; + } } } } @@ -3742,9 +3755,7 @@ public: LLContextMenuBranch(const Params&); virtual ~LLContextMenuBranch() - { - delete mBranch; - } + {} // called to rebuild the draw label virtual void buildDrawLabel( void ); @@ -3752,21 +3763,21 @@ public: // onCommit() - do the primary funcationality of the menu item. virtual void onCommit( void ); - LLContextMenu* getBranch() { return mBranch; } + LLContextMenu* getBranch() { return mBranch.get(); } void setHighlight( BOOL highlight ); protected: void showSubMenu(); - LLContextMenu* mBranch; + LLHandle<LLContextMenu> mBranch; }; LLContextMenuBranch::LLContextMenuBranch(const LLContextMenuBranch::Params& p) : LLMenuItemGL(p), - mBranch( p.branch ) + mBranch( p.branch()->getHandle() ) { - mBranch->hide(); - mBranch->setParentMenuItem(this); + mBranch.get()->hide(); + mBranch.get()->setParentMenuItem(this); } // called to rebuild the draw label @@ -3775,12 +3786,12 @@ void LLContextMenuBranch::buildDrawLabel( void ) { // default enablement is this -- if any of the subitems are // enabled, this item is enabled. JC - U32 sub_count = mBranch->getItemCount(); + U32 sub_count = mBranch.get()->getItemCount(); U32 i; BOOL any_enabled = FALSE; for (i = 0; i < sub_count; i++) { - LLMenuItemGL* item = mBranch->getItem(i); + LLMenuItemGL* item = mBranch.get()->getItem(i); item->buildDrawLabel(); if (item->getEnabled() && !item->getDrawTextDisabled() ) { @@ -3802,13 +3813,13 @@ void LLContextMenuBranch::buildDrawLabel( void ) void LLContextMenuBranch::showSubMenu() { - LLMenuItemGL* menu_item = mBranch->getParentMenuItem(); + LLMenuItemGL* menu_item = mBranch.get()->getParentMenuItem(); if (menu_item != NULL && menu_item->getVisible()) { S32 center_x; S32 center_y; localPointToScreen(getRect().getWidth(), getRect().getHeight() , ¢er_x, ¢er_y); - mBranch->show(center_x, center_y); + mBranch.get()->show(center_x, center_y); } } @@ -3828,7 +3839,7 @@ void LLContextMenuBranch::setHighlight( BOOL highlight ) } else { - mBranch->hide(); + mBranch.get()->hide(); } } @@ -3859,6 +3870,11 @@ void LLContextMenu::setVisible(BOOL visible) // Takes cursor position in screen space? void LLContextMenu::show(S32 x, S32 y) { + if (getChildList()->empty()) + { + // nothing to show, so abort + return; + } // Save click point for detecting cursor moves before mouse-up. // Must be in local coords to compare with mouseUp events. // If the mouse doesn't move, the menu will stay open ala the Mac. diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h index 35544402f4..7bde8e83ec 100644 --- a/indra/llui/llmenugl.h +++ b/indra/llui/llmenugl.h @@ -678,9 +678,12 @@ public: BOOL appendContextSubMenu(LLContextMenu *menu); + LLHandle<LLContextMenu> getHandle() { mHandle.bind(this); return mHandle; } + protected: - BOOL mHoveredAnyItem; - LLMenuItemGL* mHoverItem; + BOOL mHoveredAnyItem; + LLMenuItemGL* mHoverItem; + LLRootHandle<LLContextMenu> mHandle; }; diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp index 15edf270bd..cd0f0e36b0 100644 --- a/indra/llui/llnotifications.cpp +++ b/indra/llui/llnotifications.cpp @@ -28,6 +28,7 @@ #include "llnotifications.h" #include "llnotificationtemplate.h" +#include "llnotificationvisibilityrule.h" #include "llavatarnamecache.h" #include "llinstantmessage.h" @@ -138,12 +139,6 @@ private: bool filterIgnoredNotifications(LLNotificationPtr notification) { - // filter everything if we are to ignore ALL - if(LLNotifications::instance().getIgnoreAllNotifications()) - { - return false; - } - LLNotificationFormPtr form = notification->getForm(); // Check to see if the user wants to ignore this alert return !notification->getForm()->getIgnored(); @@ -178,6 +173,28 @@ bool handleIgnoredNotification(const LLSD& payload) return false; } +bool defaultResponse(const LLSD& payload) +{ + if (payload["sigtype"].asString() == "add") + { + LLNotificationPtr pNotif = LLNotifications::instance().find(payload["id"].asUUID()); + if (pNotif) + { + // supply default response + pNotif->respond(pNotif->getResponseTemplate(LLNotification::WITH_DEFAULT_BUTTON)); + } + } + return false; +} + +bool visibilityRuleMached(const LLSD& payload) +{ + // This is needed because LLNotifications::isVisibleByRules may have cancelled the notification. + // Returning true here makes LLNotificationChannelBase::updateItem do an early out, which prevents things from happening in the wrong order. + return true; +} + + namespace LLNotificationFilters { // a sample filter @@ -407,10 +424,47 @@ LLNotificationTemplate::LLNotificationTemplate(const LLNotificationTemplate::Par { mUniqueContext.push_back(it->value); } + + lldebugs << "notification \"" << mName << "\": tag count is " << p.tags.size() << llendl; + + for(LLInitParam::ParamIterator<LLNotificationTemplate::Tag>::const_iterator it = p.tags.begin(), + end_it = p.tags.end(); + it != end_it; + ++it) + { + lldebugs << " tag \"" << std::string(it->value) << "\"" << llendl; + mTags.push_back(it->value); + } mForm = LLNotificationFormPtr(new LLNotificationForm(p.name, p.form_ref.form)); } +LLNotificationVisibilityRule::LLNotificationVisibilityRule(const LLNotificationVisibilityRule::Rule &p) +{ + if (p.show.isChosen()) + { + mType = p.show.type; + mTag = p.show.tag; + mName = p.show.name; + mVisible = true; + } + else if (p.hide.isChosen()) + { + mType = p.hide.type; + mTag = p.hide.tag; + mName = p.hide.name; + mVisible = false; + } + else if (p.respond.isChosen()) + { + mType = p.respond.type; + mTag = p.respond.tag; + mName = p.respond.name; + mVisible = false; + mResponse = p.respond.response; + } +} + LLNotification::LLNotification(const LLNotification::Params& p) : mTimestamp(p.time_stamp), mSubstitutions(p.substitutions), @@ -680,6 +734,25 @@ bool LLNotification::hasUniquenessConstraints() const return (mTemplatep ? mTemplatep->mUnique : false); } +bool LLNotification::matchesTag(const std::string& tag) +{ + bool result = false; + + if(mTemplatep) + { + std::list<std::string>::iterator it; + for(it = mTemplatep->mTags.begin(); it != mTemplatep->mTags.end(); it++) + { + if((*it) == tag) + { + result = true; + break; + } + } + } + + return result; +} void LLNotification::setIgnored(bool ignore) { @@ -1071,12 +1144,12 @@ std::string LLNotificationChannel::summarize() // LLNotifications implementation // --- LLNotifications::LLNotifications() : LLNotificationChannelBase(LLNotificationFilters::includeEverything, - LLNotificationComparators::orderByUUID()), - mIgnoreAllNotifications(false) + LLNotificationComparators::orderByUUID()), + mIgnoreAllNotifications(false) { LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("Notification.Show", boost::bind(&LLNotifications::addFromCallback, this, _2)); - - mListener.reset(new LLNotificationsListener(*this)); + + mListener.reset(new LLNotificationsListener(*this)); } @@ -1191,6 +1264,7 @@ LLNotificationChannelPtr LLNotifications::getChannel(const std::string& channelN void LLNotifications::initSingleton() { loadTemplates(); + loadVisibilityRules(); createDefaultChannels(); } @@ -1198,15 +1272,19 @@ void LLNotifications::createDefaultChannels() { // now construct the various channels AFTER loading the notifications, // because the history channel is going to rewrite the stored notifications file - LLNotificationChannel::buildChannel("Expiration", "", + LLNotificationChannel::buildChannel("Enabled", "", + !boost::bind(&LLNotifications::getIgnoreAllNotifications, this)); + LLNotificationChannel::buildChannel("Expiration", "Enabled", boost::bind(&LLNotifications::expirationFilter, this, _1)); - LLNotificationChannel::buildChannel("Unexpired", "", + LLNotificationChannel::buildChannel("Unexpired", "Enabled", !boost::bind(&LLNotifications::expirationFilter, this, _1)); // use negated bind LLNotificationChannel::buildChannel("Unique", "Unexpired", boost::bind(&LLNotifications::uniqueFilter, this, _1)); LLNotificationChannel::buildChannel("Ignore", "Unique", filterIgnoredNotifications); - LLNotificationChannel::buildChannel("Visible", "Ignore", + LLNotificationChannel::buildChannel("VisibilityRules", "Ignore", + boost::bind(&LLNotifications::isVisibleByRules, this, _1)); + LLNotificationChannel::buildChannel("Visible", "VisibilityRules", &LLNotificationFilters::includeEverything); // create special persistent notification channel @@ -1214,6 +1292,8 @@ void LLNotifications::createDefaultChannels() new LLPersistentNotificationChannel(); // connect action methods to these channels + LLNotifications::instance().getChannel("Enabled")-> + connectFailedFilter(&defaultResponse); LLNotifications::instance().getChannel("Expiration")-> connectChanged(boost::bind(&LLNotifications::expirationHandler, this, _1)); // uniqueHandler slot should be added as first slot of the signal due to @@ -1225,6 +1305,8 @@ void LLNotifications::createDefaultChannels() // connectFailedFilter(boost::bind(&LLNotifications::failedUniquenessTest, this, _1)); LLNotifications::instance().getChannel("Ignore")-> connectFailedFilter(&handleIgnoredNotification); + LLNotifications::instance().getChannel("VisibilityRules")-> + connectFailedFilter(&visibilityRuleMached); } bool LLNotifications::addTemplate(const std::string &name, @@ -1354,6 +1436,12 @@ bool LLNotifications::loadTemplates() LLXUIParser parser; parser.readXUI(root, params, full_filename); + if(!params.validateBlock()) + { + llerrs << "Problem reading UI Notifications file: " << full_filename << llendl; + return false; + } + mTemplates.clear(); for(LLInitParam::ParamIterator<LLNotificationTemplate::GlobalString>::const_iterator it = params.strings.begin(), end_it = params.strings.end(); @@ -1403,6 +1491,34 @@ bool LLNotifications::loadTemplates() return true; } +bool LLNotifications::loadVisibilityRules() +{ + const std::string xml_filename = "notification_visibility.xml"; + std::string full_filename = gDirUtilp->findSkinnedFilename(LLUI::getXUIPaths().front(), xml_filename); + + LLNotificationVisibilityRule::Rules params; + LLSimpleXUIParser parser; + parser.readXUI(full_filename, params); + + if(!params.validateBlock()) + { + llerrs << "Problem reading UI Notification Visibility Rules file: " << full_filename << llendl; + return false; + } + + mVisibilityRules.clear(); + + for(LLInitParam::ParamIterator<LLNotificationVisibilityRule::Rule>::iterator it = params.rules.begin(), + end_it = params.rules.end(); + it != end_it; + ++it) + { + mVisibilityRules.push_back(LLNotificationVisibilityRulePtr(new LLNotificationVisibilityRule(*it))); + } + + return true; +} + // Add a simple notification (from XUI) void LLNotifications::addFromCallback(const LLSD& name) { @@ -1553,6 +1669,94 @@ bool LLNotifications::getIgnoreAllNotifications() return mIgnoreAllNotifications; } +bool LLNotifications::isVisibleByRules(LLNotificationPtr n) +{ + if(n->isRespondedTo()) + { + // This avoids infinite recursion in the case where the filter calls respond() + return true; + } + + VisibilityRuleList::iterator it; + + for(it = mVisibilityRules.begin(); it != mVisibilityRules.end(); it++) + { + // An empty type/tag/name string will match any notification, so only do the comparison when the string is non-empty in the rule. + + lldebugs + << "notification \"" << n->getName() << "\" " + << "testing against " << ((*it)->mVisible?"show":"hide") << " rule, " + << "name = \"" << (*it)->mName << "\" " + << "tag = \"" << (*it)->mTag << "\" " + << "type = \"" << (*it)->mType << "\" " + << llendl; + + if(!(*it)->mType.empty()) + { + if((*it)->mType != n->getType()) + { + // Type doesn't match, so skip this rule. + continue; + } + } + + if(!(*it)->mTag.empty()) + { + // check this notification's tag(s) against it->mTag and continue if no match is found. + if(!n->matchesTag((*it)->mTag)) + { + // This rule's non-empty tag didn't match one of the notification's tags. Skip this rule. + continue; + } + } + + if(!(*it)->mName.empty()) + { + // check this notification's name against the notification's name and continue if no match is found. + if((*it)->mName != n->getName()) + { + // This rule's non-empty name didn't match the notification. Skip this rule. + continue; + } + } + + // If we got here, the rule matches. Don't evaluate subsequent rules. + if(!(*it)->mVisible) + { + // This notification is being hidden. + + if((*it)->mResponse.empty()) + { + // Response property is empty. Cancel this notification. + lldebugs << "cancelling notification " << n->getName() << llendl; + + n->cancel(); + } + else + { + // Response property is not empty. Return the specified response. + LLSD response = n->getResponseTemplate(LLNotification::WITHOUT_DEFAULT_BUTTON); + // TODO: verify that the response template has an item with the correct name + response[(*it)->mResponse] = true; + + lldebugs << "responding to notification " << n->getName() << " with response = " << response << llendl; + + n->respond(response); + } + + return false; + } + + // If we got here, exit the loop and return true. + break; + } + + lldebugs << "allowing notification " << n->getName() << llendl; + + return true; +} + + // --- // END OF LLNotifications implementation // ========================================================= diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h index a607f52b97..34d3537781 100644 --- a/indra/llui/llnotifications.h +++ b/indra/llui/llnotifications.h @@ -271,6 +271,11 @@ struct LLNotificationTemplate; // with smart pointers typedef boost::shared_ptr<LLNotificationTemplate> LLNotificationTemplatePtr; + +struct LLNotificationVisibilityRule; + +typedef boost::shared_ptr<LLNotificationVisibilityRule> LLNotificationVisibilityRulePtr; + /** * @class LLNotification * @brief The object that expresses the details of a notification @@ -507,7 +512,7 @@ public: std::string getLabel() const; std::string getURL() const; S32 getURLOption() const; - S32 getURLOpenExternally() const; + S32 getURLOpenExternally() const; const LLNotificationFormPtr getForm(); @@ -579,6 +584,8 @@ public: bool hasUniquenessConstraints() const; + bool matchesTag(const std::string& tag); + virtual ~LLNotification() {} }; @@ -860,6 +867,10 @@ public: // OK to call more than once because it will reload bool loadTemplates(); + // load visibility rules from file; + // OK to call more than once because it will reload + bool loadVisibilityRules(); + // Add a simple notification (from XUI) void addFromCallback(const LLSD& name); @@ -905,6 +916,8 @@ public: // test for existence bool templateExists(const std::string& name); + typedef std::list<LLNotificationVisibilityRulePtr> VisibilityRuleList; + void forceResponse(const LLNotification::Params& params, S32 option); void createDefaultChannels(); @@ -920,6 +933,8 @@ public: void setIgnoreAllNotifications(bool ignore); bool getIgnoreAllNotifications(); + bool isVisibleByRules(LLNotificationPtr pNotification); + private: // we're a singleton, so we don't have a public constructor LLNotifications(); @@ -939,6 +954,8 @@ private: bool addTemplate(const std::string& name, LLNotificationTemplatePtr theTemplate); TemplateMap mTemplates; + VisibilityRuleList mVisibilityRules; + std::string mFileName; LLNotificationMap mUniqueNotifications; diff --git a/indra/llui/llnotificationtemplate.h b/indra/llui/llnotificationtemplate.h index 644d5c4d74..eff572b553 100644 --- a/indra/llui/llnotificationtemplate.h +++ b/indra/llui/llnotificationtemplate.h @@ -158,6 +158,15 @@ struct LLNotificationTemplate {} }; + struct Tag : public LLInitParam::Block<Tag> + { + Mandatory<std::string> value; + + Tag() + : value("value") + {} + }; + struct Params : public LLInitParam::Block<Params> { Mandatory<std::string> name; @@ -175,6 +184,7 @@ struct LLNotificationTemplate Optional<FormRef> form_ref; Optional<ENotificationPriority, NotificationPriorityValues> priority; + Multiple<Tag> tags; Params() @@ -191,7 +201,8 @@ struct LLNotificationTemplate expire_option("expireOption", -1), url("url"), unique("unique"), - form_ref("") + form_ref(""), + tags("tag") {} }; @@ -278,6 +289,8 @@ struct LLNotificationTemplate // this is loaded as a name, but looked up to get the UUID upon template load. // If null, it wasn't specified. LLUUID mSoundEffect; + // List of tags that rules can match against. + std::list<std::string> mTags; }; #endif //LL_LLNOTIFICATION_TEMPLATE_H diff --git a/indra/llui/llnotificationvisibilityrule.h b/indra/llui/llnotificationvisibilityrule.h new file mode 100644 index 0000000000..78bdec2a8f --- /dev/null +++ b/indra/llui/llnotificationvisibilityrule.h @@ -0,0 +1,104 @@ +/** +* @file llnotificationvisibility.h +* @brief Rules for +* @author Monroe +* +* $LicenseInfo:firstyear=2010&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_LLNOTIFICATION_VISIBILITY_RULE_H +#define LL_LLNOTIFICATION_VISIBILITY_RULE_H + +#include "llinitparam.h" +//#include "llnotifications.h" + + + +// This is the class of object read from the XML file (notification_visibility.xml, +// from the appropriate local language directory). +struct LLNotificationVisibilityRule +{ + struct Filter : public LLInitParam::Block<Filter> + { + Optional<std::string> type, + tag, + name; + + Filter() + : type("type"), + tag("tag"), + name("name") + {} + }; + + struct Respond : public LLInitParam::Block<Respond, Filter> + { + Mandatory<std::string> response; + + Respond() + : response("response") + {} + }; + + struct Rule : public LLInitParam::Choice<Rule> + { + Alternative<Filter> show; + Alternative<Filter> hide; + Alternative<Respond> respond; + + Rule() + : show("show"), + hide("hide"), + respond("respond") + {} + }; + + struct Rules : public LLInitParam::Block<Rules> + { + Multiple<Rule> rules; + + Rules() + : rules("") + {} + }; + + LLNotificationVisibilityRule(const Rule& p); + + // If true, this rule makes matching notifications visible. Otherwise, it makes them invisible. + bool mVisible; + + // Which response to give when making a notification invisible. An empty string means the notification should be cancelled instead of responded to. + std::string mResponse; + + // String to match against the notification's "type". An empty string matches all notifications. + std::string mType; + + // String to match against the notification's tag(s). An empty string matches all notifications. + std::string mTag; + + // String to match against the notification's name. An empty string matches all notifications. + std::string mName; + +}; + +#endif //LL_LLNOTIFICATION_VISIBILITY_RULE_H + diff --git a/indra/llui/llpanel.cpp b/indra/llui/llpanel.cpp index 900e2c789e..b2383106a8 100644 --- a/indra/llui/llpanel.cpp +++ b/indra/llui/llpanel.cpp @@ -194,6 +194,8 @@ void LLPanel::draw() // draw background if( mBgVisible ) { + alpha = getCurrentTransparency(); + LLRect local_rect = getLocalRect(); if (mBgOpaque ) { @@ -434,7 +436,7 @@ void LLPanel::initFromParams(const LLPanel::Params& p) //and LLView::initFromParams will use them to set visible and enabled setVisible(p.visible); setEnabled(p.enabled); - + setFocusRoot(p.focus_root); setSoundFlags(p.sound_flags); // control_name, tab_stop, focus_lost_callback, initial_value, rect, enabled, visible diff --git a/indra/llui/llprogressbar.cpp b/indra/llui/llprogressbar.cpp index aaa328754d..ead22686bc 100644 --- a/indra/llui/llprogressbar.cpp +++ b/indra/llui/llprogressbar.cpp @@ -50,7 +50,7 @@ LLProgressBar::Params::Params() LLProgressBar::LLProgressBar(const LLProgressBar::Params& p) -: LLView(p), +: LLUICtrl(p), mImageBar(p.image_bar), mImageFill(p.image_fill), mColorBackground(p.color_bg()), @@ -80,7 +80,7 @@ void LLProgressBar::draw() mImageFill->draw(progress_rect, bar_color); } -void LLProgressBar::setPercent(const F32 percent) +void LLProgressBar::setValue(const LLSD& value) { - mPercentDone = llclamp(percent, 0.f, 100.f); + mPercentDone = llclamp((F32)value.asReal(), 0.f, 100.f); } diff --git a/indra/llui/llprogressbar.h b/indra/llui/llprogressbar.h index 13297f7493..3f308e7496 100644 --- a/indra/llui/llprogressbar.h +++ b/indra/llui/llprogressbar.h @@ -27,14 +27,14 @@ #ifndef LL_LLPROGRESSBAR_H #define LL_LLPROGRESSBAR_H -#include "llview.h" +#include "lluictrl.h" #include "llframetimer.h" class LLProgressBar - : public LLView + : public LLUICtrl { public: - struct Params : public LLInitParam::Block<Params, LLView::Params> + struct Params : public LLInitParam::Block<Params, LLUICtrl::Params> { Optional<LLUIImage*> image_bar, image_fill; @@ -47,7 +47,7 @@ public: LLProgressBar(const Params&); virtual ~LLProgressBar(); - void setPercent(const F32 percent); + void setValue(const LLSD& value); /*virtual*/ void draw(); diff --git a/indra/llui/llradiogroup.cpp b/indra/llui/llradiogroup.cpp index cc348fdc63..6e9586369f 100644 --- a/indra/llui/llradiogroup.cpp +++ b/indra/llui/llradiogroup.cpp @@ -69,7 +69,7 @@ protected: static LLWidgetNameRegistry::StaticRegistrar register_radio_item(&typeid(LLRadioGroup::ItemParams), "radio_item"); LLRadioGroup::Params::Params() -: has_border("draw_border"), +: allow_deselect("allow_deselect"), items("item") { addSynonym(items, "radio_item"); @@ -85,18 +85,8 @@ LLRadioGroup::LLRadioGroup(const LLRadioGroup::Params& p) : LLUICtrl(p), mFont(p.font.isProvided() ? p.font() : LLFontGL::getFontSansSerifSmall()), mSelectedIndex(-1), - mHasBorder(p.has_border) -{ - if (mHasBorder) - { - LLViewBorder::Params params; - params.name("radio group border"); - params.rect(LLRect(0, getRect().getHeight(), getRect().getWidth(), 0)); - params.bevel_style(LLViewBorder::BEVEL_NONE); - LLViewBorder * vb = LLUICtrlFactory::create<LLViewBorder> (params); - addChild (vb); - } -} + mAllowDeselect(p.allow_deselect) +{} void LLRadioGroup::initFromParams(const Params& p) { @@ -184,7 +174,7 @@ void LLRadioGroup::setIndexEnabled(S32 index, BOOL enabled) BOOL LLRadioGroup::setSelectedIndex(S32 index, BOOL from_event) { - if (index < 0 || (S32)mRadioButtons.size() <= index ) + if ((S32)mRadioButtons.size() <= index ) { return FALSE; } @@ -202,13 +192,16 @@ BOOL LLRadioGroup::setSelectedIndex(S32 index, BOOL from_event) mSelectedIndex = index; - LLRadioCtrl* radio_item = mRadioButtons[mSelectedIndex]; - radio_item->setTabStop(true); - radio_item->setValue( TRUE ); - - if (hasFocus()) + if (mSelectedIndex >= 0) { - mRadioButtons[mSelectedIndex]->focusFirstItem(FALSE, FALSE); + LLRadioCtrl* radio_item = mRadioButtons[mSelectedIndex]; + radio_item->setTabStop(true); + radio_item->setValue( TRUE ); + + if (hasFocus()) + { + radio_item->focusFirstItem(FALSE, FALSE); + } } if (!from_event) @@ -307,8 +300,15 @@ void LLRadioGroup::onClickButton(LLUICtrl* ctrl) LLRadioCtrl* radio = *iter; if (radio == clicked_radio) { - // llinfos << "clicked button " << index << llendl; - setSelectedIndex(index); + if (index == mSelectedIndex && mAllowDeselect) + { + // don't select anything + setSelectedIndex(-1); + } + else + { + setSelectedIndex(index); + } // BUG: Calls click callback even if button didn't actually change onCommit(); diff --git a/indra/llui/llradiogroup.h b/indra/llui/llradiogroup.h index 0588900600..8bd5698538 100644 --- a/indra/llui/llradiogroup.h +++ b/indra/llui/llradiogroup.h @@ -49,7 +49,7 @@ public: struct Params : public LLInitParam::Block<Params, LLUICtrl::Params> { - Optional<bool> has_border; + Optional<bool> allow_deselect; Multiple<ItemParams, AtLeast<1> > items; Params(); }; @@ -73,7 +73,6 @@ public: void setIndexEnabled(S32 index, BOOL enabled); // return the index value of the selected item S32 getSelectedIndex() const { return mSelectedIndex; } - // set the index value programatically BOOL setSelectedIndex(S32 index, BOOL from_event = FALSE); @@ -103,12 +102,13 @@ public: /*virtual*/ BOOL operateOnAll(EOperation op); private: - const LLFontGL* mFont; - S32 mSelectedIndex; + const LLFontGL* mFont; + S32 mSelectedIndex; + typedef std::vector<class LLRadioCtrl*> button_list_t; - button_list_t mRadioButtons; + button_list_t mRadioButtons; - BOOL mHasBorder; + bool mAllowDeselect; // user can click on an already selected option to deselect it }; #endif diff --git a/indra/llui/llscrollcontainer.cpp b/indra/llui/llscrollcontainer.cpp index 3146418a7d..380c477eb2 100644 --- a/indra/llui/llscrollcontainer.cpp +++ b/indra/llui/llscrollcontainer.cpp @@ -422,9 +422,10 @@ void LLScrollContainer::draw() // Draw background if( mIsOpaque ) { + F32 alpha = getCurrentTransparency(); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gGL.color4fv( mBackgroundColor.get().mV ); - gl_rect_2d( mInnerRect ); + gl_rect_2d(mInnerRect, mBackgroundColor.get() % alpha); } // Draw mScrolledViews and update scroll bars. diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index 7df7c13dc0..b7848ec37c 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -322,6 +322,7 @@ LLScrollListCtrl::~LLScrollListCtrl() delete mSortCallback; std::for_each(mItemList.begin(), mItemList.end(), DeletePointer()); + std::for_each(mColumns.begin(), mColumns.end(), DeletePairedPointer()); } @@ -1482,8 +1483,9 @@ void LLScrollListCtrl::draw() // Draw background if (mBackgroundVisible) { + F32 alpha = getCurrentTransparency(); gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gl_rect_2d(background, getEnabled() ? mBgWriteableColor.get() : mBgReadOnlyColor.get() ); + gl_rect_2d(background, getEnabled() ? mBgWriteableColor.get() % alpha : mBgReadOnlyColor.get() % alpha ); } if (mColumnsDirty) @@ -2370,10 +2372,10 @@ void LLScrollListCtrl::onScrollChange( S32 new_pos, LLScrollbar* scrollbar ) void LLScrollListCtrl::sortByColumn(const std::string& name, BOOL ascending) { - std::map<std::string, LLScrollListColumn>::iterator itor = mColumns.find(name); + column_map_t::iterator itor = mColumns.find(name); if (itor != mColumns.end()) { - sortByColumnIndex((*itor).second.mIndex, ascending); + sortByColumnIndex((*itor).second->mIndex, ascending); } } @@ -2419,11 +2421,11 @@ void LLScrollListCtrl::dirtyColumns() // just in case someone indexes into it immediately mColumnsIndexed.resize(mColumns.size()); - std::map<std::string, LLScrollListColumn>::iterator column_itor; + column_map_t::iterator column_itor; for (column_itor = mColumns.begin(); column_itor != mColumns.end(); ++column_itor) { - LLScrollListColumn *column = &column_itor->second; - mColumnsIndexed[column_itor->second.mIndex] = column; + LLScrollListColumn *column = column_itor->second; + mColumnsIndexed[column_itor->second->mIndex] = column; } } @@ -2581,8 +2583,8 @@ void LLScrollListCtrl::addColumn(const LLScrollListColumn::Params& column_params if (mColumns.find(name) == mColumns.end()) { // Add column - mColumns[name] = LLScrollListColumn(column_params, this); - LLScrollListColumn* new_column = &mColumns[name]; + mColumns[name] = new LLScrollListColumn(column_params, this); + LLScrollListColumn* new_column = mColumns[name]; new_column->mIndex = mColumns.size()-1; // Add button @@ -2604,14 +2606,14 @@ void LLScrollListCtrl::addColumn(const LLScrollListColumn::Params& column_params S32 top = mItemListRect.mTop; S32 left = mItemListRect.mLeft; - for (std::map<std::string, LLScrollListColumn>::iterator itor = mColumns.begin(); + for (column_map_t::iterator itor = mColumns.begin(); itor != mColumns.end(); ++itor) { - if (itor->second.mIndex < new_column->mIndex && - itor->second.getWidth() > 0) + if (itor->second->mIndex < new_column->mIndex && + itor->second->getWidth() > 0) { - left += itor->second.getWidth() + mColumnPadding; + left += itor->second->getWidth() + mColumnPadding; } } @@ -2667,8 +2669,8 @@ void LLScrollListCtrl::onClickColumn(void *userdata) if (column->mSortingColumn != column->mName && parent->mColumns.find(column->mSortingColumn) != parent->mColumns.end()) { - LLScrollListColumn& info_redir = parent->mColumns[column->mSortingColumn]; - column_index = info_redir.mIndex; + LLScrollListColumn* info_redir = parent->mColumns[column->mSortingColumn]; + column_index = info_redir->mIndex; } // if this column is the primary sort key, reverse the direction @@ -2701,16 +2703,17 @@ BOOL LLScrollListCtrl::hasSortOrder() const void LLScrollListCtrl::clearColumns() { - std::map<std::string, LLScrollListColumn>::iterator itor; + column_map_t::iterator itor; for (itor = mColumns.begin(); itor != mColumns.end(); ++itor) { - LLScrollColumnHeader *header = itor->second.mHeader; + LLScrollColumnHeader *header = itor->second->mHeader; if (header) { removeChild(header); delete header; } } + std::for_each(mColumns.begin(), mColumns.end(), DeletePairedPointer()); mColumns.clear(); mSortColumns.clear(); mTotalStaticColumnWidth = 0; @@ -2744,7 +2747,7 @@ LLScrollListColumn* LLScrollListCtrl::getColumn(const std::string& name) column_map_t::iterator column_itor = mColumns.find(name); if (column_itor != mColumns.end()) { - return &column_itor->second; + return column_itor->second; } return NULL; } @@ -2805,7 +2808,7 @@ LLScrollListItem* LLScrollListCtrl::addRow(LLScrollListItem *new_item, const LLS new_column.width.pixel_width = cell_p.width; } addColumn(new_column); - columnp = &mColumns[column]; + columnp = mColumns[column]; new_item->setNumColumns(mColumns.size()); } @@ -2842,7 +2845,7 @@ LLScrollListItem* LLScrollListCtrl::addRow(LLScrollListItem *new_item, const LLS LLScrollListCell* cell = LLScrollListCell::create(LLScrollListCell::Params().value(item_p.value)); if (cell) { - LLScrollListColumn* columnp = &(mColumns.begin()->second); + LLScrollListColumn* columnp = mColumns.begin()->second; new_item->setColumn(0, cell); if (columnp->mHeader @@ -2857,10 +2860,10 @@ LLScrollListItem* LLScrollListCtrl::addRow(LLScrollListItem *new_item, const LLS // add dummy cells for missing columns for (column_map_t::iterator column_it = mColumns.begin(); column_it != mColumns.end(); ++column_it) { - S32 column_idx = column_it->second.mIndex; + S32 column_idx = column_it->second->mIndex; if (new_item->getColumn(column_idx) == NULL) { - LLScrollListColumn* column_ptr = &column_it->second; + LLScrollListColumn* column_ptr = column_it->second; LLScrollListCell::Params cell_p; cell_p.width = column_ptr->getWidth(); diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h index 8a2f893ba2..09ab89960d 100644 --- a/indra/llui/llscrolllistctrl.h +++ b/indra/llui/llscrolllistctrl.h @@ -491,7 +491,7 @@ private: mutable bool mSorted; - typedef std::map<std::string, LLScrollListColumn> column_map_t; + typedef std::map<std::string, LLScrollListColumn*> column_map_t; column_map_t mColumns; BOOL mDirty; diff --git a/indra/llui/llsdparam.cpp b/indra/llui/llsdparam.cpp index f97f80ab6c..9ad13054cb 100644 --- a/indra/llui/llsdparam.cpp +++ b/indra/llui/llsdparam.cpp @@ -45,6 +45,7 @@ LLParamSDParser::LLParamSDParser() if (sReadFuncs.empty()) { + registerParserFuncs<LLInitParam::NoParamValue>(readNoValue, &LLParamSDParser::writeNoValue); registerParserFuncs<S32>(readS32, &LLParamSDParser::writeTypedValue<S32>); registerParserFuncs<U32>(readU32, &LLParamSDParser::writeU32Param); registerParserFuncs<F32>(readF32, &LLParamSDParser::writeTypedValue<F32>); @@ -71,6 +72,18 @@ bool LLParamSDParser::writeU32Param(LLParamSDParser::parser_t& parser, const voi return true; } +bool LLParamSDParser::writeNoValue(LLParamSDParser::parser_t& parser, const void* val_ptr, const parser_t::name_stack_t& name_stack) +{ + LLParamSDParser& sdparser = static_cast<LLParamSDParser&>(parser); + if (!sdparser.mWriteRootSD) return false; + + LLSD* sd_to_write = sdparser.getSDWriteNode(name_stack); + if (!sd_to_write) return false; + + return true; +} + + void LLParamSDParser::readSD(const LLSD& sd, LLInitParam::BaseBlock& block, bool silent) { mCurReadSD = NULL; @@ -87,6 +100,8 @@ void LLParamSDParser::writeSD(LLSD& sd, const LLInitParam::BaseBlock& block) block.serializeBlock(*this); } +const LLSD NO_VALUE_MARKER; + void LLParamSDParser::readSDValues(const LLSD& sd, LLInitParam::BaseBlock& block) { if (sd.isMap()) @@ -110,6 +125,11 @@ void LLParamSDParser::readSDValues(const LLSD& sd, LLInitParam::BaseBlock& block readSDValues(*it, block); } } + else if (sd.isUndefined()) + { + mCurReadSD = &NO_VALUE_MARKER; + block.submitValue(mNameStack, *this); + } else { mCurReadSD = &sd; @@ -206,6 +226,13 @@ LLSD* LLParamSDParser::getSDWriteNode(const parser_t::name_stack_t& name_stack) return sd_to_write; } +bool LLParamSDParser::readNoValue(Parser& parser, void* val_ptr) +{ + LLParamSDParser& self = static_cast<LLParamSDParser&>(parser); + return self.mCurReadSD == &NO_VALUE_MARKER; +} + + bool LLParamSDParser::readS32(Parser& parser, void* val_ptr) { LLParamSDParser& self = static_cast<LLParamSDParser&>(parser); diff --git a/indra/llui/llsdparam.h b/indra/llui/llsdparam.h index 97e8b58e49..69dab2b411 100644 --- a/indra/llui/llsdparam.h +++ b/indra/llui/llsdparam.h @@ -63,7 +63,9 @@ private: LLSD* getSDWriteNode(const parser_t::name_stack_t& name_stack); static bool writeU32Param(Parser& parser, const void* value_ptr, const parser_t::name_stack_t& name_stack); + static bool writeNoValue(Parser& parser, const void* value_ptr, const parser_t::name_stack_t& name_stack); + static bool readNoValue(Parser& parser, void* val_ptr); static bool readS32(Parser& parser, void* val_ptr); static bool readU32(Parser& parser, void* val_ptr); static bool readF32(Parser& parser, void* val_ptr); diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 3f213ed13e..49537ef78f 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -1005,6 +1005,7 @@ void LLTextBase::draw() if (mBGVisible) { + F32 alpha = getCurrentTransparency(); // clip background rect against extents, if we support scrolling LLRect bg_rect = mVisibleTextRect; if (mScroller) @@ -1016,7 +1017,7 @@ void LLTextBase::draw() : hasFocus() ? mFocusBgColor.get() : mWriteableBgColor.get(); - gl_rect_2d(doc_rect, bg_color, TRUE); + gl_rect_2d(doc_rect, bg_color % alpha, TRUE); } // draw document view diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 94bf716e7d..5a46c7c98e 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -277,6 +277,8 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) : mHPad += UI_TEXTEDITOR_LINE_NUMBER_MARGIN; updateRects(); } + + mParseOnTheFly = TRUE; } void LLTextEditor::initFromParams( const LLTextEditor::Params& p) @@ -324,8 +326,10 @@ void LLTextEditor::setText(const LLStringExplicit &utf8str, const LLStyle::Param blockUndo(); deselect(); - + + mParseOnTheFly = FALSE; LLTextBase::setText(utf8str, input_params); + mParseOnTheFly = TRUE; resetDirty(); } @@ -1367,6 +1371,7 @@ void LLTextEditor::pastePrimary() // paste from primary (itsprimary==true) or clipboard (itsprimary==false) void LLTextEditor::pasteHelper(bool is_primary) { + mParseOnTheFly = FALSE; bool can_paste_it; if (is_primary) { @@ -1450,6 +1455,7 @@ void LLTextEditor::pasteHelper(bool is_primary) deselect(); onKeyStroke(); + mParseOnTheFly = TRUE; } @@ -2385,7 +2391,7 @@ void LLTextEditor::loadKeywords(const std::string& filename, void LLTextEditor::updateSegments() { - if (mReflowIndex < S32_MAX && mKeywords.isLoaded()) + if (mReflowIndex < S32_MAX && mKeywords.isLoaded() && mParseOnTheFly) { LLFastTimer ft(FTM_SYNTAX_HIGHLIGHTING); // HACK: No non-ascii keywords for now diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 58ecefdccb..9e4b95003b 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -315,6 +315,7 @@ private: BOOL mAllowEmbeddedItems; bool mShowContextMenu; + bool mParseOnTheFly; LLUUID mSourceID; diff --git a/indra/llui/llui.cpp b/indra/llui/llui.cpp index 19c42bf61a..c300fe55d9 100644 --- a/indra/llui/llui.cpp +++ b/indra/llui/llui.cpp @@ -950,7 +950,7 @@ void gl_ring( F32 radius, F32 width, const LLColor4& center_color, const LLColor } // Draw gray and white checkerboard with black border -void gl_rect_2d_checkerboard(const LLRect& rect) +void gl_rect_2d_checkerboard(const LLRect& rect, GLfloat alpha) { // Initialize the first time this is called. const S32 PIXELS = 32; @@ -971,11 +971,11 @@ void gl_rect_2d_checkerboard(const LLRect& rect) gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); // ...white squares - gGL.color3f( 1.f, 1.f, 1.f ); + gGL.color4f( 1.f, 1.f, 1.f, alpha ); gl_rect_2d(rect); // ...gray squares - gGL.color3f( .7f, .7f, .7f ); + gGL.color4f( .7f, .7f, .7f, alpha ); gGL.flush(); glPolygonStipple( checkerboard ); @@ -1596,23 +1596,25 @@ void LLUI::initClass(const settings_map_t& settings, sWindow = NULL; // set later in startup LLFontGL::sShadowColor = LLUIColorTable::instance().getColor("ColorDropShadow"); + LLUICtrl::CommitCallbackRegistry::Registrar& reg = LLUICtrl::CommitCallbackRegistry::defaultRegistrar(); + // Callbacks for associating controls with floater visibilty: - LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Floater.Toggle", boost::bind(&LLFloaterReg::toggleFloaterInstance, _2)); - LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Floater.Show", boost::bind(&LLFloaterReg::showFloaterInstance, _2)); - LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Floater.Hide", boost::bind(&LLFloaterReg::hideFloaterInstance, _2)); - LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Floater.InitToVisibilityControl", boost::bind(&LLFloaterReg::initUICtrlToFloaterVisibilityControl, _1, _2)); + reg.add("Floater.Toggle", boost::bind(&LLFloaterReg::toggleFloaterInstance, _2)); + reg.add("Floater.Show", boost::bind(&LLFloaterReg::showFloaterInstance, _2)); + reg.add("Floater.Hide", boost::bind(&LLFloaterReg::hideFloaterInstance, _2)); + reg.add("Floater.InitToVisibilityControl", boost::bind(&LLFloaterReg::initUICtrlToFloaterVisibilityControl, _1, _2)); // Button initialization callback for toggle buttons - LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Button.SetFloaterToggle", boost::bind(&LLButton::setFloaterToggle, _1, _2)); + reg.add("Button.SetFloaterToggle", boost::bind(&LLButton::setFloaterToggle, _1, _2)); // Button initialization callback for toggle buttons on dockale floaters - LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Button.SetDockableFloaterToggle", boost::bind(&LLButton::setDockableFloaterToggle, _1, _2)); + reg.add("Button.SetDockableFloaterToggle", boost::bind(&LLButton::setDockableFloaterToggle, _1, _2)); // Display the help topic for the current context - LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Button.ShowHelp", boost::bind(&LLButton::showHelp, _1, _2)); + reg.add("Button.ShowHelp", boost::bind(&LLButton::showHelp, _1, _2)); // Currently unused, but kept for reference: - LLUICtrl::CommitCallbackRegistry::defaultRegistrar().add("Button.ToggleFloater", boost::bind(&LLButton::toggleFloaterAndSetToggleState, _1, _2)); + reg.add("Button.ToggleFloater", boost::bind(&LLButton::toggleFloaterAndSetToggleState, _1, _2)); // Used by menus along with Floater.Toggle to display visibility as a checkmark LLUICtrl::EnableCallbackRegistry::defaultRegistrar().add("Floater.Visible", boost::bind(&LLFloaterReg::floaterInstanceVisible, _2)); diff --git a/indra/llui/llui.h b/indra/llui/llui.h index fc545c85d5..62d10df8b2 100644 --- a/indra/llui/llui.h +++ b/indra/llui/llui.h @@ -79,7 +79,7 @@ void gl_rect_2d_offset_local( S32 left, S32 top, S32 right, S32 bottom, const LL void gl_rect_2d_offset_local( S32 left, S32 top, S32 right, S32 bottom, S32 pixel_offset = 0, BOOL filled = TRUE ); void gl_rect_2d(const LLRect& rect, BOOL filled = TRUE ); void gl_rect_2d(const LLRect& rect, const LLColor4& color, BOOL filled = TRUE ); -void gl_rect_2d_checkerboard(const LLRect& rect); +void gl_rect_2d_checkerboard(const LLRect& rect, GLfloat alpha = 1.0f); void gl_drop_shadow(S32 left, S32 top, S32 right, S32 bottom, const LLColor4 &start_color, S32 lines); diff --git a/indra/llui/lluicolortable.cpp b/indra/llui/lluicolortable.cpp index 0641f6d175..9455d09cc0 100644 --- a/indra/llui/lluicolortable.cpp +++ b/indra/llui/lluicolortable.cpp @@ -215,6 +215,12 @@ bool LLUIColorTable::loadFromSettings() result |= loadFromFilename(current_filename, mLoadedColors); } + current_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SKIN, "colors.xml"); + if(current_filename != default_filename) + { + result |= loadFromFilename(current_filename, mLoadedColors); + } + std::string user_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "colors.xml"); loadFromFilename(user_filename, mUserSetColors); diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp index d81f425cce..0a06b5e74f 100644 --- a/indra/llui/lluictrl.cpp +++ b/indra/llui/lluictrl.cpp @@ -36,6 +36,9 @@ static LLDefaultChildRegistry::Register<LLUICtrl> r("ui_ctrl"); +F32 LLUICtrl::sActiveControlTransparency = 1.0f; +F32 LLUICtrl::sInactiveControlTransparency = 1.0f; + // Compiler optimization, generate extern template template class LLUICtrl* LLView::getChild<class LLUICtrl>( const std::string& name, BOOL recurse) const; @@ -110,7 +113,8 @@ LLUICtrl::LLUICtrl(const LLUICtrl::Params& p, const LLViewModelPtr& viewmodel) mMouseUpSignal(NULL), mRightMouseDownSignal(NULL), mRightMouseUpSignal(NULL), - mDoubleClickSignal(NULL) + mDoubleClickSignal(NULL), + mTransparencyType(TT_DEFAULT) { mUICtrlHandle.bind(this); } @@ -923,6 +927,37 @@ BOOL LLUICtrl::getTentative() const void LLUICtrl::setColor(const LLColor4& color) { } +F32 LLUICtrl::getCurrentTransparency() +{ + F32 alpha = 0; + + switch(mTransparencyType) + { + case TT_DEFAULT: + alpha = getDrawContext().mAlpha; + break; + + case TT_ACTIVE: + alpha = sActiveControlTransparency; + break; + + case TT_INACTIVE: + alpha = sInactiveControlTransparency; + break; + + case TT_FADING: + alpha = sInactiveControlTransparency / 2; + break; + } + + return alpha; +} + +void LLUICtrl::setTransparencyType(ETypeTransparency type) +{ + mTransparencyType = type; +} + boost::signals2::connection LLUICtrl::setCommitCallback( const commit_signal_t::slot_type& cb ) { if (!mCommitSignal) mCommitSignal = new commit_signal_t(); diff --git a/indra/llui/lluictrl.h b/indra/llui/lluictrl.h index 76dfdf754c..b37e9f6b1b 100644 --- a/indra/llui/lluictrl.h +++ b/indra/llui/lluictrl.h @@ -120,6 +120,13 @@ public: Params(); }; + enum ETypeTransparency + { + TT_DEFAULT, + TT_ACTIVE, // focused floater + TT_INACTIVE, // other floaters + TT_FADING, // fading toast + }; /*virtual*/ ~LLUICtrl(); void initFromParams(const Params& p); @@ -202,6 +209,11 @@ public: virtual void setColor(const LLColor4& color); + F32 getCurrentTransparency(); + + void setTransparencyType(ETypeTransparency type); + ETypeTransparency getTransparencyType() const {return mTransparencyType;} + BOOL focusNextItem(BOOL text_entry_only); BOOL focusPrevItem(BOOL text_entry_only); BOOL focusFirstItem(BOOL prefer_text_fields = FALSE, BOOL focus_flash = TRUE ); @@ -283,6 +295,10 @@ protected: boost::signals2::connection mMakeVisibleControlConnection; LLControlVariable* mMakeInvisibleControlVariable; boost::signals2::connection mMakeInvisibleControlConnection; + + static F32 sActiveControlTransparency; + static F32 sInactiveControlTransparency; + private: BOOL mTabStop; @@ -290,6 +306,8 @@ private: BOOL mTentative; LLRootHandle<LLUICtrl> mUICtrlHandle; + ETypeTransparency mTransparencyType; + class DefaultTabGroupFirstSorter; }; diff --git a/indra/llui/lluistring.h b/indra/llui/lluistring.h index 4faa0e070e..cb40c85582 100644 --- a/indra/llui/lluistring.h +++ b/indra/llui/lluistring.h @@ -61,7 +61,6 @@ public: LLUIString() : mArgs(NULL), mNeedsResult(false), mNeedsWResult(false) {} LLUIString(const std::string& instring, const LLStringUtil::format_map_t& args); LLUIString(const std::string& instring) : mArgs(NULL) { assign(instring); } - ~LLUIString() { delete mArgs; } void assign(const std::string& instring); diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp index 6cc72bad82..4f7b4be526 100644 --- a/indra/llui/llurlentry.cpp +++ b/indra/llui/llurlentry.cpp @@ -27,6 +27,7 @@ #include "linden_common.h" #include "llurlentry.h" +#include "lluictrl.h" #include "lluri.h" #include "llurlmatch.h" #include "llurlregistry.h" @@ -167,6 +168,15 @@ void LLUrlEntryBase::callObservers(const std::string &id, } } +/// is this a match for a URL that should not be hyperlinked? +bool LLUrlEntryBase::isLinkDisabled() const +{ + // this allows us to have a global setting to turn off text hyperlink highlighting/action + bool globally_disabled = LLUI::sSettingGroups["config"]->getBOOL("DisableTextHyperlinkActions"); + + return globally_disabled; +} + static std::string getStringAfterToken(const std::string str, const std::string token) { size_t pos = str.find(token); @@ -456,8 +466,8 @@ std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCa LLStyle::Params LLUrlEntryAgent::getStyle() const { LLStyle::Params style_params = LLUrlEntryBase::getStyle(); - style_params.color = LLUIColorTable::instance().getColor("AgentLinkColor"); - style_params.readonly_color = LLUIColorTable::instance().getColor("AgentLinkColor"); + style_params.color = LLUIColorTable::instance().getColor("HTMLLinkColor"); + style_params.readonly_color = LLUIColorTable::instance().getColor("HTMLLinkColor"); return style_params; } @@ -796,6 +806,69 @@ std::string LLUrlEntryPlace::getLocation(const std::string &url) const } // +// LLUrlEntryRegion Describes secondlife:///app/region/REGION_NAME/X/Y/Z URLs, e.g. +// secondlife:///app/region/Ahern/128/128/0 +// +LLUrlEntryRegion::LLUrlEntryRegion() +{ + mPattern = boost::regex("secondlife:///app/region/[^/\\s]+(/\\d+)?(/\\d+)?(/\\d+)?/?", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_slurl.xml"; + mTooltip = LLTrans::getString("TooltipSLURL"); +} + +std::string LLUrlEntryRegion::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + // + // we handle SLURLs in the following formats: + // - secondlife:///app/region/Place/X/Y/Z + // - secondlife:///app/region/Place/X/Y + // - secondlife:///app/region/Place/X + // - secondlife:///app/region/Place + // + + LLSD path_array = LLURI(url).pathArray(); + S32 path_parts = path_array.size(); + + if (path_parts < 3) // no region name + { + llwarns << "Failed to parse url [" << url << "]" << llendl; + return url; + } + + std::string label = unescapeUrl(path_array[2]); // region name + + if (path_parts > 3) // secondlife:///app/region/Place/X + { + std::string x = path_array[3]; + label += " (" + x; + + if (path_parts > 4) // secondlife:///app/region/Place/X/Y + { + std::string y = path_array[4]; + label += "," + y; + + if (path_parts > 5) // secondlife:///app/region/Place/X/Y/Z + { + std::string z = path_array[5]; + label = label + "," + z; + } + } + + label += ")"; + } + + return label; +} + +std::string LLUrlEntryRegion::getLocation(const std::string &url) const +{ + LLSD path_array = LLURI(url).pathArray(); + std::string region_name = unescapeUrl(path_array[2]); + return region_name; +} + +// // LLUrlEntryTeleport Describes a Second Life teleport Url, e.g., // secondlife:///app/teleport/Ahern/50/50/50/ // x-grid-location-info://lincoln.lindenlab.com/app/teleport/Ahern/50/50/50/ diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h index 1a16056041..1791739061 100644 --- a/indra/llui/llurlentry.h +++ b/indra/llui/llurlentry.h @@ -94,6 +94,8 @@ public: virtual LLUUID getID(const std::string &string) const { return LLUUID::null; } + bool isLinkDisabled() const; + protected: std::string getIDStringFromUrl(const std::string &url) const; std::string escapeUrl(const std::string &url) const; @@ -300,6 +302,18 @@ public: }; /// +/// LLUrlEntryRegion Describes a Second Life location Url, e.g., +/// secondlife:///app/region/Ahern/128/128/0 +/// +class LLUrlEntryRegion : public LLUrlEntryBase +{ +public: + LLUrlEntryRegion(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getLocation(const std::string &url) const; +}; + +/// /// LLUrlEntryTeleport Describes a Second Life teleport Url, e.g., /// secondlife:///app/teleport/Ahern/50/50/50/ /// diff --git a/indra/llui/llurlregistry.cpp b/indra/llui/llurlregistry.cpp index 478b412d5e..523ee5d78c 100644 --- a/indra/llui/llurlregistry.cpp +++ b/indra/llui/llurlregistry.cpp @@ -54,6 +54,7 @@ LLUrlRegistry::LLUrlRegistry() registerUrl(new LLUrlEntryGroup()); registerUrl(new LLUrlEntryParcel()); registerUrl(new LLUrlEntryTeleport()); + registerUrl(new LLUrlEntryRegion()); registerUrl(new LLUrlEntryWorldMap()); registerUrl(new LLUrlEntryObjectIM()); registerUrl(new LLUrlEntryPlace()); diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index 3fa86bf0ca..267640a226 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -102,6 +102,7 @@ LLView::Params::Params() left_pad("left_pad"), left_delta("left_delta", S32_MAX), from_xui("from_xui", false), + focus_root("focus_root", false), needs_translate("translate"), xmlns("xmlns"), xmlns_xsi("xmlns:xsi"), @@ -117,7 +118,7 @@ LLView::LLView(const LLView::Params& p) mParentView(NULL), mReshapeFlags(FOLLOWS_NONE), mFromXUI(p.from_xui), - mIsFocusRoot(FALSE), + mIsFocusRoot(p.focus_root), mLastVisible(FALSE), mNextInsertionOrdinal(0), mHoverCursor(getCursorFromString(p.hover_cursor)), @@ -163,8 +164,6 @@ LLView::~LLView() if (mDefaultWidgets) { - std::for_each(mDefaultWidgets->begin(), mDefaultWidgets->end(), - DeletePairedPointer()); delete mDefaultWidgets; mDefaultWidgets = NULL; } @@ -1682,18 +1681,7 @@ BOOL LLView::hasChild(const std::string& childname, BOOL recurse) const //----------------------------------------------------------------------------- LLView* LLView::getChildView(const std::string& name, BOOL recurse) const { - LLView* child = findChildView(name, recurse); - if (!child) - { - child = getDefaultWidget<LLView>(name); - if (!child) - { - LLView::Params view_params; - view_params.name = name; - child = LLUICtrlFactory::create<LLView>(view_params); - } - } - return child; + return getChild<LLView>(name, recurse); } static LLFastTimer::DeclareTimer FTM_FIND_VIEWS("Find Widgets"); @@ -2804,11 +2792,14 @@ LLView::root_to_view_iterator_t LLView::endRootToView() // only create maps on demand, as they incur heap allocation/deallocation cost // when a view is constructed/deconstructed -LLView::default_widget_map_t& LLView::getDefaultWidgetMap() const +LLView& LLView::getDefaultWidgetContainer() const { if (!mDefaultWidgets) { - mDefaultWidgets = new default_widget_map_t(); + LLView::Params p; + p.name = "default widget container"; + p.visible = false; // ensures default widgets can't steal focus, etc. + mDefaultWidgets = new LLView(p); } return *mDefaultWidgets; } diff --git a/indra/llui/llview.h b/indra/llui/llview.h index cd2f215c2d..d2bbd663b8 100644 --- a/indra/llui/llview.h +++ b/indra/llui/llview.h @@ -116,7 +116,8 @@ public: visible, mouse_opaque, use_bounding_rect, - from_xui; + from_xui, + focus_root; Optional<S32> tab_group, default_tab_group; @@ -461,12 +462,8 @@ public: template <class T> T* getDefaultWidget(const std::string& name) const { - default_widget_map_t::const_iterator found_it = getDefaultWidgetMap().find(name); - if (found_it == getDefaultWidgetMap().end()) - { - return NULL; - } - return dynamic_cast<T*>(found_it->second); + LLView* widgetp = getDefaultWidgetContainer().findChildView(name); + return dynamic_cast<T*>(widgetp); } ////////////////////////////////////////////// @@ -580,9 +577,9 @@ private: typedef std::map<std::string, LLView*> default_widget_map_t; // allocate this map no demand, as it is rarely needed - mutable default_widget_map_t* mDefaultWidgets; + mutable LLView* mDefaultWidgets; - default_widget_map_t& getDefaultWidgetMap() const; + LLView& getDefaultWidgetContainer() const; public: // Depth in view hierarchy during rendering @@ -649,7 +646,7 @@ template <class T> T* LLView::getChild(const std::string& name, BOOL recurse) co return NULL; } - getDefaultWidgetMap()[name] = result; + getDefaultWidgetContainer().addChild(result); } } return result; diff --git a/indra/llui/tests/llurlentry_test.cpp b/indra/llui/tests/llurlentry_test.cpp index 5c6623da61..8f0a48018f 100644 --- a/indra/llui/tests/llurlentry_test.cpp +++ b/indra/llui/tests/llurlentry_test.cpp @@ -27,6 +27,7 @@ #include "linden_common.h" #include "../llurlentry.h" +#include "../lluictrl.h" #include "llurlentry_stub.cpp" #include "lltut.h" #include "../lluicolortable.h" @@ -34,6 +35,14 @@ #include <boost/regex.hpp> +typedef std::map<std::string, LLControlGroup*> settings_map_t; +settings_map_t LLUI::sSettingGroups; + +BOOL LLControlGroup::getBOOL(const std::string& name) +{ + return false; +} + LLUIColor LLUIColorTable::getColor(const std::string& name, const LLColor4& default_color) const { return LLUIColor(); @@ -94,6 +103,45 @@ namespace tut ensure_equals(testname, url, expected); } + void dummyCallback(const std::string &url, const std::string &label, const std::string& icon) + { + } + + void testLabel(const std::string &testname, LLUrlEntryBase &entry, + const char *text, const std::string &expected) + { + boost::regex regex = entry.getPattern(); + std::string label = ""; + boost::cmatch result; + bool found = boost::regex_search(text, result, regex); + if (found) + { + S32 start = static_cast<U32>(result[0].first - text); + S32 end = static_cast<U32>(result[0].second - text); + std::string url = std::string(text+start, end-start); + label = entry.getLabel(url, boost::bind(dummyCallback, _1, _2, _3)); + } + ensure_equals(testname, label, expected); + } + + void testLocation(const std::string &testname, LLUrlEntryBase &entry, + const char *text, const std::string &expected) + { + boost::regex regex = entry.getPattern(); + std::string location = ""; + boost::cmatch result; + bool found = boost::regex_search(text, result, regex); + if (found) + { + S32 start = static_cast<U32>(result[0].first - text); + S32 end = static_cast<U32>(result[0].second - text); + std::string url = std::string(text+start, end-start); + location = entry.getLocation(url); + } + ensure_equals(testname, location, expected); + } + + template<> template<> void object::test<1>() { @@ -688,4 +736,114 @@ namespace tut "<nolink>My Object</nolink>", "My Object"); } + + template<> template<> + void object::test<13>() + { + // + // test LLUrlEntryRegion - secondlife:///app/region/<location> URLs + // + LLUrlEntryRegion url; + + // Regex tests. + testRegex("no valid region", url, + "secondlife:///app/region/", + ""); + + testRegex("invalid coords", url, + "secondlife:///app/region/Korea2/a/b/c", + "secondlife:///app/region/Korea2/"); // don't count invalid coords + + testRegex("Ahern (50,50,50) [1]", url, + "secondlife:///app/region/Ahern/50/50/50/", + "secondlife:///app/region/Ahern/50/50/50/"); + + testRegex("Ahern (50,50,50) [2]", url, + "XXX secondlife:///app/region/Ahern/50/50/50/ XXX", + "secondlife:///app/region/Ahern/50/50/50/"); + + testRegex("Ahern (50,50,50) [3]", url, + "XXX secondlife:///app/region/Ahern/50/50/50 XXX", + "secondlife:///app/region/Ahern/50/50/50"); + + testRegex("Ahern (50,50,50) multicase", url, + "XXX secondlife:///app/region/Ahern/50/50/50/ XXX", + "secondlife:///app/region/Ahern/50/50/50/"); + + testRegex("Ahern (50,50) [1]", url, + "XXX secondlife:///app/region/Ahern/50/50/ XXX", + "secondlife:///app/region/Ahern/50/50/"); + + testRegex("Ahern (50,50) [2]", url, + "XXX secondlife:///app/region/Ahern/50/50 XXX", + "secondlife:///app/region/Ahern/50/50"); + + // DEV-21577: In-world SLURLs containing "(" or ")" are not treated as a hyperlink in chat + testRegex("Region with brackets", url, + "XXX secondlife:///app/region/Burning%20Life%20(Hyper)/27/210/30 XXX", + "secondlife:///app/region/Burning%20Life%20(Hyper)/27/210/30"); + + // DEV-35459: SLURLs and teleport Links not parsed properly + testRegex("Region with quote", url, + "XXX secondlife:///app/region/A'ksha%20Oasis/41/166/701 XXX", + "secondlife:///app/region/A%27ksha%20Oasis/41/166/701"); + + // Rendering tests. + testLabel("Render /app/region/Ahern/50/50/50/", url, + "secondlife:///app/region/Ahern/50/50/50/", + "Ahern (50,50,50)"); + + testLabel("Render /app/region/Ahern/50/50/50", url, + "secondlife:///app/region/Ahern/50/50/50", + "Ahern (50,50,50)"); + + testLabel("Render /app/region/Ahern/50/50/", url, + "secondlife:///app/region/Ahern/50/50/", + "Ahern (50,50)"); + + testLabel("Render /app/region/Ahern/50/50", url, + "secondlife:///app/region/Ahern/50/50", + "Ahern (50,50)"); + + testLabel("Render /app/region/Ahern/50/", url, + "secondlife:///app/region/Ahern/50/", + "Ahern (50)"); + + testLabel("Render /app/region/Ahern/50", url, + "secondlife:///app/region/Ahern/50", + "Ahern (50)"); + + testLabel("Render /app/region/Ahern/", url, + "secondlife:///app/region/Ahern/", + "Ahern"); + + testLabel("Render /app/region/Ahern/ within context", url, + "XXX secondlife:///app/region/Ahern/ XXX", + "Ahern"); + + testLabel("Render /app/region/Ahern", url, + "secondlife:///app/region/Ahern", + "Ahern"); + + testLabel("Render /app/region/Ahern within context", url, + "XXX secondlife:///app/region/Ahern XXX", + "Ahern"); + + testLabel("Render /app/region/Product%20Engine/", url, + "secondlife:///app/region/Product%20Engine/", + "Product Engine"); + + testLabel("Render /app/region/Product%20Engine", url, + "secondlife:///app/region/Product%20Engine", + "Product Engine"); + + // Location parsing texts. + testLocation("Location /app/region/Ahern/50/50/50/", url, + "secondlife:///app/region/Ahern/50/50/50/", + "Ahern"); + + testLocation("Location /app/region/Product%20Engine", url, + "secondlife:///app/region/Product%20Engine", + "Product Engine"); + } } |