GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/widgets/tabbedarea.cpp Lines: 269 393 68.4 %
Date: 2021-03-17 Branches: 142 344 41.3 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2008-2009  The Mana World Development Team
4
 *  Copyright (C) 2009-2010  The Mana Developers
5
 *  Copyright (C) 2011-2019  The ManaPlus Developers
6
 *  Copyright (C) 2019-2021  Andrei Karas
7
 *
8
 *  This file is part of The ManaPlus Client.
9
 *
10
 *  This program is free software; you can redistribute it and/or modify
11
 *  it under the terms of the GNU General Public License as published by
12
 *  the Free Software Foundation; either version 2 of the License, or
13
 *  any later version.
14
 *
15
 *  This program is distributed in the hope that it will be useful,
16
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 *  GNU General Public License for more details.
19
 *
20
 *  You should have received a copy of the GNU General Public License
21
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
22
 */
23
24
/*      _______   __   __   __   ______   __   __   _______   __   __
25
 *     / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___  /\ /  |\/ /\
26
 *    / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
27
 *   / / /__   / / // / // / // / /    / ___  / // ___  / // /| ' / /
28
 *  / /_// /\ / /_// / // / // /_/_   / / // / // /\_/ / // / |  / /
29
 * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
30
 * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
31
 *
32
 * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
33
 *
34
 *
35
 * Per Larsson a.k.a finalman
36
 * Olof Naessén a.k.a jansem/yakslem
37
 *
38
 * Visit: http://guichan.sourceforge.net
39
 *
40
 * License: (BSD)
41
 * Redistribution and use in source and binary forms, with or without
42
 * modification, are permitted provided that the following conditions
43
 * are met:
44
 * 1. Redistributions of source code must retain the above copyright
45
 *    notice, this list of conditions and the following disclaimer.
46
 * 2. Redistributions in binary form must reproduce the above copyright
47
 *    notice, this list of conditions and the following disclaimer in
48
 *    the documentation and/or other materials provided with the
49
 *    distribution.
50
 * 3. Neither the name of Guichan nor the names of its contributors may
51
 *    be used to endorse or promote products derived from this software
52
 *    without specific prior written permission.
53
 *
54
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
55
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
56
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
57
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
58
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
59
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
60
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
61
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
62
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
63
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
64
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
65
 */
66
67
#include "gui/widgets/tabbedarea.h"
68
69
#include "gui/gui.h"
70
71
#include "gui/widgets/button.h"
72
#include "gui/widgets/scrollarea.h"
73
#include "gui/widgets/tabs/tab.h"
74
75
#include "utils/delete2.h"
76
#include "utils/foreach.h"
77
78
#include "debug.h"
79
80
12
TabbedArea::TabbedArea(const Widget2 *const widget) :
81
    ActionListener(),
82
    BasicContainer(widget),
83
    KeyListener(),
84
    MouseListener(),
85
    WidgetListener(),
86
    mArrowButton(),
87
    mSelectedTab(nullptr),
88

12
    mTabContainer(new BasicContainer2(widget)),
89

12
    mWidgetContainer(new BasicContainer2(widget)),
90
    mTabsToDelete(),
91
    mTabs(),
92
    mTabsWidth(0),
93
    mVisibleTabsWidth(0),
94
    mTabScrollIndex(0),
95
    mRightMargin(0),
96
    mOpaque(Opaque_false),
97
    mEnableScrollButtons(false),
98
    mFollowDownScroll(false),
99
    mBlockSwitching(true),
100
120
    mResizeHeight(true)
