GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/widgets/staticbrowserbox.cpp Lines: 197 379 52.0 %
Date: 2017-11-29 Branches: 123 333 36.9 %

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
 *  Copyright (C) 2009  Aethyra Development Team
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
#include "gui/widgets/staticbrowserbox.h"
25
26
#include "enums/gui/linkhighlightmode.h"
27
28
#include "gui/gui.h"
29
#include "gui/mouseoverlink.h"
30
#include "gui/skin.h"
31
32
#include "gui/fonts/font.h"
33
34
#include "gui/widgets/browserbox.inc"
35
#include "gui/widgets/linkhandler.h"
36
37
#include "render/graphics.h"
38
39
#include "resources/imageset.h"
40
41
#include "resources/image/image.h"
42
43
#include "resources/loaders/imageloader.h"
44
#include "resources/loaders/imagesetloader.h"
45
46
#include "utils/browserboxtools.h"
47
#include "utils/checkutils.h"
48
#include "utils/foreach.h"
49
#include "utils/stdmove.h"
50
#include "utils/stringutils.h"
51
#include "utils/translation/podict.h"
52
53
#include <algorithm>
54
55
#include "debug.h"
56
57
ImageSet *StaticBrowserBox::mEmotes = nullptr;
58
int StaticBrowserBox::mInstances = 0;
59
60
22
StaticBrowserBox::StaticBrowserBox(const Widget2 *const widget,
61
                                   const Opaque opaque,
62
22
                                   const std::string &skin) :
63
    Widget(widget),
64
    MouseListener(),
65
    mTextRows(),
66
    mTextRowLinksCount(),
67
    mLineParts(),
68
    mLinks(),
69
    mLinkHandler(nullptr),
70
    mSkin(nullptr),
71
    mHighlightMode(0),
72
    mSelectedLink(-1),
73
    mHeight(0),
74
    mWidth(0),
75
    mYStart(0),
76
    mPadding(0),
77
    mNewLinePadding(15U),
78
    mItemPadding(0),
79
44
    mHighlightColor(getThemeColor(ThemeColorId::HIGHLIGHT)),
80
44
    mHyperLinkColor(getThemeColor(ThemeColorId::HYPERLINK)),
81
    mOpaque(opaque),
82
    mUseLinksAndUserColors(true),
83
    mUseEmotes(true),
84
    mProcessVars(false),
85
    mEnableImages(false),
86
    mEnableKeys(false),
87
    mEnableTabs(false),
88
176
    mSeparator(false)
89
{
90
22
    mAllowLogic = false;
91
92
22
    setFocusable(true);
93
22
    addMouseListener(this);
94
95
44
    mBackgroundColor = getThemeColor(ThemeColorId::BACKGROUND);
96
97
22
    if (theme != nullptr)
98

110
        mSkin = theme->load(skin, "browserbox.xml");
99
22
    if (mInstances == 0)
100
    {
101

72
        mEmotes = Loader::getImageSet(
102
            "graphics/sprites/chatemotes.png", 17, 18);
103
    }
104
22
    mInstances ++;
105
106
22
    if (mSkin != nullptr)
107
    {
108
44
        mPadding = mSkin->getPadding();
109
22
        mNewLinePadding = CAST_U32(
110

88
            mSkin->getOption("newLinePadding", 15));
111

88
        mItemPadding = mSkin->getOption("itemPadding");
112

88
        if (mSkin->getOption("highlightBackground") != 0)
113
22
            mHighlightMode |= LinkHighlightMode::BACKGROUND;
114

88
        if (mSkin->getOption("highlightUnderline") != 0)
115
16
            mHighlightMode |= LinkHighlightMode::UNDERLINE;
116
    }
117
118
66
    readColor(BLACK);
119
66
    readColor(RED);
120
66
    readColor(GREEN);
121
66
    readColor(BLUE);
122
66
    readColor(ORANGE);
123
66
    readColor(YELLOW);
124
66
    readColor(PINK);
125
66
    readColor(PURPLE);
126
66
    readColor(GRAY);
127
66
    readColor(BROWN);
128
129
44
    mForegroundColor = getThemeColor(ThemeColorId::BROWSERBOX);
130
44
    mForegroundColor2 = getThemeColor(ThemeColorId::BROWSERBOX_OUTLINE);
131
22
}
132
133
132
StaticBrowserBox::~StaticBrowserBox()
134
{
135
22
    if (gui != nullptr)
136
22
        gui->removeDragged(this);
137
138
22
    if (theme != nullptr)
139
    {
140
22
        theme->unload(mSkin);
141
22
        mSkin = nullptr;
142
    }
143
144
22
    mInstances --;
145
22
    if (mInstances == 0)
146
    {
147
18
        if (mEmotes != nullptr)
148
        {
149
18
            mEmotes->decRef();
150
18
            mEmotes = nullptr;
151
        }
152
    }
153
44
}
154
155
20
void StaticBrowserBox::setLinkHandler(LinkHandler* linkHandler)
156
{
157
20
    mLinkHandler = linkHandler;
158
20
}
159
160
void StaticBrowserBox::addSeparator(const std::string &row)
161
{
162
    if (mSeparator)
163
        return;
164
    addRow(row, false);
165
    mSeparator = true;
166
}
167
168
8
void StaticBrowserBox::addRow(const std::string &row,
169
                              const bool atTop)
