GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/windows/tradewindow.cpp Lines: 95 243 39.1 %
Date: 2018-06-18 21:15:20 Branches: 90 325 27.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-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/windows/tradewindow.h"
24
25
#include "configuration.h"
26
#include "game.h"
27
28
#include "being/localplayer.h"
29
#include "being/playerinfo.h"
30
#include "being/playerrelations.h"
31
32
#include "enums/gui/layouttype.h"
33
34
#include "gui/gui.h"
35
36
#include "gui/fonts/font.h"
37
38
#include "gui/windows/inventorywindow.h"
39
#include "gui/windows/itemamountwindow.h"
40
#include "gui/windows/setupwindow.h"
41
42
#include "gui/widgets/button.h"
43
#include "gui/widgets/containerplacer.h"
44
#include "gui/widgets/itemcontainer.h"
45
#include "gui/widgets/label.h"
46
#include "gui/widgets/layout.h"
47
#include "gui/widgets/scrollarea.h"
48
#include "gui/widgets/textfield.h"
49
50
#include "gui/widgets/tabs/chat/chattab.h"
51
52
#include "resources/db/unitsdb.h"
53
54
#include "resources/item/item.h"
55
56
#include "net/net.h"
57
#include "net/tradehandler.h"
58
59
#include "utils/delete2.h"
60
#include "utils/gettext.h"
61
62
#include "debug.h"
63
64
TradeWindow *tradeWindow = nullptr;
65
66
// TRANSLATORS: trade window button
67
#define CAPTION_PROPOSE _("Propose trade")
68
// TRANSLATORS: trade window button
69
#define CAPTION_CONFIRMED _("Confirmed. Waiting...")
70
// TRANSLATORS: trade window button
71
#define CAPTION_ACCEPT _("Agree trade")
72
// TRANSLATORS: trade window button
73
#define CAPTION_ACCEPTED _("Agreed. Waiting...")
74
75
2
TradeWindow::TradeWindow() :
76
    // TRANSLATORS: trade window caption
77
2
    Window(_("Trade: You"), Modal_false, nullptr, "trade.xml"),
78
    ActionListener(),
79
    SelectionListener(),
80

2
    mMyInventory(new Inventory(InventoryType::Trade, -1)),
81

2
    mPartnerInventory(new Inventory(InventoryType::Trade, -1)),
82
    mMyItemContainer(new ItemContainer(this, mMyInventory, 100000,
83

2
        ShowEmptyRows_false, ForceQuantity_false)),
84
    mPartnerItemContainer(new ItemContainer(this, mPartnerInventory, 100000,
85

2
        ShowEmptyRows_false, ForceQuantity_false)),
86
    // TRANSLATORS: trade window money label
87

4
    mMoneyLabel(new Label(this, strprintf(_("You get %s"), ""))),
88
    // TRANSLATORS: trade window button
89

4
    mAddButton(new Button(this, _("Add"), "add", BUTTON_SKIN, this)),
90
    mOkButton(new Button(this, "", "",
91

2
        BUTTON_SKIN, this)),  // Will be filled in later
92
    // TRANSLATORS: trade window money change button
93
2
    mMoneyChangeButton(new Button(this, _("Change"), "money",
94

2
        BUTTON_SKIN, this)),
95
4
    mMoneyField(new TextField(this, std::string(),
96

4
        LoseFocusOnTab_true, nullptr, std::string(), false)),
97
    mAutoAddItem(nullptr),
98
    mAutoAddToNick(""),
99
    mGotMoney(0),
100
    mGotMaxMoney(0),
101
    mAutoMoney(0),
102
    mAutoAddAmount(0),
103
    mStatus(PROPOSING),
104
    mOkOther(false),
105





80
    mOkMe(false)
