xref: /AOO41X/main/extensions/source/propctrlr/browserlistbox.cxx (revision 2a97ec55f1442d65917e8c8b82a55ab76c9ff676)
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 recieved 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