GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/windows/inventorywindow.cpp Lines: 180 499 36.1 %
Date: 2021-03-17 Branches: 160 743 21.5 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2004-2009  The Mana World Development Team
4
 *  Copyright (C) 2009-2010  The Mana Developers
5
 *  Copyright (C) 2011-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/windows/inventorywindow.h"
25
26
#include "configuration.h"
27
28
#include "being/playerinfo.h"
29
30
#include "const/sound.h"
31
32
#include "enums/gui/layouttype.h"
33
34
#include "input/inputmanager.h"
35
36
#include "gui/gui.h"
37
38
#include "gui/fonts/font.h"
39
40
#include "gui/models/sortlistmodelinv.h"
41
42
#include "gui/popups/itempopup.h"
43
#include "gui/popups/popupmenu.h"
44
#include "gui/popups/textpopup.h"
45
46
#include "gui/windows/confirmdialog.h"
47
#include "gui/windows/itemamountwindow.h"
48
#include "gui/windows/npcdialog.h"
49
#include "gui/windows/setupwindow.h"
50
#include "gui/windows/tradewindow.h"
51
52
#include "gui/widgets/button.h"
53
#include "gui/widgets/createwidget.h"
54
#include "gui/widgets/containerplacer.h"
55
#include "gui/widgets/dropdown.h"
56
#include "gui/widgets/itemcontainer.h"
57
#include "gui/widgets/layout.h"
58
#include "gui/widgets/progressbar.h"
59
#include "gui/widgets/scrollarea.h"
60
#include "gui/widgets/tabstrip.h"
61
#include "gui/widgets/textfield.h"
62
#include "gui/widgets/windowcontainer.h"
63
64
#include "listeners/insertcardlistener.h"
65
66
#include "net/npchandler.h"
67
68
#include "resources/iteminfo.h"
69
70
#include "resources/db/unitsdb.h"
71
72
#include "resources/item/item.h"
73
74
#include "utils/delete2.h"
75
#include "utils/foreach.h"
76
77
#include "debug.h"
78
79
InventoryWindow *inventoryWindow = nullptr;
80
InventoryWindow *storageWindow = nullptr;
81
InventoryWindow *cartWindow = nullptr;
82
1
InventoryWindow::WindowList InventoryWindow::invInstances;
83
1
InsertCardListener insertCardListener;
84
85
1
InventoryWindow::InventoryWindow(Inventory *const inventory) :
86
    Window("Inventory", Modal_false, nullptr, "inventory.xml"),
87
    ActionListener(),
88
    KeyListener(),
89
    SelectionListener(),
90
    InventoryListener(),
91
    AttributeListener(),
92
    mInventory(inventory),
93
    mItems(new ItemContainer(this, mInventory, 100000,
94

1
        ShowEmptyRows_false, ForceQuantity_false)),
95
    mUseButton(nullptr),
96
    mDropButton(nullptr),
97
    mOutfitButton(nullptr),
98
    mShopButton(nullptr),
99
    mCartButton(nullptr),
100
    mEquipmentButton(nullptr),
101
    mStoreButton(nullptr),
102
    mRetrieveButton(nullptr),
103
    mInvCloseButton(nullptr),
104
    mWeightBar(nullptr),
105
    mSlotsBar(new ProgressBar(this, 0.0F, 100, 0,
106
        ProgressColorId::PROG_INVY_SLOTS,
107

1
        "slotsprogressbar.xml", "slotsprogressbar_fill.xml")),
108
    mFilter(nullptr),
109
2
    mSortModel(new SortListModelInv),
110
1
    mSortDropDown(new DropDown(this, mSortModel, false,
111

2
        Modal_false, this, "sort")),
112
    mNameFilter(new TextField(this, "", LoseFocusOnTab_true,
113

1
        this, "namefilter", true)),
114
    mSortDropDownCell(nullptr),
115
    mNameFilterCell(nullptr),
116
    mFilterCell(nullptr),
117
    mSlotsBarCell(nullptr),
118
    mSplit(false),
119




31
    mCompactMode(false)
