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