GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/windows/tradewindow.cpp Lines: 93 241 38.6 %
Date: 2017-11-29 Branches: 92 329 28.0 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2004-2009  The Mana World Development Team
4
 *  Copyright (C) 2009-2010  The Mana Developers
5
 *  Copyright (C) 2011-2017  The ManaPlus Developers
6
 *
7
 *  This file is part of The ManaPlus Client.
8
 *
9
 *  This program is free software; you can redistribute it and/or modify
10
 *  it under the terms of the GNU General Public License as published by
11
 *  the Free Software Foundation; either version 2 of the License, or
12
 *  any later version.
13
 *
14
 *  This program is distributed in the hope that it will be useful,
15
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 *  GNU General Public License for more details.
18
 *
19
 *  You should have received a copy of the GNU General Public License
20
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 */
22
23
#include "gui/windows/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)),
81

2
    mPartnerInventory(new Inventory(InventoryType::Trade)),
82

2
    mMyItemContainer(new ItemContainer(this, mMyInventory)),
83

2
    mPartnerItemContainer(new ItemContainer(this, mPartnerInventory)),
84
    // TRANSLATORS: trade window money label
85

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

4
    mAddButton(new Button(this, _("Add"), "add", this)),
88

2
    mOkButton(new Button(this, "", "", this)),  // Will be filled in later
89
    // TRANSLATORS: trade window money change button
90

4
    mMoneyChangeButton(new Button(this, _("Change"), "money", this)),
91

2
    mMoneyField(new TextField(this)),
92
    mAutoAddItem(nullptr),
93
    mAutoAddToNick(""),
94
    mGotMoney(0),
95
    mGotMaxMoney(0),
96
    mAutoMoney(0),
97
    mAutoAddAmount(0),
98
    mStatus(PROPOSING),
99
    mOkOther(false),
100






90
    mOkMe(false)
101
{
102
10
    setWindowName("Trade");
103
2
    setResizable(true);
104
2
    setCloseButton(true);
105
2
    setStickyButtonLock(true);
106
2
    setDefaultSize(386, 180, ImagePosition::CENTER);
107
2
    setMinWidth(310);
108
2
    setMinHeight(180);
109
110
2
    if (setupWindow != nullptr)
111
        setupWindow->registerWindowForReset(this);
112
113
2
    const Font *const fnt = mOkButton->getFont();
114

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

14
        fnt->getWidth(CAPTION_CONFIRMED));
116

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

10
    width = std::max(width, fnt->getWidth(CAPTION_ACCEPTED));
118
119
2
    mOkButton->setWidth(8 + width);
120
121
4
    mMyItemContainer->addSelectionListener(this);
122
123
2
    ScrollArea *const myScroll = new ScrollArea(this, mMyItemContainer,
124

10
        Opaque_true, "trade_background.xml");
125
2
    myScroll->setHorizontalScrollPolicy(ScrollArea::SHOW_NEVER);
126
127
4
    mPartnerItemContainer->addSelectionListener(this);
128
129
    ScrollArea *const partnerScroll = new ScrollArea(this,
130
2
        mPartnerItemContainer,
131

8
        Opaque_true, "trade_background.xml");
132
2
    partnerScroll->setHorizontalScrollPolicy(ScrollArea::SHOW_NEVER);
133
134
    // TRANSLATORS: trade window money label
135

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

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

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

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

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

6
}