GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/widgets/avatarlistbox.cpp Lines: 27 314 8.6 %
Date: 2017-11-29 Branches: 27 423 6.4 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2010  The Mana Developers
4
 *  Copyright (C) 2011-2017  The ManaPlus Developers
5
 *
6
 *  This file is part of The ManaPlus Client.
7
 *
8
 *  This program is free software; you can redistribute it and/or modify
9
 *  it under the terms of the GNU General Public License as published by
10
 *  the Free Software Foundation; either version 2 of the License, or
11
 *  any later version.
12
 *
13
 *  This program is distributed in the hope that it will be useful,
14
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 *  GNU General Public License for more details.
17
 *
18
 *  You should have received a copy of the GNU General Public License
19
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
 */
21
22
#include "gui/widgets/avatarlistbox.h"
23
24
#include "actormanager.h"
25
#include "configuration.h"
26
27
#include "being/localplayer.h"
28
29
#include "enums/resources/map/mapitemtype.h"
30
31
#include "gui/gui.h"
32
#include "gui/popupmanager.h"
33
#include "gui/skin.h"
34
#include "gui/viewport.h"
35
36
#include "gui/fonts/font.h"
37
38
#include "gui/popups/popupmenu.h"
39
40
#include "gui/models/avatarlistmodel.h"
41
42
#include "gui/windows/chatwindow.h"
43
44
#include "render/vertexes/imagecollection.h"
45
46
#include "resources/image/image.h"
47
48
#include "resources/map/map.h"
49
50
#include "debug.h"
51
52
int AvatarListBox::instances = 0;
53
Image *AvatarListBox::onlineIcon = nullptr;
54
Image *AvatarListBox::offlineIcon = nullptr;
55
56
10
AvatarListBox::AvatarListBox(const Widget2 *const widget,
57
10
                             AvatarListModel *const model) :
58
    ListBox(widget, model, "avatarlistbox.xml"),
59


40
    mImagePadding(mSkin != nullptr ? mSkin->getOption("imagePadding") : 0),
60

40
    mShowGender(config.getBoolValue("showgender")),
61


110
    mShowLevel(config.getBoolValue("showlevel"))
