GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/widgets/scrollarea.cpp Lines: 298 696 42.8 %
Date: 2017-11-29 Branches: 166 583 28.5 %

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

10
};
103
104
326
ScrollArea::ScrollArea(Widget2 *const widget2,
105
                       Widget *const widget,
106
                       const Opaque opaque,
107
326
                       const std::string &skin) :
108
    BasicContainer(widget2),
109
    MouseListener(),
110
    WidgetListener(),
111

326
    mVertexes(new ImageCollection),
112

326
    mVertexes2(new ImageCollection),
113
    mHPolicy(SHOW_AUTO),
114
    mVPolicy(SHOW_AUTO),
115
    mVScroll(0),
116
    mHScroll(0),
117
    mScrollbarWidth(12),
118
    mUpButtonScrollAmount(10),
119
    mDownButtonScrollAmount(10),
120
    mLeftButtonScrollAmount(10),
121
    mRightButtonScrollAmount(10),
122
    mHorizontalMarkerDragOffset(0),
123
    mVerticalMarkerDragOffset(0),
124
    mX(0),
125
    mY(0),
126
    mClickX(0),
127
    mClickY(0),
128
    mXOffset(0),
129
    mYOffset(0),
130
    mDrawWidth(0),
131
    mDrawHeight(0),
132
    mVBarVisible(false),
133
    mHBarVisible(false),
134
    mUpButtonPressed(false),
135
    mDownButtonPressed(false),
136
    mLeftButtonPressed(false),
137
    mRightButtonPressed(false),
138
    mIsVerticalMarkerDragged(false),
139
    mIsHorizontalMarkerDragged(false),
140
    mOpaque(Opaque_true),
141
1956
    mHasMouse(false)
