GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/popups/itempopup.cpp Lines: 50 234 21.4 %
Date: 2021-03-17 Branches: 37 297 12.5 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2008  The Legend of Mazzeroth Development Team
4
 *  Copyright (C) 2008-2009  The Mana World Development Team
5
 *  Copyright (C) 2009-2010  The Mana Developers
6
 *  Copyright (C) 2011-2019  The ManaPlus Developers
7
 *  Copyright (C) 2019-2021  Andrei Karas
8
 *
9
 *  This file is part of The ManaPlus Client.
10
 *
11
 *  This program is free software; you can redistribute it and/or modify
12
 *  it under the terms of the GNU General Public License as published by
13
 *  the Free Software Foundation; either version 2 of the License, or
14
 *  any later version.
15
 *
16
 *  This program is distributed in the hope that it will be useful,
17
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
 *  GNU General Public License for more details.
20
 *
21
 *  You should have received a copy of the GNU General Public License
22
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
23
 */
24
25
#include "gui/popups/itempopup.h"
26
27
#include "actormanager.h"
28
#include "configuration.h"
29
30
#include "gui/gui.h"
31
32
#include "gui/fonts/font.h"
33
34
#include "gui/widgets/icon.h"
35
#include "gui/widgets/label.h"
36
#include "gui/widgets/textbox.h"
37
38
#include "net/beinghandler.h"
39
#ifdef TMWA_SUPPORT
40
#include "net/net.h"
41
#endif  // TMWA_SUPPORT
42
43
#include "utils/foreach.h"
44
#include "utils/gettext.h"
45
#include "utils/stdmove.h"
46
47
#include "utils/translation/podict.h"
48
49
#include "resources/iteminfo.h"
50
51
#include "resources/db/itemoptiondb.h"
52
#include "resources/db/unitsdb.h"
53
54
#include "resources/image/image.h"
55
56
#include "resources/item/item.h"
57
#include "resources/item/itemfieldtype.h"
58
#include "resources/item/itemoptionslist.h"
59
60
#include "resources/loaders/imageloader.h"
61
62
#include "debug.h"
63
64
ItemPopup *itemPopup = nullptr;
65
66
2
ItemPopup::ItemPopup() :
67
    Popup("ItemPopup", "itempopup.xml"),
68

2
    mItemName(new Label(this)),
69

2
    mItemDesc(new TextBox(this)),
70

2
    mItemEffect(new TextBox(this)),
71

2
    mItemWeight(new TextBox(this)),
72

2
    mItemCards(new TextBox(this)),
73

2
    mItemOptions(new TextBox(this)),
74
    mItemType(ItemDbType::UNUSABLE),
75

2
    mIcon(new Icon(this, nullptr, AutoRelease_false)),
76
    mLastName(),
77
    mCardsStr(),
78
    mItemOptionsStr(),
79
    mLastId(0),
80

34
    mLastColor(ItemColor_one)
81
{
82
    // Item name
83
2
    mItemName->setFont(boldFont);
84
2
    mItemName->setPosition(0, 0);
85
86

2
    const int fontHeight = getFont()->getHeight();
87
88
    // Item description
89
2
    mItemDesc->setEditable(false);
90
2
    mItemDesc->setPosition(0, fontHeight);
91
6
    mItemDesc->setForegroundColorAll(
92
        getThemeColor(ThemeColorId::POPUP, 255U),
93
2
        getThemeColor(ThemeColorId::POPUP_OUTLINE, 255U));
94
95
    // Item effect
96
2
    mItemEffect->setEditable(false);
97
2
    mItemEffect->setPosition(0, 2 * fontHeight);
98
6
    mItemEffect->setForegroundColorAll(
99
        getThemeColor(ThemeColorId::POPUP, 255U),
100
2
        getThemeColor(ThemeColorId::POPUP_OUTLINE, 255U));
101
102
    // Item weight
103
2
    mItemWeight->setEditable(false);
104
2
    mItemWeight->setPosition(0, 3 * fontHeight);
105
6
    mItemWeight->setForegroundColorAll(
106
        getThemeColor(ThemeColorId::POPUP, 255U),
107
2
        getThemeColor(ThemeColorId::POPUP_OUTLINE, 255U));
108
109
    // Item cards
110
2
    mItemCards->setEditable(false);
111
2
    mItemCards->setPosition(0, 4 * fontHeight);
112
6
    mItemCards->setForegroundColorAll(
113
        getThemeColor(ThemeColorId::POPUP, 255U),
114
2
        getThemeColor(ThemeColorId::POPUP_OUTLINE, 255U));
115
116
    // Item options
117
2
    mItemOptions->setEditable(false);
118
2
    mItemOptions->setPosition(0, 5 * fontHeight);
119
6
    mItemOptions->setForegroundColorAll(
120
        getThemeColor(ThemeColorId::POPUP, 255U),
121
2
        getThemeColor(ThemeColorId::POPUP_OUTLINE, 255U));
122
2
}
123
124
2
void ItemPopup::postInit()
125
{
126
4
    Popup::postInit();
127
2
    add(mItemName);
128
2
    add(mItemDesc);
129
2
    add(mItemEffect);
130
2
    add(mItemWeight);
131
2
    add(mItemCards);
132
2
    add(mItemOptions);
133
2
    add(mIcon);
134
135
2
    addMouseListener(this);
136
2
}
137
138
12
ItemPopup::~ItemPopup()
139
{
140
2
    if (mIcon != nullptr)
141
    {
142
4
        Image *const image = mIcon->getImage();
143
2
        if (image != nullptr)
144
            image->decRef();
145
    }
146
4
}
147
148
void ItemPopup::setItem(const Item *const item,
149
                        const bool showImage)
150
{
151
    if (item == nullptr)
152
        return;
153
154
    const ItemInfo &ii = item->getInfo();
155
    setItem(ii,
156
        item->getColor(),
157
        showImage,
158
        item->getId(),
159
        item->getCards(),
160
        item->getOptions());
161
    if (item->getRefine() > 0)
162
    {
163
        mLastName = ii.getName();
164
        mLastColor = item->getColor();
165
        mLastId = item->getId();
166
#ifdef TMWA_SUPPORT
167
        if (Net::getNetworkType() == ServerType::TMWATHENA)
168
        {
169
            mItemName->setCaption(strprintf("%s (+%u), %d",
170
                ii.getName().c_str(),
171
                CAST_U32(item->getRefine()),
172
                ii.getId()));
173
        }
174
        else
175
#endif  // TMWA_SUPPORT
176
        {
177
            mItemName->setCaption(strprintf("%s (+%u), %d",
178
                ii.getName(item->getColor()).c_str(),
179
                CAST_U32(item->getRefine()),
180
                ii.getId()));
181
        }
182
        mItemName->adjustSize();
183
        const unsigned minWidth = mItemName->getWidth() + 8;
184
        if (CAST_U32(getWidth()) < minWidth)
185
            setWidth(minWidth);
186
    }
187
}
188
189
void ItemPopup::setItem(const ItemInfo &item,
190
                        const ItemColor color,
191
                        const bool showImage,
192
                        int id,
193
                        const int *const cards,
194
                        const ItemOptionsList *const options)
195
{
196
    if (mIcon == nullptr)
197
        return;
198
199
    std::string cardsStr;
200
    std::string optionsStr;
201
202
    if (item.getName() == mLastName &&
203
        color == mLastColor &&
204
        id == mLastId)
205
    {
206
        cardsStr = getCardsString(cards);
207
        optionsStr = getOptionsString(options);
208
        if (mItemOptionsStr == optionsStr &&
209
            mCardsStr == cardsStr)
210
        {
211
            return;
212
        }
213
    }
214
    else
215
    {
216
        cardsStr = getCardsString(cards);
217
        optionsStr = getOptionsString(options);
218
    }
219
    mItemOptionsStr = STD_MOVE(optionsStr);
220
    mCardsStr = STD_MOVE(cardsStr);
221
222
    if (id == -1)
223
        id = item.getId();
224
225
    int space = 0;
226
227
    Image *const oldImage = mIcon->getImage();
228
    if (oldImage != nullptr)
229
        oldImage->decRef();
230
231
    if (showImage)
232
    {
233
        Image *const image = Loader::getImage(combineDye2(
234
            pathJoin(paths.getStringValue("itemIcons"),
235
            item.getDisplay().image),
236
            item.getDyeIconColorsString(color)));
237
238
        mIcon->setImage(image);
239
        if (image != nullptr)
240
        {
241
            mIcon->setPosition(0, 0);
242
            space = mIcon->getWidth();
243
        }
244
    }
245
    else
246
    {
247
        mIcon->setImage(nullptr);
248
    }
249
250
    mItemType = item.getType();
251
252
    mLastName = item.getName();
253
    mLastColor = color;
254
    mLastId = id;
255
256
#ifdef TMWA_SUPPORT
257
    if (Net::getNetworkType() == ServerType::TMWATHENA)
258
    {
259
        mItemName->setCaption(strprintf("%s, %d",
260
            item.getName().c_str(), id));
261
        mItemDesc->setTextWrapped(item.getDescription(), 196);
262
    }
263
    else
264
#endif  // TMWA_SUPPORT
265
    {
266
        mItemName->setCaption(strprintf("%s, %d",
267
            item.getName(color).c_str(), id));
268
        mItemDesc->setTextWrapped(item.getDescription(color), 196);
269
    }
270
271
    mItemName->adjustSize();
272
    setLabelColor(mItemName, mItemType);
273
    mItemName->setPosition(space, 0);
274
275
    mItemEffect->setTextWrapped(item.getEffect(), 196);
276
    // TRANSLATORS: popup label
277
    mItemWeight->setTextWrapped(strprintf(_("Weight: %s"),
278
        UnitsDb::formatWeight(item.getWeight()).c_str()), 196);
279
    mItemCards->setTextWrapped(mCardsStr, 196);
280
    mItemOptions->setTextWrapped(mItemOptionsStr, 196);
281
282
    int minWidth = mItemName->getWidth() + space;
283
284
    if (mItemName->getWidth() + space > minWidth)
285
        minWidth = mItemName->getWidth() + space;
286
    if (mItemDesc->getMinWidth() > minWidth)
287
        minWidth = mItemDesc->getMinWidth();
288
    if (mItemEffect->getMinWidth() > minWidth)
289
        minWidth = mItemEffect->getMinWidth();
290
    if (mItemWeight->getMinWidth() > minWidth)
291
        minWidth = mItemWeight->getMinWidth();
292
    if (mItemCards->getMinWidth() > minWidth)
293
        minWidth = mItemCards->getMinWidth();
294
    if (mItemOptions->getMinWidth() > minWidth)
295
        minWidth = mItemOptions->getMinWidth();
296
297
    const int numRowsDesc = mItemDesc->getNumberOfRows();
298
    const int numRowsWeight = mItemWeight->getNumberOfRows();
299
    const int numRowsCards = mItemCards->getNumberOfRows();
300
    const int numRowsOptions = mItemOptions->getNumberOfRows();
301
    const int height = getFont()->getHeight();
302
303
    if (item.getEffect().empty())
304
    {
305
        setContentSize(minWidth,
306
            (numRowsDesc + 2 + numRowsWeight + numRowsCards + numRowsOptions) *
307
            height);
308
        mItemWeight->setPosition(0, (numRowsDesc + 2) * height);
309
        mItemCards->setPosition(0, (numRowsDesc + numRowsWeight + 2) * height);
310
        mItemOptions->setPosition(0,
311
            (numRowsDesc + numRowsWeight + numRowsCards + 2) * height);
312
    }
313
    else
314
    {
315
        const int numRowsEffect = mItemEffect->getNumberOfRows();
316
        setContentSize(minWidth, (numRowsDesc + numRowsEffect + 2
317
            + numRowsWeight + numRowsCards + numRowsOptions) * height);
318
        mItemEffect->setPosition(0, (numRowsDesc + 2) * height);
319
        mItemWeight->setPosition(0, (numRowsDesc + numRowsEffect + 2)
320
            * height);
321
        mItemCards->setPosition(0, (numRowsDesc + numRowsEffect
322
            + numRowsWeight + 2) * height);
323
        mItemOptions->setPosition(0, (numRowsDesc + numRowsEffect
324
            + numRowsWeight + numRowsCards + 2) * height);
325
    }
326
327
    mItemDesc->setPosition(0, 2 * height);
328
}
329
330
std::string ItemPopup::getCardsString(const int *const cards)
331
{
332
    if (cards == nullptr)
333
        return std::string();
334
335
    std::string label;
336
337
    switch (cards[0])
338
    {
339
        case CARD0_CREATE:  // named item
340
        {
341
            const int32_t charId = cards[2] + 65536 * cards[3];
342
            std::string name = actorManager->findCharById(charId);
343
            if (name.empty())
344
            {
345
                name = toString(charId);
346
                beingHandler->requestNameByCharId(charId);
347
                mLastId = 0;  // allow recreate popup with same data
348
            }
349
            // TRANSLATORS: named item description
350
            label.append(strprintf(_("Item named: %s"), name.c_str()));
351
            return label;
352
        }
353
        case CARD0_FORGE:  // forged item
354
        case CARD0_PET:
355
        {
356
            return label;
357
        }
358
        default:
359
        {
360
            for (int f = 0; f < maxCards; f ++)
361
            {
362
                const int id = cards[f];
363
                if (id != 0)
364
                {
365
                    if (!label.empty())
366
                        label.append(" / ");
367
                    const ItemInfo &info = ItemDB::get(id);
368
                    label.append(info.getName());
369
                }
370
            }
371
            if (label.empty())
372
                return label;
373
            // TRANSLATORS: popup label
374
            return _("Cards: ") + label;
375
        }
376
    }
377
}
378
379
std::string ItemPopup::getOptionsString(const ItemOptionsList *const options)
380
{
381
    if (options == nullptr || translator == nullptr)
382
        return std::string();
383
    const size_t sz = options->size();
384
    std::string effect;
385
    for (size_t f = 0; f < sz; f ++)
386
    {
387
        const ItemOption &option = options->get(f);
388
        if (option.index == 0)
389
            continue;
390
        const STD_VECTOR<ItemFieldType*> &fields = ItemOptionDb::getFields(
391
            option.index);
392
        if (fields.empty())
393
            continue;
394
        const std::string valueStr = toString(option.value);
395
        FOR_EACH (STD_VECTOR<ItemFieldType*>::const_iterator, it, fields)
396
        {
397
            const ItemFieldType *const field = *it;
398
            std::string value = valueStr;
399
            if (!effect.empty())
400
                effect.append(" / ");
401
            if (field->sign && value[0] != '-')
402
                value = std::string("+").append(value);
403
            const std::string format = translator->getStr(field->description);
404
            effect.append(strprintf(format.c_str(),
405
                value.c_str()));
406
        }
407
    }
408
    if (effect.empty())
409
        return effect;
410
    // TRANSLATORS: popup label
411
    return _("Options: ") + effect;
412
}
413
414
#define caseSetColor(name1, name2) \
415
    case name1: \
416
    { \
417
        return label->setForegroundColorAll( \
418
        getThemeColor(name2, 255U), \
419
        getThemeColor(name2##_OUTLINE, 255U)); \
420
    }
421
void ItemPopup::setLabelColor(Label *label,
422
                              const ItemDbTypeT type) const
423
{
424
    switch (type)
425
    {
426
        caseSetColor(ItemDbType::UNUSABLE, ThemeColorId::GENERIC)
427
        caseSetColor(ItemDbType::USABLE, ThemeColorId::USABLE)
428
        caseSetColor(ItemDbType::EQUIPMENT_ONE_HAND_WEAPON,
429
            ThemeColorId::ONEHAND)
430
        caseSetColor(ItemDbType::EQUIPMENT_TWO_HANDS_WEAPON,
431
            ThemeColorId::TWOHAND)
432
        caseSetColor(ItemDbType::EQUIPMENT_TORSO, ThemeColorId::TORSO)
433
        caseSetColor(ItemDbType::EQUIPMENT_ARMS, ThemeColorId::ARMS)
434
        caseSetColor(ItemDbType::EQUIPMENT_HEAD, ThemeColorId::HEAD)
435
        caseSetColor(ItemDbType::EQUIPMENT_LEGS, ThemeColorId::LEGS)
436
        caseSetColor(ItemDbType::EQUIPMENT_SHIELD, ThemeColorId::SHIELD)
437
        caseSetColor(ItemDbType::EQUIPMENT_RING, ThemeColorId::RING)
438
        caseSetColor(ItemDbType::EQUIPMENT_NECKLACE, ThemeColorId::NECKLACE)
439
        caseSetColor(ItemDbType::EQUIPMENT_FEET, ThemeColorId::FEET)
440
        caseSetColor(ItemDbType::EQUIPMENT_AMMO, ThemeColorId::AMMO)
441
        caseSetColor(ItemDbType::EQUIPMENT_CHARM, ThemeColorId::CHARM)
442
        caseSetColor(ItemDbType::SPRITE_RACE, ThemeColorId::UNKNOWN_ITEM)
443
        caseSetColor(ItemDbType::SPRITE_HAIR, ThemeColorId::UNKNOWN_ITEM)
444
        caseSetColor(ItemDbType::CARD, ThemeColorId::CARD)
445
        default:
446
        {
447
            return label->setForegroundColorAll(
448
                getThemeColor(ThemeColorId::UNKNOWN_ITEM, 255U),
449
                getThemeColor(ThemeColorId::UNKNOWN_ITEM_OUTLINE, 255U));
450
        }
451
    }
452
}
453
#undef caseSetColor
454
455
void ItemPopup::mouseMoved(MouseEvent &event)
456
{
457
    Popup::mouseMoved(event);
458
459
    // When the mouse moved on top of the popup, hide it
460
    setVisible(Visible_false);
461
    resetPopup();
462
}
463
464
void ItemPopup::resetPopup()
465
{
466
    mLastName.clear();
467
    mLastColor = ItemColor_one;
468
    mLastId = 0;
469
2
}