android: Update app-specific/MIME type icons
[LibreOffice.git] / svtools / source / control / tabbar.cxx
blob02a23b8aea3342078534fa6473531fdce6bf6b93
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
21 #include <svtools/tabbar.hxx>
22 #include <tools/time.hxx>
23 #include <tools/poly.hxx>
24 #include <utility>
25 #include <vcl/InterimItemWindow.hxx>
26 #include <vcl/bitmapex.hxx>
27 #include <vcl/svapp.hxx>
28 #include <vcl/help.hxx>
29 #include <vcl/decoview.hxx>
30 #include <vcl/event.hxx>
31 #include <vcl/settings.hxx>
32 #include <vcl/commandevent.hxx>
33 #include <vcl/svtaccessiblefactory.hxx>
34 #include <vcl/accessiblefactory.hxx>
35 #include <vcl/ptrstyle.hxx>
36 #include <vcl/weldutils.hxx>
37 #include <svtools/svtresid.hxx>
38 #include <svtools/strings.hrc>
39 #include <limits>
40 #include <memory>
41 #include <vector>
42 #include <vcl/idle.hxx>
43 #include <bitmaps.hlst>
45 namespace
48 constexpr sal_uInt16 TABBAR_DRAG_SCROLLOFF = 5;
49 constexpr sal_uInt16 TABBAR_MINSIZE = 5;
51 constexpr sal_uInt16 ADDNEWPAGE_AREAWIDTH = 10;
53 class TabDrawer
55 private:
56 vcl::RenderContext& mrRenderContext;
57 const StyleSettings& mrStyleSettings;
59 tools::Rectangle maRect;
60 tools::Rectangle maLineRect;
62 Color maSelectedColor;
63 Color maCustomColor;
65 public:
66 bool mbSelected:1;
67 bool mbCustomColored:1;
68 bool mbEnabled:1;
69 bool mbProtect:1;
71 explicit TabDrawer(vcl::RenderContext& rRenderContext)
72 : mrRenderContext(rRenderContext)
73 , mrStyleSettings(rRenderContext.GetSettings().GetStyleSettings())
74 , mbSelected(false)
75 , mbCustomColored(false)
76 , mbEnabled(false)
77 , mbProtect(false)
82 void drawOuterFrame()
84 // set correct FillInBrush depending on status
85 if (mbSelected)
87 // Currently selected Tab
88 mrRenderContext.SetFillColor(maSelectedColor);
89 mrRenderContext.SetLineColor(maSelectedColor);
90 mrRenderContext.DrawRect(maRect);
91 mrRenderContext.SetLineColor(mrStyleSettings.GetDarkShadowColor());
93 else if (mbCustomColored)
95 mrRenderContext.SetFillColor(maCustomColor);
96 mrRenderContext.SetLineColor(maCustomColor);
97 mrRenderContext.DrawRect(maRect);
98 mrRenderContext.SetLineColor(mrStyleSettings.GetDarkShadowColor());
102 void drawText(const OUString& aText)
104 tools::Rectangle aRect = maRect;
105 tools::Long nTextWidth = mrRenderContext.GetTextWidth(aText);
106 tools::Long nTextHeight = mrRenderContext.GetTextHeight();
107 Point aPos = aRect.TopLeft();
108 aPos.AdjustX((aRect.getOpenWidth() - nTextWidth) / 2 );
109 aPos.AdjustY((aRect.getOpenHeight() - nTextHeight) / 2 );
111 if (mbEnabled)
112 mrRenderContext.DrawText(aPos, aText);
113 else
114 mrRenderContext.DrawCtrlText(aPos, aText, 0, aText.getLength(), (DrawTextFlags::Disable | DrawTextFlags::Mnemonic));
117 void drawOverTopBorder()
119 Point aTopLeft = maRect.TopLeft() + Point(1, 0);
120 Point aTopRight = maRect.TopRight() + Point(-1, 0);
122 tools::Rectangle aDelRect(aTopLeft, aTopRight);
123 mrRenderContext.DrawRect(aDelRect);
126 void drawColorLine()
128 if (!mbSelected)
129 return;
131 // tdf#141396: the color must be different from the rest of the selected tab
132 Color aLineColor = (mbCustomColored && maCustomColor != maSelectedColor)
133 ? maCustomColor
134 : mrStyleSettings.GetDarkShadowColor();
135 mrRenderContext.SetFillColor(aLineColor);
136 mrRenderContext.SetLineColor(aLineColor);
137 mrRenderContext.DrawRect(maLineRect);
140 void drawSeparator()
142 const tools::Long cMargin = 5;
143 const tools::Long aRight( maRect.Right() - 1 );
144 mrRenderContext.SetLineColor(mrStyleSettings.GetShadowColor());
145 mrRenderContext.DrawLine(Point(aRight, maRect.Top() + cMargin),
146 Point(aRight, maRect.Bottom() - cMargin));
149 void drawTab()
151 drawOuterFrame();
152 drawColorLine();
153 if (!mbSelected && !mbCustomColored)
154 drawSeparator();
155 if (mbProtect)
157 BitmapEx aBitmap(BMP_TAB_LOCK);
158 Point aPosition = maRect.TopLeft();
159 aPosition.AdjustX(2);
160 aPosition.AdjustY((maRect.getOpenHeight() - aBitmap.GetSizePixel().Height()) / 2);
161 mrRenderContext.DrawBitmapEx(aPosition, aBitmap);
165 void setRect(const tools::Rectangle& rRect)
167 maLineRect = tools::Rectangle(rRect.BottomLeft(), rRect.BottomRight());
168 maLineRect.AdjustTop(-2);
169 maRect = rRect;
172 void setSelected(bool bSelected)
174 mbSelected = bSelected;
177 void setCustomColored(bool bCustomColored)
179 mbCustomColored = bCustomColored;
182 void setEnabled(bool bEnabled)
184 mbEnabled = bEnabled;
187 void setSelectedFillColor(const Color& rColor)
189 maSelectedColor = rColor;
192 void setCustomColor(const Color& rColor)
194 maCustomColor = rColor;
198 } // anonymous namespace
200 struct ImplTabBarItem
202 sal_uInt16 mnId;
203 TabBarPageBits mnBits;
204 OUString maText;
205 OUString maHelpText;
206 OUString maAuxiliaryText; // used in LayerTabBar for real layer name
207 tools::Rectangle maRect;
208 tools::Long mnWidth;
209 OString maHelpId;
210 bool mbShort : 1;
211 bool mbSelect : 1;
212 bool mbProtect : 1;
213 Color maTabBgColor;
214 Color maTabTextColor;
216 ImplTabBarItem(sal_uInt16 nItemId, OUString aText, TabBarPageBits nPageBits)
217 : mnId(nItemId)
218 , mnBits(nPageBits)
219 , maText(std::move(aText))
220 , mnWidth(0)
221 , mbShort(false)
222 , mbSelect(false)
223 , mbProtect(false)
224 , maTabBgColor(COL_AUTO)
225 , maTabTextColor(COL_AUTO)
229 bool IsDefaultTabBgColor() const
231 return maTabBgColor == COL_AUTO;
234 bool IsSelected(ImplTabBarItem const * pCurItem) const
236 return mbSelect || (pCurItem == this);
239 OUString const & GetRenderText() const
241 return maText;
245 class ImplTabSizer : public vcl::Window
247 public:
248 ImplTabSizer( TabBar* pParent, WinBits nWinStyle );
250 TabBar* GetParent() const { return static_cast<TabBar*>(Window::GetParent()); }
252 private:
253 void ImplTrack( const Point& rScreenPos );
255 virtual void MouseButtonDown( const MouseEvent& rMEvt ) override;
256 virtual void Tracking( const TrackingEvent& rTEvt ) override;
257 virtual void Paint( vcl::RenderContext& /*rRenderContext*/, const tools::Rectangle& rRect ) override;
259 Point maStartPos;
260 tools::Long mnStartWidth;
263 ImplTabSizer::ImplTabSizer( TabBar* pParent, WinBits nWinStyle )
264 : Window( pParent, nWinStyle & WB_3DLOOK )
265 , mnStartWidth(0)
267 SetPointer(PointerStyle::HSizeBar);
268 SetSizePixel(Size(7 * GetDPIScaleFactor(), 0));
271 void ImplTabSizer::ImplTrack( const Point& rScreenPos )
273 TabBar* pParent = GetParent();
274 tools::Long nDiff = rScreenPos.X() - maStartPos.X();
275 pParent->mnSplitSize = mnStartWidth + (pParent->IsMirrored() ? -nDiff : nDiff);
276 if ( pParent->mnSplitSize < TABBAR_MINSIZE )
277 pParent->mnSplitSize = TABBAR_MINSIZE;
278 pParent->Split();
279 pParent->PaintImmediately();
282 void ImplTabSizer::MouseButtonDown( const MouseEvent& rMEvt )
284 if ( GetParent()->IsInEditMode() )
286 GetParent()->EndEditMode();
287 return;
290 if ( rMEvt.IsLeft() )
292 maStartPos = OutputToScreenPixel( rMEvt.GetPosPixel() );
293 mnStartWidth = GetParent()->GetSizePixel().Width();
294 StartTracking();
298 void ImplTabSizer::Tracking( const TrackingEvent& rTEvt )
300 if ( rTEvt.IsTrackingEnded() )
302 if ( rTEvt.IsTrackingCanceled() )
303 ImplTrack( maStartPos );
304 GetParent()->mnSplitSize = 0;
306 else
307 ImplTrack( OutputToScreenPixel( rTEvt.GetMouseEvent().GetPosPixel() ) );
310 void ImplTabSizer::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& )
312 DecorationView aDecoView(&rRenderContext);
313 tools::Rectangle aOutputRect(Point(0, 0), GetOutputSizePixel());
314 aDecoView.DrawHandle(aOutputRect);
317 namespace {
319 // Is not named Impl. as it may be both instantiated and derived from
320 class TabBarEdit final : public InterimItemWindow
322 private:
323 std::unique_ptr<weld::Entry> m_xEntry;
324 Idle maLoseFocusIdle;
325 bool mbPostEvt;
327 DECL_LINK( ImplEndEditHdl, void*, void );
328 DECL_LINK( ImplEndTimerHdl, Timer*, void );
329 DECL_LINK( ActivatedHdl, weld::Entry&, bool );
330 DECL_LINK( KeyInputHdl, const KeyEvent&, bool );
331 DECL_LINK( FocusOutHdl, weld::Widget&, void );
333 public:
334 TabBarEdit(TabBar* pParent);
335 virtual void dispose() override;
337 TabBar* GetParent() const { return static_cast<TabBar*>(Window::GetParent()); }
339 weld::Entry& get_widget() { return *m_xEntry; }
341 void SetPostEvent() { mbPostEvt = true; }
342 void ResetPostEvent() { mbPostEvt = false; }
347 TabBarEdit::TabBarEdit(TabBar* pParent)
348 : InterimItemWindow(pParent, "svt/ui/tabbaredit.ui", "TabBarEdit")
349 , m_xEntry(m_xBuilder->weld_entry("entry"))
350 , maLoseFocusIdle( "svtools::TabBarEdit maLoseFocusIdle" )
352 InitControlBase(m_xEntry.get());
354 mbPostEvt = false;
355 maLoseFocusIdle.SetPriority( TaskPriority::REPAINT );
356 maLoseFocusIdle.SetInvokeHandler( LINK( this, TabBarEdit, ImplEndTimerHdl ) );
358 m_xEntry->connect_activate(LINK(this, TabBarEdit, ActivatedHdl));
359 m_xEntry->connect_key_press(LINK(this, TabBarEdit, KeyInputHdl));
360 m_xEntry->connect_focus_out(LINK(this, TabBarEdit, FocusOutHdl));
363 void TabBarEdit::dispose()
365 m_xEntry.reset();
366 InterimItemWindow::dispose();
369 IMPL_LINK_NOARG(TabBarEdit, ActivatedHdl, weld::Entry&, bool)
371 if ( !mbPostEvt )
373 if ( PostUserEvent( LINK( this, TabBarEdit, ImplEndEditHdl ), reinterpret_cast<void*>(false), true ) )
374 mbPostEvt = true;
376 return true;
379 IMPL_LINK(TabBarEdit, KeyInputHdl, const KeyEvent&, rKEvt, bool)
381 if (!rKEvt.GetKeyCode().GetModifier() && rKEvt.GetKeyCode().GetCode() == KEY_ESCAPE)
383 if ( !mbPostEvt )
385 if ( PostUserEvent( LINK( this, TabBarEdit, ImplEndEditHdl ), reinterpret_cast<void*>(true), true ) )
386 mbPostEvt = true;
388 return true;
390 return false;
393 IMPL_LINK_NOARG(TabBarEdit, FocusOutHdl, weld::Widget&, void)
395 if ( !mbPostEvt )
397 if ( PostUserEvent( LINK( this, TabBarEdit, ImplEndEditHdl ), reinterpret_cast<void*>(false), true ) )
398 mbPostEvt = true;
402 IMPL_LINK( TabBarEdit, ImplEndEditHdl, void*, pCancel, void )
404 ResetPostEvent();
405 maLoseFocusIdle.Stop();
407 // tdf#156958: when renaming and clicking on canvas, LO goes into GetParent()->EndEditMode first time
408 // then it calls TabBarEdit::dispose method which resets m_xEntry BUT, on the same thread, LO comes here again
409 // so return if already disposed to avoid a crash
410 if (isDisposed())
411 return;
413 // We need this query, because the edit gets a losefocus event,
414 // when it shows the context menu or the insert symbol dialog
415 if (!m_xEntry->has_focus() && m_xEntry->has_child_focus())
416 maLoseFocusIdle.Start();
417 else
418 GetParent()->EndEditMode( pCancel != nullptr );
421 IMPL_LINK_NOARG(TabBarEdit, ImplEndTimerHdl, Timer *, void)
423 if (m_xEntry->has_focus())
424 return;
426 // We need this query, because the edit gets a losefocus event,
427 // when it shows the context menu or the insert symbol dialog
428 if (m_xEntry->has_child_focus())
429 maLoseFocusIdle.Start();
430 else
431 GetParent()->EndEditMode( true );
434 namespace {
436 class TabButtons final : public InterimItemWindow
438 public:
439 std::unique_ptr<weld::Button> m_xFirstButton;
440 std::unique_ptr<weld::Button> m_xPrevButton;
441 std::unique_ptr<weld::Button> m_xNextButton;
442 std::unique_ptr<weld::Button> m_xLastButton;
443 std::unique_ptr<weld::Button> m_xAddButton;
444 std::shared_ptr<weld::ButtonPressRepeater> m_xAddRepeater;
445 std::shared_ptr<weld::ButtonPressRepeater> m_xPrevRepeater;
446 std::shared_ptr<weld::ButtonPressRepeater> m_xNextRepeater;
448 TabButtons(TabBar* pParent, bool bSheets)
449 : InterimItemWindow(pParent,
450 pParent->IsMirrored() ? OUString("svt/ui/tabbuttonsmirrored.ui")
451 : OUString("svt/ui/tabbuttons.ui"),
452 "TabButtons")
453 , m_xFirstButton(m_xBuilder->weld_button("first"))
454 , m_xPrevButton(m_xBuilder->weld_button("prev"))
455 , m_xNextButton(m_xBuilder->weld_button("next"))
456 , m_xLastButton(m_xBuilder->weld_button("last"))
457 , m_xAddButton(m_xBuilder->weld_button("add"))
459 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
460 SetPaintTransparent(false);
461 SetBackground(rStyleSettings.GetFaceColor());
463 m_xFirstButton->set_accessible_name(SvtResId(STR_TABBAR_PUSHBUTTON_MOVET0HOME));
464 m_xPrevButton->set_accessible_name(SvtResId(STR_TABBAR_PUSHBUTTON_MOVELEFT));
465 m_xNextButton->set_accessible_name(SvtResId(STR_TABBAR_PUSHBUTTON_MOVERIGHT));
466 m_xLastButton->set_accessible_name(SvtResId(STR_TABBAR_PUSHBUTTON_MOVETOEND));
467 m_xAddButton->set_accessible_name(SvtResId(STR_TABBAR_PUSHBUTTON_ADDTAB));
469 if (bSheets)
471 m_xFirstButton->set_tooltip_text(SvtResId(STR_TABBAR_HINT_MOVETOHOME_SHEETS));
472 m_xPrevButton->set_tooltip_text(SvtResId(STR_TABBAR_HINT_MOVELEFT_SHEETS));
473 m_xNextButton->set_tooltip_text(SvtResId(STR_TABBAR_HINT_MOVERIGHT_SHEETS));
474 m_xLastButton->set_tooltip_text(SvtResId(STR_TABBAR_HINT_MOVETOEND_SHEETS));
475 m_xAddButton->set_tooltip_text(SvtResId(STR_TABBAR_HINT_ADDTAB_SHEETS));
479 void AdaptToHeight(int nHeight)
481 if (m_xFirstButton->get_preferred_size() == Size(nHeight, nHeight))
482 return;
483 m_xFirstButton->set_size_request(nHeight, nHeight);
484 m_xPrevButton->set_size_request(nHeight, nHeight);
485 m_xNextButton->set_size_request(nHeight, nHeight);
486 m_xLastButton->set_size_request(nHeight, nHeight);
487 m_xAddButton->set_size_request(nHeight, nHeight);
488 InvalidateChildSizeCache();
491 virtual void dispose() override
493 m_xNextRepeater.reset();
494 m_xPrevRepeater.reset();
495 m_xAddRepeater.reset();
496 m_xAddButton.reset();
497 m_xLastButton.reset();
498 m_xNextButton.reset();
499 m_xPrevButton.reset();
500 m_xFirstButton.reset();
501 InterimItemWindow::dispose();
507 struct TabBar_Impl
509 ScopedVclPtr<ImplTabSizer> mpSizer;
510 ScopedVclPtr<TabButtons> mxButtonBox;
511 ScopedVclPtr<TabBarEdit> mxEdit;
512 std::vector<ImplTabBarItem> maItemList;
514 vcl::AccessibleFactoryAccess maAccessibleFactory;
516 sal_uInt16 getItemSize() const
518 return static_cast<sal_uInt16>(maItemList.size());
522 TabBar::TabBar( vcl::Window* pParent, WinBits nWinStyle, bool bSheets ) :
523 Window( pParent, (nWinStyle & WB_3DLOOK) | WB_CLIPCHILDREN )
525 ImplInit( nWinStyle, bSheets );
526 maCurrentItemList = 0;
529 TabBar::~TabBar()
531 disposeOnce();
534 void TabBar::dispose()
536 EndEditMode( true );
537 mpImpl.reset();
538 Window::dispose();
541 const sal_uInt16 TabBar::APPEND = ::std::numeric_limits<sal_uInt16>::max();
542 const sal_uInt16 TabBar::PAGE_NOT_FOUND = ::std::numeric_limits<sal_uInt16>::max();
544 void TabBar::ImplInit( WinBits nWinStyle, bool bSheets )
546 mpImpl.reset(new TabBar_Impl);
548 mnMaxPageWidth = 0;
549 mnCurMaxWidth = 0;
550 mnOffX = 0;
551 mnOffY = 0;
552 mnLastOffX = 0;
553 mnSplitSize = 0;
554 mnSwitchTime = 0;
555 mnWinStyle = nWinStyle;
556 mnCurPageId = 0;
557 mnFirstPos = 0;
558 mnDropPos = 0;
559 mnSwitchId = 0;
560 mnEditId = 0;
561 mbFormat = true;
562 mbFirstFormat = true;
563 mbSizeFormat = true;
564 mbAutoEditMode = false;
565 mbEditCanceled = false;
566 mbDropPos = false;
567 mbInSelect = false;
568 mbMirrored = false;
569 mbScrollAlwaysEnabled = false;
570 mbSheets = bSheets;
572 ImplInitControls();
574 SetSizePixel( Size( 100, CalcWindowSizePixel().Height() ) );
575 ImplInitSettings( true, true );
578 ImplTabBarItem* TabBar::seek( size_t i )
580 if ( i < mpImpl->maItemList.size() )
582 maCurrentItemList = i;
583 return &mpImpl->maItemList[maCurrentItemList];
585 return nullptr;
588 ImplTabBarItem* TabBar::prev()
590 if ( maCurrentItemList > 0 )
592 return &mpImpl->maItemList[--maCurrentItemList];
594 return nullptr;
597 ImplTabBarItem* TabBar::next()
599 if ( maCurrentItemList + 1 < mpImpl->maItemList.size() )
601 return &mpImpl->maItemList[++maCurrentItemList];
603 return nullptr;
606 void TabBar::ImplInitSettings( bool bFont, bool bBackground )
608 // FIXME RenderContext
610 const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
612 if (bFont)
614 vcl::Font aToolFont = rStyleSettings.GetToolFont();
615 aToolFont.SetWeight( WEIGHT_BOLD );
616 ApplyControlFont(*GetOutDev(), aToolFont);
618 // Adapt font size if window too small?
619 while (GetTextHeight() > (GetOutputSizePixel().Height() - 1))
621 vcl::Font aFont = GetFont();
622 if (aFont.GetFontHeight() <= 6)
623 break;
624 aFont.SetFontHeight(aFont.GetFontHeight() - 1);
625 SetFont(aFont);
629 if (bBackground)
631 ApplyControlBackground(*GetOutDev(), rStyleSettings.GetFaceColor());
635 void TabBar::ImplGetColors(const StyleSettings& rStyleSettings,
636 Color& rFaceColor, Color& rFaceTextColor,
637 Color& rSelectColor, Color& rSelectTextColor)
639 if (IsControlBackground())
640 rFaceColor = GetControlBackground();
641 else
642 rFaceColor = rStyleSettings.GetInactiveTabColor();
643 if (IsControlForeground())
644 rFaceTextColor = GetControlForeground();
645 else
646 rFaceTextColor = rStyleSettings.GetButtonTextColor();
647 rSelectColor = rStyleSettings.GetActiveTabColor();
648 rSelectTextColor = rStyleSettings.GetWindowTextColor();
651 bool TabBar::ImplCalcWidth()
653 // Sizes should only be retrieved if the text or the font was changed
654 if (!mbSizeFormat)
655 return false;
657 // retrieve width of tabs with bold font
658 vcl::Font aFont = GetFont();
659 if (aFont.GetWeight() != WEIGHT_BOLD)
661 aFont.SetWeight(WEIGHT_BOLD);
662 SetFont(aFont);
665 if (mnMaxPageWidth)
666 mnCurMaxWidth = mnMaxPageWidth;
667 else
669 mnCurMaxWidth = mnLastOffX - mnOffX;
670 if (mnCurMaxWidth < 1)
671 mnCurMaxWidth = 1;
674 bool bChanged = false;
675 for (auto& rItem : mpImpl->maItemList)
677 tools::Long nNewWidth = GetTextWidth(rItem.GetRenderText());
678 if (mnCurMaxWidth && (nNewWidth > mnCurMaxWidth))
680 rItem.mbShort = true;
681 nNewWidth = mnCurMaxWidth;
683 else
685 rItem.mbShort = false;
688 // Padding is dependent on font height - bigger font = bigger padding
689 tools::Long nFontWidth = aFont.GetFontHeight();
690 if (rItem.mbProtect)
691 nNewWidth += 24;
692 nNewWidth += nFontWidth * 2;
694 if (rItem.mnWidth != nNewWidth)
696 rItem.mnWidth = nNewWidth;
697 if (!rItem.maRect.IsEmpty())
698 bChanged = true;
701 mbSizeFormat = false;
702 mbFormat = true;
703 return bChanged;
706 void TabBar::ImplFormat()
708 ImplCalcWidth();
710 if (!mbFormat)
711 return;
713 tools::Long x = mnOffX;
715 const size_t nItemListSize = mpImpl->maItemList.size();
716 for (size_t nItemIndex = 0; nItemIndex < nItemListSize; nItemIndex++)
718 auto& rItem = mpImpl->maItemList[nItemIndex];
720 // At all non-visible tabs an empty rectangle is set
721 if ((nItemIndex + 1 < mnFirstPos) || (x > mnLastOffX))
722 rItem.maRect.SetEmpty();
723 else
725 // Slightly before the tab before the first visible page
726 // should also be visible
727 if (nItemIndex + 1 == mnFirstPos)
729 rItem.maRect.SetLeft(x - rItem.mnWidth);
731 else
733 rItem.maRect.SetLeft(x);
734 x += rItem.mnWidth;
736 rItem.maRect.SetRight(x);
737 rItem.maRect.SetBottom(maWinSize.Height() - 1);
739 if (mbMirrored)
741 tools::Long nNewLeft = mnOffX + mnLastOffX - rItem.maRect.Right();
742 tools::Long nNewRight = mnOffX + mnLastOffX - rItem.maRect.Left();
743 rItem.maRect.SetRight(nNewRight);
744 rItem.maRect.SetLeft(nNewLeft);
749 mbFormat = false;
751 // enable/disable button
752 ImplEnableControls();
755 sal_uInt16 TabBar::ImplGetLastFirstPos()
757 sal_uInt16 nCount = mpImpl->getItemSize();
758 if (!nCount || mbSizeFormat || mbFormat)
759 return 0;
761 sal_uInt16 nLastFirstPos = nCount - 1;
762 tools::Long nWinWidth = mnLastOffX - mnOffX - ADDNEWPAGE_AREAWIDTH;
763 tools::Long nWidth = mpImpl->maItemList[nLastFirstPos].mnWidth;
765 while (nLastFirstPos && (nWidth < nWinWidth))
767 nLastFirstPos--;
768 nWidth += mpImpl->maItemList[nLastFirstPos].mnWidth;
770 if ((nLastFirstPos != static_cast<sal_uInt16>(mpImpl->maItemList.size() - 1)) && (nWidth > nWinWidth))
771 nLastFirstPos++;
772 return nLastFirstPos;
775 IMPL_LINK(TabBar, ContextMenuHdl, const CommandEvent&, rCommandEvent, void)
777 maScrollAreaContextHdl.Call(rCommandEvent);
780 IMPL_LINK(TabBar, MousePressHdl, const MouseEvent&, rMouseEvent, bool)
782 if (rMouseEvent.IsRight())
783 ContextMenuHdl(CommandEvent(rMouseEvent.GetPosPixel(), CommandEventId::ContextMenu, true));
784 return false;
787 void TabBar::ImplInitControls()
789 if (mnWinStyle & WB_SIZEABLE)
791 if (!mpImpl->mpSizer)
793 mpImpl->mpSizer.disposeAndReset(VclPtr<ImplTabSizer>::Create( this, mnWinStyle & (WB_DRAG | WB_3DLOOK)));
795 mpImpl->mpSizer->Show();
797 else
799 mpImpl->mpSizer.disposeAndClear();
802 mpImpl->mxButtonBox.disposeAndReset(VclPtr<TabButtons>::Create(this, mbSheets));
804 Link<const CommandEvent&, void> aContextLink = LINK( this, TabBar, ContextMenuHdl );
806 if (mnWinStyle & WB_INSERTTAB)
808 Link<weld::Button&,void> aLink = LINK(this, TabBar, ImplAddClickHandler);
809 mpImpl->mxButtonBox->m_xAddRepeater = std::make_shared<weld::ButtonPressRepeater>(
810 *mpImpl->mxButtonBox->m_xAddButton, aLink, aContextLink);
811 mpImpl->mxButtonBox->m_xAddButton->show();
814 Link<weld::Button&,void> aLink = LINK( this, TabBar, ImplClickHdl );
816 if (mnWinStyle & (WB_MINSCROLL | WB_SCROLL))
818 mpImpl->mxButtonBox->m_xPrevRepeater = std::make_shared<weld::ButtonPressRepeater>(
819 *mpImpl->mxButtonBox->m_xPrevButton, aLink, aContextLink);
820 mpImpl->mxButtonBox->m_xPrevButton->show();
821 mpImpl->mxButtonBox->m_xNextRepeater = std::make_shared<weld::ButtonPressRepeater>(
822 *mpImpl->mxButtonBox->m_xNextButton, aLink, aContextLink);
823 mpImpl->mxButtonBox->m_xNextButton->show();
826 if (mnWinStyle & WB_SCROLL)
828 Link<const MouseEvent&, bool> aBtnContextLink = LINK(this, TabBar, MousePressHdl);
830 mpImpl->mxButtonBox->m_xFirstButton->connect_clicked(aLink);
831 mpImpl->mxButtonBox->m_xFirstButton->connect_mouse_press(aBtnContextLink);
832 mpImpl->mxButtonBox->m_xFirstButton->show();
833 mpImpl->mxButtonBox->m_xLastButton->connect_clicked(aLink);
834 mpImpl->mxButtonBox->m_xLastButton->connect_mouse_press(aBtnContextLink);
835 mpImpl->mxButtonBox->m_xLastButton->show();
838 mpImpl->mxButtonBox->Show();
841 void TabBar::ImplEnableControls()
843 if (mbSizeFormat || mbFormat)
844 return;
846 // enable/disable buttons
847 bool bEnableBtn = mbScrollAlwaysEnabled || mnFirstPos > 0;
848 mpImpl->mxButtonBox->m_xFirstButton->set_sensitive(bEnableBtn);
849 mpImpl->mxButtonBox->m_xPrevButton->set_sensitive(bEnableBtn);
850 if (!bEnableBtn && mpImpl->mxButtonBox->m_xPrevRepeater)
851 mpImpl->mxButtonBox->m_xPrevRepeater->Stop();
852 bEnableBtn = mbScrollAlwaysEnabled || mnFirstPos < ImplGetLastFirstPos();
853 mpImpl->mxButtonBox->m_xLastButton->set_sensitive(bEnableBtn);
854 mpImpl->mxButtonBox->m_xNextButton->set_sensitive(bEnableBtn);
855 if (!bEnableBtn && mpImpl->mxButtonBox->m_xNextRepeater)
856 mpImpl->mxButtonBox->m_xNextRepeater->Stop();
859 void TabBar::SetScrollAlwaysEnabled(bool bScrollAlwaysEnabled)
861 mbScrollAlwaysEnabled = bScrollAlwaysEnabled;
862 ImplEnableControls();
865 void TabBar::ImplShowPage( sal_uInt16 nPos )
867 if (nPos >= mpImpl->getItemSize())
868 return;
870 // calculate width
871 tools::Long nWidth = GetOutputSizePixel().Width();
873 auto& rItem = mpImpl->maItemList[nPos];
874 if (nPos < mnFirstPos)
875 SetFirstPageId( rItem.mnId );
876 else if (rItem.maRect.Right() > nWidth)
878 while (rItem.maRect.Right() > nWidth)
880 sal_uInt16 nNewPos = mnFirstPos + 1;
881 SetFirstPageId(GetPageId(nNewPos));
882 ImplFormat();
883 if (nNewPos != mnFirstPos)
884 break;
889 IMPL_LINK( TabBar, ImplClickHdl, weld::Button&, rBtn, void )
891 if (&rBtn != mpImpl->mxButtonBox->m_xFirstButton.get() && &rBtn != mpImpl->mxButtonBox->m_xLastButton.get())
893 if ((GetPointerState().mnState & (MOUSE_LEFT | MOUSE_MIDDLE | MOUSE_RIGHT)) == 0)
895 // like tdf#149482 if we didn't see a mouse up, but find that the mouse is no
896 // longer pressed at this point, then bail
897 mpImpl->mxButtonBox->m_xPrevRepeater->Stop();
898 mpImpl->mxButtonBox->m_xNextRepeater->Stop();
899 return;
903 EndEditMode();
905 sal_uInt16 nNewPos = mnFirstPos;
907 if (&rBtn == mpImpl->mxButtonBox->m_xFirstButton.get() || (&rBtn == mpImpl->mxButtonBox->m_xPrevButton.get() &&
908 mpImpl->mxButtonBox->m_xPrevRepeater->IsModKeyPressed()))
910 nNewPos = 0;
912 else if (&rBtn == mpImpl->mxButtonBox->m_xLastButton.get() || (&rBtn == mpImpl->mxButtonBox->m_xNextButton.get() &&
913 mpImpl->mxButtonBox->m_xNextRepeater->IsModKeyPressed()))
915 sal_uInt16 nCount = GetPageCount();
916 if (nCount)
917 nNewPos = nCount - 1;
919 else if (&rBtn == mpImpl->mxButtonBox->m_xPrevButton.get())
921 if (mnFirstPos)
922 nNewPos = mnFirstPos - 1;
924 else if (&rBtn == mpImpl->mxButtonBox->m_xNextButton.get())
926 sal_uInt16 nCount = GetPageCount();
927 if (mnFirstPos < nCount)
928 nNewPos = mnFirstPos+1;
930 else
932 return;
935 if (nNewPos != mnFirstPos)
936 SetFirstPageId(GetPageId(nNewPos));
939 IMPL_LINK_NOARG(TabBar, ImplAddClickHandler, weld::Button&, void)
941 if ((GetPointerState().mnState & (MOUSE_LEFT | MOUSE_MIDDLE | MOUSE_RIGHT)) == 0)
943 // tdf#149482 if we didn't see a mouse up, but find that the mouse is no
944 // longer pressed at this point, then bail
945 mpImpl->mxButtonBox->m_xAddRepeater->Stop();
946 return;
949 EndEditMode();
950 AddTabClick();
953 void TabBar::MouseMove(const MouseEvent& rMEvt)
955 if (rMEvt.IsLeaveWindow())
956 mbInSelect = false;
958 Window::MouseMove(rMEvt);
961 void TabBar::MouseButtonDown(const MouseEvent& rMEvt)
963 // Only terminate EditMode and do not execute click
964 // if clicked inside our window,
965 if (IsInEditMode())
967 EndEditMode();
968 return;
971 sal_uInt16 nSelId = GetPageId(rMEvt.GetPosPixel());
973 if (!rMEvt.IsLeft())
975 Window::MouseButtonDown(rMEvt);
976 if (nSelId > 0 && nSelId != mnCurPageId)
978 if (ImplDeactivatePage())
980 SetCurPageId(nSelId);
981 PaintImmediately();
982 ImplActivatePage();
983 ImplSelect();
985 mbInSelect = true;
987 return;
990 if (rMEvt.IsMod2() && mbAutoEditMode && nSelId)
992 if (StartEditMode(nSelId))
993 return;
996 if ((rMEvt.GetMode() & (MouseEventModifiers::MULTISELECT | MouseEventModifiers::RANGESELECT)) && (rMEvt.GetClicks() == 1))
998 if (nSelId)
1000 sal_uInt16 nPos = GetPagePos(nSelId);
1002 bool bSelectTab = false;
1004 if ((rMEvt.GetMode() & MouseEventModifiers::MULTISELECT) && (mnWinStyle & WB_MULTISELECT))
1006 if (nSelId != mnCurPageId)
1008 SelectPage(nSelId, !IsPageSelected(nSelId));
1009 bSelectTab = true;
1012 else if (mnWinStyle & (WB_MULTISELECT | WB_RANGESELECT))
1014 bSelectTab = true;
1015 sal_uInt16 n;
1016 bool bSelect;
1017 sal_uInt16 nCurPos = GetPagePos(mnCurPageId);
1018 if (nPos <= nCurPos)
1020 // Deselect all tabs till the clicked tab
1021 // and select all tabs from the clicked tab
1022 // 'till the actual position
1023 n = 0;
1024 while (n < nCurPos)
1026 auto& rItem = mpImpl->maItemList[n];
1027 bSelect = n >= nPos;
1029 if (rItem.mbSelect != bSelect)
1031 rItem.mbSelect = bSelect;
1032 if (!rItem.maRect.IsEmpty())
1033 Invalidate(rItem.maRect);
1036 n++;
1040 if (nPos >= nCurPos)
1042 // Select all tabs from the actual position till the clicked tab
1043 // and deselect all tabs from the actual position
1044 // till the last tab
1045 sal_uInt16 nCount = mpImpl->getItemSize();
1046 n = nCurPos;
1047 while (n < nCount)
1049 auto& rItem = mpImpl->maItemList[n];
1051 bSelect = n <= nPos;
1053 if (rItem.mbSelect != bSelect)
1055 rItem.mbSelect = bSelect;
1056 if (!rItem.maRect.IsEmpty())
1057 Invalidate(rItem.maRect);
1060 n++;
1065 // scroll the selected tab if required
1066 if (bSelectTab)
1068 ImplShowPage(nPos);
1069 PaintImmediately();
1070 ImplSelect();
1073 mbInSelect = true;
1075 return;
1078 else if (rMEvt.GetClicks() == 2)
1080 // call double-click-handler if required
1081 if (!rMEvt.GetModifier() && (!nSelId || (nSelId == mnCurPageId)))
1083 sal_uInt16 nOldCurId = mnCurPageId;
1084 mnCurPageId = nSelId;
1085 DoubleClick();
1086 // check, as actual page could be switched inside
1087 // the doubleclick-handler
1088 if (mnCurPageId == nSelId)
1089 mnCurPageId = nOldCurId;
1092 return;
1094 else
1096 if (nSelId)
1098 // execute Select if not actual page
1099 if (nSelId != mnCurPageId)
1101 sal_uInt16 nPos = GetPagePos(nSelId);
1102 auto& rItem = mpImpl->maItemList[nPos];
1104 if (!rItem.mbSelect)
1106 // make not valid
1107 bool bUpdate = false;
1108 if (IsReallyVisible() && IsUpdateMode())
1109 bUpdate = true;
1111 // deselect all selected items
1112 for (auto& xItem : mpImpl->maItemList)
1114 if (xItem.mbSelect || (xItem.mnId == mnCurPageId))
1116 xItem.mbSelect = false;
1117 if (bUpdate)
1118 Invalidate(xItem.maRect);
1123 if (ImplDeactivatePage())
1125 SetCurPageId(nSelId);
1126 PaintImmediately();
1127 ImplActivatePage();
1128 ImplSelect();
1131 mbInSelect = true;
1134 return;
1138 Window::MouseButtonDown(rMEvt);
1141 void TabBar::MouseButtonUp(const MouseEvent& rMEvt)
1143 mbInSelect = false;
1144 Window::MouseButtonUp(rMEvt);
1147 void TabBar::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rect)
1149 if (rRenderContext.IsNativeControlSupported(ControlType::WindowBackground,ControlPart::Entire))
1151 rRenderContext.DrawNativeControl(ControlType::WindowBackground,ControlPart::Entire,rect,
1152 ControlState::ENABLED,ImplControlValue(0),OUString());
1154 // calculate items and emit
1155 sal_uInt16 nItemCount = mpImpl->getItemSize();
1156 if (!nItemCount)
1157 return;
1159 ImplPrePaint();
1161 Color aFaceColor, aSelectColor, aFaceTextColor, aSelectTextColor;
1162 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
1163 ImplGetColors(rStyleSettings, aFaceColor, aFaceTextColor, aSelectColor, aSelectTextColor);
1165 rRenderContext.Push(vcl::PushFlags::FONT | vcl::PushFlags::CLIPREGION);
1166 rRenderContext.SetClipRegion(vcl::Region(GetPageArea()));
1168 // select font
1169 vcl::Font aFont = rRenderContext.GetFont();
1170 vcl::Font aLightFont = aFont;
1171 aLightFont.SetWeight(WEIGHT_NORMAL);
1173 TabDrawer aDrawer(rRenderContext);
1175 aDrawer.setSelectedFillColor(aSelectColor);
1177 // Now, start drawing the tabs.
1179 ImplTabBarItem* pItem = ImplGetLastTabBarItem(nItemCount);
1180 ImplTabBarItem* pCurItem = nullptr;
1181 while (pItem)
1183 // emit CurrentItem last, as it covers all others
1184 if (!pCurItem && (pItem->mnId == mnCurPageId))
1186 pCurItem = pItem;
1187 pItem = prev();
1188 if (!pItem)
1189 pItem = pCurItem;
1190 continue;
1193 bool bCurrent = pItem == pCurItem;
1195 if (!pItem->maRect.IsEmpty())
1197 tools::Rectangle aRect = pItem->maRect;
1198 bool bSelected = pItem->IsSelected(pCurItem);
1199 // We disable custom background color in high contrast mode.
1200 bool bCustomBgColor = !pItem->IsDefaultTabBgColor() && !rStyleSettings.GetHighContrastMode();
1201 OUString aText = pItem->mbShort ?
1202 rRenderContext.GetEllipsisString(pItem->GetRenderText(), mnCurMaxWidth) :
1203 pItem->GetRenderText();
1205 aDrawer.setRect(aRect);
1206 aDrawer.setSelected(bSelected);
1207 aDrawer.setCustomColored(bCustomBgColor);
1208 aDrawer.setEnabled(true);
1209 aDrawer.setCustomColor(pItem->maTabBgColor);
1210 aDrawer.mbProtect = pItem->mbProtect;
1211 aDrawer.drawTab();
1213 // currently visible sheet is drawn using a bold font
1214 if (bCurrent)
1215 rRenderContext.SetFont(aFont);
1216 else
1217 rRenderContext.SetFont(aLightFont);
1219 // Set the correct FillInBrush depending on status
1221 if (bSelected)
1222 rRenderContext.SetTextColor(aSelectTextColor);
1223 else if (bCustomBgColor)
1224 rRenderContext.SetTextColor(pItem->maTabTextColor);
1225 else
1226 rRenderContext.SetTextColor(aFaceTextColor);
1228 // Special display of tab name depending on page bits
1230 if (pItem->mnBits & TabBarPageBits::Blue)
1232 rRenderContext.SetTextColor(COL_LIGHTBLUE);
1234 if (pItem->mnBits & TabBarPageBits::Italic)
1236 vcl::Font aSpecialFont = rRenderContext.GetFont();
1237 aSpecialFont.SetItalic(FontItalic::ITALIC_NORMAL);
1238 rRenderContext.SetFont(aSpecialFont);
1240 if (pItem->mnBits & TabBarPageBits::Underline)
1242 vcl::Font aSpecialFont = rRenderContext.GetFont();
1243 aSpecialFont.SetUnderline(LINESTYLE_SINGLE);
1244 rRenderContext.SetFont(aSpecialFont);
1247 aDrawer.drawText(aText);
1249 if (bCurrent)
1251 rRenderContext.SetLineColor();
1252 rRenderContext.SetFillColor(aSelectColor);
1253 aDrawer.drawOverTopBorder();
1254 break;
1257 pItem = prev();
1259 else
1261 if (bCurrent)
1262 break;
1264 pItem = nullptr;
1267 if (!pItem)
1268 pItem = pCurItem;
1270 rRenderContext.Pop();
1273 void TabBar::Resize()
1275 Size aNewSize = GetOutputSizePixel();
1277 tools::Long nSizerWidth = 0;
1279 // order the Sizer
1280 if ( mpImpl->mpSizer )
1282 Size aSizerSize = mpImpl->mpSizer->GetSizePixel();
1283 Point aNewSizerPos( mbMirrored ? 0 : (aNewSize.Width()-aSizerSize.Width()), 0 );
1284 Size aNewSizerSize( aSizerSize.Width(), aNewSize.Height() );
1285 mpImpl->mpSizer->SetPosSizePixel( aNewSizerPos, aNewSizerSize );
1286 nSizerWidth = aSizerSize.Width();
1289 // order the scroll buttons
1290 tools::Long const nHeight = aNewSize.Height();
1291 // adapt font height?
1292 ImplInitSettings( true, false );
1294 mpImpl->mxButtonBox->AdaptToHeight(nHeight);
1295 Size const aBtnsSize(mpImpl->mxButtonBox->get_preferred_size().Width(), nHeight);
1296 Point aPos(mbMirrored ? (aNewSize.Width() - aBtnsSize.Width()) : 0, 0);
1297 mpImpl->mxButtonBox->SetPosSizePixel(aPos, aBtnsSize);
1298 auto nButtonWidth = aBtnsSize.Width();
1300 // store size
1301 maWinSize = aNewSize;
1303 if( mbMirrored )
1305 mnOffX = nSizerWidth;
1306 mnLastOffX = maWinSize.Width() - nButtonWidth - 1;
1308 else
1310 mnOffX = nButtonWidth;
1311 mnLastOffX = maWinSize.Width() - nSizerWidth - 1;
1314 // reformat
1315 mbSizeFormat = true;
1316 if ( IsReallyVisible() )
1318 if ( ImplCalcWidth() )
1319 Invalidate();
1321 ImplFormat();
1323 // Ensure as many tabs as possible are visible:
1324 sal_uInt16 nLastFirstPos = ImplGetLastFirstPos();
1325 if ( mnFirstPos > nLastFirstPos )
1327 mnFirstPos = nLastFirstPos;
1328 mbFormat = true;
1329 Invalidate();
1331 // Ensure the currently selected page is visible
1332 ImplShowPage(GetPagePos(mnCurPageId));
1334 ImplFormat();
1337 // enable/disable button
1338 ImplEnableControls();
1341 bool TabBar::PreNotify(NotifyEvent& rNEvt)
1343 if (rNEvt.GetType() == NotifyEventType::COMMAND)
1345 if (rNEvt.GetCommandEvent()->GetCommand() == CommandEventId::Wheel)
1347 const CommandWheelData* pData = rNEvt.GetCommandEvent()->GetWheelData();
1348 sal_uInt16 nNewPos = mnFirstPos;
1349 if (pData->GetNotchDelta() > 0)
1351 if (mnFirstPos)
1352 nNewPos = mnFirstPos - 1;
1354 else if (pData->GetNotchDelta() < 0)
1356 sal_uInt16 nCount = GetPageCount();
1357 if (mnFirstPos < nCount)
1358 nNewPos = mnFirstPos + 1;
1360 if (nNewPos != mnFirstPos)
1361 SetFirstPageId(GetPageId(nNewPos));
1364 return Window::PreNotify(rNEvt);
1367 void TabBar::RequestHelp(const HelpEvent& rHEvt)
1369 sal_uInt16 nItemId = GetPageId(ScreenToOutputPixel(rHEvt.GetMousePosPixel()));
1370 if (nItemId)
1372 if (rHEvt.GetMode() & HelpEventMode::BALLOON)
1374 OUString aStr = GetHelpText(nItemId);
1375 if (!aStr.isEmpty())
1377 tools::Rectangle aItemRect = GetPageRect(nItemId);
1378 Point aPt = OutputToScreenPixel(aItemRect.TopLeft());
1379 aItemRect.SetLeft( aPt.X() );
1380 aItemRect.SetTop( aPt.Y() );
1381 aPt = OutputToScreenPixel(aItemRect.BottomRight());
1382 aItemRect.SetRight( aPt.X() );
1383 aItemRect.SetBottom( aPt.Y() );
1384 Help::ShowBalloon(this, aItemRect.Center(), aItemRect, aStr);
1385 return;
1389 // show text for quick- or balloon-help
1390 // if this is isolated or not fully visible
1391 if (rHEvt.GetMode() & (HelpEventMode::QUICK | HelpEventMode::BALLOON))
1393 sal_uInt16 nPos = GetPagePos(nItemId);
1394 auto& rItem = mpImpl->maItemList[nPos];
1395 if (rItem.mbShort || (rItem.maRect.Right() - 5 > mnLastOffX))
1397 tools::Rectangle aItemRect = GetPageRect(nItemId);
1398 Point aPt = OutputToScreenPixel(aItemRect.TopLeft());
1399 aItemRect.SetLeft( aPt.X() );
1400 aItemRect.SetTop( aPt.Y() );
1401 aPt = OutputToScreenPixel(aItemRect.BottomRight());
1402 aItemRect.SetRight( aPt.X() );
1403 aItemRect.SetBottom( aPt.Y() );
1404 OUString aStr = mpImpl->maItemList[nPos].maText;
1405 if (!aStr.isEmpty())
1407 if (rHEvt.GetMode() & HelpEventMode::BALLOON)
1408 Help::ShowBalloon(this, aItemRect.Center(), aItemRect, aStr);
1409 else
1410 Help::ShowQuickHelp(this, aItemRect, aStr);
1411 return;
1417 Window::RequestHelp(rHEvt);
1420 void TabBar::StateChanged(StateChangedType nType)
1422 Window::StateChanged(nType);
1424 if (nType == StateChangedType::InitShow)
1426 if ( (mbSizeFormat || mbFormat) && !mpImpl->maItemList.empty() )
1427 ImplFormat();
1429 else if (nType == StateChangedType::Zoom ||
1430 nType == StateChangedType::ControlFont)
1432 ImplInitSettings(true, false);
1433 Invalidate();
1435 else if (nType == StateChangedType::ControlForeground)
1436 Invalidate();
1437 else if (nType == StateChangedType::ControlBackground)
1439 ImplInitSettings(false, true);
1440 Invalidate();
1442 else if (nType == StateChangedType::Mirroring)
1444 bool bIsRTLEnabled = IsRTLEnabled();
1445 // reacts on calls of EnableRTL, have to mirror all child controls
1446 if (mpImpl->mpSizer)
1447 mpImpl->mpSizer->EnableRTL(bIsRTLEnabled);
1448 if (mpImpl->mxButtonBox)
1450 mpImpl->mxButtonBox->m_xFirstButton->set_direction(bIsRTLEnabled);
1451 mpImpl->mxButtonBox->m_xPrevButton->set_direction(bIsRTLEnabled);
1452 mpImpl->mxButtonBox->m_xNextButton->set_direction(bIsRTLEnabled);
1453 mpImpl->mxButtonBox->m_xLastButton->set_direction(bIsRTLEnabled);
1454 mpImpl->mxButtonBox->m_xAddButton->set_direction(bIsRTLEnabled);
1456 if (mpImpl->mxEdit)
1458 weld::Entry& rEntry = mpImpl->mxEdit->get_widget();
1459 rEntry.set_direction(bIsRTLEnabled);
1464 void TabBar::DataChanged(const DataChangedEvent& rDCEvt)
1466 Window::DataChanged(rDCEvt);
1468 if (rDCEvt.GetType() == DataChangedEventType::FONTS
1469 || rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION
1470 || (rDCEvt.GetType() == DataChangedEventType::SETTINGS
1471 && rDCEvt.GetFlags() & AllSettingsFlags::STYLE))
1473 ImplInitSettings(true, true);
1474 Invalidate();
1478 void TabBar::ImplSelect()
1480 Select();
1481 CallEventListeners(VclEventId::TabbarPageSelected, reinterpret_cast<void*>(sal::static_int_cast<sal_IntPtr>(mnCurPageId)));
1484 void TabBar::Select()
1486 maSelectHdl.Call(this);
1489 void TabBar::DoubleClick()
1493 void TabBar::Split()
1495 maSplitHdl.Call(this);
1498 void TabBar::ImplActivatePage()
1500 ActivatePage();
1502 CallEventListeners(VclEventId::TabbarPageActivated, reinterpret_cast<void*>(sal::static_int_cast<sal_IntPtr>(mnCurPageId)));
1505 void TabBar::ActivatePage()
1508 bool TabBar::ImplDeactivatePage()
1510 bool bRet = DeactivatePage();
1512 CallEventListeners(VclEventId::TabbarPageDeactivated, reinterpret_cast<void*>(sal::static_int_cast<sal_IntPtr>(mnCurPageId)));
1514 return bRet;
1517 void TabBar::ImplPrePaint()
1519 sal_uInt16 nItemCount = mpImpl->getItemSize();
1520 if (!nItemCount)
1521 return;
1523 // tabbar should be formatted
1524 ImplFormat();
1526 // assure the actual tabpage becomes visible at first format
1527 if (!mbFirstFormat)
1528 return;
1530 mbFirstFormat = false;
1532 if (!mnCurPageId || (mnFirstPos != 0) || mbDropPos)
1533 return;
1535 auto& rItem = mpImpl->maItemList[GetPagePos(mnCurPageId)];
1536 if (rItem.maRect.IsEmpty())
1538 // set mbDropPos (or misuse) to prevent Invalidate()
1539 mbDropPos = true;
1540 SetFirstPageId(mnCurPageId);
1541 mbDropPos = false;
1542 if (mnFirstPos != 0)
1543 ImplFormat();
1547 ImplTabBarItem* TabBar::ImplGetLastTabBarItem( sal_uInt16 nItemCount )
1549 // find last visible entry
1550 sal_uInt16 n = mnFirstPos + 1;
1551 if (n >= nItemCount)
1552 n = nItemCount-1;
1553 ImplTabBarItem* pItem = seek(n);
1554 while (pItem)
1556 if (!pItem->maRect.IsEmpty())
1558 n++;
1559 pItem = next();
1561 else
1562 break;
1565 // draw all tabs (from back to front and actual last)
1566 if (pItem)
1567 n--;
1568 else if (n >= nItemCount)
1569 n = nItemCount-1;
1570 pItem = seek(n);
1571 return pItem;
1574 bool TabBar::DeactivatePage()
1576 return true;
1579 bool TabBar::StartRenaming()
1581 return true;
1584 TabBarAllowRenamingReturnCode TabBar::AllowRenaming()
1586 return TABBAR_RENAMING_YES;
1589 void TabBar::EndRenaming()
1593 void TabBar::Mirror()
1598 void TabBar::AddTabClick()
1603 void TabBar::InsertPage(sal_uInt16 nPageId, const OUString& rText,
1604 TabBarPageBits nBits, sal_uInt16 nPos)
1606 assert (nPageId && "TabBar::InsertPage(): PageId must not be 0");
1607 assert ((GetPagePos(nPageId) == PAGE_NOT_FOUND) && "TabBar::InsertPage(): Page already exists");
1608 assert ((nBits <= TPB_DISPLAY_NAME_ALLFLAGS) && "TabBar::InsertPage(): Invalid flag set in nBits");
1610 // create PageItem and insert in the item list
1611 ImplTabBarItem aItem( nPageId, rText, nBits );
1612 if (nPos < mpImpl->maItemList.size())
1614 auto it = mpImpl->maItemList.begin();
1615 it += nPos;
1616 mpImpl->maItemList.insert(it, aItem);
1618 else
1620 mpImpl->maItemList.push_back(aItem);
1622 mbSizeFormat = true;
1624 // set CurPageId if required
1625 if (!mnCurPageId)
1626 mnCurPageId = nPageId;
1628 // redraw bar
1629 if (IsReallyVisible() && IsUpdateMode())
1630 Invalidate();
1632 CallEventListeners(VclEventId::TabbarPageInserted, reinterpret_cast<void*>(sal::static_int_cast<sal_IntPtr>(nPageId)));
1635 Color TabBar::GetTabBgColor(sal_uInt16 nPageId) const
1637 sal_uInt16 nPos = GetPagePos(nPageId);
1639 if (nPos != PAGE_NOT_FOUND)
1640 return mpImpl->maItemList[nPos].maTabBgColor;
1641 else
1642 return COL_AUTO;
1645 void TabBar::SetTabBgColor(sal_uInt16 nPageId, const Color& aTabBgColor)
1647 sal_uInt16 nPos = GetPagePos(nPageId);
1648 if (nPos == PAGE_NOT_FOUND)
1649 return;
1651 auto& rItem = mpImpl->maItemList[nPos];
1652 if (aTabBgColor != COL_AUTO)
1654 rItem.maTabBgColor = aTabBgColor;
1655 if (aTabBgColor.GetLuminance() <= 128) //Do not use aTabBgColor.IsDark(), because that threshold is way too low...
1656 rItem.maTabTextColor = COL_WHITE;
1657 else
1658 rItem.maTabTextColor = COL_BLACK;
1660 else
1662 rItem.maTabBgColor = COL_AUTO;
1663 rItem.maTabTextColor = COL_AUTO;
1667 void TabBar::RemovePage(sal_uInt16 nPageId)
1669 sal_uInt16 nPos = GetPagePos(nPageId);
1671 // does item exist
1672 if (nPos == PAGE_NOT_FOUND)
1673 return;
1675 if (mnCurPageId == nPageId)
1676 mnCurPageId = 0;
1678 // check if first visible page should be moved
1679 if (mnFirstPos > nPos)
1680 mnFirstPos--;
1682 // delete item data
1683 auto it = mpImpl->maItemList.begin();
1684 it += nPos;
1685 mpImpl->maItemList.erase(it);
1687 // redraw bar
1688 if (IsReallyVisible() && IsUpdateMode())
1689 Invalidate();
1691 CallEventListeners(VclEventId::TabbarPageRemoved, reinterpret_cast<void*>(sal::static_int_cast<sal_IntPtr>(nPageId)));
1694 void TabBar::MovePage(sal_uInt16 nPageId, sal_uInt16 nNewPos)
1696 sal_uInt16 nPos = GetPagePos(nPageId);
1697 Pair aPair(nPos, nNewPos);
1699 if (nPos < nNewPos)
1700 nNewPos--;
1702 if (nPos == nNewPos)
1703 return;
1705 // does item exit
1706 if (nPos == PAGE_NOT_FOUND)
1707 return;
1709 // move tabbar item in the list
1710 auto it = mpImpl->maItemList.begin();
1711 it += nPos;
1712 ImplTabBarItem aItem = std::move(*it);
1713 mpImpl->maItemList.erase(it);
1714 if (nNewPos < mpImpl->maItemList.size())
1716 it = mpImpl->maItemList.begin();
1717 it += nNewPos;
1718 mpImpl->maItemList.insert(it, aItem);
1720 else
1722 mpImpl->maItemList.push_back(aItem);
1725 // redraw bar
1726 if (IsReallyVisible() && IsUpdateMode())
1727 Invalidate();
1729 CallEventListeners( VclEventId::TabbarPageMoved, static_cast<void*>(&aPair) );
1732 void TabBar::Clear()
1734 // delete all items
1735 mpImpl->maItemList.clear();
1737 // remove items from the list
1738 mbSizeFormat = true;
1739 mnCurPageId = 0;
1740 mnFirstPos = 0;
1741 maCurrentItemList = 0;
1743 // redraw bar
1744 if (IsReallyVisible() && IsUpdateMode())
1745 Invalidate();
1747 CallEventListeners(VclEventId::TabbarPageRemoved, reinterpret_cast<void*>(sal::static_int_cast<sal_IntPtr>(PAGE_NOT_FOUND)));
1750 bool TabBar::IsPageEnabled(sal_uInt16 nPageId) const
1752 if (isDisposed())
1753 return false;
1754 sal_uInt16 nPos = GetPagePos(nPageId);
1756 return nPos != PAGE_NOT_FOUND;
1759 void TabBar::SetPageBits(sal_uInt16 nPageId, TabBarPageBits nBits)
1761 sal_uInt16 nPos = GetPagePos(nPageId);
1763 if (nPos == PAGE_NOT_FOUND)
1764 return;
1766 auto& rItem = mpImpl->maItemList[nPos];
1768 if (rItem.mnBits != nBits)
1770 rItem.mnBits = nBits;
1772 // redraw bar
1773 if (IsReallyVisible() && IsUpdateMode())
1774 Invalidate(rItem.maRect);
1778 TabBarPageBits TabBar::GetPageBits(sal_uInt16 nPageId) const
1780 sal_uInt16 nPos = GetPagePos(nPageId);
1782 if (nPos != PAGE_NOT_FOUND)
1783 return mpImpl->maItemList[nPos].mnBits;
1784 else
1785 return TabBarPageBits::NONE;
1788 sal_uInt16 TabBar::GetPageCount() const
1790 return mpImpl->getItemSize();
1793 sal_uInt16 TabBar::GetPageId(sal_uInt16 nPos) const
1795 return nPos < mpImpl->maItemList.size() ? mpImpl->maItemList[nPos].mnId : 0;
1798 sal_uInt16 TabBar::GetPagePos(sal_uInt16 nPageId) const
1800 for (size_t i = 0; i < mpImpl->maItemList.size(); ++i)
1802 if (mpImpl->maItemList[i].mnId == nPageId)
1804 return static_cast<sal_uInt16>(i);
1807 return PAGE_NOT_FOUND;
1810 sal_uInt16 TabBar::GetPageId(const Point& rPos) const
1812 for (const auto& rItem : mpImpl->maItemList)
1814 if (rItem.maRect.Contains(rPos))
1815 return rItem.mnId;
1818 return 0;
1821 tools::Rectangle TabBar::GetPageRect(sal_uInt16 nPageId) const
1823 sal_uInt16 nPos = GetPagePos(nPageId);
1825 if (nPos != PAGE_NOT_FOUND)
1826 return mpImpl->maItemList[nPos].maRect;
1827 else
1828 return tools::Rectangle();
1831 void TabBar::SetCurPageId(sal_uInt16 nPageId)
1833 sal_uInt16 nPos = GetPagePos(nPageId);
1835 // do nothing if item does not exit
1836 if (nPos == PAGE_NOT_FOUND)
1837 return;
1839 // do nothing if the actual page did not change
1840 if (nPageId == mnCurPageId)
1841 return;
1843 // make invalid
1844 bool bUpdate = false;
1845 if (IsReallyVisible() && IsUpdateMode())
1846 bUpdate = true;
1848 auto& rItem = mpImpl->maItemList[nPos];
1849 ImplTabBarItem* pOldItem;
1851 if (mnCurPageId)
1852 pOldItem = &mpImpl->maItemList[GetPagePos(mnCurPageId)];
1853 else
1854 pOldItem = nullptr;
1856 // deselect previous page if page was not selected, if this is the
1857 // only selected page
1858 if (!rItem.mbSelect && pOldItem)
1860 sal_uInt16 nSelPageCount = GetSelectPageCount();
1861 if (nSelPageCount == 1)
1862 pOldItem->mbSelect = false;
1863 rItem.mbSelect = true;
1866 mnCurPageId = nPageId;
1867 mbFormat = true;
1869 // assure the actual page becomes visible
1870 if (IsReallyVisible())
1872 if (nPos < mnFirstPos)
1873 SetFirstPageId(nPageId);
1874 else
1876 // calculate visible width
1877 tools::Long nWidth = mnLastOffX;
1878 if (nWidth > ADDNEWPAGE_AREAWIDTH)
1879 nWidth -= ADDNEWPAGE_AREAWIDTH;
1881 if (rItem.maRect.IsEmpty())
1882 ImplFormat();
1884 while ((mbMirrored ? (rItem.maRect.Left() < mnOffX) : (rItem.maRect.Right() > nWidth)) ||
1885 rItem.maRect.IsEmpty())
1887 sal_uInt16 nNewPos = mnFirstPos + 1;
1888 // assure at least the actual tabpages are visible as first tabpage
1889 if (nNewPos >= nPos)
1891 SetFirstPageId(nPageId);
1892 break;
1894 else
1895 SetFirstPageId(GetPageId(nNewPos));
1896 ImplFormat();
1897 // abort if first page is not forwarded
1898 if (nNewPos != mnFirstPos)
1899 break;
1904 // redraw bar
1905 if (bUpdate)
1907 Invalidate(rItem.maRect);
1908 if (pOldItem)
1909 Invalidate(pOldItem->maRect);
1913 void TabBar::MakeVisible(sal_uInt16 nPageId)
1915 if (!IsReallyVisible())
1916 return;
1918 sal_uInt16 nPos = GetPagePos(nPageId);
1920 // do nothing if item does not exist
1921 if (nPos == PAGE_NOT_FOUND)
1922 return;
1924 if (nPos < mnFirstPos)
1925 SetFirstPageId(nPageId);
1926 else
1928 auto& rItem = mpImpl->maItemList[nPos];
1930 // calculate visible area
1931 tools::Long nWidth = mnLastOffX;
1933 if (mbFormat || rItem.maRect.IsEmpty())
1935 mbFormat = true;
1936 ImplFormat();
1939 while ((rItem.maRect.Right() > nWidth) ||
1940 rItem.maRect.IsEmpty())
1942 sal_uInt16 nNewPos = mnFirstPos+1;
1943 // assure at least the actual tabpages are visible as first tabpage
1944 if (nNewPos >= nPos)
1946 SetFirstPageId(nPageId);
1947 break;
1949 else
1950 SetFirstPageId(GetPageId(nNewPos));
1951 ImplFormat();
1952 // abort if first page is not forwarded
1953 if (nNewPos != mnFirstPos)
1954 break;
1959 void TabBar::SetFirstPageId(sal_uInt16 nPageId)
1961 sal_uInt16 nPos = GetPagePos(nPageId);
1963 // return false if item does not exist
1964 if (nPos == PAGE_NOT_FOUND)
1965 return;
1967 if (nPos == mnFirstPos)
1968 return;
1970 // assure as much pages are visible as possible
1971 ImplFormat();
1972 sal_uInt16 nLastFirstPos = ImplGetLastFirstPos();
1973 sal_uInt16 nNewPos;
1974 if (nPos > nLastFirstPos)
1975 nNewPos = nLastFirstPos;
1976 else
1977 nNewPos = nPos;
1979 if (nNewPos != mnFirstPos)
1981 mnFirstPos = nNewPos;
1982 mbFormat = true;
1984 // redraw bar (attention: check mbDropPos,
1985 // as if this flag was set, we do not re-paint immediately
1986 if (IsReallyVisible() && IsUpdateMode() && !mbDropPos)
1987 Invalidate();
1991 void TabBar::SelectPage(sal_uInt16 nPageId, bool bSelect)
1993 sal_uInt16 nPos = GetPagePos(nPageId);
1995 if (nPos == PAGE_NOT_FOUND)
1996 return;
1998 auto& rItem = mpImpl->maItemList[nPos];
2000 if (rItem.mbSelect != bSelect)
2002 rItem.mbSelect = bSelect;
2004 // redraw bar
2005 if (IsReallyVisible() && IsUpdateMode())
2006 Invalidate(rItem.maRect);
2010 sal_uInt16 TabBar::GetSelectPageCount() const
2012 sal_uInt16 nSelected = 0;
2013 for (const auto& rItem : mpImpl->maItemList)
2015 if (rItem.mbSelect)
2016 nSelected++;
2019 return nSelected;
2022 bool TabBar::IsPageSelected(sal_uInt16 nPageId) const
2024 sal_uInt16 nPos = GetPagePos(nPageId);
2025 if (nPos != PAGE_NOT_FOUND)
2026 return mpImpl->maItemList[nPos].mbSelect;
2027 else
2028 return false;
2031 void TabBar::SetProtectionSymbol(sal_uInt16 nPageId, bool bProtection)
2033 sal_uInt16 nPos = GetPagePos(nPageId);
2034 if (nPos != PAGE_NOT_FOUND)
2036 if (mpImpl->maItemList[nPos].mbProtect != bProtection)
2038 mpImpl->maItemList[nPos].mbProtect = bProtection;
2039 mbSizeFormat = true; // render text width changes, thus bar width
2041 // redraw bar
2042 if (IsReallyVisible() && IsUpdateMode())
2043 Invalidate();
2048 bool TabBar::StartEditMode(sal_uInt16 nPageId)
2050 sal_uInt16 nPos = GetPagePos( nPageId );
2051 if (mpImpl->mxEdit || (nPos == PAGE_NOT_FOUND) || (mnLastOffX < 8))
2052 return false;
2054 mnEditId = nPageId;
2055 if (StartRenaming())
2057 ImplShowPage(nPos);
2058 ImplFormat();
2059 PaintImmediately();
2061 mpImpl->mxEdit.disposeAndReset(VclPtr<TabBarEdit>::Create(this));
2062 tools::Rectangle aRect = GetPageRect( mnEditId );
2063 tools::Long nX = aRect.Left();
2064 tools::Long nWidth = aRect.GetWidth();
2065 if (mnEditId != GetCurPageId())
2066 nX += 1;
2067 if (nX + nWidth > mnLastOffX)
2068 nWidth = mnLastOffX-nX;
2069 if (nWidth < 3)
2071 nX = aRect.Left();
2072 nWidth = aRect.GetWidth();
2074 weld::Entry& rEntry = mpImpl->mxEdit->get_widget();
2075 rEntry.set_text(GetPageText(mnEditId));
2076 mpImpl->mxEdit->SetPosSizePixel(Point(nX, aRect.Top() + mnOffY + 1), Size(nWidth, aRect.GetHeight() - 3));
2077 vcl::Font aFont = GetPointFont(*GetOutDev()); // FIXME RenderContext
2079 Color aForegroundColor;
2080 Color aBackgroundColor;
2081 Color aFaceColor;
2082 Color aSelectColor;
2083 Color aFaceTextColor;
2084 Color aSelectTextColor;
2086 ImplGetColors(Application::GetSettings().GetStyleSettings(), aFaceColor, aFaceTextColor, aSelectColor, aSelectTextColor);
2088 if (mnEditId != GetCurPageId())
2090 aFont.SetWeight(WEIGHT_LIGHT);
2092 if (IsPageSelected(mnEditId) || mnEditId == GetCurPageId())
2094 aForegroundColor = aSelectTextColor;
2095 aBackgroundColor = aSelectColor;
2097 else
2099 aForegroundColor = aFaceTextColor;
2100 aBackgroundColor = aFaceColor;
2102 if (GetPageBits( mnEditId ) & TabBarPageBits::Blue)
2104 aForegroundColor = COL_LIGHTBLUE;
2106 rEntry.set_font(aFont);
2107 rEntry.set_font_color(aForegroundColor);
2108 mpImpl->mxEdit->SetControlBackground(aBackgroundColor);
2109 rEntry.grab_focus();
2110 rEntry.select_region(0, -1);
2111 mpImpl->mxEdit->Show();
2112 return true;
2114 else
2116 mnEditId = 0;
2117 return false;
2121 bool TabBar::IsInEditMode() const
2123 return bool(mpImpl->mxEdit);
2126 void TabBar::EndEditMode(bool bCancel)
2128 if (!mpImpl->mxEdit)
2129 return;
2131 // call hdl
2132 bool bEnd = true;
2133 mbEditCanceled = bCancel;
2134 weld::Entry& rEntry = mpImpl->mxEdit->get_widget();
2135 maEditText = rEntry.get_text();
2136 mpImpl->mxEdit->SetPostEvent();
2137 if (!bCancel)
2139 TabBarAllowRenamingReturnCode nAllowRenaming = AllowRenaming();
2140 if (nAllowRenaming == TABBAR_RENAMING_YES)
2141 SetPageText(mnEditId, maEditText);
2142 else if (nAllowRenaming == TABBAR_RENAMING_NO)
2143 bEnd = false;
2144 else // nAllowRenaming == TABBAR_RENAMING_CANCEL
2145 mbEditCanceled = true;
2148 // renaming not allowed, then reset edit data
2149 if (!bEnd)
2151 mpImpl->mxEdit->ResetPostEvent();
2152 rEntry.grab_focus();
2154 else
2156 // close edit and call end hdl
2157 mpImpl->mxEdit.disposeAndClear();
2159 EndRenaming();
2160 mnEditId = 0;
2163 // reset
2164 maEditText.clear();
2165 mbEditCanceled = false;
2168 void TabBar::SetMirrored(bool bMirrored)
2170 if (mbMirrored != bMirrored)
2172 mbMirrored = bMirrored;
2173 mbSizeFormat = true;
2174 ImplInitControls(); // for button images
2175 Resize(); // recalculates control positions
2176 Mirror();
2180 void TabBar::SetEffectiveRTL(bool bRTL)
2182 SetMirrored( bRTL != AllSettings::GetLayoutRTL() );
2185 bool TabBar::IsEffectiveRTL() const
2187 return IsMirrored() != AllSettings::GetLayoutRTL();
2190 void TabBar::SetMaxPageWidth(tools::Long nMaxWidth)
2192 if (mnMaxPageWidth != nMaxWidth)
2194 mnMaxPageWidth = nMaxWidth;
2195 mbSizeFormat = true;
2197 // redraw bar
2198 if (IsReallyVisible() && IsUpdateMode())
2199 Invalidate();
2203 void TabBar::SetPageText(sal_uInt16 nPageId, const OUString& rText)
2205 sal_uInt16 nPos = GetPagePos(nPageId);
2206 if (nPos != PAGE_NOT_FOUND)
2208 mpImpl->maItemList[nPos].maText = rText;
2209 mbSizeFormat = true;
2211 // redraw bar
2212 if (IsReallyVisible() && IsUpdateMode())
2213 Invalidate();
2215 CallEventListeners(VclEventId::TabbarPageTextChanged, reinterpret_cast<void*>(sal::static_int_cast<sal_IntPtr>(nPageId)));
2219 OUString TabBar::GetPageText(sal_uInt16 nPageId) const
2221 sal_uInt16 nPos = GetPagePos(nPageId);
2222 if (nPos != PAGE_NOT_FOUND)
2223 return mpImpl->maItemList[nPos].maText;
2224 return OUString();
2227 OUString TabBar::GetAuxiliaryText(sal_uInt16 nPageId) const
2229 sal_uInt16 nPos = GetPagePos(nPageId);
2230 if (nPos != PAGE_NOT_FOUND)
2231 return mpImpl->maItemList[nPos].maAuxiliaryText;
2232 return OUString();
2235 void TabBar::SetAuxiliaryText(sal_uInt16 nPageId, const OUString& rText )
2237 sal_uInt16 nPos = GetPagePos(nPageId);
2238 if (nPos != PAGE_NOT_FOUND)
2240 mpImpl->maItemList[nPos].maAuxiliaryText = rText;
2241 // no redraw bar, no CallEventListener, internal use in LayerTabBar
2245 OUString TabBar::GetHelpText(sal_uInt16 nPageId) const
2247 sal_uInt16 nPos = GetPagePos(nPageId);
2248 if (nPos != PAGE_NOT_FOUND)
2250 auto& rItem = mpImpl->maItemList[nPos];
2251 if (rItem.maHelpText.isEmpty() && !rItem.maHelpId.isEmpty())
2253 Help* pHelp = Application::GetHelp();
2254 if (pHelp)
2255 rItem.maHelpText = pHelp->GetHelpText(OStringToOUString(rItem.maHelpId, RTL_TEXTENCODING_UTF8), this);
2258 return rItem.maHelpText;
2260 return OUString();
2263 bool TabBar::StartDrag(const CommandEvent& rCEvt, vcl::Region& rRegion)
2265 if (!(mnWinStyle & WB_DRAG) || (rCEvt.GetCommand() != CommandEventId::StartDrag))
2266 return false;
2268 // Check if the clicked page was selected. If this is not the case
2269 // set it as actual entry. We check for this only at a mouse action
2270 // if Drag and Drop can be triggered from the keyboard.
2271 // We only do this, if Select() was not triggered, as the Select()
2272 // could have scrolled the area
2273 if (rCEvt.IsMouseEvent() && !mbInSelect)
2275 sal_uInt16 nSelId = GetPageId(rCEvt.GetMousePosPixel());
2277 // do not start dragging if no entry was clicked
2278 if (!nSelId)
2279 return false;
2281 // check if page was selected. If not set it as actual
2282 // page and call Select()
2283 if (!IsPageSelected(nSelId))
2285 if (ImplDeactivatePage())
2287 SetCurPageId(nSelId);
2288 PaintImmediately();
2289 ImplActivatePage();
2290 ImplSelect();
2292 else
2293 return false;
2296 mbInSelect = false;
2298 vcl::Region aRegion;
2300 // assign region
2301 rRegion = aRegion;
2303 return true;
2306 sal_uInt16 TabBar::ShowDropPos(const Point& rPos)
2308 sal_uInt16 nNewDropPos;
2309 sal_uInt16 nItemCount = mpImpl->getItemSize();
2310 sal_Int16 nScroll = 0;
2312 if (rPos.X() > mnLastOffX-TABBAR_DRAG_SCROLLOFF)
2314 auto& rItem = mpImpl->maItemList[mpImpl->maItemList.size() - 1];
2315 if (!rItem.maRect.IsEmpty() && (rPos.X() > rItem.maRect.Right()))
2316 nNewDropPos = mpImpl->getItemSize();
2317 else
2319 nNewDropPos = mnFirstPos + 1;
2320 nScroll = 1;
2323 else if ((rPos.X() <= mnOffX) ||
2324 (!mnOffX && (rPos.X() <= TABBAR_DRAG_SCROLLOFF)))
2326 if (mnFirstPos)
2328 nNewDropPos = mnFirstPos;
2329 nScroll = -1;
2331 else
2332 nNewDropPos = 0;
2334 else
2336 sal_uInt16 nDropId = GetPageId(rPos);
2337 if (nDropId)
2339 nNewDropPos = GetPagePos(nDropId);
2340 if (mnFirstPos && (nNewDropPos == mnFirstPos - 1))
2341 nScroll = -1;
2343 else
2344 nNewDropPos = nItemCount;
2347 if (mbDropPos && (nNewDropPos == mnDropPos) && !nScroll)
2348 return mnDropPos;
2350 if (mbDropPos)
2351 HideDropPos();
2352 mbDropPos = true;
2353 mnDropPos = nNewDropPos;
2355 if (nScroll)
2357 sal_uInt16 nOldFirstPos = mnFirstPos;
2358 SetFirstPageId(GetPageId(mnFirstPos + nScroll));
2360 // draw immediately, as Paint not possible during Drag and Drop
2361 if (nOldFirstPos != mnFirstPos)
2363 tools::Rectangle aRect(mnOffX, 0, mnLastOffX, maWinSize.Height());
2364 GetOutDev()->SetFillColor(GetBackground().GetColor());
2365 GetOutDev()->DrawRect(aRect);
2366 Invalidate(aRect);
2370 // draw drop position arrows
2371 const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings();
2372 const Color aTextColor = rStyles.GetLabelTextColor();
2373 tools::Long nX;
2374 tools::Long nY = (maWinSize.Height() / 2) - 1;
2375 sal_uInt16 nCurPos = GetPagePos(mnCurPageId);
2377 sal_Int32 nTriangleWidth = 3 * GetDPIScaleFactor();
2379 if (mnDropPos < nItemCount)
2381 GetOutDev()->SetLineColor(aTextColor);
2382 GetOutDev()->SetFillColor(aTextColor);
2384 auto& rItem = mpImpl->maItemList[mnDropPos];
2385 nX = rItem.maRect.Left();
2386 if ( mnDropPos == nCurPos )
2387 nX--;
2388 else
2389 nX++;
2391 if (!rItem.IsDefaultTabBgColor() && !rItem.mbSelect)
2393 GetOutDev()->SetLineColor(rItem.maTabTextColor);
2394 GetOutDev()->SetFillColor(rItem.maTabTextColor);
2397 tools::Polygon aPoly(3);
2398 aPoly.SetPoint(Point(nX, nY), 0);
2399 aPoly.SetPoint(Point(nX + nTriangleWidth, nY - nTriangleWidth), 1);
2400 aPoly.SetPoint(Point(nX + nTriangleWidth, nY + nTriangleWidth), 2);
2401 GetOutDev()->DrawPolygon(aPoly);
2403 if (mnDropPos > 0 && mnDropPos < nItemCount + 1)
2405 GetOutDev()->SetLineColor(aTextColor);
2406 GetOutDev()->SetFillColor(aTextColor);
2408 auto& rItem = mpImpl->maItemList[mnDropPos - 1];
2409 nX = rItem.maRect.Right();
2410 if (mnDropPos == nCurPos)
2411 nX++;
2412 if (!rItem.IsDefaultTabBgColor() && !rItem.mbSelect)
2414 GetOutDev()->SetLineColor(rItem.maTabTextColor);
2415 GetOutDev()->SetFillColor(rItem.maTabTextColor);
2417 tools::Polygon aPoly(3);
2418 aPoly.SetPoint(Point(nX, nY), 0);
2419 aPoly.SetPoint(Point(nX - nTriangleWidth, nY - nTriangleWidth), 1);
2420 aPoly.SetPoint(Point(nX - nTriangleWidth, nY + nTriangleWidth), 2);
2421 GetOutDev()->DrawPolygon(aPoly);
2424 return mnDropPos;
2427 void TabBar::HideDropPos()
2429 if (!mbDropPos)
2430 return;
2432 tools::Long nX;
2433 tools::Long nY1 = (maWinSize.Height() / 2) - 3;
2434 tools::Long nY2 = nY1 + 5;
2435 sal_uInt16 nItemCount = mpImpl->getItemSize();
2437 if (mnDropPos < nItemCount)
2439 auto& rItem = mpImpl->maItemList[mnDropPos];
2440 nX = rItem.maRect.Left();
2441 // immediately call Paint, as it is not possible during drag and drop
2442 tools::Rectangle aRect( nX-1, nY1, nX+3, nY2 );
2443 vcl::Region aRegion( aRect );
2444 GetOutDev()->SetClipRegion( aRegion );
2445 Invalidate(aRect);
2446 GetOutDev()->SetClipRegion();
2448 if (mnDropPos > 0 && mnDropPos < nItemCount + 1)
2450 auto& rItem = mpImpl->maItemList[mnDropPos - 1];
2451 nX = rItem.maRect.Right();
2452 // immediately call Paint, as it is not possible during drag and drop
2453 tools::Rectangle aRect(nX - 2, nY1, nX + 1, nY2);
2454 vcl::Region aRegion(aRect);
2455 GetOutDev()->SetClipRegion(aRegion);
2456 Invalidate(aRect);
2457 GetOutDev()->SetClipRegion();
2460 mbDropPos = false;
2461 mnDropPos = 0;
2464 void TabBar::SwitchPage(const Point& rPos)
2466 sal_uInt16 nSwitchId = GetPageId(rPos);
2467 if (!nSwitchId)
2468 EndSwitchPage();
2469 else
2471 if (nSwitchId != mnSwitchId)
2473 mnSwitchId = nSwitchId;
2474 mnSwitchTime = tools::Time::GetSystemTicks();
2476 else
2478 // change only after 500 ms
2479 if (mnSwitchId != GetCurPageId())
2481 if (tools::Time::GetSystemTicks() > mnSwitchTime + 500)
2483 if (ImplDeactivatePage())
2485 SetCurPageId( mnSwitchId );
2486 PaintImmediately();
2487 ImplActivatePage();
2488 ImplSelect();
2496 void TabBar::EndSwitchPage()
2498 mnSwitchTime = 0;
2499 mnSwitchId = 0;
2502 void TabBar::SetStyle(WinBits nStyle)
2504 if (mnWinStyle == nStyle)
2505 return;
2506 mnWinStyle = nStyle;
2507 ImplInitControls();
2508 // order possible controls
2509 if (IsReallyVisible() && IsUpdateMode())
2510 Resize();
2513 Size TabBar::CalcWindowSizePixel() const
2515 tools::Long nWidth = 0;
2517 if (!mpImpl->maItemList.empty())
2519 const_cast<TabBar*>(this)->ImplCalcWidth();
2520 for (const auto& rItem : mpImpl->maItemList)
2522 nWidth += rItem.mnWidth;
2526 return Size(nWidth, GetSettings().GetStyleSettings().GetScrollBarSize());
2529 tools::Rectangle TabBar::GetPageArea() const
2531 return tools::Rectangle(Point(mnOffX, mnOffY),
2532 Size(mnLastOffX - mnOffX + 1, GetSizePixel().Height() - mnOffY));
2535 void TabBar::SetAddButtonEnabled(bool bAddButtonEnabled)
2537 mpImpl->mxButtonBox->m_xAddButton->set_sensitive(bAddButtonEnabled);
2540 css::uno::Reference<css::accessibility::XAccessible> TabBar::CreateAccessible()
2542 return mpImpl->maAccessibleFactory.getFactory().createAccessibleTabBar(*this);
2545 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */