Bug Summary

File:home/maarten/src/libreoffice/core/include/rtl/ref.hxx
Warning:line 192, column 9
Use of memory after it is freed

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name tabctrl.cxx -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/11.0.0 -isystem /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/glm -isystem /usr/include/glib-2.0 -isystem /usr/lib64/glib-2.0/include -isystem /usr/include/libmount -isystem /usr/include/blkid -isystem /usr/include/cairo -isystem /usr/include/glib-2.0 -isystem /usr/lib64/glib-2.0/include -isystem /usr/include/pixman-1 -isystem /usr/include/freetype2 -isystem /usr/include/libpng16 -isystem /usr/include/libxml2 -isystem /usr/include/freetype2 -isystem /usr/include/libpng16 -isystem /usr/include/dbus-1.0 -isystem /usr/lib64/dbus-1.0/include -isystem /usr/include/freetype2 -isystem /usr/include/libpng16 -D BOOST_ERROR_CODE_HEADER_ONLY -D BOOST_SYSTEM_NO_DEPRECATED -D CPPU_ENV=gcc3 -D LINUX -D OSL_DEBUG_LEVEL=1 -D SAL_LOG_INFO -D SAL_LOG_WARN -D UNIX -D UNX -D X86_64 -D _PTHREADS -D _REENTRANT -D VCL_DLLIMPLEMENTATION -D DLLIMPLEMENTATION_UITEST -D CUI_DLL_NAME="libcuilo.so" -D DESKTOP_DETECTOR_DLL_NAME="libdesktop_detectorlo.so" -D TK_DLL_NAME="libtklo.so" -D SYSTEM_ZLIB -D GLM_FORCE_CTOR_INIT -D SK_USER_CONFIG_HEADER=</home/maarten/src/libreoffice/core/config_host/config_skia.h> -D SKIA_DLL -D ENABLE_CUPS -D HAVE_VALGRIND_HEADERS -D EXCEPTIONS_ON -D LIBO_INTERNAL_ONLY -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/epoxy/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/skia/include/core -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/skia/include/effects -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/skia/include/gpu -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/skia/include/config -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/skia/include/ports -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/skia/include/third_party/vulkan -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/skia/tools/gpu -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/skia -I /home/maarten/src/libreoffice/core/external/skia/inc/ -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/mdds/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/lcms2/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/icu/source -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/icu/source/i18n -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/icu/source/common -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/harfbuzz/src -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/graphite/include -I /home/maarten/src/libreoffice/core/external/boost/include -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/boost -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/pdfium -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/pdfium/public -D COMPONENT_BUILD -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/libpng -I /home/maarten/src/libreoffice/core/workdir/UnpackedTarball/libjpeg-turbo -I /home/maarten/src/libreoffice/core/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include -I /usr/lib/jvm/java-11-openjdk-11.0.9.10-0.0.ea.fc33.x86_64/include/linux -I /home/maarten/src/libreoffice/core/config_host -I /home/maarten/src/libreoffice/core/vcl/inc -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/udkapi/normal -I /home/maarten/src/libreoffice/core/workdir/UnoApiHeadersTarget/offapi/normal -I /home/maarten/src/libreoffice/core/workdir/CustomTarget/officecfg/registry -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/libxml2 -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10 -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/x86_64-redhat-linux -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/10/../../../../include/c++/10/backward -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/11.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -O0 -Wno-missing-braces -std=c++17 -fdeprecated-macro -fdebug-compilation-dir /home/maarten/src/libreoffice/core -ferror-limit 19 -fvisibility hidden -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcxx-exceptions -fexceptions -debug-info-kind=constructor -analyzer-output=html -faddrsig -o /home/maarten/tmp/wis/scan-build-libreoffice/output/report/2020-10-07-141433-9725-1 -x c++ /home/maarten/src/libreoffice/core/vcl/source/control/tabctrl.cxx

/home/maarten/src/libreoffice/core/vcl/source/control/tabctrl.cxx