142
{
143
326
    setContent(widget);
144
326
    addMouseListener(this);
145
326
    mOpaque = opaque;
146
652
    init(skin);
147
326
}
148
149
1630
ScrollArea::~ScrollArea()
150
{
151
326
    if (gui != nullptr)
152
326
        gui->removeDragged(this);
153
154
    // Garbage collection
155
262
    delete getContent();
156
157
326
    instances--;
158
326
    if (instances == 0)
159
    {
160
70
        Theme::unloadRect(background);
161
70
        Theme::unloadRect(vMarker);
162
70
        Theme::unloadRect(vMarkerHi);
163
70
        Theme::unloadRect(vBackground);
164
70
        Theme::unloadRect(hBackground);
165
210
        for (int i = 0; i < 2; i ++)
166
        {
167
1260
            for (int f = UP; f < BUTTONS_DIR; f ++)
168
            {
169
560
                if (buttons[f][i] != nullptr)
170
560
                    buttons[f][i]->decRef();
171
            }
172
        }
173
    }
174
175
326
    delete2(mVertexes);
176
326
    delete2(mVertexes2);
177
178
326
    setContent(nullptr);
179
652
}
180
181
326
void ScrollArea::init(std::string skinName)
182
{
183
652
    setOpaque(mOpaque);
184
185
652
    setUpButtonScrollAmount(2);
186
652
    setDownButtonScrollAmount(2);
187
652
    setLeftButtonScrollAmount(2);
188
652
    setRightButtonScrollAmount(2);
189
190
326
    if (instances == 0)
191
    {
192
1330
        for (int f = 0; f < 9; f ++)
193
        {
194
630
            background.grid[f] = nullptr;
195
630
            vMarker.grid[f] = nullptr;
196
630
            vMarkerHi.grid[f] = nullptr;
197
630
            vBackground.grid[f] = nullptr;
198
630
            hBackground.grid[f] = nullptr;
199
        }
200
201
        // +++ here probably need move background from static
202
70
        if (skinName.empty())
203
            skinName = "scroll_background.xml";
204
70
        if (theme != nullptr)
205
        {
206

280
            theme->loadRect(background, skinName, "scroll_background.xml");
207

490
            theme->loadRect(vMarker, "scroll.xml", "");
208

490
            theme->loadRect(vMarkerHi, "scroll_highlighted.xml", "scroll.xml");
209

490
            theme->loadRect(vBackground, "scroll_vbackground.xml", "");
210

490
            theme->loadRect(hBackground, "scroll_hbackground.xml", "");
211
        }
212
213
350
        for (int i = 0; i < 2; i ++)
214
        {
215
140
            Skin *skin = nullptr;
216
140
            if (theme != nullptr)
217

700
                skin = theme->load(buttonFiles[i], "scrollbuttons.xml");
218
140
            if (skin != nullptr)
219
            {
220
140
                const ImageRect &rect = skin->getBorder();
221
700
                for (int f = UP; f < BUTTONS_DIR; f ++)
222
                {
223
560
                    if (rect.grid[f] != nullptr)
224
560
                        rect.grid[f]->incRef();
225
560
                    buttons[f][i] = rect.grid[f];
226
                }
227
140
                if (i == 0)
228
                {
229

280
                    mShowButtons = (skin->getOption("showbuttons", 1) == 1);
230

280
                    mMarkerSize = skin->getOption("markersize", 0);
231

280
                    mScrollbarSize = skin->getOption("scrollbarsize", 12);
232
                }
233
            }
234
            else
235
            {
236
                for (int f = UP; f < BUTTONS_DIR; f ++)
237
                    buttons[f][i] = nullptr;
238
            }
239
140
            if (theme != nullptr)
240
140
                theme->unload(skin);
241
        }
242
    }
243
326
    mScrollbarWidth = mScrollbarSize;
244
326
    instances++;
245
326
}
246
247
1
void ScrollArea::logic()
248
{
249
    BLOCK_START("ScrollArea::logic")
250
1
    if (!isVisible())
251
    {
252
        BLOCK_END("ScrollArea::logic")
253
        return;
254
    }
255
256
1
    checkPolicies();
257
258
2
    setVerticalScrollAmount(mVScroll);
259
2
    setHorizontalScrollAmount(mHScroll);
260
261
1
    Widget *const content = getContent();
262
1
    if (content != nullptr)
263
    {
264
1
        unsigned int frameSize = content->getFrameSize();
265
1
        content->setPosition(-mHScroll + frameSize, -mVScroll + frameSize);
266
1
        content->logic();
267
268
        // When no scrollbar in a certain direction,
269
        // adapt content size to match the content dimension exactly.
270
1
        frameSize = 2 * content->getFrameSize();
271
1
        if (mHPolicy == ScrollArea::SHOW_NEVER)
272
        {
273
3
            content->setWidth((mVBarVisible ? (mDimension.width
274
2
                - mScrollbarWidth) : mDimension.width) - frameSize);
275
        }
276
1
        if (mVPolicy == ScrollArea::SHOW_NEVER)
277
        {
278
            content->setHeight((mHBarVisible ? (mDimension.height
279
                - mScrollbarWidth) : mDimension.height) - frameSize);
280
        }
281
    }
282
283
1
    if (mUpButtonPressed)
284
        setVerticalScrollAmount(mVScroll - mUpButtonScrollAmount);
285
1
    else if (mDownButtonPressed)
286
        setVerticalScrollAmount(mVScroll + mDownButtonScrollAmount);
287
1
    else if (mLeftButtonPressed)
288
        setHorizontalScrollAmount(mHScroll - mLeftButtonScrollAmount);
289
1
    else if (mRightButtonPressed)
290
        setHorizontalScrollAmount(mHScroll + mRightButtonScrollAmount);
291
    BLOCK_END("ScrollArea::logic")
292
}
293
294
34
void ScrollArea::updateAlpha()
295
{
296
    const float alpha = std::max(settings.guiAlpha,
297
102
        theme->getMinimumOpacity());
298
299
34
    if (alpha != mAlpha)
300
    {
301
        mAlpha = alpha;
302
        for (int a = 0; a < 9; a++)
303
        {
304
            if (background.grid[a] != nullptr)
305
                background.grid[a]->setAlpha(mAlpha);
306
            if (hBackground.grid[a] != nullptr)
307
                hBackground.grid[a]->setAlpha(mAlpha);
308
            if (vBackground.grid[a] != nullptr)
309
                vBackground.grid[a]->setAlpha(mAlpha);
310
            if (vMarker.grid[a] != nullptr)
311
                vMarker.grid[a]->setAlpha(mAlpha);
312
            if (vMarkerHi.grid[a] != nullptr)
313
                vMarkerHi.grid[a]->setAlpha(mAlpha);
314
        }
315
    }
316
34
}
317
318
34
void ScrollArea::draw(Graphics *const graphics)
319
{
320
    BLOCK_START("ScrollArea::draw")
321

34
    if (mVBarVisible || mHBarVisible)
322
    {
323
22
        if (mOpaque == Opaque_false)
324
            updateCalcFlag(graphics);
325
        // need add caching or remove calc calls.
326
//        if (mRedraw)
327
        {
328
22
            mVertexes->clear();
329
22
            if (mVBarVisible)
330
            {
331
22
                if (mShowButtons)
332
                {
333
                    calcButton(graphics, UP);
334
                    calcButton(graphics, DOWN);
335
                }
336
22
                calcVBar(graphics);
337
22
                calcVMarker(graphics);
338
            }
339
340
22
            if (mHBarVisible)
341
            {
342
                if (mShowButtons)
343
                {
344
                    calcButton(graphics, LEFT);
345
                    calcButton(graphics, RIGHT);
346
                }
347
                calcHBar(graphics);
348
                calcHMarker(graphics);
349
            }
350
22
            graphics->finalize(mVertexes);
351
        }
352
22
        graphics->drawTileCollection(mVertexes);
353
    }
354
355
34
    updateAlpha();
356
357
34
    if (mRedraw)
358
    {
359
34
        const bool redraw = graphics->getRedraw();
360
34
        graphics->setRedraw(true);
361
34
        drawChildren(graphics);
362
34
        graphics->setRedraw(redraw);
363
    }
364
    else
365
    {
366
        drawChildren(graphics);
367
    }
368
34
    mRedraw = false;
369
    BLOCK_END("ScrollArea::draw")
370
34
}
371
372
void ScrollArea::safeDraw(Graphics *const graphics)
373
{
374
    BLOCK_START("ScrollArea::draw")
375
    if (mVBarVisible)
376
    {
377
        if (mShowButtons)
378
        {
379
            drawButton(graphics, UP);
380
            drawButton(graphics, DOWN);
381
        }
382
        drawVBar(graphics);
383
        drawVMarker(graphics);
384
    }
385
386
    if (mHBarVisible)
387
    {
388
        if (mShowButtons)
389
        {
390
            drawButton(graphics, LEFT);
391
            drawButton(graphics, RIGHT);
392
        }
393
        drawHBar(graphics);
394
        drawHMarker(graphics);
395
    }
396
397
    updateAlpha();
398
399
    safeDrawChildren(graphics);
400
    mRedraw = false;
401
    BLOCK_END("ScrollArea::draw")
402
}
403
404
32
void ScrollArea::updateCalcFlag(const Graphics *const graphics)
405
{
406
32
    if (!mRedraw)
407
    {
408
        // because we don't know where parent windows was moved,
409
        // need recalc vertexes
410
        const ClipRect &rect = graphics->getTopClip();
411
        if (rect.xOffset != mXOffset || rect.yOffset != mYOffset)
412
        {
413
            mRedraw = true;
414
            mXOffset = rect.xOffset;
415
            mYOffset = rect.yOffset;
416
        }
417
        else if (rect.width != mDrawWidth || rect.height != mDrawHeight)
418
        {
419
            mRedraw = true;
420
            mDrawWidth = rect.width;
421
            mDrawHeight = rect.height;
422
        }
423
        else if (graphics->getRedraw())
424
        {
425
            mRedraw = true;
426
        }
427
    }
428
32
}
429
430
32
void ScrollArea::drawFrame(Graphics *const graphics)
431
{
432
    BLOCK_START("ScrollArea::drawFrame")
433
32
    if (mOpaque == Opaque_true)
434
    {
435
32
        const int bs = mFrameSize * 2;
436
32
        const int w = mDimension.width + bs;
437
32
        const int h = mDimension.height + bs;
438
439
32
        updateCalcFlag(graphics);
440
441
32
        if (mRedraw)
442
        {
443
32
            mVertexes2->clear();
444
32
            graphics->calcWindow(mVertexes2,
445
                0, 0,
446
                w, h,
447
32
                background);
448
32
            graphics->finalize(mVertexes2);
449
        }
450
32
        graphics->drawTileCollection(mVertexes2);
451
    }
452
    BLOCK_END("ScrollArea::drawFrame")
453
32
}
454
455
void ScrollArea::safeDrawFrame(Graphics *const graphics)
456
{
457
    BLOCK_START("ScrollArea::drawFrame")
458
    if (mOpaque == Opaque_true)
459
    {
460
        const int bs = mFrameSize * 2;
461
        const int w = mDimension.width + bs;
462
        const int h = mDimension.height + bs;
463
464
        updateCalcFlag(graphics);
465
        graphics->drawImageRect(0, 0,
466
            w, h,
467
            background);
468
    }
469
    BLOCK_END("ScrollArea::drawFrame")
470
}
471
472
void ScrollArea::setOpaque(Opaque opaque)
473
{
474
    mOpaque = opaque;
475

652
    setFrameSize(mOpaque == Opaque_true ? 2 : 0);
476
}
477
478
Image *ScrollArea::getImageByState(Rect &dim, const BUTTON_DIR dir)
479
{
480
    int state = 0;
481
482
    switch (dir)
483
    {
484
        case UP:
485
            state = mUpButtonPressed ? 1 : 0;
486
            dim = getUpButtonDimension();
487
            break;
488
        case DOWN:
489
            state = mDownButtonPressed ? 1 : 0;
490
            dim = getDownButtonDimension();
491
            break;
492
        case LEFT:
493
            state = mLeftButtonPressed ? 1 : 0;
494
            dim = getLeftButtonDimension();
495
            break;
496
        case RIGHT:
497
            state = mRightButtonPressed ? 1 : 0;
498
            dim = getRightButtonDimension();
499
            break;
500
        case BUTTONS_DIR:
501
        default:
502
            logger->log("ScrollArea::drawButton unknown dir: "
503
                        + toString(CAST_U32(dir)));
504
            return nullptr;
505
    }
506
    return buttons[CAST_SIZE(dir)][state];
507
}
508
509
void ScrollArea::drawButton(Graphics *const graphics,
510
                            const BUTTON_DIR dir)
