remove assert looking for new compatibilityMode DOCX
[LibreOffice.git] / svtools / source / control / tabbar.cxx
blob6f38a6072e67c57f4d937cb7e455284f80162b5f
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 .
20 #include "accessibletabbar.hxx"
22 #include <svtools/tabbar.hxx>
23 #include <tools/time.hxx>
24 #include <tools/poly.hxx>
25 #include <utility>
26 #include <vcl/InterimItemWindow.hxx>
27 #include <vcl/bitmapex.hxx>
28 #include <vcl/svapp.hxx>
29 #include <vcl/help.hxx>
30 #include <vcl/decoview.hxx>
31 #include <vcl/event.hxx>
32 #include <vcl/settings.hxx>
33 #include <vcl/commandevent.hxx>
34 #include <vcl/ptrstyle.hxx>
35 #include <vcl/weldutils.hxx>
36 #include <svtools/svtresid.hxx>
37 #include <svtools/strings.hrc>
38 #include <limits>
39 #include <memory>
40 #include <vector>
41 #include <vcl/idle.hxx>
42 #include <bitmaps.hlst>
44 namespace
47 constexpr sal_uInt16 TABBAR_DRAG_SCROLLOFF = 5;
48 constexpr sal_uInt16 TABBAR_MINSIZE = 5;
50 constexpr sal_uInt16 ADDNEWPAGE_AREAWIDTH = 10;
52 class TabDrawer
54 private:
55 vcl::RenderContext& mrRenderContext;
56 const StyleSettings& mrStyleSettings;
58 tools::Rectangle maRect;
59 tools::Rectangle maLineRect;
61 Color maSelectedColor;
62 Color maCustomColor;
64 public:
65 bool mbSelected:1;
66 bool mbCustomColored:1;
67 bool mbEnabled:1;
68 bool mbProtect:1;
70 explicit TabDrawer(vcl::RenderContext& rRenderContext)
71 : mrRenderContext(rRenderContext)
72 , mrStyleSettings(rRenderContext.GetSettings().GetStyleSettings())
73 , mbSelected(false)
74 , mbCustomColored(false)
75 , mbEnabled(false)
76 , mbProtect(false)
81 void drawOuterFrame()
83 // set correct FillInBrush depending on status
84 if (mbSelected)
86 // Currently selected Tab
87 mrRenderContext.SetFillColor(maSelectedColor);
88 mrRenderContext.SetLineColor(maSelectedColor);
89 mrRenderContext.DrawRect(maRect);
90 mrRenderContext.SetLineColor(mrStyleSettings.GetDarkShadowColor());
92 else if (mbCustomColored)
94 mrRenderContext.SetFillColor(maCustomColor);
95 mrRenderContext.SetLineColor(maCustomColor);
96 mrRenderContext.DrawRect(maRect);
97 mrRenderContext.SetLineColor(mrStyleSettings.GetDarkShadowColor());
101 void drawText(const OUString& aText)
103 tools::Rectangle aRect = maRect;
104 tools::Long nTextWidth = mrRenderContext.GetTextWidth(aText);
105 tools::Long nTextHeight = mrRenderContext.GetTextHeight();
106 Point aPos = aRect.TopLeft();
107 aPos.AdjustX((aRect.getOpenWidth() - nTextWidth) / 2 );
108 aPos.AdjustY((aRect.getOpenHeight() - nTextHeight) / 2 );
110 if (mbEnabled)
111 mrRenderContext.DrawText(aPos, aText);
112 else
113 mrRenderContext.DrawCtrlText(aPos, aText, 0, aText.getLength(), (DrawTextFlags::Disable | DrawTextFlags::Mnemonic));
116 void drawOverTopBorder()
118 Point aTopLeft = maRect.TopLeft() + Point(1, 0);
119 Point aTopRight = maRect.TopRight() + Point(-1, 0);
121 tools::Rectangle aDelRect(aTopLeft, aTopRight);
122 mrRenderContext.DrawRect(aDelRect);
125 void drawColorLine()
127 if (!mbSelected)
128 return;
130 // tdf#141396: the color must be different from the rest of the selected tab
131 Color aLineColor = (mbCustomColored && maCustomColor != maSelectedColor)
132 ? maCustomColor
133 : mrStyleSettings.GetDarkShadowColor();
134 mrRenderContext.SetFillColor(aLineColor);
135 mrRenderContext.SetLineColor(aLineColor);
136 mrRenderContext.DrawRect(maLineRect);
139 void drawSeparator()
141 const tools::Long cMargin = 5;
142 const tools::Long aRight( maRect.Right() - 1 );
143 mrRenderContext.SetLineColor(mrStyleSettings.GetShadowColor());
144 mrRenderContext.DrawLine(Point(aRight, maRect.Top() + cMargin),
145 Point(aRight, maRect.Bottom() - cMargin));
148 void drawTab()
150 drawOuterFrame();
151 drawColorLine();
152 if (!mbSelected && !mbCustomColored)
153 drawSeparator();
154 if (mbProtect)
156 BitmapEx aBitmap(BMP_TAB_LOCK);
157 Point aPosition = maRect.TopLeft();
158 aPosition.AdjustX(2);
159 aPosition.AdjustY((maRect.getOpenHeight() - aBitmap.GetSizePixel().Height()) / 2);
160 mrRenderContext.DrawBitmapEx(aPosition, aBitmap);
164 void setRect(const tools::Rectangle& rRect)
166 maLineRect = tools::Rectangle(rRect.BottomLeft(), rRect.BottomRight());
167 maLineRect.AdjustTop(-2);
168 maRect = rRect;
171 void setSelected(bool bSelected)
173 mbSelected = bSelected;
176 void setCustomColored(bool bCustomColored)
178 mbCustomColored = bCustomColored;
181 void setEnabled(bool bEnabled)
183 mbEnabled = bEnabled;
186 void setSelectedFillColor(const Color& rColor)
188 maSelectedColor = rColor;
191 void setCustomColor(const Color& rColor)
193 maCustomColor = rColor;
197 } // anonymous namespace
199 struct ImplTabBarItem
201 sal_uInt16 mnId;
202 TabBarPageBits mnBits;
203 OUString maText;
204 OUString maHelpText;
205 OUString maAuxiliaryText; // used in LayerTabBar for real layer name
206 tools::Rectangle maRect;
207 tools::Long mnWidth;
208 OString maHelpId;
209 bool mbShort : 1;
210 bool mbSelect : 1;
211 bool mbProtect : 1;
212 Color maTabBgColor;
213 Color maTabTextColor;
215 ImplTabBarItem(sal_uInt16 nItemId, OUString aText, TabBarPageBits nPageBits)
216 : mnId(nItemId)
217 , mnBits(nPageBits)
218 , maText(std::move(aText))
219 , mnWidth(0)
220 , mbShort(false)
221 , mbSelect(false)
222 , mbProtect(false)
223 , maTabBgColor(COL_AUTO)
224 , maTabTextColor(COL_AUTO)
228 bool IsDefaultTabBgColor() const
230 return maTabBgColor == COL_AUTO;
233 bool IsSelected(ImplTabBarItem const * pCurItem) const
235 return mbSelect || (pCurItem == this);
238 OUString const & GetRenderText() const
240 return maText;
244 class ImplTabSizer : public vcl::Window
246 public:
247 ImplTabSizer( TabBar* pParent, WinBits nWinStyle );
249 TabBar* GetParent() const { return static_cast<TabBar*>(Window::GetParent()); }
251 private:
252 void ImplTrack( const Point& rScreenPos );
254 virtual void MouseButtonDown( const MouseEvent& rMEvt ) override;
255 virtual void Tracking( const TrackingEvent& rTEvt ) override;
256 virtual void Paint( vcl::RenderContext& /*rRenderContext*/, const tools::Rectangle& rRect ) override;
258 Point maStartPos;
259 tools::Long mnStartWidth;
262 ImplTabSizer::ImplTabSizer( TabBar* pParent, WinBits nWinStyle )
263 : Window( pParent, nWinStyle & WB_3DLOOK )
264 , mnStartWidth(0)
266 SetPointer(PointerStyle::HSizeBar);
267 SetSizePixel(Size(7 * GetDPIScaleFactor(), 0));
270 void ImplTabSizer::ImplTrack( const Point& rScreenPos )
272 TabBar* pParent = GetParent();
273 tools::Long nDiff = rScreenPos.X() - maStartPos.X();
274 pParent->mnSplitSize = mnStartWidth + (pParent->IsMirrored() ? -nDiff : nDiff);
275 if ( pParent->mnSplitSize < TABBAR_MINSIZE )
276 pParent->mnSplitSize = TABBAR_MINSIZE;
277 pParent->Split();
278 pParent->PaintImmediately();
281 void ImplTabSizer::MouseButtonDown( const MouseEvent& rMEvt )
283 if ( GetParent()->IsInEditMode() )
285 GetParent()->EndEditMode();
286 return;
289 if ( rMEvt.IsLeft() )
291 maStartPos = OutputToScreenPixel( rMEvt.GetPosPixel() );
292 mnStartWidth = GetParent()->GetSizePixel().Width();
293 StartTracking();
297 void ImplTabSizer::Tracking( const TrackingEvent& rTEvt )
299 if ( rTEvt.IsTrackingEnded() )
301 if ( rTEvt.IsTrackingCanceled() )
302 ImplTrack( maStartPos );
303 GetParent()->mnSplitSize = 0;
305 else
306 ImplTrack( OutputToScreenPixel( rTEvt.GetMouseEvent().GetPosPixel() ) );
309 void ImplTabSizer::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& )
311 DecorationView aDecoView(&rRenderContext);
312 tools::Rectangle aOutputRect(Point(0, 0), GetOutputSizePixel());
313 aDecoView.DrawHandle(aOutputRect);
316 namespace {
318 // Is not named Impl. as it may be both instantiated and derived from
319 class TabBarEdit final : public InterimItemWindow
321 private:
322 std::unique_ptr<weld::Entry> m_xEntry;
323 Idle maLoseFocusIdle;
324 bool mbPostEvt;
326 DECL_LINK( ImplEndEditHdl, void*, void );
327 DECL_LINK( ImplEndTimerHdl, Timer*, void );
328 DECL_LINK( ActivatedHdl, weld::Entry&, bool );
329 DECL_LINK( KeyInputHdl, const KeyEvent&, bool );
330 DECL_LINK( FocusOutHdl, weld::Widget&, void );
332 public:
333 TabBarEdit(TabBar* pParent);
334 virtual void dispose() override;
336 TabBar* GetParent() const { return static_cast<TabBar*>(Window::GetParent()); }
338 weld::Entry& get_widget() { return *m_xEntry; }
340 void SetPostEvent() { mbPostEvt = true; }
341 void ResetPostEvent() { mbPostEvt = false; }
346 TabBarEdit::TabBarEdit(TabBar* pParent)
347 : InterimItemWindow(pParent, u"svt/ui/tabbaredit.ui"_ustr, u"TabBarEdit"_ustr)
348 , m_xEntry(m_xBuilder->weld_entry(u"entry"_ustr))
349 , maLoseFocusIdle( "svtools::TabBarEdit maLoseFocusIdle" )
351 InitControlBase(m_xEntry.get());
353 mbPostEvt = false;
354 maLoseFocusIdle.SetPriority( TaskPriority::REPAINT );
355 maLoseFocusIdle.SetInvokeHandler( LINK( this, TabBarEdit, ImplEndTimerHdl ) );
357 m_xEntry->connect_activate(LINK(this, TabBarEdit, ActivatedHdl));
358 m_xEntry->connect_key_press(LINK(this, TabBarEdit, KeyInputHdl));
359 m_xEntry->connect_focus_out(LINK(this, TabBarEdit, FocusOutHdl));
362 void TabBarEdit::dispose()
364 m_xEntry.reset();
365 InterimItemWindow::dispose();
368 IMPL_LINK_NOARG(TabBarEdit, ActivatedHdl, weld::Entry&, bool)
370 if ( !mbPostEvt )
372 if ( PostUserEvent( LINK( this, TabBarEdit, ImplEndEditHdl ), reinterpret_cast<void*>(false), true ) )
373 mbPostEvt = true;
375 return true;
378 IMPL_LINK(TabBarEdit, KeyInputHdl, const KeyEvent&, rKEvt, bool)
380 if (!rKEvt.GetKeyCode().GetModifier() && rKEvt.GetKeyCode().GetCode() == KEY_ESCAPE)
382 if ( !mbPostEvt )
384 if ( PostUserEvent( LINK( this, TabBarEdit, ImplEndEditHdl ), reinterpret_cast<void*>(true), true ) )
385 mbPostEvt = true;
387 return true;
389 return false;
392 IMPL_LINK_NOARG(TabBarEdit, FocusOutHdl, weld::Widget&, void)
394 if ( !mbPostEvt )
396 if ( PostUserEvent( LINK( this, TabBarEdit, ImplEndEditHdl ), reinterpret_cast<void*>(false), true ) )
397 mbPostEvt = true;
401 IMPL_LINK( TabBarEdit, ImplEndEditHdl, void*, pCancel, void )
403 ResetPostEvent();
404 maLoseFocusIdle.Stop();
406 // tdf#156958: when renaming and clicking on canvas, LO goes into GetParent()->EndEditMode first time
407 // then it calls TabBarEdit::dispose method which resets m_xEntry BUT, on the same thread, LO comes here again
408 // so return if already disposed to avoid a crash
409 if (isDisposed())
410 return;
412 // We need this query, because the edit gets a losefocus event,
413 // when it shows the context menu or the insert symbol dialog
414 if (!m_xEntry->has_focus() && m_xEntry->has_child_focus())
415 maLoseFocusIdle.Start();
416 else
417 GetParent()->EndEditMode( pCancel != nullptr );
420 IMPL_LINK_NOARG(TabBarEdit, ImplEndTimerHdl, Timer *, void)
422 if (m_xEntry->has_focus())
423 return;
425 // We need this query, because the edit gets a losefocus event,
426 // when it shows the context menu or the insert symbol dialog
427 if (m_xEntry->has_child_focus())
428 maLoseFocusIdle.Start();
429 else
430 GetParent()->EndEditMode( true );
433 namespace {
435 class TabButtons final : public InterimItemWindow
437 public:
438 std::unique_ptr<weld::Button> m_xFirstButton;
439 std::unique_ptr<weld::Button> m_xPrevButton;
440 std::unique_ptr<weld::Button> m_xNextButton;
441 std::unique_ptr<weld::Button> m_xLastButton;
442 std::unique_ptr<weld::Button> m_xAddButton;
443 std::shared_ptr<weld::ButtonPressRepeater> m_xAddRepeater;
444 std::shared_ptr<weld::ButtonPressRepeater> m_xPrevRepeater;
445 std::shared_ptr<weld::ButtonPressRepeater> m_xNextRepeater;
447 TabButtons(TabBar* pParent, bool bSheets)
448 : InterimItemWindow(pParent,
449 pParent->IsMirrored() ? u"svt/ui/tabbuttonsmirrored.ui"_ustr
450 : u"svt/ui/tabbuttons.ui"_ustr,
451 u"TabButtons"_ustr)
452 , m_xFirstButton(m_xBuilder->weld_button(u"first"_ustr))
453 , m_xPrevButton(m_xBuilder->weld_button(u"prev"_ustr))
454 , m_xNextButton(m_xBuilder->weld_button(u"next"_ustr))
455 , m_xLastButton(m_xBuilder->weld_button(u"last"_ustr))
456 , m_xAddButton(m_xBuilder->weld_button(u"add"_ustr))
458 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
459 SetPaintTransparent(false);
460 SetBackground(rStyleSettings.GetFaceColor());
462 m_xFirstButton->set_accessible_name(SvtResId(STR_TABBAR_PUSHBUTTON_MOVET0HOME));
463 m_xPrevButton->set_accessible_name(SvtResId(STR_TABBAR_PUSHBUTTON_MOVELEFT));
464 m_xNextButton->set_accessible_name(SvtResId(STR_TABBAR_PUSHBUTTON_MOVERIGHT));
465 m_xLastButton->set_accessible_name(SvtResId(STR_TABBAR_PUSHBUTTON_MOVETOEND));
466 m_xAddButton->set_accessible_name(SvtResId(STR_TABBAR_PUSHBUTTON_ADDTAB));
468 if (bSheets)
470 m_xFirstButton->set_tooltip_text(SvtResId(STR_TABBAR_HINT_MOVETOHOME_SHEETS));
471 m_xPrevButton->set_tooltip_text(SvtResId(STR_TABBAR_HINT_MOVELEFT_SHEETS));
472 m_xNextButton->set_tooltip_text(SvtResId(STR_TABBAR_HINT_MOVERIGHT_SHEETS));
473 m_xLastButton->set_tooltip_text(SvtResId(STR_TABBAR_HINT_MOVETOEND_SHEETS));
474 m_xAddButton->set_tooltip_text(SvtResId(STR_TABBAR_HINT_ADDTAB_SHEETS));
478 void AdaptToHeight(int nHeight)
480 if (m_xFirstButton->get_preferred_size() == Size(nHeight, nHeight))
481 return;
482 m_xFirstButton->set_size_request(nHeight, nHeight);
483 m_xPrevButton->set_size_request(nHeight, nHeight);
484 m_xNextButton->set_size_request(nHeight, nHeight);
485 m_xLastButton->set_size_request(nHeight, nHeight);
486 m_xAddButton->set_size_request(nHeight, nHeight);
487 InvalidateChildSizeCache();
490 virtual void dispose() override
492 m_xNextRepeater.reset();
493 m_xPrevRepeater.reset();
494 m_xAddRepeater.reset();
495 m_xAddButton.reset();
496 m_xLastButton.reset();
497 m_xNextButton.reset();
498 m_xPrevButton.reset();
499 m_xFirstButton.reset();
500 InterimItemWindow::dispose();
506 struct TabBar_Impl
508 ScopedVclPtr<ImplTabSizer> mpSizer;
509 ScopedVclPtr<TabButtons> mxButtonBox;
510 ScopedVclPtr<TabBarEdit> mxEdit;
511 std::vector<ImplTabBarItem> maItemList;
513 sal_uInt16 getItemSize() const
515 return static_cast<sal_uInt16>(maItemList.size());
519 TabBar::TabBar( vcl::Window* pParent, WinBits nWinStyle, bool bSheets ) :
520 Window( pParent, (nWinStyle & WB_3DLOOK) | WB_CLIPCHILDREN )
522 ImplInit( nWinStyle, bSheets );
523 maCurrentItemList = 0;
526 TabBar::~TabBar()
528 disposeOnce();
531 void TabBar::dispose()
533 EndEditMode( true );
534 mpImpl.reset();
535 Window::dispose();
538 const sal_uInt16 TabBar::APPEND = ::std::numeric_limits<sal_uInt16>::max();
539 const sal_uInt16 TabBar::PAGE_NOT_FOUND = ::std::numeric_limits<sal_uInt16>::max();
541 void TabBar::ImplInit( WinBits nWinStyle, bool bSheets )
543 mpImpl.reset(new TabBar_Impl);
545 mnMaxPageWidth = 0;
546 mnCurMaxWidth = 0;
547 mnOffX = 0;
548 mnOffY = 0;
549 mnLastOffX = 0;
550 mnSplitSize = 0;
551 mnSwitchTime = 0;
552 mnWinStyle = nWinStyle;
553 mnCurPageId = 0;
554 mnFirstPos = 0;
555 mnDropPos = 0;
556 mnSwitchId = 0;
557 mnEditId = 0;
558 mbFormat = true;
559 mbFirstFormat = true;
560 mbSizeFormat = true;
561 mbAutoEditMode = false;
562 mbEditCanceled = false;
563 mbDropPos = false;
564 mbInSelect = false;
565 mbMirrored = false;
566 mbScrollAlwaysEnabled = false;
567 mbSheets = bSheets;
569 ImplInitControls();
571 SetSizePixel( Size( 100, CalcWindowSizePixel().Height() ) );
572 ImplInitSettings( true, true );
575 ImplTabBarItem* TabBar::seek( size_t i )
577 if ( i < mpImpl->maItemList.size() )
579 maCurrentItemList = i;
580 return &mpImpl->maItemList[maCurrentItemList];
582 return nullptr;
585 ImplTabBarItem* TabBar::prev()
587 if ( maCurrentItemList > 0 )
589 return &mpImpl->maItemList[--maCurrentItemList];
591 return nullptr;
594 ImplTabBarItem* TabBar::next()
596 if ( maCurrentItemList + 1 < mpImpl->maItemList.size() )
598 return &mpImpl->maItemList[++maCurrentItemList];
600 return nullptr;
603 void TabBar::ImplInitSettings( bool bFont, bool bBackground )
605 // FIXME RenderContext
607 const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
609 if (bFont)
611 vcl::Font aToolFont = rStyleSettings.GetToolFont();
612 aToolFont.SetWeight( WEIGHT_BOLD );
613 ApplyControlFont(*GetOutDev(), aToolFont);
615 // Adapt font size if window too small?
616 while (GetTextHeight() > (GetOutputSizePixel().Height() - 1))
618 vcl::Font aFont = GetFont();
619 if (aFont.GetFontHeight() <= 6)
620 break;
621 aFont.SetFontHeight(aFont.GetFontHeight() - 1);
622 SetFont(aFont);
626 if (bBackground)
628 ApplyControlBackground(*GetOutDev(), rStyleSettings.GetFaceColor());
632 void TabBar::ImplGetColors(const StyleSettings& rStyleSettings,
633 Color& rFaceColor, Color& rFaceTextColor,
634 Color& rSelectColor, Color& rSelectTextColor)
636 if (IsControlBackground())
637 rFaceColor = GetControlBackground();
638 else
639 rFaceColor = rStyleSettings.GetInactiveTabColor();
640 if (IsControlForeground())
641 rFaceTextColor = GetControlForeground();
642 else
643 rFaceTextColor = rStyleSettings.GetButtonTextColor();
644 rSelectColor = rStyleSettings.GetActiveTabColor();
645 rSelectTextColor = rStyleSettings.GetWindowTextColor();
648 bool TabBar::ImplCalcWidth()
650 // Sizes should only be retrieved if the text or the font was changed
651 if (!mbSizeFormat)
652 return false;
654 // retrieve width of tabs with bold font
655 vcl::Font aFont = GetFont();
656 if (aFont.GetWeight() != WEIGHT_BOLD)
658 aFont.SetWeight(WEIGHT_BOLD);
659 SetFont(aFont);
662 if (mnMaxPageWidth)
663 mnCurMaxWidth = mnMaxPageWidth;
664 else
666 mnCurMaxWidth = mnLastOffX - mnOffX;
667 if (mnCurMaxWidth < 1)
668 mnCurMaxWidth = 1;
671 bool bChanged = false;
672 for (auto& rItem : mpImpl->maItemList)
674 tools::Long nNewWidth = GetTextWidth(rItem.GetRenderText());
675 if (mnCurMaxWidth && (nNewWidth > mnCurMaxWidth))
677 rItem.mbShort = true;
678 nNewWidth = mnCurMaxWidth;
680 else
682 rItem.mbShort = false;
685 // Padding is dependent on font height - bigger font = bigger padding
686 tools::Long nFontWidth = aFont.GetFontHeight();
687 if (rItem.mbProtect)
688 nNewWidth += 24;
689 nNewWidth += nFontWidth * 2;
691 if (rItem.mnWidth != nNewWidth)
693 rItem.mnWidth = nNewWidth;
694 if (!rItem.maRect.IsEmpty())
695 bChanged = true;
698 mbSizeFormat = false;
699 mbFormat = true;
700 return bChanged;
703 void TabBar::ImplFormat()
705 ImplCalcWidth();
707 if (!mbFormat)
708 return;
710 sal_uInt16 nItemIndex = 0;
711 tools::Long x = mnOffX;
712 for (auto & rItem : mpImpl->maItemList)
714 // At all non-visible tabs an empty rectangle is set
715 if ((nItemIndex + 1 < mnFirstPos) || (x > mnLastOffX))
716 rItem.maRect.SetEmpty();
717 else
719 // Slightly before the tab before the first visible page
720 // should also be visible
721 if (nItemIndex + 1 == mnFirstPos)
723 rItem.maRect.SetLeft(x - rItem.mnWidth);
725 else
727 rItem.maRect.SetLeft(x);
728 x += rItem.mnWidth;
730 rItem.maRect.SetRight(x);
731 rItem.maRect.SetBottom(maWinSize.Height() - 1);
733 if (mbMirrored)
735 tools::Long nNewLeft = mnOffX + mnLastOffX - rItem.maRect.Right();
736 tools::Long nNewRight = mnOffX + mnLastOffX - rItem.maRect.Left();
737 rItem.maRect.SetRight(nNewRight);
738 rItem.maRect.SetLeft(nNewLeft);
742 nItemIndex++;
745 mbFormat = false;
747 // enable/disable button
748 ImplEnableControls();
751 sal_uInt16 TabBar::ImplGetLastFirstPos()
753 sal_uInt16 nCount = mpImpl->getItemSize();
754 if (!nCount || mbSizeFormat || mbFormat)
755 return 0;
757 sal_uInt16 nLastFirstPos = nCount - 1;
758 tools::Long nWinWidth = mnLastOffX - mnOffX - ADDNEWPAGE_AREAWIDTH;
759 tools::Long nWidth = mpImpl->maItemList[nLastFirstPos].mnWidth;
761 while (nLastFirstPos && (nWidth < nWinWidth))
763 nLastFirstPos--;
764 nWidth += mpImpl->maItemList[nLastFirstPos].mnWidth;
766 if ((nLastFirstPos != static_cast<sal_uInt16>(mpImpl->maItemList.size() - 1)) && (nWidth > nWinWidth))
767 nLastFirstPos++;
768 return nLastFirstPos;
771 IMPL_LINK(TabBar, ContextMenuHdl, const CommandEvent&, rCommandEvent, void)
773 maScrollAreaContextHdl.Call(rCommandEvent);
776 IMPL_LINK(TabBar, MousePressHdl, const MouseEvent&, rMouseEvent, bool)
778 if (rMouseEvent.IsRight())
779 ContextMenuHdl(CommandEvent(rMouseEvent.GetPosPixel(), CommandEventId::ContextMenu, true));
780 return false;
783 void TabBar::ImplInitControls()
785 if (mnWinStyle & WB_SIZEABLE)
787 if (!mpImpl->mpSizer)
789 mpImpl->mpSizer.disposeAndReset(VclPtr<ImplTabSizer>::Create( this, mnWinStyle & (WB_DRAG | WB_3DLOOK)));
791 mpImpl->mpSizer->Show();
793 else
795 mpImpl->mpSizer.disposeAndClear();
798 mpImpl->mxButtonBox.disposeAndReset(VclPtr<TabButtons>::Create(this, mbSheets));
800 Link<const CommandEvent&, void> aContextLink = LINK( this, TabBar, ContextMenuHdl );
802 if (mnWinStyle & WB_INSERTTAB)
804 Link<weld::Button&,void> aLink = LINK(this, TabBar, ImplAddClickHandler);
805 mpImpl->mxButtonBox->m_xAddRepeater = std::make_shared<weld::ButtonPressRepeater>(
806 *mpImpl->mxButtonBox->m_xAddButton, aLink, aContextLink);
807 mpImpl->mxButtonBox->m_xAddButton->show();
810 Link<weld::Button&,void> aLink = LINK( this, TabBar, ImplClickHdl );
812 if (mnWinStyle & (WB_MINSCROLL | WB_SCROLL))
814 mpImpl->mxButtonBox->m_xPrevRepeater = std::make_shared<weld::ButtonPressRepeater>(
815 *mpImpl->mxButtonBox->m_xPrevButton, aLink, aContextLink);
816 mpImpl->mxButtonBox->m_xPrevButton->show();
817 mpImpl->mxButtonBox->m_xNextRepeater = std::make_shared<weld::ButtonPressRepeater>(
818 *mpImpl->mxButtonBox->m_xNextButton, aLink, aContextLink);
819 mpImpl->mxButtonBox->m_xNextButton->show();
822 if (mnWinStyle & WB_SCROLL)
824 Link<const MouseEvent&, bool> aBtnContextLink = LINK(this, TabBar, MousePressHdl);
826 mpImpl->mxButtonBox->m_xFirstButton->connect_clicked(aLink);
827 mpImpl->mxButtonBox->m_xFirstButton->connect_mouse_press(aBtnContextLink);
828 mpImpl->mxButtonBox->m_xFirstButton->show();
829 mpImpl->mxButtonBox->m_xLastButton->connect_clicked(aLink);
830 mpImpl->mxButtonBox->m_xLastButton->connect_mouse_press(aBtnContextLink);
831 mpImpl->mxButtonBox->m_xLastButton->show();
834 mpImpl->mxButtonBox->Show();
837 void TabBar::ImplEnableControls()
839 if (mbSizeFormat || mbFormat)
840 return;
842 // enable/disable buttons
843 bool bEnableBtn = mbScrollAlwaysEnabled || mnFirstPos > 0;
844 mpImpl->mxButtonBox->m_xFirstButton->set_sensitive(bEnableBtn);
845 mpImpl->mxButtonBox->m_xPrevButton->set_sensitive(bEnableBtn);
846 if (!bEnableBtn && mpImpl->mxButtonBox->m_xPrevRepeater)
847 mpImpl->mxButtonBox->m_xPrevRepeater->Stop();
848 bEnableBtn = mbScrollAlwaysEnabled || mnFirstPos < ImplGetLastFirstPos();
849 mpImpl->mxButtonBox->m_xLastButton->set_sensitive(bEnableBtn);
850 mpImpl->mxButtonBox->m_xNextButton->set_sensitive(bEnableBtn);
851 if (!bEnableBtn && mpImpl->mxButtonBox->m_xNextRepeater)
852 mpImpl->mxButtonBox->m_xNextRepeater->Stop();
855 void TabBar::SetScrollAlwaysEnabled(bool bScrollAlwaysEnabled)
857 mbScrollAlwaysEnabled = bScrollAlwaysEnabled;
858 ImplEnableControls();
861 void TabBar::ImplShowPage( sal_uInt16 nPos )
863 if (nPos >= mpImpl->getItemSize())
864 return;
866 // calculate width
867 tools::Long nWidth = GetOutputSizePixel().Width();
869 auto& rItem = mpImpl->maItemList[nPos];
870 if (nPos < mnFirstPos)
871 SetFirstPageId( rItem.mnId );
872 else if (rItem.maRect.Right() > nWidth)
874 while (rItem.maRect.Right() > nWidth)
876 sal_uInt16 nNewPos = mnFirstPos + 1;
877 SetFirstPageId(GetPageId(nNewPos));
878 ImplFormat();
879 if (nNewPos != mnFirstPos)
880 break;
885 IMPL_LINK( TabBar, ImplClickHdl, weld::Button&, rBtn, void )
887 if (&rBtn != mpImpl->mxButtonBox->m_xFirstButton.get() && &rBtn != mpImpl->mxButtonBox->m_xLastButton.get())
889 if ((GetPointerState().mnState & (MOUSE_LEFT | MOUSE_MIDDLE | MOUSE_RIGHT)) == 0)
891 // like tdf#149482 if we didn't see a mouse up, but find that the mouse is no
892 // longer pressed at this point, then bail
893 mpImpl->mxButtonBox->m_xPrevRepeater->Stop();
894 mpImpl->mxButtonBox->m_xNextRepeater->Stop();
895 return;
899 EndEditMode();
901 sal_uInt16 nNewPos = mnFirstPos;
903 if (&rBtn == mpImpl->mxButtonBox->m_xFirstButton.get() || (&rBtn == mpImpl->mxButtonBox->m_xPrevButton.get() &&
904 mpImpl->mxButtonBox->m_xPrevRepeater->IsModKeyPressed()))
906 nNewPos = 0;
908 else if (&rBtn == mpImpl->mxButtonBox->m_xLastButton.get() || (&rBtn == mpImpl->mxButtonBox->m_xNextButton.get() &&
909 mpImpl->mxButtonBox->m_xNextRepeater->IsModKeyPressed()))
911 sal_uInt16 nCount = GetPageCount();
912 if (nCount)
913 nNewPos = nCount - 1;
915 else if (&rBtn == mpImpl->mxButtonBox->m_xPrevButton.get())
917 if (mnFirstPos)
918 nNewPos = mnFirstPos - 1;
920 else if (&rBtn == mpImpl->mxButtonBox->m_xNextButton.get())
922 sal_uInt16 nCount = GetPageCount();
923 if (mnFirstPos < nCount)
924 nNewPos = mnFirstPos+1;
926 else
928 return;
931 if (nNewPos != mnFirstPos)
932 SetFirstPageId(GetPageId(nNewPos));
935 IMPL_LINK_NOARG(TabBar, ImplAddClickHandler, weld::Button&, void)
937 if ((GetPointerState().mnState & (MOUSE_LEFT | MOUSE_MIDDLE | MOUSE_RIGHT)) == 0)
939 // tdf#149482 if we didn't see a mouse up, but find that the mouse is no
940 // longer pressed at this point, then bail
941 mpImpl->mxButtonBox->m_xAddRepeater->Stop();
942 return;
945 EndEditMode();
946 AddTabClick();
949 void TabBar::MouseMove(const MouseEvent& rMEvt)
951 if (rMEvt.IsLeaveWindow())
952 mbInSelect = false;
954 Window::MouseMove(rMEvt);
957 void TabBar::MouseButtonDown(const MouseEvent& rMEvt)
959 // Only terminate EditMode and do not execute click
960 // if clicked inside our window,
961 if (IsInEditMode())
963 EndEditMode();
964 return;
967 sal_uInt16 nSelId = GetPageId(rMEvt.GetPosPixel());
969 if (!rMEvt.IsLeft())
971 Window::MouseButtonDown(rMEvt);
972 if (nSelId > 0 && nSelId != mnCurPageId)
974 if (ImplDeactivatePage())
976 SetCurPageId(nSelId);
977 PaintImmediately();
978 ImplActivatePage();
979 ImplSelect();
981 mbInSelect = true;
983 return;
986 if (rMEvt.IsMod2() && mbAutoEditMode && nSelId)
988 if (StartEditMode(nSelId))
989 return;
992 if ((rMEvt.GetMode() & (MouseEventModifiers::MULTISELECT | MouseEventModifiers::RANGESELECT)) && (rMEvt.GetClicks() == 1))
994 if (nSelId)
996 sal_uInt16 nPos = GetPagePos(nSelId);
998 bool bSelectTab = false;
1000 if ((rMEvt.GetMode() & MouseEventModifiers::MULTISELECT) && (mnWinStyle & WB_MULTISELECT))
1002 if (nSelId != mnCurPageId)
1004 SelectPage(nSelId, !IsPageSelected(nSelId));
1005 bSelectTab = true;
1008 else if (mnWinStyle & (WB_MULTISELECT | WB_RANGESELECT))
1010 bSelectTab = true;
1011 sal_uInt16 n;
1012 bool bSelect;
1013 sal_uInt16 nCurPos = GetPagePos(mnCurPageId);
1014 if (nPos <= nCurPos)
1016 // Deselect all tabs till the clicked tab
1017 // and select all tabs from the clicked tab
1018 // 'till the actual position
1019 n = 0;
1020 while (n < nCurPos)
1022 auto& rItem = mpImpl->maItemList[n];
1023 bSelect = n >= nPos;
1025 if (rItem.mbSelect != bSelect)
1027 rItem.mbSelect = bSelect;
1028 if (!rItem.maRect.IsEmpty())
1029 Invalidate(rItem.maRect);
1032 n++;
1036 if (nPos >= nCurPos)
1038 // Select all tabs from the actual position till the clicked tab
1039 // and deselect all tabs from the actual position
1040 // till the last tab
1041 sal_uInt16 nCount = mpImpl->getItemSize();
1042 n = nCurPos;
1043 while (n < nCount)
1045 auto& rItem = mpImpl->maItemList[n];
1047 bSelect = n <= nPos;
1049 if (rItem.mbSelect != bSelect)
1051 rItem.mbSelect = bSelect;
1052 if (!rItem.maRect.IsEmpty())
1053 Invalidate(rItem.maRect);
1056 n++;
1061 // scroll the selected tab if required
1062 if (bSelectTab)
1064 ImplShowPage(nPos);
1065 PaintImmediately();
1066 ImplSelect();
1069 mbInSelect = true;
1071 return;
1074 else if (rMEvt.GetClicks() == 2)
1076 // call double-click-handler if required
1077 if (!rMEvt.GetModifier() && (!nSelId || (nSelId == mnCurPageId)))
1079 sal_uInt16 nOldCurId = mnCurPageId;
1080 mnCurPageId = nSelId;
1081 DoubleClick();
1082 // check, as actual page could be switched inside
1083 // the doubleclick-handler
1084 if (mnCurPageId == nSelId)
1085 mnCurPageId = nOldCurId;
1088 return;
1090 else
1092 if (nSelId)
1094 // execute Select if not actual page
1095 if (nSelId != mnCurPageId)
1097 sal_uInt16 nPos = GetPagePos(nSelId);
1098 auto& rItem = mpImpl->maItemList[nPos];
1100 if (!rItem.mbSelect)
1102 // make not valid
1103 bool bUpdate = false;
1104 if (IsReallyVisible() && IsUpdateMode())
1105 bUpdate = true;
1107 // deselect all selected items
1108 for (auto& xItem : mpImpl->maItemList)
1110 if (xItem.mbSelect || (xItem.mnId == mnCurPageId))
1112 xItem.mbSelect = false;
1113 if (bUpdate)
1114 Invalidate(xItem.maRect);
1119 if (ImplDeactivatePage())
1121 SetCurPageId(nSelId);
1122 PaintImmediately();
1123 ImplActivatePage();
1124 ImplSelect();
1127 mbInSelect = true;
1130 return;
1134 Window::MouseButtonDown(rMEvt);
1137 void TabBar::MouseButtonUp(const MouseEvent& rMEvt)
1139 mbInSelect = false;
1140 Window::MouseButtonUp(rMEvt);
1143 void TabBar::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rect)
1145 if (rRenderContext.IsNativeControlSupported(ControlType::WindowBackground,ControlPart::Entire))
1147 rRenderContext.DrawNativeControl(ControlType::WindowBackground,ControlPart::Entire,rect,
1148 ControlState::ENABLED,ImplControlValue(0),OUString());
1150 // calculate items and emit
1151 sal_uInt16 nItemCount = mpImpl->getItemSize();
1152 if (!nItemCount)
1153 return;
1155 ImplPrePaint();
1157 Color aFaceColor, aSelectColor, aFaceTextColor, aSelectTextColor;
1158 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
1159 ImplGetColors(rStyleSettings, aFaceColor, aFaceTextColor, aSelectColor, aSelectTextColor);
1161 rRenderContext.Push(vcl::PushFlags::FONT | vcl::PushFlags::CLIPREGION);
1162 rRenderContext.SetClipRegion(vcl::Region(GetPageArea()));
1164 // select font
1165 vcl::Font aFont = rRenderContext.GetFont();
1166 vcl::Font aLightFont = aFont;
1167 aLightFont.SetWeight(WEIGHT_NORMAL);
1169 TabDrawer aDrawer(rRenderContext);
1171 aDrawer.setSelectedFillColor(aSelectColor);
1173 // Now, start drawing the tabs.
1175 ImplTabBarItem* pItem = ImplGetLastTabBarItem(nItemCount);
1176 ImplTabBarItem* pCurItem = nullptr;
1177 while (pItem)
1179 // emit CurrentItem last, as it covers all others
1180 if (!pCurItem && (pItem->mnId == mnCurPageId))
1182 pCurItem = pItem;
1183 pItem = prev();
1184 if (!pItem)
1185 pItem = pCurItem;
1186 continue;
1189 bool bCurrent = pItem == pCurItem;
1191 if (!pItem->maRect.IsEmpty())
1193 tools::Rectangle aRect = pItem->maRect;
1194 bool bSelected = pItem->IsSelected(pCurItem);
1195 // We disable custom background color in high contrast mode.
1196 bool bCustomBgColor = !pItem->IsDefaultTabBgColor() && !rStyleSettings.GetHighContrastMode();
1197 OUString aText = pItem->mbShort ?
1198 rRenderContext.GetEllipsisString(pItem->GetRenderText(), mnCurMaxWidth) :
1199 pItem->GetRenderText();
1201 aDrawer.setRect(aRect);
1202 aDrawer.setSelected(bSelected);
1203 aDrawer.setCustomColored(bCustomBgColor);
1204 aDrawer.setEnabled(true);
1205 aDrawer.setCustomColor(pItem->maTabBgColor);
1206 aDrawer.mbProtect = pItem->mbProtect;
1207 aDrawer.drawTab();
1209 // currently visible sheet is drawn using a bold font
1210 if (bCurrent)
1211 rRenderContext.SetFont(aFont);
1212 else
1213 rRenderContext.SetFont(aLightFont);
1215 // Set the correct FillInBrush depending on status
1217 if (bSelected)
1218 rRenderContext.SetTextColor(aSelectTextColor);
1219 else if (bCustomBgColor)
1220 rRenderContext.SetTextColor(pItem->maTabTextColor);
1221 else
1222 rRenderContext.SetTextColor(aFaceTextColor);
1224 // Special display of tab name depending on page bits
1226 if (pItem->mnBits & TabBarPageBits::Blue)
1228 rRenderContext.SetTextColor(COL_LIGHTBLUE);
1230 if (pItem->mnBits & TabBarPageBits::Italic)
1232 vcl::Font aSpecialFont = rRenderContext.GetFont();
1233 aSpecialFont.SetItalic(FontItalic::ITALIC_NORMAL);
1234 rRenderContext.SetFont(aSpecialFont);
1236 if (pItem->mnBits & TabBarPageBits::Underline)
1238 vcl::Font aSpecialFont = rRenderContext.GetFont();
1239 aSpecialFont.SetUnderline(LINESTYLE_SINGLE);
1240 rRenderContext.SetFont(aSpecialFont);
1243 aDrawer.drawText(aText);
1245 if (bCurrent)
1247 rRenderContext.SetLineColor();
1248 rRenderContext.SetFillColor(aSelectColor);
1249 aDrawer.drawOverTopBorder();
1250 break;
1253 pItem = prev();
1255 else
1257 if (bCurrent)
1258 break;
1260 pItem = nullptr;
1263 if (!pItem)
1264 pItem = pCurItem;
1266 rRenderContext.Pop();
1269 void TabBar::Resize()
1271 Size aNewSize = GetOutputSizePixel();
1273 tools::Long nSizerWidth = 0;
1275 // order the Sizer
1276 if ( mpImpl->mpSizer )
1278 Size aSizerSize = mpImpl->mpSizer->GetSizePixel();
1279 Point aNewSizerPos( mbMirrored ? 0 : (aNewSize.Width()-aSizerSize.Width()), 0 );
1280 Size aNewSizerSize( aSizerSize.Width(), aNewSize.Height() );
1281 mpImpl->mpSizer->SetPosSizePixel( aNewSizerPos, aNewSizerSize );
1282 nSizerWidth = aSizerSize.Width();
1285 // order the scroll buttons
1286 tools::Long const nHeight = aNewSize.Height();
1287 // adapt font height?
1288 ImplInitSettings( true, false );
1290 mpImpl->mxButtonBox->AdaptToHeight(nHeight);
1291 Size const aBtnsSize(mpImpl->mxButtonBox->get_preferred_size().Width(), nHeight);
1292 Point aPos(mbMirrored ? (aNewSize.Width() - aBtnsSize.Width()) : 0, 0);
1293 mpImpl->mxButtonBox->SetPosSizePixel(aPos, aBtnsSize);
1294 auto nButtonWidth = aBtnsSize.Width();
1296 // store size
1297 maWinSize = aNewSize;
1299 if( mbMirrored )
1301 mnOffX = nSizerWidth;
1302 mnLastOffX = maWinSize.Width() - nButtonWidth - 1;
1304 else
1306 mnOffX = nButtonWidth;
1307 mnLastOffX = maWinSize.Width() - nSizerWidth - 1;
1310 // reformat
1311 mbSizeFormat = true;
1312 if ( IsReallyVisible() )
1314 if ( ImplCalcWidth() )
1315 Invalidate();
1317 ImplFormat();
1319 // Ensure as many tabs as possible are visible:
1320 sal_uInt16 nLastFirstPos = ImplGetLastFirstPos();
1321 if ( mnFirstPos > nLastFirstPos )
1323 mnFirstPos = nLastFirstPos;
1324 mbFormat = true;
1325 Invalidate();
1327 // Ensure the currently selected page is visible
1328 ImplShowPage(GetPagePos(mnCurPageId));
1330 ImplFormat();
1333 // enable/disable button
1334 ImplEnableControls();
1337 bool TabBar::PreNotify(NotifyEvent& rNEvt)
1339 if (rNEvt.GetType() == NotifyEventType::COMMAND)
1341 if (rNEvt.GetCommandEvent()->GetCommand() == CommandEventId::Wheel)
1343 const CommandWheelData* pData = rNEvt.GetCommandEvent()->GetWheelData();
1344 sal_uInt16 nNewPos = mnFirstPos;
1345 if (pData->GetNotchDelta() > 0)
1347 if (mnFirstPos)
1348 nNewPos = mnFirstPos - 1;
1350 else if (pData->GetNotchDelta() < 0)
1352 sal_uInt16 nCount = GetPageCount();
1353 if (mnFirstPos < nCount)
1354 nNewPos = mnFirstPos + 1;
1356 if (nNewPos != mnFirstPos)
1357 SetFirstPageId(GetPageId(nNewPos));
1360 return Window::PreNotify(rNEvt);
1363 void TabBar::RequestHelp(const HelpEvent& rHEvt)
1365 sal_uInt16 nItemId = GetPageId(ScreenToOutputPixel(rHEvt.GetMousePosPixel()));
1366 if (nItemId)
1368 if (rHEvt.GetMode() & HelpEventMode::BALLOON)
1370 OUString aStr = GetHelpText(nItemId);
1371 if (!aStr.isEmpty())
1373 tools::Rectangle aItemRect = GetPageRect(nItemId);
1374 Point aPt = OutputToScreenPixel(aItemRect.TopLeft());
1375 aItemRect.SetLeft( aPt.X() );
1376 aItemRect.SetTop( aPt.Y() );
1377 aPt = OutputToScreenPixel(aItemRect.BottomRight());
1378 aItemRect.SetRight( aPt.X() );
1379 aItemRect.SetBottom( aPt.Y() );
1380 Help::ShowBalloon(this, aItemRect.Center(), aItemRect, aStr);
1381 return;
1385 // show text for quick- or balloon-help
1386 // if this is isolated or not fully visible
1387 if (rHEvt.GetMode() & (HelpEventMode::QUICK | HelpEventMode::BALLOON))
1389 sal_uInt16 nPos = GetPagePos(nItemId);
1390 auto& rItem = mpImpl->maItemList[nPos];
1391 if (rItem.mbShort || (rItem.maRect.Right() - 5 > mnLastOffX))
1393 tools::Rectangle aItemRect = GetPageRect(nItemId);
1394 Point aPt = OutputToScreenPixel(aItemRect.TopLeft());
1395 aItemRect.SetLeft( aPt.X() );
1396 aItemRect.SetTop( aPt.Y() );
1397 aPt = OutputToScreenPixel(aItemRect.BottomRight());
1398 aItemRect.SetRight( aPt.X() );
1399 aItemRect.SetBottom( aPt.Y() );
1400 OUString aStr = mpImpl->maItemList[nPos].maText;
1401 if (!aStr.isEmpty())
1403 if (rHEvt.GetMode() & HelpEventMode::BALLOON)
1404 Help::ShowBalloon(this, aItemRect.Center(), aItemRect, aStr);
1405 else
1406 Help::ShowQuickHelp(this, aItemRect, aStr);
1407 return;
1413 Window::RequestHelp(rHEvt);
1416 void TabBar::StateChanged(StateChangedType nType)
1418 Window::StateChanged(nType);
1420 if (nType == StateChangedType::InitShow)
1422 if ( (mbSizeFormat || mbFormat) && !mpImpl->maItemList.empty() )
1423 ImplFormat();
1425 else if (nType == StateChangedType::Zoom ||
1426 nType == StateChangedType::ControlFont)
1428 ImplInitSettings(true, false);
1429 Invalidate();
1431 else if (nType == StateChangedType::ControlForeground)
1432 Invalidate();
1433 else if (nType == StateChangedType::ControlBackground)
1435 ImplInitSettings(false, true);
1436 Invalidate();
1438 else if (nType == StateChangedType::Mirroring)
1440 bool bIsRTLEnabled = IsRTLEnabled();
1441 // reacts on calls of EnableRTL, have to mirror all child controls
1442 if (mpImpl->mpSizer)
1443 mpImpl->mpSizer->EnableRTL(bIsRTLEnabled);
1444 if (mpImpl->mxButtonBox)
1446 mpImpl->mxButtonBox->m_xFirstButton->set_direction(bIsRTLEnabled);
1447 mpImpl->mxButtonBox->m_xPrevButton->set_direction(bIsRTLEnabled);
1448 mpImpl->mxButtonBox->m_xNextButton->set_direction(bIsRTLEnabled);
1449 mpImpl->mxButtonBox->m_xLastButton->set_direction(bIsRTLEnabled);
1450 mpImpl->mxButtonBox->m_xAddButton->set_direction(bIsRTLEnabled);
1452 if (mpImpl->mxEdit)
1454 weld::Entry& rEntry = mpImpl->mxEdit->get_widget();
1455 rEntry.set_direction(bIsRTLEnabled);
1460 void TabBar::DataChanged(const DataChangedEvent& rDCEvt)
1462 Window::DataChanged(rDCEvt);
1464 if (rDCEvt.GetType() == DataChangedEventType::FONTS
1465 || rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION
1466 || (rDCEvt.GetType() == DataChangedEventType::SETTINGS
1467 && rDCEvt.GetFlags() & AllSettingsFlags::STYLE))
1469 ImplInitSettings(true, true);
1470 Invalidate();
1474 void TabBar::ImplSelect()
1476 Select();
1477 CallEventListeners(VclEventId::TabbarPageSelected, reinterpret_cast<void*>(sal::static_int_cast<sal_IntPtr>(mnCurPageId)));
1480 void TabBar::Select()
1482 maSelectHdl.Call(this);
1485 void TabBar::DoubleClick()
1489 void TabBar::Split()
1491 maSplitHdl.Call(this);
1494 void TabBar::ImplActivatePage()
1496 ActivatePage();
1498 CallEventListeners(VclEventId::TabbarPageActivated, reinterpret_cast<void*>(sal::static_int_cast<sal_IntPtr>(mnCurPageId)));
1501 void TabBar::ActivatePage()
1504 bool TabBar::ImplDeactivatePage()
1506 bool bRet = DeactivatePage();
1508 CallEventListeners(VclEventId::TabbarPageDeactivated, reinterpret_cast<void*>(sal::static_int_cast<sal_IntPtr>(mnCurPageId)));
1510 return bRet;
1513 void TabBar::ImplPrePaint()
1515 sal_uInt16 nItemCount = mpImpl->getItemSize();
1516 if (!nItemCount)
1517 return;
1519 // tabbar should be formatted
1520 ImplFormat();
1522 // assure the actual tabpage becomes visible at first format
1523 if (!mbFirstFormat)
1524 return;
1526 mbFirstFormat = false;
1528 if (!mnCurPageId || (mnFirstPos != 0) || mbDropPos)
1529 return;
1531 auto& rItem = mpImpl->maItemList[GetPagePos(mnCurPageId)];
1532 if (rItem.maRect.IsEmpty())
1534 // set mbDropPos (or misuse) to prevent Invalidate()
1535 mbDropPos = true;
1536 SetFirstPageId(mnCurPageId);
1537 mbDropPos = false;
1538 if (mnFirstPos != 0)
1539 ImplFormat();
1543 ImplTabBarItem* TabBar::ImplGetLastTabBarItem( sal_uInt16 nItemCount )
1545 // find last visible entry
1546 sal_uInt16 n = mnFirstPos + 1;
1547 if (n >= nItemCount)
1548 n = nItemCount-1;
1549 ImplTabBarItem* pItem = seek(n);
1550 while (pItem)
1552 if (!pItem->maRect.IsEmpty())
1554 n++;
1555 pItem = next();
1557 else
1558 break;
1561 // draw all tabs (from back to front and actual last)
1562 if (pItem)
1563 n--;
1564 else if (n >= nItemCount)
1565 n = nItemCount-1;
1566 pItem = seek(n);
1567 return pItem;
1570 bool TabBar::DeactivatePage()
1572 return true;
1575 bool TabBar::StartRenaming()
1577 return true;
1580 TabBarAllowRenamingReturnCode TabBar::AllowRenaming()
1582 return TABBAR_RENAMING_YES;
1585 void TabBar::EndRenaming()
1589 void TabBar::Mirror()
1594 void TabBar::AddTabClick()
1599 void TabBar::InsertPage(sal_uInt16 nPageId, const OUString& rText,
1600 TabBarPageBits nBits, sal_uInt16 nPos)
1602 assert (nPageId && "TabBar::InsertPage(): PageId must not be 0");
1603 assert ((GetPagePos(nPageId) == PAGE_NOT_FOUND) && "TabBar::InsertPage(): Page already exists");
1604 assert ((nBits <= TPB_DISPLAY_NAME_ALLFLAGS) && "TabBar::InsertPage(): Invalid flag set in nBits");
1606 // create PageItem and insert in the item list
1607 ImplTabBarItem aItem( nPageId, rText, nBits );
1608 if (nPos < mpImpl->maItemList.size())
1610 auto it = mpImpl->maItemList.begin();
1611 it += nPos;
1612 mpImpl->maItemList.insert(it, aItem);
1614 else
1616 mpImpl->maItemList.push_back(aItem);
1618 mbSizeFormat = true;
1620 // set CurPageId if required
1621 if (!mnCurPageId)
1622 mnCurPageId = nPageId;
1624 // redraw bar
1625 if (IsReallyVisible() && IsUpdateMode())
1626 Invalidate();
1628 CallEventListeners(VclEventId::TabbarPageInserted, reinterpret_cast<void*>(sal::static_int_cast<sal_IntPtr>(nPageId)));
1631 Color TabBar::GetTabBgColor(sal_uInt16 nPageId) const
1633 sal_uInt16 nPos = GetPagePos(nPageId);
1635 if (nPos != PAGE_NOT_FOUND)
1636 return mpImpl->maItemList[nPos].maTabBgColor;
1637 else
1638 return COL_AUTO;
1641 void TabBar::SetTabBgColor(sal_uInt16 nPageId, const Color& aTabBgColor)
1643 sal_uInt16 nPos = GetPagePos(nPageId);
1644 if (nPos == PAGE_NOT_FOUND)
1645 return;
1647 auto& rItem = mpImpl->maItemList[nPos];
1648 if (aTabBgColor != COL_AUTO)
1650 rItem.maTabBgColor = aTabBgColor;
1651 if (aTabBgColor.GetLuminance() <= 128) //Do not use aTabBgColor.IsDark(), because that threshold is way too low...
1652 rItem.maTabTextColor = COL_WHITE;
1653 else
1654 rItem.maTabTextColor = COL_BLACK;
1656 else
1658 rItem.maTabBgColor = COL_AUTO;
1659 rItem.maTabTextColor = COL_AUTO;
1663 void TabBar::RemovePage(sal_uInt16 nPageId)
1665 sal_uInt16 nPos = GetPagePos(nPageId);
1667 // does item exist
1668 if (nPos == PAGE_NOT_FOUND)
1669 return;
1671 if (mnCurPageId == nPageId)
1672 mnCurPageId = 0;
1674 // check if first visible page should be moved
1675 if (mnFirstPos > nPos)
1676 mnFirstPos--;
1678 // delete item data
1679 auto it = mpImpl->maItemList.begin();
1680 it += nPos;
1681 mpImpl->maItemList.erase(it);
1683 // redraw bar
1684 if (IsReallyVisible() && IsUpdateMode())
1685 Invalidate();
1687 CallEventListeners(VclEventId::TabbarPageRemoved, reinterpret_cast<void*>(sal::static_int_cast<sal_IntPtr>(nPageId)));
1690 void TabBar::MovePage(sal_uInt16 nPageId, sal_uInt16 nNewPos)
1692 sal_uInt16 nPos = GetPagePos(nPageId);
1693 Pair aPair(nPos, nNewPos);
1695 if (nPos < nNewPos)
1696 nNewPos--;
1698 if (nPos == nNewPos)
1699 return;
1701 // does item exit
1702 if (nPos == PAGE_NOT_FOUND)
1703 return;
1705 // move tabbar item in the list
1706 auto it = mpImpl->maItemList.begin();
1707 it += nPos;
1708 ImplTabBarItem aItem = std::move(*it);
1709 mpImpl->maItemList.erase(it);
1710 if (nNewPos < mpImpl->maItemList.size())
1712 it = mpImpl->maItemList.begin();
1713 it += nNewPos;
1714 mpImpl->maItemList.insert(it, aItem);
1716 else
1718 mpImpl->maItemList.push_back(aItem);
1721 // redraw bar
1722 if (IsReallyVisible() && IsUpdateMode())
1723 Invalidate();
1725 CallEventListeners( VclEventId::TabbarPageMoved, static_cast<void*>(&aPair) );
1728 void TabBar::Clear()
1730 // delete all items
1731 mpImpl->maItemList.clear();
1733 // remove items from the list
1734 mbSizeFormat = true;
1735 mnCurPageId = 0;
1736 mnFirstPos = 0;
1737 maCurrentItemList = 0;
1739 // redraw bar
1740 if (IsReallyVisible() && IsUpdateMode())
1741 Invalidate();
1743 CallEventListeners(VclEventId::TabbarPageRemoved, reinterpret_cast<void*>(sal::static_int_cast<sal_IntPtr>(PAGE_NOT_FOUND)));
1746 bool TabBar::IsPageEnabled(sal_uInt16 nPageId) const
1748 if (isDisposed())
1749 return false;
1750 sal_uInt16 nPos = GetPagePos(nPageId);
1752 return nPos != PAGE_NOT_FOUND;
1755 void TabBar::SetPageBits(sal_uInt16 nPageId, TabBarPageBits nBits)
1757 sal_uInt16 nPos = GetPagePos(nPageId);
1759 if (nPos == PAGE_NOT_FOUND)
1760 return;
1762 auto& rItem = mpImpl->maItemList[nPos];
1764 if (rItem.mnBits != nBits)
1766 rItem.mnBits = nBits;
1768 // redraw bar
1769 if (IsReallyVisible() && IsUpdateMode())
1770 Invalidate(rItem.maRect);
1774 TabBarPageBits TabBar::GetPageBits(sal_uInt16 nPageId) const
1776 sal_uInt16 nPos = GetPagePos(nPageId);
1778 if (nPos != PAGE_NOT_FOUND)
1779 return mpImpl->maItemList[nPos].mnBits;
1780 else
1781 return TabBarPageBits::NONE;
1784 sal_uInt16 TabBar::GetPageCount() const
1786 return mpImpl->getItemSize();
1789 sal_uInt16 TabBar::GetPageId(sal_uInt16 nPos) const
1791 return nPos < mpImpl->maItemList.size() ? mpImpl->maItemList[nPos].mnId : 0;
1794 sal_uInt16 TabBar::GetPagePos(sal_uInt16 nPageId) const
1796 for (size_t i = 0; i < mpImpl->maItemList.size(); ++i)
1798 if (mpImpl->maItemList[i].mnId == nPageId)
1800 return static_cast<sal_uInt16>(i);
1803 return PAGE_NOT_FOUND;
1806 sal_uInt16 TabBar::GetPageId(const Point& rPos) const
1808 for (const auto& rItem : mpImpl->maItemList)
1810 if (rItem.maRect.Contains(rPos))
1811 return rItem.mnId;
1814 return 0;
1817 tools::Rectangle TabBar::GetPageRect(sal_uInt16 nPageId) const
1819 sal_uInt16 nPos = GetPagePos(nPageId);
1821 if (nPos != PAGE_NOT_FOUND)
1822 return mpImpl->maItemList[nPos].maRect;
1823 else
1824 return tools::Rectangle();
1827 void TabBar::SetCurPageId(sal_uInt16 nPageId)
1829 sal_uInt16 nPos = GetPagePos(nPageId);
1831 // do nothing if item does not exit
1832 if (nPos == PAGE_NOT_FOUND)
1833 return;
1835 // do nothing if the actual page did not change
1836 if (nPageId == mnCurPageId)
1837 return;
1839 // make invalid
1840 bool bUpdate = false;
1841 if (IsReallyVisible() && IsUpdateMode())
1842 bUpdate = true;
1844 auto& rItem = mpImpl->maItemList[nPos];
1845 ImplTabBarItem* pOldItem;
1847 if (mnCurPageId)
1848 pOldItem = &mpImpl->maItemList[GetPagePos(mnCurPageId)];
1849 else
1850 pOldItem = nullptr;
1852 // deselect previous page if page was not selected, if this is the
1853 // only selected page
1854 if (!rItem.mbSelect && pOldItem)
1856 sal_uInt16 nSelPageCount = GetSelectPageCount();
1857 if (nSelPageCount == 1)
1858 pOldItem->mbSelect = false;
1859 rItem.mbSelect = true;
1862 mnCurPageId = nPageId;
1863 mbFormat = true;
1865 // assure the actual page becomes visible
1866 if (IsReallyVisible())
1868 if (nPos < mnFirstPos)
1869 SetFirstPageId(nPageId);
1870 else
1872 // calculate visible width
1873 tools::Long nWidth = mnLastOffX;
1874 if (nWidth > ADDNEWPAGE_AREAWIDTH)
1875 nWidth -= ADDNEWPAGE_AREAWIDTH;
1877 if (rItem.maRect.IsEmpty())
1878 ImplFormat();
1880 while ((mbMirrored ? (rItem.maRect.Left() < mnOffX) : (rItem.maRect.Right() > nWidth)) ||
1881 rItem.maRect.IsEmpty())
1883 sal_uInt16 nNewPos = mnFirstPos + 1;
1884 // assure at least the actual tabpages are visible as first tabpage
1885 if (nNewPos >= nPos)
1887 SetFirstPageId(nPageId);
1888 break;
1890 else
1891 SetFirstPageId(GetPageId(nNewPos));
1892 ImplFormat();
1893 // abort if first page is not forwarded
1894 if (nNewPos != mnFirstPos)
1895 break;
1900 // redraw bar
1901 if (bUpdate)
1903 Invalidate(rItem.maRect);
1904 if (pOldItem)
1905 Invalidate(pOldItem->maRect);
1909 void TabBar::MakeVisible(sal_uInt16 nPageId)
1911 if (!IsReallyVisible())
1912 return;
1914 sal_uInt16 nPos = GetPagePos(nPageId);
1916 // do nothing if item does not exist
1917 if (nPos == PAGE_NOT_FOUND)
1918 return;
1920 if (nPos < mnFirstPos)
1921 SetFirstPageId(nPageId);
1922 else
1924 auto& rItem = mpImpl->maItemList[nPos];
1926 // calculate visible area
1927 tools::Long nWidth = mnLastOffX;
1929 if (mbFormat || rItem.maRect.IsEmpty())
1931 mbFormat = true;
1932 ImplFormat();
1935 while ((rItem.maRect.Right() > nWidth) ||
1936 rItem.maRect.IsEmpty())
1938 sal_uInt16 nNewPos = mnFirstPos+1;
1939 // assure at least the actual tabpages are visible as first tabpage
1940 if (nNewPos >= nPos)
1942 SetFirstPageId(nPageId);
1943 break;
1945 else
1946 SetFirstPageId(GetPageId(nNewPos));
1947 ImplFormat();
1948 // abort if first page is not forwarded
1949 if (nNewPos != mnFirstPos)
1950 break;
1955 void TabBar::SetFirstPageId(sal_uInt16 nPageId)
1957 sal_uInt16 nPos = GetPagePos(nPageId);
1959 // return false if item does not exist
1960 if (nPos == PAGE_NOT_FOUND)
1961 return;
1963 if (nPos == mnFirstPos)
1964 return;
1966 // assure as much pages are visible as possible
1967 ImplFormat();
1968 sal_uInt16 nLastFirstPos = ImplGetLastFirstPos();
1969 sal_uInt16 nNewPos;
1970 if (nPos > nLastFirstPos)
1971 nNewPos = nLastFirstPos;
1972 else
1973 nNewPos = nPos;
1975 if (nNewPos != mnFirstPos)
1977 mnFirstPos = nNewPos;
1978 mbFormat = true;
1980 // redraw bar (attention: check mbDropPos,
1981 // as if this flag was set, we do not re-paint immediately
1982 if (IsReallyVisible() && IsUpdateMode() && !mbDropPos)
1983 Invalidate();
1987 void TabBar::SelectPage(sal_uInt16 nPageId, bool bSelect)
1989 sal_uInt16 nPos = GetPagePos(nPageId);
1991 if (nPos == PAGE_NOT_FOUND)
1992 return;
1994 auto& rItem = mpImpl->maItemList[nPos];
1996 if (rItem.mbSelect != bSelect)
1998 rItem.mbSelect = bSelect;
2000 // redraw bar
2001 if (IsReallyVisible() && IsUpdateMode())
2002 Invalidate(rItem.maRect);
2006 sal_uInt16 TabBar::GetSelectPageCount() const
2008 sal_uInt16 nSelected = 0;
2009 for (const auto& rItem : mpImpl->maItemList)
2011 if (rItem.mbSelect)
2012 nSelected++;
2015 return nSelected;
2018 bool TabBar::IsPageSelected(sal_uInt16 nPageId) const
2020 sal_uInt16 nPos = GetPagePos(nPageId);
2021 if (nPos != PAGE_NOT_FOUND)
2022 return mpImpl->maItemList[nPos].mbSelect;
2023 else
2024 return false;
2027 void TabBar::SetProtectionSymbol(sal_uInt16 nPageId, bool bProtection)
2029 sal_uInt16 nPos = GetPagePos(nPageId);
2030 if (nPos != PAGE_NOT_FOUND)
2032 if (mpImpl->maItemList[nPos].mbProtect != bProtection)
2034 mpImpl->maItemList[nPos].mbProtect = bProtection;
2035 mbSizeFormat = true; // render text width changes, thus bar width
2037 // redraw bar
2038 if (IsReallyVisible() && IsUpdateMode())
2039 Invalidate();
2044 bool TabBar::StartEditMode(sal_uInt16 nPageId)
2046 sal_uInt16 nPos = GetPagePos( nPageId );
2047 if (mpImpl->mxEdit || (nPos == PAGE_NOT_FOUND) || (mnLastOffX < 8))
2048 return false;
2050 mnEditId = nPageId;
2051 if (StartRenaming())
2053 ImplShowPage(nPos);
2054 ImplFormat();
2055 PaintImmediately();
2057 mpImpl->mxEdit.disposeAndReset(VclPtr<TabBarEdit>::Create(this));
2058 tools::Rectangle aRect = GetPageRect( mnEditId );
2059 tools::Long nX = aRect.Left();
2060 tools::Long nWidth = aRect.GetWidth();
2061 if (mnEditId != GetCurPageId())
2062 nX += 1;
2063 if (nX + nWidth > mnLastOffX)
2064 nWidth = mnLastOffX-nX;
2065 if (nWidth < 3)
2067 nX = aRect.Left();
2068 nWidth = aRect.GetWidth();
2070 weld::Entry& rEntry = mpImpl->mxEdit->get_widget();
2071 rEntry.set_text(GetPageText(mnEditId));
2072 mpImpl->mxEdit->SetPosSizePixel(Point(nX, aRect.Top() + mnOffY + 1), Size(nWidth, aRect.GetHeight() - 3));
2073 vcl::Font aFont = GetPointFont(*GetOutDev()); // FIXME RenderContext
2075 Color aForegroundColor;
2076 Color aBackgroundColor;
2077 Color aFaceColor;
2078 Color aSelectColor;
2079 Color aFaceTextColor;
2080 Color aSelectTextColor;
2082 ImplGetColors(Application::GetSettings().GetStyleSettings(), aFaceColor, aFaceTextColor, aSelectColor, aSelectTextColor);
2084 if (mnEditId != GetCurPageId())
2086 aFont.SetWeight(WEIGHT_LIGHT);
2088 if (IsPageSelected(mnEditId) || mnEditId == GetCurPageId())
2090 aForegroundColor = aSelectTextColor;
2091 aBackgroundColor = aSelectColor;
2093 else
2095 aForegroundColor = aFaceTextColor;
2096 aBackgroundColor = aFaceColor;
2098 if (GetPageBits( mnEditId ) & TabBarPageBits::Blue)
2100 aForegroundColor = COL_LIGHTBLUE;
2102 rEntry.set_font(aFont);
2103 rEntry.set_font_color(aForegroundColor);
2104 mpImpl->mxEdit->SetControlBackground(aBackgroundColor);
2105 rEntry.grab_focus();
2106 rEntry.select_region(0, -1);
2107 mpImpl->mxEdit->Show();
2108 return true;
2110 else
2112 mnEditId = 0;
2113 return false;
2117 bool TabBar::IsInEditMode() const
2119 return bool(mpImpl->mxEdit);
2122 void TabBar::EndEditMode(bool bCancel)
2124 if (!mpImpl->mxEdit)
2125 return;
2127 // call hdl
2128 bool bEnd = true;
2129 mbEditCanceled = bCancel;
2130 weld::Entry& rEntry = mpImpl->mxEdit->get_widget();
2131 maEditText = rEntry.get_text();
2132 mpImpl->mxEdit->SetPostEvent();
2133 if (!bCancel)
2135 TabBarAllowRenamingReturnCode nAllowRenaming = AllowRenaming();
2136 if (nAllowRenaming == TABBAR_RENAMING_YES)
2137 SetPageText(mnEditId, maEditText);
2138 else if (nAllowRenaming == TABBAR_RENAMING_NO)
2139 bEnd = false;
2140 else // nAllowRenaming == TABBAR_RENAMING_CANCEL
2141 mbEditCanceled = true;
2144 // renaming not allowed, then reset edit data
2145 if (!bEnd)
2147 mpImpl->mxEdit->ResetPostEvent();
2148 rEntry.grab_focus();
2150 else
2152 // close edit and call end hdl
2153 mpImpl->mxEdit.disposeAndClear();
2155 EndRenaming();
2156 mnEditId = 0;
2159 // reset
2160 maEditText.clear();
2161 mbEditCanceled = false;
2164 void TabBar::SetMirrored(bool bMirrored)
2166 if (mbMirrored != bMirrored)
2168 mbMirrored = bMirrored;
2169 mbSizeFormat = true;
2170 ImplInitControls(); // for button images
2171 Resize(); // recalculates control positions
2172 Mirror();
2176 void TabBar::SetEffectiveRTL(bool bRTL)
2178 SetMirrored( bRTL != AllSettings::GetLayoutRTL() );
2181 bool TabBar::IsEffectiveRTL() const
2183 return IsMirrored() != AllSettings::GetLayoutRTL();
2186 void TabBar::SetMaxPageWidth(tools::Long nMaxWidth)
2188 if (mnMaxPageWidth != nMaxWidth)
2190 mnMaxPageWidth = nMaxWidth;
2191 mbSizeFormat = true;
2193 // redraw bar
2194 if (IsReallyVisible() && IsUpdateMode())
2195 Invalidate();
2199 void TabBar::SetPageText(sal_uInt16 nPageId, const OUString& rText)
2201 sal_uInt16 nPos = GetPagePos(nPageId);
2202 if (nPos != PAGE_NOT_FOUND)
2204 mpImpl->maItemList[nPos].maText = rText;
2205 mbSizeFormat = true;
2207 // redraw bar
2208 if (IsReallyVisible() && IsUpdateMode())
2209 Invalidate();
2211 CallEventListeners(VclEventId::TabbarPageTextChanged, reinterpret_cast<void*>(sal::static_int_cast<sal_IntPtr>(nPageId)));
2215 const OUString & TabBar::GetPageText(sal_uInt16 nPageId) const
2217 sal_uInt16 nPos = GetPagePos(nPageId);
2218 if (nPos != PAGE_NOT_FOUND)
2219 return mpImpl->maItemList[nPos].maText;
2220 return EMPTY_OUSTRING;
2223 const OUString & TabBar::GetAuxiliaryText(sal_uInt16 nPageId) const
2225 sal_uInt16 nPos = GetPagePos(nPageId);
2226 if (nPos != PAGE_NOT_FOUND)
2227 return mpImpl->maItemList[nPos].maAuxiliaryText;
2228 return EMPTY_OUSTRING;
2231 void TabBar::SetAuxiliaryText(sal_uInt16 nPageId, const OUString& rText )
2233 sal_uInt16 nPos = GetPagePos(nPageId);
2234 if (nPos != PAGE_NOT_FOUND)
2236 mpImpl->maItemList[nPos].maAuxiliaryText = rText;
2237 // no redraw bar, no CallEventListener, internal use in LayerTabBar
2241 OUString TabBar::GetHelpText(sal_uInt16 nPageId) const
2243 sal_uInt16 nPos = GetPagePos(nPageId);
2244 if (nPos != PAGE_NOT_FOUND)
2246 auto& rItem = mpImpl->maItemList[nPos];
2247 if (rItem.maHelpText.isEmpty() && !rItem.maHelpId.isEmpty())
2249 Help* pHelp = Application::GetHelp();
2250 if (pHelp)
2251 rItem.maHelpText = pHelp->GetHelpText(OStringToOUString(rItem.maHelpId, RTL_TEXTENCODING_UTF8));
2254 return rItem.maHelpText;
2256 return OUString();
2259 bool TabBar::StartDrag(const CommandEvent& rCEvt, vcl::Region& rRegion)
2261 if (!(mnWinStyle & WB_DRAG) || (rCEvt.GetCommand() != CommandEventId::StartDrag))
2262 return false;
2264 // Check if the clicked page was selected. If this is not the case
2265 // set it as actual entry. We check for this only at a mouse action
2266 // if Drag and Drop can be triggered from the keyboard.
2267 // We only do this, if Select() was not triggered, as the Select()
2268 // could have scrolled the area
2269 if (rCEvt.IsMouseEvent() && !mbInSelect)
2271 sal_uInt16 nSelId = GetPageId(rCEvt.GetMousePosPixel());
2273 // do not start dragging if no entry was clicked
2274 if (!nSelId)
2275 return false;
2277 // check if page was selected. If not set it as actual
2278 // page and call Select()
2279 if (!IsPageSelected(nSelId))
2281 if (ImplDeactivatePage())
2283 SetCurPageId(nSelId);
2284 PaintImmediately();
2285 ImplActivatePage();
2286 ImplSelect();
2288 else
2289 return false;
2292 mbInSelect = false;
2294 // assign region
2295 rRegion = vcl::Region();
2297 return true;
2300 sal_uInt16 TabBar::ShowDropPos(const Point& rPos)
2302 sal_uInt16 nNewDropPos;
2303 sal_uInt16 nItemCount = mpImpl->getItemSize();
2304 sal_Int16 nScroll = 0;
2306 if (rPos.X() > mnLastOffX-TABBAR_DRAG_SCROLLOFF)
2308 auto& rItem = mpImpl->maItemList[mpImpl->maItemList.size() - 1];
2309 if (!rItem.maRect.IsEmpty() && (rPos.X() > rItem.maRect.Right()))
2310 nNewDropPos = mpImpl->getItemSize();
2311 else
2313 nNewDropPos = mnFirstPos + 1;
2314 nScroll = 1;
2317 else if ((rPos.X() <= mnOffX) ||
2318 (!mnOffX && (rPos.X() <= TABBAR_DRAG_SCROLLOFF)))
2320 if (mnFirstPos)
2322 nNewDropPos = mnFirstPos;
2323 nScroll = -1;
2325 else
2326 nNewDropPos = 0;
2328 else
2330 sal_uInt16 nDropId = GetPageId(rPos);
2331 if (nDropId)
2333 nNewDropPos = GetPagePos(nDropId);
2334 if (mnFirstPos && (nNewDropPos == mnFirstPos - 1))
2335 nScroll = -1;
2337 else
2338 nNewDropPos = nItemCount;
2341 if (mbDropPos && (nNewDropPos == mnDropPos) && !nScroll)
2342 return mnDropPos;
2344 if (mbDropPos)
2345 HideDropPos();
2346 mbDropPos = true;
2347 mnDropPos = nNewDropPos;
2349 if (nScroll)
2351 sal_uInt16 nOldFirstPos = mnFirstPos;
2352 SetFirstPageId(GetPageId(mnFirstPos + nScroll));
2354 // draw immediately, as Paint not possible during Drag and Drop
2355 if (nOldFirstPos != mnFirstPos)
2357 tools::Rectangle aRect(mnOffX, 0, mnLastOffX, maWinSize.Height());
2358 GetOutDev()->SetFillColor(GetBackground().GetColor());
2359 GetOutDev()->DrawRect(aRect);
2360 Invalidate(aRect);
2364 // draw drop position arrows
2365 const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings();
2366 const Color aTextColor = rStyles.GetLabelTextColor();
2367 tools::Long nX;
2368 tools::Long nY = (maWinSize.Height() / 2) - 1;
2369 sal_uInt16 nCurPos = GetPagePos(mnCurPageId);
2371 sal_Int32 nTriangleWidth = 3 * GetDPIScaleFactor();
2373 if (mnDropPos < nItemCount)
2375 GetOutDev()->SetLineColor(aTextColor);
2376 GetOutDev()->SetFillColor(aTextColor);
2378 auto& rItem = mpImpl->maItemList[mnDropPos];
2379 nX = rItem.maRect.Left();
2380 if ( mnDropPos == nCurPos )
2381 nX--;
2382 else
2383 nX++;
2385 if (!rItem.IsDefaultTabBgColor() && !rItem.mbSelect)
2387 GetOutDev()->SetLineColor(rItem.maTabTextColor);
2388 GetOutDev()->SetFillColor(rItem.maTabTextColor);
2391 tools::Polygon aPoly(3);
2392 aPoly.SetPoint(Point(nX, nY), 0);
2393 aPoly.SetPoint(Point(nX + nTriangleWidth, nY - nTriangleWidth), 1);
2394 aPoly.SetPoint(Point(nX + nTriangleWidth, nY + nTriangleWidth), 2);
2395 GetOutDev()->DrawPolygon(aPoly);
2397 if (mnDropPos > 0 && mnDropPos < nItemCount + 1)
2399 GetOutDev()->SetLineColor(aTextColor);
2400 GetOutDev()->SetFillColor(aTextColor);
2402 auto& rItem = mpImpl->maItemList[mnDropPos - 1];
2403 nX = rItem.maRect.Right();
2404 if (mnDropPos == nCurPos)
2405 nX++;
2406 if (!rItem.IsDefaultTabBgColor() && !rItem.mbSelect)
2408 GetOutDev()->SetLineColor(rItem.maTabTextColor);
2409 GetOutDev()->SetFillColor(rItem.maTabTextColor);
2411 tools::Polygon aPoly(3);
2412 aPoly.SetPoint(Point(nX, nY), 0);
2413 aPoly.SetPoint(Point(nX - nTriangleWidth, nY - nTriangleWidth), 1);
2414 aPoly.SetPoint(Point(nX - nTriangleWidth, nY + nTriangleWidth), 2);
2415 GetOutDev()->DrawPolygon(aPoly);
2418 return mnDropPos;
2421 void TabBar::HideDropPos()
2423 if (!mbDropPos)
2424 return;
2426 tools::Long nX;
2427 tools::Long nY1 = (maWinSize.Height() / 2) - 3;
2428 tools::Long nY2 = nY1 + 5;
2429 sal_uInt16 nItemCount = mpImpl->getItemSize();
2431 if (mnDropPos < nItemCount)
2433 auto& rItem = mpImpl->maItemList[mnDropPos];
2434 nX = rItem.maRect.Left();
2435 // immediately call Paint, as it is not possible during drag and drop
2436 tools::Rectangle aRect( nX-1, nY1, nX+3, nY2 );
2437 vcl::Region aRegion( aRect );
2438 GetOutDev()->SetClipRegion( aRegion );
2439 Invalidate(aRect);
2440 GetOutDev()->SetClipRegion();
2442 if (mnDropPos > 0 && mnDropPos < nItemCount + 1)
2444 auto& rItem = mpImpl->maItemList[mnDropPos - 1];
2445 nX = rItem.maRect.Right();
2446 // immediately call Paint, as it is not possible during drag and drop
2447 tools::Rectangle aRect(nX - 2, nY1, nX + 1, nY2);
2448 vcl::Region aRegion(aRect);
2449 GetOutDev()->SetClipRegion(aRegion);
2450 Invalidate(aRect);
2451 GetOutDev()->SetClipRegion();
2454 mbDropPos = false;
2455 mnDropPos = 0;
2458 void TabBar::SwitchPage(const Point& rPos)
2460 sal_uInt16 nSwitchId = GetPageId(rPos);
2461 if (!nSwitchId)
2462 EndSwitchPage();
2463 else
2465 if (nSwitchId != mnSwitchId)
2467 mnSwitchId = nSwitchId;
2468 mnSwitchTime = tools::Time::GetSystemTicks();
2470 else
2472 // change only after 500 ms
2473 if (mnSwitchId != GetCurPageId())
2475 if (tools::Time::GetSystemTicks() > mnSwitchTime + 500)
2477 if (ImplDeactivatePage())
2479 SetCurPageId( mnSwitchId );
2480 PaintImmediately();
2481 ImplActivatePage();
2482 ImplSelect();
2490 void TabBar::EndSwitchPage()
2492 mnSwitchTime = 0;
2493 mnSwitchId = 0;
2496 void TabBar::SetStyle(WinBits nStyle)
2498 if (mnWinStyle == nStyle)
2499 return;
2500 mnWinStyle = nStyle;
2501 ImplInitControls();
2502 // order possible controls
2503 if (IsReallyVisible() && IsUpdateMode())
2504 Resize();
2507 Size TabBar::CalcWindowSizePixel() const
2509 tools::Long nWidth = 0;
2511 if (!mpImpl->maItemList.empty())
2513 const_cast<TabBar*>(this)->ImplCalcWidth();
2514 for (const auto& rItem : mpImpl->maItemList)
2516 nWidth += rItem.mnWidth;
2520 return Size(nWidth, GetSettings().GetStyleSettings().GetScrollBarSize());
2523 tools::Rectangle TabBar::GetPageArea() const
2525 return tools::Rectangle(Point(mnOffX, mnOffY),
2526 Size(mnLastOffX - mnOffX + 1, GetSizePixel().Height() - mnOffY));
2529 void TabBar::SetAddButtonEnabled(bool bAddButtonEnabled)
2531 mpImpl->mxButtonBox->m_xAddButton->set_sensitive(bAddButtonEnabled);
2534 css::uno::Reference<css::accessibility::XAccessible> TabBar::CreateAccessible()
2536 return new accessibility::AccessibleTabBar(this);
2539 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */