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