511
{
512
    Rect dim;
513
    const Image *const image = getImageByState(dim, dir);
514
515
    if (image != nullptr)
516
        graphics->drawImage(image, dim.x, dim.y);
517
}
518
519
void ScrollArea::calcButton(Graphics *const graphics,
520
                            const BUTTON_DIR dir)
521
{
522
    Rect dim;
523
    const Image *const image = getImageByState(dim, dir);
524
525
    if (image != nullptr)
526
    {
527
        static_cast<Graphics*>(graphics)->calcTileCollection(
528
            mVertexes, image, dim.x, dim.y);
529
    }
530
}
531
532
void ScrollArea::drawVBar(Graphics *const graphics) const
533
{
534
    const Rect &dim = getVerticalBarDimension();
535
536
    if (vBackground.grid[4] != nullptr)
537
    {
538
        graphics->drawPattern(vBackground.grid[4],
539
            dim.x, dim.y, dim.width, dim.height);
540
    }
541
    if (vBackground.grid[1] != nullptr)
542
    {
543
        graphics->drawPattern(vBackground.grid[1],
544
            dim.x, dim.y,
545
            dim.width, vBackground.grid[1]->getHeight());
546
    }
547
    if (vBackground.grid[7] != nullptr)
548
    {
549
        graphics->drawPattern(vBackground.grid[7],
550
            dim.x, dim.height - vBackground.grid[7]->getHeight() + dim.y,
551
            dim.width, vBackground.grid[7]->getHeight());
552
    }
553
}
554
555
22
void ScrollArea::calcVBar(const Graphics *const graphics)
556
{
557
44
    const Rect &dim = getVerticalBarDimension();
558
559
22
    if (vBackground.grid[4] != nullptr)
560
    {
561
22
        graphics->calcPattern(mVertexes,
562
            vBackground.grid[4],
563
22
            dim.x, dim.y,
564
22
            dim.width, dim.height);
565
    }
566
22
    if (vBackground.grid[1] != nullptr)
567
    {
568
44
        graphics->calcPattern(mVertexes,
569
            vBackground.grid[1],
570
22
            dim.x, dim.y,
571
44
            dim.width, vBackground.grid[1]->getHeight());
572
    }
573
22
    if (vBackground.grid[7] != nullptr)
574
    {
575
66
        graphics->calcPattern(mVertexes,
576
            vBackground.grid[7],
577
44
            dim.x, dim.height - vBackground.grid[7]->getHeight() + dim.y,
578
44
            dim.width, vBackground.grid[7]->getHeight());
579
    }
580
22
}
581
582
void ScrollArea::drawHBar(Graphics *const graphics) const
583
{
584
    const Rect &dim = getHorizontalBarDimension();
585
586
    if (hBackground.grid[4] != nullptr)
587
    {
588
        graphics->drawPattern(hBackground.grid[4],
589
            dim.x, dim.y,
590
            dim.width, dim.height);
591
    }
592
593
    if (hBackground.grid[3] != nullptr)
594
    {
595
        graphics->drawPattern(hBackground.grid[3],
596
            dim.x, dim.y,
597
            hBackground.grid[3]->getWidth(), dim.height);
598
    }
599
600
    if (hBackground.grid[5] != nullptr)
601
    {
602
        graphics->drawPattern(hBackground.grid[5],
603
            dim.x + dim.width - hBackground.grid[5]->getWidth(),
604
            dim.y,
605
            hBackground.grid[5]->getWidth(),
606
            dim.height);
607
    }
608
}
609
610
void ScrollArea::calcHBar(const Graphics *const graphics)
611
{
612
    const Rect &dim = getHorizontalBarDimension();
613
614
    if (hBackground.grid[4] != nullptr)
615
    {
616
        graphics->calcPattern(mVertexes,
617
            hBackground.grid[4],
618
            dim.x, dim.y,
619
            dim.width, dim.height);
620
    }
621
622
    if (hBackground.grid[3] != nullptr)
623
    {
624
        graphics->calcPattern(mVertexes,
625
            hBackground.grid[3],
626
            dim.x, dim.y,
627
            hBackground.grid[3]->getWidth(), dim.height);
628
    }
629
630
    if (hBackground.grid[5] != nullptr)
631
    {
632
        graphics->calcPattern(mVertexes,
633
            hBackground.grid[5],
634
            dim.x + dim.width - hBackground.grid[5]->getWidth(),
635
            dim.y,
636
            hBackground.grid[5]->getWidth(),
637
            dim.height);
638
    }
639
}
640
641
void ScrollArea::drawVMarker(Graphics *const graphics)
642
{
643
    const Rect &dim = getVerticalMarkerDimension();
644
645
    if ((mHasMouse) && (mX > (mDimension.width - mScrollbarWidth)))
646
    {
647
        graphics->drawImageRect(dim.x, dim.y,
648
            dim.width, dim.height,
649
            vMarkerHi);
650
    }
651
    else
652
    {
653
        graphics->drawImageRect(dim.x, dim.y,
654
            dim.width, dim.height,
655
            vMarker);
656
    }
657
}
658
659
22
void ScrollArea::calcVMarker(Graphics *const graphics)
660
{
661
44
    const Rect &dim = getVerticalMarkerDimension();
662
663

22
    if ((mHasMouse) && (mX > (mDimension.width - mScrollbarWidth)))
664
    {
665
        graphics->calcWindow(mVertexes,
666
            dim.x, dim.y,
667
            dim.width, dim.height,
668
            vMarkerHi);
669
    }
670
    else
671
    {
672
22
        graphics->calcWindow(mVertexes,
673
22
            dim.x, dim.y,
674
22
            dim.width, dim.height,
675
22
            vMarker);
676
    }
677
22
}
678
679
void ScrollArea::drawHMarker(Graphics *const graphics)
680
{
681
    const Rect dim = getHorizontalMarkerDimension();
682
683
    if ((mHasMouse) && (mY > (mDimension.height - mScrollbarWidth)))
684
    {
685
        graphics->drawImageRect(dim.x, dim.y,
686
            dim.width, dim.height,
687
            vMarkerHi);
688
    }
689
    else
690
    {
691
        graphics->drawImageRect(
692
            dim.x, dim.y,
693
            dim.width, dim.height,
694
            vMarker);
695
    }
696
}
697
698
void ScrollArea::calcHMarker(Graphics *const graphics)
699
{
700
    const Rect dim = getHorizontalMarkerDimension();
701
702
    if ((mHasMouse) && (mY > (mDimension.height - mScrollbarWidth)))
703
    {
704
        graphics->calcWindow(mVertexes,
705
            dim.x, dim.y,
706
            dim.width, dim.height,
707
            vMarkerHi);
708
    }
709
    else
710
    {
711
        graphics->calcWindow(mVertexes,
712
            dim.x, dim.y,
713
            dim.width, dim.height,
714
            vMarker);
715
    }
716
}
717
718
void ScrollArea::mouseMoved(MouseEvent& event)
719
{
720
    mX = event.getX();
721
    mY = event.getY();
722
}
723
724
void ScrollArea::mouseEntered(MouseEvent& event A_UNUSED)
725
{
726
    mHasMouse = true;
727
}
728
729
void ScrollArea::mouseExited(MouseEvent& event A_UNUSED)
730
{
731
    mHasMouse = false;
732
}
733
734
void ScrollArea::widgetResized(const Event &event A_UNUSED)
735
{
736
    mRedraw = true;
737
    const unsigned int frameSize = 2 * mFrameSize;
738
    Widget *const content = getContent();
739
    if (content != nullptr)
740
    {
741
        content->setSize(mDimension.width - frameSize,
742
            mDimension.height - frameSize);
743
    }
744
}
745
746
void ScrollArea::widgetMoved(const Event& event A_UNUSED)
747
{
748
    mRedraw = true;
749
}
750
751
void ScrollArea::mousePressed(MouseEvent& event)
752
{
753
    const int x = event.getX();
754
    const int y = event.getY();
755
756
    if (getUpButtonDimension().isPointInRect(x, y))
757
    {
758
        setVerticalScrollAmount(mVScroll
759
                                - mUpButtonScrollAmount);
760
        mUpButtonPressed = true;
761
        event.consume();
762
    }
763
    else if (getDownButtonDimension().isPointInRect(x, y))
764
    {
765
        setVerticalScrollAmount(mVScroll
766
                                + mDownButtonScrollAmount);
767
        mDownButtonPressed = true;
768
        event.consume();
769
    }
770
    else if (getLeftButtonDimension().isPointInRect(x, y))
771
    {
772
        setHorizontalScrollAmount(mHScroll
773
                                  - mLeftButtonScrollAmount);
774
        mLeftButtonPressed = true;
775
        event.consume();
776
    }
777
    else if (getRightButtonDimension().isPointInRect(x, y))
778
    {
779
        setHorizontalScrollAmount(mHScroll
780
                                  + mRightButtonScrollAmount);
781
        mRightButtonPressed = true;
782
        event.consume();
783
    }
784
    else if (getVerticalMarkerDimension().isPointInRect(x, y))
785
    {
786
        mIsHorizontalMarkerDragged = false;
787
        mIsVerticalMarkerDragged = true;
788
789
        mVerticalMarkerDragOffset = y - getVerticalMarkerDimension().y;
790
        event.consume();
791
    }
792
    else if (getVerticalBarDimension().isPointInRect(x, y))
793
    {
794
        if (y < getVerticalMarkerDimension().y)
795
        {
796
            setVerticalScrollAmount(mVScroll
797
                - CAST_S32(getChildrenArea().height * 0.1));
798
        }
799
        else
800
        {
801
            setVerticalScrollAmount(mVScroll
802
                + CAST_S32(getChildrenArea().height * 0.1));
803
        }
804
        event.consume();
805
    }
806
    else if (getHorizontalMarkerDimension().isPointInRect(x, y))
807
    {
808
        mIsHorizontalMarkerDragged = true;
809
        mIsVerticalMarkerDragged = false;
810
        mHorizontalMarkerDragOffset = x - getHorizontalMarkerDimension().x;
811
        event.consume();
812
    }
813
    else if (getHorizontalBarDimension().isPointInRect(x, y))
814
    {
815
        if (x < getHorizontalMarkerDimension().x)
816
        {
817
            setHorizontalScrollAmount(mHScroll
818
                - CAST_S32(getChildrenArea().width * 0.1));
819
        }
820
        else
821
        {
822
            setHorizontalScrollAmount(mHScroll
823
                + CAST_S32(getChildrenArea().width * 0.1));
824
        }
825
        event.consume();
826
    }
827
828
    if (event.getButton() == MouseButton::LEFT &&
829
        !event.isConsumed())
830
    {
831
        mClickX = event.getX();
832
        mClickY = event.getY();
833
    }
834
}
835
836
void ScrollArea::mouseReleased(MouseEvent& event)
837
{
838
    if (event.getButton() == MouseButton::LEFT &&
839
        mClickX != 0 &&
840
        mClickY != 0)
841
    {
842
        if (!event.isConsumed())
843
        {
844
#ifdef ANDROID
845
            int dx = mClickX - event.getX();
846
            int dy = mClickY - event.getY();
847
#else  // ANDROID
848
849
            int dx = event.getX() - mClickX;
850
            int dy = event.getY() - mClickY;
851
#endif  // ANDROID
852
853
            if ((dx < 20 && dx > 0) || (dx > -20 && dx < 0))
854
                dx = 0;
855
856
            if ((dy < 20 && dy > 0) || (dy > -20 && dy < 0))
857
                dy = 0;
858
859
            if (abs(dx) > abs(dy))
860
            {
861
                int s = mHScroll + dx;
862
                if (s < 0)
863
                {
864
                    s = 0;
865
                }
866
                else
867
                {
868
                    const int maxH = getHorizontalMaxScroll();
869
                    if (s > maxH)
870
                        s = maxH;
871
                }
872
873
                setHorizontalScrollAmount(s);
874
            }
875
            else if (dy != 0)
876
            {
877
                int s = mVScroll + dy;
878
                if (s < 0)
879
                {
880
                    s = 0;
881
                }
882
                else
883
                {
884
                    const int maxV = getVerticalMaxScroll();
885
                    if (s > maxV)
886
                        s = maxV;
887
                }
888
889
                setVerticalScrollAmount(s);
890
            }
891
            mClickX = 0;
892
            mClickY = 0;
893
            if (mMouseConsume && ((dx != 0) || (dy != 0)))
894
                event.consume();
895
        }
896
    }
897
    mUpButtonPressed = false;
898
    mDownButtonPressed = false;
899
    mLeftButtonPressed = false;
900
    mRightButtonPressed = false;
901
    mIsHorizontalMarkerDragged = false;
902
    mIsVerticalMarkerDragged = false;
903
    if (mMouseConsume)
904
        event.consume();
905
    mRedraw = true;
906
}
907
908
void ScrollArea::mouseDragged(MouseEvent &event)
909
{
910
    if (mIsVerticalMarkerDragged)
911
    {
912
        const Rect barDim = getVerticalBarDimension();
913
914
        const int pos = event.getY() - barDim.y
915
              - mVerticalMarkerDragOffset;
916
        const int length = getVerticalMarkerDimension().height;
917
918
        if ((barDim.height - length) > 0)
919
        {
920
            setVerticalScrollAmount((getVerticalMaxScroll() * pos)
921
                                      / (barDim.height - length));
922
        }
923
        else
924
        {
925
            setVerticalScrollAmount(0);
926
        }
927
    }
928
929
    if (mIsHorizontalMarkerDragged)
930
    {
931
        const Rect barDim = getHorizontalBarDimension();
932
933
        const int pos = event.getX() - barDim.x
934
            - mHorizontalMarkerDragOffset;
935
        const int length = getHorizontalMarkerDimension().width;
936
937
        if ((barDim.width - length) > 0)
938
        {
939
            setHorizontalScrollAmount((getHorizontalMaxScroll() * pos)
940
                                      / (barDim.width - length));
941
        }
942
        else
943
        {
944
            setHorizontalScrollAmount(0);
945
        }
946
    }
947
948
    event.consume();
949
    mRedraw = true;
950
}
951
952
22
Rect ScrollArea::getVerticalBarDimension() const
953
{
954
22
    if (!mVBarVisible)
955
        return Rect(0, 0, 0, 0);
956
957
22
    const int height = mShowButtons ? mScrollbarWidth : 0;
958
22
    if (mHBarVisible)
959
    {
960
        return Rect(mDimension.width - mScrollbarWidth,
961
            height,
962
            mScrollbarWidth,
963
            mDimension.height - 2 * height - mScrollbarWidth);
964
    }
965
966
22
    return Rect(mDimension.width - mScrollbarWidth,
967
        height,
968
22
        mScrollbarWidth,
969
44
        mDimension.height - 2 * height);
970
}
971
972
Rect ScrollArea::getHorizontalBarDimension() const
973
{
974
    if (!mHBarVisible)
975
        return Rect(0, 0, 0, 0);
976
977
    const int width = mShowButtons ? mScrollbarWidth : 0;
978
    if (mVBarVisible)
979
    {
980
        return Rect(width,
981
            mDimension.height - mScrollbarWidth,
982
            mDimension.width - 2 * width - mScrollbarWidth,
983
            mScrollbarWidth);
984
    }
985
986
    return Rect(width,
987
                      mDimension.height - mScrollbarWidth,
988
                      mDimension.width - 2 * width,
989
                      mScrollbarWidth);
990
}
991
992
22
Rect ScrollArea::getVerticalMarkerDimension()
993
{
994
22
    if (!mVBarVisible)
995
        return Rect(0, 0, 0, 0);
996
997
    int length, pos;
998
    int height;
999
22
    const int h2 = mShowButtons
1000
22
        ? mScrollbarWidth : mMarkerSize / 2;
1001
    const Widget *content;
1002
44
    if (!mWidgets.empty())
1003
44
        content = *mWidgets.begin();
1004
    else
1005
        content = nullptr;
1006
1007
22
    if (mHBarVisible)
1008
        height = mDimension.height - 2 * h2 - mScrollbarWidth;
1009
    else
1010
22
        height = mDimension.height - 2 * h2;
1011
1012
22
    const int maxV = getVerticalMaxScroll();
1013

22
    if ((mMarkerSize != 0) && (maxV != 0))
1014
    {
1015
        pos = (mVScroll * height / maxV - mMarkerSize / 2);
1016
        length = mMarkerSize;
1017
    }
1018
    else
1019
    {
1020
22
        if (content != nullptr)
1021
        {
1022
22
            const int h3 = content->getHeight();
1023
22
            if (h3 != 0)
1024
20
                length = (height * getChildrenArea().height) / h3;
1025
            else
1026
                length = height;
1027
        }
1028
        else
1029
        {
1030
            length = height;
1031
        }
1032
1033
22
        if (length < mScrollbarWidth)
1034
            length = mScrollbarWidth;
1035
1036
22
        if (length > height)
1037
20
            length = height;
1038
1039
22
        const int maxScroll = getVerticalMaxScroll();
1040
22
        if (maxScroll != 0)
1041
            pos = ((height - length) * mVScroll) / maxScroll;
1042
        else
1043
            pos = 0;
1044
    }
1045
1046
22
    return Rect(mDimension.width - mScrollbarWidth, h2 + pos,
1047
44
        mScrollbarWidth, length);
1048
}
1049
1050
Rect ScrollArea::getHorizontalMarkerDimension()
1051
{
1052
    if (!mHBarVisible)
1053
        return Rect(0, 0, 0, 0);
1054
1055
    int length, pos;
1056
    int width;
1057
    const int w2 = mShowButtons
1058
        ? mScrollbarWidth : mMarkerSize / 2;
1059
    const Widget *content;
1060
    if (!mWidgets.empty())
1061
        content = *mWidgets.begin();
1062
    else
1063
        content = nullptr;
1064
1065
    if (mVBarVisible)
1066
        width = mDimension.width - 2 * w2 - mScrollbarWidth;
1067
    else
1068
        width = mDimension.width - w2 - mScrollbarWidth;
1069
1070
    const int maxH = getHorizontalMaxScroll();
1071
    if ((mMarkerSize != 0) && (maxH != 0))
1072
    {
1073
        pos = (mHScroll * width / maxH - mMarkerSize / 2);
1074
        length = mMarkerSize;
1075
    }
1076
    else
1077
    {
1078
        if (content != nullptr)
1079
        {
1080
            const int w3 = content->getWidth();
1081
            if (w3 != 0)
1082
                length = (width * getChildrenArea().width) / w3;
1083
            else
1084
                length = width;
1085
        }
1086
        else
1087
        {
1088
            length = width;
1089
        }
1090
1091
        if (length < mScrollbarWidth)
1092
            length = mScrollbarWidth;
1093
1094
        if (length > width)
1095
            length = width;
1096
1097
        if (getHorizontalMaxScroll() != 0)
1098
        {
1099
            pos = ((width - length) * mHScroll)
1100
                / getHorizontalMaxScroll();
1101
        }
1102
        else
1103
        {
1104
            pos = 0;
1105
        }
1106
    }
1107
1108
    return Rect(w2 + pos, mDimension.height - mScrollbarWidth,
1109
        length, mScrollbarWidth);
1110
}
1111
1112
Rect ScrollArea::getUpButtonDimension() const
1113
{
1114
    if (!mVBarVisible || !mShowButtons)
1115
        return Rect(0, 0, 0, 0);
1116
1117
    return Rect(mDimension.width - mScrollbarWidth, 0,
1118
        mScrollbarWidth, mScrollbarWidth);
1119
}
1120
1121
Rect ScrollArea::getDownButtonDimension() const
1122
{
1123
    if (!mVBarVisible || !mShowButtons)
1124
        return Rect(0, 0, 0, 0);
1125
1126
    if (mHBarVisible)
1127
    {
1128
        return Rect(mDimension.width - mScrollbarWidth,
1129
            mDimension.height - mScrollbarWidth * 2,
1130
            mScrollbarWidth,
1131
            mScrollbarWidth);
1132
    }
1133
1134
    return Rect(mDimension.width - mScrollbarWidth,
1135
        mDimension.height - mScrollbarWidth,
1136
        mScrollbarWidth,
1137
        mScrollbarWidth);
1138
}
1139
1140
Rect ScrollArea::getLeftButtonDimension() const
1141
{
1142
    if (!mHBarVisible || !mShowButtons)
1143
        return Rect(0, 0, 0, 0);
1144
1145
    return Rect(0, mDimension.height - mScrollbarWidth,
1146
        mScrollbarWidth, mScrollbarWidth);
1147
}
1148
1149
Rect ScrollArea::getRightButtonDimension() const
1150
{
1151
    if (!mHBarVisible || !mShowButtons)
1152
        return Rect(0, 0, 0, 0);
1153
1154
    if (mVBarVisible)
1155
    {
1156
        return Rect(mDimension.width - mScrollbarWidth*2,
1157
            mDimension.height - mScrollbarWidth,
1158
            mScrollbarWidth,
1159
            mScrollbarWidth);
1160
    }
1161
1162
    return Rect(mDimension.width - mScrollbarWidth,
1163
        mDimension.height - mScrollbarWidth,
1164
        mScrollbarWidth,
1165
        mScrollbarWidth);
1166
}
1167
1168
328
void ScrollArea::setContent(Widget* widget)
1169
{
1170
328
    if (widget != nullptr)
1171
    {
1172
328
        clear();
1173
328
        add(widget);
1174
328
        widget->setPosition(0, 0);
1175
    }
1176
    else
1177
    {
1178
326
        clear();
1179
    }
1180
1181
654
    checkPolicies();
1182
328
}
1183
1184
4
Widget* ScrollArea::getContent()
1185
{
1186




9036
    if (!mWidgets.empty())
1187
8256
        return *mWidgets.begin();
1188
1189
    return nullptr;
1190
}
1191
1192
112
void ScrollArea::setHorizontalScrollPolicy(const ScrollPolicy hPolicy)
1193
{
1194
112
    mHPolicy = hPolicy;
1195
112
    checkPolicies();
1196
112
}
1197
1198
52
void ScrollArea::setVerticalScrollPolicy(const ScrollPolicy vPolicy)
1199
{
1200
52
    mVPolicy = vPolicy;
1201
52
    checkPolicies();
1202
52
}
1203
1204
8
void ScrollArea::setScrollPolicy(const ScrollPolicy hPolicy,
1205
                                 const ScrollPolicy vPolicy)