170
{
171
16
    std::string tmp = row;
172
16
    std::string newRow;
173
    size_t idx1;
174
8
    const Font *const font = getFont();
175
8
    int linksCount = 0;
176
177
16
    if (getWidth() < 0)
178
        return;
179
180
8
    mSeparator = false;
181
182
8
    if (mProcessVars)
183
    {
184
4
        BrowserBoxTools::replaceVars(tmp);
185
    }
186
187
    // Use links and user defined colors
188
8
    if (mUseLinksAndUserColors)
189
    {
190
16
        BrowserLink bLink;
191
192
        // Check for links in format "@@link|[email protected]@"
193
16
        const uint32_t sz = CAST_U32(mTextRows.size());
194
195
8
        if (mEnableKeys)
196
        {
197
            BrowserBoxTools::replaceKeys(tmp);
198
        }
199
200
8
        idx1 = tmp.find("@@");
201
12
        while (idx1 != std::string::npos)
202
        {
203
2
            const size_t idx2 = tmp.find('|', idx1);
204
2
            const size_t idx3 = tmp.find("@@", idx2);
205
206
2
            if (idx2 == std::string::npos || idx3 == std::string::npos)
207
                break;
208
4
            bLink.link = tmp.substr(idx1 + 2, idx2 - (idx1 + 2));
209
4
            bLink.caption = tmp.substr(idx2 + 1, idx3 - (idx2 + 1));
210
2
            bLink.y1 = CAST_S32(sz) * font->getHeight();
211
2
            bLink.y2 = bLink.y1 + font->getHeight();
212
2
            if (bLink.caption.empty())
213
            {
214
                bLink.caption = BrowserBoxTools::replaceLinkCommands(
215
                    bLink.link);
216
                if (translator != nullptr)
217
                    bLink.caption = translator->getStr(bLink.caption);
218
            }
219
220
6
            newRow.append(tmp.substr(0, idx1));
221
222
4
            std::string tmp2 = newRow;
223
2
            idx1 = tmp2.find("##");
224
2
            while (idx1 != std::string::npos)
225
            {
226
                tmp2.erase(idx1, 3);
227
                idx1 = tmp2.find("##");
228
            }
229
2
            bLink.x1 = font->getWidth(tmp2) - 1;
230
2
            bLink.x2 = bLink.x1 + font->getWidth(bLink.caption) + 1;
231
232
2
            if (atTop)
233
                mLinks.insert(mLinks.begin(), bLink);
234
            else
235
2
                mLinks.push_back(bLink);
236
2
            linksCount ++;
237
238
4
            newRow.append("##<").append(bLink.caption);
239
240
2
            tmp.erase(0, idx3 + 2);
241
2
            if (!tmp.empty())
242
                newRow.append("##>");
243
244
2
            idx1 = tmp.find("@@");
245
        }
246
247
8
        newRow.append(tmp);
248
    }
249
    // Don't use links and user defined colors
250
    else
251
    {
252
        newRow = row;
253
    }
254
255
8
    if (mEnableTabs)
256
    {
257
4
        BrowserBoxTools::replaceTabs(newRow);
258
    }
259
260
8
    if (atTop)
261
    {
262
        mTextRows.push_front(newRow);
263
        mTextRowLinksCount.push_front(linksCount);
264
    }
265
    else
266
    {
267
8
        mTextRows.push_back(newRow);
268
8
        mTextRowLinksCount.push_back(linksCount);
269
    }
270
271
24
    std::string plain = STD_MOVE(newRow);
272
    // workaround if used only one string started from bold
273
    // width for this string can be calculated wrong
274
    // this workaround fix width if string start from bold sign
275
8
    const bool startBold = (plain.find("##B") == 0);
276
12
    for (idx1 = plain.find("##");
277
12
         idx1 != std::string::npos;
278
         idx1 = plain.find("##"))
279
    {
280
4
        plain.erase(idx1, 3);
281
    }
282
283
    // Adjust the StaticBrowserBox size. This need only for implementing "---"
284

16
    const int w = startBold ?
285
16
        boldFont->getWidth(plain) : font->getWidth(plain) + 2 * mPadding;
286
16
    if (w > getWidth())
287
4
        setWidth(w);
288
}
289
290
2
void StaticBrowserBox::addRow(const std::string &cmd,
291
                              const char *const text)