1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include <sal/config.h>
21#include <sal/log.hxx>
22
23#include <vcl/notebookbar.hxx>
24#include <vcl/svapp.hxx>
25#include <vcl/help.hxx>
26#include <vcl/event.hxx>
27#include <vcl/menu.hxx>
28#include <vcl/toolkit/button.hxx>
29#include <vcl/tabpage.hxx>
30#include <vcl/tabctrl.hxx>
31#include <vcl/toolkit/controllayout.hxx>
32#include <vcl/layout.hxx>
33#include <vcl/toolkit/lstbox.hxx>
34#include <vcl/settings.hxx>
35#include <vcl/uitest/uiobject.hxx>
36#include <bitmaps.hlst>
37#include <tools/json_writer.hxx>
38
39#include <controldata.hxx>
40#include <svdata.hxx>
41#include <window.h>
42
43#include <deque>
44#include <unordered_map>
45#include <vector>
46
47class ImplTabItem final
48{
49 sal_uInt16 m_nId;
50
51public:
52 VclPtr<TabPage> mpTabPage;
53 OUString maText;
54 OUString maFormatText;
55 OUString maHelpText;
56 OUString maAccessibleName;
57 OUString maAccessibleDescription;
58 OString maTabName;
59 tools::Rectangle maRect;
60 sal_uInt16 mnLine;
61 bool mbFullVisible;
62 bool m_bEnabled; ///< the tab / page is selectable
63 bool m_bVisible; ///< the tab / page can be visible
64 Image maTabImage;
65
66 ImplTabItem(sal_uInt16 nId);
67
68 sal_uInt16 id() const { return m_nId; }
69};
70
71ImplTabItem::ImplTabItem(sal_uInt16 nId)
72 : m_nId(nId)
73 , mnLine(0)
74 , mbFullVisible(false)
75 , m_bEnabled(true)
76 , m_bVisible(true)
77{
78}
79
80struct ImplTabCtrlData
81{
82 std::unordered_map< int, int > maLayoutPageIdToLine;
83 std::unordered_map< int, int > maLayoutLineToPageId;
84 Point maItemsOffset; // offset of the tabitems
85 std::vector< ImplTabItem > maItemList;
86 VclPtr<ListBox> mpListBox;
87};
88
89// for the Tab positions
90#define TAB_PAGERECT0xFFFF 0xFFFF
91
92void TabControl::ImplInit( vcl::Window* pParent, WinBits nStyle )
93{
94 mbLayoutDirty = true;
95
96 if ( !(nStyle & WB_NOTABSTOP) )
97 nStyle |= WB_TABSTOP;
98 if ( !(nStyle & WB_NOGROUP) )
99 nStyle |= WB_GROUP;
100 if ( !(nStyle & WB_NODIALOGCONTROL) )
101 nStyle |= WB_DIALOGCONTROL;
102
103 Control::ImplInit( pParent, nStyle, nullptr );
104
105 mnLastWidth = 0;
106 mnLastHeight = 0;
107 mnActPageId = 0;
108 mnCurPageId = 0;
109 mbFormat = true;
110 mbRestoreHelpId = false;
111 mbSmallInvalidate = false;
112 mpTabCtrlData.reset(new ImplTabCtrlData);
113 mpTabCtrlData->mpListBox = nullptr;
114
115 ImplInitSettings( true );
116
117 if( nStyle & WB_DROPDOWN )
118 {
119 mpTabCtrlData->mpListBox = VclPtr<ListBox>::Create( this, WB_DROPDOWN );
120 mpTabCtrlData->mpListBox->SetPosSizePixel( Point( 0, 0 ), Size( 200, 20 ) );
121 mpTabCtrlData->mpListBox->SetSelectHdl( LINK( this, TabControl, ImplListBoxSelectHdl )::tools::detail::makeLink( ::tools::detail::castTo<TabControl
*>(this), &TabControl::LinkStubImplListBoxSelectHdl)
);
122 mpTabCtrlData->mpListBox->Show();
123 }
124
125 // if the tabcontrol is drawn (ie filled) by a native widget, make sure all controls will have transparent background
126 // otherwise they will paint with a wrong background
127 if( IsNativeControlSupported(ControlType::TabPane, ControlPart::Entire) )
128 EnableChildTransparentMode();
129
130 if (pParent && pParent->IsDialog())
131 pParent->AddChildEventListener( LINK( this, TabControl, ImplWindowEventListener )::tools::detail::makeLink( ::tools::detail::castTo<TabControl
*>(this), &TabControl::LinkStubImplWindowEventListener
)
);
132}
133
134const vcl::Font& TabControl::GetCanonicalFont( const StyleSettings& _rStyle ) const
135{
136 return _rStyle.GetTabFont();
137}
138
139const Color& TabControl::GetCanonicalTextColor( const StyleSettings& _rStyle ) const
140{
141 return _rStyle.GetTabTextColor();
142}
143
144void TabControl::ImplInitSettings( bool bBackground )
145{
146 Control::ImplInitSettings();
147
148 if ( !bBackground )
149 return;
150
151 vcl::Window* pParent = GetParent();
152 if ( !IsControlBackground() &&
153 (pParent->IsChildTransparentModeEnabled()
154 || IsNativeControlSupported(ControlType::TabPane, ControlPart::Entire)
155 || IsNativeControlSupported(ControlType::TabItem, ControlPart::Entire) ) )
156
157 {
158 // set transparent mode for NWF tabcontrols to have
159 // the background always cleared properly
160 EnableChildTransparentMode();
161 SetParentClipMode( ParentClipMode::NoClip );
162 SetPaintTransparent( true );
163 SetBackground();
164 ImplGetWindowImpl()->mbUseNativeFocus = ImplGetSVData()->maNWFData.mbNoFocusRects;
165 }
166 else
167 {
168 EnableChildTransparentMode( false );
169 SetParentClipMode();
170 SetPaintTransparent( false );
171
172 if ( IsControlBackground() )
173 SetBackground( GetControlBackground() );
174 else
175 SetBackground( pParent->GetBackground() );
176 }
177}
178
179void TabControl::ImplFreeLayoutData()
180{
181 if( HasLayoutData() )
182 {
183 ImplClearLayoutData();
184 mpTabCtrlData->maLayoutPageIdToLine.clear();
185 mpTabCtrlData->maLayoutLineToPageId.clear();
186 }
187}
188
189TabControl::TabControl( vcl::Window* pParent, WinBits nStyle ) :
190 Control( WindowType::TABCONTROL )
191{
192 ImplInit( pParent, nStyle );
193 SAL_INFO( "vcl", "*** TABCONTROL no notabs? " << (( GetStyle() & WB_NOBORDER ) ? "true" : "false") )do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_INFO
, "vcl")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break; case SAL_DETAIL_LOG_ACTION_LOG
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "*** TABCONTROL no notabs? " << (( GetStyle
() & WB_NOBORDER ) ? "true" : "false")) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl"), ("/home/maarten/src/libreoffice/core/vcl/source/control/tabctrl.cxx"
":" "193" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "*** TABCONTROL no notabs? " << (
( GetStyle() & WB_NOBORDER ) ? "true" : "false")), 0); } else
{ ::std::ostringstream sal_detail_stream; sal_detail_stream <<
"*** TABCONTROL no notabs? " << (( GetStyle() & WB_NOBORDER
) ? "true" : "false"); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl"), ("/home/maarten/src/libreoffice/core/vcl/source/control/tabctrl.cxx"
":" "193" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "*** TABCONTROL no notabs? " << (( GetStyle
() & WB_NOBORDER ) ? "true" : "false")) == 1) { ::sal_detail_log
( (::SAL_DETAIL_LOG_LEVEL_INFO), ("vcl"), ("/home/maarten/src/libreoffice/core/vcl/source/control/tabctrl.cxx"
":" "193" ": "), ::sal::detail::unwrapStream( ::sal::detail::
StreamStart() << "*** TABCONTROL no notabs? " << (
( GetStyle() & WB_NOBORDER ) ? "true" : "false")), 0); } else
{ ::std::ostringstream sal_detail_stream; sal_detail_stream <<
"*** TABCONTROL no notabs? " << (( GetStyle() & WB_NOBORDER
) ? "true" : "false"); ::sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_INFO
), ("vcl"), ("/home/maarten/src/libreoffice/core/vcl/source/control/tabctrl.cxx"
":" "193" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
194}
195
196TabControl::~TabControl()
197{
198 disposeOnce();
199}
200
201void TabControl::dispose()
202{
203 Window *pParent = GetParent();
204 if (pParent && pParent->IsDialog())
205 GetParent()->RemoveChildEventListener( LINK( this, TabControl, ImplWindowEventListener )::tools::detail::makeLink( ::tools::detail::castTo<TabControl
*>(this), &TabControl::LinkStubImplWindowEventListener
)
);
206
207 ImplFreeLayoutData();
208
209 // delete TabCtrl data
210 if (mpTabCtrlData)
211 mpTabCtrlData->mpListBox.disposeAndClear();
212 mpTabCtrlData.reset();
213 Control::dispose();
214}
215
216ImplTabItem* TabControl::ImplGetItem( sal_uInt16 nId ) const
217{
218 for (auto & item : mpTabCtrlData->maItemList)
219 {
220 if (item.id() == nId)
221 return &item;
222 }
223
224 return nullptr;
225}
226
227Size TabControl::ImplGetItemSize( ImplTabItem* pItem, long nMaxWidth )
228{
229 pItem->maFormatText = pItem->maText;
230 Size aSize( GetCtrlTextWidth( pItem->maFormatText ), GetTextHeight() );
231 Size aImageSize( 0, 0 );
232 if( !!pItem->maTabImage )
233 {
234 aImageSize = pItem->maTabImage.GetSizePixel();
235 if( !pItem->maFormatText.isEmpty() )
236 aImageSize.AdjustWidth(GetTextHeight()/4 );
237 }
238 aSize.AdjustWidth(aImageSize.Width() );
239 if( aImageSize.Height() > aSize.Height() )
240 aSize.setHeight( aImageSize.Height() );
241
242 aSize.AdjustWidth(TAB_TABOFFSET_X3*2 );
243 aSize.AdjustHeight(TAB_TABOFFSET_Y3*2 );
244
245 tools::Rectangle aCtrlRegion( Point( 0, 0 ), aSize );
246 tools::Rectangle aBoundingRgn, aContentRgn;
247 const TabitemValue aControlValue(tools::Rectangle(TAB_TABOFFSET_X3, TAB_TABOFFSET_Y3,
248 aSize.Width() - TAB_TABOFFSET_X3 * 2,
249 aSize.Height() - TAB_TABOFFSET_Y3 * 2));
250 if(GetNativeControlRegion( ControlType::TabItem, ControlPart::Entire, aCtrlRegion,
251 ControlState::ENABLED, aControlValue,
252 aBoundingRgn, aContentRgn ) )
253 {
254 return aContentRgn.GetSize();
255 }
256
257 // For languages with short names (e.g. Chinese), because the space is
258 // normally only one pixel per char
259 if ( pItem->maFormatText.getLength() < TAB_EXTRASPACE_X6 )
260 aSize.AdjustWidth(TAB_EXTRASPACE_X6-pItem->maFormatText.getLength() );
261
262 // shorten Text if needed
263 if ( aSize.Width()+4 >= nMaxWidth )
264 {
265 OUString aAppendStr("...");
266 pItem->maFormatText += aAppendStr;
267 do
268 {
269 if (pItem->maFormatText.getLength() > aAppendStr.getLength())
270 pItem->maFormatText = pItem->maFormatText.replaceAt( pItem->maFormatText.getLength()-aAppendStr.getLength()-1, 1, "" );
271 aSize.setWidth( GetCtrlTextWidth( pItem->maFormatText ) );
272 aSize.AdjustWidth(aImageSize.Width() );
273 aSize.AdjustWidth(TAB_TABOFFSET_X3*2 );
274 }
275 while ( (aSize.Width()+4 >= nMaxWidth) && (pItem->maFormatText.getLength() > aAppendStr.getLength()) );
276 if ( aSize.Width()+4 >= nMaxWidth )
277 {
278 pItem->maFormatText = ".";
279 aSize.setWidth( 1 );
280 }
281 }
282
283 if( pItem->maFormatText.isEmpty() )
284 {
285 if( aSize.Height() < aImageSize.Height()+4 ) //leave space for focus rect
286 aSize.setHeight( aImageSize.Height()+4 );
287 }
288
289 return aSize;
290}
291
292// Feel free to move this to some more general place for reuse
293// http://en.wikipedia.org/wiki/Word_wrap#Minimum_raggedness
294// Mostly based on Alexey Frunze's nifty example at
295// http://stackoverflow.com/questions/9071205/balanced-word-wrap-minimum-raggedness-in-php
296namespace MinimumRaggednessWrap
297{
298 static std::deque<size_t> GetEndOfLineIndexes(const std::vector<sal_Int32>& rWidthsOf, sal_Int32 nLineWidth)
299 {
300 ++nLineWidth;
301
302 size_t nWidthsCount = rWidthsOf.size();
303 std::vector<sal_Int32> aCosts(nWidthsCount * nWidthsCount);
304
305 // cost function c(i, j) that computes the cost of a line consisting of
306 // the words Word[i] to Word[j]
307 for (size_t i = 0; i < nWidthsCount; ++i)
308 {
309 for (size_t j = 0; j < nWidthsCount; ++j)
310 {
311 if (j >= i)
312 {
313 sal_Int32 c = nLineWidth - (j - i);
314 for (size_t k = i; k <= j; ++k)
315 c -= rWidthsOf[k];
316 c = (c >= 0) ? c * c : SAL_MAX_INT32((sal_Int32) 0x7FFFFFFF);
317 aCosts[j * nWidthsCount + i] = c;
318 }
319 else
320 {
321 aCosts[j * nWidthsCount + i] = SAL_MAX_INT32((sal_Int32) 0x7FFFFFFF);
322 }
323 }
324 }
325
326 std::vector<sal_Int32> aFunction(nWidthsCount);
327 std::vector<sal_Int32> aWrapPoints(nWidthsCount);
328
329 // f(j) in aFunction[], collect wrap points in aWrapPoints[]
330 for (size_t j = 0; j < nWidthsCount; ++j)
331 {
332 aFunction[j] = aCosts[j * nWidthsCount];
333 if (aFunction[j] == SAL_MAX_INT32((sal_Int32) 0x7FFFFFFF))
334 {
335 for (size_t k = 0; k < j; ++k)
336 {
337 sal_Int32 s;
338 if (aFunction[k] == SAL_MAX_INT32((sal_Int32) 0x7FFFFFFF) || aCosts[j * nWidthsCount + k + 1] == SAL_MAX_INT32((sal_Int32) 0x7FFFFFFF))
339 s = SAL_MAX_INT32((sal_Int32) 0x7FFFFFFF);
340 else
341 s = aFunction[k] + aCosts[j * nWidthsCount + k + 1];
342 if (aFunction[j] > s)
343 {
344 aFunction[j] = s;
345 aWrapPoints[j] = k + 1;
346 }
347 }
348 }
349 }
350
351 std::deque<size_t> aSolution;
352
353 // no solution
354 if (aFunction[nWidthsCount - 1] == SAL_MAX_INT32((sal_Int32) 0x7FFFFFFF))
355 return aSolution;
356
357 // optimal solution
358 size_t j = nWidthsCount - 1;
359 while (true)
360 {
361 aSolution.push_front(j);
362 if (!aWrapPoints[j])
363 break;
364 j = aWrapPoints[j] - 1;
365 }
366
367 return aSolution;
368 }
369};
370
371static void lcl_AdjustSingleLineTabs(long nMaxWidth, ImplTabCtrlData *pTabCtrlData)
372{
373 if (!ImplGetSVData()->maNWFData.mbCenteredTabs)
374 return;
375
376 int nRightSpace = nMaxWidth; // space left on the right by the tabs
377 for (auto const& item : pTabCtrlData->maItemList)
378 {
379 if (!item.m_bVisible)
380 continue;
381 nRightSpace -= item.maRect.Right() - item.maRect.Left();
382 }
383 nRightSpace /= 2;
384
385 for (auto& item : pTabCtrlData->maItemList)
386 {
387 if (!item.m_bVisible)
388 continue;
389 item.maRect.AdjustLeft(nRightSpace);
390 item.maRect.AdjustRight(nRightSpace);
391 }
392}
393
394bool TabControl::ImplPlaceTabs( long nWidth )
395{
396 if ( nWidth <= 0 )
397 return false;
398 if ( mpTabCtrlData->maItemList.empty() )
399 return false;
400
401 long nMaxWidth = nWidth;
402
403 const long nOffsetX = 2 + GetItemsOffset().X();
404 const long nOffsetY = 2 + GetItemsOffset().Y();
405
406 //fdo#66435 throw Knuth/Tex minimum raggedness algorithm at the problem
407 //of ugly bare tabs on lines of their own
408
409 //collect widths
410 std::vector<sal_Int32> aWidths;
411 for (auto & item : mpTabCtrlData->maItemList)
412 {
413 if (!item.m_bVisible)
414 continue;
415 aWidths.push_back(ImplGetItemSize(&item, nMaxWidth).Width());
416 }
417
418 //aBreakIndexes will contain the indexes of the last tab on each row
419 std::deque<size_t> aBreakIndexes(MinimumRaggednessWrap::GetEndOfLineIndexes(aWidths, nMaxWidth - nOffsetX - 2));
420
421 nMaxWidth -= GetItemsOffset().X();
422
423 long nX = nOffsetX;
424 long nY = nOffsetY;
425
426 sal_uInt16 nLines = 0;
427 sal_uInt16 nCurLine = 0;
428
429 long nLineWidthAry[100];
430 sal_uInt16 nLinePosAry[101];
431 nLineWidthAry[0] = 0;
432 nLinePosAry[0] = 0;
433
434 size_t nIndex = 0;
435
436 for (auto & item : mpTabCtrlData->maItemList)
437 {
438 if (!item.m_bVisible)
439 continue;
440
441 Size aSize = ImplGetItemSize( &item, nMaxWidth );
442
443 bool bNewLine = false;
444 if (!aBreakIndexes.empty() && nIndex > aBreakIndexes.front())
445 {
446 aBreakIndexes.pop_front();
447 bNewLine = true;
448 }
449
450 if ( bNewLine && (nWidth > 2+nOffsetX) )
451 {
452 if ( nLines == 99 )
453 break;
454
455 nX = nOffsetX;
456 nY += aSize.Height();
457 nLines++;
458 nLineWidthAry[nLines] = 0;
459 nLinePosAry[nLines] = nIndex;
460 }
461
462 tools::Rectangle aNewRect( Point( nX, nY ), aSize );
463 if ( mbSmallInvalidate && (item.maRect != aNewRect) )
464 mbSmallInvalidate = false;
465 item.maRect = aNewRect;
466 item.mnLine = nLines;
467 item.mbFullVisible = true;
468
469 nLineWidthAry[nLines] += aSize.Width();
470 nX += aSize.Width();
471
472 if (item.id() == mnCurPageId)
473 nCurLine = nLines;
474
475 ++nIndex;
476 }
477
478 if (nLines) // two or more lines
479 {
480 long nLineHeightAry[100];
481 long nIH = 0;
482 for (const auto& item : mpTabCtrlData->maItemList)
483 {
484 if (!item.m_bVisible)
485 continue;
486 nIH = item.maRect.Bottom() - 1;
487 break;
488 }
489
490 for ( sal_uInt16 i = 0; i < nLines+1; i++ )
491 {
492 if ( i <= nCurLine )
493 nLineHeightAry[i] = nIH*(nLines-(nCurLine-i)) + GetItemsOffset().Y();
494 else
495 nLineHeightAry[i] = nIH*(i-nCurLine-1) + GetItemsOffset().Y();
496 }
497
498 nLinePosAry[nLines+1] = static_cast<sal_uInt16>(mpTabCtrlData->maItemList.size());
499
500 long nDX = 0;
501 long nModDX = 0;
502 long nIDX = 0;
503
504 sal_uInt16 i = 0;
505 sal_uInt16 n = 0;
506
507 for (auto & item : mpTabCtrlData->maItemList)
508 {
509 if (!item.m_bVisible)
510 continue;
511
512 if ( i == nLinePosAry[n] )
513 {
514 if ( n == nLines+1 )
515 break;
516
517 nIDX = 0;
518 if( nLinePosAry[n+1]-i > 0 )
519 {
520 nDX = ( nWidth - nOffsetX - nLineWidthAry[n] ) / ( nLinePosAry[n+1] - i );
521 nModDX = ( nWidth - nOffsetX - nLineWidthAry[n] ) % ( nLinePosAry[n+1] - i );
522 }
523 else
524 {
525 // FIXME: this is a case of tabctrl way too small
526 nDX = 0;
527 nModDX = 0;
528 }
529 n++;
530 }
531
532 item.maRect.AdjustLeft(nIDX );
533 item.maRect.AdjustRight(nIDX + nDX );
534 item.maRect.SetTop( nLineHeightAry[n-1] );
535 item.maRect.SetBottom(nLineHeightAry[n-1] + nIH - 1);
536 nIDX += nDX;
537
538 if ( nModDX )
539 {
540 nIDX++;
541 item.maRect.AdjustRight( 1 );
542 nModDX--;
543 }
544
545 i++;
546 }
547 }
548 else // only one line
549 lcl_AdjustSingleLineTabs(nMaxWidth, mpTabCtrlData.get());
550
551 return true;
552}
553
554tools::Rectangle TabControl::ImplGetTabRect( sal_uInt16 nItemPos, long nWidth, long nHeight )
555{
556 Size aWinSize = Control::GetOutputSizePixel();
557 if ( nWidth < 0 )
558 nWidth = aWinSize.Width();
559 if ( nHeight < 0 )
560 nHeight = aWinSize.Height();
561
562 if ( mpTabCtrlData->maItemList.empty() )
563 {
564 long nW = nWidth-TAB_OFFSET3*2;
565 long nH = nHeight-TAB_OFFSET3*2;
566 return (nW > 0 && nH > 0)
567 ? tools::Rectangle(Point(TAB_OFFSET3, TAB_OFFSET3), Size(nW, nH))
568 : tools::Rectangle();
569 }
570
571 if ( nItemPos == TAB_PAGERECT0xFFFF )
572 {
573 sal_uInt16 nLastPos;
574 if ( mnCurPageId )
575 nLastPos = GetPagePos( mnCurPageId );
576 else
577 nLastPos = 0;
578
579 tools::Rectangle aRect = ImplGetTabRect( nLastPos, nWidth, nHeight );
580 if (aRect.IsEmpty())
581 return aRect;
582
583 long nW = nWidth-TAB_OFFSET3*2;
584 long nH = nHeight-aRect.Bottom()-TAB_OFFSET3*2;
585 return (nW > 0 && nH > 0)
586 ? tools::Rectangle( Point( TAB_OFFSET3, aRect.Bottom()+TAB_OFFSET3 ), Size( nW, nH ) )
587 : tools::Rectangle();
588 }
589
590 ImplTabItem* const pItem = (nItemPos < mpTabCtrlData->maItemList.size())
591 ? &mpTabCtrlData->maItemList[nItemPos] : nullptr;
592 return ImplGetTabRect(pItem, nWidth, nHeight);
593}
594
595tools::Rectangle TabControl::ImplGetTabRect(const ImplTabItem* pItem, long nWidth, long nHeight)
596{
597 if ((nWidth <= 1) || (nHeight <= 0) || !pItem || !pItem->m_bVisible)
598 return tools::Rectangle();
599
600 nWidth -= 1;
601
602 if ( mbFormat || (mnLastWidth != nWidth) || (mnLastHeight != nHeight) )
603 {
604 vcl::Font aFont( GetFont() );
605 aFont.SetTransparent( true );
606 SetFont( aFont );
607
608 bool bRet = ImplPlaceTabs( nWidth );
609 if ( !bRet )
610 return tools::Rectangle();
611
612 mnLastWidth = nWidth;
613 mnLastHeight = nHeight;
614 mbFormat = false;
615 }
616
617 return pItem->maRect;
618}
619
620void TabControl::ImplChangeTabPage( sal_uInt16 nId, sal_uInt16 nOldId )
621{
622 ImplFreeLayoutData();
623
624 ImplTabItem* pOldItem = ImplGetItem( nOldId );
625 ImplTabItem* pItem = ImplGetItem( nId );
626 TabPage* pOldPage = pOldItem ? pOldItem->mpTabPage.get() : nullptr;
627 TabPage* pPage = pItem ? pItem->mpTabPage.get() : nullptr;
628 vcl::Window* pCtrlParent = GetParent();
629
630 if ( IsReallyVisible() && IsUpdateMode() )
631 {
632 sal_uInt16 nPos = GetPagePos( nId );
633 tools::Rectangle aRect = ImplGetTabRect( nPos );
634
635 if ( !pOldItem || !pItem || (pOldItem->mnLine != pItem->mnLine) )
636 {
637 aRect.SetLeft( 0 );
638 aRect.SetTop( 0 );
639 aRect.SetRight( Control::GetOutputSizePixel().Width() );
640 }
641 else
642 {
643 aRect.AdjustLeft( -3 );
644 aRect.AdjustTop( -2 );
645 aRect.AdjustRight(3 );
646 Invalidate( aRect );
647 nPos = GetPagePos( nOldId );
648 aRect = ImplGetTabRect( nPos );
649 aRect.AdjustLeft( -3 );
650 aRect.AdjustTop( -2 );
651 aRect.AdjustRight(3 );
652 }
653 Invalidate( aRect );
654 }
655
656 if ( pOldPage == pPage )
657 return;
658
659 tools::Rectangle aRect = ImplGetTabRect( TAB_PAGERECT0xFFFF );
660
661 if ( pOldPage )
662 {
663 if ( mbRestoreHelpId )
664 pCtrlParent->SetHelpId( OString() );
665 }
666
667 if ( pPage )
668 {
669 if ( GetStyle() & WB_NOBORDER )
670 {
671 tools::Rectangle aRectNoTab(Point(0, 0), GetSizePixel());
672 pPage->SetPosSizePixel( aRectNoTab.TopLeft(), aRectNoTab.GetSize() );
673 }
674 else
675 pPage->SetPosSizePixel( aRect.TopLeft(), aRect.GetSize() );
676
677 // activate page here so the controls can be switched
678 // also set the help id of the parent window to that of the tab page
679 if ( GetHelpId().isEmpty() )
680 {
681 mbRestoreHelpId = true;
682 pCtrlParent->SetHelpId( pPage->GetHelpId() );
683 }
684
685 pPage->Show();
686
687 if ( pOldPage && pOldPage->HasChildPathFocus() )
688 {
689 vcl::Window* pFirstChild = pPage->ImplGetDlgWindow( 0, GetDlgWindowType::First );
690 if ( pFirstChild )
691 pFirstChild->ImplControlFocus( GetFocusFlags::Init );
692 else
693 GrabFocus();
694 }
695 }
696
697 if ( pOldPage )
698 pOldPage->Hide();
699
700 // Invalidate the same region that will be send to NWF
701 // to always allow for bitmap caching
702 // see Window::DrawNativeControl()
703 if( IsNativeControlSupported( ControlType::TabPane, ControlPart::Entire ) )
704 {
705 aRect.AdjustLeft( -(TAB_OFFSET3) );
706 aRect.AdjustTop( -(TAB_OFFSET3) );
707 aRect.AdjustRight(TAB_OFFSET3 );
708 aRect.AdjustBottom(TAB_OFFSET3 );
709 }
710
711 Invalidate( aRect );
712}
713
714bool TabControl::ImplPosCurTabPage()
715{
716 // resize/position current TabPage
717 ImplTabItem* pItem = ImplGetItem( GetCurPageId() );
718 if ( pItem && pItem->mpTabPage )
719 {
720 if ( GetStyle() & WB_NOBORDER )
721 {
722 tools::Rectangle aRectNoTab(Point(0, 0), GetSizePixel());
723 pItem->mpTabPage->SetPosSizePixel( aRectNoTab.TopLeft(), aRectNoTab.GetSize() );
724 return true;
725 }
726 tools::Rectangle aRect = ImplGetTabRect( TAB_PAGERECT0xFFFF );
727 pItem->mpTabPage->SetPosSizePixel( aRect.TopLeft(), aRect.GetSize() );
728 return true;
729 }
730
731 return false;
732}
733
734void TabControl::ImplActivateTabPage( bool bNext )
735{
736 sal_uInt16 nCurPos = GetPagePos( GetCurPageId() );
737
738 if ( bNext )
739 nCurPos = (nCurPos + 1) % GetPageCount();
740 else
741 {
742 if ( !nCurPos )
743 nCurPos = GetPageCount()-1;
744 else
745 nCurPos--;
746 }
747
748 SelectTabPage( GetPageId( nCurPos ) );
749}
750
751void TabControl::ImplShowFocus()
752{
753 if ( !GetPageCount() || mpTabCtrlData->mpListBox )
754 return;
755
756 sal_uInt16 nCurPos = GetPagePos( mnCurPageId );
757 tools::Rectangle aRect = ImplGetTabRect( nCurPos );
758 const ImplTabItem& rItem = mpTabCtrlData->maItemList[ nCurPos ];
759 Size aTabSize = aRect.GetSize();
760 Size aImageSize( 0, 0 );
761 long nTextHeight = GetTextHeight();
762 long nTextWidth = GetCtrlTextWidth( rItem.maFormatText );
763 sal_uInt16 nOff;
764
765 if ( !(GetSettings().GetStyleSettings().GetOptions() & StyleSettingsOptions::Mono) )
766 nOff = 1;
767 else
768 nOff = 0;
769
770 if( !! rItem.maTabImage )
771 {
772 aImageSize = rItem.maTabImage.GetSizePixel();
773 if( !rItem.maFormatText.isEmpty() )
774 aImageSize.AdjustWidth(GetTextHeight()/4 );
775 }
776
777 if( !rItem.maFormatText.isEmpty() )
778 {
779 // show focus around text
780 aRect.SetLeft( aRect.Left()+aImageSize.Width()+((aTabSize.Width()-nTextWidth-aImageSize.Width())/2)-nOff-1-1 );
781 aRect.SetTop( aRect.Top()+((aTabSize.Height()-nTextHeight)/2)-1-1 );
782 aRect.SetRight( aRect.Left()+nTextWidth+2 );
783 aRect.SetBottom( aRect.Top()+nTextHeight+2 );
784 }
785 else
786 {
787 // show focus around image
788 long nXPos = aRect.Left()+((aTabSize.Width()-nTextWidth-aImageSize.Width())/2)-nOff-1;
789 long nYPos = aRect.Top();
790 if( aImageSize.Height() < aRect.GetHeight() )
791 nYPos += (aRect.GetHeight() - aImageSize.Height())/2;
792
793 aRect.SetLeft( nXPos - 2 );
794 aRect.SetTop( nYPos - 2 );
795 aRect.SetRight( aRect.Left() + aImageSize.Width() + 4 );
796 aRect.SetBottom( aRect.Top() + aImageSize.Height() + 4 );
797 }
798 ShowFocus( aRect );
799}
800
801void TabControl::ImplDrawItem(vcl::RenderContext& rRenderContext, ImplTabItem const * pItem, const tools::Rectangle& rCurRect,
802 bool bFirstInGroup, bool bLastInGroup )
803{
804 if (!pItem->m_bVisible || pItem->maRect.IsEmpty())
805 return;
806
807 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
808 tools::Rectangle aRect = pItem->maRect;
809 long nLeftBottom = aRect.Bottom();
810 long nRightBottom = aRect.Bottom();
811 bool bLeftBorder = true;
812 bool bRightBorder = true;
813 sal_uInt16 nOff;
814 bool bNativeOK = false;
815
816 sal_uInt16 nOff2 = 0;
817 sal_uInt16 nOff3 = 0;
818
819 if (!(rStyleSettings.GetOptions() & StyleSettingsOptions::Mono))
820 nOff = 1;
821 else
822 nOff = 0;
823
824 // if this is the active Page, we have to draw a little more
825 if (pItem->id() == mnCurPageId)
826 {
827 nOff2 = 2;
828 if (!ImplGetSVData()->maNWFData.mbNoActiveTabTextRaise)
829 nOff3 = 1;
830 }
831 else
832 {
833 Point aLeftTestPos = aRect.BottomLeft();
834 Point aRightTestPos = aRect.BottomRight();
835 if (aLeftTestPos.Y() == rCurRect.Bottom())
836 {
837 aLeftTestPos.AdjustX( -2 );
838 if (rCurRect.IsInside(aLeftTestPos))
839 bLeftBorder = false;
840 aRightTestPos.AdjustX(2 );
841 if (rCurRect.IsInside(aRightTestPos))
842 bRightBorder = false;
843 }
844 else
845 {
846 if (rCurRect.IsInside(aLeftTestPos))
847 nLeftBottom -= 2;
848 if (rCurRect.IsInside(aRightTestPos))
849 nRightBottom -= 2;
850 }
851 }
852
853 ControlState nState = ControlState::NONE;
854
855 if (pItem->id() == mnCurPageId)
856 {
857 nState |= ControlState::SELECTED;
858 // only the selected item can be focused
859 if (HasFocus())
860 nState |= ControlState::FOCUSED;
861 }
862 if (IsEnabled())
863 nState |= ControlState::ENABLED;
864 if (IsMouseOver() && pItem->maRect.IsInside(GetPointerPosPixel()))
865 {
866 nState |= ControlState::ROLLOVER;
867 for (auto const& item : mpTabCtrlData->maItemList)
868 if ((&item != pItem) && item.m_bVisible && item.maRect.IsInside(GetPointerPosPixel()))
869 {
870 nState &= ~ControlState::ROLLOVER; // avoid multiple highlighted tabs
871 break;
872 }
873 assert(nState & ControlState::ROLLOVER)(static_cast <bool> (nState & ControlState::ROLLOVER
) ? void (0) : __assert_fail ("nState & ControlState::ROLLOVER"
, "/home/maarten/src/libreoffice/core/vcl/source/control/tabctrl.cxx"
, 873, __extension__ __PRETTY_FUNCTION__))
;
874 }
875
876 bNativeOK = rRenderContext.IsNativeControlSupported(ControlType::TabItem, ControlPart::Entire);
877 if ( bNativeOK )
878 {
879 TabitemValue tiValue(tools::Rectangle(pItem->maRect.Left() + TAB_TABOFFSET_X3,
880 pItem->maRect.Top() + TAB_TABOFFSET_Y3,
881 pItem->maRect.Right() - TAB_TABOFFSET_X3,
882 pItem->maRect.Bottom() - TAB_TABOFFSET_Y3));
883 if (pItem->maRect.Left() < 5)
884 tiValue.mnAlignment |= TabitemFlags::LeftAligned;
885 if (pItem->maRect.Right() > mnLastWidth - 5)
886 tiValue.mnAlignment |= TabitemFlags::RightAligned;
887 if (bFirstInGroup)
888 tiValue.mnAlignment |= TabitemFlags::FirstInGroup;
889 if (bLastInGroup)
890 tiValue.mnAlignment |= TabitemFlags::LastInGroup;
891
892 tools::Rectangle aCtrlRegion( pItem->maRect );
893 aCtrlRegion.AdjustBottom(TabPaneValue::m_nOverlap);
894 bNativeOK = rRenderContext.DrawNativeControl(ControlType::TabItem, ControlPart::Entire,
895 aCtrlRegion, nState, tiValue, OUString() );
896 }
897
898 if (!bNativeOK)
899 {
900 if (!(rStyleSettings.GetOptions() & StyleSettingsOptions::Mono))
901 {
902 rRenderContext.SetLineColor(rStyleSettings.GetLightColor());
903 rRenderContext.DrawPixel(Point(aRect.Left() + 1 - nOff2, aRect.Top() + 1 - nOff2)); // diagonally indented top-left pixel
904 if (bLeftBorder)
905 {
906 rRenderContext.DrawLine(Point(aRect.Left() - nOff2, aRect.Top() + 2 - nOff2),
907 Point(aRect.Left() - nOff2, nLeftBottom - 1));
908 }
909 rRenderContext.DrawLine(Point(aRect.Left() + 2 - nOff2, aRect.Top() - nOff2), // top line starting 2px from left border
910 Point(aRect.Right() + nOff2 - 3, aRect.Top() - nOff2)); // ending 3px from right border
911
912 if (bRightBorder)
913 {
914 rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
915 rRenderContext.DrawLine(Point(aRect.Right() + nOff2 - 2, aRect.Top() + 1 - nOff2),
916 Point(aRect.Right() + nOff2 - 2, nRightBottom - 1));
917
918 rRenderContext.SetLineColor(rStyleSettings.GetDarkShadowColor());
919 rRenderContext.DrawLine(Point(aRect.Right() + nOff2 - 1, aRect.Top() + 3 - nOff2),
920 Point(aRect.Right() + nOff2 - 1, nRightBottom - 1));
921 }
922 }
923 else
924 {
925 rRenderContext.SetLineColor(COL_BLACK);
926 rRenderContext.DrawPixel(Point(aRect.Left() + 1 - nOff2, aRect.Top() + 1 - nOff2));
927 rRenderContext.DrawPixel(Point(aRect.Right() + nOff2 - 2, aRect.Top() + 1 - nOff2));
928 if (bLeftBorder)
929 {
930 rRenderContext.DrawLine(Point(aRect.Left() - nOff2, aRect.Top() + 2 - nOff2),
931 Point(aRect.Left() - nOff2, nLeftBottom - 1));
932 }
933 rRenderContext.DrawLine(Point(aRect.Left() + 2 - nOff2, aRect.Top() - nOff2),
934 Point(aRect.Right() - 3, aRect.Top() - nOff2));
935 if (bRightBorder)
936 {
937 rRenderContext.DrawLine(Point(aRect.Right() + nOff2 - 1, aRect.Top() + 2 - nOff2),
938 Point(aRect.Right() + nOff2 - 1, nRightBottom - 1));
939 }
940 }
941 }
942
943 // set font accordingly, current item is painted bold
944 // we set the font attributes always before drawing to be re-entrant (DrawNativeControl may trigger additional paints)
945 vcl::Font aFont(rRenderContext.GetFont());
946 aFont.SetTransparent(true);
947 rRenderContext.SetFont(aFont);
948
949 Size aTabSize = aRect.GetSize();
950 Size aImageSize(0, 0);
951 long nTextHeight = rRenderContext.GetTextHeight();
952 long nTextWidth = rRenderContext.GetCtrlTextWidth(pItem->maFormatText);
953 if (!!pItem->maTabImage)
954 {
955 aImageSize = pItem->maTabImage.GetSizePixel();
956 if (!pItem->maFormatText.isEmpty())
957 aImageSize.AdjustWidth(GetTextHeight() / 4 );
958 }
959 long nXPos = aRect.Left() + ((aTabSize.Width() - nTextWidth - aImageSize.Width()) / 2) - nOff - nOff3;
960 long nYPos = aRect.Top() + ((aTabSize.Height() - nTextHeight) / 2) - nOff3;
961 if (!pItem->maFormatText.isEmpty())
962 {
963 DrawTextFlags nStyle = DrawTextFlags::Mnemonic;
964 if (!pItem->m_bEnabled)
965 nStyle |= DrawTextFlags::Disable;
966
967 Color aColor(rStyleSettings.GetTabTextColor());
968 if (nState & ControlState::SELECTED)
969 aColor = rStyleSettings.GetTabHighlightTextColor();
970 else if (nState & ControlState::ROLLOVER)
971 aColor = rStyleSettings.GetTabRolloverTextColor();
972
973 Color aOldColor(rRenderContext.GetTextColor());
974 rRenderContext.SetTextColor(aColor);
975
976 const tools::Rectangle aOutRect(nXPos + aImageSize.Width(), nYPos,
977 nXPos + aImageSize.Width() + nTextWidth, nYPos + nTextHeight);
978 DrawControlText(rRenderContext, aOutRect, pItem->maFormatText, nStyle,
979 nullptr, nullptr);
980
981 rRenderContext.SetTextColor(aOldColor);
982 }
983
984 if (!!pItem->maTabImage)
985 {
986 Point aImgTL( nXPos, aRect.Top() );
987 if (aImageSize.Height() < aRect.GetHeight())
988 aImgTL.AdjustY((aRect.GetHeight() - aImageSize.Height()) / 2 );
989 rRenderContext.DrawImage(aImgTL, pItem->maTabImage, pItem->m_bEnabled ? DrawImageFlags::NONE : DrawImageFlags::Disable );
990 }
991}
992
993bool TabControl::ImplHandleKeyEvent( const KeyEvent& rKeyEvent )
994{
995 bool bRet = false;
996
997 if ( GetPageCount() > 1 )
998 {
999 vcl::KeyCode aKeyCode = rKeyEvent.GetKeyCode();
1000 sal_uInt16 nKeyCode = aKeyCode.GetCode();
1001
1002 if ( aKeyCode.IsMod1() )
1003 {
1004 if ( aKeyCode.IsShift() || (nKeyCode == KEY_PAGEUP) )
1005 {
1006 if ( (nKeyCode == KEY_TAB) || (nKeyCode == KEY_PAGEUP) )
1007 {
1008 ImplActivateTabPage( false );
1009 bRet = true;
1010 }
1011 }
1012 else
1013 {
1014 if ( (nKeyCode == KEY_TAB) || (nKeyCode == KEY_PAGEDOWN) )
1015 {
1016 ImplActivateTabPage( true );
1017 bRet = true;
1018 }
1019 }
1020 }
1021 }
1022
1023 return bRet;
1024}
1025
1026IMPL_LINK_NOARG(TabControl, ImplListBoxSelectHdl, ListBox&, void)void TabControl::LinkStubImplListBoxSelectHdl(void * instance
, ListBox& data) { return static_cast<TabControl *>
(instance)->ImplListBoxSelectHdl(data); } void TabControl::
ImplListBoxSelectHdl(__attribute__ ((unused)) ListBox&)
1027{
1028 SelectTabPage( GetPageId( mpTabCtrlData->mpListBox->GetSelectedEntryPos() ) );
1029}
1030
1031IMPL_LINK( TabControl, ImplWindowEventListener, VclWindowEvent&, rEvent, void )void TabControl::LinkStubImplWindowEventListener(void * instance
, VclWindowEvent& data) { return static_cast<TabControl
*>(instance)->ImplWindowEventListener(data); } void TabControl
::ImplWindowEventListener(VclWindowEvent& rEvent)
1032{
1033 if ( rEvent.GetId() == VclEventId::WindowKeyInput )
1034 {
1035 // Do not handle events from TabControl or its children, which is done in Notify(), where the events can be consumed.
1036 if ( !IsWindowOrChild( rEvent.GetWindow() ) )
1037 {
1038 KeyEvent* pKeyEvent = static_cast< KeyEvent* >(rEvent.GetData());
1039 ImplHandleKeyEvent( *pKeyEvent );
1040 }
1041 }
1042}
1043
1044void TabControl::MouseButtonDown( const MouseEvent& rMEvt )
1045{
1046 if (mpTabCtrlData->mpListBox || !rMEvt.IsLeft())
1047 return;
1048
1049 ImplTabItem *pItem = ImplGetItem(rMEvt.GetPosPixel());
1050 if (pItem && pItem->m_bEnabled)
1051 SelectTabPage(pItem->id());
1052}
1053
1054void TabControl::KeyInput( const KeyEvent& rKEvt )
1055{
1056 if( mpTabCtrlData->mpListBox )
1057 mpTabCtrlData->mpListBox->KeyInput( rKEvt );
1058 else if ( GetPageCount() > 1 )
1059 {
1060 vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
1061 sal_uInt16 nKeyCode = aKeyCode.GetCode();
1062
1063 if ( (nKeyCode == KEY_LEFT) || (nKeyCode == KEY_RIGHT) )
1064 {
1065 bool bNext = (nKeyCode == KEY_RIGHT);
1066 ImplActivateTabPage( bNext );
1067 }
1068 }
1069
1070 Control::KeyInput( rKEvt );
1071}
1072
1073static bool lcl_canPaint(const vcl::RenderContext& rRenderContext, const tools::Rectangle& rDrawRect,
1074 const tools::Rectangle& rItemRect)
1075{
1076 vcl::Region aClipRgn(rRenderContext.GetActiveClipRegion());
1077 aClipRgn.Intersect(rItemRect);
1078 if (!rDrawRect.IsEmpty())
1079 aClipRgn.Intersect(rDrawRect);
1080 return !aClipRgn.IsEmpty();
1081}
1082
1083void TabControl::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
1084{
1085 if (GetStyle() & WB_NOBORDER)
1086 return;
1087
1088 Control::Paint(rRenderContext, rRect);
1089
1090 HideFocus();
1091
1092 // reformat if needed
1093 tools::Rectangle aRect = ImplGetTabRect(TAB_PAGERECT0xFFFF);
1094
1095 // find current item
1096 ImplTabItem* pCurItem = nullptr;
1097 for (auto & item : mpTabCtrlData->maItemList)
1098 {
1099 if (item.id() == mnCurPageId)
1100 {
1101 pCurItem = &item;
1102 break;
1103 }
1104 }
1105
1106 // Draw the TabPage border
1107 const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
1108 tools::Rectangle aCurRect;
1109 aRect.AdjustLeft( -(TAB_OFFSET3) );
1110 aRect.AdjustTop( -(TAB_OFFSET3) );
1111 aRect.AdjustRight(TAB_OFFSET3 );
1112 aRect.AdjustBottom(TAB_OFFSET3 );
1113
1114 // if we have an invisible tabpage or no tabpage at all the tabpage rect should be
1115 // increased to avoid round corners that might be drawn by a theme
1116 // in this case we're only interested in the top border of the tabpage because the tabitems are used
1117 // standalone (eg impress)
1118 bool bNoTabPage = false;
1119 TabPage* pCurPage = pCurItem ? pCurItem->mpTabPage.get() : nullptr;
1120 if (!pCurPage || !pCurPage->IsVisible())
1121 {
1122 bNoTabPage = true;
1123 aRect.AdjustLeft( -10 );
1124 aRect.AdjustRight(10 );
1125 }
1126
1127 if (rRenderContext.IsNativeControlSupported(ControlType::TabPane, ControlPart::Entire))
1128 {
1129 const bool bPaneWithHeader = rRenderContext.IsNativeControlSupported(ControlType::TabPane, ControlPart::TabPaneWithHeader);
1130 tools::Rectangle aHeaderRect(aRect.Left(), 0, aRect.Right(), aRect.Top());
1131 if (bPaneWithHeader)
1132 {
1133 aRect.SetTop(0);
1134 if (mpTabCtrlData->maItemList.size())
1135 {
1136 long nRight = 0;
1137 for (const auto &item : mpTabCtrlData->maItemList)
1138 if (item.m_bVisible)
1139 nRight = item.maRect.Right();
1140 assert(nRight)(static_cast <bool> (nRight) ? void (0) : __assert_fail
("nRight", "/home/maarten/src/libreoffice/core/vcl/source/control/tabctrl.cxx"
, 1140, __extension__ __PRETTY_FUNCTION__))
;
1141 aHeaderRect.SetRight(nRight);
1142 }
1143 }
1144 const TabPaneValue aTabPaneValue(aHeaderRect, pCurItem ? pCurItem->maRect : tools::Rectangle());
1145
1146 ControlState nState = ControlState::ENABLED;
1147 if (!IsEnabled())
1148 nState &= ~ControlState::ENABLED;
1149 if (HasFocus())
1150 nState |= ControlState::FOCUSED;
1151
1152 if (lcl_canPaint(rRenderContext, rRect, aRect))
1153 rRenderContext.DrawNativeControl(ControlType::TabPane, ControlPart::Entire,
1154 aRect, nState, aTabPaneValue, OUString());
1155
1156 if (!bPaneWithHeader && rRenderContext.IsNativeControlSupported(ControlType::TabHeader, ControlPart::Entire)
1157 && lcl_canPaint(rRenderContext, rRect, aHeaderRect))
1158 rRenderContext.DrawNativeControl(ControlType::TabHeader, ControlPart::Entire,
1159 aHeaderRect, nState, aTabPaneValue, OUString());
1160 }
1161 else
1162 {
1163 long nTopOff = 1;
1164 if (!(rStyleSettings.GetOptions() & StyleSettingsOptions::Mono))
1165 rRenderContext.SetLineColor(rStyleSettings.GetLightColor());
1166 else
1167 rRenderContext.SetLineColor(COL_BLACK);
1168 if (pCurItem && !pCurItem->maRect.IsEmpty())
1169 {
1170 aCurRect = pCurItem->maRect;
1171 rRenderContext.DrawLine(aRect.TopLeft(), Point(aCurRect.Left() - 2, aRect.Top()));
1172 if (aCurRect.Right() + 1 < aRect.Right())
1173 {
1174 rRenderContext.DrawLine(Point(aCurRect.Right(), aRect.Top()), aRect.TopRight());
1175 }
1176 else
1177 {
1178 nTopOff = 0;
1179 }
1180 }
1181 else
1182 rRenderContext.DrawLine(aRect.TopLeft(), aRect.TopRight());
1183
1184 rRenderContext.DrawLine(aRect.TopLeft(), aRect.BottomLeft());
1185
1186 if (!(rStyleSettings.GetOptions() & StyleSettingsOptions::Mono))
1187 {
1188 // if we have not tab page the bottom line of the tab page
1189 // directly touches the tab items, so choose a color that fits seamlessly
1190 if (bNoTabPage)
1191 rRenderContext.SetLineColor(rStyleSettings.GetDialogColor());
1192 else
1193 rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
1194 rRenderContext.DrawLine(Point(1, aRect.Bottom() - 1), Point(aRect.Right() - 1, aRect.Bottom() - 1));
1195 rRenderContext.DrawLine(Point(aRect.Right() - 1, aRect.Top() + nTopOff), Point(aRect.Right() - 1, aRect.Bottom() - 1));
1196 if (bNoTabPage)
1197 rRenderContext.SetLineColor(rStyleSettings.GetDialogColor());
1198 else
1199 rRenderContext.SetLineColor(rStyleSettings.GetDarkShadowColor());
1200 rRenderContext.DrawLine(Point(0, aRect.Bottom()), Point(aRect.Right(), aRect.Bottom()));
1201 rRenderContext.DrawLine(Point(aRect.Right(), aRect.Top() + nTopOff), Point(aRect.Right(), aRect.Bottom()));
1202 }
1203 else
1204 {
1205 rRenderContext.DrawLine(aRect.TopRight(), aRect.BottomRight());
1206 rRenderContext.DrawLine(aRect.BottomLeft(), aRect.BottomRight());
1207 }
1208 }
1209
1210 if (!mpTabCtrlData->maItemList.empty() && mpTabCtrlData->mpListBox == nullptr)
1211 {
1212 // Some native toolkits (GTK+) draw tabs right-to-left, with an
1213 // overlap between adjacent tabs
1214 bool bDrawTabsRTL = rRenderContext.IsNativeControlSupported(ControlType::TabItem, ControlPart::TabsDrawRtl);
1215 ImplTabItem* pFirstTab = nullptr;
1216 ImplTabItem* pLastTab = nullptr;
1217 size_t idx;
1218
1219 // Even though there is a tab overlap with GTK+, the first tab is not
1220 // overlapped on the left side. Other toolkits ignore this option.
1221 if (bDrawTabsRTL)
1222 {
1223 pFirstTab = mpTabCtrlData->maItemList.data();
1224 pLastTab = pFirstTab + mpTabCtrlData->maItemList.size() - 1;
1225 idx = mpTabCtrlData->maItemList.size() - 1;
1226 }
1227 else
1228 {
1229 pLastTab = mpTabCtrlData->maItemList.data();
1230 pFirstTab = pLastTab + mpTabCtrlData->maItemList.size() - 1;
1231 idx = 0;
1232 }
1233
1234 while (idx < mpTabCtrlData->maItemList.size())
1235 {
1236 ImplTabItem* pItem = &mpTabCtrlData->maItemList[idx];
1237
1238 if (pItem != pCurItem && pItem->m_bVisible && lcl_canPaint(rRenderContext, rRect, pItem->maRect))
1239 ImplDrawItem(rRenderContext, pItem, aCurRect, pItem == pFirstTab, pItem == pLastTab);
1240
1241 if (bDrawTabsRTL)
1242 idx--;
1243 else
1244 idx++;
1245 }
1246
1247 if (pCurItem && lcl_canPaint(rRenderContext, rRect, pCurItem->maRect))
1248 ImplDrawItem(rRenderContext, pCurItem, aCurRect, pCurItem == pFirstTab, pCurItem == pLastTab);
1249 }
1250
1251 if (HasFocus())
1252 ImplShowFocus();
1253
1254 mbSmallInvalidate = true;
1255}
1256
1257void TabControl::setAllocation(const Size &rAllocation)
1258{
1259 ImplFreeLayoutData();
1260
1261 if ( !IsReallyShown() )
1262 return;
1263
1264 if( mpTabCtrlData->mpListBox )
1265 {
1266 // get the listbox' preferred size
1267 Size aTabCtrlSize( GetSizePixel() );
1268 long nPrefWidth = mpTabCtrlData->mpListBox->get_preferred_size().Width();
1269 if( nPrefWidth > aTabCtrlSize.Width() )
1270 nPrefWidth = aTabCtrlSize.Width();
1271 Size aNewSize( nPrefWidth, LogicToPixel( Size( 12, 12 ), MapMode( MapUnit::MapAppFont ) ).Height() );
1272 Point aNewPos( (aTabCtrlSize.Width() - nPrefWidth) / 2, 0 );
1273 mpTabCtrlData->mpListBox->SetPosSizePixel( aNewPos, aNewSize );
1274 }
1275
1276 mbFormat = true;
1277
1278 // resize/position active TabPage
1279 bool bTabPage = ImplPosCurTabPage();
1280
1281 // check what needs to be invalidated
1282 Size aNewSize = rAllocation;
1283 long nNewWidth = aNewSize.Width();
1284 for (auto const& item : mpTabCtrlData->maItemList)
1285 {
1286 if (!item.m_bVisible)
1287 continue;
1288 if (!item.mbFullVisible || (item.maRect.Right()-2 >= nNewWidth))
1289 {
1290 mbSmallInvalidate = false;
1291 break;
1292 }
1293 }
1294
1295 if ( mbSmallInvalidate )
1296 {
1297 tools::Rectangle aRect = ImplGetTabRect( TAB_PAGERECT0xFFFF );
1298 aRect.AdjustLeft( -(TAB_OFFSET3+TAB_BORDER_LEFT1) );
1299 aRect.AdjustTop( -(TAB_OFFSET3+TAB_BORDER_TOP1) );
1300 aRect.AdjustRight(TAB_OFFSET3+TAB_BORDER_RIGHT2 );
1301 aRect.AdjustBottom(TAB_OFFSET3+TAB_BORDER_BOTTOM2 );
1302 if ( bTabPage )
1303 Invalidate( aRect, InvalidateFlags::NoChildren );
1304 else
1305 Invalidate( aRect );
1306
1307 }
1308 else
1309 {
1310 if ( bTabPage )
1311 Invalidate( InvalidateFlags::NoChildren );
1312 else
1313 Invalidate();
1314 }
1315
1316 mbLayoutDirty = false;
1317}
1318
1319void TabControl::SetPosSizePixel(const Point& rNewPos, const Size& rNewSize)
1320{
1321 Window::SetPosSizePixel(rNewPos, rNewSize);
1322 //if size changed, TabControl::Resize got called already
1323 if (mbLayoutDirty)
1324 setAllocation(rNewSize);
1325}
1326
1327void TabControl::SetSizePixel(const Size& rNewSize)
1328{
1329 Window::SetSizePixel(rNewSize);
1330 //if size changed, TabControl::Resize got called already
1331 if (mbLayoutDirty)
1332 setAllocation(rNewSize);
1333}
1334
1335void TabControl::SetPosPixel(const Point& rPos)
1336{
1337 Window::SetPosPixel(rPos);
1338 if (mbLayoutDirty)
1339 setAllocation(GetOutputSizePixel());
1340}
1341
1342void TabControl::Resize()
1343{
1344 setAllocation(Control::GetOutputSizePixel());
1345}
1346
1347void TabControl::GetFocus()
1348{
1349 if( ! mpTabCtrlData->mpListBox )
1350 {
1351 ImplShowFocus();
1352 SetInputContext( InputContext( GetFont() ) );
1353 }
1354 else
1355 {
1356 if( mpTabCtrlData->mpListBox->IsReallyVisible() )
1357 mpTabCtrlData->mpListBox->GrabFocus();
1358 }
1359 Control::GetFocus();
1360}
1361
1362void TabControl::LoseFocus()
1363{
1364 if( mpTabCtrlData && ! mpTabCtrlData->mpListBox )
1365 HideFocus();
1366 Control::LoseFocus();
1367}
1368
1369void TabControl::RequestHelp( const HelpEvent& rHEvt )
1370{
1371 sal_uInt16 nItemId = rHEvt.KeyboardActivated() ? mnCurPageId : GetPageId( ScreenToOutputPixel( rHEvt.GetMousePosPixel() ) );
1372
1373 if ( nItemId )
1374 {
1375 if ( rHEvt.GetMode() & HelpEventMode::BALLOON )
1376 {
1377 OUString aStr = GetHelpText( nItemId );
1378 if ( !aStr.isEmpty() )
1379 {
1380 tools::Rectangle aItemRect = ImplGetTabRect( GetPagePos( nItemId ) );
1381 Point aPt = OutputToScreenPixel( aItemRect.TopLeft() );
1382 aItemRect.SetLeft( aPt.X() );
1383 aItemRect.SetTop( aPt.Y() );
1384 aPt = OutputToScreenPixel( aItemRect.BottomRight() );
1385 aItemRect.SetRight( aPt.X() );
1386 aItemRect.SetBottom( aPt.Y() );
1387 Help::ShowBalloon( this, aItemRect.Center(), aItemRect, aStr );
1388 return;
1389 }
1390 }
1391
1392 // for Quick or Ballon Help, we show the text, if it is cut
1393 if ( rHEvt.GetMode() & (HelpEventMode::QUICK | HelpEventMode::BALLOON) )
1394 {
1395 ImplTabItem* pItem = ImplGetItem( nItemId );
1396 const OUString& rStr = pItem->maText;
1397 if ( rStr != pItem->maFormatText )
1398 {
1399 tools::Rectangle aItemRect = ImplGetTabRect( GetPagePos( nItemId ) );
1400 Point aPt = OutputToScreenPixel( aItemRect.TopLeft() );
1401 aItemRect.SetLeft( aPt.X() );
1402 aItemRect.SetTop( aPt.Y() );
1403 aPt = OutputToScreenPixel( aItemRect.BottomRight() );
1404 aItemRect.SetRight( aPt.X() );
1405 aItemRect.SetBottom( aPt.Y() );
1406 if ( !rStr.isEmpty() )
1407 {
1408 if ( rHEvt.GetMode() & HelpEventMode::BALLOON )
1409 Help::ShowBalloon( this, aItemRect.Center(), aItemRect, rStr );
1410 else
1411 Help::ShowQuickHelp( this, aItemRect, rStr );
1412 return;
1413 }
1414 }
1415 }
1416
1417 if ( rHEvt.GetMode() & HelpEventMode::QUICK )
1418 {
1419 ImplTabItem* pItem = ImplGetItem( nItemId );
1420 const OUString& rHelpText = pItem->maHelpText;
1421 // show tooltip if not text but image is set and helptext is available
1422 if ( !rHelpText.isEmpty() && pItem->maText.isEmpty() && !!pItem->maTabImage )
1423 {
1424 tools::Rectangle aItemRect = ImplGetTabRect( GetPagePos( nItemId ) );
1425 Point aPt = OutputToScreenPixel( aItemRect.TopLeft() );
1426 aItemRect.SetLeft( aPt.X() );
1427 aItemRect.SetTop( aPt.Y() );
1428 aPt = OutputToScreenPixel( aItemRect.BottomRight() );
1429 aItemRect.SetRight( aPt.X() );
1430 aItemRect.SetBottom( aPt.Y() );
1431 Help::ShowQuickHelp( this, aItemRect, rHelpText );
1432 return;
1433 }
1434 }
1435 }
1436
1437 Control::RequestHelp( rHEvt );
1438}
1439
1440void TabControl::Command( const CommandEvent& rCEvt )
1441{
1442 if( (mpTabCtrlData->mpListBox == nullptr) && (rCEvt.GetCommand() == CommandEventId::ContextMenu) && (GetPageCount() > 1) )
1443 {
1444 Point aMenuPos;
1445 bool bMenu;
1446 if ( rCEvt.IsMouseEvent() )
1447 {
1448 aMenuPos = rCEvt.GetMousePosPixel();
1449 bMenu = GetPageId( aMenuPos ) != 0;
1450 }
1451 else
1452 {
1453 aMenuPos = ImplGetTabRect( GetPagePos( mnCurPageId ) ).Center();
1454 bMenu = true;
1455 }
1456
1457 if ( bMenu )
1458 {
1459 ScopedVclPtrInstance<PopupMenu> aMenu;
1460 for (auto const& item : mpTabCtrlData->maItemList)
1461 {
1462 aMenu->InsertItem(item.id(), item.maText, MenuItemBits::CHECKABLE | MenuItemBits::RADIOCHECK);
1463 if (item.id() == mnCurPageId)
1464 aMenu->CheckItem(item.id());
1465 aMenu->SetHelpId(item.id(), OString());
1466 }
1467
1468 sal_uInt16 nId = aMenu->Execute( this, aMenuPos );
1469 if ( nId && (nId != mnCurPageId) )
1470 SelectTabPage( nId );
1471 return;
1472 }
1473 }
1474
1475 Control::Command( rCEvt );
1476}
1477
1478void TabControl::StateChanged( StateChangedType nType )
1479{
1480 Control::StateChanged( nType );
1481
1482 if ( nType == StateChangedType::InitShow )
1483 {
1484 ImplPosCurTabPage();
1485 if( mpTabCtrlData->mpListBox )
1486 Resize();
1487 }
1488 else if ( nType == StateChangedType::UpdateMode )
1489 {
1490 if ( IsUpdateMode() )
1491 Invalidate();
1492 }
1493 else if ( (nType == StateChangedType::Zoom) ||
1494 (nType == StateChangedType::ControlFont) )
1495 {
1496 ImplInitSettings( false );
1497 Invalidate();
1498 }
1499 else if ( nType == StateChangedType::ControlForeground )
1500 {
1501 ImplInitSettings( false );
1502 Invalidate();
1503 }
1504 else if ( nType == StateChangedType::ControlBackground )
1505 {
1506 ImplInitSettings( true );
1507 Invalidate();
1508 }
1509}
1510
1511void TabControl::DataChanged( const DataChangedEvent& rDCEvt )
1512{
1513 Control::DataChanged( rDCEvt );
1514
1515 if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
1516 (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
1517 ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
1518 (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
1519 {
1520 ImplInitSettings( true );
1521 Invalidate();
1522 }
1523}
1524
1525ImplTabItem* TabControl::ImplGetItem(const Point& rPt) const
1526{
1527 ImplTabItem* pFoundItem = nullptr;
1528 int nFound = 0;
1529 for (auto & item : mpTabCtrlData->maItemList)
1530 {
1531 if (item.m_bVisible && item.maRect.IsInside(rPt))
1532 {
1533 nFound++;
1534 pFoundItem = &item;
1535 }
1536 }
1537
1538 // assure that only one tab is highlighted at a time
1539 assert(nFound <= 1)(static_cast <bool> (nFound <= 1) ? void (0) : __assert_fail
("nFound <= 1", "/home/maarten/src/libreoffice/core/vcl/source/control/tabctrl.cxx"
, 1539, __extension__ __PRETTY_FUNCTION__))
;
1540 return nFound == 1 ? pFoundItem : nullptr;
1541}
1542
1543bool TabControl::PreNotify( NotifyEvent& rNEvt )
1544{
1545 if( rNEvt.GetType() == MouseNotifyEvent::MOUSEMOVE )
1546 {
1547 const MouseEvent* pMouseEvt = rNEvt.GetMouseEvent();
1548 if( pMouseEvt && !pMouseEvt->GetButtons() && !pMouseEvt->IsSynthetic() && !pMouseEvt->IsModifierChanged() )
1549 {
1550 // trigger redraw if mouse over state has changed
1551 if( IsNativeControlSupported(ControlType::TabItem, ControlPart::Entire) )
1552 {
1553 ImplTabItem *pItem = ImplGetItem(GetPointerPosPixel());
1554 ImplTabItem *pLastItem = ImplGetItem(GetLastPointerPosPixel());
1555 if ((pItem != pLastItem) || pMouseEvt->IsLeaveWindow() || pMouseEvt->IsEnterWindow())
1556 {
1557 vcl::Region aClipRgn;
1558 if (pLastItem)
1559 {
1560 // allow for slightly bigger tabitems
1561 // as used by gtk
1562 // TODO: query for the correct sizes
1563 tools::Rectangle aRect(pLastItem->maRect);
1564 aRect.AdjustLeft( -2 );
1565 aRect.AdjustRight(2 );
1566 aRect.AdjustTop( -3 );
1567 aClipRgn.Union( aRect );
1568 }
1569
1570 if (pItem)
1571 {
1572 // allow for slightly bigger tabitems
1573 // as used by gtk
1574 // TODO: query for the correct sizes
1575 tools::Rectangle aRect(pItem->maRect);
1576 aRect.AdjustLeft( -2 );
1577 aRect.AdjustRight(2 );
1578 aRect.AdjustTop( -3 );
1579 aClipRgn.Union( aRect );
1580 }
1581
1582 if( !aClipRgn.IsEmpty() )
1583 Invalidate( aClipRgn );
1584 }
1585 }
1586 }
1587 }
1588
1589 return Control::PreNotify(rNEvt);
1590}
1591
1592bool TabControl::EventNotify( NotifyEvent& rNEvt )
1593{
1594 bool bRet = false;
1595
1596 if ( rNEvt.GetType() == MouseNotifyEvent::KEYINPUT )
1597 bRet = ImplHandleKeyEvent( *rNEvt.GetKeyEvent() );
1598
1599 return bRet || Control::EventNotify( rNEvt );
1600}
1601
1602void TabControl::ActivatePage()
1603{
1604 maActivateHdl.Call( this );
1605}
1606
1607bool TabControl::DeactivatePage()
1608{
1609 return !maDeactivateHdl.IsSet() || maDeactivateHdl.Call( this );
1610}
1611
1612void TabControl::SetTabPageSizePixel( const Size& rSize )
1613{
1614 ImplFreeLayoutData();
1615
1616 Size aNewSize( rSize );
1617 aNewSize.AdjustWidth(TAB_OFFSET3*2 );
1618 tools::Rectangle aRect = ImplGetTabRect( TAB_PAGERECT0xFFFF,
1619 aNewSize.Width(), aNewSize.Height() );
1620 aNewSize.AdjustHeight(aRect.Top()+TAB_OFFSET3 );
1621 Window::SetOutputSizePixel( aNewSize );
1622}
1623
1624void TabControl::InsertPage( sal_uInt16 nPageId, const OUString& rText,
1625 sal_uInt16 nPos )
1626{
1627 SAL_WARN_IF( !nPageId, "vcl", "TabControl::InsertPage(): PageId == 0" )do { if (true && (!nPageId)) { switch (sal_detail_log_report
(::SAL_DETAIL_LOG_LEVEL_WARN, "vcl")) { case SAL_DETAIL_LOG_ACTION_IGNORE
: break; case SAL_DETAIL_LOG_ACTION_LOG: if (sizeof ::sal::detail
::getResult( ::sal::detail::StreamStart() << "TabControl::InsertPage(): PageId == 0"
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("vcl"
), ("/home/maarten/src/libreoffice/core/vcl/source/control/tabctrl.cxx"
":" "1627" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "TabControl::InsertPage(): PageId == 0"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "TabControl::InsertPage(): PageId == 0"; ::sal::detail
::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("vcl"), ("/home/maarten/src/libreoffice/core/vcl/source/control/tabctrl.cxx"
":" "1627" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "TabControl::InsertPage(): PageId == 0") == 1) { ::
sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("vcl"), ("/home/maarten/src/libreoffice/core/vcl/source/control/tabctrl.cxx"
":" "1627" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "TabControl::InsertPage(): PageId == 0"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "TabControl::InsertPage(): PageId == 0"; ::sal::detail
::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("vcl"), ("/home/maarten/src/libreoffice/core/vcl/source/control/tabctrl.cxx"
":" "1627" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
1628 SAL_WARN_IF( GetPagePos( nPageId ) != TAB_PAGE_NOTFOUND, "vcl",do { if (true && (GetPagePos( nPageId ) != (sal_uInt16
(0xFFFF)))) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "vcl")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break; case SAL_DETAIL_LOG_ACTION_LOG
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "TabControl::InsertPage(): PageId already exists"
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("vcl"
), ("/home/maarten/src/libreoffice/core/vcl/source/control/tabctrl.cxx"
":" "1629" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "TabControl::InsertPage(): PageId already exists"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "TabControl::InsertPage(): PageId already exists"; ::
sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("vcl"), ("/home/maarten/src/libreoffice/core/vcl/source/control/tabctrl.cxx"
":" "1629" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "TabControl::InsertPage(): PageId already exists"
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("vcl"
), ("/home/maarten/src/libreoffice/core/vcl/source/control/tabctrl.cxx"
":" "1629" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "TabControl::InsertPage(): PageId already exists"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "TabControl::InsertPage(): PageId already exists"; ::
sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("vcl"), ("/home/maarten/src/libreoffice/core/vcl/source/control/tabctrl.cxx"
":" "1629" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
1629 "TabControl::InsertPage(): PageId already exists" )do { if (true && (GetPagePos( nPageId ) != (sal_uInt16
(0xFFFF)))) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN
, "vcl")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break; case SAL_DETAIL_LOG_ACTION_LOG
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "TabControl::InsertPage(): PageId already exists"
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("vcl"
), ("/home/maarten/src/libreoffice/core/vcl/source/control/tabctrl.cxx"
":" "1629" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "TabControl::InsertPage(): PageId already exists"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "TabControl::InsertPage(): PageId already exists"; ::
sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("vcl"), ("/home/maarten/src/libreoffice/core/vcl/source/control/tabctrl.cxx"
":" "1629" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL
: if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart
() << "TabControl::InsertPage(): PageId already exists"
) == 1) { ::sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("vcl"
), ("/home/maarten/src/libreoffice/core/vcl/source/control/tabctrl.cxx"
":" "1629" ": "), ::sal::detail::unwrapStream( ::sal::detail
::StreamStart() << "TabControl::InsertPage(): PageId already exists"
), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream
<< "TabControl::InsertPage(): PageId already exists"; ::
sal::detail::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("vcl"), ("/home/maarten/src/libreoffice/core/vcl/source/control/tabctrl.cxx"
":" "1629" ": "), sal_detail_stream, 0); }; std::abort(); break
; } } } while (false)
;
1630
1631 // insert new page item
1632 ImplTabItem* pItem = nullptr;
1633 if( nPos == TAB_APPEND(sal_uInt16(0xFFFF)) || size_t(nPos) >= mpTabCtrlData->maItemList.size() )
1634 {
1635 mpTabCtrlData->maItemList.emplace_back(nPageId);
1636 pItem = &mpTabCtrlData->maItemList.back();
1637 if( mpTabCtrlData->mpListBox )
1638 mpTabCtrlData->mpListBox->InsertEntry( rText );
1639 }
1640 else
1641 {
1642 std::vector< ImplTabItem >::iterator new_it =
1643 mpTabCtrlData->maItemList.emplace(mpTabCtrlData->maItemList.begin() + nPos, nPageId);
1644 pItem = &(*new_it);
1645 if( mpTabCtrlData->mpListBox )
1646 mpTabCtrlData->mpListBox->InsertEntry( rText, nPos);
1647 }
1648 if( mpTabCtrlData->mpListBox )
1649 {
1650 if( ! mnCurPageId )
1651 mpTabCtrlData->mpListBox->SelectEntryPos( 0 );
1652 mpTabCtrlData->mpListBox->SetDropDownLineCount( mpTabCtrlData->mpListBox->GetEntryCount() );
1653 }
1654
1655 // set current page id
1656 if ( !mnCurPageId )
1657 mnCurPageId = nPageId;
1658
1659 // init new page item
1660 pItem->maText = rText;
1661 pItem->mbFullVisible = false;
1662
1663 mbFormat = true;
1664 if ( IsUpdateMode() )
1665 Invalidate();
1666
1667 ImplFreeLayoutData();
1668 if( mpTabCtrlData->mpListBox ) // reposition/resize listbox
1669 Resize();
1670
1671 CallEventListeners( VclEventId::TabpageInserted, reinterpret_cast<void*>(nPageId) );
1672}
1673
1674void TabControl::RemovePage( sal_uInt16 nPageId )
1675{
1676 sal_uInt16 nPos = GetPagePos( nPageId );
1677
1678 // does the item exist ?
1679 if ( nPos == TAB_PAGE_NOTFOUND(sal_uInt16(0xFFFF)) )
1680 return;
1681
1682 //remove page item
1683 std::vector< ImplTabItem >::iterator it = mpTabCtrlData->maItemList.begin() + nPos;
1684 bool bIsCurrentPage = (it->id() == mnCurPageId);
1685 mpTabCtrlData->maItemList.erase( it );
1686 if( mpTabCtrlData->mpListBox )
1687 {
1688 mpTabCtrlData->mpListBox->RemoveEntry( nPos );
1689 mpTabCtrlData->mpListBox->SetDropDownLineCount( mpTabCtrlData->mpListBox->GetEntryCount() );
1690 }
1691
1692 // If current page is removed, then first page gets the current page
1693 if ( bIsCurrentPage )
1694 {
1695 mnCurPageId = 0;
1696
1697 if( ! mpTabCtrlData->maItemList.empty() )
1698 {
1699 // don't do this by simply setting mnCurPageId to pFirstItem->id()
1700 // this leaves a lot of stuff (such trivia as _showing_ the new current page) undone
1701 // instead, call SetCurPageId
1702 // without this, the next (outside) call to SetCurPageId with the id of the first page
1703 // will result in doing nothing (as we assume that nothing changed, then), and the page
1704 // will never be shown.
1705 // 86875 - 05/11/2001 - frank.schoenheit@germany.sun.com
1706
1707 SetCurPageId(mpTabCtrlData->maItemList[0].id());
1708 }
1709 }
1710
1711 mbFormat = true;
1712 if ( IsUpdateMode() )
1713 Invalidate();
1714
1715 ImplFreeLayoutData();
1716
1717 CallEventListeners( VclEventId::TabpageRemoved, reinterpret_cast<void*>(nPageId) );
1718}
1719
1720void TabControl::SetPageEnabled( sal_uInt16 i_nPageId, bool i_bEnable )
1721{
1722 ImplTabItem* pItem = ImplGetItem( i_nPageId );
1723
1724 if (!pItem || pItem->m_bEnabled == i_bEnable)
1725 return;
1726
1727 pItem->m_bEnabled = i_bEnable;
1728 if (!pItem->m_bVisible)
1729 return;
1730
1731 mbFormat = true;
1732 if( mpTabCtrlData->mpListBox )
1733 mpTabCtrlData->mpListBox->SetEntryFlags( GetPagePos( i_nPageId ),
1734 i_bEnable ? ListBoxEntryFlags::NONE : (ListBoxEntryFlags::DisableSelection | ListBoxEntryFlags::DrawDisabled) );
1735
1736 // SetCurPageId will change to a valid page
1737 if (pItem->id() == mnCurPageId)
1738 SetCurPageId( mnCurPageId );
1739 else if ( IsUpdateMode() )
1740 Invalidate();
1741}
1742
1743void TabControl::SetPageVisible( sal_uInt16 nPageId, bool bVisible )
1744{
1745 ImplTabItem* pItem = ImplGetItem( nPageId );
1746 if (!pItem || pItem->m_bVisible == bVisible)
1747 return;
1748
1749 pItem->m_bVisible = bVisible;
1750 if (!bVisible)
1751 {
1752 if (pItem->mbFullVisible)
1753 mbSmallInvalidate = false;
1754 pItem->mbFullVisible = false;
1755 pItem->maRect.SetEmpty();
1756 }
1757 mbFormat = true;
1758
1759 // SetCurPageId will change to a valid page
1760 if (pItem->id() == mnCurPageId)
1761 SetCurPageId(mnCurPageId);
1762 else if (IsUpdateMode())
1763 Invalidate();
1764}
1765
1766sal_uInt16 TabControl::GetPageCount() const
1767{
1768 return static_cast<sal_uInt16>(mpTabCtrlData->maItemList.size());
1769}
1770
1771sal_uInt16 TabControl::GetPageId( sal_uInt16 nPos ) const
1772{
1773 if( size_t(nPos) < mpTabCtrlData->maItemList.size() )
1774 return mpTabCtrlData->maItemList[nPos].id();
1775 return 0;
1776}
1777
1778sal_uInt16 TabControl::GetPagePos( sal_uInt16 nPageId ) const
1779{
1780 sal_uInt16 nPos = 0;
1781 for (auto const& item : mpTabCtrlData->maItemList)
1782 {
1783 if (item.id() == nPageId)
1784 return nPos;
1785 ++nPos;
1786 }
1787
1788 return TAB_PAGE_NOTFOUND(sal_uInt16(0xFFFF));
1789}
1790
1791sal_uInt16 TabControl::GetPageId( const Point& rPos ) const
1792{
1793 const auto &rList = mpTabCtrlData->maItemList;
1794 const auto it = std::find_if(rList.begin(), rList.end(), [&rPos, this](const auto &item) {
1795 return const_cast<TabControl*>(this)->ImplGetTabRect(&item).IsInside(rPos); });
1796 return (it != rList.end()) ? it->id() : 0;
1797}
1798
1799sal_uInt16 TabControl::GetPageId( const OString& rName ) const
1800{
1801 const auto &rList = mpTabCtrlData->maItemList;
1802 const auto it = std::find_if(rList.begin(), rList.end(), [&rName](const auto &item) {
1803 return item.maTabName == rName; });
1804 return (it != rList.end()) ? it->id() : 0;
1805}
1806
1807void TabControl::SetCurPageId( sal_uInt16 nPageId )
1808{
1809 sal_uInt16 nPos = GetPagePos( nPageId );
1810 while (nPos != TAB_PAGE_NOTFOUND(sal_uInt16(0xFFFF)) && !mpTabCtrlData->maItemList[nPos].m_bEnabled)
1811 {
1812 nPos++;
1813 if( size_t(nPos) >= mpTabCtrlData->maItemList.size() )
1814 nPos = 0;
1815 if (mpTabCtrlData->maItemList[nPos].id() == nPageId)
1816 break;
1817 }
1818
1819 if( nPos == TAB_PAGE_NOTFOUND(sal_uInt16(0xFFFF)) )
1820 return;
1821
1822 nPageId = mpTabCtrlData->maItemList[nPos].id();
1823 if ( nPageId == mnCurPageId )
1824 {
1825 if ( mnActPageId )
1826 mnActPageId = nPageId;
1827 return;
1828 }
1829
1830 if ( mnActPageId )
1831 mnActPageId = nPageId;
1832 else
1833 {
1834 mbFormat = true;
1835 sal_uInt16 nOldId = mnCurPageId;
1836 mnCurPageId = nPageId;
1837 ImplChangeTabPage( nPageId, nOldId );
1838 }
1839}
1840
1841sal_uInt16 TabControl::GetCurPageId() const
1842{
1843 if ( mnActPageId )
1844 return mnActPageId;
1845 else
1846 return mnCurPageId;
1847}
1848
1849void TabControl::SelectTabPage( sal_uInt16 nPageId )
1850{
1851 if ( !nPageId || (nPageId == mnCurPageId) )
1852 return;
1853
1854 ImplFreeLayoutData();
1855
1856 CallEventListeners( VclEventId::TabpageDeactivate, reinterpret_cast<void*>(mnCurPageId) );
1857 if ( DeactivatePage() )
1858 {
1859 mnActPageId = nPageId;
1860 ActivatePage();
1861 // Page could have been switched by the Activate handler
1862 nPageId = mnActPageId;
1863 mnActPageId = 0;
1864 SetCurPageId( nPageId );
1865 if( mpTabCtrlData->mpListBox )
1866 mpTabCtrlData->mpListBox->SelectEntryPos( GetPagePos( nPageId ) );
1867 CallEventListeners( VclEventId::TabpageActivate, reinterpret_cast<void*>(nPageId) );
1868 }
1869}
1870
1871void TabControl::SetTabPage( sal_uInt16 nPageId, TabPage* pTabPage )
1872{
1873 ImplTabItem* pItem = ImplGetItem( nPageId );
1874
1875 if ( !pItem || (pItem->mpTabPage.get() == pTabPage) )
1876 return;
1877
1878 if ( pTabPage )
1879 {
1880 if ( IsDefaultSize() )
1881 SetTabPageSizePixel( pTabPage->GetSizePixel() );
1882
1883 // only set here, so that Resize does not reposition TabPage
1884 pItem->mpTabPage = pTabPage;
1885 queue_resize();
1886
1887 if (pItem->id() == mnCurPageId)
1888 ImplChangeTabPage(pItem->id(), 0);
1889 }
1890 else
1891 {
1892 pItem->mpTabPage = nullptr;
1893 queue_resize();
1894 }
1895}
1896
1897TabPage* TabControl::GetTabPage( sal_uInt16 nPageId ) const
1898{
1899 ImplTabItem* pItem = ImplGetItem( nPageId );
1900
1901 if ( pItem )
1902 return pItem->mpTabPage;
1903 else
1904 return nullptr;
1905}
1906
1907void TabControl::SetPageText( sal_uInt16 nPageId, const OUString& rText )
1908{
1909 ImplTabItem* pItem = ImplGetItem( nPageId );
1910
1911 if ( !pItem || pItem->maText == rText )
1912 return;
1913
1914 pItem->maText = rText;
1915 mbFormat = true;
1916 if( mpTabCtrlData->mpListBox )
1917 {
1918 sal_uInt16 nPos = GetPagePos( nPageId );
1919 mpTabCtrlData->mpListBox->RemoveEntry( nPos );
1920 mpTabCtrlData->mpListBox->InsertEntry( rText, nPos );
1921 }
1922 if ( IsUpdateMode() )
1923 Invalidate();
1924 ImplFreeLayoutData();
1925 CallEventListeners( VclEventId::TabpagePageTextChanged, reinterpret_cast<void*>(nPageId) );
1926}
1927
1928OUString const & TabControl::GetPageText( sal_uInt16 nPageId ) const
1929{
1930 ImplTabItem* pItem = ImplGetItem( nPageId );
1931
1932 assert( pItem )(static_cast <bool> (pItem) ? void (0) : __assert_fail (
"pItem", "/home/maarten/src/libreoffice/core/vcl/source/control/tabctrl.cxx"
, 1932, __extension__ __PRETTY_FUNCTION__))
;
1933
1934 return pItem->maText;
1935}
1936
1937void TabControl::SetHelpText( sal_uInt16 nPageId, const OUString& rText )
1938{
1939 ImplTabItem* pItem = ImplGetItem( nPageId );
1940
1941 assert( pItem )(static_cast <bool> (pItem) ? void (0) : __assert_fail (
"pItem", "/home/maarten/src/libreoffice/core/vcl/source/control/tabctrl.cxx"
, 1941, __extension__ __PRETTY_FUNCTION__))
;
1942
1943 pItem->maHelpText = rText;
1944}
1945
1946const OUString& TabControl::GetHelpText( sal_uInt16 nPageId ) const
1947{
1948 ImplTabItem* pItem = ImplGetItem( nPageId );
1949 assert( pItem )(static_cast <bool> (pItem) ? void (0) : __assert_fail (
"pItem", "/home/maarten/src/libreoffice/core/vcl/source/control/tabctrl.cxx"
, 1949, __extension__ __PRETTY_FUNCTION__))
;
1950 return pItem->maHelpText;
1951}
1952
1953void TabControl::SetAccessibleName(sal_uInt16 nPageId, const OUString& rName)
1954{
1955 ImplTabItem* pItem = ImplGetItem( nPageId );
1956 assert( pItem )(static_cast <bool> (pItem) ? void (0) : __assert_fail (
"pItem", "/home/maarten/src/libreoffice/core/vcl/source/control/tabctrl.cxx"
, 1956, __extension__ __PRETTY_FUNCTION__))
;
1957 pItem->maAccessibleName = rName;
1958}
1959
1960OUString TabControl::GetAccessibleName( sal_uInt16 nPageId ) const
1961{
1962 ImplTabItem* pItem = ImplGetItem( nPageId );
1963 assert( pItem )(static_cast <bool> (pItem) ? void (0) : __assert_fail (
"pItem", "/home/maarten/src/libreoffice/core/vcl/source/control/tabctrl.cxx"
, 1963, __extension__ __PRETTY_FUNCTION__))
;
1964 if (!pItem->maAccessibleName.isEmpty())
1965 return pItem->maAccessibleName;
1966 return OutputDevice::GetNonMnemonicString(pItem->maText);
1967}
1968
1969void TabControl::SetAccessibleDescription(sal_uInt16 nPageId, const OUString& rDesc)
1970{
1971 ImplTabItem* pItem = ImplGetItem( nPageId );
1972 assert( pItem )(static_cast <bool> (pItem) ? void (0) : __assert_fail (
"pItem", "/home/maarten/src/libreoffice/core/vcl/source/control/tabctrl.cxx"
, 1972, __extension__ __PRETTY_FUNCTION__))
;
1973 pItem->maAccessibleDescription = rDesc;
1974}
1975
1976OUString TabControl::GetAccessibleDescription( sal_uInt16 nPageId ) const
1977{
1978 ImplTabItem* pItem = ImplGetItem( nPageId );
1979 assert( pItem )(static_cast <bool> (pItem) ? void (0) : __assert_fail (
"pItem", "/home/maarten/src/libreoffice/core/vcl/source/control/tabctrl.cxx"
, 1979, __extension__ __PRETTY_FUNCTION__))
;
1980 if (!pItem->maAccessibleDescription.isEmpty())
1981 return pItem->maAccessibleDescription;
1982 return pItem->maHelpText;
1983}
1984
1985void TabControl::SetPageName( sal_uInt16 nPageId, const OString& rName ) const
1986{
1987 ImplTabItem* pItem = ImplGetItem( nPageId );
1988
1989 if ( pItem )
1990 pItem->maTabName = rName;
1991}
1992
1993OString TabControl::GetPageName( sal_uInt16 nPageId ) const
1994{
1995 ImplTabItem* pItem = ImplGetItem( nPageId );
1996
1997 if (pItem)
1998 return pItem->maTabName;
1999
2000 return OString();
2001}
2002
2003void TabControl::SetPageImage( sal_uInt16 i_nPageId, const Image& i_rImage )
2004{
2005 ImplTabItem* pItem = ImplGetItem( i_nPageId );
2006
2007 if ( pItem )
2008 {
2009 pItem->maTabImage = i_rImage;
2010 mbFormat = true;
2011 if ( IsUpdateMode() )
2012 Invalidate();
2013 }
2014}
2015
2016tools::Rectangle TabControl::GetCharacterBounds( sal_uInt16 nPageId, long nIndex ) const
2017{
2018 tools::Rectangle aRet;
2019
2020 if( !HasLayoutData() || mpTabCtrlData->maLayoutPageIdToLine.empty() )
2021 FillLayoutData();
2022
2023 if( HasLayoutData() )
2024 {
2025 std::unordered_map< int, int >::const_iterator it = mpTabCtrlData->maLayoutPageIdToLine.find( static_cast<int>(nPageId) );
2026 if( it != mpTabCtrlData->maLayoutPageIdToLine.end() )
2027 {
2028 Pair aPair = mpControlData->mpLayoutData->GetLineStartEnd( it->second );
2029 if( (aPair.B() - aPair.A()) >= nIndex )
2030 aRet = mpControlData->mpLayoutData->GetCharacterBounds( aPair.A() + nIndex );
2031 }
2032 }
2033
2034 return aRet;
2035}
2036
2037long TabControl::GetIndexForPoint( const Point& rPoint, sal_uInt16& rPageId ) const
2038{
2039 long nRet = -1;
2040
2041 if( !HasLayoutData() || mpTabCtrlData->maLayoutPageIdToLine.empty() )
2042 FillLayoutData();
2043
2044 if( HasLayoutData() )
2045 {
2046 int nIndex = mpControlData->mpLayoutData->GetIndexForPoint( rPoint );
2047 if( nIndex != -1 )
2048 {
2049 // what line (->pageid) is this index in ?
2050 int nLines = mpControlData->mpLayoutData->GetLineCount();
2051 int nLine = -1;
2052 while( ++nLine < nLines )
2053 {
2054 Pair aPair = mpControlData->mpLayoutData->GetLineStartEnd( nLine );
2055 if( aPair.A() <= nIndex && aPair.B() >= nIndex )
2056 {
2057 nRet = nIndex - aPair.A();
2058 rPageId = static_cast<sal_uInt16>(mpTabCtrlData->maLayoutLineToPageId[ nLine ]);
2059 break;
2060 }
2061 }
2062 }
2063 }
2064
2065 return nRet;
2066}
2067
2068void TabControl::FillLayoutData() const
2069{
2070 mpTabCtrlData->maLayoutLineToPageId.clear();
2071 mpTabCtrlData->maLayoutPageIdToLine.clear();
2072 const_cast<TabControl*>(this)->Invalidate();
2073}
2074
2075tools::Rectangle TabControl::GetTabBounds( sal_uInt16 nPageId ) const
2076{
2077 tools::Rectangle aRet;
2078
2079 ImplTabItem* pItem = ImplGetItem( nPageId );
2080 if (pItem && pItem->m_bVisible)
2081 aRet = pItem->maRect;
2082
2083 return aRet;
2084}
2085
2086void TabControl::SetItemsOffset( const Point& rOffs )
2087{
2088 if( mpTabCtrlData )
2089 mpTabCtrlData->maItemsOffset = rOffs;
2090}
2091
2092Point TabControl::GetItemsOffset() const
2093{
2094 if( mpTabCtrlData )
2095 return mpTabCtrlData->maItemsOffset;
2096 else
2097 return Point();
2098}
2099
2100Size TabControl::ImplCalculateRequisition(sal_uInt16& nHeaderHeight) const
2101{
2102 Size aOptimalPageSize(0, 0);
2103
2104 sal_uInt16 nOrigPageId = GetCurPageId();
2105 for (auto const& item : mpTabCtrlData->maItemList)
2106 {
2107 const TabPage *pPage = item.mpTabPage;
2108 //it's a real nuisance if the page is not inserted yet :-(
2109 //We need to force all tabs to exist to get overall optimal size for dialog
2110 if (!pPage)
2111 {
2112 TabControl *pThis = const_cast<TabControl*>(this);
2113 pThis->SetCurPageId(item.id());
2114 pThis->ActivatePage();
2115 pPage = item.mpTabPage;
2116 }
2117
2118 if (!pPage)
2119 continue;
2120
2121 Size aPageSize(VclContainer::getLayoutRequisition(*pPage));
2122
2123 if (aPageSize.Width() > aOptimalPageSize.Width())
2124 aOptimalPageSize.setWidth( aPageSize.Width() );
2125 if (aPageSize.Height() > aOptimalPageSize.Height())
2126 aOptimalPageSize.setHeight( aPageSize.Height() );
2127 }
2128
2129 //fdo#61940 If we were forced to activate pages in order to on-demand
2130 //create them to get their optimal size, then switch back to the original
2131 //page and re-activate it
2132 if (nOrigPageId != GetCurPageId())
2133 {
2134 TabControl *pThis = const_cast<TabControl*>(this);
2135 pThis->SetCurPageId(nOrigPageId);
2136 pThis->ActivatePage();
2137 }
2138
2139 long nTabLabelsBottom = 0, nTabLabelsRight = 0;
2140 for (sal_uInt16 nPos(0), sizeList(static_cast <sal_uInt16> (mpTabCtrlData->maItemList.size()));
2141 nPos < sizeList; ++nPos)
2142 {
2143 TabControl* pThis = const_cast<TabControl*>(this);
2144
2145 tools::Rectangle aTabRect = pThis->ImplGetTabRect(nPos, aOptimalPageSize.Width(), LONG_MAX9223372036854775807L);
2146 if (aTabRect.Bottom() > nTabLabelsBottom)
2147 {
2148 nTabLabelsBottom = aTabRect.Bottom();
2149 nHeaderHeight = nTabLabelsBottom;
2150 }
2151 if (!aTabRect.IsEmpty() && aTabRect.Right() > nTabLabelsRight)
2152 nTabLabelsRight = aTabRect.Right();
2153 }
2154
2155 Size aOptimalSize(aOptimalPageSize);
2156 aOptimalSize.AdjustHeight(nTabLabelsBottom );
2157 aOptimalSize.setWidth( std::max(nTabLabelsRight, aOptimalSize.Width()) );
2158
2159 aOptimalSize.AdjustWidth(TAB_OFFSET3 * 2 );
2160 aOptimalSize.AdjustHeight(TAB_OFFSET3 * 2 );
2161
2162 return aOptimalSize;
2163}
2164
2165Size TabControl::calculateRequisition() const
2166{
2167 sal_uInt16 nHeaderHeight;
2168 return ImplCalculateRequisition(nHeaderHeight);
2169}
2170
2171Size TabControl::GetOptimalSize() const
2172{
2173 return calculateRequisition();
2174}
2175
2176void TabControl::queue_resize(StateChangedType eReason)
2177{
2178 mbLayoutDirty = true;
2179 Window::queue_resize(eReason);
2180}
2181
2182std::vector<sal_uInt16> TabControl::GetPageIDs() const
2183{
2184 std::vector<sal_uInt16> aIDs;
2185 for (auto const& item : mpTabCtrlData->maItemList)
2186 {
2187 aIDs.push_back(item.id());
2188 }
2189
2190 return aIDs;
2191}
2192
2193FactoryFunction TabControl::GetUITestFactory() const
2194{
2195 return TabControlUIObject::create;
2196}
2197
2198void TabControl::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
2199{
2200 Control::DumpAsPropertyTree(rJsonWriter);
2201 {
2202 auto tabsNode = rJsonWriter.startNode("tabs");
2203 for(auto id : GetPageIDs())
2204 {
2205 auto tabNode = rJsonWriter.startNode("");
2206 rJsonWriter.put("text", GetPageText(id));
2207 rJsonWriter.put("id", id);
2208 rJsonWriter.put("name", GetPageName(id));
2209 }
2210 }
2211 rJsonWriter.put("selected", GetCurPageId());
2212}
2213
2214sal_uInt16 NotebookbarTabControlBase::m_nHeaderHeight = 0;
2215
2216IMPL_LINK_NOARG(NotebookbarTabControlBase, OpenMenu, Button*, void)void NotebookbarTabControlBase::LinkStubOpenMenu(void * instance
, Button* data) { return static_cast<NotebookbarTabControlBase
*>(instance)->OpenMenu(data); } void NotebookbarTabControlBase
::OpenMenu(__attribute__ ((unused)) Button*)
2217{
2218 m_aIconClickHdl.Call(static_cast<NotebookBar*>(GetParent()->GetParent()));
2219}
2220
2221NotebookbarTabControlBase::NotebookbarTabControlBase(vcl::Window* pParent)
2222 : TabControl(pParent, WB_STDTABCONTROL)
2223 , bLastContextWasSupported(true)
2224 , eLastContext(vcl::EnumContext::Context::Any)
2225{
2226 m_pOpenMenu = VclPtr<PushButton>::Create( this , WB_CENTER | WB_VCENTER );
2227 m_pOpenMenu->SetClickHdl(LINK(this, NotebookbarTabControlBase, OpenMenu)::tools::detail::makeLink( ::tools::detail::castTo<NotebookbarTabControlBase
*>(this), &NotebookbarTabControlBase::LinkStubOpenMenu
)
);
2228 m_pOpenMenu->SetModeImage(Image(StockImage::Yes, SV_RESID_BITMAP_NOTEBOOKBAR"res/notebookbar.png"));
2229 m_pOpenMenu->SetSizePixel(m_pOpenMenu->GetOptimalSize());
2230 m_pOpenMenu->Show();
2231}
2232
2233NotebookbarTabControlBase::~NotebookbarTabControlBase()
2234{
2235 disposeOnce();
2236}
2237
2238void NotebookbarTabControlBase::SetContext( vcl::EnumContext::Context eContext )
2239{
2240 if (eLastContext == eContext)
2241 return;
2242
2243 bool bHandled = false;
2244
2245 for (int nChild = 0; nChild < GetPageCount(); ++nChild)
2246 {
2247 sal_uInt16 nPageId = TabControl::GetPageId(nChild);
2248 TabPage* pPage = GetTabPage(nPageId);
2249
2250 if (pPage)
2251 {
2252 SetPageVisible(nPageId, pPage->HasContext(eContext) || pPage->HasContext(vcl::EnumContext::Context::Any));
2253
2254 if (!bHandled && bLastContextWasSupported
2255 && pPage->HasContext(vcl::EnumContext::Context::Default))
2256 {
2257 SetCurPageId(nPageId);
2258 }
2259
2260 if (pPage->HasContext(eContext) && eContext != vcl::EnumContext::Context::Any)
2261 {
2262 SetCurPageId(nPageId);
2263 bHandled = true;
2264 bLastContextWasSupported = true;
2265 }
2266 }
2267 }
2268
2269 if (!bHandled)
2270 bLastContextWasSupported = false;
2271 eLastContext = eContext;
2272}
2273
2274void NotebookbarTabControlBase::dispose()
2275{
2276 m_pShortcuts.disposeAndClear();
2277 m_pOpenMenu.disposeAndClear();
1
Calling 'VclPtr::disposeAndClear'
2278 TabControl::dispose();
2279}
2280
2281void NotebookbarTabControlBase::SetToolBox( ToolBox* pToolBox )
2282{
2283 m_pShortcuts.set( pToolBox );
2284}
2285
2286void NotebookbarTabControlBase::SetIconClickHdl( Link<NotebookBar*, void> aHdl )
2287{
2288 m_aIconClickHdl = aHdl;
2289}
2290
2291static bool lcl_isValidPage(const ImplTabItem& rItem, bool& bFound)
2292{
2293 if (rItem.m_bVisible && rItem.m_bEnabled)
2294 bFound = true;
2295 return bFound;
2296}
2297
2298void NotebookbarTabControlBase::ImplActivateTabPage( bool bNext )
2299{
2300 const sal_uInt16 nOldPos = GetPagePos(GetCurPageId());
2301 bool bFound = false;
2302 sal_Int32 nCurPos = nOldPos;
2303
2304 if (bNext)
2305 {
2306 for (nCurPos++; nCurPos < GetPageCount(); nCurPos++)
2307 if (lcl_isValidPage(mpTabCtrlData->maItemList[nCurPos], bFound))
2308 break;
2309 }
2310 else
2311 {
2312 for (nCurPos--; nCurPos >= 0; nCurPos--)
2313 if (lcl_isValidPage(mpTabCtrlData->maItemList[nCurPos], bFound))
2314 break;
2315 }
2316
2317 if (!bFound)
2318 nCurPos = nOldPos;
2319 SelectTabPage( TabControl::GetPageId( nCurPos ) );
2320}
2321
2322sal_uInt16 NotebookbarTabControlBase::GetHeaderHeight()
2323{
2324 return m_nHeaderHeight;
2325}
2326
2327bool NotebookbarTabControlBase::ImplPlaceTabs( long nWidth )
2328{
2329 if ( nWidth <= 0 )
2330 return false;
2331 if ( mpTabCtrlData->maItemList.empty() )
2332 return false;
2333 if (!m_pOpenMenu || m_pOpenMenu->isDisposed())
2334 return false;
2335
2336 const long nHamburgerWidth = m_pOpenMenu->GetSizePixel().Width();
2337 long nMaxWidth = nWidth - nHamburgerWidth;
2338 long nShortcutsWidth = m_pShortcuts != nullptr ? m_pShortcuts->GetSizePixel().getWidth() + 1 : 0;
2339 long nFullWidth = nShortcutsWidth;
2340
2341 const long nOffsetX = 2 + GetItemsOffset().X() + nShortcutsWidth;
2342 const long nOffsetY = 2 + GetItemsOffset().Y();
2343
2344 //fdo#66435 throw Knuth/Tex minimum raggedness algorithm at the problem
2345 //of ugly bare tabs on lines of their own
2346
2347 for (auto & item : mpTabCtrlData->maItemList)
2348 {
2349 long nTabWidth = 0;
2350 if (item.m_bVisible)
2351 {
2352 nTabWidth = ImplGetItemSize(&item, nMaxWidth).getWidth();
2353 if (!item.maText.isEmpty() && nTabWidth < 100)
2354 nTabWidth = 100;
2355 }
2356 nFullWidth += nTabWidth;
2357 }
2358
2359 nMaxWidth -= GetItemsOffset().X();
2360
2361 long nX = nOffsetX;
2362 long nY = nOffsetY;
2363
2364 long nLineWidthAry[100];
2365 nLineWidthAry[0] = 0;
2366
2367 for (auto & item : mpTabCtrlData->maItemList)
2368 {
2369 if (!item.m_bVisible)
2370 continue;
2371
2372 Size aSize = ImplGetItemSize( &item, nMaxWidth );
2373
2374 // set minimum tab size
2375 if( nFullWidth < nMaxWidth && !item.maText.isEmpty() && aSize.getWidth() < 100)
2376 aSize.setWidth( 100 );
2377
2378 if( !item.maText.isEmpty() && aSize.getHeight() < 28 )
2379 aSize.setHeight( 28 );
2380
2381 tools::Rectangle aNewRect( Point( nX, nY ), aSize );
2382 if ( mbSmallInvalidate && (item.maRect != aNewRect) )
2383 mbSmallInvalidate = false;
2384
2385 item.maRect = aNewRect;
2386 item.mnLine = 0;
2387 item.mbFullVisible = true;
2388
2389 nLineWidthAry[0] += aSize.Width();
2390 nX += aSize.Width();
2391 }
2392
2393 // we always have only one line of tabs
2394 lcl_AdjustSingleLineTabs(nMaxWidth, mpTabCtrlData.get());
2395
2396 // position the shortcutbox
2397 if (m_pShortcuts)
2398 {
2399 long nPosY = (m_nHeaderHeight - m_pShortcuts->GetSizePixel().getHeight()) / 2;
2400 m_pShortcuts->SetPosPixel(Point(0, nPosY));
2401 }
2402
2403 long nPosY = (m_nHeaderHeight - m_pOpenMenu->GetSizePixel().getHeight()) / 2;
2404 // position the menu
2405 m_pOpenMenu->SetPosPixel(Point(nWidth - nHamburgerWidth, nPosY));
2406
2407 return true;
2408}
2409
2410Size NotebookbarTabControlBase::calculateRequisition() const
2411{
2412 return TabControl::ImplCalculateRequisition(m_nHeaderHeight);
2413}
2414
2415/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

/home/maarten/src/libreoffice/core/include/vcl/vclptr.hxx

1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#ifndef INCLUDED_VCL_PTR_HXX
21#define INCLUDED_VCL_PTR_HXX
22
23#include <sal/config.h>
24
25#include <rtl/ref.hxx>
26
27#include <utility>
28#include <type_traits>
29
30#ifdef DBG_UTIL
31#ifndef _WIN32
32#include <vcl/vclmain.hxx>
33#endif
34#endif
35
36class VclReferenceBase;
37
38namespace vcl::detail {
39
40template<typename>
41constexpr bool isIncompleteOrDerivedFromVclReferenceBase(...) { return true; }
42
43template<typename T> constexpr bool isIncompleteOrDerivedFromVclReferenceBase(
44 int (*)[sizeof(T)])
45{ return std::is_base_of<VclReferenceBase, T>::value; }
46
47} // namespace vcl::detail
48
49/**
50 * A thin wrapper around rtl::Reference to implement the acquire and dispose semantics we want for references to vcl::Window subclasses.
51 *
52 * For more details on the design please see vcl/README.lifecycle
53 *
54 * @param reference_type must be a subclass of vcl::Window
55 */
56template <class reference_type>
57class VclPtr
58{
59 static_assert(
60 vcl::detail::isIncompleteOrDerivedFromVclReferenceBase<reference_type>(
61 nullptr),
62 "template argument type must be derived from VclReferenceBase");
63
64 ::rtl::Reference<reference_type> m_rInnerRef;
65
66public:
67 /** Constructor...
68 */
69 VclPtr()
70 : m_rInnerRef()
71 {}
72
73 /** Constructor...
74 */
75 VclPtr (reference_type * pBody)
76 : m_rInnerRef(pBody)
77 {}
78
79 /** Constructor... that doesn't take a ref.
80 */
81 VclPtr (reference_type * pBody, __sal_NoAcquire)
82 : m_rInnerRef(pBody, SAL_NO_ACQUIRE)
83 {}
84
85 /** Up-casting conversion constructor: Copies interface reference.
86
87 Does not work for up-casts to ambiguous bases. For the special case of
88 up-casting to Reference< XInterface >, see the corresponding conversion
89 operator.
90
91 @param rRef another reference
92 */
93 template< class derived_type >
94 VclPtr(
95 const VclPtr< derived_type > & rRef,
96 typename std::enable_if<
97 std::is_base_of<reference_type, derived_type>::value, int>::type
98 = 0 )
99 : m_rInnerRef( static_cast<reference_type*>(rRef) )
100 {
101 }
102
103#if defined(DBG_UTIL) && !defined(_WIN32)
104 virtual ~VclPtr()
105 {
106 assert(m_rInnerRef.get() == nullptr || vclmain::isAlive())(static_cast <bool> (m_rInnerRef.get() == nullptr || vclmain
::isAlive()) ? void (0) : __assert_fail ("m_rInnerRef.get() == nullptr || vclmain::isAlive()"
, "/home/maarten/src/libreoffice/core/include/vcl/vclptr.hxx"
, 106, __extension__ __PRETTY_FUNCTION__))
;
107 // We can be one of the intermediate counts, but if we are the last
108 // VclPtr keeping this object alive, then something forgot to call dispose().
109 assert((!m_rInnerRef.get() || m_rInnerRef->isDisposed() || m_rInnerRef->getRefCount() > 1)(static_cast <bool> ((!m_rInnerRef.get() || m_rInnerRef
->isDisposed() || m_rInnerRef->getRefCount() > 1) &&
"someone forgot to call dispose()") ? void (0) : __assert_fail
("(!m_rInnerRef.get() || m_rInnerRef->isDisposed() || m_rInnerRef->getRefCount() > 1) && \"someone forgot to call dispose()\""
, "/home/maarten/src/libreoffice/core/include/vcl/vclptr.hxx"
, 110, __extension__ __PRETTY_FUNCTION__))
110 && "someone forgot to call dispose()")(static_cast <bool> ((!m_rInnerRef.get() || m_rInnerRef
->isDisposed() || m_rInnerRef->getRefCount() > 1) &&
"someone forgot to call dispose()") ? void (0) : __assert_fail
("(!m_rInnerRef.get() || m_rInnerRef->isDisposed() || m_rInnerRef->getRefCount() > 1) && \"someone forgot to call dispose()\""
, "/home/maarten/src/libreoffice/core/include/vcl/vclptr.hxx"
, 110, __extension__ __PRETTY_FUNCTION__))
;
111 }
112 VclPtr(VclPtr const &) = default;
113 VclPtr(VclPtr &&) = default;
114 VclPtr & operator =(VclPtr const &) = default;
115 VclPtr & operator =(VclPtr &&) = default;
116#endif
117
118 /**
119 * A construction helper for VclPtr. Since VclPtr types are created
120 * with a reference-count of one - to help fit into the existing
121 * code-flow; this helps us to construct them easily.
122 *
123 * For more details on the design please see vcl/README.lifecycle
124 *
125 * @tparam reference_type must be a subclass of vcl::Window
126 */
127 template<typename... Arg> [[nodiscard]] static VclPtr< reference_type > Create(Arg &&... arg)
128 {
129 return VclPtr< reference_type >( new reference_type(std::forward<Arg>(arg)...), SAL_NO_ACQUIRE );
130 }
131
132 /** Probably most common used: handle->someBodyOp().
133 */
134 reference_type * operator->() const
135 {
136 return m_rInnerRef.get();
137 }
138
139 /** Get the body. Can be used instead of operator->().
140 I.e. handle->someBodyOp() and handle.get()->someBodyOp()
141 are the same.
142 */
143 reference_type * get() const
144 {
145 return m_rInnerRef.get();
146 }
147
148 void set(reference_type *pBody)
149 {
150 m_rInnerRef.set(pBody);
151 }
152
153 void reset(reference_type *pBody)
154 {
155 m_rInnerRef.set(pBody);
156 }
157
158 /** Up-casting copy assignment operator.
159
160 Does not work for up-casts to ambiguous bases.
161
162 @param rRef another reference
163 */
164 template<typename derived_type>
165 typename std::enable_if<
166 std::is_base_of<reference_type, derived_type>::value,
167 VclPtr &>::type
168 operator =(VclPtr<derived_type> const & rRef)
169 {
170 m_rInnerRef.set(rRef.get());
171 return *this;
172 }
173
174 VclPtr & operator =(reference_type * pBody)
175 {
176 m_rInnerRef.set(pBody);
177 return *this;
178 }
179
180 operator reference_type * () const
181 {
182 return m_rInnerRef.get();
183 }
184
185 explicit operator bool () const
186 {
187 return m_rInnerRef.get() != nullptr;
188 }
189
190 void clear()
191 {
192 m_rInnerRef.clear();
193 }
194
195 void reset()
196 {
197 m_rInnerRef.clear();
198 }
199
200 void disposeAndClear()
201 {
202 // hold it alive for the lifetime of this method
203 ::rtl::Reference<reference_type> aTmp(m_rInnerRef);
2
Calling copy constructor for 'Reference<PushButton>'
5
Returning from copy constructor for 'Reference<PushButton>'
204 m_rInnerRef.clear(); // we should use some 'swap' method ideally ;-)
6
Calling 'Reference::clear'
13
Returning; memory was released
205 if (aTmp.get()) {
14
Calling 'Reference::get'
206 aTmp->disposeOnce();
207 }
208 }
209
210 /** Needed to place VclPtr's into STL collection.
211 */
212 bool operator< (const VclPtr<reference_type> & handle) const
213 {
214 return (m_rInnerRef < handle.m_rInnerRef);
215 }
216}; // class VclPtr
217
218template<typename T1, typename T2>
219inline bool operator ==(VclPtr<T1> const & p1, VclPtr<T2> const & p2) {
220 return p1.get() == p2.get();
221}
222
223template<typename T> inline bool operator ==(VclPtr<T> const & p1, T const * p2)
224{
225 return p1.get() == p2;
226}
227
228template<typename T> inline bool operator ==(VclPtr<T> const & p1, T * p2) {
229 return p1.get() == p2;
230}
231
232template<typename T> inline bool operator ==(T const * p1, VclPtr<T> const & p2)
233{
234 return p1 == p2.get();
235}
236
237template<typename T> inline bool operator ==(T * p1, VclPtr<T> const & p2) {
238 return p1 == p2.get();
239}
240
241template<typename T1, typename T2>
242inline bool operator !=(VclPtr<T1> const & p1, VclPtr<T2> const & p2) {
243 return !(p1 == p2);
244}
245
246template<typename T> inline bool operator !=(VclPtr<T> const & p1, T const * p2)
247{
248 return !(p1 == p2);
249}
250
251template<typename T> inline bool operator !=(VclPtr<T> const & p1, T * p2) {
252 return !(p1 == p2);
253}
254
255template<typename T> inline bool operator !=(T const * p1, VclPtr<T> const & p2)
256{
257 return !(p1 == p2);
258}
259
260template<typename T> inline bool operator !=(T * p1, VclPtr<T> const & p2) {
261 return !(p1 == p2);
262}
263
264/**
265 * A construction helper for a temporary VclPtr. Since VclPtr types
266 * are created with a reference-count of one - to help fit into
267 * the existing code-flow; this helps us to construct them easily.
268 * see also VclPtr::Create and ScopedVclPtr
269 *
270 * For more details on the design please see vcl/README.lifecycle
271 *
272 * @param reference_type must be a subclass of vcl::Window
273 */
274template <class reference_type>
275class SAL_WARN_UNUSED__attribute__((warn_unused)) VclPtrInstance final : public VclPtr<reference_type>
276{
277public:
278 template<typename... Arg> VclPtrInstance(Arg &&... arg)
279 : VclPtr<reference_type>( new reference_type(std::forward<Arg>(arg)...), SAL_NO_ACQUIRE )
280 {
281 }
282
283 /**
284 * Override and disallow this, to prevent people accidentally calling it and actually
285 * getting VclPtr::Create and getting a naked VclPtr<> instance
286 */
287 template<typename... Arg> static VclPtrInstance< reference_type > Create(Arg &&... ) = delete;
288};
289
290template <class reference_type>
291class ScopedVclPtr : public VclPtr<reference_type>
292{
293public:
294 /** Constructor...
295 */
296 ScopedVclPtr()
297 : VclPtr<reference_type>()
298 {}
299
300 /** Constructor
301 */
302 ScopedVclPtr (reference_type * pBody)
303 : VclPtr<reference_type>(pBody)
304 {}
305
306 /** Copy constructor...
307 */
308 ScopedVclPtr (const VclPtr<reference_type> & handle)
309 : VclPtr<reference_type>(handle)
310 {}
311
312 /**
313 Assignment that releases the last reference.
314 */
315 void disposeAndReset(reference_type *pBody)
316 {
317 if (pBody != this->get()) {
318 VclPtr<reference_type>::disposeAndClear();
319 VclPtr<reference_type>::set(pBody);
320 }
321 }
322
323 /**
324 Assignment that releases the last reference.
325 */
326 ScopedVclPtr<reference_type>& operator = (reference_type * pBody)
327 {
328 disposeAndReset(pBody);
329 return *this;
330 }
331
332 /** Up-casting conversion constructor: Copies interface reference.
333
334 Does not work for up-casts to ambiguous bases. For the special case of
335 up-casting to Reference< XInterface >, see the corresponding conversion
336 operator.
337
338 @param rRef another reference
339 */
340 template< class derived_type >
341 ScopedVclPtr(
342 const VclPtr< derived_type > & rRef,
343 typename std::enable_if<
344 std::is_base_of<reference_type, derived_type>::value, int>::type
345 = 0 )
346 : VclPtr<reference_type>( rRef )
347 {
348 }
349
350 /** Up-casting assignment operator.
351
352 Does not work for up-casts to ambiguous bases.
353
354 @param rRef another VclPtr
355 */
356 template<typename derived_type>
357 typename std::enable_if<
358 std::is_base_of<reference_type, derived_type>::value,
359 ScopedVclPtr &>::type
360 operator =(VclPtr<derived_type> const & rRef)
361 {
362 disposeAndReset(rRef.get());
363 return *this;
364 }
365
366 /**
367 * Override and disallow this, to prevent people accidentally calling it and actually
368 * getting VclPtr::Create and getting a naked VclPtr<> instance
369 */
370 template<typename... Arg> static ScopedVclPtr< reference_type > Create(Arg &&... ) = delete;
371
372 ~ScopedVclPtr()
373 {
374 VclPtr<reference_type>::disposeAndClear();
375 assert(VclPtr<reference_type>::get() == nullptr)(static_cast <bool> (VclPtr<reference_type>::get(
) == nullptr) ? void (0) : __assert_fail ("VclPtr<reference_type>::get() == nullptr"
, "/home/maarten/src/libreoffice/core/include/vcl/vclptr.hxx"
, 375, __extension__ __PRETTY_FUNCTION__))
; // make sure there are no lingering references
376 }
377
378private:
379 // Most likely we don't want this default copy-constructor.
380 ScopedVclPtr (const ScopedVclPtr<reference_type> &) = delete;
381 // And certainly we don't want a default assignment operator.
382 ScopedVclPtr<reference_type>& operator = (const ScopedVclPtr<reference_type> &) = delete;
383 // And disallow reset as that doesn't call disposeAndClear on the original reference
384 void reset() = delete;
385 void reset(reference_type *pBody) = delete;
386
387protected:
388 ScopedVclPtr (reference_type * pBody, __sal_NoAcquire)
389 : VclPtr<reference_type>(pBody, SAL_NO_ACQUIRE)
390 {}
391};
392
393/**
394 * A construction helper for ScopedVclPtr. Since VclPtr types are created
395 * with a reference-count of one - to help fit into the existing
396 * code-flow; this helps us to construct them easily.
397 *
398 * For more details on the design please see vcl/README.lifecycle
399 *
400 * @param reference_type must be a subclass of vcl::Window
401 */
402#if defined _MSC_VER
403#pragma warning(push)
404#pragma warning(disable: 4521) // " multiple copy constructors specified"
405#endif
406template <class reference_type>
407class SAL_WARN_UNUSED__attribute__((warn_unused)) ScopedVclPtrInstance final : public ScopedVclPtr<reference_type>
408{
409public:
410 template<typename... Arg> ScopedVclPtrInstance(Arg &&... arg)
411 : ScopedVclPtr<reference_type>( new reference_type(std::forward<Arg>(arg)...), SAL_NO_ACQUIRE )
412 {
413 }
414
415 /**
416 * Override and disallow this, to prevent people accidentally calling it and actually
417 * getting VclPtr::Create and getting a naked VclPtr<> instance
418 */
419 template<typename... Arg> static ScopedVclPtrInstance< reference_type > Create(Arg &&...) = delete;
420
421private:
422 // Prevent the above perfect forwarding ctor from hijacking (accidental)
423 // attempts at ScopedVclPtrInstance copy construction (where the hijacking
424 // would typically lead to somewhat obscure error messages); both non-const
425 // and const variants are needed here, as the ScopedVclPtr base class has a
426 // const--variant copy ctor, so the implicitly declared copy ctor for
427 // ScopedVclPtrInstance would also be the const variant, so non-const copy
428 // construction attempts would be hijacked by the perfect forwarding ctor;
429 // but if we only declared a non-const variant here, the const variant would
430 // no longer be implicitly declared (as there would already be an explicitly
431 // declared copy ctor), so const copy construction attempts would then be
432 // hijacked by the perfect forwarding ctor:
433 ScopedVclPtrInstance(ScopedVclPtrInstance &) = delete;
434 ScopedVclPtrInstance(ScopedVclPtrInstance const &) = delete;
435};
436#if defined _MSC_VER
437#pragma warning(pop)
438#endif
439
440#endif // INCLUDED_VCL_PTR_HXX
441
442/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

/home/maarten/src/libreoffice/core/include/rtl/ref.hxx

1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#ifndef INCLUDED_RTL_REF_HXX
21#define INCLUDED_RTL_REF_HXX
22
23#include "sal/config.h"
24
25#include <cassert>
26#include <cstddef>
27#include <functional>
28#ifdef LIBO_INTERNAL_ONLY1
29#include <type_traits>
30#endif
31
32#include "sal/types.h"
33
34namespace rtl
35{
36
37/** Template reference class for reference type.
38*/
39template <class reference_type>
40class Reference
41{
42 /** The <b>reference_type</b> body pointer.
43 */
44 reference_type * m_pBody;
45
46
47public:
48 /** Constructor...
49 */
50 Reference()
51 : m_pBody (NULL__null)
52 {}
53
54
55 /** Constructor...
56 */
57 Reference (reference_type * pBody, __sal_NoAcquire)
58 : m_pBody (pBody)
59 {
60 }
61
62 /** Constructor...
63 */
64 Reference (reference_type * pBody)
65 : m_pBody (pBody)
66 {
67 if (m_pBody)
68 m_pBody->acquire();
69 }
70
71 /** Copy constructor...
72 */
73 Reference (const Reference<reference_type> & handle)
74 : m_pBody (handle.m_pBody)
75 {
76 if (m_pBody)
3
Assuming field 'm_pBody' is non-null
4
Taking true branch
77 m_pBody->acquire();
78 }
79
80#ifdef LIBO_INTERNAL_ONLY1
81 /** Move constructor...
82 */
83 Reference (Reference<reference_type> && handle) noexcept
84 : m_pBody (handle.m_pBody)
85 {
86 handle.m_pBody = nullptr;
87 }
88#endif
89
90#if defined LIBO_INTERNAL_ONLY1
91 /** Up-casting conversion constructor: Copies interface reference.
92
93 Does not work for up-casts to ambiguous bases.
94
95 @param rRef another reference
96 */
97 template< class derived_type >
98 inline Reference(
99 const Reference< derived_type > & rRef,
100 std::enable_if_t<std::is_base_of_v<reference_type, derived_type>, int> = 0 )
101 : m_pBody (rRef.get())
102 {
103 if (m_pBody)
104 m_pBody->acquire();
105 }
106#endif
107
108 /** Destructor...
109 */
110 ~Reference() COVERITY_NOEXCEPT_FALSE
111 {
112 if (m_pBody)
113 m_pBody->release();
114 }
115
116 /** Set...
117 Similar to assignment.
118 */
119 Reference<reference_type> &
120 SAL_CALL set (reference_type * pBody)
121 {
122 if (pBody)
123 pBody->acquire();
124 reference_type * const pOld = m_pBody;
125 m_pBody = pBody;
126 if (pOld)
127 pOld->release();
128 return *this;
129 }
130
131 /** Assignment.
132 Unbinds this instance from its body (if bound) and
133 bind it to the body represented by the handle.
134 */
135 Reference<reference_type> &
136 SAL_CALL operator= (const Reference<reference_type> & handle)
137 {
138 return set( handle.m_pBody );
139 }
140
141#ifdef LIBO_INTERNAL_ONLY1
142 /** Assignment.
143 * Unbinds this instance from its body (if bound),
144 * bind it to the body represented by the handle, and
145 * set the body represented by the handle to nullptr.
146 */
147 Reference<reference_type> &
148 operator= (Reference<reference_type> && handle)
149 {
150 // self-movement guts ourself
151 if (m_pBody)
152 m_pBody->release();
153 m_pBody = handle.m_pBody;
154 handle.m_pBody = nullptr;
155 return *this;
156 }
157#endif
158
159 /** Assignment...
160 */
161 Reference<reference_type> &
162 SAL_CALL operator= (reference_type * pBody)
163 {
164 return set( pBody );
165 }
166
167 /** Unbind the body from this handle.
168 Note that for a handle representing a large body,
169 "handle.clear().set(new body());" _might_
170 perform a little bit better than "handle.set(new body());",
171 since in the second case two large objects exist in memory
172 (the old body and the new body).
173 */
174 Reference<reference_type> & SAL_CALL clear()
175 {
176 if (m_pBody
6.1
Field 'm_pBody' is non-null
6.1
Field 'm_pBody' is non-null
6.1
Field 'm_pBody' is non-null
6.1
Field 'm_pBody' is non-null
)
7
Taking true branch
177 {
178 reference_type * const pOld = m_pBody;
179 m_pBody = NULL__null;
180 pOld->release();
8
Calling 'VclReferenceBase::release'
12
Returning; memory was released
181 }
182 return *this;
183 }
184
185
186 /** Get the body. Can be used instead of operator->().
187 I.e. handle->someBodyOp() and handle.get()->someBodyOp()
188 are the same.
189 */
190 reference_type * SAL_CALL get() const
191 {
192 return m_pBody;
15
Use of memory after it is freed
193 }
194
195
196 /** Probably most common used: handle->someBodyOp().
197 */
198 reference_type * SAL_CALL operator->() const
199 {
200 assert(m_pBody != NULL)(static_cast <bool> (m_pBody != __null) ? void (0) : __assert_fail
("m_pBody != NULL", "/home/maarten/src/libreoffice/core/include/rtl/ref.hxx"
, 200, __extension__ __PRETTY_FUNCTION__))
;
201 return m_pBody;
202 }
203
204
205 /** Allows (*handle).someBodyOp().
206 */
207 reference_type & SAL_CALL operator*() const
208 {
209 assert(m_pBody != NULL)(static_cast <bool> (m_pBody != __null) ? void (0) : __assert_fail
("m_pBody != NULL", "/home/maarten/src/libreoffice/core/include/rtl/ref.hxx"
, 209, __extension__ __PRETTY_FUNCTION__))
;
210 return *m_pBody;
211 }
212
213
214 /** Returns True if the handle does point to a valid body.
215 */
216 bool SAL_CALL is() const
217 {
218 return (m_pBody != NULL__null);
219 }
220
221#if defined LIBO_INTERNAL_ONLY1
222 /** Returns True if the handle does point to a valid body.
223 */
224 explicit operator bool() const
225 {
226 return is();
227 }
228#endif
229
230 /** Returns True if this points to pBody.
231 */
232 bool SAL_CALL operator== (const reference_type * pBody) const
233 {
234 return (m_pBody == pBody);
235 }
236
237
238 /** Returns True if handle points to the same body.
239 */
240 bool
241 SAL_CALL operator== (const Reference<reference_type> & handle) const
242 {
243 return (m_pBody == handle.m_pBody);
244 }
245
246
247 /** Needed to place References into STL collection.
248 */
249 bool
250 SAL_CALL operator!= (const Reference<reference_type> & handle) const
251 {
252 return (m_pBody != handle.m_pBody);
253 }
254
255
256 /** Needed to place References into STL collection.
257 */
258 bool
259 SAL_CALL operator< (const Reference<reference_type> & handle) const
260 {
261 return (m_pBody < handle.m_pBody);
262 }
263
264
265 /** Needed to place References into STL collection.
266 */
267 bool
268 SAL_CALL operator> (const Reference<reference_type> & handle) const
269 {
270 return (m_pBody > handle.m_pBody);
271 }
272};
273
274} // namespace rtl
275
276#if defined LIBO_INTERNAL_ONLY1
277namespace std
278{
279
280/// @cond INTERNAL
281/**
282 Make rtl::Reference hashable by default for use in STL containers.
283
284 @since LibreOffice 6.3
285*/
286template<typename T>
287struct hash<::rtl::Reference<T>>
288{
289 std::size_t operator()(::rtl::Reference<T> const & s) const
290 { return std::size_t(s.get()); }
291};
292/// @endcond
293
294}
295
296#endif
297
298#endif /* ! INCLUDED_RTL_REF_HXX */
299
300/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

/home/maarten/src/libreoffice/core/include/vcl/vclreferencebase.hxx

1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19#ifndef INCLUDED_VCL_Reference_HXX
20#define INCLUDED_VCL_Reference_HXX
21
22#include <vcl/dllapi.h>
23#include <osl/interlck.h>
24
25class VCL_DLLPUBLIC__attribute__ ((visibility("default"))) VclReferenceBase
26{
27 mutable oslInterlockedCount mnRefCnt;
28
29 template<typename T> friend class VclPtr;
30
31public:
32 void acquire() const
33 {
34 osl_atomic_increment(&mnRefCnt)__sync_add_and_fetch((&mnRefCnt), 1);
35 }
36
37 void release() const
38 {
39 if (osl_atomic_decrement(&mnRefCnt)__sync_sub_and_fetch((&mnRefCnt), 1) == 0)
9
Assuming the condition is true
10
Taking true branch
40 delete this;
11
Memory is released
41 }
42#ifdef DBG_UTIL
43#ifndef _WIN32
44 sal_Int32 getRefCount() const { return mnRefCnt; }
45#endif
46#endif
47
48
49private:
50 VclReferenceBase(const VclReferenceBase&) = delete;
51 VclReferenceBase& operator=(const VclReferenceBase&) = delete;
52
53 bool mbDisposed : 1;
54
55protected:
56 VclReferenceBase();
57protected:
58 virtual ~VclReferenceBase();
59
60protected:
61 virtual void dispose();
62
63public:
64 void disposeOnce();
65 bool isDisposed() const { return mbDisposed; }
66
67};
68#endif