GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/widgets/itemcontainer.cpp Lines: 74 605 12.2 %
Date: 2017-11-29 Branches: 67 629 10.7 %

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/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
12
ItemContainer::ItemContainer(const Widget2 *const widget,
225
                             Inventory *const inventory,
226
                             const int maxColumns,
227
                             const ShowEmptyRows showEmptyRows,
228
12
                             const ForceQuantity forceQuantity) :
229
    Widget(widget),
230
    KeyListener(),
231
    MouseListener(),
232
    WidgetListener(),
233
    mInventory(inventory),
234

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

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

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





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

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


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


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


48
    mEquippedTextPadding(mSkin != nullptr ? mSkin->getOption(
257
                         "equippedTextPadding", 29) : 29),
258


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


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

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

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

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