292
{
293

12
    addRow(strprintf("@@%s|%[email protected]@", encodeLinkText(cmd).c_str(),
294

10
        encodeLinkText(text).c_str()));
295
2
}
296
297
void StaticBrowserBox::addImage(const std::string &path)
298
{
299
    if (!mEnableImages)
300
        return;
301
302
    mTextRows.push_back("~~~" + path);
303
    mTextRowLinksCount.push_back(0);
304
}
305
306
void StaticBrowserBox::clearRows()
307
{
308
    mTextRows.clear();
309
    mTextRowLinksCount.clear();
310
    mLinks.clear();
311
    setWidth(0);
312
    setHeight(0);
313
    mSelectedLink = -1;
314
}
315
316
void StaticBrowserBox::mousePressed(MouseEvent &event)
317
{
318
    if (mLinkHandler == nullptr)
319
        return;
320
321
    const LinkIterator i = std::find_if(mLinks.begin(), mLinks.end(),
322
        MouseOverLink(event.getX(), event.getY()));
323
324
    if (i != mLinks.end())
325
    {
326
        mLinkHandler->handleLink(i->link, &event);
327
        event.consume();
328
    }
329
}
330
331
void StaticBrowserBox::mouseMoved(MouseEvent &event)
332
{
333
    const LinkIterator i = std::find_if(mLinks.begin(), mLinks.end(),
334
        MouseOverLink(event.getX(), event.getY()));
335
336
    mSelectedLink = (i != mLinks.end())
337
        ? CAST_S32(i - mLinks.begin()) : -1;
338
}
339
340
void StaticBrowserBox::mouseExited(MouseEvent &event A_UNUSED)
341
{
342
    mSelectedLink = -1;
343
}
344
345
4
void StaticBrowserBox::draw(Graphics *const graphics)
346
{
347
    BLOCK_START("StaticBrowserBox::draw")
348
4
    const ClipRect &cr = graphics->getTopClip();
349
4
    mYStart = cr.y - cr.yOffset;
350
4
    const int yEnd = mYStart + cr.height;
351
4
    if (mYStart < 0)
352
        mYStart = 0;
353
354
4
    if (mDimension.width != mWidth)
355
    {
356
        updateHeight();
357
        reportAlways("browserbox resize in draw: %d, %d",
358
            mDimension.width,
359
            mWidth);
360
    }
361
362
4
    if (mOpaque == Opaque_true)
363
    {
364
        graphics->setColor(mBackgroundColor);
365
        graphics->fillRectangle(Rect(0, 0,
366
            mDimension.width, mDimension.height));
367
    }
368
369

4
    if (mSelectedLink >= 0 &&
370
        mSelectedLink < CAST_S32(mLinks.size()))
371
    {
372
        if ((mHighlightMode & LinkHighlightMode::BACKGROUND) != 0u)
373
        {
374
            BrowserLink &link = mLinks[CAST_SIZE(mSelectedLink)];
375
            graphics->setColor(mHighlightColor);
376
            graphics->fillRectangle(Rect(
377
                link.x1,
378
                link.y1,
379
                link.x2 - link.x1,
380
                link.y2 - link.y1));
381
        }
382
383
        if ((mHighlightMode & LinkHighlightMode::UNDERLINE) != 0u)
384
        {
385
            BrowserLink &link = mLinks[CAST_SIZE(mSelectedLink)];
386
            graphics->setColor(mHyperLinkColor);
387
            graphics->drawLine(
388
                link.x1,
389
                link.y2,
390
                link.x2,
391
                link.y2);
392
        }
393
    }
394
395
4
    Font *const font = getFont();
396
397
24
    FOR_EACH (LinePartCIter, i, mLineParts)
398
    {
399
2
        const LinePart &part = *i;
400
2
        if (part.mY + 50 < mYStart)
401
            continue;
402
2
        if (part.mY > yEnd)
403
            break;
404
        if (part.mType == 0u)
405
        {
406
            if (part.mBold)
407
            {
408
                boldFont->drawString(graphics,
409
                    part.mColor,
410
                    part.mColor2,
411
                    part.mText,
412
                    part.mX, part.mY);
413
            }
414
            else
415
            {
416
                font->drawString(graphics,
417
                    part.mColor,
418
                    part.mColor2,
419
                    part.mText,
420
                    part.mX, part.mY);
421
            }
422
        }
423
        else if (part.mImage != nullptr)
424
        {
425
            graphics->drawImage(part.mImage, part.mX, part.mY);
426
        }
427
    }
428
429
    BLOCK_END("StaticBrowserBox::draw")
430
4
}
431
432
void StaticBrowserBox::safeDraw(Graphics *const graphics)
433
{
434
    StaticBrowserBox::draw(graphics);
435
}
436
437
4
void StaticBrowserBox::updateHeight()
438
{
439
4
    unsigned int y = CAST_U32(mPadding);
440
4
    int moreHeight = 0;
441
4
    int link = 0;
442
4
    bool bold = false;
443
4
    const unsigned int wWidth = CAST_U32(mDimension.width - mPadding);
444
4
    const Font *const font = getFont();
445
4
    const int fontHeight = font->getHeight() + 2 * mItemPadding;
446

16
    const int fontWidthMinus = font->getWidth("-");
447
448
4
    Color selColor[2] = {mForegroundColor, mForegroundColor2};
449
4
    const Color textColor[2] = {mForegroundColor, mForegroundColor2};
450
8
    mLineParts.clear();
451
4
    uint32_t dataWidth = 0;
452
453
4
    if (mSeparator)
454
    {
455
        mSeparator = false;
456
        mTextRows.pop_back();
457
    }
458
459
20
    FOR_EACH (TextRowCIter, i, mTextRows)
460
    {
461
8
        unsigned int x = CAST_U32(mPadding);
462
24
        const std::string row = *(i);
463
8
        int objects = 0;
464
465
        // Check for separator lines
466
8
        if (row.find("---", 0) == 0)
467
        {
468
            const int dashWidth = fontWidthMinus;
469
            for (x = CAST_U32(mPadding); x < wWidth; x ++)
470
            {
471
                mLineParts.push_back(LinePart(CAST_S32(x),
472
                    CAST_S32(y) + mItemPadding,
473
                    selColor[0], selColor[1], "-", false));
474
                x += CAST_U32(CAST_S32(
475
                    dashWidth) - 2);
476
            }
477
478
            y += CAST_U32(fontHeight);
479
            continue;
480
        }
481

8
        else if (mEnableImages && row.find("~~~", 0) == 0)
482
        {
483
            std::string str = row.substr(3);
484
            const size_t sz = str.size();
485
            if (sz > 2 && str.substr(sz - 1) == "~")
486
                str = str.substr(0, sz - 1);
487
            Image *const img = Loader::getImage(str);
488
            if (img != nullptr)
489
            {
490
                img->incRef();
491
                mLineParts.push_back(LinePart(CAST_S32(x),
492
                    CAST_S32(y) + mItemPadding,
493
                    selColor[0], selColor[1], img));
494
                y += CAST_U32(img->getHeight() + 2);
495
                moreHeight += img->getHeight();
496
                if (img->getWidth() + mPadding + 2 > CAST_S32(dataWidth))
497
                    dataWidth = img->getWidth() + 2 + mPadding;
498
            }
499
            continue;
500
        }
501
502
        Color prevColor[2];
503
        prevColor[0] = selColor[0];
504
        prevColor[1] = selColor[1];
505
        bold = false;
506
507
6
        for (size_t start = 0, end = std::string::npos;
508
14
             start != std::string::npos;
509
6
             start = end, end = std::string::npos)
510
        {
511
8
            size_t idx1 = end;
512
8
            size_t idx2 = end;
513
514
            // "Tokenize" the string at control sequences
515
8
            if (mUseLinksAndUserColors)
516
16
                idx1 = row.find("##", start + 1);
517

8
            if (start == 0 || mUseLinksAndUserColors)
518
            {
519
                // Check for color change in format "##x", x = [L,P,0..9]
520

8
                if (row.find("##", start) == start && row.size() > start + 2)
521
                {
522
8
                    const signed char c = row.at(start + 2);
523
524
4
                    bool valid(false);
525
                    const Color col[2] =
526
                    {
527
8
                        getThemeCharColor(c, valid),
528
                        getThemeCharColor(CAST_S8(
529
8
                            c | 0x80), valid)
530
8
                    };
531
532
4
                    if (c == '>')
533
                    {
534
                        selColor[0] = prevColor[0];
535
                        selColor[1] = prevColor[1];
536
                    }
537
4
                    else if (c == '<')
538
                    {
539
                        prevColor[0] = selColor[0];
540
                        prevColor[1] = selColor[1];
541
                        selColor[0] = col[0];
542
                        selColor[1] = col[1];
543
                    }
544
2
                    else if (c == 'B')
545
                    {
546
                        bold = true;
547
                    }
548
                    else if (c == 'b')
549
                    {
550
                        bold = false;
551
                    }
552
                    else if (valid)
553
                    {
554
                        selColor[0] = col[0];
555
                        selColor[1] = col[1];
556
                    }
557
                    else
558
                    {
559
                        switch (c)
560
                        {
561
                            case '0':
562
                                selColor[0] = mColors[0][ColorName::BLACK];
563
                                selColor[1] = mColors[1][ColorName::BLACK];
564
                                break;
565
                            case '1':
566
                                selColor[0] = mColors[0][ColorName::RED];
567
                                selColor[1] = mColors[1][ColorName::RED];
568
                                break;
569
                            case '2':
570
                                selColor[0] = mColors[0][ColorName::GREEN];
571
                                selColor[1] = mColors[1][ColorName::GREEN];
572
                                break;
573
                            case '3':
574
                                selColor[0] = mColors[0][ColorName::BLUE];
575
                                selColor[1] = mColors[1][ColorName::BLUE];
576
                                break;
577
                            case '4':
578
                                selColor[0] = mColors[0][ColorName::ORANGE];
579
                                selColor[1] = mColors[1][ColorName::ORANGE];
580
                                break;
581
                            case '5':
582
                                selColor[0] = mColors[0][ColorName::YELLOW];
583
                                selColor[1] = mColors[1][ColorName::YELLOW];
584
                                break;
585
                            case '6':
586
                                selColor[0] = mColors[0][ColorName::PINK];
587
                                selColor[1] = mColors[1][ColorName::PINK];
588
                                break;
589
                            case '7':
590
                                selColor[0] = mColors[0][ColorName::PURPLE];
591
                                selColor[1] = mColors[1][ColorName::PURPLE];
592
                                break;
593
                            case '8':
594
                                selColor[0] = mColors[0][ColorName::GRAY];
595
                                selColor[1] = mColors[1][ColorName::GRAY];
596
                                break;
597
                            case '9':
598
                                selColor[0] = mColors[0][ColorName::BROWN];
599
                                selColor[1] = mColors[1][ColorName::BROWN];
600
                                break;
601
                            default:
602
                                selColor[0] = textColor[0];
603
                                selColor[1] = textColor[1];
604
                                break;
605
                        }
606
                    }
607
608

6
                    if (c == '<' && link < CAST_S32(mLinks.size()))
609
                    {
610
                        int size;
611
2
                        if (bold)
612
                        {
613
                            size = boldFont->getWidth(
614
                                mLinks[CAST_SIZE(link)].caption) + 1;
615
                        }
616
                        else
617
                        {
618
2
                            size = font->getWidth(
619
4
                                mLinks[CAST_SIZE(link)].caption) + 1;
620
                        }
621
622
                        BrowserLink &linkRef = mLinks[CAST_SIZE(
623
4
                            link)];
624
2
                        linkRef.x1 = CAST_S32(x);
625
2
                        linkRef.y1 = CAST_S32(y);
626
2
                        linkRef.x2 = linkRef.x1 + size;
627
2
                        linkRef.y2 = CAST_S32(y) + fontHeight - 1;
628
2
                        link++;
629
                    }
630
631
4
                    start += 3;
632
4
                    if (start == row.size())
633
                        break;
634
                }
635
            }
636
8
            if (mUseEmotes)
637
16
                idx2 = row.find("%%", start + 1);
638
8
            if (idx1 < idx2)
639
                end = idx1;
640
            else
641
8
                end = idx2;
642
8
            if (mUseEmotes)
643
            {
644
                // check for emote icons
645


22
                if (row.size() > start + 2 && row.substr(start, 2) == "%%")
646
                {
647
                    if (objects < 5)
648
                    {
649
                        const int cid = row.at(start + 2) - '0';
650
                        if (cid >= 0)
651
                        {
652
                            if (mEmotes != nullptr)
653
                            {
654
                                const size_t sz = mEmotes->size();
655
                                if (CAST_SIZE(cid) < sz)
656
                                {
657
                                    Image *const img = mEmotes->get(
658
                                        CAST_SIZE(cid));
659
                                    if (img != nullptr)
660
                                    {
661
                                        mLineParts.push_back(LinePart(
662
                                            CAST_S32(x),
663
                                            CAST_S32(y) + mItemPadding,
664
                                            selColor[0], selColor[1], img));
665
                                        x += 18;
666
                                    }
667
                                }
668
                            }
669
                        }
670
                        objects ++;
671
                    }
672
673
                    start += 3;
674
                    if (start == row.size())
675
                    {
676
                        if (x > dataWidth)
677
                            dataWidth = x;
678
                        break;
679
                    }
680
                }
681
            }
682
8
            const size_t len = (end == std::string::npos) ? end : end - start;
683
684
8
            if (start >= row.length())
685
                break;
686
687
12
            std::string part = row.substr(start, len);
688
689
48
            mLineParts.push_back(LinePart(CAST_S32(x),
690
6
                CAST_S32(y) + mItemPadding,
691
                selColor[0], selColor[1], part.c_str(), bold));
692
693
6
            int width = 0;
694
6
            if (bold)
695
2
                width = boldFont->getWidth(part);
696
            else
697
4
                width = font->getWidth(part);
698
699
6
            x += CAST_U32(width);
700
6
            if (x > dataWidth)
701
4
                dataWidth = x;
702
        }
703
8
        y += CAST_U32(fontHeight);
704
    }
705
4
    mWidth = dataWidth + mPadding;
706
12
    mHeight = CAST_S32(mTextRows.size())
707
4
        * fontHeight + moreHeight + 2 * mPadding;
708
4
    setSize(mWidth,
709
        mHeight);
710
4
}
711
712
std::string StaticBrowserBox::getTextAtPos(const int x,
713
                                           const int y) const