106
{
107
10
    setWindowName("Trade");
108
2
    setResizable(true);
109
2
    setCloseButton(true);
110
2
    setStickyButtonLock(true);
111
2
    setDefaultSize(386, 180, ImagePosition::CENTER, 0, 0);
112
2
    setMinWidth(310);
113
2
    setMinHeight(180);
114
115
2
    if (setupWindow != nullptr)
116
        setupWindow->registerWindowForReset(this);
117
118
2
    const Font *const fnt = mOkButton->getFont();
119

12
    int width = std::max(fnt->getWidth(CAPTION_PROPOSE),
120

14
        fnt->getWidth(CAPTION_CONFIRMED));
121

10
    width = std::max(width, fnt->getWidth(CAPTION_ACCEPT));
122

10
    width = std::max(width, fnt->getWidth(CAPTION_ACCEPTED));
123
124
2
    mOkButton->setWidth(8 + width);
125
126
4
    mMyItemContainer->addSelectionListener(this);
127
128
2
    ScrollArea *const myScroll = new ScrollArea(this, mMyItemContainer,
129

10
        Opaque_true, "trade_background.xml");
130
2
    myScroll->setHorizontalScrollPolicy(ScrollArea::SHOW_NEVER);
131
132
4
    mPartnerItemContainer->addSelectionListener(this);
133
134
    ScrollArea *const partnerScroll = new ScrollArea(this,
135
2
        mPartnerItemContainer,
136

10
        Opaque_true, "trade_background.xml");
137
2
    partnerScroll->setHorizontalScrollPolicy(ScrollArea::SHOW_NEVER);
138
139
    // TRANSLATORS: trade window money label
140

8
    Label *const moneyLabel2 = new Label(this, _("You give:"));
141
142
2
    mMoneyField->setWidth(40);
143
144
2
    place(1, 0, mMoneyLabel, 1, 1);
145
4
    place(0, 1, myScroll, 1, 1).setPadding(3);
146
4
    place(1, 1, partnerScroll, 1, 1).setPadding(3);
147
2
    ContainerPlacer placer = getPlacer(0, 0);
148
2
    placer(0, 0, moneyLabel2, 1, 1);
149
2
    placer(1, 0, mMoneyField, 2, 1);
150
4
    placer(3, 0, mMoneyChangeButton, 1, 1).setHAlign(LayoutCell::LEFT);
151
2
    placer = getPlacer(0, 2);
152
2
    placer(0, 0, mAddButton, 1, 1);
153
2
    placer(1, 0, mOkButton, 1, 1);
154
2
    Layout &layout = getLayout();
155
2
    layout.extend(0, 2, 2, 1);
156
2
    layout.setRowHeight(1, LayoutType::SET);
157
2
    layout.setRowHeight(2, 0);
158
2
    layout.setColWidth(0, LayoutType::SET);
159
2
    layout.setColWidth(1, LayoutType::SET);
160
161
2
    loadWindowState();
162
4
    enableVisibleSound(true);
163
164
2
    reset();
165
2
}
166
167
12
TradeWindow::~TradeWindow()
168
{
169
2
    delete2(mMyInventory);
170
2
    delete2(mPartnerInventory);
171
4
}
172
173
2
void TradeWindow::setMoney(const int amount)
174
{
175

2
    if (amount < 0 || amount < mGotMaxMoney)
176
    {
177
        if (config.getBoolValue("securetrades"))
178
        {
179
            close();
180
            return;
181
        }
182
        mMoneyLabel->setForegroundColorAll(
183
            getThemeColor(ThemeColorId::WARNING, 255U),
184
            getThemeColor(ThemeColorId::WARNING_OUTLINE, 255U));
185
    }
186
    else
187
    {
188
6
        mMoneyLabel->setForegroundColorAll(
189
            getThemeColor(ThemeColorId::LABEL, 255U),
190
            getThemeColor(ThemeColorId::LABEL_OUTLINE, 255U));
191
2
        mGotMaxMoney = amount;
192
    }
193
194
2
    mGotMoney = amount;
195
    // TRANSLATORS: trade window money label
196

6
    mMoneyLabel->setCaption(strprintf(_("You get %s"),
197
4
        UnitsDb::formatCurrency(amount).c_str()));
198
2
    mMoneyLabel->adjustSize();
199
}
200
201
void TradeWindow::addItem(const int id,
202
                          const ItemTypeT type,
203
                          const bool own,
204
                          const int quantity,
205
                          const uint8_t refine,
206
                          const ItemColor color,
207
                          const Identified identified,
208
                          const Damaged damaged,
209
                          const Favorite favorite) const
