GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/widgets/itemcontainer.cpp Lines: 75 595 12.6 %
Date: 2018-12-09 Branches: 68 674 10.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-2018  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/widgets/itemcontainer.h"
24
25
#include "dragdrop.h"
26
#include "settings.h"
27
28
#include "being/playerinfo.h"
29
30
#include "gui/gui.h"
31
#include "gui/skin.h"
32
#include "gui/viewport.h"
33
34
#include "gui/fonts/font.h"
35
36
#include "gui/shortcut/itemshortcut.h"
37
38
#include "gui/popups/itempopup.h"
39
40
#include "gui/windows/chatwindow.h"
41
#include "gui/windows/inventorywindow.h"
42
#include "gui/windows/shopwindow.h"
43
#include "gui/windows/shortcutwindow.h"
44
#include "gui/windows/itemamountwindow.h"
45
#include "gui/windows/maileditwindow.h"
46
#include "gui/windows/npcdialog.h"
47
48
#include "input/inputmanager.h"
49
50
#include "net/inventoryhandler.h"
51
#include "net/net.h"
52
#include "net/mail2handler.h"
53
#include "net/npchandler.h"
54
#include "net/tradehandler.h"
55
56
#include "utils/delete2.h"
57
#include "utils/foreach.h"
58
#include "utils/gettext.h"
59
#include "utils/stringutils.h"
60
61
#include "render/vertexes/imagecollection.h"
62
63
#include "resources/iteminfo.h"
64
65
#include <algorithm>
66
67
#include "debug.h"
68
69
namespace
70
{
71
    class ItemIdPair final
72
    {
73
        public:
74
            A_DELETE_COPY(ItemIdPair)
75
76
            ItemIdPair(const int id, Item *const item) :
77
                mId(id), mItem(item)
78
            {
79
            }
80
81
            int mId;
82
            Item* mItem;
83
    };
84
85
    class SortItemAlphaFunctor final
86
    {
87
        public:
88
            A_DEFAULT_COPY(SortItemAlphaFunctor)
89
90
            bool operator() (const ItemIdPair *const pair1,
91
                             const ItemIdPair *const pair2) const
92
            {
93
                const Item *const item1 = pair1->mItem;
94
                const Item *const item2 = pair2->mItem;
95
                if ((item1 == nullptr) || (item2 == nullptr))
96
                    return false;
97
98
                const std::string name1 = item1->getInfo().getName(
99
                    item1->getColor());
100
                const std::string name2 = item2->getInfo().getName(
101
                    item2->getColor());
102
                if (name1 == name2)
103
                {
104
                    return item1->getInvIndex() <
105
                        item2->getInvIndex();
106
                }
107
                return name1 < name2;
108
            }
109
    } itemAlphaInvSorter;
110
111
    class SortItemIdFunctor final
112
    {
113
        public:
114
            A_DEFAULT_COPY(SortItemIdFunctor)
115
116
            bool operator() (const ItemIdPair *const pair1,
117
                             const ItemIdPair *const pair2) const
118
            {
119
                if ((pair1->mItem == nullptr) || (pair2->mItem == nullptr))
120
                    return false;
121
122
                const int id1 = pair1->mItem->getId();
123
                const int id2 = pair2->mItem->getId();
124
                if (id1 == id2)
125
                {
126
                    return pair1->mItem->getInvIndex() <
127
                        pair2->mItem->getInvIndex();
128
                }
129
                return id1 < id2;
130
            }
131
    } itemIdInvSorter;
132
133
    class SortItemWeightFunctor final
134
    {
135
        public:
136
            A_DEFAULT_COPY(SortItemWeightFunctor)
137
138
            bool operator() (const ItemIdPair *const pair1,
139
                             const ItemIdPair *const pair2) const
140
            {
141
                if ((pair1->mItem == nullptr) || (pair2->mItem == nullptr))
142
                    return false;
143
144
                const int w1 = pair1->mItem->getInfo().getWeight();
145
                const int w2 = pair2->mItem->getInfo().getWeight();
146
                if (w1 == w2)
147
                {
148
                    const std::string name1 =
149
                        pair1->mItem->getInfo().getName();
150
                    const std::string name2 =
151
                        pair2->mItem->getInfo().getName();
152
                    if (name1 == name2)
153
                    {
154
                        return pair1->mItem->getInvIndex() <
155
                            pair2->mItem->getInvIndex();
156
                    }
157
                    return name1 < name2;
158
                }
159
                return w1 < w2;
160
            }
161
    } itemWeightInvSorter;
162
163
    class SortItemAmountFunctor final
164
    {
165
        public:
166
            A_DEFAULT_COPY(SortItemAmountFunctor)
167
168
            bool operator() (const ItemIdPair *const pair1,
169
                             const ItemIdPair *const pair2) const
170
            {
171
                if ((pair1->mItem == nullptr) || (pair2->mItem == nullptr))
172
                    return false;
173
174
                const int c1 = pair1->mItem->getQuantity();
175
                const int c2 = pair2->mItem->getQuantity();
176
                if (c1 == c2)
177
                {
178
                    const std::string name1 =
179
                        pair1->mItem->getInfo().getName();
180
                    const std::string name2 =
181
                        pair2->mItem->getInfo().getName();
182
                    if (name1 == name2)
183
                    {
184
                        return pair1->mItem->getInvIndex() <
185
                            pair2->mItem->getInvIndex();
186
                    }
187
                    return name1 < name2;
188
                }
189
                return c1 < c2;
190
            }
191
    } itemAmountInvSorter;
192
193
    class SortItemTypeFunctor final
194
    {
195
        public:
196
            A_DEFAULT_COPY(SortItemTypeFunctor)
197
198
            bool operator() (const ItemIdPair *const pair1,
199
                             const ItemIdPair *const pair2) const
200
            {
201
                if ((pair1->mItem == nullptr) || (pair2->mItem == nullptr))
202
                    return false;
203
204
                const ItemInfo &info1 = pair1->mItem->getInfo();
205
                const ItemInfo &info2 = pair2->mItem->getInfo();
206
                const ItemDbTypeT t1 = info1.getType();
207
                const ItemDbTypeT t2 = info2.getType();
208
                if (t1 == t2)
209
                {
210
                    const std::string &name1 = info1.getName();
211
                    const std::string &name2 = info2.getName();
212
                    if (name1 == name2)
213
                    {
214
                        return pair1->mItem->getInvIndex() <
215
                            pair2->mItem->getInvIndex();
216
                    }
217
                    return name1 < name2;
218
                }
219
                return t1 < t2;
220
            }
221
    } itemTypeInvSorter;
222
}  // namespace
223
224
6
ItemContainer::ItemContainer(const Widget2 *const widget,
225
                             Inventory *const inventory,
226
                             const int maxColumns,
227
                             const ShowEmptyRows showEmptyRows,
228
6
                             const ForceQuantity forceQuantity) :