120
{
121
3
    mSlotsBar->setColor(getThemeColor(ThemeColorId::SLOTS_BAR, 255U),
122
1
        getThemeColor(ThemeColorId::SLOTS_BAR_OUTLINE, 255U));
123
124
1
    if (inventory != nullptr)
125
    {
126

7
        setCaption(gettext(inventory->getName().c_str()));
127
3
        setWindowName(inventory->getName());
128
1
        switch (inventory->getType())
129
        {
130
            case InventoryType::Inventory:
131
            case InventoryType::Trade:
132
            case InventoryType::Npc:
133
            case InventoryType::Vending:
134
            case InventoryType::MailEdit:
135
            case InventoryType::MailView:
136
            case InventoryType::Craft:
137
            case InventoryType::TypeEnd:
138
            default:
139

3
                mSortDropDown->setSelected(config.getIntValue(
140
1
                    "inventorySortOrder"));
141
1
                break;
142
            case InventoryType::Storage:
143
                mSortDropDown->setSelected(config.getIntValue(
144
                    "storageSortOrder"));
145
                break;
146
            case InventoryType::Cart:
147
                mSortDropDown->setSelected(config.getIntValue(
148
                    "cartSortOrder"));
149
                break;
150
        }
151
    }
152
    else
153
    {
154
        // TRANSLATORS: inventory window name
155
        setCaption(_("Inventory"));
156
        setWindowName("Inventory");
157
        mSortDropDown->setSelected(0);
158
    }
159
160

2
    if ((setupWindow != nullptr) &&
161

1
        (inventory != nullptr) &&
162
        inventory->getType() != InventoryType::Storage)
163
    {
164
        setupWindow->registerWindowForReset(this);
165
    }
166
167
1
    setResizable(true);
168
1
    setCloseButton(true);
169
2
    setSaveVisible(true);
170
1
    setStickyButtonLock(true);
171
172
1
    if (mainGraphics->mWidth > 600)
173
1
        setDefaultSize(450, 310, ImagePosition::CENTER, 0, 0);
174
    else
175
        setDefaultSize(387, 307, ImagePosition::CENTER, 0, 0);
176
1
    setMinWidth(310);
177
1
    setMinHeight(179);
178
1
    addKeyListener(this);
179
180
2
    mItems->addSelectionListener(this);
181
182

4
    const int size = config.getIntValue("fontSize");
183

3
    mFilter = new TabStrip(this, "filter_" + getWindowName(), size + 16, 0);
184
1
    mFilter->addActionListener(this);
185
5
    mFilter->setActionEventId("tag_");
186
2
    mFilter->setSelectable(false);
187
188

2
    StringVect tags = ItemDB::getTags();
189
2
    const size_t sz = tags.size();
190
1
    for (size_t f = 0; f < sz; f ++)
191
        mFilter->addButton(tags[f], tags[f], false);
192
193
1
    if (mInventory == nullptr)
194
    {
195
        invInstances.push_back(this);
196
        return;
197
    }
198
199
1
    ScrollArea *const invenScroll = new ScrollArea(this, mItems,
200

4
        fromBool(getOptionBool("showbackground", false), Opaque),
201

5
        "inventory_background.xml");
202
1
    invenScroll->setHorizontalScrollPolicy(ScrollArea::SHOW_NEVER);
203
204

2
    switch (mInventory->getType())
205
    {
206
        case InventoryType::Inventory:
207
        {
208
            // TRANSLATORS: inventory button
209
4
            const std::string equip = _("Equip");
210
            // TRANSLATORS: inventory button
211
4
            const std::string use = _("Use");
212
            // TRANSLATORS: inventory button
213
4
            const std::string unequip = _("Unequip");
214
215

1
            std::string longestUseString = getFont()->getWidth(equip) >
216

3
                getFont()->getWidth(use) ? equip : use;
217
218

2
            if (getFont()->getWidth(longestUseString) <
219

1
                getFont()->getWidth(unequip))
220
            {
221
                longestUseString = unequip;
222
            }
223
224
1
            mUseButton = new Button(this,
225
                longestUseString,
226
                "use",
227
                BUTTON_SKIN,
228

4
                this);
229
1
            mDropButton = new Button(this,
230
                // TRANSLATORS: inventory button
231
1
                _("Drop..."),
232
                "drop",
233
                BUTTON_SKIN,
234


7
                this);
235
1
            mOutfitButton = new Button(this,
236
                // TRANSLATORS: inventory outfits button
237
1
                _("O"),
238
                "outfit",
239
                BUTTON_SKIN,
240


7
                this);
241
1
            mCartButton = new Button(this,
242
                // TRANSLATORS: inventory cart button
243
1
                _("C"),
244
                "cart",
245
                BUTTON_SKIN,
246


7
                this);
247
1
            mShopButton = new Button(this,
248
                // TRANSLATORS: inventory shop button
249
1
                _("S"),
250
                "shop",
251
                BUTTON_SKIN,
252


7
                this);
253
1
            mEquipmentButton = new Button(this,
254
                // TRANSLATORS: inventory equipment button
255
1
                _("E"),
256
                "equipment",
257
                BUTTON_SKIN,
258


7
                this);
259
1
            mWeightBar = new ProgressBar(this, 0.0F, 100, 0,
260
                ProgressColorId::PROG_WEIGHT,
261


7
                "weightprogressbar.xml", "weightprogressbar_fill.xml");
262
3
            mWeightBar->setColor(getThemeColor(ThemeColorId::WEIGHT_BAR, 255U),
263
1
                getThemeColor(ThemeColorId::WEIGHT_BAR_OUTLINE, 255U));
264
265
            // TRANSLATORS: outfits button tooltip
266
5
            mOutfitButton->setDescription(_("Outfits"));
267
            // TRANSLATORS: cart button tooltip
268
5
            mCartButton->setDescription(_("Cart"));
269
            // TRANSLATORS: shop button tooltip
270
5
            mShopButton->setDescription(_("Shop"));
271
            // TRANSLATORS: equipment button tooltip
272
5
            mEquipmentButton->setDescription(_("Equipment"));
273
274
1
            place(0, 0, mWeightBar, 4, 1);
275
1
            mSlotsBarCell = &place(4, 0, mSlotsBar, 4, 1);
276

1
            mSortDropDownCell = &place(8, 0, mSortDropDown, 3, 1);
277
278
2
            mFilterCell = &place(0, 1, mFilter, 10, 1).setPadding(3);
279
1
            mNameFilterCell = &place(8, 1, mNameFilter, 3, 1);
280
281
2
            place(0, 2, invenScroll, 11, 1).setPadding(3);
282
1
            place(0, 3, mUseButton, 1, 1);
283
1
            place(1, 3, mDropButton, 1, 1);
284
1
            ContainerPlacer placer = getPlacer(10, 3);
285
1
            placer(0, 0, mShopButton, 1, 1);
286
1
            placer(1, 0, mOutfitButton, 1, 1);
287
1
            placer(2, 0, mCartButton, 1, 1);
288
1
            placer(3, 0, mEquipmentButton, 1, 1);
289
290
1
            updateWeight();
291
            break;
292
        }
293
294
        case InventoryType::Storage:
295
        {
296
            mStoreButton = new Button(this,
297
                // TRANSLATORS: storage button
298
                _("Store"),
299
                "store",
300
                BUTTON_SKIN,
301
                this);
302
            mRetrieveButton = new Button(this,
303
                // TRANSLATORS: storage button
304
                _("Retrieve"),
305
                "retrieve",
306
                BUTTON_SKIN,
307
                this);
308
            mInvCloseButton = new Button(this,
309
                // TRANSLATORS: storage button
310
                _("Close"),
311
                "close",
312
                BUTTON_SKIN,
313
                this);
314
315
            mSlotsBarCell = &place(0, 0, mSlotsBar, 6, 1);
316
            mSortDropDownCell = &place(6, 0, mSortDropDown, 1, 1);
317
318
            mFilterCell = &place(0, 1, mFilter, 7, 1).setPadding(3);
319
            mNameFilterCell = &place(6, 1, mNameFilter, 1, 1);
320
321
            place(0, 2, invenScroll, 7, 4);
322
            place(0, 6, mStoreButton, 1, 1);
323
            place(1, 6, mRetrieveButton, 1, 1);
324
            place(6, 6, mInvCloseButton, 1, 1);
325
            break;
326
        }
327
328
        case InventoryType::Cart:
329
        {
330
            mStoreButton = new Button(this,
331
                // TRANSLATORS: storage button
332
                _("Store"),
333
                "store",
334
                BUTTON_SKIN,
335
                this);
336
            mRetrieveButton = new Button(this,
337
                // TRANSLATORS: storage button
338
                _("Retrieve"),
339
                "retrieve",
340
                BUTTON_SKIN,
341
                this);
342
            mInvCloseButton = new Button(this,
343
                // TRANSLATORS: storage button
344
                _("Close"),
345
                "close",
346
                BUTTON_SKIN,
347
                this);
348
349
            mWeightBar = new ProgressBar(this, 0.0F, 100, 0,
350
                ProgressColorId::PROG_WEIGHT,
351
                "weightprogressbar.xml", "weightprogressbar_fill.xml");
352
            mWeightBar->setColor(getThemeColor(ThemeColorId::WEIGHT_BAR, 255U),
353
                getThemeColor(ThemeColorId::WEIGHT_BAR_OUTLINE, 255U));
354
355
            mSlotsBarCell = &place(3, 0, mSlotsBar, 3, 1);
356
            mSortDropDownCell = &place(6, 0, mSortDropDown, 1, 1);
357
358
            mFilterCell = &place(0, 1, mFilter, 7, 1).setPadding(3);
359
            mNameFilterCell = &place(6, 1, mNameFilter, 1, 1);
360
361
            place(0, 0, mWeightBar, 3, 1);
362
            place(0, 2, invenScroll, 7, 4);
363
            place(0, 6, mStoreButton, 1, 1);
364
            place(1, 6, mRetrieveButton, 1, 1);
365
            place(6, 6, mInvCloseButton, 1, 1);
366
            break;
367
        }
368
369
        default:
370
        case InventoryType::Trade:
371
        case InventoryType::Npc:
372
        case InventoryType::Vending:
373
        case InventoryType::MailEdit:
374
        case InventoryType::MailView:
375
        case InventoryType::Craft:
376
        case InventoryType::TypeEnd:
377
            break;
378
    }
379
380
1
    Layout &layout = getLayout();
381
1
    layout.setRowHeight(2, LayoutType::SET);
382
383
1
    mInventory->addInventoyListener(this);
384
385
2
    invInstances.push_back(this);
386
387
1
    if (inventory->isMainInventory())
388
    {
389
1
        updateDropButton();
390
    }
391
    else
392
    {
393
        if (!invInstances.empty())
394
            invInstances.front()->updateDropButton();
395
    }
396
397
1
    loadWindowState();
398
2
    enableVisibleSound(true);
399
}
400
401
1
void InventoryWindow::postInit()
402
{
403
1
    Window::postInit();
404
1
    slotsChanged(mInventory);
405
406
1
    mItems->setSortType(mSortDropDown->getSelected());
407
1
    widgetResized(Event(nullptr));
408

2
    if (mInventory != nullptr &&
409
2
        mInventory->getType() == InventoryType::Storage)
410
    {
411
        setVisible(Visible_true);
412
    }
413
1
}
414
415
7
InventoryWindow::~InventoryWindow()
416
{
417
1
    invInstances.remove(this);
418
1
    if (mInventory != nullptr)
419
1
        mInventory->removeInventoyListener(this);
420
1
    if (!invInstances.empty())
421
        invInstances.front()->updateDropButton();
422
423
1
    mSortDropDown->hideDrop(false);
424
2
    delete2(mSortModel)
425
2
}
426
427
1
void InventoryWindow::storeSortOrder() const
428
{
429
1
    if (mInventory != nullptr)
430
    {
431
2
        switch (mInventory->getType())
432
        {
433
            case InventoryType::Inventory:
434
            case InventoryType::Trade:
435
            case InventoryType::Npc:
436
            case InventoryType::Vending:
437
            case InventoryType::MailEdit:
438
            case InventoryType::MailView:
439
            case InventoryType::Craft:
440
            case InventoryType::TypeEnd:
441
            default:
442
4
                config.setValue("inventorySortOrder",
443
2
                    mSortDropDown->getSelected());
444
1
                break;
445
            case InventoryType::Storage:
446
                config.setValue("storageSortOrder",
447
                    mSortDropDown->getSelected());
448
                break;
449
            case InventoryType::Cart:
450
                config.setValue("cartSortOrder",
451
                    mSortDropDown->getSelected());
452
                break;
453
        }
454
    }
455
1
}
456
457
1
void InventoryWindow::action(const ActionEvent &event)
458
{
459
1
    const std::string &eventId = event.getId();
460
1
    if (eventId == "outfit")
461
    {
462
        inputManager.executeAction(InputAction::WINDOW_OUTFIT);
463
    }
464
1
    else if (eventId == "shop")
465
    {
466
        inputManager.executeAction(InputAction::WINDOW_SHOP);
467
    }
468
1
    else if (eventId == "equipment")
469
    {
470
        inputManager.executeAction(InputAction::WINDOW_EQUIPMENT);
471
    }
472
1
    else if (eventId == "cart")
473
    {
474
        inputManager.executeAction(InputAction::WINDOW_CART);
475
    }
476
1
    else if (eventId == "close")
477
    {
478
        close();
479
    }
480
1
    else if (eventId == "store")
481
    {
482
        if (inventoryWindow == nullptr || !inventoryWindow->isWindowVisible())
483
            return;
484
485
        Item *const item = inventoryWindow->getSelectedItem();
486
487
        if (item == nullptr)
488
            return;
489
490
        if (storageWindow != nullptr)
491
        {
492
            ItemAmountWindow::showWindow(ItemAmountWindowUsage::StoreAdd,
493
                this,
494
                item,
495
                0,
496
                0);
497
        }
498
        else if ((cartWindow != nullptr) && cartWindow->isWindowVisible())
499
        {
500
            ItemAmountWindow::showWindow(ItemAmountWindowUsage::CartAdd,
501
                this,
502
                item,
503
                0,
504
                0);
505
        }
506
    }
507
1
    else if (eventId == "sort")
508
    {
509
1
        mItems->setSortType(mSortDropDown->getSelected());
510
1
        storeSortOrder();
511
1
        return;
512
    }
513
    else if (eventId == "namefilter")
514
    {
515
        mItems->setName(mNameFilter->getText());
516
        mItems->updateMatrix();
517
    }
518
    else if (eventId.find("tag_") == 0U)
519
    {
520
        std::string tagName = event.getId().substr(4);
521
        mItems->setFilter(ItemDB::getTagId(tagName));
522
        return;
523
    }
524
525
    Item *const item = mItems->getSelectedItem();
526
527
    if (item == nullptr)
528
        return;
529
530
    if (eventId == "use")
531
    {
532
        PlayerInfo::useEquipItem(item, 0, Sfx_true);
533
    }
534
    if (eventId == "equip")
535
    {
536
        PlayerInfo::useEquipItem2(item, 0, Sfx_true);
537
    }
538
    else if (eventId == "drop")
539
    {
540
        if (isStorageActive())
541
        {
542
            inventoryHandler->moveItem2(InventoryType::Inventory,
543
                item->getInvIndex(),
544
                item->getQuantity(),
545
                InventoryType::Storage);
546
        }
547
        else if ((cartWindow != nullptr) && cartWindow->isWindowVisible())
548
        {
549
            inventoryHandler->moveItem2(InventoryType::Inventory,
550
                item->getInvIndex(),
551
                item->getQuantity(),
552
                InventoryType::Cart);
553
        }
554
        else
555
        {
556
            if (PlayerInfo::isItemProtected(item->getId()))
557
                return;
558
559
            if (inputManager.isActionActive(InputAction::STOP_ATTACK))
560
            {
561
                PlayerInfo::dropItem(item, item->getQuantity(), Sfx_true);
562
            }
563
            else
564
            {
565
                ItemAmountWindow::showWindow(ItemAmountWindowUsage::ItemDrop,
566
                    this,
567
                    item,
568
                    0,
569
                    0);
570
            }
571
        }
572
    }
573
    else if (eventId == "split")
574
    {
575
        ItemAmountWindow::showWindow(ItemAmountWindowUsage::ItemSplit,
576
            this,
577
            item,
578
            item->getQuantity() - 1,
579
            9);
580
    }
581
    else if (eventId == "retrieve")
582
    {
583
        if (storageWindow != nullptr)
584
        {
585
            ItemAmountWindow::showWindow(ItemAmountWindowUsage::StoreRemove,
586
                this,
587
                item,
588
                0,
589
                0);
590
        }
591
        else if ((cartWindow != nullptr) && cartWindow->isWindowVisible())
592
        {
593
            ItemAmountWindow::showWindow(ItemAmountWindowUsage::CartRemove,
594
                this,
595
                item,
596
                0,
597
                0);
598
        }
599
    }
600
}
601
602
Item *InventoryWindow::getSelectedItem() const
603
{
604
    return mItems->getSelectedItem();
605
}
606
607
void InventoryWindow::unselectItem()
608
{
609
    mItems->selectNone();
610
}
611
612
void InventoryWindow::widgetHidden(const Event &event)
613
{
614
    Window::widgetHidden(event);
615
    if (itemPopup != nullptr)
616
        itemPopup->setVisible(Visible_false);
617
}
618
619
void InventoryWindow::mouseClicked(MouseEvent &event)
620
{
621
    Window::mouseClicked(event);
622
623
    const int clicks = event.getClickCount();
624
625
    if (clicks == 2 && (gui != nullptr))
626
        gui->resetClickCount();
627
628
    const bool mod = (isStorageActive() &&
629
        inputManager.isActionActive(InputAction::STOP_ATTACK));
630
631
    const bool mod2 = (tradeWindow != nullptr &&
632
        tradeWindow->isWindowVisible() &&
633
        inputManager.isActionActive(InputAction::STOP_ATTACK));
634
635
    if (mInventory != nullptr)
636
    {
637
        if (!mod && !mod2 && event.getButton() == MouseButton::RIGHT)
638
        {
639
            Item *const item = mItems->getSelectedItem();
640
641
            if (item == nullptr)
642
                return;
643
644
            /* Convert relative to the window coordinates to absolute screen
645
             * coordinates.
646
             */
647
            if (popupMenu != nullptr)
648
            {
649
                const int mx = event.getX() + getX();
650
                const int my = event.getY() + getY();
651
652
                popupMenu->showPopup(this,
653
                    mx, my,
654
                    item,
655
                    mInventory->getType());
656
            }
657
        }
658
    }
659
    else
660
    {
661
        return;
662
    }
663
664
    if (event.getButton() == MouseButton::LEFT ||
665
        event.getButton() == MouseButton::RIGHT)
666
    {
667
        Item *const item = mItems->getSelectedItem();
668
669
        if (item == nullptr)
670
            return;
671
672
        if (mod)
673
        {
674
            if (mInventory->isMainInventory())
675
            {
676
                if (event.getButton() == MouseButton::RIGHT)
677
                {
678
                    ItemAmountWindow::showWindow(
679
                        ItemAmountWindowUsage::StoreAdd,
680
                        inventoryWindow,
681
                        item,
682
                        0,
683
                        0);
684
                }
685
                else
686
                {
687
                    inventoryHandler->moveItem2(InventoryType::Inventory,
688
                        item->getInvIndex(),
689
                        item->getQuantity(),
690
                        InventoryType::Storage);
691
                }
692
            }
693
            else
694
            {
695
                if (event.getButton() == MouseButton::RIGHT)
696
                {
697
                    ItemAmountWindow::showWindow(
698
                        ItemAmountWindowUsage::StoreRemove,
699
                        inventoryWindow,
700
                        item,
701
                        0,
702
                        0);
703
                }
704
                else
705
                {
706
                    inventoryHandler->moveItem2(InventoryType::Storage,
707
                        item->getInvIndex(),
708
                        item->getQuantity(),
709
                        InventoryType::Inventory);
710
                }
711
            }
712
        }
713
        else if (mod2 && mInventory->isMainInventory())
714
        {
715
            if (PlayerInfo::isItemProtected(item->getId()))
716
                return;
717
            if (event.getButton() == MouseButton::RIGHT)
718
            {
719
                ItemAmountWindow::showWindow(ItemAmountWindowUsage::TradeAdd,
720
                    tradeWindow,
721
                    item,
722
                    0,
723
                    0);
724
            }
725
            else
726
            {
727
                if (tradeWindow != nullptr)
728
                    tradeWindow->tradeItem(item, item->getQuantity(), true);
729
            }
730
        }
731
        else if (clicks == 2)
732
        {
733
            if (mInventory->isMainInventory())
734
            {
735
                if (isStorageActive())
736
                {
737
                    ItemAmountWindow::showWindow(
738
                        ItemAmountWindowUsage::StoreAdd,
739
                        inventoryWindow,
740
                        item,
741
                        0,
742
                        0);
743
                }
744
                else if (tradeWindow != nullptr &&
745
                         tradeWindow->isWindowVisible())
746
                {
747
                    if (PlayerInfo::isItemProtected(item->getId()))
748
                        return;
749
                    ItemAmountWindow::showWindow(
750
                        ItemAmountWindowUsage::TradeAdd,
751
                        tradeWindow,
752
                        item,
753
                        0,
754
                        0);
755
                }
756
                else
757
                {
758
                    PlayerInfo::useEquipItem(item, 0, Sfx_true);
759
                }
760
            }
761
            else
762
            {
763
                if (isStorageActive())
764
                {
765
                    ItemAmountWindow::showWindow(
766
                        ItemAmountWindowUsage::StoreRemove,
767
                        inventoryWindow,
768
                        item,
769
                        0,
770
                        0);
771
                }
772
            }
773
        }
774
    }
775
}
776
777
void InventoryWindow::mouseMoved(MouseEvent &event)
778
{
779
    Window::mouseMoved(event);
780
    if (textPopup == nullptr)
781
        return;
782
783
    const Widget *const src = event.getSource();
784
    if (src == nullptr)
785
    {
786
        textPopup->hide();
787
        return;
788
    }
789
    const int x = event.getX();
790
    const int y = event.getY();
791
    const Rect &rect = mDimension;
792
    if (src == mSlotsBar || src == mWeightBar)
793
    {
794
        // TRANSLATORS: money label
795
        textPopup->show(rect.x + x, rect.y + y, strprintf(_("Money: %s"),
796
            UnitsDb::formatCurrency(PlayerInfo::getAttribute(
797
            Attributes::MONEY)).c_str()));
798
    }
799
    else
800
    {
801
        const Button *const btn = dynamic_cast<const Button *>(src);
802
        if (btn == nullptr)
803
        {
804
            textPopup->hide();
805
            return;
806
        }
807
        const std::string text = btn->getDescription();
808
        if (!text.empty())
809
            textPopup->show(x + rect.x, y + rect.y, text);
810
    }
811
}
812
813
void InventoryWindow::mouseExited(MouseEvent &event A_UNUSED)
814
{
815
    textPopup->hide();
816
}
817
818
void InventoryWindow::keyPressed(KeyEvent &event)
819
{
820
    if (event.getActionId() == InputAction::GUI_MOD)
821
        mSplit = true;
822
}
823
824
void InventoryWindow::keyReleased(KeyEvent &event)
825
{
826
    if (event.getActionId() == InputAction::GUI_MOD)
827
        mSplit = false;
828
}
829
830
void InventoryWindow::valueChanged(const SelectionEvent &event A_UNUSED)
831
{
832
    if ((mInventory == nullptr) || !mInventory->isMainInventory())
833
        return;
834
835
    Item *const item = mItems->getSelectedItem();
836
837
    if (mSplit && (item != nullptr) && inventoryHandler->
838
        canSplit(mItems->getSelectedItem()))
839
    {
840
        ItemAmountWindow::showWindow(ItemAmountWindowUsage::ItemSplit,
841
            this,
842
            item,
843
            item->getQuantity() - 1,
844
            0);
845
    }
846
    updateButtons(item);
847
}
848
849
void InventoryWindow::updateButtons(const Item *item)
850
{
851
    if ((mInventory == nullptr) || !mInventory->isMainInventory())
852
        return;
853
854
    const Item *const selectedItem = mItems->getSelectedItem();
855
    if ((item != nullptr) && selectedItem != item)
856
        return;
857
858
    if (item == nullptr)
859
        item = selectedItem;
860
861
    if ((item == nullptr) || item->getQuantity() == 0)
862
    {
863
        if (mUseButton != nullptr)
864
            mUseButton->setEnabled(false);
865
        if (mDropButton != nullptr)
866
            mDropButton->setEnabled(false);
867
        return;
868
    }
869
870
    if (mDropButton != nullptr)
871
        mDropButton->setEnabled(true);
872
873
    if (mUseButton != nullptr)
874
    {
875
        const ItemInfo &info = item->getInfo();
876
        const std::string &str = (item->isEquipment() == Equipm_true
877
            && item->isEquipped() == Equipped_true)
878
            ? info.getUseButton2() : info.getUseButton();
879
        if (str.empty())
880
        {
881
            mUseButton->setEnabled(false);
882
            // TRANSLATORS: default use button name
883
            mUseButton->setCaption(_("Use"));
884
        }
885
        else
886
        {
887
            mUseButton->setEnabled(true);
888
            mUseButton->setCaption(str);
889
        }
890
    }
891
892
    updateDropButton();
893
}
894
895
void InventoryWindow::close()
896
{
897
    if (mInventory == nullptr)
898
    {
899
        Window::close();
900
        return;
901
    }
902
903
    switch (mInventory->getType())
904
    {
905
        case InventoryType::Inventory:
906
        case InventoryType::Cart:
907
            setVisible(Visible_false);
908
            break;
909
910
        case InventoryType::Storage:
911
            if (inventoryHandler != nullptr)
912
            {
913
                inventoryHandler->closeStorage();
914
                inventoryHandler->forgotStorage();
915
            }
916
            scheduleDelete();
917
            break;
918
919
        default:
920
        case InventoryType::Trade:
921
        case InventoryType::Npc:
922
        case InventoryType::Vending:
923
        case InventoryType::MailEdit:
924
        case InventoryType::MailView:
925
        case InventoryType::Craft:
926
        case InventoryType::TypeEnd:
927
            break;
928
    }
929
}
930
931
1
void InventoryWindow::updateWeight()
932
{
933

1
    if ((mInventory == nullptr) || (mWeightBar == nullptr))
934
        return;
935
2
    const InventoryTypeT type = mInventory->getType();
936
2
    if (type != InventoryType::Inventory &&
937
1
        type != InventoryType::Cart)
938
    {
939
        return;
940
    }
941
942
1
    const bool isInv = type == InventoryType::Inventory;
943
1
    const int total = PlayerInfo::getAttribute(isInv
944
1
        ? Attributes::TOTAL_WEIGHT : Attributes::CART_TOTAL_WEIGHT);
945
1
    const int max = PlayerInfo::getAttribute(isInv
946
1
        ? Attributes::MAX_WEIGHT : Attributes::CART_MAX_WEIGHT);
947
948
1
    if (max <= 0)
949
        return;
950
951
    // Adjust progress bar
952
    mWeightBar->setProgress(static_cast<float>(total)
953
        / static_cast<float>(max));
954
    mWeightBar->setText(strprintf("%s/%s",
955
        UnitsDb::formatWeight(total).c_str(),
956
        UnitsDb::formatWeight(max).c_str()));
957
}
958
959
1
void InventoryWindow::slotsChanged(const Inventory *const inventory)
960
{
961
1
    if (inventory == mInventory)
962
    {
963
2
        const int usedSlots = mInventory->getNumberOfSlotsUsed();
964
2
        const int maxSlots = mInventory->getSize();
965
966
1
        if (maxSlots != 0)
967
        {
968
2
            mSlotsBar->setProgress(static_cast<float>(usedSlots)
969
2
                / static_cast<float>(maxSlots));
970
        }
971
972
2
        mSlotsBar->setText(strprintf("%d/%d", usedSlots, maxSlots));
973
1
        mItems->updateMatrix();
974
    }
975
1
}
976
977
1
void InventoryWindow::updateDropButton()
978
{
979
1
    if (mDropButton == nullptr)
980
        return;
981
982

3
    if (isStorageActive() ||
983
1
        (cartWindow != nullptr && cartWindow->isWindowVisible()))
984
    {
985
        // TRANSLATORS: inventory button
986
        mDropButton->setCaption(_("Store"));
987
    }
988
    else
989
    {
990
1
        const Item *const item = mItems->getSelectedItem();
991

1
        if ((item != nullptr) && item->getQuantity() > 1)
992
        {
993
            // TRANSLATORS: inventory button
994
            mDropButton->setCaption(_("Drop..."));
995
        }
996
        else
997
        {
998
            // TRANSLATORS: inventory button
999
5
            mDropButton->setCaption(_("Drop"));
1000
        }
1001
    }
1002
}
1003
1004
bool InventoryWindow::isInputFocused() const
1005
{
1006
    return (mNameFilter != nullptr) && mNameFilter->isFocused();
1007
}
1008
1009
bool InventoryWindow::isAnyInputFocused()
1010
{
1011
    FOR_EACH (WindowList::const_iterator, it, invInstances)
1012
    {
1013
        if (((*it) != nullptr) && (*it)->isInputFocused())
1014
            return true;
1015
    }
1016
    return false;
1017
}
1018
1019
InventoryWindow *InventoryWindow::getFirstVisible()
1020
{
1021
    std::set<Widget*> list;
1022
    FOR_EACH (WindowList::const_iterator, it, invInstances)
1023
    {
1024
        if (((*it) != nullptr) && (*it)->isWindowVisible())
1025
            list.insert(*it);
1026
    }
1027
    return dynamic_cast<InventoryWindow*>(
1028
        windowContainer->findFirstWidget(list));
1029
}
1030
1031
void InventoryWindow::nextTab()
1032
{
1033
    const InventoryWindow *const window = getFirstVisible();
1034
    if (window != nullptr)
1035
        window->mFilter->nextTab();
1036
}
1037
1038
void InventoryWindow::prevTab()
1039
{
1040
    const InventoryWindow *const window = getFirstVisible();
1041
    if (window != nullptr)
1042
        window->mFilter->prevTab();
1043
}
1044
1045
2
void InventoryWindow::widgetResized(const Event &event)
1046
{
1047
2
    Window::widgetResized(event);
1048
1049
2
    if (mInventory == nullptr)
1050
        return;
1051
4
    const InventoryTypeT type = mInventory->getType();
1052
4
    if (type != InventoryType::Inventory &&
1053
2
        type != InventoryType::Cart)
1054
    {
1055
        return;
1056
    }
1057
1058
4
    if (getWidth() < 600)
1059
    {
1060
2
        if (!mCompactMode)
1061
        {
1062
1
            mNameFilter->setVisible(Visible_false);
1063
2
            mNameFilterCell->setType(LayoutCell::NONE);
1064
3
            mFilterCell->setWidth(mFilterCell->getWidth() + 3);
1065
1
            mCompactMode = true;
1066
        }
1067
    }
1068
    else if (mCompactMode)
1069
    {
1070
        mNameFilter->setVisible(Visible_true);
1071
        mNameFilterCell->setType(LayoutCell::WIDGET);
1072
        mFilterCell->setWidth(mFilterCell->getWidth() - 3);
1073
        mCompactMode = false;
1074
    }
1075
}
1076
1077
1
void InventoryWindow::setVisible(Visible visible)
1078
{
1079
1
    if (visible == Visible_false)
1080
1
        mSortDropDown->hideDrop(true);
1081
1
    Window::setVisible(visible);
1082
1
}
1083
1084
void InventoryWindow::unsetInventory()
1085
{
1086
    if (mInventory != nullptr)
1087
    {
1088
        mInventory->removeInventoyListener(this);
1089
        if (mItems != nullptr)
1090
            mItems->unsetInventory();
1091
    }
1092
    mInventory = nullptr;
1093
}
1094
1095
void InventoryWindow::attributeChanged(const AttributesT id,
1096
                                       const int64_t oldVal A_UNUSED,
1097
                                       const int64_t newVal A_UNUSED)