101
{
102
12
    setFocusable(true);
103
12
    addKeyListener(this);
104
12
    addMouseListener(this);
105
12
}
106
107
12
void TabbedArea::postInit()
108
{
109
24
    mTabContainer->setOpaque(Opaque_false);
110
111
12
    add(mTabContainer);
112
12
    add(mWidgetContainer);
113
114
24
    mWidgetContainer->setOpaque(Opaque_false);
115
12
    addWidgetListener(this);
116
117
12
    mArrowButton[0] = new Button(this,
118
        "<",
119
        "shift_left",
120
        BUTTON_SKIN,
121

72
        this);
122
12
    mArrowButton[1] = new Button(this,
123
        ">",
124
        "shift_right",
125
        BUTTON_SKIN,
126

84
        this);
127
128
12
    widgetResized(Event(nullptr));
129
12
}
130
131
96
TabbedArea::~TabbedArea()
132
{
133
12
    if (gui != nullptr)
134
12
        gui->removeDragged(this);
135
136
    // +++ virtual method calls
137
12
    remove(mTabContainer);
138
12
    remove(mWidgetContainer);
139
140
12
    delete2(mTabContainer)
141
12
    delete2(mWidgetContainer)
142
143
63
    for (size_t i = 0, sz = mTabsToDelete.size(); i < sz; i++)
144
117
        delete2(mTabsToDelete[i])
145
146
12
    delete2(mArrowButton[0])
147
12
    delete2(mArrowButton[1])
148
24
}
149
150
4
void TabbedArea::enableScrollButtons(const bool enable)
151
{
152

4
    if (mEnableScrollButtons && !enable)
153
    {
154
        if (mArrowButton[0] != nullptr)
155
            remove(mArrowButton[0]);
156
        if (mArrowButton[1] != nullptr)
157
            remove(mArrowButton[1]);
158
    }
159

4
    else if (!mEnableScrollButtons && enable)
160
    {
161
4
        if (mArrowButton[0] != nullptr)
162
4
            add(mArrowButton[0]);
163
4
        if (mArrowButton[1] != nullptr)
164
4
            add(mArrowButton[1]);
165
    }
166
4
    mEnableScrollButtons = enable;
167
4
}
168
169
int TabbedArea::getNumberOfTabs() const
170
{
171
18
    return CAST_S32(mTabs.size());
172
}
173
174
Tab *TabbedArea::getTab(const std::string &name) const
175
{
176
    TabContainer::const_iterator itr = mTabs.begin();
177
    const TabContainer::const_iterator itr_end = mTabs.end();
178
    while (itr != itr_end)
179
    {
180
        if ((*itr).first->getCaption() == name)
181
            return static_cast<Tab*>((*itr).first);
182
183
        ++itr;
184
    }
185
    return nullptr;
186
}
187
188
1
void TabbedArea::draw(Graphics *const graphics)
189
{
190
    BLOCK_START("TabbedArea::draw")
191
2
    if (mTabs.empty())
192
    {
193
        BLOCK_END("TabbedArea::draw")
194
        return;
195
    }
196
197
    drawChildren(graphics);
198
    BLOCK_END("TabbedArea::draw")
199
}
200
201
void TabbedArea::safeDraw(Graphics *const graphics)
202
{
203
    BLOCK_START("TabbedArea::draw")
204
    if (mTabs.empty())
205
    {
206
        BLOCK_END("TabbedArea::draw")
207
        return;
208
    }
209
210
    safeDrawChildren(graphics);
211
    BLOCK_END("TabbedArea::draw")
212
}
213
214
192
Widget *TabbedArea::getWidget(const std::string &name) const
215
{
216
384
    TabContainer::const_iterator itr = mTabs.begin();
217
384
    const TabContainer::const_iterator itr_end = mTabs.end();
218
192
    while (itr != itr_end)
219
    {
220
192
        if ((*itr).first->getCaption() == name)
221
192
            return (*itr).second;
222
223
        ++itr;
224
    }
225
226
    return nullptr;
227
}
228
229
279
Widget *TabbedArea::getCurrentWidget() const
230
{
231
279
    const Tab *const tab = getSelectedTab();
232
233
279
    if (tab != nullptr)
234
192
        return getWidget(tab->getCaption());
235
    return nullptr;
236
}
237
238
49
void TabbedArea::addTab(Tab *const tab,
239
                        Widget *const widget)