229
    Widget(widget),
230
    KeyListener(),
231
    MouseListener(),
232
    WidgetListener(),
233
    mInventory(inventory),
234

42
    mSelImg(Theme::getImageFromThemeXml("item_selection.xml", "")),
235

24
    mProtectedImg(Theme::getImageFromTheme("lock.png")),
236

42
    mCellBackgroundImg(Theme::getImageFromThemeXml("inventory_cell.xml", "")),
237
    mName(),
238
    mShowMatrix(nullptr),
239




42
    mSkin(theme != nullptr ? theme->load("itemcontainer.xml", "",
240

12
        true, Theme::getThemePath()) : nullptr),
241

6
    mVertexes(new ImageCollection),
242
12
    mEquipedColor(getThemeColor(ThemeColorId::ITEM_EQUIPPED, 255U)),
243
12
    mEquipedColor2(getThemeColor(ThemeColorId::ITEM_EQUIPPED_OUTLINE, 255U)),
244
12
    mUnEquipedColor(getThemeColor(ThemeColorId::ITEM_NOT_EQUIPPED, 255U)),
245
    mUnEquipedColor2(getThemeColor(ThemeColorId::ITEM_NOT_EQUIPPED_OUTLINE,
246
12
        255U)),
247
    mSelectionListeners(),
248
    mGridColumns(1),
249
    mGridRows(1),
250
    mDrawRows(1),
251
    mSelectedIndex(-1),
252
    mLastUsedSlot(-1),
253
    mTag(0),
254
    mSortType(0),
255
    mClicks(1),
256


24
    mBoxWidth(mSkin != nullptr ? mSkin->getOption("boxWidth", 35) : 35),
257


24
    mBoxHeight(mSkin != nullptr ? mSkin->getOption("boxHeight", 43) : 43),
258


24
    mEquippedTextPadding(mSkin != nullptr ? mSkin->getOption(
259
                         "equippedTextPadding", 29) : 29),
260


24
    mPaddingItemX(mSkin != nullptr ? mSkin->getOption("paddingItemX", 0) : 0),
261


24
    mPaddingItemY(mSkin != nullptr ? mSkin->getOption("paddingItemY", 0) : 0),
262
    mMaxColumns(maxColumns),
263
    mSelectionStatus(SEL_NONE),
264
    mForceQuantity(forceQuantity),
265
    mShowEmptyRows(showEmptyRows),
266
132
    mDescItems(false)
