GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/windows/inventorywindow.cpp Lines: 168 465 36.1 %
Date: 2017-11-29 Branches: 159 719 22.1 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2004-2009  The Mana World Development Team
4
 *  Copyright (C) 2009-2010  The Mana Developers
5
 *  Copyright (C) 2011-2017  The ManaPlus Developers
6
 *
7
 *  This file is part of The ManaPlus Client.
8
 *
9
 *  This program is free software; you can redistribute it and/or modify
10
 *  it under the terms of the GNU General Public License as published by
11
 *  the Free Software Foundation; either version 2 of the License, or
12
 *  any later version.
13
 *
14
 *  This program is distributed in the hope that it will be useful,
15
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 *  GNU General Public License for more details.
18
 *
19
 *  You should have received a copy of the GNU General Public License
20
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 */
22
23
#include "gui/windows/inventorywindow.h"
24
25
#include "configuration.h"
26
27
#include "being/playerinfo.h"
28
29
#include "enums/gui/layouttype.h"
30
31
#include "input/inputmanager.h"
32
33
#include "gui/gui.h"
34
35
#include "gui/fonts/font.h"
36
37
#include "gui/models/sortlistmodelinv.h"
38
39
#include "gui/popups/itempopup.h"
40
#include "gui/popups/popupmenu.h"
41
#include "gui/popups/textpopup.h"
42
43
#include "gui/windows/confirmdialog.h"
44
#include "gui/windows/itemamountwindow.h"
45
#include "gui/windows/npcdialog.h"
46
#include "gui/windows/setupwindow.h"
47
#include "gui/windows/tradewindow.h"
48
49
#include "gui/widgets/button.h"
50
#include "gui/widgets/createwidget.h"
51
#include "gui/widgets/containerplacer.h"
52
#include "gui/widgets/dropdown.h"
53
#include "gui/widgets/itemcontainer.h"
54
#include "gui/widgets/layout.h"
55
#include "gui/widgets/progressbar.h"
56
#include "gui/widgets/scrollarea.h"
57
#include "gui/widgets/tabstrip.h"
58
#include "gui/widgets/textfield.h"
59
#include "gui/widgets/windowcontainer.h"
60
61
#include "listeners/insertcardlistener.h"
62
63
#include "net/npchandler.h"
64
65
#include "resources/iteminfo.h"
66
67
#include "resources/db/unitsdb.h"
68
69
#include "resources/item/item.h"
70
71
#include "utils/delete2.h"
72
#include "utils/foreach.h"
73
74
#include "debug.h"
75
76
InventoryWindow *inventoryWindow = nullptr;
77
InventoryWindow *storageWindow = nullptr;
78
InventoryWindow *cartWindow = nullptr;
79
2
InventoryWindow::WindowList InventoryWindow::invInstances;
80
2
InsertCardListener insertCardListener;
81
82
2
InventoryWindow::InventoryWindow(Inventory *const inventory) :
83
    Window("Inventory", Modal_false, nullptr, "inventory.xml"),
84
    ActionListener(),
85
    KeyListener(),
86
    SelectionListener(),
87
    InventoryListener(),
88
    AttributeListener(),
89
    mInventory(inventory),
90

2
    mItems(new ItemContainer(this, mInventory)),
91
    mUseButton(nullptr),
92
    mDropButton(nullptr),
93
    mOutfitButton(nullptr),
94
    mShopButton(nullptr),
95
    mCartButton(nullptr),
96
    mEquipmentButton(nullptr),
97
    mStoreButton(nullptr),
98
    mRetrieveButton(nullptr),
99
    mInvCloseButton(nullptr),
100
    mWeightBar(nullptr),
101
    mSlotsBar(new ProgressBar(this, 0.0F, 100, 0,
102
        ProgressColorId::PROG_INVY_SLOTS,
103

2
        "slotsprogressbar.xml", "slotsprogressbar_fill.xml")),
104
    mFilter(nullptr),
105
4
    mSortModel(new SortListModelInv),
106
2
    mSortDropDown(new DropDown(this, mSortModel, false,
107

4
        Modal_false, this, "sort")),
108
    mNameFilter(new TextField(this, "", LoseFocusOnTab_true,
109

2
        this, "namefilter", true)),
110
    mSortDropDownCell(nullptr),
111
    mNameFilterCell(nullptr),
112
    mFilterCell(nullptr),
113
    mSlotsBarCell(nullptr),
114
    mSplit(false),
115




60
    mCompactMode(false)