1098
{
1099
    if (id == Attributes::TOTAL_WEIGHT
1100
        || id == Attributes::MAX_WEIGHT
1101
        || id == Attributes::CART_TOTAL_WEIGHT
1102
        || id == Attributes::CART_MAX_WEIGHT)
1103
    {
1104
        updateWeight();
1105
    }
1106
}
1107
1108
void InventoryWindow::combineItems(const int index1,
1109
                                   const int index2)
1110
{
1111
    if (mInventory == nullptr)
1112
        return;
1113
    const Item *item1 = mInventory->getItem(index1);
1114
    if (item1 == nullptr)
1115
        return;
1116
    const Item *item2 = mInventory->getItem(index2);
1117
    if (item2 == nullptr)
1118
        return;
1119
1120
    if (item1->getType() != ItemType::Card)
1121
    {
1122
        const Item *tmpItem = item1;
1123
        item1 = item2;
1124
        item2 = tmpItem;
1125
    }
1126
1127
    ConfirmDialog *const confirmDlg = CREATEWIDGETR(ConfirmDialog,
1128
        // TRANSLATORS: question dialog title
1129
        _("Insert card request"),
1130
        // TRANSLATORS: question dialog message
1131
        strprintf(_("Insert %s into %s?"),
1132
        item1->getName().c_str(),
1133
        item2->getName().c_str()),
1134
        SOUND_REQUEST,
1135
        false,
1136
        Modal_true,
1137
        nullptr);
1138
    insertCardListener.itemIndex = item2->getInvIndex();
1139
    insertCardListener.cardIndex = item1->getInvIndex();
1140
    confirmDlg->addActionListener(&insertCardListener);
1141
}
1142
1143
void InventoryWindow::moveItemToCraft(const int craftSlot)
1144
{
1145
    if (npcHandler == nullptr)
1146
        return;
1147
1148
    Item *const item = mItems->getSelectedItem();
1149
    if (item == nullptr)
1150
        return;
1151
1152
    NpcDialog *const dialog = npcHandler->getCurrentNpcDialog();
1153
    if ((dialog != nullptr) &&
1154
        dialog->getInputState() == NpcInputState::ITEM_CRAFT)
1155
    {
1156
        if (item->getQuantity() > 1
1157
            && !inputManager.isActionActive(InputAction::STOP_ATTACK))
1158
        {
1159
            ItemAmountWindow::showWindow(ItemAmountWindowUsage::CraftAdd,
1160
                npcHandler->getCurrentNpcDialog(),
1161
                item,
1162
                0,
1163
                craftSlot);
1164
        }
1165
        else
1166
        {
1167
            dialog->addCraftItem(item, 1, craftSlot);
1168
        }
1169
    }
1170

3
}