240
{
241

49
    if ((tab == nullptr) || (widget == nullptr))
242
        return;
243
244
49
    tab->setTabbedArea(this);
245
49
    tab->addActionListener(this);
246
247
49
    mTabContainer->add(tab);
248
98
    mTabs.push_back(std::pair<Tab*, Widget*>(tab, widget));
249
250

49
    if ((mSelectedTab == nullptr) && tab->mVisible == Visible_true)
251
9
        setSelectedTab(tab);
252
253
49
    adjustTabPositions();
254
49
    adjustSize();
255
256
49
    const int frameSize = 2 * mFrameSize;
257
98
    widget->setSize(getWidth() - frameSize,
258
196
        getHeight() - frameSize - mTabContainer->getHeight());
259
260
49
    widgetResized(Event(nullptr));
261
49
    updateTabsWidth();
262
49
    updateArrowEnableState();
263
}
264
265
4
void TabbedArea::adjustWidget(Widget *const widget) const
266
{
267
4
    if (widget == nullptr)
268
        return;
269
4
    const int frameSize = 2 * mFrameSize;
270
8
    widget->setSize(getWidth() - frameSize,
271
16
        getHeight() - frameSize - mTabContainer->getHeight());
272
}
273
274
43
void TabbedArea::addTab(const std::string &caption, Widget *const widget)
275
{
276
43
    Tab *const tab = new Tab(this);
277
43
    tab->setCaption(caption);
278
43
    mTabsToDelete.push_back(tab);
279
280
43
    addTab(tab, widget);
281
43
}
282
283
void TabbedArea::addTab(Image *const image, Widget *const widget)
284
{
285
    Tab *const tab = new Tab(this);
286
    tab->setImage(image);
287
    mTabsToDelete.push_back(tab);
288
289
    addTab(tab, widget);
290
}
291
292
bool TabbedArea::isTabSelected(const size_t index) const
293
{
294
    if (index >= mTabs.size())
295
        return false;
296
297
    return mSelectedTab == mTabs[index].first;
298
}
299
300
bool TabbedArea::isTabPresent(const Tab *const tab) const
301
{
302
    FOR_EACH (TabContainer::const_iterator, it, mTabs)
303
    {
304
        if ((*it).first == tab || (*it).second == tab)
305
            return true;
306
    }
307
    return false;
308
}
309
310
bool TabbedArea::isTabSelected(const Tab *const tab) const
311
{
312
    return mSelectedTab == tab;
313
}
314
315
void TabbedArea::setSelectedTabByIndex(const size_t index)
316
{
317

1
    if (index >= mTabs.size())
318
        return;
319
320
1
    setSelectedTab(mTabs[index].first);
321
}
322
323
6
void TabbedArea::removeTab(Tab *const tab)
324
{
325
6
    int tabIndexToBeSelected = -1;
326
327
6
    if (tab == mSelectedTab)
328
    {
329
2
        const int index = getSelectedTabIndex();
330
2
        const size_t sz = mTabs.size();
331

2
        if (index == CAST_S32(sz) - 1 && sz == 1)
332
            tabIndexToBeSelected = -1;
333
        else
334
1
            tabIndexToBeSelected = index - 1;
335
    }
336
337
24
    for (TabContainer::iterator iter = mTabs.begin();
338
24
         iter != mTabs.end(); ++iter)
339
    {
340
11
        if (iter->first == tab)
341
        {
342
5
            mTabContainer->remove(tab);
343
10
            mTabs.erase(iter);
344
5
            break;
345
        }
346
    }
347
348
24
    for (STD_VECTOR<Tab*>::iterator iter2 = mTabsToDelete.begin();
349
24
         iter2 != mTabsToDelete.end(); ++iter2)
350
    {
351
10
        if (*iter2 == tab)
352
        {
353
8
            mTabsToDelete.erase(iter2);
354
4
            delete tab;
355
            break;
356
        }
357
    }
358
359
12
    const int tabsSize = CAST_S32(mTabs.size());
360
6
    if (tabIndexToBeSelected >= tabsSize)
361
        tabIndexToBeSelected = tabsSize - 1;
362
6
    if (tabIndexToBeSelected < -1)
363
1
        tabIndexToBeSelected = -1;
364
365
6
    if (tabIndexToBeSelected == -1)
366
    {
367
6
        mSelectedTab = nullptr;
368
6
        mWidgetContainer->clear();
369
    }
370
    else
371
    {
372
        setSelectedTabByIndex(tabIndexToBeSelected);
373
    }
374
375
6
    adjustSize();
376
6
    updateTabsWidth();
377
6
    widgetResized(Event(nullptr));
378
6
}
379
380
1
void TabbedArea::logic()
381
{
382
    BLOCK_START("TabbedArea::logic")
383
1
    logicChildren();
384
    BLOCK_END("TabbedArea::logic")
385
1
}
386
387
void TabbedArea::mousePressed(MouseEvent &event)
388
{
389
    if (event.isConsumed())
390
        return;
391
392
    if (event.getButton() == MouseButton::LEFT)
393
    {
394
        Widget *const widget = mTabContainer->getWidgetAt(
395
            event.getX(), event.getY());
396
        Tab *const tab = dynamic_cast<Tab *>(widget);
397
398
        if (tab != nullptr)
399
        {
400
            event.consume();
401
            setSelectedTab(tab);
402
            requestFocus();
403
        }
404
    }
405
}
406
407
10
void TabbedArea::setSelectedTab(Tab *const tab)
408
{
409
46
    for (size_t i = 0; i < mTabs.size(); i++)
410
    {
411
26
        if (mTabs[i].first == mSelectedTab)
412
1
            mWidgetContainer->remove(mTabs[i].second);
413
    }
414
415
59
    for (size_t i = 0; i < mTabs.size(); i++)
416
    {
417
26
        if (mTabs[i].first == tab)
418
        {
419
10
            mSelectedTab = tab;
420
10
            mWidgetContainer->add(mTabs[i].second);
421
        }
422
    }
423
424
10
    Tab *const newTab = tab;
425
426
10
    if (newTab != nullptr)
427
10
        newTab->setCurrent();
428
429
10
    widgetResized(Event(nullptr));
430
10
}
431
432
void TabbedArea::setSelectedTabDefault()
433
{
434
    if (mSelectedTab == nullptr ||
435
        mSelectedTab->mVisible == Visible_false)
436
    {
437
        for (size_t i = 0; i < mTabs.size(); i++)
438
        {
439
            Tab *const tab = mTabs[i].first;
440
            if ((tab != nullptr) && tab->mVisible == Visible_true)
441
            {
442
                setSelectedTab(tab);
443
                return;
444
            }
445
        }
446
    }
447
}
448
449
int TabbedArea::getSelectedTabIndex() const
450
{
451
10
    for (unsigned int i = 0, fsz = CAST_U32(mTabs.size());
452



5
         i < fsz;
453
         i++)
454
    {
455



4
        if (mTabs[i].first == mSelectedTab)
456
2
            return i;
457
    }
458
459
    return -1;
460
}
461
462
void TabbedArea::setSelectedTabByName(const std::string &name)
463
{
464
    FOR_EACH (TabContainer::const_iterator, itr, mTabs)
465
    {
466
        if (((*itr).first != nullptr) && (*itr).first->getCaption() == name)
467
        {
468
            setSelectedTab((*itr).first);
469
            return;
470
        }
471
    }
472
}
473
474
99
void TabbedArea::widgetResized(const Event &event A_UNUSED)
475
{
476
99
    adjustSize();
477
478
99
    const int frameSize = 2 * mFrameSize;
479
198
    const int widgetFrameSize = 2 * mWidgetContainer->getFrameSize();
480
99
    const int w1 = mDimension.width;
481
99
    const int h1 = mDimension.height;
482
99
    const int height = h1 - frameSize
483
198
        - mWidgetContainer->getY() - widgetFrameSize;
484
485
99
    Widget *const w = getCurrentWidget();
486
99
    ScrollArea *const scr = dynamic_cast<ScrollArea *>(w);
487
99
    if (scr != nullptr)
488
    {
489

8
        if (mFollowDownScroll && height != 0)
490
        {
491
2
            const Rect &rect = w->getDimension();
492

2
            if (rect.height != 0 && rect.height > height + 2)
493
            {
494
                if (scr->getVerticalScrollAmount()
495
                    >= scr->getVerticalMaxScroll() - 2
496
                    && scr->getVerticalScrollAmount()
497
                    <= scr->getVerticalMaxScroll() + 2)
498
                {
499
                    const int newScroll = scr->getVerticalScrollAmount()
500
                        + rect.height - height;
501
                    w->setSize(mWidgetContainer->getWidth() - frameSize,
502
                        mWidgetContainer->getHeight() - frameSize);
503
                    if (newScroll != 0)
504
                        scr->setVerticalScrollAmount(newScroll);
505
                }
506
            }
507
        }
508
    }
509
510
99
    if (mArrowButton[1] != nullptr)
511
    {
512
        // Check whether there is room to show more tabs now.
513
198
        int innerWidth = w1 - 4 - mArrowButton[0]->getWidth()
514
198
            - mArrowButton[1]->getWidth() - mRightMargin;
515
99
        if (innerWidth < 0)
516
28
            innerWidth = 0;
517
518
99
        int newWidth = mVisibleTabsWidth;
519

99
        while ((mTabScrollIndex != 0U) && newWidth < innerWidth)
520
        {
521
            Tab *const tab = mTabs[mTabScrollIndex - 1].first;
522
            if ((tab != nullptr) && tab->mVisible == Visible_true)
523
            {
524
                newWidth += tab->getWidth();
525
                if (newWidth < innerWidth)
526
                    --mTabScrollIndex;
527
            }
528
        }
529
530
        if (mArrowButton[1] != nullptr)
531
        {
532
99
            const int width = w1 - frameSize - widgetFrameSize;
533
            // Move the right arrow to fit the windows content.
534
198
            newWidth = width - mArrowButton[1]->getWidth() - mRightMargin;
535
99
            if (newWidth < 0)
536
28
                newWidth = 0;
537
99
            mArrowButton[1]->setPosition(newWidth, 0);
538
        }
539
    }
540
541
99
    updateArrowEnableState();
542
99
    adjustTabPositions();
543
99
}
544
545
203
void TabbedArea::updateTabsWidth()
546
{
547
203
    mTabsWidth = 0;
548
2010
    FOR_EACH (TabContainer::const_iterator, itr, mTabs)
549
    {
550
792
        Tab *const tab = (*itr).first;
551

792
        if ((tab != nullptr) && tab->mVisible == Visible_true)
552
1584
            mTabsWidth += tab->getWidth();
553
    }
554
203
    updateVisibleTabsWidth();
555
203
}
556
557
203
void TabbedArea::updateVisibleTabsWidth()
558
{
559
203
    unsigned int visibleTabsWidth = 0;
560
1198
    for (size_t i = mTabScrollIndex, sz = mTabs.size(); i < sz; ++i)
561
    {
562
1584
        Tab *const tab = mTabs[i].first;
563

792
        if (tab != nullptr && tab->mVisible == Visible_true)
564
1584
            visibleTabsWidth += CAST_S32(tab->getWidth());
565
    }
566
203
    mVisibleTabsWidth = visibleTabsWidth;
567
203
}
568
569
180
void TabbedArea::adjustSize()
570
{
571
180
    int maxTabHeight = 0;
572
573
180
    const int width = mDimension.width;
574
180
    const int height = mDimension.height;
575
576
964
    for (size_t i = 0, sz = mTabs.size(); i < sz; i++)
577
    {
578
1812
        if (mTabs[i].first->getHeight() > maxTabHeight)
579
131
            maxTabHeight = mTabs[i].first->getHeight();
580
    }
581
582
180
    mTabContainer->setSize(width - mRightMargin, maxTabHeight);
583
584
180
    mWidgetContainer->setPosition(0, maxTabHeight);
585
180
    mWidgetContainer->setSize(width, height - maxTabHeight);
586
180
    Widget *const w = getCurrentWidget();
587
180
    if (w != nullptr)
588
    {
589
125
        const int wFrameSize = w->getFrameSize();
590
125
        const int frame2 = 2 * wFrameSize;
591
592
125
        w->setPosition(wFrameSize, wFrameSize);
593
125
        if (mResizeHeight)
594
        {
595
244
            w->setSize(mWidgetContainer->getWidth() - frame2,
596
366
                mWidgetContainer->getHeight() - frame2);
597
        }
598
        else
599
        {
600
6
            w->setSize(mWidgetContainer->getWidth() - frame2,
601
3
                w->getHeight());
602
        }
603
    }
604
180
}
605
606
148
void TabbedArea::adjustTabPositions()
607
{
608
148
    int maxTabHeight = 0;
609
296
    const size_t sz = mTabs.size();
610
695
    for (size_t i = 0; i < sz; ++i)
611
    {
612
1094
        const Tab *const tab = mTabs[i].first;
613

1094
        if ((tab != nullptr) &&
614

1094
            tab->mVisible == Visible_true &&
615
1094
            tab->getHeight() > maxTabHeight)
616
        {
617
238
            maxTabHeight = tab->getHeight();
618
        }
619
    }
620
621
212
    unsigned int x = (mEnableScrollButtons &&
622
160
        mArrowButton[0]->mVisible == Visible_true) ?
623
172
        mArrowButton[0]->getWidth() : 0U;
624
695
    for (size_t i = mTabScrollIndex; i < sz; ++i)
625
    {
626
1094
        Tab *const tab = mTabs[i].first;
627

547
        if ((tab == nullptr) || tab->mVisible == Visible_false)
628
            continue;
629
1094
        tab->setPosition(x, maxTabHeight - tab->getHeight());
630
1094
        x += tab->getWidth();
631
    }
632
633
    // If the tabs are scrolled, we hide them away.
634
148
    if (mTabScrollIndex > 0)
635
    {
636
        x = 0;
637
        for (unsigned i = 0; i < mTabScrollIndex; ++i)
638
        {
639
            Tab *const tab = mTabs[i].first;
640
            if ((tab != nullptr) && tab->mVisible == Visible_true)
641
            {
642
                x -= tab->getWidth();
643
                tab->setPosition(x, maxTabHeight - tab->getHeight());
644
            }
645
        }
646
    }
647
148
}
648
649
void TabbedArea::action(const ActionEvent& actionEvent)
650
{
651
    Widget *const source = actionEvent.getSource();
652
    Tab *const tab = dynamic_cast<Tab *>(source);
653
654
    if (tab != nullptr)
655
    {
656
        setSelectedTab(tab);
657
    }
658
    else
659
    {
660
        const std::string &eventId = actionEvent.getId();
661
        if (eventId == "shift_left")
662
        {
663
            if (mTabScrollIndex != 0U)
664
                --mTabScrollIndex;
665
        }
666
        else if (eventId == "shift_right")
667
        {
668
            if (CAST_SIZE(mTabScrollIndex) < mTabs.size() - 1)
669
                ++mTabScrollIndex;
670
        }
671
        adjustTabPositions();
672
        updateArrowEnableState();
673
    }
674
}
675
676
148
void TabbedArea::updateArrowEnableState()
677
{
678
148
    updateTabsWidth();
679

148
    if ((mArrowButton[0] == nullptr) || (mArrowButton[1] == nullptr))
680
        return;
681
682
148
    const int width = mDimension.width;
683
296
    if (mTabsWidth > width - 4
684
296
        - mArrowButton[0]->getWidth()
685
296
        - mArrowButton[1]->getWidth() - mRightMargin)
686
    {
687
64
        mArrowButton[0]->setVisible(Visible_true);
688
64
        mArrowButton[1]->setVisible(Visible_true);
689
    }
690
    else
691
    {
692
84
        mArrowButton[0]->setVisible(Visible_false);
693
84
        mArrowButton[1]->setVisible(Visible_false);
694
84
        mTabScrollIndex = 0;
695
    }
696
697
    // Left arrow consistency check
698
148
    if (mTabScrollIndex == 0U)
699
148
        mArrowButton[0]->setEnabled(false);
700
    else
701
        mArrowButton[0]->setEnabled(true);
702
703
    // Right arrow consistency check
704
296
    if (mVisibleTabsWidth < width - 4
705
296
        - mArrowButton[0]->getWidth()
706
296
        - mArrowButton[1]->getWidth() - mRightMargin)
707
    {
708
84
        mArrowButton[1]->setEnabled(false);
709
    }
710
    else
711
    {
712
64
        mArrowButton[1]->setEnabled(true);
713
    }
714
}
715
716
1
Tab *TabbedArea::getTabByIndex(const int index) const
717
{
718

2
    if (index < 0 || index >= CAST_S32(mTabs.size()))
719
        return nullptr;
720
    return static_cast<Tab*>(mTabs[index].first);
721
}
722
723
Widget *TabbedArea::getWidgetByIndex(const int index) const
724
{
725
    if (index < 0 || index >= CAST_S32(mTabs.size()))
726
        return nullptr;
727
    return mTabs[index].second;
728
}
729
730
3
void TabbedArea::removeAll(const bool del)
731
{
732
3
    if (getSelectedTabIndex() != -1)
733
    {
734
        setSelectedTabByIndex(CAST_U32(0));
735
    }
736
7
    while (getNumberOfTabs() > 0)
737
    {
738
4
        const int idx = getNumberOfTabs() - 1;
739
8
        Tab *tab = mTabs[idx].first;
740
4
        Widget *widget = mTabs[idx].second;
741
4
        removeTab(tab);
742
4
        if (del)
743
        {
744
            delete tab;
745
            delete widget;
746
        }
747
    }
748
3
}
749
750
3
void TabbedArea::setWidth(int width)
751
{
752
    // +++ need use virtual
753
3
    Widget::setWidth(width);
754
3
    adjustSize();
755
3
}
756
757
3
void TabbedArea::setHeight(int height)
758
{
759
    // +++ need use virtual
760
3
    Widget::setHeight(height);
761
3
    adjustSize();
762
3
}
763
764
2
void TabbedArea::setSize(int width, int height)
765
{
766
    // +++ need use virtual
767
2
    Widget::setSize(width, height);
768
2
    adjustSize();
769
2
}
770
771
12
void TabbedArea::setDimension(const Rect &dimension)
772
{
773
    // +++ need use virtual
774
12
    Widget::setDimension(dimension);
775
12
    adjustSize();
776
12
}
777
778
void TabbedArea::keyPressed(KeyEvent& event)
779
{
780
    if (mBlockSwitching || event.isConsumed() || !isFocused())
781
        return;
782
783
    const InputActionT actionId = event.getActionId();
784
785
    if (actionId == InputAction::GUI_LEFT)
786
    {
787
        int index = getSelectedTabIndex();
788
        index--;
789
790
        if (index < 0)
791
            return;
792
793
        setSelectedTab(mTabs[index].first);
794
        event.consume();
795
    }
796
    else if (actionId == InputAction::GUI_RIGHT)
797
    {
798
        int index = getSelectedTabIndex();
799
        index++;
800
801
        if (index >= CAST_S32(mTabs.size()))
802
            return;
803
804
        setSelectedTab(mTabs[index].first);
805
        event.consume();
806
    }
807
}
808
809
8
void TabbedArea::death(const Event &event)
810
{
811
8
    Tab *const tab = dynamic_cast<Tab*>(event.getSource());
812
813
8
    if (tab != nullptr)
814
        removeTab(tab);
815
    else
816
8
        BasicContainer::death(event);
817
8
}
818
819
void TabbedArea::selectNextTab()
820
{
821
    int tab = getSelectedTabIndex();
822
    tab++;
823
    if (tab == CAST_S32(mTabs.size()))
824
        tab = 0;
825
    setSelectedTab(mTabs[tab].first);
826
}
827
828
void TabbedArea::selectPrevTab()
829
{
830
    int tab = getSelectedTabIndex();
831
832
    if (tab == 0)
833
        tab = CAST_S32(mTabs.size());
834
    if (tab < 0)
835
        return;
836
    tab--;
837
    setSelectedTab(mTabs[tab].first);
838

3
}