/** * @file llfocusmgr.cpp * @brief LLFocusMgr base class * * $LicenseInfo:firstyear=2002&license=viewergpl$ * * Copyright (c) 2002-2009, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at * http://secondlifegrid.net/programs/open_source/licensing/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, * and agree to abide by those obligations. * * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ */ #include "linden_common.h" #include "llfocusmgr.h" #include "lluictrl.h" #include "v4color.h" const F32 FOCUS_FADE_TIME = 0.3f; LLFocusableElement::LLFocusableElement() : mFocusLostCallback(NULL), mFocusReceivedCallback(NULL), mFocusChangedCallback(NULL), mTopLostCallback(NULL) { } // virtual BOOL LLFocusableElement::handleKey(KEY key, MASK mask, BOOL called_from_parent) { return FALSE; } // virtual BOOL LLFocusableElement::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent) { return FALSE; } // virtual LLFocusableElement::~LLFocusableElement() { delete mFocusLostCallback; delete mFocusReceivedCallback; delete mFocusChangedCallback; delete mTopLostCallback; } void LLFocusableElement::onFocusReceived() { if (mFocusReceivedCallback) (*mFocusReceivedCallback)(this); if (mFocusChangedCallback) (*mFocusChangedCallback)(this); } void LLFocusableElement::onFocusLost() { if (mFocusLostCallback) (*mFocusLostCallback)(this); if (mFocusChangedCallback) (*mFocusChangedCallback)(this); } void LLFocusableElement::onTopLost() { if (mTopLostCallback) (*mTopLostCallback)(this); } BOOL LLFocusableElement::hasFocus() const { return gFocusMgr.getKeyboardFocus() == this; } void LLFocusableElement::setFocus(BOOL b) { } boost::signals2::connection LLFocusableElement::setFocusLostCallback( const focus_signal_t::slot_type& cb) { if (!mFocusLostCallback) mFocusLostCallback = new focus_signal_t(); return mFocusLostCallback->connect(cb); } boost::signals2::connection LLFocusableElement::setFocusReceivedCallback(const focus_signal_t::slot_type& cb) { if (!mFocusReceivedCallback) mFocusReceivedCallback = new focus_signal_t(); return mFocusReceivedCallback->connect(cb); } boost::signals2::connection LLFocusableElement::setFocusChangedCallback(const focus_signal_t::slot_type& cb) { if (!mFocusChangedCallback) mFocusChangedCallback = new focus_signal_t(); return mFocusChangedCallback->connect(cb); } boost::signals2::connection LLFocusableElement::setTopLostCallback(const focus_signal_t::slot_type& cb) { if (!mTopLostCallback) mTopLostCallback = new focus_signal_t(); return mTopLostCallback->connect(cb); } LLFocusMgr gFocusMgr; LLFocusMgr::LLFocusMgr() : mLockedView( NULL ), mMouseCaptor( NULL ), mKeyboardFocus( NULL ), mLastKeyboardFocus( NULL ), mDefaultKeyboardFocus( NULL ), mKeystrokesOnly(FALSE), mTopCtrl( NULL ), mAppHasFocus(TRUE) // Macs don't seem to notify us that we've gotten focus, so default to true { } void LLFocusMgr::releaseFocusIfNeeded( LLView* view ) { if( childHasMouseCapture( view ) ) { setMouseCapture( NULL ); } if( childHasKeyboardFocus( view )) { if (view == mLockedView) { mLockedView = NULL; setKeyboardFocus( NULL ); } else { setKeyboardFocus( mLockedView ); } } LLUI::removePopup(view); } void LLFocusMgr::setKeyboardFocus(LLFocusableElement* new_focus, BOOL lock, BOOL keystrokes_only) { // notes if keyboard focus is changed again (by onFocusLost/onFocusReceived) // making the rest of our processing unnecessary since it will already be // handled by the recursive call static bool focus_dirty; focus_dirty = false; if (mLockedView && (new_focus == NULL || (new_focus != mLockedView && dynamic_cast<LLView*>(new_focus) && !dynamic_cast<LLView*>(new_focus)->hasAncestor(mLockedView)))) { // don't allow focus to go to anything that is not the locked focus // or one of its descendants return; } mKeystrokesOnly = keystrokes_only; if( new_focus != mKeyboardFocus ) { mLastKeyboardFocus = mKeyboardFocus; mKeyboardFocus = new_focus; // list of the focus and it's ancestors view_handle_list_t old_focus_list = mCachedKeyboardFocusList; view_handle_list_t new_focus_list; // walk up the tree to root and add all views to the new_focus_list for (LLView* ctrl = dynamic_cast<LLView*>(mKeyboardFocus); ctrl; ctrl = ctrl->getParent()) { new_focus_list.push_back(ctrl->getHandle()); } // remove all common ancestors since their focus is unchanged while (!new_focus_list.empty() && !old_focus_list.empty() && new_focus_list.back() == old_focus_list.back()) { new_focus_list.pop_back(); old_focus_list.pop_back(); } // walk up the old focus branch calling onFocusLost // we bubble up the tree to release focus, and back down to add for (view_handle_list_t::iterator old_focus_iter = old_focus_list.begin(); old_focus_iter != old_focus_list.end() && !focus_dirty; old_focus_iter++) { LLView* old_focus_view = old_focus_iter->get(); if (old_focus_view) { mCachedKeyboardFocusList.pop_front(); old_focus_view->onFocusLost(); } } // walk down the new focus branch calling onFocusReceived for (view_handle_list_t::reverse_iterator new_focus_riter = new_focus_list.rbegin(); new_focus_riter != new_focus_list.rend() && !focus_dirty; new_focus_riter++) { LLView* new_focus_view = new_focus_riter->get(); if (new_focus_view) { mCachedKeyboardFocusList.push_front(new_focus_view->getHandle()); new_focus_view->onFocusReceived(); } } // if focus was changed as part of an onFocusLost or onFocusReceived call // stop iterating on current list since it is now invalid if (focus_dirty) { return; } // If we've got a default keyboard focus, and the caller is // releasing keyboard focus, move to the default. if (mDefaultKeyboardFocus != NULL && mKeyboardFocus == NULL) { mDefaultKeyboardFocus->setFocus(TRUE); } LLView* focus_subtree = dynamic_cast<LLView*>(mKeyboardFocus); LLView* viewp = dynamic_cast<LLView*>(mKeyboardFocus); // find root-most focus root while(viewp) { if (viewp->isFocusRoot()) { focus_subtree = viewp; } viewp = viewp->getParent(); } if (focus_subtree) { LLView* focused_view = dynamic_cast<LLView*>(mKeyboardFocus); mFocusHistory[focus_subtree->getHandle()] = focused_view ? focused_view->getHandle() : LLHandle<LLView>(); } } if (lock) { lockFocus(); } focus_dirty = true; } // Returns TRUE is parent or any descedent of parent has keyboard focus. BOOL LLFocusMgr::childHasKeyboardFocus(const LLView* parent ) const { LLView* focus_view = dynamic_cast<LLView*>(mKeyboardFocus); while( focus_view ) { if( focus_view == parent ) { return TRUE; } focus_view = focus_view->getParent(); } return FALSE; } // Returns TRUE is parent or any descedent of parent is the mouse captor. BOOL LLFocusMgr::childHasMouseCapture( const LLView* parent ) const { if( mMouseCaptor && dynamic_cast<LLView*>(mMouseCaptor) != NULL ) { LLView* captor_view = (LLView*)mMouseCaptor; while( captor_view ) { if( captor_view == parent ) { return TRUE; } captor_view = captor_view->getParent(); } } return FALSE; } void LLFocusMgr::removeKeyboardFocusWithoutCallback( const LLFocusableElement* focus ) { // should be ok to unlock here, as you have to know the locked view // in order to unlock it if (focus == mLockedView) { mLockedView = NULL; } if( mKeyboardFocus == focus ) { mKeyboardFocus = NULL; } } void LLFocusMgr::setMouseCapture( LLMouseHandler* new_captor ) { if( new_captor != mMouseCaptor ) { LLMouseHandler* old_captor = mMouseCaptor; mMouseCaptor = new_captor; if (LLView::sDebugMouseHandling) { if (new_captor) { llinfos << "New mouse captor: " << new_captor->getName() << llendl; } else { llinfos << "New mouse captor: NULL" << llendl; } } if( old_captor ) { old_captor->onMouseCaptureLost(); } } } void LLFocusMgr::removeMouseCaptureWithoutCallback( const LLMouseHandler* captor ) { if( mMouseCaptor == captor ) { mMouseCaptor = NULL; } } BOOL LLFocusMgr::childIsTopCtrl( const LLView* parent ) const { LLView* top_view = (LLView*)mTopCtrl; while( top_view ) { if( top_view == parent ) { return TRUE; } top_view = top_view->getParent(); } return FALSE; } // set new_top = NULL to release top_view. void LLFocusMgr::setTopCtrl( LLUICtrl* new_top ) { LLUICtrl* old_top = mTopCtrl; if( new_top != old_top ) { mTopCtrl = new_top; if (old_top) { old_top->onTopLost(); } } } void LLFocusMgr::removeTopCtrlWithoutCallback( const LLUICtrl* top_view ) { if( mTopCtrl == top_view ) { mTopCtrl = NULL; } } void LLFocusMgr::lockFocus() { mLockedView = dynamic_cast<LLUICtrl*>(mKeyboardFocus); } void LLFocusMgr::unlockFocus() { mLockedView = NULL; } F32 LLFocusMgr::getFocusFlashAmt() const { return clamp_rescale(mFocusFlashTimer.getElapsedTimeF32(), 0.f, FOCUS_FADE_TIME, 1.f, 0.f); } LLColor4 LLFocusMgr::getFocusColor() const { static LLUIColor focus_color_cached = LLUIColorTable::instance().getColor("FocusColor"); LLColor4 focus_color = lerp(focus_color_cached, LLColor4::white, getFocusFlashAmt()); // de-emphasize keyboard focus when app has lost focus (to avoid typing into wrong window problem) if (!mAppHasFocus) { focus_color.mV[VALPHA] *= 0.4f; } return focus_color; } void LLFocusMgr::triggerFocusFlash() { mFocusFlashTimer.reset(); } void LLFocusMgr::setAppHasFocus(BOOL focus) { if (!mAppHasFocus && focus) { triggerFocusFlash(); } // release focus from "top ctrl"s, which generally hides them if (!focus) { LLUI::clearPopups(); } mAppHasFocus = focus; } LLUICtrl* LLFocusMgr::getLastFocusForGroup(LLView* subtree_root) const { if (subtree_root) { focus_history_map_t::const_iterator found_it = mFocusHistory.find(subtree_root->getHandle()); if (found_it != mFocusHistory.end()) { // found last focus for this subtree return static_cast<LLUICtrl*>(found_it->second.get()); } } return NULL; } void LLFocusMgr::clearLastFocusForGroup(LLView* subtree_root) { if (subtree_root) { mFocusHistory.erase(subtree_root->getHandle()); } }