Coverage Report

Created: 2026-08-14 10:22

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