210
{
211
    Inventory *const inv = own ? mMyInventory : mPartnerInventory;
212
    inv->addItem(id,
213
        type,
214
        quantity,
215
        refine,
216
        color,
217
        identified,
218
        damaged,
219
        favorite,
220
        Equipm_false,
221
        Equipped_false);
222
}
223
224
void TradeWindow::addItem2(const int id,
225
                           const ItemTypeT type,
226
                           const int *const cards,
227
                           const ItemOptionsList *const options,
228
                           const int sz,
229
                           const bool own,
230
                           const int quantity,
231
                           const uint8_t refine,
232
                           const ItemColor color,
233
                           const Identified identified,
234
                           const Damaged damaged,
235
                           const Favorite favorite,
236
                           const Equipm equipment) const
237
{
238
    Inventory *const inv = own ? mMyInventory : mPartnerInventory;
239
    const int slot = inv->addItem(id,
240
        type,
241
        quantity,
242
        refine,
243
        color,
244
        identified,
245
        damaged,
246
        favorite,
247
        equipment,
248
        Equipped_false);
249
    if (slot >= 0)
250
    {
251
        inv->setCards(slot, cards, sz);
252
        inv->setOptions(slot, options);
253
    }
254
}
255
256
void TradeWindow::changeQuantity(const int index, const bool own,
257
                                 const int quantity) const
258
{
259
    Item *item;
260
    if (own)
261
        item = mMyInventory->getItem(index);
262
    else
263
        item = mPartnerInventory->getItem(index);
264
    if (item != nullptr)
265
        item->setQuantity(quantity);
266
}
267
268
void TradeWindow::increaseQuantity(const int index, const bool own,
269
                                   const int quantity) const
270
{
271
    Item *item;
272
    if (own)
273
        item = mMyInventory->getItem(index);
274
    else
275
        item = mPartnerInventory->getItem(index);
276
    if (item != nullptr)
277
        item->increaseQuantity(quantity);
278
}
279
280
2
void TradeWindow::reset()
281
{
282
2
    mMyInventory->clear();
283
2
    mPartnerInventory->clear();
284
2
    mOkOther = false;
285
2
    mOkMe = false;
286
2
    setMoney(0);
287
4
    mMoneyField->setEnabled(true);
288

8
    mMoneyField->setText("");
289
6
    mMoneyLabel->setForegroundColorAll(
290
        getThemeColor(ThemeColorId::LABEL, 255U),
291
        getThemeColor(ThemeColorId::LABEL_OUTLINE, 255U));
292
4
    mAddButton->setEnabled(true);
293
4
    mMoneyChangeButton->setEnabled(true);
294
2
    mGotMoney = 0;
295
2
    mGotMaxMoney = 0;
296
2
    setStatus(PREPARING);
297
2
}
298
299
void TradeWindow::receivedOk(const bool own)
300
{
301
    if (own)
302
        mOkMe = true;
303
    else
304
        mOkOther = true;
305
306
    if (mOkMe && mOkOther)
307
        setStatus(ACCEPTING);
308
}
309
310
void TradeWindow::completeTrade()
311
{
312
    if (config.getBoolValue("tradescreenshot"))
313
        Game::createScreenshot(std::string());
314
    setVisible(Visible_false);
315
    reset();
316
}
317
318
void TradeWindow::tradeItem(const Item *const item, const int quantity,
319
                            const bool check) const
