GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/widgets/itemshortcutcontainer.cpp Lines: 1 306 0.3 %
Date: 2021-03-17 Branches: 0 292 0.0 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2007-2009  The Mana World Development Team
4
 *  Copyright (C) 2009-2010  The Mana Developers
5
 *  Copyright (C) 2011-2019  The ManaPlus Developers
6
 *  Copyright (C) 2019-2021  Andrei Karas
7
 *
8
 *  This file is part of The ManaPlus Client.
9
 *
10
 *  This program is free software; you can redistribute it and/or modify
11
 *  it under the terms of the GNU General Public License as published by
12
 *  the Free Software Foundation; either version 2 of the License, or
13
 *  any later version.
14
 *
15
 *  This program is distributed in the hope that it will be useful,
16
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 *  GNU General Public License for more details.
19
 *
20
 *  You should have received a copy of the GNU General Public License
21
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
22
 */
23
24
#include "gui/widgets/itemshortcutcontainer.h"
25
26
#include "dragdrop.h"
27
#include "settings.h"
28
#include "spellmanager.h"
29
30
#include "being/playerinfo.h"
31
32
#include "input/inputmanager.h"
33
34
#include "gui/skin.h"
35
#include "gui/viewport.h"
36
37
#include "gui/fonts/font.h"
38
39
#include "gui/shortcut/itemshortcut.h"
40
#include "gui/shortcut/spellshortcut.h"
41
42
#include "gui/popups/itempopup.h"
43
#include "gui/popups/popupmenu.h"
44
#include "gui/popups/skillpopup.h"
45
#include "gui/popups/spellpopup.h"
46
47
#include "gui/windows/inventorywindow.h"
48
#include "gui/windows/skilldialog.h"
49
50
#include "input/inputactionoperators.h"
51
52
#include "const/resources/skill.h"
53
54
#include "utils/stringutils.h"
55
56
#include "debug.h"
57
58
ItemShortcutContainer::ItemShortcutContainer(Widget2 *const widget,
59
                                             const unsigned number) :
60
    ShortcutContainer(widget),
61
    mEquipedColor(getThemeColor(ThemeColorId::ITEM_EQUIPPED, 255U)),
62
    mEquipedColor2(getThemeColor(ThemeColorId::ITEM_EQUIPPED_OUTLINE, 255U)),
63
    mUnEquipedColor(getThemeColor(ThemeColorId::ITEM_NOT_EQUIPPED, 255U)),
64
    mUnEquipedColor2(getThemeColor(ThemeColorId::ITEM_NOT_EQUIPPED_OUTLINE,
65
        255U)),
66
    mNumber(number),
67
    mKeyOffsetX(2),
68
    mKeyOffsetY(2),
69
    mItemClicked(false)
70
{
71
    mMaxItems = ItemShortcut::getItemCount();
72
}
73
74
ItemShortcutContainer::~ItemShortcutContainer()
75
{
76
}
77
78
void ItemShortcutContainer::setSkin(const Widget2 *const widget,
79
                                    Skin *const skin)
80
{
81
    ShortcutContainer::setSkin(widget, skin);
82
    mEquipedColor = getThemeColor(ThemeColorId::ITEM_EQUIPPED, 255U);
83
    mEquipedColor2 = getThemeColor(ThemeColorId::ITEM_EQUIPPED_OUTLINE, 255U);
84
    mUnEquipedColor = getThemeColor(ThemeColorId::ITEM_NOT_EQUIPPED, 255U);
85
    mUnEquipedColor2 = getThemeColor(ThemeColorId::ITEM_NOT_EQUIPPED_OUTLINE,
86
        255U);
87
    mForegroundColor = getThemeColor(ThemeColorId::TEXT, 255U);
88
    mForegroundColor2 = getThemeColor(ThemeColorId::TEXT_OUTLINE, 255U);
89
    if (mSkin != nullptr)
90
    {
91
        mKeyOffsetX = mSkin->getOption("keyOffsetX", 2);
92
        mKeyOffsetY = mSkin->getOption("keyOffsetY", 2);
93
    }
94
}
95
96
void ItemShortcutContainer::draw(Graphics *const graphics)
97
{
98
    BLOCK_START("ItemShortcutContainer::draw")
99
    const ItemShortcut *const selShortcut = itemShortcut[mNumber];
100
    if (selShortcut == nullptr)
101
    {
102
        BLOCK_END("ItemShortcutContainer::draw")
103
        return;
104
    }
105
106
    if (settings.guiAlpha != mAlpha)
107
    {
108
        if (mBackgroundImg != nullptr)
109
            mBackgroundImg->setAlpha(mAlpha);
110
        mAlpha = settings.guiAlpha;
111
    }
112
113
    Font *const font = getFont();
114
    drawBackground(graphics);
115
116
    const Inventory *const inv = PlayerInfo::getInventory();
117
    if (inv == nullptr)
118
    {
119
        BLOCK_END("ItemShortcutContainer::draw")
120
        return;
121
    }
122
123
    // +++ for future usage need reorder drawing images before text or back
124
    for (unsigned i = 0; i < mMaxItems; i++)
125
    {
126
        const int itemX = (i % mGridWidth) * mBoxWidth;
127
        const int itemY = (i / mGridWidth) * mBoxHeight;
128
129
        // Draw item keyboard shortcut.
130
        const std::string key = inputManager.getKeyValueString(
131
            InputAction::SHORTCUT_1 + i);
132
        font->drawString(graphics,
133
            mForegroundColor,
134
            mForegroundColor,
135
            key,
136
            itemX + mKeyOffsetX,
137
            itemY + mKeyOffsetY);
138
139
        const int itemId = selShortcut->getItem(i);
140
        const ItemColor itemColor = selShortcut->getItemColor(i);
141
142
        if (itemId < 0)
143
            continue;
144
145
        // this is item
146
        if (itemId < SPELL_MIN_ID)
147
        {
148
            const Item *const item = inv->findItem(itemId, itemColor);
149
            if (item != nullptr)
150
            {
151
                // Draw item icon.
152
                Image *const image = item->getImage();
153
                if (image != nullptr)
154
                {
155
                    std::string caption;
156
                    if (item->getQuantity() > 1)
157
                        caption = toString(item->getQuantity());
158
                    else if (item->isEquipped() == Equipped_true)
159
                        caption = "Eq.";
160
161
                    image->setAlpha(1.0F);
162
                    graphics->drawImage(image,
163
                        itemX + mImageOffsetX,
164
                        itemY + mImageOffsetY);
165
                    if (item->isEquipped() == Equipped_true)
166
                    {
167
                        font->drawString(graphics,
168
                            mEquipedColor,
169
                            mEquipedColor2,
170
                            caption,
171
                            itemX + (mBoxWidth - font->getWidth(caption)) / 2,
172
                            itemY + mBoxHeight - 14);
173
                    }
174
                    else
175
                    {
176
                        font->drawString(graphics,
177
                            mUnEquipedColor,
178
                            mUnEquipedColor2,
179
                            caption,
180
                            itemX + (mBoxWidth - font->getWidth(caption)) / 2,
181
                            itemY + mBoxHeight - 14);
182
                    }
183
                }
184
            }
185
        }
186
        else if (itemId < SKILL_MIN_ID && (spellManager != nullptr))
187
        {   // this is magic shortcut
188
            const TextCommand *const spell = spellManager
189
                ->getSpellByItem(itemId);
190
            if (spell != nullptr)
191
            {
192
                if (!spell->isEmpty())
193
                {
194
                    Image *const image = spell->getImage();
195
196
                    if (image != nullptr)
197
                    {
198
                        image->setAlpha(1.0F);
199
                        graphics->drawImage(image,
200
                            itemX + mImageOffsetX,
201
                            itemY + mImageOffsetY);
202
                    }
203
                }
204
205
                font->drawString(graphics,
206
                    mForegroundColor,
207
                    mForegroundColor,
208
                    spell->getSymbol(),
209
                    itemX + mTextOffsetX,
210
                    itemY + mTextOffsetY);
211
            }
212
        }
213
        else if (skillDialog != nullptr)
214
        {
215
            const SkillInfo *const skill = skillDialog->getSkill(
216
                itemId - SKILL_MIN_ID);
217
            if (skill != nullptr)
218
            {
219
                Image *const image = skill->data->icon;
220
221
                if (image != nullptr)
222
                {
223
                    image->setAlpha(1.0F);
224
                    graphics->drawImage(image,
225
                        itemX + mImageOffsetX,
226
                        itemY + mImageOffsetY);
227
                }
228
                if (!skill->data->haveIcon)
229
                {
230
                    font->drawString(graphics,
231
                        mForegroundColor,
232
                        mForegroundColor,
233
                        skill->data->shortName,
234
                        itemX + mTextOffsetX,
235
                        itemY + mTextOffsetY);
236
                }
237
            }
238
        }
239
    }
240
    BLOCK_END("ItemShortcutContainer::draw")
241
}
242
243
void ItemShortcutContainer::safeDraw(Graphics *const graphics)
244
{
245
    BLOCK_START("ItemShortcutContainer::draw")
246
    const ItemShortcut *const selShortcut = itemShortcut[mNumber];
247
    if (selShortcut == nullptr)
248
    {
249
        BLOCK_END("ItemShortcutContainer::draw")
250
        return;
251
    }
252
253
    if (settings.guiAlpha != mAlpha)
254
    {
255
        if (mBackgroundImg != nullptr)
256
            mBackgroundImg->setAlpha(mAlpha);
257
        mAlpha = settings.guiAlpha;
258
    }
259
260
    Font *const font = getFont();
261
    safeDrawBackground(graphics);
262
263
    const Inventory *const inv = PlayerInfo::getInventory();
264
    if (inv == nullptr)
265
    {
266
        BLOCK_END("ItemShortcutContainer::draw")
267
        return;
268
    }
269
270
    // +++ for future usage need reorder drawing images before text or back
271
    for (unsigned i = 0; i < mMaxItems; i++)
272
    {
273
        const int itemX = (i % mGridWidth) * mBoxWidth;
274
        const int itemY = (i / mGridWidth) * mBoxHeight;
275
276
        // Draw item keyboard shortcut.
277
        const std::string key = inputManager.getKeyValueString(
278
            InputAction::SHORTCUT_1 + i);
279
        font->drawString(graphics,
280
            mForegroundColor,
281
            mForegroundColor,
282
            key,
283
            itemX + mKeyOffsetX,
284
            itemY + mKeyOffsetY);
285
286
        const int itemId = selShortcut->getItem(i);
287
        const ItemColor itemColor = selShortcut->getItemColor(i);
288
289
        if (itemId < 0)
290
            continue;
291
292
        // this is item
293
        if (itemId < SPELL_MIN_ID)
294
        {
295
            const Item *const item = inv->findItem(itemId, itemColor);
296
            if (item != nullptr)
297
            {
298
                // Draw item icon.
299
                Image *const image = item->getImage();
300
                if (image != nullptr)
301
                {
302
                    std::string caption;
303
                    if (item->getQuantity() > 1)
304
                        caption = toString(item->getQuantity());
305
                    else if (item->isEquipped() == Equipped_true)
306
                        caption = "Eq.";
307
308
                    image->setAlpha(1.0F);
309
                    graphics->drawImage(image,
310
                        itemX + mImageOffsetX,
311
                        itemY + mImageOffsetY);
312
                    if (item->isEquipped() == Equipped_true)
313
                    {
314
                        font->drawString(graphics,
315
                            mEquipedColor,
316
                            mEquipedColor2,
317
                            caption,
318
                            itemX + (mBoxWidth - font->getWidth(caption)) / 2,
319
                            itemY + mBoxHeight - 14);
320
                    }
321
                    else
322
                    {
323
                        font->drawString(graphics,
324
                            mUnEquipedColor,
325
                            mUnEquipedColor2,
326
                            caption,
327
                            itemX + (mBoxWidth - font->getWidth(caption)) / 2,
328
                            itemY + mBoxHeight - 14);
329
                    }
330
                }
331
            }
332
        }
333
        else if (itemId < SKILL_MIN_ID && (spellManager != nullptr))
334
        {   // this is magic shortcut
335
            const TextCommand *const spell = spellManager
336
                ->getSpellByItem(itemId);
337
            if (spell != nullptr)
338
            {
339
                if (!spell->isEmpty())
340
                {
341
                    Image *const image = spell->getImage();
342
343
                    if (image != nullptr)
344
                    {
345
                        image->setAlpha(1.0F);
346
                        graphics->drawImage(image,
347
                            itemX + mImageOffsetX,
348
                            itemY + mImageOffsetY);
349
                    }
350
                }
351
352
                font->drawString(graphics,
353
                    mForegroundColor,
354
                    mForegroundColor,
355
                    spell->getSymbol(),
356
                    itemX + mTextOffsetX,
357
                    itemY + mTextOffsetY);
358
            }
359
        }
360
        else if (skillDialog != nullptr)
361
        {
362
            const SkillInfo *const skill = skillDialog->getSkill(
363
                itemId - SKILL_MIN_ID);
364
            if (skill != nullptr)
365
            {
366
                Image *const image = skill->data->icon;
367
368
                if (image != nullptr)
369
                {
370
                    image->setAlpha(1.0F);
371
                    graphics->drawImage(image,
372
                        itemX + mImageOffsetX,
373
                        itemY + mImageOffsetY);
374
                }
375
                if (!skill->data->haveIcon)
376
                {
377
                    font->drawString(graphics,
378
                        mForegroundColor,
379
                        mForegroundColor,
380
                        skill->data->shortName,
381
                        itemX + mTextOffsetX,
382
                        itemY + mTextOffsetY);
383
                }
384
            }
385
        }
386
    }
387
    BLOCK_END("ItemShortcutContainer::draw")
388
}
389
390
void ItemShortcutContainer::mouseDragged(MouseEvent &event)
391
{
392
    if (mNumber == SHORTCUT_AUTO_TAB)
393
        return;
394
    ItemShortcut *const selShortcut = itemShortcut[mNumber];
395
    if (selShortcut == nullptr)
396
        return;
397
398
    if (event.getButton() == MouseButton::LEFT)
399
    {
400
        if (dragDrop.isEmpty() && mItemClicked)
401
        {
402
            mItemClicked = false;
403
404
            const int index = getIndexFromGrid(event.getX(), event.getY());
405
            if (index == -1)
406
                return;
407
408
            const int itemId = selShortcut->getItem(index);
409
            const ItemColor itemColor = selShortcut->getItemColor(index);
410
411
            if (itemId < 0)
412
                return;
413
414
            event.consume();
415
            if (itemId < SPELL_MIN_ID)
416
            {   // items
417
                if (PlayerInfo::getInventory() == nullptr)
418
                    return;
419
420
                const Item *const item = PlayerInfo::getInventory()->findItem(
421
                    itemId, itemColor);
422
423
                if (item != nullptr)
424
                {
425
                    selShortcut->removeItem(index);
426
                    dragDrop.dragItem(item, DragDropSource::Shortcuts, index);
427
                }
428
                else
429
                {
430
                    dragDrop.clear();
431
                }
432
            }
433
            else if (itemId < SKILL_MIN_ID)
434
            {   // spells/commands
435
                if (spellManager == nullptr)
436
                {
437
                    dragDrop.clear();
438
                    return;
439
                }
440
441
                const TextCommand *const spell = spellManager->getSpellByItem(
442
                    itemId);
443
                if (spell != nullptr)
444
                {
445
                    selShortcut->removeItem(index);
446
                    dragDrop.dragCommand(spell,
447
                        DragDropSource::Shortcuts, index);
448
                    dragDrop.setItem(itemId);
449
                }
450
                else
451
                {
452
                    dragDrop.clear();
453
                }
454
            }
455
            else
456
            {   // skills
457
                if (skillDialog == nullptr)
458
                {
459
                    dragDrop.clear();
460
                    return;
461
                }
462
                const SkillInfo *const skill
463
                    = skillDialog->getSkillByItem(itemId);
464
                if (skill != nullptr)
465
                {
466
                    const std::string itemData = selShortcut->getItemData(
467
                        index);
468
                    selShortcut->removeItem(index);
469
                    dragDrop.dragSkill(skill,
470
                        DragDropSource::Shortcuts,
471
                        index);
472
                    dragDrop.setItem(itemId);
473
                    dragDrop.setItemColor(itemColor);
474
                    dragDrop.setItemData(itemData);
475
                }
476
                else
477
                {
478
                    dragDrop.clear();
479
                }
480
            }
481
        }
482
    }
483
}
484
485
void ItemShortcutContainer::mousePressed(MouseEvent &event)
486
{
487
    ItemShortcut *const selShortcut = itemShortcut[mNumber];
488
    if (selShortcut == nullptr)
489
        return;
490
491
    const int index = getIndexFromGrid(event.getX(), event.getY());
492
493
    if (index == -1)
494
        return;
495
496
    if (event.getButton() == MouseButton::LEFT)
497
    {
498
        event.consume();
499
        // Stores the selected item if theirs one.
500
        if (selShortcut->isItemSelected() && (inventoryWindow != nullptr) &&
501
            (inventoryWindow->isWindowVisible()
502
            || selShortcut->getSelectedItem() >= SPELL_MIN_ID))
503
        {
504
            selShortcut->setItem(index);
505
            selShortcut->setItemSelected(-1);
506
            if (spellShortcut != nullptr)
507
                spellShortcut->setItemSelected(-1);
508
            inventoryWindow->unselectItem();
509
        }
510
        else if (selShortcut->getItem(index) != 0)
511
        {
512
            mItemClicked = true;
513
        }
514
    }
515
    else if (event.getButton() == MouseButton::RIGHT)
516
    {
517
        event.consume();
518
        if (popupMenu != nullptr &&
519
            viewport != nullptr)
520
        {
521
            popupMenu->showItemPopup(viewport->mMouseX,
522
                viewport->mMouseY,
523
                selShortcut->getItem(index),
524
                selShortcut->getItemColor(index));
525
        }
526
    }
527
}
528
529
void ItemShortcutContainer::mouseReleased(MouseEvent &event)
530
{
531
    ItemShortcut *const selShortcut = itemShortcut[mNumber];
532
    if (selShortcut == nullptr)
533
        return;
534
535
    if (event.getButton() == MouseButton::LEFT)
536
    {
537
        if (selShortcut->isItemSelected())
538
            selShortcut->setItemSelected(-1);
539
540
        const int index = getIndexFromGrid(event.getX(), event.getY());
541
        if (index == -1)
542
            return;
543
544
        if (dragDrop.isEmpty())
545
        {
546
            if ((selShortcut->getItem(index) != 0) && mItemClicked)
547
                selShortcut->useItem(index);
548
        }
549
        else
550
        {
551
            if (dragDrop.getSource() == DragDropSource::Shortcuts)
552
            {
553
                const int oldIndex = dragDrop.getTag();
554
                selShortcut->setItem(oldIndex, dragDrop.getItem(),
555
                    dragDrop.getItemColor());
556
                selShortcut->swap(oldIndex, index);
557
            }
558
            else
559
            {
560
                selShortcut->setItemData(index,
561
                    dragDrop.getItemData());
562
                selShortcut->setItem(index, dragDrop.getItem(),
563
                    dragDrop.getItemColor());
564
            }
565
            dragDrop.clear();
566
            dragDrop.deselect();
567
        }
568
569
        mItemClicked = false;
570
    }
571
}
572
573
void ItemShortcutContainer::mouseMoved(MouseEvent &event)
574
{
575
    const ItemShortcut *const selShortcut = itemShortcut[mNumber];
576
    if (selShortcut == nullptr)
577
        return;
578
579
    const int index = getIndexFromGrid(event.getX(), event.getY());
580
581
    if (index == -1)
582
        return;
583
584
    const int itemId = selShortcut->getItem(index);
585
    const ItemColor itemColor = selShortcut->getItemColor(index);
586
587
    if (itemId < 0)
588
        return;
589
590
    if (itemId < SPELL_MIN_ID)
591
    {
592
        skillPopup->setVisible(Visible_false);
593
        spellPopup->setVisible(Visible_false);
594
595
        Inventory *const inv = PlayerInfo::getInventory();
596
        if (inv == nullptr)
597
            return;
598
599
        const Item *const item = inv->findItem(itemId, itemColor);
600
        if ((item != nullptr) && (viewport != nullptr))
601
        {
602
            itemPopup->setItem(item, false);
603
            itemPopup->position(viewport->mMouseX, viewport->mMouseY);
604
        }
605
        else
606
        {
607
            itemPopup->setVisible(Visible_false);
608
        }
609
    }
610
    else if (itemId < SKILL_MIN_ID && (spellManager != nullptr))
611
    {
612
        skillPopup->setVisible(Visible_false);
613
        itemPopup->setVisible(Visible_false);
614
        const TextCommand *const spell = spellManager->getSpellByItem(itemId);
615
        if ((spell != nullptr) && (viewport != nullptr))
616
        {
617
            spellPopup->setItem(spell);
618
            spellPopup->view(viewport->mMouseX,
619
                viewport->mMouseY);
620
        }
621
        else
622
        {
623
            spellPopup->setVisible(Visible_false);
624
        }
625
    }
626
    else if (skillDialog != nullptr)
627
    {
628
        itemPopup->setVisible(Visible_false);
629
        spellPopup->setVisible(Visible_false);
630
        const SkillInfo *const skill = skillDialog->getSkillByItem(itemId);
631
        if (skill == nullptr)
632
            return;
633
634
        const std::string data = selShortcut->getItemData(index);
635
        CastTypeT castType = CastType::Default;
636
        int offsetX = 0;
637
        int offsetY = 0;
638
        if (!data.empty())
639
        {
640
            STD_VECTOR<int> vect;
641
            splitToIntVector(vect, data, ' ');
642
            const size_t sz = vect.size();
643
            if (sz > 0)
644
                castType = static_cast<CastTypeT>(vect[0]);
645
            if (sz > 2)
646
            {
647
                offsetX = vect[1];
648
                offsetY = vect[2];
649
            }
650
        }
651
        skillPopup->show(skill,
652
            toInt(itemColor, int),
653
            castType,
654
            offsetX,
655
            offsetY);
656
        skillPopup->position(viewport->mMouseX,
657
            viewport->mMouseY);
658
    }
659
}
660
661
// Hide ItemTooltip
662
void ItemShortcutContainer::mouseExited(MouseEvent &event A_UNUSED)
663
{
664
    if (itemPopup != nullptr)
665
        itemPopup->setVisible(Visible_false);
666
    if (spellPopup != nullptr)
667
        spellPopup->setVisible(Visible_false);
668
    if (skillPopup != nullptr)
669
        skillPopup->setVisible(Visible_false);
670
}
671
672
void ItemShortcutContainer::widgetHidden(const Event &event A_UNUSED)
673
{
674
    if (itemPopup != nullptr)
675
        itemPopup->setVisible(Visible_false);
676
    if (spellPopup != nullptr)
677
        spellPopup->setVisible(Visible_false);
678
2
}