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 : #include "FocusManager.hxx"
21 : #include "Panel.hxx"
22 : #include "DeckTitleBar.hxx"
23 : #include "PanelTitleBar.hxx"
24 : #include <sfx2/sidebar/Tools.hxx>
25 : #include "TitleBar.hxx"
26 : #include <vcl/button.hxx>
27 : #include <vcl/toolbox.hxx>
28 : #include <toolkit/helper/vclunohelper.hxx>
29 :
30 :
31 : namespace sfx2 { namespace sidebar {
32 :
33 0 : FocusManager::FocusLocation::FocusLocation (const PanelComponent eComponent, const sal_Int32 nIndex)
34 : : meComponent(eComponent),
35 0 : mnIndex(nIndex)
36 : {
37 0 : }
38 :
39 :
40 :
41 :
42 4700 : FocusManager::FocusManager (const ::boost::function<void(const Panel&)>& rShowPanelFunctor)
43 : : mpDeckTitleBar(),
44 : maPanels(),
45 : maButtons(),
46 : maShowPanelFunctor(rShowPanelFunctor),
47 : mbObservingContentControlFocus(false),
48 : mpFirstFocusedContentControl(NULL),
49 4700 : mpLastFocusedWindow(NULL)
50 : {
51 4700 : }
52 :
53 :
54 :
55 :
56 9400 : FocusManager::~FocusManager (void)
57 : {
58 4700 : Clear();
59 4700 : }
60 :
61 :
62 :
63 :
64 0 : void FocusManager::GrabFocus (void)
65 : {
66 0 : FocusDeckTitle();
67 0 : }
68 :
69 :
70 :
71 :
72 19586 : void FocusManager::Clear (void)
73 : {
74 19586 : SetDeckTitle(NULL);
75 19586 : ClearPanels();
76 19586 : ClearButtons();
77 19586 : }
78 :
79 :
80 :
81 :
82 25072 : void FocusManager::ClearPanels (void)
83 : {
84 25072 : ::std::vector<Panel*> aPanels;
85 25072 : aPanels.swap(maPanels);
86 31044 : for (::std::vector<Panel*>::iterator iPanel(aPanels.begin()),iEnd(aPanels.end());
87 : iPanel!=iEnd;
88 : ++iPanel)
89 : {
90 5972 : UnregisterWindow(**iPanel);
91 5972 : if ((*iPanel)->GetTitleBar() != NULL)
92 : {
93 5972 : UnregisterWindow(*(*iPanel)->GetTitleBar());
94 5972 : UnregisterWindow((*iPanel)->GetTitleBar()->GetToolBox());
95 : }
96 :
97 5972 : (*iPanel)->RemoveChildEventListener(LINK(this, FocusManager, ChildEventListener));
98 25072 : }
99 25072 : }
100 :
101 :
102 :
103 :
104 25072 : void FocusManager::ClearButtons (void)
105 : {
106 25072 : ::std::vector<Button*> aButtons;
107 25072 : aButtons.swap(maButtons);
108 33702 : for (::std::vector<Button*>::iterator iButton(aButtons.begin()),iEnd(aButtons.end());
109 : iButton!=iEnd;
110 : ++iButton)
111 : {
112 8630 : UnregisterWindow(**iButton);
113 25072 : }
114 25072 : }
115 :
116 :
117 :
118 :
119 25072 : void FocusManager::SetDeckTitle (DeckTitleBar* pDeckTitleBar)
120 : {
121 25072 : if (mpDeckTitleBar != NULL)
122 : {
123 5486 : UnregisterWindow(*mpDeckTitleBar);
124 5486 : UnregisterWindow(mpDeckTitleBar->GetToolBox());
125 : }
126 25072 : mpDeckTitleBar = pDeckTitleBar;
127 :
128 25072 : if (mpDeckTitleBar != NULL)
129 : {
130 5486 : RegisterWindow(*mpDeckTitleBar);
131 5486 : RegisterWindow(mpDeckTitleBar->GetToolBox());
132 : }
133 25072 : }
134 :
135 :
136 :
137 :
138 5486 : void FocusManager::SetPanels (const SharedPanelContainer& rPanels)
139 : {
140 5486 : ClearPanels();
141 11458 : for(SharedPanelContainer::const_iterator iPanel(rPanels.begin()),iEnd(rPanels.end());
142 : iPanel!=iEnd;
143 : ++iPanel)
144 : {
145 5972 : RegisterWindow(**iPanel);
146 5972 : if ((*iPanel)->GetTitleBar() != NULL)
147 : {
148 5972 : RegisterWindow(*(*iPanel)->GetTitleBar());
149 5972 : RegisterWindow((*iPanel)->GetTitleBar()->GetToolBox());
150 : }
151 :
152 : // Register also as child event listener at the panel.
153 5972 : (*iPanel)->AddChildEventListener(LINK(this, FocusManager, ChildEventListener));
154 :
155 5972 : maPanels.push_back(iPanel->get());
156 : }
157 5486 : }
158 :
159 :
160 :
161 :
162 5486 : void FocusManager::SetButtons (const ::std::vector<Button*>& rButtons)
163 : {
164 5486 : ClearButtons();
165 14188 : for (::std::vector<Button*>::const_iterator iButton(rButtons.begin()),iEnd(rButtons.end());
166 : iButton!=iEnd;
167 : ++iButton)
168 : {
169 8702 : RegisterWindow(**iButton);
170 8702 : maButtons.push_back(*iButton);
171 : }
172 5486 : }
173 :
174 :
175 :
176 :
177 37590 : void FocusManager::RegisterWindow (vcl::Window& rWindow)
178 : {
179 37590 : rWindow.AddEventListener(LINK(this, FocusManager, WindowEventListener));
180 37590 : }
181 :
182 :
183 :
184 :
185 37590 : void FocusManager::UnregisterWindow (vcl::Window& rWindow)
186 : {
187 37590 : rWindow.RemoveEventListener(LINK(this, FocusManager, WindowEventListener));
188 37590 : }
189 :
190 :
191 :
192 :
193 0 : FocusManager::FocusLocation FocusManager::GetFocusLocation (const vcl::Window& rWindow) const
194 : {
195 : // Check the deck title.
196 0 : if (mpDeckTitleBar != NULL)
197 : {
198 0 : if (mpDeckTitleBar == &rWindow)
199 0 : return FocusLocation(PC_DeckTitle, -1);
200 0 : else if (&mpDeckTitleBar->GetToolBox() == &rWindow)
201 0 : return FocusLocation(PC_DeckToolBox, -1);
202 : }
203 :
204 : // Search the panels.
205 0 : for (sal_Int32 nIndex=0,nCount(maPanels.size()); nIndex<nCount; ++nIndex)
206 : {
207 0 : if (maPanels[nIndex] == &rWindow)
208 0 : return FocusLocation(PC_PanelContent, nIndex);
209 0 : TitleBar* pTitleBar = maPanels[nIndex]->GetTitleBar();
210 0 : if (pTitleBar == &rWindow)
211 0 : return FocusLocation(PC_PanelTitle, nIndex);
212 0 : if (pTitleBar!=NULL && &pTitleBar->GetToolBox()==&rWindow)
213 0 : return FocusLocation(PC_PanelToolBox, nIndex);
214 : }
215 :
216 : // Search the buttons.
217 0 : for (sal_Int32 nIndex=0,nCount(maButtons.size()); nIndex<nCount; ++nIndex)
218 0 : if (maButtons[nIndex] == &rWindow)
219 0 : return FocusLocation(PC_TabBar, nIndex);
220 :
221 0 : return FocusLocation(PC_None, -1);
222 : }
223 :
224 0 : void FocusManager::FocusDeckTitle (void)
225 : {
226 0 : if (mpDeckTitleBar != NULL)
227 : {
228 0 : if (IsDeckTitleVisible())
229 : {
230 0 : mpDeckTitleBar->GrabFocus();
231 : }
232 0 : else if (mpDeckTitleBar->GetToolBox().GetItemCount() > 0)
233 : {
234 0 : ToolBox& rToolBox = mpDeckTitleBar->GetToolBox();
235 0 : rToolBox.GrabFocus();
236 0 : rToolBox.Invalidate();
237 : }
238 : else
239 0 : FocusPanel(0, false);
240 : }
241 : else
242 0 : FocusPanel(0, false);
243 0 : }
244 :
245 :
246 :
247 :
248 0 : bool FocusManager::IsDeckTitleVisible (void) const
249 : {
250 0 : return mpDeckTitleBar != NULL && mpDeckTitleBar->IsVisible();
251 : }
252 :
253 :
254 :
255 :
256 0 : bool FocusManager::IsPanelTitleVisible (const sal_Int32 nPanelIndex) const
257 : {
258 0 : if (nPanelIndex<0 || nPanelIndex>=static_cast<sal_Int32>(maPanels.size()))
259 0 : return false;
260 :
261 0 : TitleBar* pTitleBar = maPanels[nPanelIndex]->GetTitleBar();
262 0 : if (pTitleBar==NULL)
263 0 : return false;
264 0 : return pTitleBar->IsVisible();
265 : }
266 :
267 :
268 :
269 :
270 0 : void FocusManager::FocusPanel (
271 : const sal_Int32 nPanelIndex,
272 : const bool bFallbackToDeckTitle)
273 : {
274 0 : if (nPanelIndex<0 || nPanelIndex>=static_cast<sal_Int32>(maPanels.size()))
275 : {
276 0 : if (bFallbackToDeckTitle)
277 0 : FocusDeckTitle();
278 0 : return;
279 : }
280 :
281 0 : Panel& rPanel (*maPanels[nPanelIndex]);
282 0 : TitleBar* pTitleBar = rPanel.GetTitleBar();
283 0 : if (pTitleBar!=NULL && pTitleBar->IsVisible())
284 : {
285 0 : rPanel.SetExpanded(true);
286 0 : pTitleBar->GrabFocus();
287 : }
288 0 : else if (bFallbackToDeckTitle)
289 : {
290 : // The panel title is not visible, fall back to the deck
291 : // title.
292 : // Make sure that the desk title is visible here to prevent a
293 : // loop when both the title of panel 0 and the deck title are
294 : // not present.
295 0 : if (IsDeckTitleVisible())
296 0 : FocusDeckTitle();
297 : else
298 0 : FocusPanelContent(nPanelIndex);
299 : }
300 : else
301 0 : FocusPanelContent(nPanelIndex);
302 :
303 0 : if (maShowPanelFunctor)
304 0 : maShowPanelFunctor(rPanel);
305 : }
306 :
307 :
308 :
309 :
310 0 : void FocusManager::FocusPanelContent (const sal_Int32 nPanelIndex)
311 : {
312 0 : vcl::Window* pWindow = VCLUnoHelper::GetWindow(maPanels[nPanelIndex]->GetElementWindow());
313 0 : if (pWindow != NULL)
314 : {
315 0 : mbObservingContentControlFocus = true;
316 0 : pWindow->GrabFocus();
317 0 : mbObservingContentControlFocus = false;
318 : }
319 0 : }
320 :
321 :
322 :
323 :
324 0 : void FocusManager::FocusButton (const sal_Int32 nButtonIndex)
325 : {
326 0 : maButtons[nButtonIndex]->GrabFocus();
327 0 : maButtons[nButtonIndex]->Invalidate();
328 0 : }
329 :
330 :
331 :
332 :
333 0 : void FocusManager::ClickButton (const sal_Int32 nButtonIndex)
334 : {
335 0 : maButtons[nButtonIndex]->Click();
336 0 : if (nButtonIndex > 0)
337 0 : if ( ! maPanels.empty())
338 0 : FocusPanel(0, true);
339 0 : maButtons[nButtonIndex]->GetParent()->Invalidate();
340 0 : }
341 :
342 :
343 :
344 :
345 72 : void FocusManager::RemoveWindow (vcl::Window& rWindow)
346 : {
347 72 : ::std::vector<Panel*>::iterator iPanel (::std::find(maPanels.begin(), maPanels.end(), &rWindow));
348 72 : if (iPanel != maPanels.end())
349 : {
350 0 : UnregisterWindow(rWindow);
351 0 : if ((*iPanel)->GetTitleBar() != NULL)
352 : {
353 0 : UnregisterWindow(*(*iPanel)->GetTitleBar());
354 0 : UnregisterWindow((*iPanel)->GetTitleBar()->GetToolBox());
355 : }
356 0 : maPanels.erase(iPanel);
357 72 : return;
358 : }
359 :
360 72 : ::std::vector<Button*>::iterator iButton (::std::find(maButtons.begin(), maButtons.end(), &rWindow));
361 72 : if (iButton != maButtons.end())
362 : {
363 72 : UnregisterWindow(rWindow);
364 72 : maButtons.erase(iButton);
365 72 : return;
366 : }
367 : }
368 :
369 :
370 :
371 :
372 0 : bool FocusManager::MoveFocusInsidePanel (
373 : const FocusLocation& rFocusLocation,
374 : const sal_Int32 nDirection)
375 : {
376 : const bool bHasToolBoxItem (
377 0 : maPanels[rFocusLocation.mnIndex]->GetTitleBar()->GetToolBox().GetItemCount() > 0);
378 0 : switch (rFocusLocation.meComponent)
379 : {
380 : case PC_PanelTitle:
381 0 : if (nDirection > 0 && bHasToolBoxItem)
382 0 : maPanels[rFocusLocation.mnIndex]->GetTitleBar()->GetToolBox().GrabFocus();
383 : else
384 0 : FocusPanelContent(rFocusLocation.mnIndex);
385 0 : return true;
386 :
387 : case PC_PanelToolBox:
388 0 : if (nDirection < 0 && bHasToolBoxItem)
389 0 : maPanels[rFocusLocation.mnIndex]->GetTitleBar()->GrabFocus();
390 : else
391 0 : FocusPanelContent(rFocusLocation.mnIndex);
392 0 : return true;
393 :
394 : default:
395 0 : return false;
396 : }
397 : }
398 :
399 :
400 :
401 :
402 0 : bool FocusManager::MoveFocusInsideDeckTitle (
403 : const FocusLocation& rFocusLocation,
404 : const sal_Int32 nDirection)
405 : {
406 : // Note that when the title bar of the first (and only) panel is
407 : // not visible then the deck title takes its place and the focus
408 : // is moved between a) deck title, b) deck closer and c) content
409 : // of panel 0.
410 : const bool bHasToolBoxItem (
411 0 : mpDeckTitleBar->GetToolBox().GetItemCount() > 0);
412 0 : switch (rFocusLocation.meComponent)
413 : {
414 : case PC_DeckTitle:
415 0 : if (nDirection<0 && ! IsPanelTitleVisible(0))
416 0 : FocusPanelContent(0);
417 0 : else if (bHasToolBoxItem)
418 0 : mpDeckTitleBar->GetToolBox().GrabFocus();
419 0 : return true;
420 :
421 : case PC_DeckToolBox:
422 0 : if (nDirection>0 && ! IsPanelTitleVisible(0))
423 0 : FocusPanelContent(0);
424 : else
425 0 : mpDeckTitleBar->GrabFocus();
426 0 : return true;
427 :
428 : default:
429 0 : return false;
430 : }
431 : }
432 :
433 :
434 :
435 :
436 0 : void FocusManager::HandleKeyEvent (
437 : const vcl::KeyCode& rKeyCode,
438 : const vcl::Window& rWindow)
439 : {
440 0 : const FocusLocation aLocation (GetFocusLocation(rWindow));
441 0 : mpLastFocusedWindow = NULL;
442 :
443 0 : switch (rKeyCode.GetCode())
444 : {
445 : case KEY_SPACE:
446 0 : switch (aLocation.meComponent)
447 : {
448 : case PC_PanelTitle:
449 : // Toggle panel between expanded and collapsed.
450 0 : maPanels[aLocation.mnIndex]->SetExpanded( ! maPanels[aLocation.mnIndex]->IsExpanded());
451 0 : break;
452 :
453 : case PC_TabBar:
454 : // Activate the button.
455 0 : ClickButton(aLocation.mnIndex);
456 0 : break;
457 :
458 : default:
459 0 : break;
460 : }
461 0 : return;
462 :
463 : case KEY_RETURN:
464 0 : switch (aLocation.meComponent)
465 : {
466 : case PC_DeckToolBox:
467 0 : FocusButton(0);
468 0 : break;
469 :
470 : case PC_PanelTitle:
471 : // Enter the panel.
472 0 : FocusPanelContent(aLocation.mnIndex);
473 0 : break;
474 :
475 : case PC_TabBar:
476 : // Activate the button.
477 0 : ClickButton(aLocation.mnIndex);
478 0 : break;
479 :
480 : default:
481 0 : break;
482 : }
483 0 : return;
484 :
485 : case KEY_TAB:
486 : {
487 : const sal_Int32 nDirection (
488 0 : rKeyCode.IsShift()
489 : ? -1
490 0 : : +1);
491 0 : switch (aLocation.meComponent)
492 : {
493 : case PC_PanelTitle:
494 : case PC_PanelToolBox:
495 : case PC_PanelContent:
496 0 : MoveFocusInsidePanel(aLocation, nDirection);
497 0 : break;
498 :
499 : case PC_DeckTitle:
500 : case PC_DeckToolBox:
501 0 : MoveFocusInsideDeckTitle(aLocation, nDirection);
502 0 : break;
503 :
504 : default:
505 0 : break;
506 : }
507 0 : break;
508 : }
509 :
510 : case KEY_LEFT:
511 : case KEY_UP:
512 0 : switch (aLocation.meComponent)
513 : {
514 : case PC_PanelTitle:
515 : case PC_PanelToolBox:
516 : case PC_PanelContent:
517 : // Go to previous panel or the deck title.
518 0 : if (aLocation.mnIndex > 0)
519 0 : FocusPanel(aLocation.mnIndex-1, true);
520 0 : else if (IsDeckTitleVisible())
521 0 : FocusDeckTitle();
522 : else
523 0 : FocusButton(maButtons.size()-1);
524 0 : break;
525 :
526 : case PC_DeckTitle:
527 : case PC_DeckToolBox:
528 : // Focus the last button.
529 0 : FocusButton(maButtons.size()-1);
530 0 : break;
531 :
532 : case PC_TabBar:
533 : // Go to previous tab bar item.
534 0 : if (aLocation.mnIndex == 0)
535 0 : FocusPanel(maPanels.size()-1, true);
536 : else
537 0 : FocusButton((aLocation.mnIndex + maButtons.size() - 1) % maButtons.size());
538 0 : break;
539 :
540 : default:
541 0 : break;
542 : }
543 0 : break;
544 :
545 : case KEY_RIGHT:
546 : case KEY_DOWN:
547 0 : switch(aLocation.meComponent)
548 : {
549 : case PC_PanelTitle:
550 : case PC_PanelToolBox:
551 : case PC_PanelContent:
552 : // Go to next panel.
553 0 : if (aLocation.mnIndex < static_cast<sal_Int32>(maPanels.size())-1)
554 0 : FocusPanel(aLocation.mnIndex+1, false);
555 : else
556 0 : FocusButton(0);
557 0 : break;
558 :
559 : case PC_DeckTitle:
560 : case PC_DeckToolBox:
561 : // Focus the first panel.
562 0 : if (IsPanelTitleVisible(0))
563 0 : FocusPanel(0, false);
564 : else
565 0 : FocusButton(0);
566 0 : break;
567 :
568 : case PC_TabBar:
569 : // Go to next tab bar item.
570 0 : if (aLocation.mnIndex < static_cast<sal_Int32>(maButtons.size())-1)
571 0 : FocusButton(aLocation.mnIndex + 1);
572 0 : else if (IsDeckTitleVisible())
573 0 : FocusDeckTitle();
574 : else
575 0 : FocusPanel(0, true);
576 0 : break;
577 :
578 : default:
579 0 : break;
580 : }
581 0 : break;
582 : }
583 : }
584 :
585 :
586 :
587 :
588 272806 : IMPL_LINK(FocusManager, WindowEventListener, VclSimpleEvent*, pEvent)
589 : {
590 136403 : if (pEvent == NULL)
591 0 : return 0;
592 :
593 136403 : if ( ! pEvent->ISA(VclWindowEvent))
594 0 : return 0;
595 :
596 136403 : VclWindowEvent* pWindowEvent = static_cast<VclWindowEvent*>(pEvent);
597 136403 : vcl::Window* pSource = pWindowEvent->GetWindow();
598 136403 : if (pSource == NULL)
599 0 : return 0;
600 :
601 136403 : switch (pWindowEvent->GetId())
602 : {
603 : case VCLEVENT_WINDOW_KEYINPUT:
604 : {
605 0 : KeyEvent* pKeyEvent = static_cast<KeyEvent*>(pWindowEvent->GetData());
606 0 : HandleKeyEvent(pKeyEvent->GetKeyCode(), *pSource);
607 0 : return 1;
608 : }
609 :
610 : case VCLEVENT_OBJECT_DYING:
611 72 : RemoveWindow(*pSource);
612 72 : return 1;
613 :
614 : case VCLEVENT_WINDOW_GETFOCUS:
615 : case VCLEVENT_WINDOW_LOSEFOCUS:
616 0 : pSource->Invalidate();
617 0 : return 1;
618 :
619 : default:
620 136331 : break;
621 : }
622 :
623 136331 : return 0;
624 : }
625 :
626 :
627 :
628 :
629 365410 : IMPL_LINK(FocusManager, ChildEventListener, VclSimpleEvent*, pEvent)
630 : {
631 182705 : if (pEvent == NULL)
632 0 : return 0;
633 :
634 182705 : if ( ! pEvent->ISA(VclWindowEvent))
635 0 : return 0;
636 :
637 182705 : VclWindowEvent* pWindowEvent = static_cast<VclWindowEvent*>(pEvent);
638 182705 : vcl::Window* pSource = pWindowEvent->GetWindow();
639 182705 : if (pSource == NULL)
640 0 : return 0;
641 :
642 182705 : switch (pWindowEvent->GetId())
643 : {
644 : case VCLEVENT_WINDOW_KEYINPUT:
645 : {
646 0 : KeyEvent* pKeyEvent = static_cast<KeyEvent*>(pWindowEvent->GetData());
647 :
648 : // Go up the window hierarchy to find out whether the
649 : // parent of the event source is known to us.
650 0 : vcl::Window* pWindow = pSource;
651 0 : FocusLocation aLocation (PC_None, -1);
652 : while (true)
653 : {
654 0 : if (pWindow == NULL)
655 0 : break;
656 0 : aLocation = GetFocusLocation(*pWindow);
657 0 : if (aLocation.meComponent != PC_None)
658 0 : break;
659 0 : pWindow = pWindow->GetParent();
660 : }
661 :
662 0 : if (aLocation.meComponent != PC_None)
663 : {
664 0 : switch (pKeyEvent->GetKeyCode().GetCode())
665 : {
666 : case KEY_ESCAPE:
667 : // Return focus back to the panel title.
668 0 : FocusPanel(aLocation.mnIndex, true);
669 0 : break;
670 :
671 : case KEY_TAB:
672 0 : if (mpFirstFocusedContentControl!=NULL
673 0 : && mpLastFocusedWindow == mpFirstFocusedContentControl)
674 : {
675 : // Move focus back to panel (or deck)
676 : // title.
677 0 : FocusPanel(aLocation.mnIndex, true);
678 : }
679 0 : break;
680 :
681 : default:
682 0 : break;
683 : }
684 : }
685 0 : return 1;
686 : }
687 :
688 : case VCLEVENT_WINDOW_GETFOCUS:
689 : // Keep track of focused controls in panel content.
690 : // Remember the first focused control. When it is later
691 : // focused again due to pressing the TAB key then the
692 : // focus is moved to the panel or deck title.
693 4 : mpLastFocusedWindow = pSource;
694 4 : if (mbObservingContentControlFocus)
695 0 : mpFirstFocusedContentControl = pSource;
696 4 : break;
697 :
698 : default:
699 182701 : break;
700 : }
701 :
702 182705 : return 0;
703 : }
704 :
705 :
706 951 : } } // end of namespace sfx2::sidebar
707 :
708 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|