267
{
268
6
    setFocusable(true);
269
6
    addKeyListener(this);
270
6
    addMouseListener(this);
271
6
    addWidgetListener(this);
272
6
    mAllowLogic = false;
273
6
}
274
275
48
ItemContainer::~ItemContainer()
276
{
277
6
    if (gui != nullptr)
278
6
        gui->removeDragged(this);
279
280
6
    if (mSelImg != nullptr)
281
    {
282
6
        mSelImg->decRef();
283
6
        mSelImg = nullptr;
284
    }
285
6
    if (mProtectedImg != nullptr)
286
    {
287
6
        mProtectedImg->decRef();
288
6
        mProtectedImg = nullptr;
289
    }
290
6
    if (mCellBackgroundImg != nullptr)
291
    {
292
        mCellBackgroundImg->decRef();
293
        mCellBackgroundImg = nullptr;
294
    }
295
296
6
    if (theme != nullptr)
297
6
        theme->unload(mSkin);
298
299
6
    delete []mShowMatrix;
300
6
    delete2(mVertexes)
301
12
}
302
303
void ItemContainer::logic()
304
{
305
    BLOCK_START("ItemContainer::logic")
306
    Widget::logic();
307
308
    if (mInventory == nullptr)
309
    {
310
        BLOCK_END("ItemContainer::logic")
311
        return;
312
    }
313
314
    const int lastUsedSlot = mInventory->getLastUsedSlot();
315
316
    if (lastUsedSlot != mLastUsedSlot)
317
    {
318
        mLastUsedSlot = lastUsedSlot;
319
        adjustHeight();
320
    }
321
    BLOCK_END("ItemContainer::logic")
322
}
323
324
2
void ItemContainer::draw(Graphics *const graphics)
325
{
326

2
    if ((mInventory == nullptr) || (mShowMatrix == nullptr))
327
        return;
328
329
    BLOCK_START("ItemContainer::draw")
330
    Font *const font = getFont();
331
332
    if (mCellBackgroundImg != nullptr)
333
    {
334
        if (mRedraw || graphics->getRedraw())
335
        {
336
            mRedraw = false;
337
            mVertexes->clear();
338
339
            const int invSize = mInventory->getSize();
340
            const int maxRows = mShowEmptyRows == ShowEmptyRows_true ?
341
                std::max(invSize / mGridColumns, mGridRows) : mGridRows;
342
            const int maxColumns = mGridColumns > invSize ?
343
                invSize : mGridColumns;
344
            for (int j = 0; j < maxRows; j++)
345
            {
346
                const int intY0 = j * mBoxHeight;
347
                for (int i = 0; i < maxColumns; i++)
348
                {
349
                    int itemX = i * mBoxWidth;
350
                    int itemY = intY0;
351
                    graphics->calcTileCollection(mVertexes,
352
                        mCellBackgroundImg,
353
                        itemX + mPaddingItemX,
354
                        itemY + mPaddingItemY);
355
                }
356
            }
357
            graphics->finalize(mVertexes);
358
        }
359
        graphics->drawTileCollection(mVertexes);
360
    }
361
362
    for (int j = 0; j < mDrawRows; j++)
363
    {
364
        const int intY0 = j * mBoxHeight;
365
        int itemIndex = j * mGridColumns - 1;
366
        for (int i = 0; i < mGridColumns; i++)
367
        {
368
            int itemX = i * mBoxWidth;
369
            int itemY = intY0;
370
            itemIndex ++;
371
            if (mShowMatrix[itemIndex] < 0)
372
                continue;
373
374
            const Item *const item = mInventory->getItem(
375
                mShowMatrix[itemIndex]);
376
377
            if ((item == nullptr) || item->getId() == 0)
378
                continue;
379
380
            Image *const image = item->getImage();
381
            if (image != nullptr)
382
            {
383
                if (mShowMatrix[itemIndex] == mSelectedIndex)
384
                {
385
                    if (mSelImg != nullptr)
386
                        graphics->drawImage(mSelImg, itemX, itemY);
387
                }
388
                image->setAlpha(1.0F);  // ensure the image if fully drawn...
389
                graphics->drawImage(image,
390
                    itemX + mPaddingItemX,
391
                    itemY + mPaddingItemY);
392
                if ((mProtectedImg != nullptr) && PlayerInfo::isItemProtected(
393
                    item->getId()))
394
                {
395
                    graphics->drawImage(mProtectedImg,
396
                        itemX + mPaddingItemX,
397
                        itemY + mPaddingItemY);
398
                }
399
            }
400
        }
401
    }
402
403
    for (int j = 0; j < mDrawRows; j++)
404
    {
405
        const int intY0 = j * mBoxHeight;
406
        int itemIndex = j * mGridColumns - 1;
407
        for (int i = 0; i < mGridColumns; i++)
408
        {
409
            int itemX = i * mBoxWidth;
410
            int itemY = intY0;
411
            itemIndex ++;
412
            if (mShowMatrix[itemIndex] < 0)
413
                continue;
414
415
            const Item *const item = mInventory->getItem(
416
                mShowMatrix[itemIndex]);
417
418
            if ((item == nullptr) || item->getId() == 0)
419
                continue;
420
421
            // Draw item caption
422
            std::string caption;
423
            if (item->getQuantity() > 1 ||
424
                mForceQuantity == ForceQuantity_true)
425
            {
426
                caption = toString(item->getQuantity());
427
            }
428
            else if (item->isEquipped() == Equipped_true)
429
            {
430
                // TRANSLATORS: Text under equipped items (should be small)
431
                caption = _("Eq.");
432
            }
433
434
            if (item->isEquipped() == Equipped_true)
435
            {
436
                font->drawString(graphics,
437
                    mEquipedColor, mEquipedColor2,
438
                    caption,
439
                    itemX + (mBoxWidth - font->getWidth(caption)) / 2,
440
                    itemY + mEquippedTextPadding);
441
            }
442
            else
443
            {
444
                font->drawString(graphics,
445
                    mUnEquipedColor, mUnEquipedColor2,
446
                    caption,
447
                    itemX + (mBoxWidth - font->getWidth(caption)) / 2,
448
                    itemY + mEquippedTextPadding);
449
            }
450
        }
451
    }
452
    BLOCK_END("ItemContainer::draw")
453
}
454
455
void ItemContainer::safeDraw(Graphics *const graphics)
456
{
457
    if ((mInventory == nullptr) || (mShowMatrix == nullptr))
458
        return;
459
460
    BLOCK_START("ItemContainer::draw")
461
    Font *const font = getFont();
462
463
    if (mCellBackgroundImg != nullptr)
464
    {
465
        const int invSize = mInventory->getSize();
466
        const int maxRows = mShowEmptyRows == ShowEmptyRows_true ?
467
            std::max(invSize / mGridColumns, mGridRows) : mGridRows;
468
        const int maxColumns = mGridColumns > invSize ?
469
            invSize : mGridColumns;
470
        for (int j = 0; j < maxRows; j++)
471
        {
472
            const int intY0 = j * mBoxHeight;
473
            for (int i = 0; i < maxColumns; i++)
474
            {
475
                int itemX = i * mBoxWidth;
476
                int itemY = intY0;
477
                graphics->drawImage(mCellBackgroundImg,
478
                    itemX + mPaddingItemX,
479
                    itemY + mPaddingItemY);
480
            }
481
        }
482
    }
483
484
    for (int j = 0; j < mDrawRows; j++)
485
    {
486
        const int intY0 = j * mBoxHeight;
487
        int itemIndex = j * mGridColumns - 1;
488
        for (int i = 0; i < mGridColumns; i++)
489
        {
490
            int itemX = i * mBoxWidth;
491
            int itemY = intY0;
492
            itemIndex ++;
493
            if (mShowMatrix[itemIndex] < 0)
494
                continue;
495
496
            const Item *const item = mInventory->getItem(
497
                mShowMatrix[itemIndex]);
498
499
            if ((item == nullptr) || item->getId() == 0)
500
                continue;
501
502
            Image *const image = item->getImage();
503
            if (image != nullptr)
504
            {
505
                if (mShowMatrix[itemIndex] == mSelectedIndex)
506
                {
507
                    if (mSelImg != nullptr)
508
                        graphics->drawImage(mSelImg, itemX, itemY);
509
                }
510
                image->setAlpha(1.0F);  // ensure the image if fully drawn...
511
                graphics->drawImage(image,
512
                    itemX + mPaddingItemX,
513
                    itemY + mPaddingItemY);
514
                if ((mProtectedImg != nullptr) && PlayerInfo::isItemProtected(
515
                    item->getId()))
516
                {
517
                    graphics->drawImage(mProtectedImg,
518
                        itemX + mPaddingItemX,
519
                        itemY + mPaddingItemY);
520
                }
521
            }
522
        }
523
    }
524
525
    for (int j = 0; j < mDrawRows; j++)
526
    {
527
        const int intY0 = j * mBoxHeight;
528
        int itemIndex = j * mGridColumns - 1;
529
        for (int i = 0; i < mGridColumns; i++)
530
        {
531
            int itemX = i * mBoxWidth;
532
            int itemY = intY0;
533
            itemIndex ++;
534
            if (mShowMatrix[itemIndex] < 0)
535
                continue;
536
537
            const Item *const item = mInventory->getItem(
538
                mShowMatrix[itemIndex]);
539
540
            if ((item == nullptr) || item->getId() == 0)
541
                continue;
542
543
            // Draw item caption
544
            std::string caption;
545
            if (item->getQuantity() > 1 ||
546
                mForceQuantity == ForceQuantity_true)
547
            {
548
                caption = toString(item->getQuantity());
549
            }
550
            else if (item->isEquipped() == Equipped_true)
551
            {
552
                // TRANSLATORS: Text under equipped items (should be small)
553
                caption = _("Eq.");
554
            }
555
556
            if (item->isEquipped() == Equipped_true)
557
            {
558
                font->drawString(graphics,
559
                    mEquipedColor, mEquipedColor2,
560
                    caption,
561
                    itemX + (mBoxWidth - font->getWidth(caption)) / 2,
562
                    itemY + mEquippedTextPadding);
563
            }
564
            else
565
            {
566
                font->drawString(graphics,
567
                    mUnEquipedColor, mUnEquipedColor2,
568
                    caption,
569
                    itemX + (mBoxWidth - font->getWidth(caption)) / 2,
570
                    itemY + mEquippedTextPadding);
571
            }
572
        }
573
    }
574
    BLOCK_END("ItemContainer::draw")
575
}
576
577
void ItemContainer::selectNone()
578
{
579
    dragDrop.clear();
580
581
    setSelectedIndex(-1);
582
    mSelectionStatus = SEL_NONE;
583
/*
584
    if (outfitWindow)
585
        outfitWindow->setItemSelected(-1);
586
    if (shopWindow)
587
        shopWindow->setItemSelected(-1);
588
*/
589
}
590
591
void ItemContainer::setSelectedIndex(const int newIndex)
592
{
593
    if (mSelectedIndex != newIndex)
594
    {
595
        mSelectedIndex = newIndex;
596
        distributeValueChangedEvent();
597
    }
598
}
599
600
1
Item *ItemContainer::getSelectedItem() const
601
{
602
1
    if (mInventory != nullptr)
603
1
        return mInventory->getItem(mSelectedIndex);
604
    return nullptr;
605
}
606
607
void ItemContainer::distributeValueChangedEvent()
608
{
609
    FOR_EACH (SelectionListenerIterator, i, mSelectionListeners)
610
    {
611
        if (*i != nullptr)
612
        {
613
            SelectionEvent event(this);
614
            (*i)->valueChanged(event);
615
        }
616
    }
617
}
618
619
void ItemContainer::keyPressed(KeyEvent &event A_UNUSED)
620
{
621
}
622
623
void ItemContainer::keyReleased(KeyEvent &event A_UNUSED)
624
{
625
}
626
627
void ItemContainer::mousePressed(MouseEvent &event)
628
{
629
    if (mInventory == nullptr)
630
        return;
631
632
    const MouseButtonT button = event.getButton();
633
    mClicks = event.getClickCount();
634
635
    if (button == MouseButton::LEFT || button == MouseButton::RIGHT)
636
    {
637
        event.consume();
638
        const int index = getSlotIndex(event.getX(), event.getY());
639
        if (index == Inventory::NO_SLOT_INDEX)
640
            return;
641
642
        Item *const item = mInventory->getItem(index);
643
644
        // put item name into chat window
645
        if ((item != nullptr) && mDescItems && (chatWindow != nullptr))
646
            chatWindow->addItemText(item->getInfo().getName());
647
648
        DragDropSourceT src = DragDropSource::Empty;
649
        switch (mInventory->getType())
650
        {
651
            case InventoryType::Inventory:
652
                src = DragDropSource::Inventory;
653
                break;
654
            case InventoryType::Storage:
655
                src = DragDropSource::Storage;
656
                break;
657
            case InventoryType::Trade:
658
                src = DragDropSource::Trade;
659
                break;
660
            case InventoryType::Npc:
661
                src = DragDropSource::Npc;
662
                break;
663
            case InventoryType::Cart:
664
                src = DragDropSource::Cart;
665
                break;
666
            case InventoryType::MailEdit:
667
                src = DragDropSource::MailEdit;
668
                break;
669
            case InventoryType::MailView:
670
                src = DragDropSource::MailView;
671
                break;
672
            case InventoryType::Craft:
673
                src = DragDropSource::Craft;
674
                break;
675
            default:
676
            case InventoryType::Vending:
677
            case InventoryType::TypeEnd:
678
                break;
679
        }
680
        if (src == DragDropSource::MailView)
681
            return;
682
        if (mSelectedIndex == index && mClicks != 2)
683
        {
684
            dragDrop.dragItem(item, src, index);
685
            dragDrop.select(item);
686
            mSelectionStatus = SEL_DESELECTING;
687
        }
688
        else if ((item != nullptr) && (item->getId() != 0))
689
        {
690
            if (index >= 0)
691
            {
692
                dragDrop.dragItem(item, src, index);
693
                dragDrop.select(item);
694
                setSelectedIndex(index);
695
                mSelectionStatus = SEL_SELECTING;
696
697
                if (itemShortcutWindow != nullptr)
698
                {
699
                    const int num = itemShortcutWindow->getTabIndex();
700
                    if (num >= 0 && num < CAST_S32(SHORTCUT_TABS))
701
                    {
702
                        if (itemShortcut[num] != nullptr)
703
                            itemShortcut[num]->setItemSelected(item);
704
                    }
705
                }
706
                if (shopWindow != nullptr)
707
                    shopWindow->setItemSelected(item->getId());
708
            }
709
        }
710
        else
711
        {
712
            dragDrop.deselect();
713
            selectNone();
714
        }
715
    }
716
}
717
718
void ItemContainer::mouseDragged(MouseEvent &event A_UNUSED)
719
{
720
    if (mSelectionStatus != SEL_NONE)
721
        mSelectionStatus = SEL_DRAGGING;
722
}
723
724
void ItemContainer::mouseReleased(MouseEvent &event)
725
{
726
    if (mClicks == 2 ||
727
        inventoryHandler == nullptr ||
728
        tradeHandler == nullptr)
729
    {
730
        return;
731
    }
732
733
    switch (mSelectionStatus)
734
    {
735
        case SEL_SELECTING:
736
            mSelectionStatus = SEL_SELECTED;
737
            break;
738
        case SEL_DESELECTING:
739
            selectNone();
740
            break;
741
        case SEL_DRAGGING:
742
            mSelectionStatus = SEL_SELECTED;
743
            break;
744
        case SEL_NONE:
745
        case SEL_SELECTED:
746
        default:
747
            break;
748
    }
749
750
    if (dragDrop.isEmpty())
751
    {
752
        const int index = getSlotIndex(event.getX(), event.getY());
753
        if (index == Inventory::NO_SLOT_INDEX)
754
            return;
755
        if (index == mSelectedIndex || mSelectedIndex == -1)
756
            return;
757
        inventoryHandler->moveItem(mSelectedIndex, index);
758
        selectNone();
759
    }
760
    else if (mInventory != nullptr)
761
    {
762
        const DragDropSourceT src = dragDrop.getSource();
763
        DragDropSourceT dst = DragDropSource::Empty;
764
        switch (mInventory->getType())
765
        {
766
            case InventoryType::Inventory:
767
                dst = DragDropSource::Inventory;
768
                break;
769
            case InventoryType::Storage:
770
                dst = DragDropSource::Storage;
771
                break;
772
            case InventoryType::Trade:
773
                dst = DragDropSource::Trade;
774
                break;
775
            case InventoryType::Npc:
776
                dst = DragDropSource::Npc;
777
                break;
778
            case InventoryType::MailEdit:
779
                dst = DragDropSource::MailEdit;
780
                break;
781
            case InventoryType::MailView:
782
                dst = DragDropSource::MailView;
783
                break;
784
            case InventoryType::Cart:
785
                dst = DragDropSource::Cart;
786
                break;
787
            case InventoryType::Craft:
788
                dst = DragDropSource::Craft;
789
                break;
790
            default:
791
            case InventoryType::Vending:
792
            case InventoryType::TypeEnd:
793
                break;
794
        }
795
        InventoryTypeT srcContainer = InventoryType::TypeEnd;
796
        InventoryTypeT dstContainer = InventoryType::TypeEnd;
797
        bool checkProtection(false);
798
        Inventory *inventory = nullptr;
799
        if (src == DragDropSource::Inventory
800
            && dst == DragDropSource::Storage)
801
        {
802
            srcContainer = InventoryType::Inventory;
803
            dstContainer = InventoryType::Storage;
804
            inventory = PlayerInfo::getInventory();
805
        }
806
        else if (src == DragDropSource::Storage
807
                 && dst == DragDropSource::Inventory)
808
        {
809
            srcContainer = InventoryType::Storage;
810
            dstContainer = InventoryType::Inventory;
811
            inventory = PlayerInfo::getStorageInventory();
812
        }
813
        if (src == DragDropSource::Inventory
814
            && dst == DragDropSource::Cart)
815
        {
816
            srcContainer = InventoryType::Inventory;
817
            dstContainer = InventoryType::Cart;
818
            inventory = PlayerInfo::getInventory();
819
        }
820
        if (src == DragDropSource::Inventory
821
            && dst == DragDropSource::Inventory)
822
        {
823
            if (Net::getNetworkType() == ServerType::TMWATHENA)
824
                return;
825
            const int index = getSlotIndex(event.getX(), event.getY());
826
            if (index == Inventory::NO_SLOT_INDEX)
827
                return;
828
            if (index == mSelectedIndex || mSelectedIndex == -1)
829
                return;
830
            if (inventoryWindow != nullptr)
831
                inventoryWindow->combineItems(index, mSelectedIndex);
832
            return;
833
        }
834
        else if (src == DragDropSource::Cart
835
                 && dst == DragDropSource::Inventory)
836
        {
837
            srcContainer = InventoryType::Cart;
838
            dstContainer = InventoryType::Inventory;
839
            inventory = PlayerInfo::getCartInventory();
840
        }
841
        else if (src == DragDropSource::Cart
842
                 && dst == DragDropSource::Storage)
843
        {
844
            srcContainer = InventoryType::Cart;
845
            dstContainer = InventoryType::Storage;
846
            inventory = PlayerInfo::getCartInventory();
847
        }
848
        else if (src == DragDropSource::Storage
849
                 && dst == DragDropSource::Cart)
850
        {
851
            srcContainer = InventoryType::Storage;
852
            dstContainer = InventoryType::Cart;
853
            inventory = PlayerInfo::getStorageInventory();
854
        }
855
        if (src == DragDropSource::Inventory
856
            && dst == DragDropSource::Trade)
857
        {
858
            checkProtection = true;
859
            inventory = PlayerInfo::getInventory();
860
        }
861
        else if (src == DragDropSource::Inventory &&
862
                 dst == DragDropSource::Npc)
863
        {
864
            inventory = PlayerInfo::getInventory();
865
            if (inventory != nullptr)
866
            {
867
                Item *const item = inventory->getItem(dragDrop.getTag());
868
                if (mInventory->addVirtualItem(
869
                    item,
870
                    getSlotByXY(event.getX(), event.getY()),
871
                    1))
872
                {
873
                    inventory->virtualRemove(item, 1);
874
                }
875
            }
876
            return;
877
        }
878
        else if (src == DragDropSource::Inventory &&
879
                 dst == DragDropSource::MailEdit)
880
        {
881
            inventory = PlayerInfo::getInventory();
882
            if (inventory == nullptr)
883
                return;
884
            Item *const item = inventory->getItem(dragDrop.getTag());
885
            if (item == nullptr)
886
                return;
887
            if (settings.enableNewMailSystem)
888
            {
889
                if (item->getQuantity() > 1
890
                    && !inputManager.isActionActive(InputAction::STOP_ATTACK))
891
                {
892
                    ItemAmountWindow::showWindow(
893
                        ItemAmountWindowUsage::MailAdd,
894
                        mailEditWindow,
895
                        item,
896
                        0,
897
                        0);
898
                }
899
                else
900
                {
901
                    mail2Handler->addItem(item, 1);
902
                }
903
            }
904
            else
905
            {
906
                if (mInventory->addVirtualItem(
907
                    item,
908
                    getSlotByXY(event.getX(), event.getY()),
909
                    1))
910
                {
911
                    inventory->virtualRemove(item, 1);
912
                }
913
            }
914
            return;
915
        }
916
        else if (src == DragDropSource::Npc)
917
        {
918
            inventory = PlayerInfo::getInventory();
919
            if (dst == DragDropSource::Npc)
920
            {
921
                const Item *const item = mInventory->getItem(
922
                    dragDrop.getTag());
923
                const int index = getSlotByXY(event.getX(), event.getY());
924
                if (index == Inventory::NO_SLOT_INDEX)
925
                {
926
                    if (inventory != nullptr)
927
                        inventory->virtualRestore(item, 1);
928
                    mInventory->removeItemAt(dragDrop.getTag());
929
                    return;
930
                }
931
                mInventory->removeItemAt(index);
932
                mInventory->setItem(index,
933
                    item->getId(),
934
                    item->getType(),
935
                    1,
936
                    1,
937
                    item->getColor(),
938
                    item->getIdentified(),
939
                    item->getDamaged(),
940
                    item->getFavorite(),
941
                    Equipm_false,
942
                    Equipped_false);
943
                Item *const item2 = mInventory->getItem(index);
944
                if (item2 != nullptr)
945
                    item2->setTag(item->getTag());
946
                mInventory->removeItemAt(dragDrop.getTag());
947
            }
948
            else
949
            {
950
                if (inventory != nullptr)
951
                {
952
                    const Item *const item = inventory->getItem(
953
                        dragDrop.getTag());
954
                    if (item != nullptr)
955
                    {
956
                        inventory->virtualRestore(item, 1);
957
                        mInventory->removeItemAt(dragDrop.getTag());
958
                    }
959
                }
960
                return;
961
            }
962
        }
963
        else if (src == DragDropSource::Inventory &&
964
                 dst == DragDropSource::Craft)
965
        {
966
            inventory = PlayerInfo::getInventory();
967
            if (inventory != nullptr)
968
            {
969
                Item *const item = inventory->getItem(dragDrop.getTag());
970
                if ((item == nullptr) || item->isEquipped() == Equipped_true)
971
                    return;
972
                const int slot = getSlotByXY(event.getX(), event.getY());
973
                if (item->getQuantity() > 1
974
                    && !inputManager.isActionActive(InputAction::STOP_ATTACK))
975
                {
976
                    ItemAmountWindow::showWindow(
977
                        ItemAmountWindowUsage::CraftAdd,
978
                        npcHandler->getCurrentNpcDialog(),
979
                        item,
980
                        0,
981
                        slot);
982
                }
983
                else
984
                {
985
                    if (mInventory->addVirtualItem(
986
                        item,
987
                        slot,
988
                        1))
989
                    {
990
                        inventory->virtualRemove(item, 1);
991
                    }
992
                }
993
            }
994
            return;
995
        }
996
        else if (src == DragDropSource::Craft)
997
        {
998
            inventory = PlayerInfo::getInventory();
999
            if (dst == DragDropSource::Craft)
1000
            {
1001
                const Item *const item = mInventory->getItem(
1002
                    dragDrop.getTag());
1003
                const int index = getSlotByXY(event.getX(), event.getY());
1004
                if (index == Inventory::NO_SLOT_INDEX)
1005
                {
1006
                    if (inventory != nullptr)
1007
                    {
1008
                        inventory->virtualRestore(item,
1009
                            item->getQuantity());
1010
                        mInventory->removeItemAt(dragDrop.getTag());
1011
                    }
1012
                    return;
1013
                }
1014
                mInventory->moveItem(index, dragDrop.getTag());
1015
            }
1016
            else
1017
            {
1018
                if (inventory != nullptr)
1019
                {
1020
                    const Item *const item = inventory->getItem(
1021
                        dragDrop.getTag());
1022
                    if (item != nullptr)
1023
                    {
1024
                        inventory->virtualRestore(item,
1025
                            item->getQuantity());
1026
                        mInventory->removeItemAt(dragDrop.getTag());
1027
                    }
1028
                }
1029
                return;
1030
            }
1031
        }
1032
        else if (src == DragDropSource::MailEdit)
1033
        {
1034
            if (event.getType() == MouseEventType::RELEASED2)
1035
                return;
1036
            if (settings.enableNewMailSystem)
1037
            {
1038
                if (mailEditWindow == nullptr)
1039
                    return;
1040
                inventory = mailEditWindow->getInventory();
1041
                if (inventory == nullptr)
1042
                    return;
1043
                const Item *const item = inventory->getItem(dragDrop.getTag());
1044
                if (item == nullptr)
1045
                    return;
1046
                mail2Handler->removeItem(item->getTag(),
1047
                    item->getQuantity());
1048
            }
1049
            else
1050
            {
1051
                inventory = PlayerInfo::getInventory();
1052
                if (inventory == nullptr)
1053
                    return;
1054
                const Item *const item = inventory->getItem(dragDrop.getTag());
1055
                if (item == nullptr)
1056
                    return;
1057
                mInventory->removeItemAt(dragDrop.getTag());
1058
            }
1059
            return;
1060
        }
1061
1062
        if (inventory != nullptr)
1063
        {
1064
            const Item *const item = inventory->getItem(dragDrop.getTag());
1065
            if (item != nullptr)
1066
            {
1067
                if (srcContainer != InventoryType::TypeEnd)
1068
                {   // inventory <--> storage, cart
1069
                    inventoryHandler->moveItem2(srcContainer,
1070
                        item->getInvIndex(),
1071
                        item->getQuantity(),
1072
                        dstContainer);
1073
                }
1074
                else
1075
                {   // inventory --> trade
1076
                    if (!checkProtection || !PlayerInfo::isItemProtected(
1077
                        item->getId()))
1078
                    {
1079
                        tradeHandler->addItem(item, item->getQuantity());
1080
                    }
1081
                }
1082
            }
1083
        }
1084
    }
1085
}
1086
1087
void ItemContainer::mouseMoved(MouseEvent &event)
1088
{
1089
    if (mInventory == nullptr)
1090
        return;
1091
1092
    const Item *const item = mInventory->getItem(
1093
        getSlotIndex(event.getX(), event.getY()));
1094
1095
    if ((item != nullptr) && (viewport != nullptr))
1096
    {
1097
        itemPopup->setItem(item, false);
1098
        itemPopup->position(viewport->mMouseX, viewport->mMouseY);
1099
    }
1100
    else
1101
    {
1102
        itemPopup->setVisible(Visible_false);
1103
    }
1104
}
1105
1106
void ItemContainer::mouseExited(MouseEvent &event A_UNUSED)
1107
{
1108
    itemPopup->setVisible(Visible_false);
1109
}
1110
1111
void ItemContainer::widgetResized(const Event &event A_UNUSED)
1112
{
1113
    updateSize();
1114
}
1115
1116
void ItemContainer::updateSize()
1117
{
1118
    mGridColumns = std::min(mMaxColumns,
1119
        std::max(1, mDimension.width / mBoxWidth));
1120
    if (mGridColumns > mMaxColumns)
1121
        mGridColumns = mMaxColumns;
1122
    adjustHeight();
1123
    mRedraw = true;
1124
}
1125
1126
void ItemContainer::widgetMoved(const Event &event A_UNUSED)
1127
{
1128
    mRedraw = true;
1129
}
1130
1131
void ItemContainer::adjustHeight()
1132
{
1133
    if (mGridColumns == 0)
1134
        return;
1135
1136
    mGridRows = (mLastUsedSlot + 1) / mGridColumns;
1137
    if (mGridRows == 0 || (mLastUsedSlot + 1) % mGridColumns > 0)
1138
        ++mGridRows;
1139
1140
    const int invSize = mInventory->getSize();
1141
    int maxRows = mShowEmptyRows == ShowEmptyRows_true ?
1142
        std::max(invSize / mGridColumns, mGridRows) : mGridRows;
1143
1144
    if (mShowEmptyRows == ShowEmptyRows_true)
1145
    {
1146
        if (mGridColumns * maxRows < invSize)
1147
            maxRows ++;
1148
        mGridRows = maxRows;
1149
    }
1150
1151
    const int num = updateMatrix();
1152
    if (mShowEmptyRows == ShowEmptyRows_false)
1153
    {
1154
        mDrawRows = num / mGridColumns;
1155
        if (mDrawRows == 0 || num % mGridColumns > 0)
1156
            ++mDrawRows;
1157
1158
        maxRows = mDrawRows;
1159
    }
1160
    else
1161
    {
1162
        mDrawRows = mGridRows;
1163
    }
1164
    setHeight(maxRows * mBoxHeight);
1165
}
1166
1167
3
int ItemContainer::updateMatrix()
1168
{
1169
3
    if (mInventory == nullptr)
1170
        return 0;
1171
1172
3
    mRedraw = true;
1173
3
    delete []mShowMatrix;
1174
3
    mShowMatrix = new int[CAST_SIZE(mGridRows * mGridColumns)];
1175
1176
3
    STD_VECTOR<ItemIdPair*> sortedItems;
1177
3
    int i = 0;
1178
3
    int j = 0;
1179
1180
9
    std::string temp = mName;
1181
3
    toLower(temp);
1182
1183
6
    const unsigned int invSize = mInventory->getSize();
1184
303
    for (unsigned int idx = 0; idx < invSize; idx ++)
1185
    {
1186
300
        Item *const item = mInventory->getItem(idx);
1187
1188

300
        if (item == nullptr ||
1189
            item->getId() == 0 ||
1190

300
            !item->isHaveTag(mTag) ||
1191
            item->getQuantity() == 0)
1192
        {
1193
300
            if (mShowEmptyRows == ShowEmptyRows_true)
1194
                sortedItems.push_back(new ItemIdPair(idx, nullptr));
1195
300
            continue;
1196
        }
1197
1198
        if (!item->isHaveTag(mTag))
1199
            continue;
1200
1201
        if (mName.empty())
1202
        {
1203
            sortedItems.push_back(new ItemIdPair(idx, item));
1204
            continue;
1205
        }
1206
        std::string name = item->getInfo().getName();
1207
        toLower(name);
1208
        if (name.find(temp) != std::string::npos)
1209
            sortedItems.push_back(new ItemIdPair(idx, item));
1210
    }
1211
1212

3
    switch (mSortType)
1213
    {
1214
        case 0:
1215
        default:
1216
            break;
1217
        case 1:
1218
            std::sort(sortedItems.begin(), sortedItems.end(),
1219
                itemAlphaInvSorter);
1220
            break;
1221
        case 2:
1222
            std::sort(sortedItems.begin(), sortedItems.end(), itemIdInvSorter);
1223
            break;
1224
        case 3:
1225
            std::sort(sortedItems.begin(), sortedItems.end(),
1226
                itemWeightInvSorter);
1227
            break;
1228
        case 4:
1229
            std::sort(sortedItems.begin(), sortedItems.end(),
1230
                itemAmountInvSorter);
1231
            break;
1232
        case 5:
1233
            std::sort(sortedItems.begin(), sortedItems.end(),
1234
                itemTypeInvSorter);
1235
            break;
1236
    }
1237
1238
3
    int jMult = j * mGridColumns;
1239
3
    const int maxSize = mGridRows * mGridColumns;
1240
15
    FOR_EACH (STD_VECTOR<ItemIdPair*>::const_iterator, iter, sortedItems)
1241
    {
1242
        if (jMult >= maxSize)
1243
            break;
1244
1245
        mShowMatrix[jMult + i] = (*iter)->mId;
1246
1247
        i ++;
1248
        if (i >= mGridColumns)
1249
        {
1250
            i = 0;
1251
            j ++;
1252
            jMult += mGridColumns;
1253
        }
1254
    }
1255
1256
6
    for (int idx = j * mGridColumns + i; idx < maxSize; idx ++)
1257
3
        mShowMatrix[idx] = -1;
1258
1259
3
    const int num = CAST_S32(sortedItems.size());
1260
3
    for (size_t idx = 0, sz = num; idx < sz; idx ++)
1261
        delete sortedItems[idx];
1262
3
    return num;
1263
}
1264
1265
int ItemContainer::getSlotIndex(int x, int y) const
1266
{
1267
    if (mShowMatrix == nullptr)
1268
        return Inventory::NO_SLOT_INDEX;
1269
1270
    if (x < mDimension.width && y < mDimension.height && x >= 0 && y >= 0)
1271
    {
1272
        if (x > mBoxWidth * mGridColumns)
1273
            return Inventory::NO_SLOT_INDEX;
1274
        const int idx = (y / mBoxHeight) * mGridColumns + (x / mBoxWidth);
1275
        if (idx >= 0 && idx < mGridRows * mGridColumns
1276
            && mShowMatrix[idx] >= 0)
1277
        {
1278
            return mShowMatrix[idx];
1279
        }
1280
    }
1281
1282
    return Inventory::NO_SLOT_INDEX;
1283
}
1284
1285
int ItemContainer::getSlotByXY(int x, int y) const
1286
{
1287
    if (mShowMatrix == nullptr)
1288
        return Inventory::NO_SLOT_INDEX;
1289
1290
    if (x < mDimension.width && y < mDimension.height && x >= 0 && y >= 0)
1291
    {
1292
        if (x > mBoxWidth * mGridColumns)
1293
            return Inventory::NO_SLOT_INDEX;
1294
        const int idx = (y / mBoxHeight) * mGridColumns + (x / mBoxWidth);
1295
        if (idx >= 0 && idx < mGridRows * mGridColumns)
1296
            return idx;
1297
    }
1298
1299
    return Inventory::NO_SLOT_INDEX;
1300
}
1301
1302
void ItemContainer::setFilter(const int tag)
1303
{
1304
    mTag = tag;
1305
    adjustHeight();
1306
}
1307
1308
2
void ItemContainer::setSortType(const int sortType)
1309
{
1310
2
    mSortType = sortType;
1311
2
    updateMatrix();
1312
2
}
1313
1314
void ItemContainer::setCellBackgroundImage(const std::string &xmlName)
1315
{
1316
    if (mCellBackgroundImg != nullptr)
1317
        mCellBackgroundImg->decRef();
1318
    mCellBackgroundImg = Theme::getImageFromThemeXml(xmlName, "");
1319
    mRedraw = true;
1320
}
1321
1322
void ItemContainer::setMaxColumns(const int maxColumns)
1323
{
1324
    mMaxColumns = maxColumns;
1325
    updateSize();
1326
2
}