320
{
321
    if (check && !checkItem(item))
322
        return;
323
324
    tradeHandler->addItem(item, quantity);
325
}
326
327
void TradeWindow::valueChanged(const SelectionEvent &event)
328
{
329
    if ((mMyItemContainer == nullptr) || (mPartnerItemContainer == nullptr))
330
        return;
331
332
    /* If an item is selected in one container, make sure no item is selected
333
     * in the other container.
334
     */
335
    if (event.getSource() == mMyItemContainer &&
336
        (mMyItemContainer->getSelectedItem() != nullptr))
337
    {
338
        mPartnerItemContainer->selectNone();
339
    }
340
    else if (mPartnerItemContainer->getSelectedItem() != nullptr)
341
    {
342
        mMyItemContainer->selectNone();
343
    }
344
}
345
346
2
void TradeWindow::setStatus(const Status s)
347
{
348
2
    if (s == mStatus)
349
        return;
350
2
    mStatus = s;
351
352

2
    switch (s)
353
    {
354
2
        case PREPARING:
355
10
            mOkButton->setCaption(CAPTION_PROPOSE);
356
10
            mOkButton->setActionEventId("ok");
357
2
            break;
358
        case PROPOSING:
359
            mOkButton->setCaption(CAPTION_CONFIRMED);
360
            mOkButton->setActionEventId("");
361
            break;
362
        case ACCEPTING:
363
            mOkButton->setCaption(CAPTION_ACCEPT);
364
            mOkButton->setActionEventId("trade");
365
            break;
366
        case ACCEPTED:
367
            mOkButton->setCaption(CAPTION_ACCEPTED);
368
            mOkButton->setActionEventId("");
369
            break;
370
        default:
371
            break;
372
    }
373
374
2
    mOkButton->setEnabled((s != PROPOSING && s != ACCEPTED));
375
}
376
377
void TradeWindow::action(const ActionEvent &event)
378
{
379
    if (inventoryWindow == nullptr)
380
        return;
381
382
    Item *const item = inventoryWindow->getSelectedItem();
383
    const std::string &eventId = event.getId();
384
385
    if (eventId == "add")
386
    {
387
        if (mStatus != PREPARING)
388
            return;
389
390
        if (!inventoryWindow->isWindowVisible())
391
        {
392
            inventoryWindow->setVisible(Visible_true);
393
            return;
394
        }
395
396
        if (item == nullptr)
397
            return;
398
399
        if (mMyInventory->getFreeSlot() == -1)
400
            return;
401
402
        if (!checkItem(item))
403
            return;
404
405
        // Choose amount of items to trade
406
        ItemAmountWindow::showWindow(ItemAmountWindowUsage::TradeAdd,
407
            this,
408
            item,
409
            0,
410
            0);
411
412
        setStatus(PREPARING);
413
    }
414
    else if (eventId == "cancel")
415
    {
416
        setVisible(Visible_false);
417
        reset();
418
        PlayerInfo::setTrading(Trading_false);
419
        tradeHandler->cancel();
420
    }
421
    else if (eventId == "ok")
422
    {
423
        mMoneyField->setEnabled(false);
424
        mAddButton->setEnabled(false);
425
        mMoneyChangeButton->setEnabled(false);
426
        receivedOk(true);
427
        setStatus(PROPOSING);
428
        tradeHandler->confirm();
429
    }
430
    else if (eventId == "trade")
431
    {
432
        receivedOk(true);
433
        setStatus(ACCEPTED);
434
        tradeHandler->finish();
435
    }
436
    else if (eventId == "money")
437
    {
438
        if (mStatus != PREPARING)
439
            return;
440
441
        int v = atoi(mMoneyField->getText().c_str());
442
        const int curMoney = PlayerInfo::getAttribute(Attributes::MONEY);
443
        if (v > curMoney)
444
        {
445
            if (localChatTab != nullptr)
446
            {
447
                // TRANSLATORS: trade error
448
                localChatTab->chatLog(_("You don't have enough money."),
449
                    ChatMsgType::BY_SERVER,
450
                    IgnoreRecord_false,
451
                    TryRemoveColors_true);
452
            }
453
            v = curMoney;
454
        }
455
        tradeHandler->setMoney(v);
456
        mMoneyField->setText(strprintf("%d", v));
457
    }
458
}
459
460
void TradeWindow::close()
461
{
462
    tradeHandler->cancel();
463
    clear();
464
}
465
466
void TradeWindow::clear()
467
{
468
    mAutoAddItem = nullptr;
469
    mAutoAddToNick.clear();
470
    mAutoMoney = 0;
471
    mAutoAddAmount = 0;
472
    mGotMoney = 0;
473
    mGotMaxMoney = 0;
474
    mMoneyLabel->setForegroundColorAll(
475
        getThemeColor(ThemeColorId::LABEL, 255U),
476
        getThemeColor(ThemeColorId::LABEL_OUTLINE, 255U));
477
}
478
479
void TradeWindow::addAutoItem(const std::string &nick, Item* const item,
480
                              const int amount)
