1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
22 #include <vcl/toolkit/ivctrl.hxx>
23 #include "imivctl.hxx"
24 #include <vcl/accessiblefactory.hxx>
25 #include <vcl/bitmapex.hxx>
26 #include <vcl/commandevent.hxx>
27 #include <vcl/mnemonic.hxx>
28 #include <vcl/settings.hxx>
29 #include <vcl/tabctrl.hxx>
30 #include <vcl/vclevent.hxx>
31 #include <vcl/uitest/uiobject.hxx>
32 #include <vcl/uitest/logger.hxx>
33 #include <vcl/uitest/eventdescription.hxx>
34 #include <verticaltabctrl.hxx>
36 using namespace ::com::sun::star::accessibility
;
40 void collectUIInformation( const OUString
& aID
, const OUString
& aPos
)
42 EventDescription aDescription
;
43 aDescription
.aID
= aID
;
44 aDescription
.aParameters
= {{ "POS" , aPos
}};
45 aDescription
.aAction
= "SELECT";
46 aDescription
.aKeyWord
= "VerticalTab";
47 UITestLogger::getInstance().logEvent(aDescription
);
51 /*****************************************************************************
53 | class : SvxIconChoiceCtrlEntry
55 \*****************************************************************************/
57 SvxIconChoiceCtrlEntry::SvxIconChoiceCtrlEntry( OUString _aText
,
59 : aImage(std::move(_aImage
))
60 , aText(std::move(_aText
))
63 , nFlags(SvxIconViewFlags::NONE
)
67 OUString
SvxIconChoiceCtrlEntry::GetDisplayText() const
69 return MnemonicGenerator::EraseAllMnemonicChars( aText
);
72 /*****************************************************************************
74 | class : SvtIconChoiceCtrl
76 \*****************************************************************************/
78 SvtIconChoiceCtrl::SvtIconChoiceCtrl( vcl::Window
* pParent
, WinBits nWinStyle
) :
80 // WB_CLIPCHILDREN on, as ScrollBars lie on the window!
81 Control( pParent
, nWinStyle
| WB_CLIPCHILDREN
),
83 _pImpl ( new SvxIconChoiceCtrl_Impl( this, nWinStyle
) )
85 GetOutDev()->SetLineColor();
86 _pImpl
->InitSettings();
89 SvtIconChoiceCtrl::~SvtIconChoiceCtrl()
94 void SvtIconChoiceCtrl::dispose()
98 _pImpl
->CallEventListeners( VclEventId::ObjectDying
, nullptr );
104 SvxIconChoiceCtrlEntry
* SvtIconChoiceCtrl::InsertEntry( const OUString
& rText
, const Image
& rImage
)
106 SvxIconChoiceCtrlEntry
* pEntry
= new SvxIconChoiceCtrlEntry( rText
, rImage
);
108 _pImpl
->InsertEntry(std::unique_ptr
<SvxIconChoiceCtrlEntry
>(pEntry
), _pImpl
->GetEntryCount());
113 void SvtIconChoiceCtrl::RemoveEntry(sal_Int32 nIndex
)
115 _pImpl
->RemoveEntry(nIndex
);
118 void SvtIconChoiceCtrl::Paint(vcl::RenderContext
& rRenderContext
, const tools::Rectangle
& rRect
)
120 _pImpl
->Paint(rRenderContext
, rRect
);
123 void SvtIconChoiceCtrl::MouseButtonDown( const MouseEvent
& rMEvt
)
125 if( !_pImpl
->MouseButtonDown( rMEvt
) )
126 Control::MouseButtonDown( rMEvt
);
129 void SvtIconChoiceCtrl::MouseMove( const MouseEvent
& rMEvt
)
131 if( !_pImpl
->MouseMove( rMEvt
) )
132 Control::MouseMove( rMEvt
);
134 void SvtIconChoiceCtrl::ArrangeIcons()
137 tools::Rectangle aEntryRect
;
139 for ( sal_Int32 i
= 0; i
< GetEntryCount(); i
++ )
141 SvxIconChoiceCtrlEntry
* pEntry
= GetEntry ( i
);
142 aEntryRect
= _pImpl
->GetEntryBoundRect ( pEntry
);
144 aFullSize
.setHeight ( aFullSize
.getHeight()+aEntryRect
.GetHeight() );
147 _pImpl
->Arrange(aFullSize
.getHeight());
149 _pImpl
->Arrange(1000);
151 void SvtIconChoiceCtrl::Resize()
157 void SvtIconChoiceCtrl::GetFocus()
161 SvxIconChoiceCtrlEntry
* pSelectedEntry
= GetSelectedEntry();
162 if ( pSelectedEntry
)
163 _pImpl
->CallEventListeners( VclEventId::ListboxSelect
, pSelectedEntry
);
166 void SvtIconChoiceCtrl::LoseFocus()
170 Control::LoseFocus();
173 void SvtIconChoiceCtrl::SetFont(const vcl::Font
& rFont
)
175 if (rFont
!= GetFont())
177 Control::SetFont(rFont
);
178 _pImpl
->FontModified();
182 void SvtIconChoiceCtrl::SetPointFont(const vcl::Font
& rFont
)
184 if (rFont
!= GetPointFont(*GetOutDev())) //FIXME
186 Control::SetPointFont(*GetOutDev(), rFont
); //FIXME
187 _pImpl
->FontModified();
191 void SvtIconChoiceCtrl::Command(const CommandEvent
& rCEvt
)
193 _pImpl
->Command( rCEvt
);
194 //pass at least alt press/release to parent impl
195 if (rCEvt
.GetCommand() == CommandEventId::ModKeyChange
)
196 Control::Command(rCEvt
);
199 sal_Int32
SvtIconChoiceCtrl::GetEntryCount() const
201 return _pImpl
? _pImpl
->GetEntryCount() : 0;
204 SvxIconChoiceCtrlEntry
* SvtIconChoiceCtrl::GetEntry( sal_Int32 nPos
) const
206 return _pImpl
? _pImpl
->GetEntry( nPos
) : nullptr;
209 SvxIconChoiceCtrlEntry
* SvtIconChoiceCtrl::GetSelectedEntry() const
211 return _pImpl
? _pImpl
->GetFirstSelectedEntry() : nullptr;
214 void SvtIconChoiceCtrl::ClickIcon()
217 _aClickIconHdl
.Call( this );
220 void SvtIconChoiceCtrl::KeyInput( const KeyEvent
& rKEvt
)
222 bool bKeyUsed
= DoKeyInput( rKEvt
);
225 Control::KeyInput( rKEvt
);
228 bool SvtIconChoiceCtrl::DoKeyInput( const KeyEvent
& rKEvt
)
230 return _pImpl
->KeyInput( rKEvt
);
232 sal_Int32
SvtIconChoiceCtrl::GetEntryListPos( SvxIconChoiceCtrlEntry
const * pEntry
) const
234 return _pImpl
->GetEntryListPos( pEntry
);
236 SvxIconChoiceCtrlEntry
* SvtIconChoiceCtrl::GetCursor( ) const
238 return _pImpl
->GetCurEntry( );
240 void SvtIconChoiceCtrl::SetCursor( SvxIconChoiceCtrlEntry
* pEntry
)
242 _pImpl
->SetCursor( pEntry
);
245 void SvtIconChoiceCtrl::DataChanged( const DataChangedEvent
& rDCEvt
)
247 if ( ((rDCEvt
.GetType() == DataChangedEventType::SETTINGS
) ||
248 (rDCEvt
.GetType() == DataChangedEventType::FONTSUBSTITUTION
) ||
249 (rDCEvt
.GetType() == DataChangedEventType::FONTS
) ) &&
250 (rDCEvt
.GetFlags() & AllSettingsFlags::STYLE
) )
252 _pImpl
->InitSettings();
253 Invalidate(InvalidateFlags::NoChildren
);
256 Control::DataChanged( rDCEvt
);
259 void SvtIconChoiceCtrl::SetBackground( const Wallpaper
& rPaper
)
261 if( rPaper
== GetBackground() )
264 const StyleSettings
& rStyleSettings
= GetSettings().GetStyleSettings();
265 // if it is the default (empty) wallpaper
266 if (rPaper
.IsEmpty())
268 Control::SetBackground( rStyleSettings
.GetFieldColor() );
272 Wallpaper
aBackground( rPaper
);
273 // HACK, as background might be transparent!
274 if( !aBackground
.IsBitmap() )
275 aBackground
.SetStyle( WallpaperStyle::Tile
);
277 WallpaperStyle eStyle
= aBackground
.GetStyle();
278 Color
aBack( aBackground
.GetColor());
279 if( aBack
== COL_TRANSPARENT
&&
280 (!aBackground
.IsBitmap() ||
281 aBackground
.GetBitmap().IsAlpha() ||
282 (eStyle
!= WallpaperStyle::Tile
&& eStyle
!= WallpaperStyle::Scale
)) )
284 aBackground
.SetColor( rStyleSettings
.GetFieldColor() );
286 if( aBackground
.IsScrollable() )
288 tools::Rectangle aRect
;
289 aRect
.SetSize( Size(32765, 32765) );
290 aBackground
.SetRect( aRect
);
294 tools::Rectangle
aRect( _pImpl
->GetOutputRect() );
295 aBackground
.SetRect( aRect
);
297 Control::SetBackground( aBackground
);
300 // If text colors are attributed "hard," don't use automatism to select
301 // a readable text color.
302 vcl::Font
aFont( GetFont() );
303 aFont
.SetColor( rStyleSettings
.GetFieldTextColor() );
306 Invalidate(InvalidateFlags::NoChildren
);
309 void SvtIconChoiceCtrl::RequestHelp( const HelpEvent
& rHEvt
)
311 if ( !_pImpl
->RequestHelp( rHEvt
) )
312 Control::RequestHelp( rHEvt
);
315 tools::Rectangle
SvtIconChoiceCtrl::GetBoundingBox( SvxIconChoiceCtrlEntry
* pEntry
) const
317 return _pImpl
->GetEntryBoundRect( pEntry
);
320 void SvtIconChoiceCtrl::FillLayoutData() const
323 const_cast<SvtIconChoiceCtrl
*>(this)->Invalidate();
326 tools::Rectangle
SvtIconChoiceCtrl::GetEntryCharacterBounds( const sal_Int32 _nEntryPos
, const sal_Int32 _nCharacterIndex
) const
328 tools::Rectangle aRect
;
330 Pair aEntryCharacterRange
= GetLineStartEnd( _nEntryPos
);
331 if ( aEntryCharacterRange
.A() + _nCharacterIndex
< aEntryCharacterRange
.B() )
333 aRect
= GetCharacterBounds( aEntryCharacterRange
.A() + _nCharacterIndex
);
339 void SvtIconChoiceCtrl::CallImplEventListeners(VclEventId nEvent
, void* pData
)
341 CallEventListeners(nEvent
, pData
);
343 css::uno::Reference
< XAccessible
> SvtIconChoiceCtrl::CreateAccessible()
345 vcl::Window
* pParent
= GetAccessibleParentWindow();
346 DBG_ASSERT( pParent
, "SvTreeListBox::CreateAccessible - accessible parent not found" );
348 css::uno::Reference
< XAccessible
> xAccessible
;
351 css::uno::Reference
< XAccessible
> xAccParent
= pParent
->GetAccessible();
352 if ( xAccParent
.is() )
354 css::uno::Reference
< css::awt::XVclWindowPeer
> xHoldAlive(GetComponentInterface());
355 xAccessible
= AccessibleFactory::createAccessibleIconChoiceCtrl(*this, xAccParent
);
361 struct VerticalTabPageData
364 SvxIconChoiceCtrlEntry
* pEntry
;
365 VclPtr
<vcl::Window
> xPage
; ///< the TabPage itself
368 VerticalTabControl::VerticalTabControl(vcl::Window
* pParent
, bool bWithIcons
)
370 , m_xChooser(VclPtr
<SvtIconChoiceCtrl
>::Create(this, WB_3DLOOK
| (bWithIcons
? WB_ICON
: WB_DETAILS
) | WB_BORDER
|
372 WB_NODRAGSELECTION
| WB_TABSTOP
| WB_CLIPCHILDREN
|
374 , m_xBox(VclPtr
<VclVBox
>::Create(this))
376 SetStyle(GetStyle() | WB_DIALOGCONTROL
);
377 SetType(WindowType::VERTICALTABCONTROL
);
378 m_xChooser
->SetClickHdl(LINK(this, VerticalTabControl
, ChosePageHdl_Impl
));
379 m_xChooser
->set_width_request(150);
380 m_xChooser
->set_height_request(400);
381 m_xChooser
->SetSizePixel(Size(150, 400));
382 m_xBox
->set_vexpand(true);
383 m_xBox
->set_hexpand(true);
384 m_xBox
->set_expand(true);
389 VerticalTabControl::~VerticalTabControl()
394 void VerticalTabControl::dispose()
396 m_xChooser
.disposeAndClear();
397 m_xBox
.disposeAndClear();
401 IMPL_LINK_NOARG(VerticalTabControl
, ChosePageHdl_Impl
, SvtIconChoiceCtrl
*, void)
403 SvxIconChoiceCtrlEntry
*pEntry
= m_xChooser
->GetSelectedEntry();
405 pEntry
= m_xChooser
->GetCursor();
407 VerticalTabPageData
* pData
= GetPageData(pEntry
);
409 if (pData
->sId
!= m_sCurrentPageId
)
410 SetCurPageId(pData
->sId
);
413 bool VerticalTabControl::EventNotify(NotifyEvent
& rNEvt
)
415 if (rNEvt
.GetType() == NotifyEventType::KEYINPUT
)
417 sal_uInt16 nCode
= rNEvt
.GetKeyEvent()->GetKeyCode().GetCode();
418 if (nCode
== KEY_PAGEUP
|| nCode
== KEY_PAGEDOWN
)
420 m_xChooser
->DoKeyInput(*(rNEvt
.GetKeyEvent()));
424 return VclHBox::EventNotify(rNEvt
);
427 void VerticalTabControl::ActivatePage()
429 m_aActivateHdl
.Call( this );
432 bool VerticalTabControl::DeactivatePage()
434 return !m_aDeactivateHdl
.IsSet() || m_aDeactivateHdl
.Call(this);
437 VerticalTabPageData
* VerticalTabControl::GetPageData(const SvxIconChoiceCtrlEntry
* pEntry
) const
439 VerticalTabPageData
* pRet
= nullptr;
440 for (auto & pData
: maPageList
)
442 if (pData
->pEntry
== pEntry
)
451 VerticalTabPageData
* VerticalTabControl::GetPageData(std::u16string_view rId
) const
453 VerticalTabPageData
* pRet
= nullptr;
454 for (auto & pData
: maPageList
)
456 if (pData
->sId
== rId
)
465 void VerticalTabControl::SetCurPageId(const OUString
& rId
)
467 OUString sOldPageId
= GetCurPageId();
468 if (sOldPageId
== rId
)
471 VerticalTabPageData
* pOldData
= GetPageData(sOldPageId
);
472 if (pOldData
&& pOldData
->xPage
)
474 if (!DeactivatePage())
476 pOldData
->xPage
->Hide();
479 m_sCurrentPageId
= "";
481 VerticalTabPageData
* pNewData
= GetPageData(rId
);
482 if (pNewData
&& pNewData
->xPage
)
484 m_sCurrentPageId
= rId
;
485 m_xChooser
->SetCursor(pNewData
->pEntry
);
488 pNewData
->xPage
->Show();
490 collectUIInformation(get_id(),m_sCurrentPageId
);
493 const OUString
& VerticalTabControl::GetPageId(sal_uInt16 nIndex
) const
495 return maPageList
[nIndex
]->sId
;
498 void VerticalTabControl::InsertPage(const rtl::OUString
&rIdent
, const rtl::OUString
& rLabel
, const Image
& rImage
,
499 const rtl::OUString
& rTooltip
, VclPtr
<vcl::Window
> xPage
, int nPos
)
501 SvxIconChoiceCtrlEntry
* pEntry
= m_xChooser
->InsertEntry(rLabel
, rImage
);
502 pEntry
->SetQuickHelpText(rTooltip
);
503 m_xChooser
->ArrangeIcons();
504 VerticalTabPageData
* pNew
;
507 maPageList
.emplace_back(new VerticalTabPageData
);
508 pNew
= maPageList
.back().get();
512 maPageList
.emplace(maPageList
.begin() + nPos
, new VerticalTabPageData
);
513 pNew
= maPageList
[nPos
].get();
516 pNew
->pEntry
= pEntry
;
518 Size
aOrigPrefSize(m_xBox
->get_preferred_size());
519 Size
aPagePrefSize(xPage
->get_preferred_size());
520 m_xBox
->set_width_request(std::max(aOrigPrefSize
.Width(), aPagePrefSize
.Width()));
521 m_xBox
->set_height_request(std::max(aOrigPrefSize
.Height(), aPagePrefSize
.Height()));
525 void VerticalTabControl::RemovePage(std::u16string_view rPageId
)
527 for (auto it
= maPageList
.begin(), end
= maPageList
.end(); it
!= end
; ++it
)
529 VerticalTabPageData
* pData
= it
->get();
530 if (pData
->sId
== rPageId
)
532 sal_Int32 nEntryListPos
= m_xChooser
->GetEntryListPos(pData
->pEntry
);
533 assert(nEntryListPos
>= 0);
534 m_xChooser
->RemoveEntry(nEntryListPos
);
535 m_xChooser
->ArrangeIcons();
536 maPageList
.erase(it
);
542 sal_uInt16
VerticalTabControl::GetPagePos(std::u16string_view rPageId
) const
544 VerticalTabPageData
* pData
= GetPageData(rPageId
);
546 return TAB_PAGE_NOTFOUND
;
547 return m_xChooser
->GetEntryListPos(pData
->pEntry
);
550 VclPtr
<vcl::Window
> VerticalTabControl::GetPage(std::u16string_view rPageId
) const
552 VerticalTabPageData
* pData
= GetPageData(rPageId
);
558 OUString
VerticalTabControl::GetPageText(std::u16string_view rPageId
) const
560 VerticalTabPageData
* pData
= GetPageData(rPageId
);
563 return pData
->pEntry
->GetText();
566 void VerticalTabControl::SetPageText(std::u16string_view rPageId
, const OUString
& rText
)
568 VerticalTabPageData
* pData
= GetPageData(rPageId
);
571 pData
->pEntry
->SetText(rText
);
574 Size
VerticalTabControl::GetOptimalSize() const
576 // re-calculate size - we might have replaced dummy tab pages with
578 Size
aOptimalPageSize(m_xBox
->get_preferred_size());
580 for (auto const& item
: maPageList
)
582 Size
aPagePrefSize(item
->xPage
->get_preferred_size());
583 if (aPagePrefSize
.Width() > aOptimalPageSize
.Width())
584 aOptimalPageSize
.setWidth( aPagePrefSize
.Width() );
585 if (aPagePrefSize
.Height() > aOptimalPageSize
.Height())
586 aOptimalPageSize
.setHeight( aPagePrefSize
.Height() );
589 return aOptimalPageSize
;
592 void VerticalTabControl::DumpAsPropertyTree(tools::JsonWriter
& rJsonWriter
)
594 rJsonWriter
.put("id", get_id());
595 rJsonWriter
.put("type", "tabcontrol");
596 rJsonWriter
.put("vertical", true);
597 rJsonWriter
.put("selected", GetCurPageId());
600 auto childrenNode
= rJsonWriter
.startArray("children");
601 for (int i
= 0; i
< GetPageCount(); i
++)
603 VclPtr
<vcl::Window
> pChild
= GetPage(GetPageId(i
));
607 if (!pChild
->GetChildCount())
610 auto aChildNode
= rJsonWriter
.startStruct();
611 pChild
->DumpAsPropertyTree(rJsonWriter
);
616 auto tabsNode
= rJsonWriter
.startArray("tabs");
617 for(int i
= 0; i
< GetPageCount(); i
++)
619 VclPtr
<vcl::Window
> pChild
= GetPage(GetPageId(i
));
623 if (!pChild
->GetChildCount())
626 auto aTabNode
= rJsonWriter
.startStruct();
627 auto sId
= GetPageId(i
);
628 rJsonWriter
.put("text", GetPageText(sId
));
629 rJsonWriter
.put("id", sId
);
630 rJsonWriter
.put("name", GetPageText(sId
));
636 FactoryFunction
VerticalTabControl::GetUITestFactory() const
638 return VerticalTabControlUIObject::create
;
641 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */