1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_extensions.hxx"
30 #include "browserlistbox.hxx"
31 #ifndef EXTENSIONS_PROPRESID_HRC
32 #include "propresid.hrc"
33 #endif
34 #include "proplinelistener.hxx"
35 #include "propcontrolobserver.hxx"
36 #include "linedescriptor.hxx"
37 #include "inspectorhelpwindow.hxx"
38 
39 /** === begin UNO includes === **/
40 #include <com/sun/star/lang/DisposedException.hpp>
41 #include <com/sun/star/lang/XComponent.hpp>
42 #include <com/sun/star/inspection/PropertyControlType.hpp>
43 /** === end UNO includes === **/
44 #include <tools/debug.hxx>
45 #include <tools/diagnose_ex.h>
46 #include <comphelper/asyncnotification.hxx>
47 #include <cppuhelper/implbase1.hxx>
48 #include <vcl/svapp.hxx>
49 #include <vos/mutex.hxx>
50 
51 //............................................................................
52 namespace pcr
53 {
54 //............................................................................
55 
56     #define FRAME_OFFSET 4
57 	    // TODO: find out what this is really for ... and check if it does make sense in the new
58 	    // browser environment
59     #define LAYOUT_HELP_WINDOW_DISTANCE_APPFONT 3
60 
61     /** === begin UNO using === **/
62     using ::com::sun::star::uno::Any;
63     using ::com::sun::star::uno::Exception;
64     using ::com::sun::star::inspection::XPropertyControlContext;
65     using ::com::sun::star::uno::Reference;
66     using ::com::sun::star::inspection::XPropertyControl;
67     using ::com::sun::star::uno::RuntimeException;
68     using ::com::sun::star::lang::DisposedException;
69     using ::com::sun::star::lang::XComponent;
70     using ::com::sun::star::uno::UNO_QUERY;
71     using ::com::sun::star::graphic::XGraphic;
72     /** === end UNO using === **/
73     namespace PropertyControlType = ::com::sun::star::inspection::PropertyControlType;
74 
75 	//==================================================================
76 	//= ControlEvent
77 	//==================================================================
78     enum ControlEventType
79     {
80         FOCUS_GAINED,
81         VALUE_CHANGED,
82         ACTIVATE_NEXT
83     };
84 
85     struct ControlEvent : public ::comphelper::AnyEvent
86     {
87         Reference< XPropertyControl >   xControl;
88         ControlEventType                eType;
89 
90         ControlEvent( const Reference< XPropertyControl >& _rxControl, ControlEventType _eType )
91             :xControl( _rxControl )
92             ,eType( _eType )
93         {
94         }
95     };
96 
97 	//==================================================================
98 	//= SharedNotifier
99 	//==================================================================
100     class SharedNotifier
101     {
102     private:
103         static ::osl::Mutex&                                        getMutex();
104         static ::rtl::Reference< ::comphelper::AsyncEventNotifier > s_pNotifier;
105 
106     public:
107         static const ::rtl::Reference< ::comphelper::AsyncEventNotifier >&
108             getNotifier();
109 
110     private:
111         SharedNotifier();                                   // never implemented
112         SharedNotifier( const SharedNotifier& );            // never implemented
113         SharedNotifier& operator=( const SharedNotifier& ); // never implemented
114     };
115 
116 	//------------------------------------------------------------------
117     ::rtl::Reference< ::comphelper::AsyncEventNotifier > SharedNotifier::s_pNotifier;
118 
119 	//------------------------------------------------------------------
120     ::osl::Mutex& SharedNotifier::getMutex()
121     {
122         static ::osl::Mutex s_aMutex;
123         return s_aMutex;
124     }
125 
126 	//------------------------------------------------------------------
127     const ::rtl::Reference< ::comphelper::AsyncEventNotifier >& SharedNotifier::getNotifier()
128     {
129         ::osl::MutexGuard aGuard( getMutex() );
130         if ( !s_pNotifier.is() )
131         {
132             s_pNotifier.set( new ::comphelper::AsyncEventNotifier );
133             s_pNotifier->create();
134         }
135         return s_pNotifier;
136     }
137 
138 	//==================================================================
139 	//= PropertyControlContext_Impl
140 	//==================================================================
141     /** implementation for of <type scope="com::sun::star::inspection">XPropertyControlContext</type>
142         which forwards all events to a non-UNO version of this interface
143     */
144     typedef ::cppu::WeakImplHelper1< XPropertyControlContext > PropertyControlContext_Impl_Base;
145     class PropertyControlContext_Impl   :public PropertyControlContext_Impl_Base
146                                         ,public ::comphelper::IEventProcessor
147     {
148     public:
149         enum NotifcationMode
150         {
151             eSynchronously,
152             eAsynchronously
153         };
154 
155     private:
156         IControlContext*    m_pContext;
157         NotifcationMode     m_eMode;
158 
159     public:
160         /** creates an instance
161             @param _rContextImpl
162                 the instance to delegate events to
163         */
164         PropertyControlContext_Impl( IControlContext& _rContextImpl );
165 
166         /** disposes the context.
167 
168             When you call this method, all subsequent callbacks to the
169             <type scope="com::sun::star::inspection">XPropertyControlContext</type> methods
170             will throw a <type scope="com::sun::star::lang">DisposedException</type>.
171         */
172         void SAL_CALL dispose();
173 
174         /** sets the notification mode, so that notifications recieved from the controls are
175             forwarded to our IControlContext either synchronously or asynchronously
176             @param  _eMode
177                 the new notification mode
178         */
179         void setNotificationMode( NotifcationMode _eMode );
180 
181         virtual void SAL_CALL acquire() throw();
182         virtual void SAL_CALL release() throw();
183 
184     protected:
185         ~PropertyControlContext_Impl();
186 
187         // XPropertyControlObserver
188         virtual void SAL_CALL focusGained( const Reference< XPropertyControl >& Control ) throw (RuntimeException);
189         virtual void SAL_CALL valueChanged( const Reference< XPropertyControl >& Control ) throw (RuntimeException);
190         // XPropertyControlContext
191         virtual void SAL_CALL activateNextControl( const Reference< XPropertyControl >& CurrentControl ) throw (RuntimeException);
192 
193         // IEventProcessor
194         virtual void processEvent( const ::comphelper::AnyEvent& _rEvent );
195 
196     private:
197         /** processes the given event, i.e. notifies it to our IControlContext
198             @param  _rEvent
199                 the event no notify
200             @precond
201                 our mutex (well, the SolarMutex) is locked
202         */
203         void impl_processEvent_throw( const ::comphelper::AnyEvent& _rEvent );
204 
205         /** checks whether we're alive
206 
207             @throws DisposedException
208                 if the instance is already disposed
209         */
210         void impl_checkAlive_throw() const;
211 
212         /** checks whether the instance is already disposed
213         */
214         bool impl_isDisposed_nothrow() const { return m_pContext == NULL; }
215 
216         /** notifies the given event originating from the given control
217         @throws DisposedException
218         @param  _rxControl
219         @param  _eType
220         */
221         void impl_notify_throw( const Reference< XPropertyControl >& _rxControl, ControlEventType _eType );
222     };
223 
224     //--------------------------------------------------------------------
225     PropertyControlContext_Impl::PropertyControlContext_Impl( IControlContext& _rContextImpl )
226         :m_pContext( &_rContextImpl )
227         ,m_eMode( eAsynchronously )
228     {
229     }
230 
231     //--------------------------------------------------------------------
232     PropertyControlContext_Impl::~PropertyControlContext_Impl()
233     {
234         if ( !impl_isDisposed_nothrow() )
235             dispose();
236     }
237 
238     //--------------------------------------------------------------------
239     void PropertyControlContext_Impl::impl_checkAlive_throw() const
240     {
241         if ( impl_isDisposed_nothrow() )
242             throw DisposedException( ::rtl::OUString(), *const_cast< PropertyControlContext_Impl* >( this ) );
243     }
244 
245     //--------------------------------------------------------------------
246     void SAL_CALL PropertyControlContext_Impl::dispose()
247     {
248         ::vos::OGuard aGuard( Application::GetSolarMutex() );
249         if ( impl_isDisposed_nothrow() )
250             return;
251 
252         SharedNotifier::getNotifier()->removeEventsForProcessor( this );
253         m_pContext = NULL;
254     }
255 
256     //--------------------------------------------------------------------
257     void PropertyControlContext_Impl::setNotificationMode( NotifcationMode _eMode )
258     {
259         ::vos::OGuard aGuard( Application::GetSolarMutex() );
260         m_eMode = _eMode;
261     }
262 
263     //--------------------------------------------------------------------
264     void PropertyControlContext_Impl::impl_notify_throw( const Reference< XPropertyControl >& _rxControl, ControlEventType _eType )
265     {
266         ::comphelper::AnyEventRef pEvent;
267 
268         {
269             ::vos::OGuard aGuard( Application::GetSolarMutex() );
270             impl_checkAlive_throw();
271             pEvent = new ControlEvent( _rxControl, _eType );
272 
273             if ( m_eMode == eSynchronously )
274             {
275                 impl_processEvent_throw( *pEvent );
276                 return;
277             }
278         }
279 
280         SharedNotifier::getNotifier()->addEvent( pEvent, this );
281     }
282 
283     //--------------------------------------------------------------------
284     void SAL_CALL PropertyControlContext_Impl::focusGained( const Reference< XPropertyControl >& Control ) throw (RuntimeException)
285     {
286         DBG_TRACE( "PropertyControlContext_Impl: FOCUS_GAINED" );
287         impl_notify_throw( Control, FOCUS_GAINED );
288     }
289 
290     //--------------------------------------------------------------------
291     void SAL_CALL PropertyControlContext_Impl::valueChanged( const Reference< XPropertyControl >& Control ) throw (RuntimeException)
292     {
293         DBG_TRACE( "PropertyControlContext_Impl: VALUE_CHANGED" );
294         impl_notify_throw( Control, VALUE_CHANGED );
295     }
296 
297     //--------------------------------------------------------------------
298     void SAL_CALL PropertyControlContext_Impl::activateNextControl( const Reference< XPropertyControl >& CurrentControl ) throw (RuntimeException)
299     {
300         DBG_TRACE( "PropertyControlContext_Impl: ACTIVATE_NEXT" );
301         impl_notify_throw( CurrentControl, ACTIVATE_NEXT );
302     }
303 
304     //--------------------------------------------------------------------
305     void SAL_CALL PropertyControlContext_Impl::acquire() throw()
306     {
307         PropertyControlContext_Impl_Base::acquire();
308     }
309 
310     //--------------------------------------------------------------------
311     void SAL_CALL PropertyControlContext_Impl::release() throw()
312     {
313         PropertyControlContext_Impl_Base::release();
314     }
315 
316     //--------------------------------------------------------------------
317     void PropertyControlContext_Impl::processEvent( const ::comphelper::AnyEvent& _rEvent )
318     {
319         ::vos::OGuard aGuard( Application::GetSolarMutex() );
320         if ( impl_isDisposed_nothrow() )
321             return;
322 
323         try
324         {
325             impl_processEvent_throw( _rEvent );
326         }
327         catch( const Exception& )
328         {
329             // can't handle otherwise, since our caller (the notification thread) does not allow
330             // for exceptions (it could itself abort only)
331         	DBG_UNHANDLED_EXCEPTION();
332         }
333     }
334 
335     //--------------------------------------------------------------------
336     void PropertyControlContext_Impl::impl_processEvent_throw( const ::comphelper::AnyEvent& _rEvent )
337     {
338         const ControlEvent& rControlEvent = static_cast< const ControlEvent& >( _rEvent );
339         switch ( rControlEvent.eType )
340         {
341         case FOCUS_GAINED:
342             DBG_TRACE( "PropertyControlContext_Impl::processEvent: FOCUS_GAINED" );
343             m_pContext->focusGained( rControlEvent.xControl );
344             break;
345         case VALUE_CHANGED:
346             DBG_TRACE( "PropertyControlContext_Impl::processEvent: VALUE_CHANGED" );
347             m_pContext->valueChanged( rControlEvent.xControl );
348             break;
349         case ACTIVATE_NEXT:
350             DBG_TRACE( "PropertyControlContext_Impl::processEvent: ACTIVATE_NEXT" );
351             m_pContext->activateNextControl( rControlEvent.xControl );
352             break;
353         }
354     }
355 
356     //==================================================================
357 	//= OBrowserListBox
358 	//==================================================================
359 	DBG_NAME(OBrowserListBox)
360 	//------------------------------------------------------------------
361 	OBrowserListBox::OBrowserListBox( Window* pParent, WinBits nWinStyle)
362 			:Control(pParent, nWinStyle| WB_CLIPCHILDREN)
363 			,m_aLinesPlayground(this,WB_DIALOGCONTROL | WB_CLIPCHILDREN)
364 			,m_aVScroll(this,WB_VSCROLL|WB_REPEAT|WB_DRAG)
365             ,m_pHelpWindow( new InspectorHelpWindow( this ) )
366 			,m_pLineListener(NULL)
367             ,m_pControlObserver( NULL )
368 			,m_nYOffset(0)
369             ,m_nCurrentPreferredHelpHeight(0)
370 			,m_nTheNameSize(0)
371 			,m_bIsActive(sal_False)
372 			,m_bUpdate(sal_True)
373             ,m_pControlContextImpl( new PropertyControlContext_Impl( *this ) )
374 	{
375 		DBG_CTOR(OBrowserListBox,NULL);
376 
377 		ListBox aListBox(this,WB_DROPDOWN);
378 		aListBox.SetPosSizePixel(Point(0,0),Size(100,100));
379 		m_nRowHeight = (sal_uInt16)aListBox.GetSizePixel().Height()+2;
380 		SetBackground( pParent->GetBackground() );
381         m_aLinesPlayground.SetBackground( GetBackground() );
382 
383 		m_aLinesPlayground.SetPosPixel(Point(0,0));
384 		m_aLinesPlayground.SetPaintTransparent(sal_True);
385 		m_aLinesPlayground.Show();
386 		m_aVScroll.Hide();
387 		m_aVScroll.SetScrollHdl(LINK(this, OBrowserListBox, ScrollHdl));
388 	}
389 
390 	//------------------------------------------------------------------
391 	OBrowserListBox::~OBrowserListBox()
392 	{
393 		OSL_ENSURE( !IsModified(), "OBrowserListBox::~OBrowserListBox: still modified - should have been committed before!" );
394 			// doing the commit here, while we, as well as our owner, as well as some other components,
395 			// are already "half dead" (means within their dtor) is potentially dangerous.
396 			// By definition, CommitModified has to be called (if necessary) before destruction
397 			// #105868# - 2002-12-13 - fs@openoffice.org
398 
399         m_pControlContextImpl->dispose();
400         m_pControlContextImpl.clear();
401 
402 		Hide();
403 		Clear();
404 
405 		DBG_DTOR(OBrowserListBox,NULL);
406 	}
407 
408 	//------------------------------------------------------------------
409 	sal_Bool OBrowserListBox::IsModified( ) const
410 	{
411 		sal_Bool bModified = sal_False;
412 
413 		if ( m_bIsActive && m_xActiveControl.is() )
414 			bModified = m_xActiveControl->isModified();
415 
416         return bModified;
417 	}
418 
419 	//------------------------------------------------------------------
420 	void OBrowserListBox::CommitModified( )
421 	{
422 		if ( IsModified() && m_xActiveControl.is() )
423         {
424             // for the time of this commit, notify all events synchronously
425             // #i63814# / 2006-03-31 / frank.schoenheit@sun.com
426             m_pControlContextImpl->setNotificationMode( PropertyControlContext_Impl::eSynchronously );
427             try
428             {
429 			    m_xActiveControl->notifyModifiedValue();
430             }
431             catch( const Exception& )
432             {
433             	DBG_UNHANDLED_EXCEPTION();
434             }
435             m_pControlContextImpl->setNotificationMode( PropertyControlContext_Impl::eAsynchronously );
436         }
437 	}
438 
439 	//------------------------------------------------------------------
440 	void OBrowserListBox::ActivateListBox(sal_Bool _bActive)
441 	{
442 		m_bIsActive = _bActive;
443 		if (m_bIsActive)
444 		{
445 			// TODO: what's the sense of this?
446 			m_aVScroll.SetThumbPos(100);
447 			MoveThumbTo(0);
448 			Resize();
449 		}
450 	}
451 
452 	//------------------------------------------------------------------
453     long OBrowserListBox::impl_getPrefererredHelpHeight()
454     {
455         return HasHelpSection() ? m_pHelpWindow->GetOptimalHeightPixel() : 0;
456     }
457 
458 	//------------------------------------------------------------------
459 	void OBrowserListBox::Resize()
460 	{
461         Rectangle aPlayground( Point( 0, 0 ), GetOutputSizePixel() );
462         Size aHelpWindowDistance( LogicToPixel( Size( 0, LAYOUT_HELP_WINDOW_DISTANCE_APPFONT ), MAP_APPFONT ) );
463 
464         long nHelpWindowHeight = m_nCurrentPreferredHelpHeight = impl_getPrefererredHelpHeight();
465         bool bPositionHelpWindow = ( nHelpWindowHeight != 0 );
466 
467         Rectangle aLinesArea( aPlayground );
468         if ( bPositionHelpWindow )
469         {
470             aLinesArea.Bottom() -= nHelpWindowHeight;
471             aLinesArea.Bottom() -= aHelpWindowDistance.Height();
472         }
473 		m_aLinesPlayground.SetPosSizePixel( aLinesArea.TopLeft(), aLinesArea.GetSize() );
474 
475 		UpdateVScroll();
476 
477 		sal_Bool bNeedScrollbar = m_aOrderedLines.size() > (sal_uInt32)CalcVisibleLines();
478         if ( !bNeedScrollbar )
479 		{
480 			if ( m_aVScroll.IsVisible() )
481 				m_aVScroll.Hide();
482             // scroll to top
483 			m_nYOffset = 0;
484 			m_aVScroll.SetThumbPos( 0 );
485 		}
486 		else
487 		{
488             Size aVScrollSize( m_aVScroll.GetSizePixel() );
489 
490             // adjust the playground's width
491             aLinesArea.Right() -= aVScrollSize.Width();
492 		    m_aLinesPlayground.SetPosSizePixel( aLinesArea.TopLeft(), aLinesArea.GetSize() );
493 
494             // position the scrollbar
495 			aVScrollSize.Height() = aLinesArea.GetHeight();
496             Point aVScrollPos( aLinesArea.GetWidth(), 0 );
497             m_aVScroll.SetPosSizePixel( aVScrollPos, aVScrollSize );
498 		}
499 
500 		for ( sal_uInt16 i = 0; i < m_aOrderedLines.size(); ++i )
501             m_aOutOfDateLines.insert( i );
502 
503 		// repaint
504 		EnablePaint(sal_False);
505 		UpdatePlayGround();
506 		EnablePaint(sal_True);
507 
508 		// show the scrollbar
509 		if ( bNeedScrollbar )
510 			m_aVScroll.Show();
511 
512         // position the help window
513         if ( bPositionHelpWindow )
514         {
515             Rectangle aHelpArea( aPlayground );
516             aHelpArea.Top() = aLinesArea.Bottom() + aHelpWindowDistance.Height();
517             m_pHelpWindow->SetPosSizePixel( aHelpArea.TopLeft(), aHelpArea.GetSize() );
518         }
519 	}
520 
521 	//------------------------------------------------------------------
522 	void OBrowserListBox::SetListener( IPropertyLineListener* _pListener )
523 	{
524 		m_pLineListener = _pListener;
525 	}
526 
527 	//------------------------------------------------------------------
528 	void OBrowserListBox::SetObserver( IPropertyControlObserver* _pObserver )
529 	{
530 		m_pControlObserver = _pObserver;
531 	}
532 
533 	//------------------------------------------------------------------
534 	void OBrowserListBox::EnableHelpSection( bool _bEnable )
535     {
536         m_pHelpWindow->Show( _bEnable );
537         Resize();
538     }
539 
540 	//------------------------------------------------------------------
541     bool OBrowserListBox::HasHelpSection() const
542     {
543         return m_pHelpWindow->IsVisible();
544     }
545 
546 	//------------------------------------------------------------------
547 	void OBrowserListBox::SetHelpText( const ::rtl::OUString& _rHelpText )
548     {
549         OSL_ENSURE( HasHelpSection(), "OBrowserListBox::SetHelpText: help section not visible!" );
550         m_pHelpWindow->SetText( _rHelpText );
551         if ( m_nCurrentPreferredHelpHeight != impl_getPrefererredHelpHeight() )
552             Resize();
553     }
554 
555 	//------------------------------------------------------------------
556 	void OBrowserListBox::SetHelpLineLimites( sal_Int32 _nMinLines, sal_Int32 _nMaxLines )
557     {
558         m_pHelpWindow->SetLimits( _nMinLines, _nMaxLines );
559     }
560 
561 	//------------------------------------------------------------------
562 	sal_uInt16 OBrowserListBox::CalcVisibleLines()
563 	{
564 		Size aSize(m_aLinesPlayground.GetOutputSizePixel());
565 		sal_uInt16 nResult = 0;
566 		if (0 != m_nRowHeight)
567 			nResult = (sal_uInt16) aSize.Height()/m_nRowHeight;
568 
569 		return nResult;
570 	}
571 
572 	//------------------------------------------------------------------
573 	void OBrowserListBox::UpdateVScroll()
574 	{
575 		sal_uInt16 nLines = CalcVisibleLines();
576 		m_aVScroll.SetPageSize(nLines-1);
577 		m_aVScroll.SetVisibleSize(nLines-1);
578 
579 		size_t nCount = m_aLines.size();
580 		if (nCount>0)
581 		{
582 			m_aVScroll.SetRange(Range(0,nCount-1));
583 			m_nYOffset = -m_aVScroll.GetThumbPos()*m_nRowHeight;
584 		}
585 		else
586 		{
587 			m_aVScroll.SetRange(Range(0,0));
588 			m_nYOffset = 0;
589 		}
590 	}
591 
592 	//------------------------------------------------------------------
593 	void OBrowserListBox::PositionLine( sal_uInt16 _nIndex )
594 	{
595 		Size aSize(m_aLinesPlayground.GetOutputSizePixel());
596 		Point aPos(0, m_nYOffset);
597 
598 		aSize.Height() = m_nRowHeight;
599 
600 		aPos.Y() += _nIndex * m_nRowHeight;
601 
602 		if ( _nIndex < m_aOrderedLines.size() )
603 		{
604     		m_aOrderedLines[ _nIndex ]->second.pLine->SetPosSizePixel( aPos, aSize );
605 
606 			m_aOrderedLines[ _nIndex ]->second.pLine->SetTitleWidth( m_nTheNameSize + 2 * FRAME_OFFSET );
607 
608 			// show the line if necessary
609 			if ( !m_aOrderedLines[ _nIndex ]->second.pLine->IsVisible() )
610 				m_aOrderedLines[ _nIndex ]->second.pLine->Show();
611 		}
612 	}
613 
614 	//------------------------------------------------------------------
615 	void OBrowserListBox::UpdatePosNSize()
616 	{
617         for  (  ::std::set< sal_uInt16 >::const_iterator aLoop = m_aOutOfDateLines.begin();
618                 aLoop != m_aOutOfDateLines.end();
619                 ++aLoop
620              )
621         {
622             DBG_ASSERT( *aLoop < m_aOrderedLines.size(), "OBrowserListBox::UpdatePosNSize: invalid line index!" );
623             if ( *aLoop < m_aOrderedLines.size() )
624 				PositionLine( *aLoop );
625 		}
626         m_aOutOfDateLines.clear();
627 	}
628 
629 	//------------------------------------------------------------------
630 	void OBrowserListBox::UpdatePlayGround()
631 	{
632 		sal_Int32 nThumbPos = m_aVScroll.GetThumbPos();
633 		sal_Int32 nLines = CalcVisibleLines();
634 
635 		sal_uInt16 nEnd = (sal_uInt16)(nThumbPos + nLines);
636 		if (nEnd >= m_aOrderedLines.size())
637 			nEnd = (sal_uInt16)m_aOrderedLines.size()-1;
638 
639 		if ( !m_aOrderedLines.empty() )
640 		{
641 			for ( sal_uInt16 i = (sal_uInt16)nThumbPos; i <= nEnd; ++i )
642                 m_aOutOfDateLines.insert( i );
643 			UpdatePosNSize();
644 		}
645 	}
646 
647 	//------------------------------------------------------------------
648 	void OBrowserListBox::UpdateAll()
649 	{
650 		Resize();
651 	}
652 
653 	//------------------------------------------------------------------
654 	void OBrowserListBox::DisableUpdate()
655 	{
656 		m_bUpdate = sal_False;
657 	}
658 
659 	//------------------------------------------------------------------
660 	void OBrowserListBox::EnableUpdate()
661 	{
662 		m_bUpdate = sal_True;
663 		UpdateAll();
664 	}
665 
666 	//------------------------------------------------------------------
667 	void OBrowserListBox::SetPropertyValue(const ::rtl::OUString& _rEntryName, const Any& _rValue, bool _bUnknownValue )
668 	{
669         ListBoxLines::iterator line = m_aLines.find( _rEntryName );
670         if ( line != m_aLines.end() )
671         {
672             if ( _bUnknownValue )
673             {
674                 Reference< XPropertyControl > xControl( line->second.pLine->getControl() );
675                 OSL_ENSURE( xControl.is(), "OBrowserListBox::SetPropertyValue: illegal control!" );
676                 if ( xControl.is() )
677 		            xControl->setValue( Any() );
678             }
679             else
680                 impl_setControlAsPropertyValue( line->second, _rValue );
681         }
682 	}
683 
684 	//------------------------------------------------------------------------
685 	sal_uInt16 OBrowserListBox::GetPropertyPos( const ::rtl::OUString& _rEntryName ) const
686 	{
687 		sal_uInt16 nRet = LISTBOX_ENTRY_NOTFOUND;
688         for ( OrderedListBoxLines::const_iterator linePos = m_aOrderedLines.begin();
689               linePos != m_aOrderedLines.end();
690               ++linePos
691             )
692         {
693             if ( (*linePos)->first == _rEntryName )
694             {
695                 nRet = (sal_uInt16)( linePos - m_aOrderedLines.begin() );
696                 break;
697             }
698         }
699 
700         return nRet;
701 	}
702 
703 	//------------------------------------------------------------------------
704     bool OBrowserListBox::impl_getBrowserLineForName( const ::rtl::OUString& _rEntryName, BrowserLinePointer& _out_rpLine ) const
705     {
706         ListBoxLines::const_iterator line = m_aLines.find( _rEntryName );
707         if ( line != m_aLines.end() )
708             _out_rpLine = line->second.pLine;
709         else
710             _out_rpLine.reset();
711         return ( NULL != _out_rpLine.get() );
712     }
713 
714 	//------------------------------------------------------------------------
715     void OBrowserListBox::EnablePropertyControls( const ::rtl::OUString& _rEntryName, sal_Int16 _nControls, bool _bEnable )
716     {
717         BrowserLinePointer pLine;
718         if ( impl_getBrowserLineForName( _rEntryName, pLine ) )
719             pLine->EnablePropertyControls( _nControls, _bEnable );
720     }
721 
722     //------------------------------------------------------------------------
723     void OBrowserListBox::EnablePropertyLine( const ::rtl::OUString& _rEntryName, bool _bEnable )
724     {
725         BrowserLinePointer pLine;
726         if ( impl_getBrowserLineForName( _rEntryName, pLine ) )
727             pLine->EnablePropertyLine( _bEnable );
728     }
729 
730 	//------------------------------------------------------------------------
731 	Reference< XPropertyControl > OBrowserListBox::GetPropertyControl( const ::rtl::OUString& _rEntryName )
732 	{
733         BrowserLinePointer pLine;
734         if ( impl_getBrowserLineForName( _rEntryName, pLine ) )
735             return pLine->getControl();
736         return NULL;
737 	}
738 
739 	//------------------------------------------------------------------
740 	sal_uInt16 OBrowserListBox::InsertEntry(const OLineDescriptor& _rPropertyData, sal_uInt16 _nPos)
741 	{
742 		// create a new line
743 		BrowserLinePointer pBrowserLine( new OBrowserLine( _rPropertyData.sName, &m_aLinesPlayground ) );
744 
745         ListBoxLine aNewLine( pBrowserLine, _rPropertyData.xPropertyHandler );
746         ::std::pair< ListBoxLines::iterator, bool > insertPoint =
747             m_aLines.insert( ListBoxLines::value_type( _rPropertyData.sName, aNewLine ) );
748         OSL_ENSURE( insertPoint.second, "OBrowserListBox::InsertEntry: already have another line for this name!" );
749 
750 		sal_uInt16 nInsertPos = _nPos;
751         if ( nInsertPos > m_aOrderedLines.size() )
752             nInsertPos = EDITOR_LIST_APPEND;
753 		if ( EDITOR_LIST_APPEND == nInsertPos )
754 		{
755 			nInsertPos = (sal_uInt16)m_aOrderedLines.size();
756 			m_aOrderedLines.push_back( insertPoint.first );
757 		}
758 		else
759 			m_aOrderedLines.insert( m_aOrderedLines.begin() + nInsertPos, insertPoint.first );
760 
761 		pBrowserLine->SetTitleWidth(m_nTheNameSize);
762 		if (m_bUpdate)
763 		{
764 			UpdateVScroll();
765 			Invalidate();
766 		}
767 
768 		// initialize the entry
769 		ChangeEntry(_rPropertyData, nInsertPos);
770 
771         // update the positions of possibly affected lines
772         sal_uInt16 nUpdatePos = nInsertPos;
773         while ( nUpdatePos < m_aOrderedLines.size() )
774             m_aOutOfDateLines.insert( nUpdatePos++ );
775         UpdatePosNSize( );
776 
777         return nInsertPos;
778 	}
779 
780 	//------------------------------------------------------------------
781 	sal_Int32 OBrowserListBox::GetMinimumWidth()
782 	{
783 		return m_nTheNameSize + 2 * FRAME_OFFSET + (m_nRowHeight - 4) * 8;
784 	}
785 
786 	//------------------------------------------------------------------
787 	sal_Int32 OBrowserListBox::GetMinimumHeight()
788 	{
789         // assume that we want to display 5 rows, at least
790         sal_Int32 nMinHeight = m_nRowHeight * 5;
791 
792         if ( HasHelpSection() )
793         {
794             Size aHelpWindowDistance( LogicToPixel( Size( 0, LAYOUT_HELP_WINDOW_DISTANCE_APPFONT ), MAP_APPFONT ) );
795             nMinHeight += aHelpWindowDistance.Height();
796 
797             nMinHeight += m_pHelpWindow->GetMinimalHeightPixel();
798         }
799 
800         return nMinHeight;
801 	}
802 
803 	//------------------------------------------------------------------
804 	void OBrowserListBox::ShowEntry(sal_uInt16 _nPos)
805 	{
806 		if ( _nPos < m_aOrderedLines.size() )
807 		{
808 			sal_Int32 nThumbPos = m_aVScroll.GetThumbPos();
809 
810 			if (_nPos < nThumbPos)
811 				MoveThumbTo(_nPos);
812 			else
813 			{
814 				sal_Int32 nLines = CalcVisibleLines();
815 				if (_nPos >= nThumbPos + nLines)
816 					MoveThumbTo(_nPos - nLines + 1);
817 			}
818 		}
819 
820 	}
821 
822 	//------------------------------------------------------------------
823 	void OBrowserListBox::MoveThumbTo(sal_Int32 _nNewThumbPos)
824 	{
825 		// disable painting to prevent flicker
826 		m_aLinesPlayground.EnablePaint(sal_False);
827 
828 		sal_Int32 nDelta = _nNewThumbPos - m_aVScroll.GetThumbPos();
829 		// adjust the scrollbar
830 		m_aVScroll.SetThumbPos(_nNewThumbPos);
831 		sal_Int32 nThumbPos = _nNewThumbPos;
832 
833 		m_nYOffset = -m_aVScroll.GetThumbPos() * m_nRowHeight;
834 
835 		sal_Int32 nLines = CalcVisibleLines();
836 		sal_uInt16 nEnd = (sal_uInt16)(nThumbPos + nLines);
837 
838 		m_aLinesPlayground.Scroll(0, -nDelta * m_nRowHeight, SCROLL_CHILDREN);
839 
840 		if (1 == nDelta)
841 		{
842 			// TODO: what's the sense of this two PositionLines? Why not just one call?
843 			PositionLine(nEnd-1);
844 			PositionLine(nEnd);
845 		}
846 		else if (-1 == nDelta)
847 		{
848 			PositionLine((sal_uInt16)nThumbPos);
849 		}
850 		else if (0 != nDelta)
851 		{
852 			UpdatePlayGround();
853 		}
854 
855 		m_aLinesPlayground.EnablePaint(sal_True);
856 		m_aLinesPlayground.Invalidate(INVALIDATE_CHILDREN);
857 	}
858 
859 	//------------------------------------------------------------------
860 	IMPL_LINK(OBrowserListBox, ScrollHdl, ScrollBar*, _pScrollBar )
861 	{
862 		DBG_ASSERT(_pScrollBar == &m_aVScroll, "OBrowserListBox::ScrollHdl: where does this come from?");
863         (void)_pScrollBar;
864 
865 		// disable painting to prevent flicker
866 		m_aLinesPlayground.EnablePaint(sal_False);
867 
868 		sal_Int32 nThumbPos = m_aVScroll.GetThumbPos();
869 
870 		sal_Int32 nDelta = m_aVScroll.GetDelta();
871 		m_nYOffset = -nThumbPos * m_nRowHeight;
872 
873 		sal_uInt16 nEnd = (sal_uInt16)(nThumbPos + CalcVisibleLines());
874 
875 		m_aLinesPlayground.Scroll(0, -nDelta * m_nRowHeight, SCROLL_CHILDREN);
876 
877 		if (1 == nDelta)
878 		{
879 			PositionLine(nEnd-1);
880 			PositionLine(nEnd);
881 		}
882 		else if (nDelta==-1)
883 		{
884 			PositionLine((sal_uInt16)nThumbPos);
885 		}
886 		else if (nDelta!=0 || m_aVScroll.GetType() == SCROLL_DONTKNOW)
887 		{
888 			UpdatePlayGround();
889 		}
890 
891 		m_aLinesPlayground.EnablePaint(sal_True);
892 		return 0;
893 	}
894 
895 	//------------------------------------------------------------------
896 	void OBrowserListBox::buttonClicked( OBrowserLine* _pLine, sal_Bool _bPrimary )
897     {
898         DBG_ASSERT( _pLine, "OBrowserListBox::buttonClicked: invalid browser line!" );
899 		if ( _pLine && m_pLineListener )
900 		{
901 			m_pLineListener->Clicked( _pLine->GetEntryName(), _bPrimary );
902 		}
903     }
904 
905 	//------------------------------------------------------------------
906     void OBrowserListBox::impl_setControlAsPropertyValue( const ListBoxLine& _rLine, const Any& _rPropertyValue )
907     {
908         Reference< XPropertyControl > xControl( _rLine.pLine->getControl() );
909         try
910         {
911             if ( _rPropertyValue.getValueType().equals( _rLine.pLine->getControl()->getValueType() ) )
912             {
913 		        xControl->setValue( _rPropertyValue );
914             }
915             else
916             {
917     #ifdef DBG_UTIL
918                 if ( !_rLine.xHandler.is() )
919                 {
920                     ::rtl::OString sMessage( "OBrowserListBox::impl_setControlAsPropertyValue: no handler -> no conversion (property: '" );
921                     ::rtl::OUString sPropertyName( _rLine.pLine->GetEntryName() );
922                     sMessage += ::rtl::OString( sPropertyName.getStr(), sPropertyName.getLength(), RTL_TEXTENCODING_ASCII_US );
923                     sMessage += ::rtl::OString( "')!" );
924                     DBG_ERROR( sMessage );
925                 }
926     #endif
927                 if ( _rLine.xHandler.is() )
928                 {
929                     Any aControlValue = _rLine.xHandler->convertToControlValue(
930                         _rLine.pLine->GetEntryName(), _rPropertyValue, xControl->getValueType() );
931                     xControl->setValue( aControlValue );
932                 }
933             }
934         }
935         catch( const Exception& )
936         {
937             DBG_UNHANDLED_EXCEPTION();
938         }
939     }
940 
941 	//------------------------------------------------------------------
942     Any OBrowserListBox::impl_getControlAsPropertyValue( const ListBoxLine& _rLine ) const
943     {
944         Reference< XPropertyControl > xControl( _rLine.pLine->getControl() );
945         Any aPropertyValue;
946         try
947         {
948         #ifdef DBG_UTIL
949             if ( !_rLine.xHandler.is() )
950             {
951                 ::rtl::OString sMessage( "OBrowserListBox::impl_getControlAsPropertyValue: no handler -> no conversion (property: '" );
952                 ::rtl::OUString sPropertyName( _rLine.pLine->GetEntryName() );
953                 sMessage += ::rtl::OString( sPropertyName.getStr(), sPropertyName.getLength(), RTL_TEXTENCODING_ASCII_US );
954                 sMessage += ::rtl::OString( "')!" );
955                 DBG_ERROR( sMessage );
956             }
957         #endif
958             if ( _rLine.xHandler.is() )
959                 aPropertyValue = _rLine.xHandler->convertToPropertyValue( _rLine.pLine->GetEntryName(), xControl->getValue() );
960             else
961                 aPropertyValue = xControl->getValue();
962         }
963         catch( const Exception& )
964         {
965             DBG_UNHANDLED_EXCEPTION();
966         }
967         return aPropertyValue;
968     }
969 
970     //------------------------------------------------------------------
971     sal_uInt16 OBrowserListBox::impl_getControlPos( const Reference< XPropertyControl >& _rxControl ) const
972     {
973         for (   OrderedListBoxLines::const_iterator search = m_aOrderedLines.begin();
974                 search != m_aOrderedLines.end();
975                 ++search
976             )
977             if ( (*search)->second.pLine->getControl().get() == _rxControl.get() )
978                 return sal_uInt16( search - m_aOrderedLines.begin() );
979         DBG_ERROR( "OBrowserListBox::impl_getControlPos: invalid control - not part of any of our lines!" );
980         return (sal_uInt16)-1;
981     }
982 
983     //--------------------------------------------------------------------
984     void SAL_CALL OBrowserListBox::focusGained( const Reference< XPropertyControl >& _rxControl ) throw (RuntimeException)
985     {
986         DBG_TESTSOLARMUTEX();
987 
988         DBG_ASSERT( _rxControl.is(), "OBrowserListBox::focusGained: invalid event source!" );
989 		if ( !_rxControl.is() )
990 			return;
991 
992         if ( m_pControlObserver )
993             m_pControlObserver->focusGained( _rxControl );
994 
995         m_xActiveControl = _rxControl;
996 		ShowEntry( impl_getControlPos( m_xActiveControl ) );
997     }
998 
999     //--------------------------------------------------------------------
1000     void SAL_CALL OBrowserListBox::valueChanged( const Reference< XPropertyControl >& _rxControl ) throw (RuntimeException)
1001     {
1002         DBG_TESTSOLARMUTEX();
1003 
1004 		DBG_ASSERT( _rxControl.is(), "OBrowserListBox::valueChanged: invalid event source!" );
1005 		if ( !_rxControl.is() )
1006 			return;
1007 
1008         if ( m_pControlObserver )
1009             m_pControlObserver->valueChanged( _rxControl );
1010 
1011 		if ( m_pLineListener )
1012 		{
1013             const ListBoxLine& rLine = impl_getControlLine( _rxControl );
1014 			m_pLineListener->Commit(
1015 				rLine.pLine->GetEntryName(),
1016 				impl_getControlAsPropertyValue( rLine )
1017 			);
1018 		}
1019     }
1020 
1021     //--------------------------------------------------------------------
1022     void SAL_CALL OBrowserListBox::activateNextControl( const Reference< XPropertyControl >& _rxCurrentControl ) throw (RuntimeException)
1023     {
1024         DBG_TESTSOLARMUTEX();
1025 
1026 		sal_uInt16 nLine = impl_getControlPos( _rxCurrentControl );
1027 
1028 		// cycle forwards, 'til we've the next control which can grab the focus
1029 		++nLine;
1030 		while ( (size_t)nLine < m_aOrderedLines.size() )
1031 		{
1032 			if ( m_aOrderedLines[nLine]->second.pLine->GrabFocus() )
1033 				break;
1034 			++nLine;
1035 		}
1036 
1037 		if	(	( (size_t)nLine >= m_aOrderedLines.size() )
1038 			&&	( m_aOrderedLines.size() > 0 )
1039 			)
1040 			// wrap around
1041 			m_aOrderedLines[0]->second.pLine->GrabFocus();
1042     }
1043 
1044 	//------------------------------------------------------------------
1045     namespace
1046     {
1047 	    //..............................................................
1048         void lcl_implDisposeControl_nothrow( const Reference< XPropertyControl >& _rxControl )
1049         {
1050             if ( !_rxControl.is() )
1051                 return;
1052             try
1053             {
1054 			    _rxControl->setControlContext( NULL );
1055                 Reference< XComponent > xControlComponent( _rxControl, UNO_QUERY );
1056                 if ( xControlComponent.is() )
1057                     xControlComponent->dispose();
1058             }
1059             catch( const Exception& )
1060             {
1061                 DBG_UNHANDLED_EXCEPTION();
1062             }
1063         }
1064     }
1065 
1066 	//------------------------------------------------------------------
1067 	void OBrowserListBox::Clear()
1068 	{
1069         for (	ListBoxLines::iterator loop = m_aLines.begin();
1070 				loop != m_aLines.end();
1071 				++loop
1072 			)
1073 		{
1074 			// hide the line
1075 			loop->second.pLine->Hide();
1076 			// reset the listener
1077             lcl_implDisposeControl_nothrow( loop->second.pLine->getControl() );
1078 		}
1079 
1080         clearContainer( m_aLines );
1081         clearContainer( m_aOrderedLines );
1082 	}
1083 
1084 	//------------------------------------------------------------------
1085 	sal_Bool OBrowserListBox::RemoveEntry( const ::rtl::OUString& _rName )
1086     {
1087         sal_uInt16 nPos = GetPropertyPos( _rName );
1088         if ( nPos == LISTBOX_ENTRY_NOTFOUND )
1089             return sal_False;
1090 
1091         OrderedListBoxLines::iterator orderedPos = m_aOrderedLines.begin() + nPos;
1092         BrowserLinePointer pLine = (*orderedPos)->second.pLine;
1093         pLine->Hide();
1094         lcl_implDisposeControl_nothrow( pLine->getControl() );
1095 
1096         m_aLines.erase( *orderedPos );
1097         m_aOrderedLines.erase( orderedPos );
1098         m_aOutOfDateLines.erase( (sal_uInt16)m_aOrderedLines.size() );
1099             // this index *may* have been out of date, which is obsoleted now by m_aOrderedLines shrinking
1100 
1101         // update the positions of possibly affected lines
1102         while ( nPos < m_aOrderedLines.size() )
1103             m_aOutOfDateLines.insert( nPos++ );
1104         UpdatePosNSize( );
1105 
1106         return sal_True;
1107     }
1108 
1109 	//------------------------------------------------------------------
1110 	void OBrowserListBox::ChangeEntry( const OLineDescriptor& _rPropertyData, sal_uInt16 nPos )
1111 	{
1112         OSL_PRECOND( _rPropertyData.Control.is(), "OBrowserListBox::ChangeEntry: invalid control!" );
1113         if ( !_rPropertyData.Control.is() )
1114             return;
1115 
1116         if ( nPos == EDITOR_LIST_REPLACE_EXISTING )
1117             nPos = GetPropertyPos( _rPropertyData.sName );
1118 
1119 		if ( nPos < m_aOrderedLines.size() )
1120 		{
1121 			Window*	pRefWindow = NULL;
1122 			if ( nPos > 0 )
1123 				pRefWindow = m_aOrderedLines[nPos-1]->second.pLine->GetRefWindow();
1124 
1125 			// the current line and control
1126             ListBoxLine& rLine = m_aOrderedLines[nPos]->second;
1127 
1128             // the old control and some data about it
1129             Reference< XPropertyControl > xControl = rLine.pLine->getControl();
1130             Window* pControlWindow = rLine.pLine->getControlWindow();
1131 			Point aControlPos;
1132 			if ( pControlWindow )
1133 				aControlPos = pControlWindow->GetPosPixel();
1134 
1135             // clean up the old control
1136             lcl_implDisposeControl_nothrow( xControl );
1137 
1138             // set the new control at the line
1139 			rLine.pLine->setControl( _rPropertyData.Control );
1140             xControl = rLine.pLine->getControl();
1141 
1142             if ( xControl.is() )
1143 				xControl->setControlContext( m_pControlContextImpl.get() );
1144 
1145 			// the initial property value
1146             if ( _rPropertyData.bUnknownValue )
1147 		        xControl->setValue( Any() );
1148             else
1149                 impl_setControlAsPropertyValue( rLine, _rPropertyData.aValue );
1150 
1151 			rLine.pLine->SetTitle(_rPropertyData.DisplayName);
1152             rLine.xHandler = _rPropertyData.xPropertyHandler;
1153 
1154 			sal_uInt16 nTextWidth = (sal_uInt16)m_aLinesPlayground.GetTextWidth(_rPropertyData.DisplayName);
1155 			if (m_nTheNameSize< nTextWidth)
1156 				m_nTheNameSize = nTextWidth;
1157 
1158 			if ( _rPropertyData.HasPrimaryButton )
1159 			{
1160                 if ( _rPropertyData.PrimaryButtonImageURL.getLength() )
1161 				    rLine.pLine->ShowBrowseButton( _rPropertyData.PrimaryButtonImageURL, true );
1162                 else if ( _rPropertyData.PrimaryButtonImage.is() )
1163 				    rLine.pLine->ShowBrowseButton( Image( _rPropertyData.PrimaryButtonImage ), true );
1164                 else
1165                     rLine.pLine->ShowBrowseButton( true );
1166 
1167                 if ( _rPropertyData.HasSecondaryButton )
1168                 {
1169                     if ( _rPropertyData.SecondaryButtonImageURL.getLength() )
1170 				        rLine.pLine->ShowBrowseButton( _rPropertyData.SecondaryButtonImageURL, false );
1171                     else if ( _rPropertyData.SecondaryButtonImage.is() )
1172 				        rLine.pLine->ShowBrowseButton( Image( _rPropertyData.SecondaryButtonImage ), false );
1173                     else
1174                         rLine.pLine->ShowBrowseButton( false );
1175                 }
1176                 else
1177     				rLine.pLine->HideBrowseButton( false );
1178 
1179                 rLine.pLine->SetClickListener( this );
1180 			}
1181 			else
1182             {
1183 				rLine.pLine->HideBrowseButton( true );
1184     			rLine.pLine->HideBrowseButton( false );
1185             }
1186 
1187             DBG_ASSERT( ( _rPropertyData.IndentLevel == 0 ) || ( _rPropertyData.IndentLevel == 1 ),
1188                 "OBrowserListBox::ChangeEntry: unsupported indent level!" );
1189 			rLine.pLine->IndentTitle( _rPropertyData.IndentLevel > 0 );
1190 
1191 			if ( nPos > 0 )
1192 				rLine.pLine->SetTabOrder( pRefWindow, WINDOW_ZORDER_BEHIND );
1193 			else
1194 				rLine.pLine->SetTabOrder( pRefWindow, WINDOW_ZORDER_FIRST );
1195 
1196             m_aOutOfDateLines.insert( nPos );
1197 			rLine.pLine->SetComponentHelpIds(
1198                 HelpIdUrl::getHelpId( _rPropertyData.HelpURL ),
1199                 rtl::OUStringToOString( _rPropertyData.PrimaryButtonId, RTL_TEXTENCODING_UTF8 ),
1200                 rtl::OUStringToOString( _rPropertyData.SecondaryButtonId, RTL_TEXTENCODING_UTF8 )
1201             );
1202 
1203             if ( _rPropertyData.bReadOnly )
1204             {
1205                 rLine.pLine->SetReadOnly( true );
1206 
1207                 // user controls (i.e. the ones not provided by the usual
1208                 // XPropertyControlFactory) have no chance to know that they should be read-only,
1209                 // since XPropertyHandler::describePropertyLine does not transport this
1210                 // information.
1211                 // So, we manually switch this to read-only.
1212                 if ( xControl.is() && ( xControl->getControlType() == PropertyControlType::Unknown ) )
1213                 {
1214                     Edit* pControlWindowAsEdit = dynamic_cast< Edit* >( rLine.pLine->getControlWindow() );
1215                     if ( pControlWindowAsEdit )
1216                         pControlWindowAsEdit->SetReadOnly( sal_True );
1217                     else
1218                         pControlWindowAsEdit->Enable( sal_False );
1219                 }
1220             }
1221 		}
1222 	}
1223 
1224 	//------------------------------------------------------------------
1225 	long OBrowserListBox::PreNotify( NotifyEvent& _rNEvt )
1226     {
1227         switch ( _rNEvt.GetType() )
1228         {
1229         case EVENT_KEYINPUT:
1230         {
1231             const KeyEvent* pKeyEvent = _rNEvt.GetKeyEvent();
1232             if  (   ( pKeyEvent->GetKeyCode().GetModifier() != 0 )
1233                 ||  (   ( pKeyEvent->GetKeyCode().GetCode() != KEY_PAGEUP )
1234                     &&  ( pKeyEvent->GetKeyCode().GetCode() != KEY_PAGEDOWN )
1235                     )
1236                 )
1237                 break;
1238 
1239             long nScrollOffset = 0;
1240             if ( m_aVScroll.IsVisible() )
1241             {
1242                 if ( pKeyEvent->GetKeyCode().GetCode() == KEY_PAGEUP )
1243                     nScrollOffset = -m_aVScroll.GetPageSize();
1244                 else if ( pKeyEvent->GetKeyCode().GetCode() == KEY_PAGEDOWN )
1245                     nScrollOffset = m_aVScroll.GetPageSize();
1246             }
1247 
1248             if ( nScrollOffset )
1249             {
1250                 long nNewThumbPos = m_aVScroll.GetThumbPos() + nScrollOffset;
1251                 nNewThumbPos = ::std::max( nNewThumbPos, m_aVScroll.GetRangeMin() );
1252                 nNewThumbPos = ::std::min( nNewThumbPos, m_aVScroll.GetRangeMax() );
1253                 m_aVScroll.DoScroll( nNewThumbPos );
1254                 nNewThumbPos = m_aVScroll.GetThumbPos();
1255 
1256                 sal_uInt16 nFocusControlPos = 0;
1257                 sal_uInt16 nActiveControlPos = impl_getControlPos( m_xActiveControl );
1258                 if ( nActiveControlPos < nNewThumbPos )
1259                     nFocusControlPos = (sal_uInt16)nNewThumbPos;
1260                 else if ( nActiveControlPos >= nNewThumbPos + CalcVisibleLines() )
1261                     nFocusControlPos = (sal_uInt16)nNewThumbPos + CalcVisibleLines() - 1;
1262                 if ( nFocusControlPos )
1263                 {
1264                     if ( nFocusControlPos < m_aOrderedLines.size() )
1265                     {
1266 			            m_aOrderedLines[ nFocusControlPos ]->second.pLine->GrabFocus();
1267                     }
1268                     else
1269                         OSL_ENSURE( false, "OBrowserListBox::PreNotify: internal error, invalid focus control position!" );
1270                 }
1271             }
1272 
1273             return 1L;
1274             // handled this. In particular, we also consume PageUp/Down events if we do not use them for scrolling,
1275             // otherwise they would be used to scroll the document view, which does not sound like it is desired by
1276             // the user.
1277         }
1278         }
1279         return Control::PreNotify( _rNEvt );
1280     }
1281 
1282 	//------------------------------------------------------------------
1283 	long OBrowserListBox::Notify( NotifyEvent& _rNEvt )
1284 	{
1285         switch ( _rNEvt.GetType() )
1286         {
1287         case EVENT_COMMAND:
1288         {
1289             const CommandEvent* pCommand = _rNEvt.GetCommandEvent();
1290             if	(   ( COMMAND_WHEEL == pCommand->GetCommand() )
1291                 ||	( COMMAND_STARTAUTOSCROLL == pCommand->GetCommand() )
1292                 ||	( COMMAND_AUTOSCROLL == pCommand->GetCommand() )
1293                 )
1294             {
1295                 // interested in scroll events if we have a scrollbar
1296 		        if ( m_aVScroll.IsVisible() )
1297 		        {
1298 					HandleScrollCommand( *pCommand, NULL, &m_aVScroll );
1299                 }
1300 		    }
1301 		}
1302         break;
1303         }
1304 
1305 		return Control::Notify( _rNEvt );
1306 	}
1307 
1308 //............................................................................
1309 } // namespace pcr
1310 //............................................................................
1311 
1312 
1313