62
{
63
10
    instances++;
64
65
10
    if (instances == 1)
66
    {
67

14
        onlineIcon = Theme::getImageFromThemeXml("circle-on.xml", "");
68

14
        offlineIcon = Theme::getImageFromThemeXml("circle-off.xml", "");
69
    }
70
71
10
    setWidth(200);
72
73

40
    config.addListener("showgender", this);
74

40
    config.addListener("showlevel", this);
75
76
20
    mForegroundColor = getThemeColor(ThemeColorId::TEXT);
77
20
    mForegroundColor2 = getThemeColor(ThemeColorId::TEXT_OUTLINE);
78
10
}
79
80
40
AvatarListBox::~AvatarListBox()
81
{
82
10
    config.removeListeners(this);
83
    CHECKLISTENERS
84
85
10
    instances--;
86
87
10
    if (instances == 0)
88
    {
89
2
        if (onlineIcon != nullptr)
90
        {
91
2
            onlineIcon->decRef();
92
2
            onlineIcon = nullptr;
93
        }
94
2
        if (offlineIcon != nullptr)
95
        {
96
2
            offlineIcon->decRef();
97
2
            offlineIcon = nullptr;
98
        }
99
    }
100
20
}
101
102
void AvatarListBox::draw(Graphics *const graphics)
103
{
104
    BLOCK_START("AvatarListBox::draw")
105
    if ((mListModel == nullptr) || (localPlayer == nullptr))
106
    {
107
        BLOCK_END("AvatarListBox::draw")
108
        return;
109
    }
110
111
    const Widget *const parent = mParent;
112
    if (parent == nullptr)
113
        return;
114
115
    AvatarListModel *const model = static_cast<AvatarListModel *>(
116
        mListModel);
117
    updateAlpha();
118
119
    Font *const font = getFont();
120
    const int fontHeight = font->getHeight();
121
    const int parentWidth = parent->getWidth();
122
    const std::string &name = localPlayer->getName();
123
124
    // Draw the list elements
125
    ImageCollection vertexes;
126
    const int num = model->getNumberOfElements();
127
    for (int i = 0, y = 0;
128
         i < num;
129
         ++i, y += fontHeight)
130
    {
131
        const Avatar *const a = model->getAvatarAt(i);
132
        if (a == nullptr)
133
            continue;
134
135
        const MapItemType::Type type = static_cast<MapItemType::Type>(
136
            a->getType());
137
        if (type != MapItemType::SEPARATOR)
138
        {
139
            // Draw online status
140
            const Image *const icon = a->getOnline()
141
                ? onlineIcon : offlineIcon;
142
            if (icon != nullptr)
143
            {
144
                graphics->calcTileCollection(&vertexes, icon,
145
                    mImagePadding, y + mPadding);
146
            }
147
        }
148
    }
149
150
    graphics->finalize(&vertexes);
151
    graphics->drawTileCollection(&vertexes);
152
153
    for (int i = 0, y = 0;
154
         i < num;
155
         ++i, y += fontHeight)
156
    {
157
        const Avatar *const a = model->getAvatarAt(i);
158
        if (a == nullptr)
159
            continue;
160
161
        const MapItemType::Type type = static_cast<MapItemType::Type>(
162
            a->getType());
163
        std::string text;
164
165
        if (a->getMaxHp() > 0)
166
        {
167
            if (mShowLevel && a->getLevel() > 1)
168
            {
169
                text = strprintf("%s %d/%d (%d)", a->getComplexName().c_str(),
170
                                 a->getHp(), a->getMaxHp(), a->getLevel());
171
            }
172
            else
173
            {
174
                text = strprintf("%s %d/%d", a->getComplexName().c_str(),
175
                                 a->getHp(), a->getMaxHp());
176
            }
177
            const bool isPoison = a->getPoison();
178
            if (a->getMaxHp() != 0)
179
            {
180
                const ProgressColorIdT themeColor = (isPoison
181
                    ? ProgressColorId::PROG_HP_POISON
182
                    : ProgressColorId::PROG_HP);
183
                Color color = Theme::getProgressColor(
184
                    themeColor, static_cast<float>(a->getHp())
185
                    / static_cast<float>(a->getMaxHp()));
186
                color.a = 80;
187
                graphics->setColor(color);
188
                graphics->fillRectangle(Rect(mPadding, y + mPadding,
189
                    parentWidth * a->getHp() / a->getMaxHp()
190
                    - 2 * mPadding, fontHeight));
191
            }
192
        }
193
        else if (a->getDamageHp() != 0 && a->getName() != name)
194
        {
195
            if (mShowLevel && a->getLevel() > 1)
196
            {
197
                text = strprintf("%s -%d (%d)", a->getComplexName().c_str(),
198
                                 a->getDamageHp(), a->getLevel());
199
            }
200
            else
201
            {
202
                text = strprintf("%s -%d", a->getComplexName().c_str(),
203
                                 a->getDamageHp());
204
            }
205
206
            const ProgressColorIdT themeColor = (a->getPoison()
207
                ? ProgressColorId::PROG_HP_POISON : ProgressColorId::PROG_HP);
208
            Color color = Theme::getProgressColor(themeColor, 1);
209
            color.a = 80;
210
            graphics->setColor(color);
211
            graphics->fillRectangle(Rect(mPadding, y + mPadding,
212
                parentWidth * a->getDamageHp() / 1024
213
                - 2 * mPadding, fontHeight));
214
215
            if (a->getLevel() > 1)
216
            {
217
                graphics->setColor(mForegroundColor);
218
                int minHp = 40 + ((a->getLevel() - 1) * 5);
219
                if (minHp < 0)
220
                    minHp = 40;
221
222
                graphics->drawLine(parentWidth * minHp / 1024
223
                    + mPadding, y + mPadding,
224
                    parentWidth * minHp / 1024, y + fontHeight);
225
            }
226
        }
227
        else
228
        {
229
            if (mShowLevel && a->getLevel() > 1)
230
            {
231
                text = strprintf("%s (%d)", a->getComplexName().c_str(),
232
                                 a->getLevel());
233
            }
234
            else
235
            {
236
                text = a->getComplexName();
237
            }
238
        }
239
240
        if (!a->getMap().empty())
241
        {
242
            if (a->getX() != -1)
243
            {
244
                text.append(strprintf(" [%d,%d %s]", a->getX(), a->getY(),
245
                    a->getMap().c_str()));
246
            }
247
            else
248
            {
249
                text.append(strprintf(" [%s]", a->getMap().c_str()));
250
            }
251
        }
252
253
        if (graphics->getSecure())
254
        {
255
            if (mShowGender)
256
            {
257
                switch (a->getGender())
258
                {
259
                    case Gender::FEMALE:
260
                        text.append(" \u2640 ");
261
                        break;
262
                    case Gender::MALE:
263
                        text.append(" \u2642 ");
264
                        break;
265
                    default:
266
                    case Gender::UNSPECIFIED:
267
                    case Gender::OTHER:
268
                        break;
269
                }
270
            }
271
        }
272
        else
273
        {
274
            if (mShowGender)
275
            {
276
                switch (a->getGender())
277
                {
278
                    case Gender::FEMALE:
279
                        text.append(strprintf(" \u2640 %s",
280
                            a->getAdditionString().c_str()));
281
                        break;
282
                    case Gender::MALE:
283
                        text.append(strprintf(" \u2642 %s",
284
                            a->getAdditionString().c_str()));
285
                        break;
286
                    default:
287
                    case Gender::UNSPECIFIED:
288
                    case Gender::OTHER:
289
                        break;
290
                }
291
            }
292
            else
293
            {
294
                text.append(a->getAdditionString());
295
            }
296
        }
297
298
        // Draw Name
299
        if (a->getDisplayBold())
300
        {
301
            if (type == MapItemType::SEPARATOR)
302
            {
303
                boldFont->drawString(graphics,
304
                    mForegroundColor,
305
                    mForegroundColor,
306
                    text,
307
                    mImagePadding + mPadding,
308
                    y + mPadding);
309
            }
310
            else
311
            {
312
                boldFont->drawString(graphics,
313
                    mForegroundColor,
314
                    mForegroundColor,
315
                    text,
316
                    15 + mImagePadding + mPadding,
317
                    y + mPadding);
318
            }
319
        }
320
        else
321
        {
322
            if (type == MapItemType::SEPARATOR)
323
            {
324
                font->drawString(graphics,
325
                    mForegroundColor,
326
                    mForegroundColor,
327
                    text,
328
                    mImagePadding + mPadding,
329
                    y + mPadding);
330
            }
331
            else
332
            {
333
                font->drawString(graphics,
334
                    mForegroundColor,
335
                    mForegroundColor,
336
                    text,
337
                    15 + mImagePadding + mPadding,
338
                    y + mPadding);
339
            }
340
        }
341
    }
342
343
    setWidth(parentWidth - 10);
344
    BLOCK_END("AvatarListBox::draw")
345
}
346
347
void AvatarListBox::safeDraw(Graphics *const graphics)
348
{
349
    BLOCK_START("AvatarListBox::draw")
350
    if ((mListModel == nullptr) || (localPlayer == nullptr))
351
    {
352
        BLOCK_END("AvatarListBox::draw")
353
        return;
354
    }
355
356
    const Widget *const parent = mParent;
357
    if (parent == nullptr)
358
        return;
359
360
    AvatarListModel *const model = static_cast<AvatarListModel *>(
361
        mListModel);
362
    updateAlpha();
363
364
    Font *const font = getFont();
365
    const int fontHeight = font->getHeight();
366
    const int parentWidth = parent->getWidth();
367
    const std::string &name = localPlayer->getName();
368
369
    // Draw the list elements
370
    const int num = model->getNumberOfElements();
371
    for (int i = 0, y = 0;
372
         i < num;
373
         ++i, y += fontHeight)
374
    {
375
        const Avatar *const a = model->getAvatarAt(i);
376
        if (a == nullptr)
377
            continue;
378
379
        const MapItemType::Type type = static_cast<MapItemType::Type>(
380
            a->getType());
381
        if (type != MapItemType::SEPARATOR)
382
        {
383
            // Draw online status
384
            const Image *const icon = a->getOnline()
385
                ? onlineIcon : offlineIcon;
386
            if (icon != nullptr)
387
                graphics->drawImage(icon, mImagePadding, y + mPadding);
388
        }
389
    }
390
391
    for (int i = 0, y = 0;
392
         i < model->getNumberOfElements();
393
         ++i, y += fontHeight)
394
    {
395
        const Avatar *const a = model->getAvatarAt(i);
396
        if (a == nullptr)
397
            continue;
398
399
        const MapItemType::Type type = static_cast<MapItemType::Type>(
400
            a->getType());
401
        std::string text;
402
403
        if (a->getMaxHp() > 0)
404
        {
405
            if (mShowLevel && a->getLevel() > 1)
406
            {
407
                text = strprintf("%s %d/%d (%d)", a->getComplexName().c_str(),
408
                                 a->getHp(), a->getMaxHp(), a->getLevel());
409
            }
410
            else
411
            {
412
                text = strprintf("%s %d/%d", a->getComplexName().c_str(),
413
                                 a->getHp(), a->getMaxHp());
414
            }
415
            const bool isPoison = a->getPoison();
416
            if (a->getMaxHp() != 0)
417
            {
418
                const ProgressColorIdT themeColor = (isPoison
419
                    ? ProgressColorId::PROG_HP_POISON
420
                    : ProgressColorId::PROG_HP);
421
                Color color = Theme::getProgressColor(
422
                    themeColor, static_cast<float>(a->getHp())
423
                    / static_cast<float>(a->getMaxHp()));
424
                color.a = 80;
425
                graphics->setColor(color);
426
                graphics->fillRectangle(Rect(mPadding, y + mPadding,
427
                    parentWidth * a->getHp() / a->getMaxHp()
428
                    - 2 * mPadding, fontHeight));
429
            }
430
        }
431
        else if (a->getDamageHp() != 0 && a->getName() != name)
432
        {
433
            if (mShowLevel && a->getLevel() > 1)
434
            {
435
                text = strprintf("%s -%d (%d)", a->getComplexName().c_str(),
436
                                 a->getDamageHp(), a->getLevel());
437
            }
438
            else
439
            {
440
                text = strprintf("%s -%d", a->getComplexName().c_str(),
441
                                 a->getDamageHp());
442
            }
443
444
            const ProgressColorIdT themeColor = (a->getPoison()
445
                ? ProgressColorId::PROG_HP_POISON : ProgressColorId::PROG_HP);
446
            Color color = Theme::getProgressColor(themeColor, 1);
447
            color.a = 80;
448
            graphics->setColor(color);
449
            graphics->fillRectangle(Rect(mPadding, y + mPadding,
450
                parentWidth * a->getDamageHp() / 1024
451
                - 2 * mPadding, fontHeight));
452
453
            if (a->getLevel() > 1)
454
            {
455
                graphics->setColor(mForegroundColor);
456
                int minHp = 40 + ((a->getLevel() - 1) * 5);
457
                if (minHp < 0)
458
                    minHp = 40;
459
460
                graphics->drawLine(parentWidth * minHp / 1024
461
                    + mPadding, y + mPadding,
462
                    parentWidth * minHp / 1024, y + fontHeight);
463
            }
464
        }
465
        else
466
        {
467
            if (mShowLevel && a->getLevel() > 1)
468
            {
469
                text = strprintf("%s (%d)", a->getComplexName().c_str(),
470
                                 a->getLevel());
471
            }
472
            else
473
            {
474
                text = a->getComplexName();
475
            }
476
        }
477
478
        if (!a->getMap().empty())
479
        {
480
            if (a->getX() != -1)
481
            {
482
                text.append(strprintf(" [%d,%d %s]", a->getX(), a->getY(),
483
                    a->getMap().c_str()));
484
            }
485
            else
486
            {
487
                text.append(strprintf(" [%s]", a->getMap().c_str()));
488
            }
489
        }
490
491
        if (graphics->getSecure())
492
        {
493
            if (mShowGender)
494
            {
495
                switch (a->getGender())
496
                {
497
                    case Gender::FEMALE:
498
                        text.append(" \u2640 ");
499
                        break;
500
                    case Gender::MALE:
501
                        text.append(" \u2642 ");
502
                        break;
503
                    default:
504
                    case Gender::UNSPECIFIED:
505
                    case Gender::OTHER:
506
                        break;
507
                }
508
            }
509
        }
510
        else
511
        {
512
            if (mShowGender)
513
            {
514
                switch (a->getGender())
515
                {
516
                    case Gender::FEMALE:
517
                        text.append(strprintf(" \u2640 %s",
518
                            a->getAdditionString().c_str()));
519
                        break;
520
                    case Gender::MALE:
521
                        text.append(strprintf(" \u2642 %s",
522
                            a->getAdditionString().c_str()));
523
                        break;
524
                    default:
525
                    case Gender::UNSPECIFIED:
526
                    case Gender::OTHER:
527
                        break;
528
                }
529
            }
530
            else
531
            {
532
                text.append(a->getAdditionString());
533
            }
534
        }
535
536
        // Draw Name
537
        if (a->getDisplayBold())
538
        {
539
            if (type == MapItemType::SEPARATOR)
540
            {
541
                boldFont->drawString(graphics,
542
                    mForegroundColor,
543
                    mForegroundColor,
544
                    text,
545
                    mImagePadding + mPadding,
546
                    y + mPadding);
547
            }
548
            else
549
            {
550
                boldFont->drawString(graphics,
551
                    mForegroundColor,
552
                    mForegroundColor,
553
                    text,
554
                    15 + mImagePadding + mPadding,
555
                    y + mPadding);
556
            }
557
        }
558
        else
559
        {
560
            if (type == MapItemType::SEPARATOR)
561
            {
562
                font->drawString(graphics,
563
                    mForegroundColor,
564
                    mForegroundColor,
565
                    text,
566
                    mImagePadding + mPadding,
567
                    y + mPadding);
568
            }
569
            else
570
            {
571
                font->drawString(graphics,
572
                    mForegroundColor,
573
                    mForegroundColor,
574
                    text,
575
                    15 + mImagePadding + mPadding,
576
                    y + mPadding);
577
            }
578
        }
579
    }
580
581
    setWidth(parentWidth - 10);
582
    BLOCK_END("AvatarListBox::draw")
583
}
584
585
void AvatarListBox::mousePressed(MouseEvent &event)
586
{
587
    if (actorManager == nullptr ||
588
        localPlayer == nullptr ||
589
        popupManager == nullptr)
590
    {
591
        return;
592
    }
593
594
    const int height = getFont()->getHeight();
595
    if (height == 0)
596
        return;
597
598
    const int y = (event.getY() - mPadding) / height;
599
    if ((mListModel == nullptr) || y > mListModel->getNumberOfElements())
600
        return;
601
602
    setSelected(y);
603
    distributeActionEvent();
604
    const int selected = getSelected();
605
    AvatarListModel *const model = static_cast<AvatarListModel *>(
606
        mListModel);
607
    if (model == nullptr)
608
        return;
609
    const Avatar *ava = model->getAvatarAt(selected);
610
    if (ava == nullptr)
611
        return;
612
613
    const MapItemType::Type type = static_cast<MapItemType::Type>(
614
        ava->getType());
615
616
    event.consume();
617
    const MouseButtonT eventButton = event.getButton();
618
    if (eventButton == MouseButton::LEFT)
619
    {
620
#ifdef TMWA_SUPPORT
621
        if (type == MapItemType::EMPTY)
622
        {
623
            const Being *const being = actorManager->findBeingByName(
624
                ava->getName(), ActorType::Player);
625
            if (being != nullptr)
626
                actorManager->heal(being);
627
        }
628
        else
629
#endif  // TMWA_SUPPORT
630
        {
631
            localPlayer->navigateTo(ava->getX(), ava->getY());
632
        }
633
    }
634
    else if (eventButton == MouseButton::RIGHT)
635
    {
636
        switch (type)
637
        {
638
            case MapItemType::EMPTY:
639
            {
640
                const Avatar *const avatar = model->getAvatarAt(selected);
641
                if (avatar != nullptr)
642
                {
643
                    const Being *const being = actorManager->findBeingByName(
644
                        avatar->getName(), ActorType::Player);
645
                    if (being != nullptr)
646
                    {
647
                        popupMenu->showPopup(viewport->mMouseX,
648
                            viewport->mMouseY,
649
                            being);
650
                    }
651
                    else
652
                    {
653
                        popupMenu->showPlayerPopup(avatar->getName());
654
                    }
655
                }
656
                break;
657
            }
658
            case MapItemType::ATTACK:
659
            case MapItemType::PRIORITY:
660
            case MapItemType::IGNORE_:
661
            {
662
                std::string name;
663
                if (model->getAvatarAt(selected)->getLevel() == 0)
664
                    name.clear();
665
                else
666
                    name = model->getAvatarAt(selected)->getName();
667
668
                popupMenu->showAttackMonsterPopup(viewport->mMouseX,
669
                    viewport->mMouseY,
670
                    name,
671
                    model->getAvatarAt(selected)->getType());
672
                break;
673
            }
674
            case MapItemType::PICKUP:
675
            case MapItemType::NOPICKUP:
676
            {
677
                std::string name;
678
                if (model->getAvatarAt(selected)->getLevel() == 0)
679
                    name.clear();
680
                else
681
                    name = model->getAvatarAt(selected)->getName();
682
683
                popupMenu->showPickupItemPopup(viewport->mMouseX,
684
                    viewport->mMouseY,
685
                    name);
686
                break;
687
            }
688
            case MapItemType::HOME:
689
            case MapItemType::ROAD:
690
            case MapItemType::CROSS:
691
            case MapItemType::ARROW_UP:
692
            case MapItemType::ARROW_DOWN:
693
            case MapItemType::ARROW_LEFT:
694
            case MapItemType::ARROW_RIGHT:
695
            case MapItemType::PORTAL:
696
            case MapItemType::MUSIC:
697
            case MapItemType::SEPARATOR:
698
            {
699
                break;
700
            }
701
            default:
702
            {
703
                const Map *const map = viewport->getMap();
704
                ava = model->getAvatarAt(selected);
705
                if ((map != nullptr) && (ava != nullptr))
706
                {
707
                    MapItem *const mapItem = map->findPortalXY(
708
                        ava->getX(), ava->getY());
709
                    popupMenu->showPopup(viewport->mMouseX,
710
                        viewport->mMouseY,
711
                        mapItem);
712
                }
713
                break;
714
            }
715
        }
716
    }
717
    else if (eventButton == MouseButton::MIDDLE)
718
    {
719
        if (type == MapItemType::EMPTY && (chatWindow != nullptr))
720
        {
721
            const std::string &name = model->getAvatarAt(selected)->getName();
722
            const WhisperTab *const tab = chatWindow->addWhisperTab(
723
                name, name, true);
724
            if (tab != nullptr)
725
                chatWindow->saveState();
726
        }
727
    }
728
}
729
730
void AvatarListBox::mouseReleased(MouseEvent &event A_UNUSED)
731
{
732
}
733
734
void AvatarListBox::optionChanged(const std::string &value)
735
{
736
    if (value == "showgender")
737
        mShowGender = config.getBoolValue("showgender");
738
    else if (value == "showlevel")
739
        mShowLevel = config.getBoolValue("showlevel");
740
4
}