116
{
117
6
    mSlotsBar->setColor(getThemeColor(ThemeColorId::SLOTS_BAR),
118
        getThemeColor(ThemeColorId::SLOTS_BAR_OUTLINE));
119
120
2
    if (inventory != nullptr)
121
    {
122

14
        setCaption(gettext(inventory->getName().c_str()));
123
6
        setWindowName(inventory->getName());
124
2
        switch (inventory->getType())
125
        {
126
2
            case InventoryType::Inventory:
127
            case InventoryType::Trade:
128
            case InventoryType::Npc:
129
            case InventoryType::Vending:
130
            case InventoryType::MailEdit:
131
            case InventoryType::MailView:
132
            case InventoryType::Craft:
133
            case InventoryType::TypeEnd:
134
            default:
135

8
                mSortDropDown->setSelected(config.getIntValue(
136
                    "inventorySortOrder"));
137
2
                break;
138
            case InventoryType::Storage:
139
                mSortDropDown->setSelected(config.getIntValue(
140
                    "storageSortOrder"));
141
                break;
142
            case InventoryType::Cart:
143
                mSortDropDown->setSelected(config.getIntValue(
144
                    "cartSortOrder"));
145
                break;
146
        };
147
    }
148
    else
149
    {
150
        // TRANSLATORS: inventory window name
151
        setCaption(_("Inventory"));
152
        setWindowName("Inventory");
153
        mSortDropDown->setSelected(0);
154
    }
155
156
2
    if ((setupWindow != nullptr) &&
157

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

8
    const int size = config.getIntValue("fontSize");
179

6
    mFilter = new TabStrip(this, "filter_" + getWindowName(), size + 16);
180
2
    mFilter->addActionListener(this);
181
10
    mFilter->setActionEventId("tag_");
182
4
    mFilter->setSelectable(false);
183
184

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

8
        fromBool(getOptionBool("showbackground"), Opaque),
197

10
        "inventory_background.xml");
198
2
    invenScroll->setHorizontalScrollPolicy(ScrollArea::SHOW_NEVER);
199
200

4
    switch (mInventory->getType())
201
    {
202
        case InventoryType::Inventory:
203
        {
204
            // TRANSLATORS: inventory button
205
8
            const std::string equip = _("Equip");
206
            // TRANSLATORS: inventory button
207
8
            const std::string use = _("Use");
208
            // TRANSLATORS: inventory button
209
8
            const std::string unequip = _("Unequip");
210
211

2
            std::string longestUseString = getFont()->getWidth(equip) >
212

6
                getFont()->getWidth(use) ? equip : use;
213
214

4
            if (getFont()->getWidth(longestUseString) <
215

2
                getFont()->getWidth(unequip))
216
            {
217
                longestUseString = unequip;
218
            }
219
220

8
            mUseButton = new Button(this, longestUseString, "use", this);
221
            // TRANSLATORS: inventory button
222


14
            mDropButton = new Button(this, _("Drop..."), "drop", this);
223
            // TRANSLATORS: inventory outfits button
224


14
            mOutfitButton = new Button(this, _("O"), "outfit", this);
225
            // TRANSLATORS: inventory cart button
226


14
            mCartButton = new Button(this, _("C"), "cart", this);
227
            // TRANSLATORS: inventory shop button
228


14
            mShopButton = new Button(this, _("S"), "shop", this);
229
            // TRANSLATORS: inventory equipment button
230


14
            mEquipmentButton = new Button(this, _("E"), "equipment", this);
231

16
            mWeightBar = new ProgressBar(this, 0.0F, 100, 0,
232
                ProgressColorId::PROG_WEIGHT,
233

2
                "weightprogressbar.xml", "weightprogressbar_fill.xml");
234
6
            mWeightBar->setColor(getThemeColor(ThemeColorId::WEIGHT_BAR),
235
                getThemeColor(ThemeColorId::WEIGHT_BAR_OUTLINE));
236
237
            // TRANSLATORS: outfits button tooltip
238
10
            mOutfitButton->setDescription(_("Outfits"));
239
            // TRANSLATORS: cart button tooltip
240
10
            mCartButton->setDescription(_("Cart"));
241
            // TRANSLATORS: shop button tooltip
242
10
            mShopButton->setDescription(_("Shop"));
243
            // TRANSLATORS: equipment button tooltip
244
10
            mEquipmentButton->setDescription(_("Equipment"));
245
246
2
            place(0, 0, mWeightBar, 4);
247
2
            mSlotsBarCell = &place(4, 0, mSlotsBar, 4);
248

2
            mSortDropDownCell = &place(8, 0, mSortDropDown, 3);
249
250
4
            mFilterCell = &place(0, 1, mFilter, 10).setPadding(3);
251
2
            mNameFilterCell = &place(8, 1, mNameFilter, 3);
252
253
4
            place(0, 2, invenScroll, 11).setPadding(3);
254
2
            place(0, 3, mUseButton);
255
2
            place(1, 3, mDropButton);
256
2
            ContainerPlacer placer = getPlacer(10, 3);
257
2
            placer(0, 0, mShopButton);
258
2
            placer(1, 0, mOutfitButton);
259
2
            placer(2, 0, mCartButton);
260
2
            placer(3, 0, mEquipmentButton);
261
262
2
            updateWeight();
263
            break;
264
        }
265
266
        case InventoryType::Storage:
267
        {
268
            // TRANSLATORS: storage button
269
            mStoreButton = new Button(this, _("Store"), "store", this);
270
            // TRANSLATORS: storage button
271
            mRetrieveButton = new Button(this, _("Retrieve"),
272
                "retrieve", this);
273
            // TRANSLATORS: storage button
274
            mInvCloseButton = new Button(this, _("Close"), "close", this);
275
276
            mSlotsBarCell = &place(0, 0, mSlotsBar, 6);
277
            mSortDropDownCell = &place(6, 0, mSortDropDown, 1);
278
279
            mFilterCell = &place(0, 1, mFilter, 7).setPadding(3);
280
            mNameFilterCell = &place(6, 1, mNameFilter, 1);
281
282
            place(0, 2, invenScroll, 7, 4);
283
            place(0, 6, mStoreButton);
284
            place(1, 6, mRetrieveButton);
285
            place(6, 6, mInvCloseButton);
286
            break;
287
        }
288
289
        case InventoryType::Cart:
290
        {
291
            // TRANSLATORS: storage button
292
            mStoreButton = new Button(this, _("Store"), "store", this);
293
            // TRANSLATORS: storage button
294
            mRetrieveButton = new Button(this, _("Retrieve"),
295
                "retrieve", this);
296
            // TRANSLATORS: storage button
297
            mInvCloseButton = new Button(this, _("Close"), "close", this);
298
299
            mWeightBar = new ProgressBar(this, 0.0F, 100, 0,
300
                ProgressColorId::PROG_WEIGHT,
301
                "weightprogressbar.xml", "weightprogressbar_fill.xml");
302
            mWeightBar->setColor(getThemeColor(ThemeColorId::WEIGHT_BAR),
303
                getThemeColor(ThemeColorId::WEIGHT_BAR_OUTLINE));
304
305
            mSlotsBarCell = &place(3, 0, mSlotsBar, 3);
306
            mSortDropDownCell = &place(6, 0, mSortDropDown, 1);
307
308
            mFilterCell = &place(0, 1, mFilter, 7).setPadding(3);
309
            mNameFilterCell = &place(6, 1, mNameFilter, 1);
310
311
            place(0, 0, mWeightBar, 3);
312
            place(0, 2, invenScroll, 7, 4);
313
            place(0, 6, mStoreButton);
314
            place(1, 6, mRetrieveButton);
315
            place(6, 6, mInvCloseButton);
316
            break;
317
        }
318
319
        default:
320
        case InventoryType::Trade:
321
        case InventoryType::Npc:
322
        case InventoryType::Vending:
323
        case InventoryType::MailEdit:
324
        case InventoryType::MailView:
325
        case InventoryType::Craft:
326
        case InventoryType::TypeEnd:
327
            break;
328
    };
329
330
2
    Layout &layout = getLayout();
331
2
    layout.setRowHeight(2, LayoutType::SET);
332
333
2
    mInventory->addInventoyListener(this);
334
335
4
    invInstances.push_back(this);
336
337
2
    if (inventory->isMainInventory())
338
    {
339
2
        updateDropButton();
340
    }
341
    else
342
    {
343
        if (!invInstances.empty())
344
            invInstances.front()->updateDropButton();
345
    }
346
347
2
    loadWindowState();
348
4
    enableVisibleSound(true);
349
}
350
351
2
void InventoryWindow::postInit()
352
{
353
2
    Window::postInit();
354
2
    slotsChanged(mInventory);
355
356
2
    mItems->setSortType(mSortDropDown->getSelected());
357
2
    widgetResized(Event(nullptr));
358

4
    if (mInventory != nullptr &&
359
4
        mInventory->getType() == InventoryType::Storage)
360
    {
361
        setVisible(Visible_true);
362
    }
363
2
}
364
365
14
InventoryWindow::~InventoryWindow()
366
{
367
2
    invInstances.remove(this);
368
2
    if (mInventory != nullptr)
369
2
        mInventory->removeInventoyListener(this);
370
2
    if (!invInstances.empty())
371
        invInstances.front()->updateDropButton();
372
373
2
    mSortDropDown->hideDrop(false);
374
4
    delete2(mSortModel);
375
4
}
376
377
2
void InventoryWindow::storeSortOrder() const
378
{
379
2
    if (mInventory != nullptr)
380
    {
381
4
        switch (mInventory->getType())
382
        {
383
2
            case InventoryType::Inventory:
384
            case InventoryType::Trade:
385
            case InventoryType::Npc:
386
            case InventoryType::Vending:
387
            case InventoryType::MailEdit:
388
            case InventoryType::MailView:
389
            case InventoryType::Craft:
390
            case InventoryType::TypeEnd:
391
            default:
392

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

2
    if ((mInventory == nullptr) || (mWeightBar == nullptr))
848
        return;
849
4
    const InventoryTypeT type = mInventory->getType();
850
4
    if (type != InventoryType::Inventory &&
851
2
        type != InventoryType::Cart)
852
    {
853
        return;
854
    }
855
856
2
    const bool isInv = type == InventoryType::Inventory;
857
2
    const int total = PlayerInfo::getAttribute(isInv
858
2
        ? Attributes::TOTAL_WEIGHT : Attributes::CART_TOTAL_WEIGHT);
859
2
    const int max = PlayerInfo::getAttribute(isInv
860
2
        ? Attributes::MAX_WEIGHT : Attributes::CART_MAX_WEIGHT);
861
862
2
    if (max <= 0)
863
        return;
864
865
    // Adjust progress bar
866
    mWeightBar->setProgress(static_cast<float>(total)
867
        / static_cast<float>(max));
868
    mWeightBar->setText(strprintf("%s/%s",
869
        UnitsDb::formatWeight(total).c_str(),
870
        UnitsDb::formatWeight(max).c_str()));
871
}
872
873
2
void InventoryWindow::slotsChanged(const Inventory *const inventory)
874
{
875
2
    if (inventory == mInventory)
876
    {
877
4
        const int usedSlots = mInventory->getNumberOfSlotsUsed();
878
4
        const int maxSlots = mInventory->getSize();
879
880
2
        if (maxSlots != 0)
881
        {
882
4
            mSlotsBar->setProgress(static_cast<float>(usedSlots)
883
2
                / static_cast<float>(maxSlots));
884
        }
885
886
4
        mSlotsBar->setText(strprintf("%d/%d", usedSlots, maxSlots));
887
2
        mItems->updateMatrix();
888
    }
889
2
}
890
891
2
void InventoryWindow::updateDropButton()
892
{
893
2
    if (mDropButton == nullptr)
894
        return;
895
896

4
    if (isStorageActive() ||
897
2
        (cartWindow != nullptr && cartWindow->isWindowVisible()))
898
    {
899
        // TRANSLATORS: inventory button
900
        mDropButton->setCaption(_("Store"));
901
    }
902
    else
903
    {
904
2
        const Item *const item = mItems->getSelectedItem();
905

2
        if ((item != nullptr) && item->getQuantity() > 1)
906
        {
907
            // TRANSLATORS: inventory button
908
            mDropButton->setCaption(_("Drop..."));
909
        }
910
        else
911
        {
912
            // TRANSLATORS: inventory button
913
10
            mDropButton->setCaption(_("Drop"));
914
        }
915
    }
916
}
917
918
bool InventoryWindow::isInputFocused() const
919
{
920
    return (mNameFilter != nullptr) && mNameFilter->isFocused();
921
}
922
923
bool InventoryWindow::isAnyInputFocused()
924
{
925
    FOR_EACH (WindowList::const_iterator, it, invInstances)
926
    {
927
        if (((*it) != nullptr) && (*it)->isInputFocused())
928
            return true;
929
    }
930
    return false;
931
}
932
933
InventoryWindow *InventoryWindow::getFirstVisible()
934
{
935
    std::set<Widget*> list;
936
    FOR_EACH (WindowList::const_iterator, it, invInstances)
937
    {
938
        if (((*it) != nullptr) && (*it)->isWindowVisible())
939
            list.insert(*it);
940
    }
941
    return dynamic_cast<InventoryWindow*>(
942
        windowContainer->findFirstWidget(list));
943
}
944
945
void InventoryWindow::nextTab()
946
{
947
    const InventoryWindow *const window = getFirstVisible();
948
    if (window != nullptr)
949
        window->mFilter->nextTab();
950
}
951
952
void InventoryWindow::prevTab()
953
{
954
    const InventoryWindow *const window = getFirstVisible();
955
    if (window != nullptr)
956
        window->mFilter->prevTab();
957
}
958
959
4
void InventoryWindow::widgetResized(const Event &event)
960
{
961
4
    Window::widgetResized(event);
962
963
4
    if (mInventory == nullptr)
964
        return;
965
8
    const InventoryTypeT type = mInventory->getType();
966
8
    if (type != InventoryType::Inventory &&
967
4
        type != InventoryType::Cart)
968
    {
969
        return;
970
    }
971
972
8
    if (getWidth() < 600)
973
    {
974
4
        if (!mCompactMode)
975
        {
976
2
            mNameFilter->setVisible(Visible_false);
977
4
            mNameFilterCell->setType(LayoutCell::NONE);
978
6
            mFilterCell->setWidth(mFilterCell->getWidth() + 3);
979
2
            mCompactMode = true;
980
        }
981
    }
982
    else if (mCompactMode)
983
    {
984
        mNameFilter->setVisible(Visible_true);
985
        mNameFilterCell->setType(LayoutCell::WIDGET);
986
        mFilterCell->setWidth(mFilterCell->getWidth() - 3);
987
        mCompactMode = false;
988
    }
989
}
990
991
2
void InventoryWindow::setVisible(Visible visible)
992
{
993
2
    if (visible == Visible_false)
994
2
        mSortDropDown->hideDrop();
995
2
    Window::setVisible(visible);
996
2
}
997
998
void InventoryWindow::unsetInventory()
999
{
1000
    if (mInventory != nullptr)
1001
    {
1002
        mInventory->removeInventoyListener(this);
1003
        if (mItems != nullptr)
1004
            mItems->unsetInventory();
1005
    }
1006
    mInventory = nullptr;
1007
}
1008
1009
void InventoryWindow::attributeChanged(const AttributesT id,
1010
                                       const int64_t oldVal A_UNUSED,
1011
                                       const int64_t newVal A_UNUSED)
1012
{
1013
    if (id == Attributes::TOTAL_WEIGHT
1014
        || id == Attributes::MAX_WEIGHT
1015
        || id == Attributes::CART_TOTAL_WEIGHT
1016
        || id == Attributes::CART_MAX_WEIGHT)
1017
    {
1018
        updateWeight();
1019
    }
1020
}
1021
1022
void InventoryWindow::combineItems(const int index1,
1023
                                   const int index2)
1024
{
1025
    if (mInventory == nullptr)
1026
        return;
1027
    const Item *item1 = mInventory->getItem(index1);
1028
    if (item1 == nullptr)
1029
        return;
1030
    const Item *item2 = mInventory->getItem(index2);
1031
    if (item2 == nullptr)
1032
        return;
1033
1034
    if (item1->getType() != ItemType::Card)
1035
    {
1036
        const Item *tmpItem = item1;
1037
        item1 = item2;
1038
        item2 = tmpItem;
1039
    }
1040
1041
    ConfirmDialog *const confirmDlg = CREATEWIDGETR(ConfirmDialog,
1042
        // TRANSLATORS: question dialog title
1043
        _("Insert card request"),
1044
        // TRANSLATORS: question dialog message
1045
        strprintf(_("Insert %s into %s?"),
1046
        item1->getName().c_str(),
1047
        item2->getName().c_str()),
1048
        SOUND_REQUEST,
1049
        false,
1050
        Modal_true);
1051
    insertCardListener.itemIndex = item2->getInvIndex();
1052
    insertCardListener.cardIndex = item1->getInvIndex();
1053
    confirmDlg->addActionListener(&insertCardListener);
1054
}
1055
1056
void InventoryWindow::moveItemToCraft(const int craftSlot)
1057
{
1058
    if (npcHandler == nullptr)
1059
        return;
1060
1061
    Item *const item = mItems->getSelectedItem();
1062
    if (item == nullptr)
1063
        return;
1064
1065
    NpcDialog *const dialog = npcHandler->getCurrentNpcDialog();
1066
    if ((dialog != nullptr) &&
1067
        dialog->getInputState() == NpcInputState::ITEM_CRAFT)
1068
    {
1069
        if (item->getQuantity() > 1
1070
            && !inputManager.isActionActive(InputAction::STOP_ATTACK))
1071
        {
1072
            ItemAmountWindow::showWindow(ItemAmountWindowUsage::CraftAdd,
1073
                npcHandler->getCurrentNpcDialog(),
1074
                item,
1075
                0,
1076
                craftSlot);
1077
        }
1078
        else
1079
        {
1080
            dialog->addCraftItem(item, 1, craftSlot);
1081
        }
1082
    }
1083

6
}