481
{
482
    mAutoAddToNick = nick;
483
    mAutoAddItem = item;
484
    mAutoAddAmount = amount;
485
}
486
487
void TradeWindow::addAutoMoney(const std::string &nick, const int money)
488
{
489
    mAutoAddToNick = nick;
490
    mAutoMoney = money;
491
}
492
493
void TradeWindow::initTrade(const std::string &nick)
494
{
495
    if (localPlayer == nullptr)
496
        return;
497
498
    if (!mAutoAddToNick.empty() && mAutoAddToNick == nick)
499
    {
500
        if ((mAutoAddItem != nullptr) && (mAutoAddItem->getQuantity() != 0))
501
        {
502
            const Inventory *const inv = PlayerInfo::getInventory();
503
            if (inv != nullptr)
504
            {
505
                const Item *const item = inv->findItem(mAutoAddItem->getId(),
506
                    mAutoAddItem->getColor());
507
                if (item != nullptr)
508
                    tradeItem(item, mAutoAddAmount, false);
509
            }
510
        }
511
        if (mAutoMoney != 0)
512
        {
513
            tradeHandler->setMoney(mAutoMoney);
514
            mMoneyField->setText(strprintf("%d", mAutoMoney));
515
        }
516
    }
517
    clear();
518
    if (!playerRelations.isGoodName(nick))
519
        setCaptionFont(gui->getSecureFont());
520
}
521
522
bool TradeWindow::checkItem(const Item *const item) const
523
{
524
    if (item == nullptr)
525
        return false;
526
527
    const int itemId = item->getId();
528
    if (PlayerInfo::isItemProtected(itemId))
529
        return false;
530
    const Item *const tItem = mMyInventory->findItem(
531
        itemId, item->getColor());
532
533
    if ((tItem != nullptr) && (tItem->getQuantity() > 1
534
        || item->getQuantity() > 1))
535
    {
536
        if (localChatTab != nullptr)
537
        {
538
            // TRANSLATORS: trade error
539
            localChatTab->chatLog(_("Failed adding item. You can not "
540
                "overlap one kind of item on the window."),
541
                ChatMsgType::BY_SERVER,
542
                IgnoreRecord_false,
543
                TryRemoveColors_true);
544
        }
545
        return false;
546
    }
547
    if (Net::getNetworkType() != ServerType::TMWATHENA &&
548
        item->isEquipped() == Equipped_true)
549
    {
550
        if (localChatTab != nullptr)
551
        {
552
            localChatTab->chatLog(
553
                // TRANSLATORS: trade error
554
                _("Failed adding item. You can not trade equipped items."),
555
                ChatMsgType::BY_SERVER,
556
                IgnoreRecord_false,
557
                TryRemoveColors_true);
558
        }
559
        return false;
560
    }
561
    return true;
562
}
563
564
bool TradeWindow::isInpupFocused() const
565
{
566
    return mMoneyField != nullptr && mMoneyField->isFocused();
567

6
}