714
{
715
    int textX = 0;
716
    int textY = 0;
717
718
    getAbsolutePosition(textX, textY);
719
    if (x < textX || y < textY)
720
        return std::string();
721
722
    textY = y - textY;
723
    std::string str;
724
    int lastY = 0;
725
726
    FOR_EACH (LinePartCIter, i, mLineParts)
727
    {
728
        const LinePart &part = *i;
729
        if (part.mY + 50 < mYStart)
730
            continue;
731
        if (part.mY > textY)
732
            break;
733
734
        if (part.mY > lastY)
735
        {
736
            str = part.mText;
737
            lastY = part.mY;
738
        }
739
        else
740
        {
741
            str.append(part.mText);
742
        }
743
    }
744
745
    return str;
746
}
747
748
2
void StaticBrowserBox::setForegroundColorAll(const Color &color1,
749
                                             const Color &color2)
750
{
751
2
    mForegroundColor = color1;
752
2
    mForegroundColor2 = color2;
753
2
}
754
755
void StaticBrowserBox::moveSelectionUp()
756
{
757
    if (mSelectedLink <= 0)
758
        mSelectedLink = CAST_S32(mLinks.size()) - 1;
759
    else
760
        mSelectedLink --;
761
}
762
763
void StaticBrowserBox::moveSelectionDown()
764
{
765
    mSelectedLink ++;
766
    if (mSelectedLink >= static_cast<signed int>(mLinks.size()))
767
        mSelectedLink = 0;
768
}
769
770
void StaticBrowserBox::selectSelection()
771
{
772
    if ((mLinkHandler == nullptr) ||
773
        mSelectedLink < 0 ||
774
        mSelectedLink >= static_cast<signed int>(mLinks.size()))
775
    {
776
        return;
777
    }
778
779
    mLinkHandler->handleLink(mLinks[CAST_SIZE(mSelectedLink)].link,
780
        nullptr);
781
}