1206
{
1207
8
    mHPolicy = hPolicy;
1208
8
    mVPolicy = vPolicy;
1209
8
    checkPolicies();
1210
8
}
1211
1212
6
void ScrollArea::setVerticalScrollAmount(const int vScroll)
1213
{
1214
407
    const int max = getVerticalMaxScroll();
1215
1216
407
    mVScroll = vScroll;
1217
1218







407
    if (vScroll > max)
1219
10
        mVScroll = max;
1220
1221







407
    if (vScroll < 0)
1222
        mVScroll = 0;
1223
6
}
1224
1225
void ScrollArea::setHorizontalScrollAmount(int hScroll)
1226
{
1227
401
    const int max = getHorizontalMaxScroll();
1228
1229
401
    mHScroll = hScroll;
1230
1231






401
    if (hScroll > max)
1232
        mHScroll = max;
1233






401
    else if (hScroll < 0)
1234
        mHScroll = 0;
1235
}
1236
1237
2
void ScrollArea::setScrollAmount(const int hScroll, const int vScroll)
1238
{
1239
2
    setHorizontalScrollAmount(hScroll);
1240
2
    setVerticalScrollAmount(vScroll);
1241
2
}
1242
1243
401
int ScrollArea::getHorizontalMaxScroll()
1244
{
1245
401
    checkPolicies();
1246
1247
401
    const Widget *const content = getContent();
1248
401
    if (content == nullptr)
1249
        return 0;
1250
1251
1203
    const int value = content->getWidth() - getChildrenArea().width +
1252
802
        2 * content->getFrameSize();
1253
1254
401
    if (value < 0)
1255
        return 0;
1256
1257
2
    return value;
1258
}
1259
1260
453
int ScrollArea::getVerticalMaxScroll()
1261
{
1262
453
    checkPolicies();
1263
1264
453
    const Widget *const content = getContent();
1265
453
    if (content == nullptr)
1266
        return 0;
1267
1268
    int value;
1269
1270
906
    value = content->getHeight() - getChildrenArea().height +
1271
453
        2 * content->getFrameSize();
1272
1273
453
    if (value < 0)
1274
        return 0;
1275
1276
396
    return value;
1277
}
1278
1279
void ScrollArea::setScrollbarWidth(const int width)
1280
{
1281
    if (width > 0)
1282
        mScrollbarWidth = width;
1283
}
1284
1285
398
void ScrollArea::showWidgetPart(Widget *const widget, const Rect &area)
1286
{
1287
398
    const Widget *const content = getContent();
1288
398
    if (widget != content || (content == nullptr))
1289
        return;
1290
1291
398
    BasicContainer::showWidgetPart(widget, area);
1292
1293
1194
    setHorizontalScrollAmount(content->getFrameSize()
1294
398
        - content->getX());
1295
796
    setVerticalScrollAmount(content->getFrameSize()
1296
398
        - content->getY());
1297
}
1298
1299
1306
Rect ScrollArea::getChildrenArea()
1300
{
1301
    const Rect area = Rect(0, 0,
1302
3578
        mVBarVisible ? (getWidth() - mScrollbarWidth) : getWidth(),
1303

7836
        mHBarVisible ? (getHeight() - mScrollbarWidth) : getHeight());
1304
1305

1306
    if (area.width < 0 || area.height < 0)
1306
        return Rect();
1307
1308
    return area;
1309
}
1310
1311
Widget *ScrollArea::getWidgetAt(int x, int y)
1312
{
1313
    if (getChildrenArea().isPointInRect(x, y))
1314
        return getContent();
1315
1316
    return nullptr;
1317
}
1318
1319
624
void ScrollArea::setWidth(int width)
1320
{
1321
624
    Widget::setWidth(width);
1322
624
    checkPolicies();
1323
624
}
1324
1325
628
void ScrollArea::setHeight(int height)
1326
{
1327
628
    Widget::setHeight(height);
1328
628
    checkPolicies();
1329
628
}
1330
1331
2
void ScrollArea::setDimension(const Rect& dimension)
1332
{
1333
2
    Widget::setDimension(dimension);
1334
2
    checkPolicies();
1335
2
}
1336
1337
void ScrollArea::mouseWheelMovedUp(MouseEvent& event)
1338
{
1339
    if (event.isConsumed())
1340
        return;
1341
1342
    setVerticalScrollAmount(mVScroll
1343
        - getChildrenArea().height / 8);
1344
1345
    event.consume();
1346
}
1347
1348
void ScrollArea::mouseWheelMovedDown(MouseEvent& event)
1349
{
1350
    if (event.isConsumed())
1351
        return;
1352
1353
    setVerticalScrollAmount(mVScroll
1354
        + getChildrenArea().height / 8);
1355
1356
    event.consume();
1357
}
1358
1359
2935
void ScrollArea::checkPolicies()
1360
{
1361
5870
    const int w = getWidth();
1362
5870
    const int h = getHeight();
1363
1364
2935
    mHBarVisible = false;
1365
2935
    mVBarVisible = false;
1366
1367
2609
    const Widget *const content = getContent();
1368
2609
    if (content == nullptr)
1369
    {
1370
326
        mHBarVisible = (mHPolicy == SHOW_ALWAYS);
1371
326
        mVBarVisible = (mVPolicy == SHOW_ALWAYS);
1372
326
        return;
1373
    }
1374
1375
2609
    if (mHPolicy == SHOW_AUTO &&
1376
        mVPolicy == SHOW_AUTO)
1377
    {
1378
2254
        if (content->getWidth() <= w
1379
            && content->getHeight() <= h)
1380
        {
1381
            mHBarVisible = false;
1382
            mVBarVisible = false;
1383
        }
1384
1385
2254
        if (content->getWidth() > w)
1386
        {
1387
638
            mHBarVisible = true;
1388
        }
1389
1390
2254
        if ((content->getHeight() > h)
1391

2258
            || (mHBarVisible && content->getHeight()
1392
4
            > h - mScrollbarWidth))
1393
        {
1394
1906
            mVBarVisible = true;
1395
        }
1396
1397

4160
        if (mVBarVisible && content->getWidth() > w - mScrollbarWidth)
1398
1902
            mHBarVisible = true;
1399
1400
        return;
1401
    }
1402
1403
355
    switch (mHPolicy)
1404
    {
1405
        case SHOW_NEVER:
1406
            mHBarVisible = false;
1407
            break;
1408
1409
        case SHOW_ALWAYS:
1410
            mHBarVisible = true;
1411
            break;
1412
1413
16
        case SHOW_AUTO:
1414
16
            if (mVPolicy == SHOW_NEVER)
1415
            {
1416
4
                mHBarVisible = (content->getWidth() > w);
1417
            }
1418
            else  // (mVPolicy == SHOW_ALWAYS)
1419
            {
1420
24
                mHBarVisible = (content->getWidth()
1421
12
                    > w - mScrollbarWidth);
1422
            }
1423
            break;
1424
1425
        default:
1426
            break;
1427
    }
1428
1429
355
    switch (mVPolicy)
1430
    {
1431
        case SHOW_NEVER:
1432
            mVBarVisible = false;
1433
            break;
1434
1435
33
        case SHOW_ALWAYS:
1436
33
            mVBarVisible = true;
1437
33
            break;
1438
1439
308
        case SHOW_AUTO:
1440
308
            if (mHPolicy == SHOW_NEVER)
1441
            {
1442
308
                mVBarVisible = (content->getHeight() > h);
1443
            }
1444
            else  // (mHPolicy == SHOW_ALWAYS)
1445
            {
1446
                mVBarVisible = (content->getHeight()
1447
                    > h - mScrollbarWidth);
1448
            }
1449
            break;
1450
        default:
1451
            break;
1452
    }
1453
}
1454
1455
bool ScrollArea::isSelectable() const noexcept2
1456
{
1457
    if (mVBarVisible || mHBarVisible)
1458
        return true;
1459
    return Widget::isSelectable();
1460

6
}