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