1 /*************************************************************************
2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3  *
4  * Copyright 2000, 2010 Oracle and/or its affiliates.
5  *
6  * OpenOffice.org - a multi-platform office productivity suite
7  *
8  * This file is part of OpenOffice.org.
9  *
10  * OpenOffice.org is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU Lesser General Public License version 3
12  * only, as published by the Free Software Foundation.
13  *
14  * OpenOffice.org is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU Lesser General Public License version 3 for more details
18  * (a copy is included in the LICENSE file that accompanied this code).
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * version 3 along with OpenOffice.org.  If not, see
22  * <http://www.openoffice.org/license.html>
23  * for a copy of the LGPLv3 License.
24  *
25 ************************************************************************/
26 
27 #include "precompiled_svtools.hxx"
28 
29 #include "dummypanel.hxx"
30 #include "toolpanelcollection.hxx"
31 #include "paneldecklisteners.hxx"
32 #include "toolpaneldeckpeer.hxx"
33 #include "svtools/toolpanel/toolpaneldeck.hxx"
34 #include "svtools/toolpanel/tablayouter.hxx"
35 #include "svtools/toolpanel/drawerlayouter.hxx"
36 
37 /** === begin UNO includes === **/
38 #include <com/sun/star/accessibility/XAccessible.hpp>
39 #include <com/sun/star/accessibility/AccessibleRole.hpp>
40 /** === end UNO includes === **/
41 
42 #include <tools/diagnose_ex.h>
43 
44 #include <boost/optional.hpp>
45 
46 //........................................................................
47 namespace svt
48 {
49 //........................................................................
50 
51     /** === begin UNO using === **/
52     using ::com::sun::star::uno::Reference;
53     using ::com::sun::star::accessibility::XAccessible;
54     using ::com::sun::star::awt::XWindowPeer;
55     using ::com::sun::star::uno::UNO_SET_THROW;
56     /** === end UNO using === **/
57     namespace AccessibleRole = ::com::sun::star::accessibility::AccessibleRole;
58 
59     enum DeckAction
60     {
61         /// activates the first panel
62         ACTION_ACTIVATE_FIRST,
63         // activates the panel after the currently active panel
64         ACTION_ACTIVATE_NEXT,
65         // activates the panel before the currently active panel
66         ACTION_ACTIVATE_PREV,
67         // activates the last panel
68         ACTION_ACTIVATE_LAST,
69 
70         // toggles the focus between the active panel and the panel selector
71         ACTION_TOGGLE_FOCUS,
72     };
73 
74 	//====================================================================
75 	//= ToolPanelDeck_Impl
76 	//====================================================================
77     class ToolPanelDeck_Impl : public IToolPanelDeckListener
78     {
79     public:
80         ToolPanelDeck_Impl( ToolPanelDeck& i_rDeck )
81             :m_rDeck( i_rDeck )
82             ,m_aPanelAnchor( &i_rDeck, WB_DIALOGCONTROL | WB_CHILDDLGCTRL )
83             ,m_aPanels()
84             ,m_pDummyPanel( new DummyPanel )
85             ,m_pLayouter()
86             ,m_bInDtor( false )
87             ,m_pAccessibleParent( NULL )
88         {
89             m_aPanels.AddListener( *this );
90             m_aPanelAnchor.Show();
91             m_aPanelAnchor.SetAccessibleRole( AccessibleRole::PANEL );
92         }
93 
94         ~ToolPanelDeck_Impl()
95         {
96             m_bInDtor = true;
97         }
98 
99         PDeckLayouter       GetLayouter() const { return m_pLayouter; }
100         void                SetLayouter( const PDeckLayouter& i_pNewLayouter );
101 
102         Window&             GetPanelWindowAnchor()       { return m_aPanelAnchor; }
103         const Window&       GetPanelWindowAnchor() const { return m_aPanelAnchor; }
104 
105         bool                IsDead() const { return m_bInDtor; }
106 
107         /// notifies our listeners that we're going to die. Only to be called from with our anti-impl's destructor
108         void                NotifyDying()
109         {
110             m_aPanels.RemoveListener( *this );
111             m_aListeners.Dying();
112         }
113 
114         // IToolPanelDeck equivalents
115         size_t              GetPanelCount() const;
116         PToolPanel          GetPanel( const size_t i_nPos ) const;
117         ::boost::optional< size_t >
118                             GetActivePanel() const;
119         void                ActivatePanel( const ::boost::optional< size_t >& i_rPanel );
120         size_t              InsertPanel( const PToolPanel& i_pPanel, const size_t i_nPosition );
121         PToolPanel          RemovePanel( const size_t i_nPosition );
122         void                AddListener( IToolPanelDeckListener& i_rListener );
123         void                RemoveListener( IToolPanelDeckListener& i_rListener );
124 
125         /// re-layouts everything
126         void                LayoutAll() { ImplDoLayout(); }
127 
128         void                DoAction( const DeckAction i_eAction );
129 
130         bool                FocusActivePanel();
131 
132         void                SetAccessibleParentWindow( Window* i_pAccessibleParent );
133         Window*             GetAccessibleParentWindow() const { return m_pAccessibleParent; }
134 
135     protected:
136         // IToolPanelDeckListener
137         virtual void        PanelInserted( const PToolPanel& i_pPanel, const size_t i_nPosition );
138         virtual void        PanelRemoved( const size_t i_nPosition );
139         virtual void        ActivePanelChanged( const ::boost::optional< size_t >& i_rOldActive, const ::boost::optional< size_t >& i_rNewActive );
140         virtual void        LayouterChanged( const PDeckLayouter& i_rNewLayouter );
141         virtual void        Dying();
142 
143     private:
144         void                ImplDoLayout();
145         PToolPanel          GetActiveOrDummyPanel_Impl();
146 
147     private:
148         ToolPanelDeck&      m_rDeck;
149         Window              m_aPanelAnchor;
150         ToolPanelCollection m_aPanels;
151         PToolPanel          m_pDummyPanel;
152         PanelDeckListeners  m_aListeners;
153         PDeckLayouter       m_pLayouter;
154         bool                m_bInDtor;
155         Window*             m_pAccessibleParent;
156     };
157 
158 	//--------------------------------------------------------------------
159     PToolPanel ToolPanelDeck_Impl::GetActiveOrDummyPanel_Impl()
160     {
161         ::boost::optional< size_t > aActivePanel( m_aPanels.GetActivePanel() );
162         if ( !aActivePanel )
163             return m_pDummyPanel;
164         return m_aPanels.GetPanel( *aActivePanel );
165     }
166 
167 	//--------------------------------------------------------------------
168     void ToolPanelDeck_Impl::SetLayouter( const PDeckLayouter& i_pNewLayouter )
169     {
170         ENSURE_OR_RETURN_VOID( i_pNewLayouter.get(), "invalid layouter" );
171 
172         if ( m_pLayouter.get() )
173             m_pLayouter->Destroy();
174 
175         m_pLayouter = i_pNewLayouter;
176 
177         ImplDoLayout();
178 
179         m_aListeners.LayouterChanged( m_pLayouter );
180     }
181 
182 	//--------------------------------------------------------------------
183     size_t ToolPanelDeck_Impl::GetPanelCount() const
184     {
185         return m_aPanels.GetPanelCount();
186     }
187 
188 	//--------------------------------------------------------------------
189     PToolPanel ToolPanelDeck_Impl::GetPanel( const size_t i_nPos ) const
190     {
191         return m_aPanels.GetPanel( i_nPos );
192     }
193 
194 	//--------------------------------------------------------------------
195     ::boost::optional< size_t > ToolPanelDeck_Impl::GetActivePanel() const
196     {
197         return m_aPanels.GetActivePanel();
198     }
199 
200 	//--------------------------------------------------------------------
201     void ToolPanelDeck_Impl::ActivatePanel( const ::boost::optional< size_t >& i_rPanel )
202     {
203         m_aPanels.ActivatePanel( i_rPanel );
204     }
205 
206 	//--------------------------------------------------------------------
207     size_t ToolPanelDeck_Impl::InsertPanel( const PToolPanel& i_pPanel, const size_t i_nPosition )
208     {
209         return m_aPanels.InsertPanel( i_pPanel, i_nPosition );
210     }
211 
212 	//--------------------------------------------------------------------
213     PToolPanel ToolPanelDeck_Impl::RemovePanel( const size_t i_nPosition )
214     {
215         return m_aPanels.RemovePanel( i_nPosition );
216     }
217 
218 	//--------------------------------------------------------------------
219     void ToolPanelDeck_Impl::ImplDoLayout()
220     {
221         const Rectangle aDeckPlayground( Point(), m_rDeck.GetOutputSizePixel() );
222 
223         // ask the layouter what is left for our panel, and position the panel container window appropriately
224         Rectangle aPlaygroundArea( aDeckPlayground );
225         OSL_ENSURE( m_pLayouter.get(), "ToolPanelDeck_Impl::ImplDoLayout: no layouter!" );
226         if ( m_pLayouter.get() )
227         {
228             aPlaygroundArea = m_pLayouter->Layout( aDeckPlayground );
229         }
230         m_aPanelAnchor.SetPosSizePixel( aPlaygroundArea.TopLeft(), aPlaygroundArea.GetSize() );
231 
232         // position the active panel
233         const PToolPanel pActive( GetActiveOrDummyPanel_Impl() );
234         pActive->SetSizePixel( m_aPanelAnchor.GetOutputSizePixel() );
235     }
236 
237 	//--------------------------------------------------------------------
238     void ToolPanelDeck_Impl::AddListener( IToolPanelDeckListener& i_rListener )
239     {
240         m_aListeners.AddListener( i_rListener );
241     }
242 
243 	//--------------------------------------------------------------------
244     void ToolPanelDeck_Impl::RemoveListener( IToolPanelDeckListener& i_rListener )
245     {
246         m_aListeners.RemoveListener( i_rListener );
247     }
248 
249 	//--------------------------------------------------------------------
250     void ToolPanelDeck_Impl::DoAction( const DeckAction i_eAction )
251     {
252         const size_t nPanelCount( m_aPanels.GetPanelCount() );
253         ::boost::optional< size_t > aActivatePanel;
254         ::boost::optional< size_t > aCurrentPanel( GetActivePanel() );
255 
256         switch ( i_eAction )
257         {
258         case ACTION_ACTIVATE_FIRST:
259             if ( nPanelCount > 0 )
260                 aActivatePanel = 0;
261             break;
262         case ACTION_ACTIVATE_PREV:
263             if ( !aCurrentPanel && ( nPanelCount > 0 ) )
264                 aActivatePanel = nPanelCount - 1;
265             else
266             if ( !!aCurrentPanel && ( *aCurrentPanel > 0 ) )
267                 aActivatePanel = *aCurrentPanel - 1;
268             break;
269         case ACTION_ACTIVATE_NEXT:
270             if ( !aCurrentPanel && ( nPanelCount > 0 ) )
271                 aActivatePanel = 0;
272             else
273             if ( !!aCurrentPanel && ( *aCurrentPanel < nPanelCount - 1 ) )
274                 aActivatePanel = *aCurrentPanel + 1;
275             break;
276         case ACTION_ACTIVATE_LAST:
277             if ( nPanelCount > 0 )
278                 aActivatePanel = nPanelCount - 1;
279             break;
280         case ACTION_TOGGLE_FOCUS:
281             {
282                 PToolPanel pActivePanel( GetActiveOrDummyPanel_Impl() );
283                 if ( !m_aPanelAnchor.HasChildPathFocus() )
284                     pActivePanel->GrabFocus();
285                 else
286                     GetLayouter()->SetFocusToPanelSelector();
287             }
288             break;
289         }
290 
291         if ( !!aActivatePanel )
292         {
293             ActivatePanel( aActivatePanel );
294         }
295     }
296 
297 	//--------------------------------------------------------------------
298     bool ToolPanelDeck_Impl::FocusActivePanel()
299     {
300         ::boost::optional< size_t > aActivePanel( m_aPanels.GetActivePanel() );
301         if ( !aActivePanel )
302             return false;
303 
304         PToolPanel pActivePanel( m_aPanels.GetPanel( *aActivePanel ) );
305         pActivePanel->GrabFocus();
306         return true;
307     }
308 
309 	//--------------------------------------------------------------------
310     void ToolPanelDeck_Impl::PanelInserted( const PToolPanel& i_pPanel, const size_t i_nPosition )
311     {
312         // multiplex to our own listeners
313         m_aListeners.PanelInserted( i_pPanel, i_nPosition );
314     }
315 
316 	//--------------------------------------------------------------------
317     void ToolPanelDeck_Impl::PanelRemoved( const size_t i_nPosition )
318     {
319         // multiplex to our own listeners
320         m_aListeners.PanelRemoved( i_nPosition );
321     }
322 
323 	//--------------------------------------------------------------------
324     void ToolPanelDeck_Impl::ActivePanelChanged( const ::boost::optional< size_t >& i_rOldActive, const ::boost::optional< size_t >& i_rNewActive )
325     {
326         // hide the old panel
327         if ( !!i_rOldActive )
328         {
329             const PToolPanel pOldActive( m_aPanels.GetPanel( *i_rOldActive ) );
330             pOldActive->Deactivate();
331         }
332 
333         // position and show the new panel
334         const PToolPanel pNewActive( !i_rNewActive ? m_pDummyPanel : m_aPanels.GetPanel( *i_rNewActive ) );
335         pNewActive->Activate( m_aPanelAnchor );
336         pNewActive->GrabFocus();
337 
338         // resize the panel (cannot guarantee it has ever been resized before
339         pNewActive->SetSizePixel( m_aPanelAnchor.GetOutputSizePixel() );
340 
341         // multiplex to our own listeners
342         m_aListeners.ActivePanelChanged( i_rOldActive, i_rNewActive );
343     }
344 
345 	//--------------------------------------------------------------------
346     void ToolPanelDeck_Impl::LayouterChanged( const PDeckLayouter& i_rNewLayouter )
347     {
348         // not interested in
349         (void)i_rNewLayouter;
350     }
351 
352 	//--------------------------------------------------------------------
353     void ToolPanelDeck_Impl::Dying()
354     {
355         // not interested in. Since the ToolPanelCollection is our member, this just means we ourself
356         // are dying, and we already sent this notification in our dtor.
357     }
358 
359 	//--------------------------------------------------------------------
360     void ToolPanelDeck_Impl::SetAccessibleParentWindow( Window* i_pAccessibleParent )
361     {
362         m_pAccessibleParent = i_pAccessibleParent;
363     }
364 
365     //====================================================================
366 	//= ToolPanelDeck
367 	//====================================================================
368 	//--------------------------------------------------------------------
369     ToolPanelDeck::ToolPanelDeck( Window& i_rParent, const WinBits i_nStyle )
370         :Control( &i_rParent, i_nStyle )
371         ,m_pImpl( new ToolPanelDeck_Impl( *this ) )
372     {
373         // use a default layouter
374 //        SetLayouter( PDeckLayouter( new TabDeckLayouter( *this, *this, TABS_RIGHT, TABITEM_IMAGE_AND_TEXT ) ) );
375         SetLayouter( PDeckLayouter( new DrawerDeckLayouter( *this, *this ) ) );
376     }
377 
378 	//--------------------------------------------------------------------
379     ToolPanelDeck::~ToolPanelDeck()
380     {
381         m_pImpl->NotifyDying();
382         GetLayouter()->Destroy();
383 
384         Hide();
385         for ( size_t i=0; i<GetPanelCount(); ++i )
386         {
387             PToolPanel pPanel( GetPanel( i ) );
388             pPanel->Dispose();
389         }
390     }
391 
392 	//--------------------------------------------------------------------
393     size_t ToolPanelDeck::GetPanelCount() const
394     {
395         return m_pImpl->GetPanelCount();
396     }
397 
398 	//--------------------------------------------------------------------
399     PToolPanel ToolPanelDeck::GetPanel( const size_t i_nPos ) const
400     {
401         return m_pImpl->GetPanel( i_nPos );
402     }
403 
404 	//--------------------------------------------------------------------
405     ::boost::optional< size_t > ToolPanelDeck::GetActivePanel() const
406     {
407         return m_pImpl->GetActivePanel();
408     }
409 
410 	//--------------------------------------------------------------------
411     void ToolPanelDeck::ActivatePanel( const ::boost::optional< size_t >& i_rPanel )
412     {
413         m_pImpl->ActivatePanel( i_rPanel );
414     }
415 
416 	//--------------------------------------------------------------------
417     size_t ToolPanelDeck::InsertPanel( const PToolPanel& i_pPanel, const size_t i_nPosition )
418     {
419         return m_pImpl->InsertPanel( i_pPanel, i_nPosition );
420     }
421 
422 	//--------------------------------------------------------------------
423     PToolPanel ToolPanelDeck::RemovePanel( const size_t i_nPosition )
424     {
425         return m_pImpl->RemovePanel( i_nPosition );
426     }
427 
428 	//--------------------------------------------------------------------
429     PDeckLayouter ToolPanelDeck::GetLayouter() const
430     {
431         return m_pImpl->GetLayouter();
432     }
433 
434 	//--------------------------------------------------------------------
435     void ToolPanelDeck::SetLayouter( const PDeckLayouter& i_pNewLayouter )
436     {
437         return m_pImpl->SetLayouter( i_pNewLayouter );
438     }
439 
440 	//--------------------------------------------------------------------
441     void ToolPanelDeck::AddListener( IToolPanelDeckListener& i_rListener )
442     {
443         m_pImpl->AddListener( i_rListener );
444     }
445 
446 	//--------------------------------------------------------------------
447     void ToolPanelDeck::RemoveListener( IToolPanelDeckListener& i_rListener )
448     {
449         m_pImpl->RemoveListener( i_rListener );
450     }
451 
452 	//--------------------------------------------------------------------
453     Window& ToolPanelDeck::GetPanelWindowAnchor()
454     {
455         return m_pImpl->GetPanelWindowAnchor();
456     }
457 
458 	//--------------------------------------------------------------------
459     const Window& ToolPanelDeck::GetPanelWindowAnchor() const
460     {
461         return m_pImpl->GetPanelWindowAnchor();
462     }
463 
464 	//--------------------------------------------------------------------
465     void ToolPanelDeck::Resize()
466     {
467         Control::Resize();
468         m_pImpl->LayoutAll();
469     }
470 
471 	//--------------------------------------------------------------------
472     long ToolPanelDeck::Notify( NotifyEvent& i_rNotifyEvent )
473     {
474         bool bHandled = false;
475         if ( i_rNotifyEvent.GetType() == EVENT_KEYINPUT )
476         {
477             const KeyEvent* pEvent = i_rNotifyEvent.GetKeyEvent();
478             const KeyCode& rKeyCode = pEvent->GetKeyCode();
479             if ( rKeyCode.GetModifier() == KEY_MOD1 )
480             {
481                 bHandled = true;
482                 switch ( rKeyCode.GetCode() )
483                 {
484                 case KEY_HOME:
485                     m_pImpl->DoAction( ACTION_ACTIVATE_FIRST );
486                     break;
487                 case KEY_PAGEUP:
488                     m_pImpl->DoAction( ACTION_ACTIVATE_PREV );
489                     break;
490                 case KEY_PAGEDOWN:
491                     m_pImpl->DoAction( ACTION_ACTIVATE_NEXT );
492                     break;
493                 case KEY_END:
494                     m_pImpl->DoAction( ACTION_ACTIVATE_LAST );
495                     break;
496                 default:
497                     bHandled = false;
498                     break;
499                 }
500             }
501             else if ( rKeyCode.GetModifier() == ( KEY_MOD1 | KEY_SHIFT ) )
502             {
503                 if ( rKeyCode.GetCode() == KEY_E )
504                 {
505                     m_pImpl->DoAction( ACTION_TOGGLE_FOCUS );
506                     bHandled = true;
507                 }
508             }
509         }
510 
511         if ( bHandled )
512             return 1;
513 
514         return Control::Notify( i_rNotifyEvent );
515     }
516 
517 	//--------------------------------------------------------------------
518     void ToolPanelDeck::GetFocus()
519     {
520         Control::GetFocus();
521         if ( m_pImpl->IsDead() )
522             return;
523         if ( !m_pImpl->FocusActivePanel() )
524         {
525             PDeckLayouter pLayouter( GetLayouter() );
526             ENSURE_OR_RETURN_VOID( pLayouter.get(), "ToolPanelDeck::GetFocus: no layouter?!" );
527             pLayouter->SetFocusToPanelSelector();
528         }
529     }
530 
531 	//--------------------------------------------------------------------
532     void ToolPanelDeck::SetAccessibleParentWindow( Window* i_pAccessibleParent )
533     {
534         m_pImpl->SetAccessibleParentWindow( i_pAccessibleParent );
535     }
536 
537 	//--------------------------------------------------------------------
538     Window* ToolPanelDeck::GetAccessibleParentWindow() const
539     {
540         Window* pAccessibleParent( m_pImpl->GetAccessibleParentWindow() );
541         if ( !pAccessibleParent )
542             pAccessibleParent = Window::GetAccessibleParentWindow();
543         return pAccessibleParent;
544     }
545 
546 	//--------------------------------------------------------------------
547     Reference< XWindowPeer > ToolPanelDeck::GetComponentInterface( sal_Bool i_bCreate )
548     {
549         Reference< XWindowPeer > xWindowPeer( Control::GetComponentInterface( sal_False ) );
550         if ( !xWindowPeer.is() && i_bCreate )
551         {
552             xWindowPeer.set( new ToolPanelDeckPeer( *this ) );
553             SetComponentInterface( xWindowPeer );
554         }
555         return xWindowPeer;
556     }
557 
558 //........................................................................
559 } // namespace svt
560 //........................................................................
561