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