PageRenderTime 139ms CodeModel.GetById 56ms app.highlight 77ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/llui/llfocusmgr.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 492 lines | 368 code | 71 blank | 53 comment | 76 complexity | 56948b365f5fc4d5b16a2ba34866197e MD5 | raw file
  1/** 
  2 * @file llfocusmgr.cpp
  3 * @brief LLFocusMgr base class
  4 *
  5 * $LicenseInfo:firstyear=2002&license=viewerlgpl$
  6 * Second Life Viewer Source Code
  7 * Copyright (C) 2010, Linden Research, Inc.
  8 * 
  9 * This library is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU Lesser General Public
 11 * License as published by the Free Software Foundation;
 12 * version 2.1 of the License only.
 13 * 
 14 * This library is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17 * Lesser General Public License for more details.
 18 * 
 19 * You should have received a copy of the GNU Lesser General Public
 20 * License along with this library; if not, write to the Free Software
 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 22 * 
 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 24 * $/LicenseInfo$
 25 */
 26
 27#include "linden_common.h"
 28
 29#include "llfocusmgr.h"
 30#include "lluictrl.h"
 31#include "v4color.h"
 32
 33const F32 FOCUS_FADE_TIME = 0.3f;
 34
 35LLFocusableElement::LLFocusableElement()
 36:	mFocusLostCallback(NULL),
 37	mFocusReceivedCallback(NULL),
 38	mFocusChangedCallback(NULL),
 39	mTopLostCallback(NULL)
 40{
 41}
 42
 43// virtual
 44BOOL LLFocusableElement::handleKey(KEY key, MASK mask, BOOL called_from_parent)
 45{
 46	return FALSE;
 47}
 48
 49// virtual
 50BOOL LLFocusableElement::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent)
 51{
 52	return FALSE;
 53}
 54
 55// virtual
 56LLFocusableElement::~LLFocusableElement()
 57{
 58	delete mFocusLostCallback;
 59	delete mFocusReceivedCallback;
 60	delete mFocusChangedCallback;
 61	delete mTopLostCallback;
 62}
 63
 64void LLFocusableElement::onFocusReceived()
 65{
 66	if (mFocusReceivedCallback) (*mFocusReceivedCallback)(this);
 67	if (mFocusChangedCallback) (*mFocusChangedCallback)(this);
 68}
 69
 70void LLFocusableElement::onFocusLost()
 71{
 72	if (mFocusLostCallback) (*mFocusLostCallback)(this);
 73	if (mFocusChangedCallback) (*mFocusChangedCallback)(this);
 74}
 75
 76void LLFocusableElement::onTopLost()
 77{
 78	if (mTopLostCallback) (*mTopLostCallback)(this);
 79}
 80
 81BOOL LLFocusableElement::hasFocus() const
 82{
 83	return gFocusMgr.getKeyboardFocus() == this;
 84}
 85
 86void LLFocusableElement::setFocus(BOOL b)
 87{
 88}
 89
 90boost::signals2::connection LLFocusableElement::setFocusLostCallback( const focus_signal_t::slot_type& cb)	
 91{ 
 92	if (!mFocusLostCallback) mFocusLostCallback = new focus_signal_t();
 93	return mFocusLostCallback->connect(cb);
 94}
 95
 96boost::signals2::connection	LLFocusableElement::setFocusReceivedCallback(const focus_signal_t::slot_type& cb)	
 97{ 
 98	if (!mFocusReceivedCallback) mFocusReceivedCallback = new focus_signal_t();
 99	return mFocusReceivedCallback->connect(cb);
100}
101
102boost::signals2::connection	LLFocusableElement::setFocusChangedCallback(const focus_signal_t::slot_type& cb)	
103{
104	if (!mFocusChangedCallback) mFocusChangedCallback = new focus_signal_t();
105	return mFocusChangedCallback->connect(cb);
106}
107
108boost::signals2::connection	LLFocusableElement::setTopLostCallback(const focus_signal_t::slot_type& cb)	
109{ 
110	if (!mTopLostCallback) mTopLostCallback = new focus_signal_t();
111	return mTopLostCallback->connect(cb);
112}
113
114
115
116typedef std::list<LLHandle<LLView> > view_handle_list_t;
117typedef std::map<LLHandle<LLView>, LLHandle<LLView> > focus_history_map_t;
118struct LLFocusMgr::Impl
119{
120	// caching list of keyboard focus ancestors for calling onFocusReceived and onFocusLost
121	view_handle_list_t mCachedKeyboardFocusList;
122
123	focus_history_map_t mFocusHistory;
124};
125
126LLFocusMgr gFocusMgr;
127
128LLFocusMgr::LLFocusMgr()
129:	mLockedView( NULL ),
130	mMouseCaptor( NULL ),
131	mKeyboardFocus( NULL ),
132	mLastKeyboardFocus( NULL ),
133	mDefaultKeyboardFocus( NULL ),
134	mKeystrokesOnly(FALSE),
135	mTopCtrl( NULL ),
136	mAppHasFocus(TRUE),   // Macs don't seem to notify us that we've gotten focus, so default to true
137	mImpl(new LLFocusMgr::Impl)
138{
139}
140
141LLFocusMgr::~LLFocusMgr()
142{
143	mImpl->mFocusHistory.clear();
144	delete mImpl;
145	mImpl = NULL;
146}
147
148void LLFocusMgr::releaseFocusIfNeeded( LLView* view )
149{
150	if( childHasMouseCapture( view ) )
151	{
152		setMouseCapture( NULL );
153	}
154
155	if( childHasKeyboardFocus( view ))
156	{
157		if (view == mLockedView)
158		{
159			mLockedView = NULL;
160			setKeyboardFocus( NULL );
161		}
162		else
163		{
164			setKeyboardFocus( mLockedView );
165		}
166	}
167
168	LLUI::removePopup(view);
169}
170
171
172void LLFocusMgr::setKeyboardFocus(LLFocusableElement* new_focus, BOOL lock, BOOL keystrokes_only)
173{
174	// notes if keyboard focus is changed again (by onFocusLost/onFocusReceived)
175    // making the rest of our processing unnecessary since it will already be
176    // handled by the recursive call
177	static bool focus_dirty;
178	focus_dirty = false;
179
180	if (mLockedView && 
181		(new_focus == NULL || 
182			(new_focus != mLockedView 
183			&& dynamic_cast<LLView*>(new_focus)
184			&& !dynamic_cast<LLView*>(new_focus)->hasAncestor(mLockedView))))
185	{
186		// don't allow focus to go to anything that is not the locked focus
187		// or one of its descendants
188		return;
189	}
190
191	mKeystrokesOnly = keystrokes_only;
192
193	if( new_focus != mKeyboardFocus )
194	{
195		mLastKeyboardFocus = mKeyboardFocus;
196		mKeyboardFocus = new_focus;
197
198		// list of the focus and it's ancestors
199		view_handle_list_t old_focus_list = mImpl->mCachedKeyboardFocusList;
200		view_handle_list_t new_focus_list;
201
202		// walk up the tree to root and add all views to the new_focus_list
203		for (LLView* ctrl = dynamic_cast<LLView*>(mKeyboardFocus); ctrl; ctrl = ctrl->getParent())
204		{
205			new_focus_list.push_back(ctrl->getHandle());
206		}
207
208		// remove all common ancestors since their focus is unchanged
209		while (!new_focus_list.empty() &&
210			   !old_focus_list.empty() &&
211			   new_focus_list.back() == old_focus_list.back())
212		{
213			new_focus_list.pop_back();
214			old_focus_list.pop_back();
215		}
216
217		// walk up the old focus branch calling onFocusLost
218		// we bubble up the tree to release focus, and back down to add
219		for (view_handle_list_t::iterator old_focus_iter = old_focus_list.begin();
220			 old_focus_iter != old_focus_list.end() && !focus_dirty;
221			 old_focus_iter++)
222		{			
223			LLView* old_focus_view = old_focus_iter->get();
224			if (old_focus_view)
225			{
226				mImpl->mCachedKeyboardFocusList.pop_front();
227				old_focus_view->onFocusLost();
228			}
229		}
230
231		// walk down the new focus branch calling onFocusReceived
232		for (view_handle_list_t::reverse_iterator new_focus_riter = new_focus_list.rbegin();
233			 new_focus_riter != new_focus_list.rend() && !focus_dirty;
234			 new_focus_riter++)
235		{			
236			LLView* new_focus_view = new_focus_riter->get();
237			if (new_focus_view)
238			{
239                mImpl->mCachedKeyboardFocusList.push_front(new_focus_view->getHandle());
240				new_focus_view->onFocusReceived();
241			}
242		}
243		
244		// if focus was changed as part of an onFocusLost or onFocusReceived call
245		// stop iterating on current list since it is now invalid
246		if (focus_dirty)
247		{
248			return;
249		}
250
251		// If we've got a default keyboard focus, and the caller is
252		// releasing keyboard focus, move to the default.
253		if (mDefaultKeyboardFocus != NULL && mKeyboardFocus == NULL)
254		{
255			mDefaultKeyboardFocus->setFocus(TRUE);
256		}
257
258		LLView* focus_subtree = dynamic_cast<LLView*>(mKeyboardFocus);
259		LLView* viewp = dynamic_cast<LLView*>(mKeyboardFocus);
260		// find root-most focus root
261		while(viewp)
262		{
263			if (viewp->isFocusRoot())
264			{
265				focus_subtree = viewp;
266			}
267			viewp = viewp->getParent();
268		}
269
270		
271		if (focus_subtree)
272		{
273			LLView* focused_view = dynamic_cast<LLView*>(mKeyboardFocus);
274			mImpl->mFocusHistory[focus_subtree->getHandle()] = focused_view ? focused_view->getHandle() : LLHandle<LLView>(); 
275		}
276	}
277	
278	if (lock)
279	{
280		lockFocus();
281	}
282
283	focus_dirty = true;
284}
285
286
287// Returns TRUE is parent or any descedent of parent has keyboard focus.
288BOOL LLFocusMgr::childHasKeyboardFocus(const LLView* parent ) const
289{
290	LLView* focus_view = dynamic_cast<LLView*>(mKeyboardFocus);
291	while( focus_view )
292	{
293		if( focus_view == parent )
294		{
295			return TRUE;
296		}
297		focus_view = focus_view->getParent();
298	}
299	return FALSE;
300}
301
302// Returns TRUE is parent or any descedent of parent is the mouse captor.
303BOOL LLFocusMgr::childHasMouseCapture( const LLView* parent ) const
304{
305	if( mMouseCaptor && dynamic_cast<LLView*>(mMouseCaptor) != NULL )
306	{
307		LLView* captor_view = (LLView*)mMouseCaptor;
308		while( captor_view )
309		{
310			if( captor_view == parent )
311			{
312				return TRUE;
313			}
314			captor_view = captor_view->getParent();
315		}
316	}
317	return FALSE;
318}
319
320void LLFocusMgr::removeKeyboardFocusWithoutCallback( const LLFocusableElement* focus )
321{
322	// should be ok to unlock here, as you have to know the locked view
323	// in order to unlock it
324	if (focus == mLockedView)
325	{
326		mLockedView = NULL;
327	}
328
329	if( mKeyboardFocus == focus )
330	{
331		mKeyboardFocus = NULL;
332	}
333}
334
335bool LLFocusMgr::keyboardFocusHasAccelerators() const
336{
337	LLView* focus_view = dynamic_cast<LLView*>(mKeyboardFocus);
338	while( focus_view )
339	{
340		if(focus_view->hasAccelerators())
341		{
342			return true;
343		}
344
345		focus_view = focus_view->getParent();
346	}
347	return false;
348}
349
350void LLFocusMgr::setMouseCapture( LLMouseHandler* new_captor )
351{
352	if( new_captor != mMouseCaptor )
353	{
354		LLMouseHandler* old_captor = mMouseCaptor;
355		mMouseCaptor = new_captor;
356		
357		if (LLView::sDebugMouseHandling)
358		{
359			if (new_captor)
360			{
361				llinfos << "New mouse captor: " << new_captor->getName() << llendl;
362			}
363			else
364			{
365				llinfos << "New mouse captor: NULL" << llendl;
366			}
367		}
368			
369		if( old_captor )
370		{
371			old_captor->onMouseCaptureLost();
372		}
373
374	}
375}
376
377void LLFocusMgr::removeMouseCaptureWithoutCallback( const LLMouseHandler* captor )
378{
379	if( mMouseCaptor == captor )
380	{
381		mMouseCaptor = NULL;
382	}
383}
384
385
386BOOL LLFocusMgr::childIsTopCtrl( const LLView* parent ) const
387{
388	LLView* top_view = (LLView*)mTopCtrl;
389	while( top_view )
390	{
391		if( top_view == parent )
392		{
393			return TRUE;
394		}
395		top_view = top_view->getParent();
396	}
397	return FALSE;
398}
399
400
401
402// set new_top = NULL to release top_view.
403void LLFocusMgr::setTopCtrl( LLUICtrl* new_top  )
404{
405	LLUICtrl* old_top = mTopCtrl;
406	if( new_top != old_top )
407	{
408		mTopCtrl = new_top;
409
410		if (old_top)
411		{
412			old_top->onTopLost();
413		}
414	}
415}
416
417void LLFocusMgr::removeTopCtrlWithoutCallback( const LLUICtrl* top_view )
418{
419	if( mTopCtrl == top_view )
420	{
421		mTopCtrl = NULL;
422	}
423}
424
425void LLFocusMgr::lockFocus()
426{
427	mLockedView = dynamic_cast<LLUICtrl*>(mKeyboardFocus); 
428}
429
430void LLFocusMgr::unlockFocus()
431{
432	mLockedView = NULL; 
433}
434
435F32 LLFocusMgr::getFocusFlashAmt() const
436{
437	return clamp_rescale(mFocusFlashTimer.getElapsedTimeF32(), 0.f, FOCUS_FADE_TIME, 1.f, 0.f);
438}
439
440LLColor4 LLFocusMgr::getFocusColor() const
441{
442	static LLUIColor focus_color_cached = LLUIColorTable::instance().getColor("FocusColor");
443	LLColor4 focus_color = lerp(focus_color_cached, LLColor4::white, getFocusFlashAmt());
444	// de-emphasize keyboard focus when app has lost focus (to avoid typing into wrong window problem)
445	if (!mAppHasFocus)
446	{
447		focus_color.mV[VALPHA] *= 0.4f;
448	}
449	return focus_color;
450}
451
452void LLFocusMgr::triggerFocusFlash()
453{
454	mFocusFlashTimer.reset();
455}
456
457void LLFocusMgr::setAppHasFocus(BOOL focus) 
458{ 
459	if (!mAppHasFocus && focus)
460	{
461		triggerFocusFlash();
462	}
463	
464	// release focus from "top ctrl"s, which generally hides them
465	if (!focus)
466	{
467		LLUI::clearPopups();
468	}
469	mAppHasFocus = focus; 
470}
471
472LLUICtrl* LLFocusMgr::getLastFocusForGroup(LLView* subtree_root) const
473{
474	if (subtree_root)
475	{
476		focus_history_map_t::const_iterator found_it = mImpl->mFocusHistory.find(subtree_root->getHandle());
477		if (found_it != mImpl->mFocusHistory.end())
478		{
479			// found last focus for this subtree
480			return static_cast<LLUICtrl*>(found_it->second.get());
481		}
482	}
483	return NULL;
484}
485
486void LLFocusMgr::clearLastFocusForGroup(LLView* subtree_root)
487{
488	if (subtree_root)
489	{
490		mImpl->mFocusHistory.erase(subtree_root->getHandle());
491	}
492}