GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/windows/charselectdialog.cpp Lines: 82 293 28.0 %
Date: 2018-09-20 Branches: 78 398 19.6 %

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/charselectdialog.h"
24
25
#include "client.h"
26
#include "configuration.h"
27
#include "pincodemanager.h"
28
29
#include "listeners/charrenamelistener.h"
30
31
#include "gui/windows/charcreatedialog.h"
32
#include "gui/windows/chardeleteconfirm.h"
33
#include "gui/windows/editdialog.h"
34
#include "gui/windows/logindialog.h"
35
#include "gui/windows/okdialog.h"
36
#include "gui/windows/textdialog.h"
37
38
#include "gui/widgets/button.h"
39
#include "gui/widgets/characterdisplay.h"
40
#include "gui/widgets/characterviewnormal.h"
41
#include "gui/widgets/characterviewsmall.h"
42
#include "gui/widgets/containerplacer.h"
43
#include "gui/widgets/createwidget.h"
44
45
#include "net/character.h"
46
#include "net/charserverhandler.h"
47
#include "net/logindata.h"
48
#ifdef TMWA_SUPPORT
49
#include "net/net.h"
50
#endif  // TMWA_SUPPORT
51
#include "net/serverfeatures.h"
52
53
#include "utils/foreach.h"
54
55
#include "resources/db/unitsdb.h"
56
57
#include "debug.h"
58
59
// Character slots per row in the dialog
60
static const int SLOTS_PER_ROW = 5;
61
62
1
CharSelectDialog::CharSelectDialog(LoginData &data) :
63
    // TRANSLATORS: char select dialog name
64
4
    Window(strprintf(_("Account %s (last login time %s)"),
65
        data.username.c_str(), data.lastLogin.c_str()),
66
        Modal_false,
67
        nullptr,
68
        "char.xml"),
69
    ActionListener(),
70
    KeyListener(),
71
    mLoginData(&data),
72
    // TRANSLATORS: char select dialog. button.
73
1
    mSwitchLoginButton(new Button(this, _("Switch"), "switch",
74

1
        BUTTON_SKIN, this)),
75
    // TRANSLATORS: char select dialog. button.
76
1
    mChangePasswordButton(new Button(this, _("Password"), "change_password",
77

1
        BUTTON_SKIN, this)),
78
    // TRANSLATORS: char select dialog. button.
79

2
    mPlayButton(new Button(this, _("Play"), "use", BUTTON_SKIN, this)),
80
    // TRANSLATORS: char select dialog. button.
81

2
    mInfoButton(new Button(this, _("Info"), "info", BUTTON_SKIN, this)),
82
    // TRANSLATORS: char select dialog. button.
83

2
    mDeleteButton(new Button(this, _("Delete"), "delete", BUTTON_SKIN, this)),
84
    // TRANSLATORS: char select dialog. button.
85
    mRenameButton(nullptr),
86
    mChangePinButton(nullptr),
87
    mCharacterView(nullptr),
88
    mCharacterEntries(0),
89
    mCharServerHandler(charServerHandler),
90
    mDeleteDialog(nullptr),
91
    mDeleteIndex(-1),
92
    mLocked(false),
93
1
    mSmallScreen(mainGraphics->getWidth() < 470
94







45
                 || mainGraphics->getHeight() < 370)
95
{
96
1
    setCloseButton(true);
97
1
    setFocusable(true);
98
99
1
    ContainerPlacer placer(nullptr, nullptr);
100
1
    placer = getPlacer(0, 0);
101
102
1
    placer(0, 0, mSwitchLoginButton, 1, 1);
103
104
1
    int n = 1;
105
1
    placer(n, 0, mChangePasswordButton, 1, 1);
106
1
    n ++;
107
1
    placer(n, 0, mDeleteButton, 1, 1);
108
1
    n ++;
109
#ifdef TMWA_SUPPORT
110

1
    if (Net::getNetworkType() != ServerType::TMWATHENA)
111
#endif
112
    {
113
1
        mRenameButton = new Button(this,
114
            // TRANSLATORS: character rename button
115
1
            _("Rename"),
116
            "rename",
117
            BUTTON_SKIN,
118


7
            this);
119
1
        placer(n, 0, mRenameButton, 1, 1);
120
        n ++;
121
    }
122
#ifdef TMWA_SUPPORT
123

1
    if (serverFeatures->havePincode())
124
#endif
125
    {
126
1
        mChangePinButton = new Button(this,
127
            // TRANSLATORS: change pincode button
128
1
            _("Change pin"),
129
            "changepin",
130
            BUTTON_SKIN,
131
6
            this);
132
1
        placer(n, 0, mChangePinButton, 1, 1);
133
1
        n ++;
134
    }
135
1
    placer(n, 0, mInfoButton, 1, 1);
136
    n ++;
137
138
19
    for (int i = 0; i < CAST_S32(mLoginData->characterSlots); i++)
139
    {
140

9
        CharacterDisplay *const character = new CharacterDisplay(this, this);
141
9
        character->setVisible(Visible_false);
142
9
        mCharacterEntries.push_back(character);
143
    }
144
145
1
    placer(0, 2, mPlayButton, 1, 1);
146
147
1
    if (!mSmallScreen)
148
    {
149
1
        mCharacterView = new CharacterViewNormal(
150

1
            this, &mCharacterEntries, mPadding);
151
1
        placer(0, 1, mCharacterView, 10, 1);
152
1
        int sz = 410 + 2 * mPadding;
153

4
        if (config.getIntValue("fontSize") > 18)
154
            sz = 500 + 2 * mPadding;
155
2
        const int width = mCharacterView->getWidth() + 2 * mPadding;
156
1
        if (sz < width)
157
1
            sz = width;
158

1
        if (sz > mainGraphics->getWidth())
159
            sz = mainGraphics->getWidth();
160
1
        reflowLayout(sz, 0);
161
    }
162
    else
163
    {
164
        // TRANSLATORS: char select dialog name
165
        setCaption(strprintf(_("Account %s"), mLoginData->username.c_str()));
166
        mCharacterView = new CharacterViewSmall(
167
            this, &mCharacterEntries, mPadding);
168
        mCharacterView->setWidth(mainGraphics->getWidth()
169
            - 2 * getPadding());
170
        placer(0, 1, mCharacterView, 10, 1);
171
        reflowLayout(0, 0);
172
    }
173
1
    addKeyListener(this);
174
1
    center();
175
176
1
    charServerHandler->setCharSelectDialog(this);
177
1
    mCharacterView->show(0);
178
1
    updateState();
179
1
}
180
181
6
CharSelectDialog::~CharSelectDialog()
182
{
183
1
    mDeleteDialog = nullptr;
184
2
}
185
186
1
void CharSelectDialog::postInit()
187
{
188
1
    Window::postInit();
189
1
    setVisible(Visible_true);
190
1
    requestFocus();
191
1
}
192
193
void CharSelectDialog::action(const ActionEvent &event)
194
{
195
    // Check if a button of a character was pressed
196
    const Widget *const sourceParent = event.getSource()->getParent();
197
    int selected = -1;
198
    for (unsigned int i = 0, fsz = CAST_U32(
199
         mCharacterEntries.size());
200
         i < fsz;
201
         ++i)
202
    {
203
        if (mCharacterEntries[i] == sourceParent)
204
        {
205
            selected = i;
206
            mCharacterView->show(i);
207
            updateState();
208
            break;
209
        }
210
    }
211
    if (selected == -1)
212
        selected = mCharacterView->getSelected();
213
214
    const std::string &eventId = event.getId();
215
    const bool blocked = pincodeManager.isBlocked();
216
217
    if (selected >= 0)
218
    {
219
        if (blocked)
220
            return;
221
        if (eventId == "use")
222
        {
223
            use(selected);
224
            return;
225
        }
226
        else if (eventId == "delete"
227
                 && (mCharacterEntries[selected]->getCharacter() != nullptr))
228
        {
229
            CREATEWIDGET(CharDeleteConfirm, this, selected);
230
            return;
231
        }
232
        else if (eventId == "rename"
233
                 && (mCharacterEntries[selected]->getCharacter() != nullptr))
234
        {
235
            const LocalPlayer *const player = mCharacterEntries[
236
                selected]->getCharacter()->dummy;
237
            EditDialog *const dialog = CREATEWIDGETR(EditDialog,
238
                // TRANSLATORS: character rename dialog header.
239
                _("Please enter new name"),
240
                player->getName(),
241
                "OK",
242
                300,
243
                nullptr,
244
                Modal_true);
245
            charRenameListener.setId(player->getId());
246
            charRenameListener.setDialog(dialog);
247
            dialog->addActionListener(&charRenameListener);
248
        }
249
        else if (eventId == "info")
250
        {
251
            Net::Character *const character = mCharacterEntries[
252
                selected]->getCharacter();
253
            if (character == nullptr)
254
                return;
255
256
            const LocalPlayer *const data = character->dummy;
257
            if (data == nullptr)
258
                return;
259
260
            const std::string strExp = toString(CAST_U64(
261
                character->data.mAttributes[Attributes::PLAYER_EXP]));
262
            const std::string msg = strprintf(
263
                // TRANSLATORS: char select dialog. player info message.
264
                _("Hp: %u/%u\nMp: %u/%u\nLevel: %u\n"
265
                "Experience: %s\nMoney: %s"),
266
                CAST_U32(
267
                character->data.mAttributes[Attributes::PLAYER_HP]),
268
                CAST_U32(
269
                character->data.mAttributes[Attributes::PLAYER_MAX_HP]),
270
                CAST_U32(
271
                character->data.mAttributes[Attributes::PLAYER_MP]),
272
                CAST_U32(
273
                character->data.mAttributes[Attributes::PLAYER_MAX_MP]),
274
                CAST_U32(
275
                character->data.mAttributes[Attributes::PLAYER_BASE_LEVEL]),
276
                strExp.c_str(),
277
                UnitsDb::formatCurrency(CAST_S32(
278
                character->data.mAttributes[Attributes::MONEY])).c_str());
279
            CREATEWIDGET(OkDialog, data->getName(), msg,
280
                // TRANSLATORS: ok dialog button
281
                _("OK"),
282
                DialogType::SILENCE,
283
                Modal_true,
284
                ShowCenter_true,
285
                nullptr,
286
                260);
287
        }
288
    }
289
    if (eventId == "switch")
290
    {
291
        charServerHandler->clear();
292
        close();
293
    }
294
    else if (eventId == "change_password")
295
    {
296
        if (blocked)
297
            return;
298
        client->setState(State::CHANGEPASSWORD);
299
    }
300
    else if (eventId == "change_email")
301
    {
302
        if (blocked)
303
            return;
304
        client->setState(State::CHANGEEMAIL);
305
    }
306
    else if (eventId == "try delete character")
307
    {
308
        if (blocked)
309
            return;
310
        if ((mDeleteDialog != nullptr) && mDeleteIndex != -1)
311
        {
312
            if (serverFeatures->haveEmailOnDelete())
313
            {
314
                attemptCharacterDelete(mDeleteIndex, mDeleteDialog->getText());
315
                mDeleteDialog = nullptr;
316
            }
317
            else if (mDeleteDialog->getText() == LoginDialog::savedPassword)
318
            {
319
                attemptCharacterDelete(mDeleteIndex, "");
320
                mDeleteDialog = nullptr;
321
            }
322
            else
323
            {
324
                CREATEWIDGET(OkDialog,
325
                    // TRANSLATORS: error header
326
                    _("Error"),
327
                    // TRANSLATORS: error message
328
                    _("Incorrect password"),
329
                    // TRANSLATORS: ok dialog button
330
                    _("OK"),
331
                    DialogType::ERROR,
332
                    Modal_true,
333
                    ShowCenter_true,
334
                    nullptr,
335
                    260);
336
            }
337
        }
338
        mDeleteIndex = -1;
339
    }
340
    else if (eventId == "changepin")
341
    {
342
        if (blocked)
343
            return;
344
        pincodeManager.setState(PincodeState::Change);
345
        pincodeManager.updateState();
346
    }
347
}
348
349
void CharSelectDialog::use(const int selected)
350
{
351
    if ((mCharacterEntries[selected] != nullptr)
352
        && (mCharacterEntries[selected]->getCharacter() != nullptr))
353
    {
354
        attemptCharacterSelect(selected);
355
    }
356
    else
357
    {
358
        CharCreateDialog *const charCreateDialog =
359
            CREATEWIDGETR(CharCreateDialog, this, selected);
360
        mCharServerHandler->setCharCreateDialog(charCreateDialog);
361
    }
362
}
363
364
void CharSelectDialog::keyPressed(KeyEvent &event)
365
{
366
    const InputActionT actionId = event.getActionId();
367
    const bool blocked = pincodeManager.isBlocked();
368
    PRAGMA45(GCC diagnostic push)
369
    PRAGMA45(GCC diagnostic ignored "-Wswitch-enum")
370
    switch (actionId)
371
    {
372
        case InputAction::GUI_CANCEL:
373
            event.consume();
374
            action(ActionEvent(mSwitchLoginButton,
375
                mSwitchLoginButton->getActionEventId()));
376
            break;
377
378
        case InputAction::GUI_RIGHT:
379
        {
380
            event.consume();
381
            int idx = mCharacterView->getSelected();
382
            if (idx >= 0)
383
            {
384
                if ((idx + 1) % SLOTS_PER_ROW == 0)
385
                    break;
386
                idx ++;
387
                mCharacterView->show(idx);
388
                updateState();
389
            }
390
            break;
391
        }
392
393
        case InputAction::GUI_LEFT:
394
        {
395
            event.consume();
396
            int idx = mCharacterView->getSelected();
397
            if (idx >= 0)
398
            {
399
                if (idx == 0 || idx % SLOTS_PER_ROW == 0)
400
                    break;
401
                idx --;
402
                mCharacterView->show(idx);
403
                updateState();
404
            }
405
            break;
406
        }
407
408
        case InputAction::GUI_UP:
409
        {
410
            event.consume();
411
            int idx = mCharacterView->getSelected();
412
            if (idx >= 0)
413
            {
414
                if (idx < SLOTS_PER_ROW)
415
                    break;
416
                idx -= SLOTS_PER_ROW;
417
                mCharacterView->show(idx);
418
                updateState();
419
            }
420
            break;
421
        }
422
423
        case InputAction::GUI_DOWN:
424
        {
425
            event.consume();
426
            int idx = mCharacterView->getSelected();
427
            if (idx >= 0)
428
            {
429
                if (idx >= mLoginData->characterSlots - SLOTS_PER_ROW)
430
                    break;
431
                idx += SLOTS_PER_ROW;
432
                mCharacterView->show(idx);
433
                updateState();
434
            }
435
            break;
436
        }
437
438
        case InputAction::GUI_DELETE:
439
        {
440
            if (blocked)
441
                return;
442
            event.consume();
443
            const int idx = mCharacterView->getSelected();
444
            if (idx >= 0 && (mCharacterEntries[idx] != nullptr)
445
                && (mCharacterEntries[idx]->getCharacter() != nullptr))
446
            {
447
                CREATEWIDGET(CharDeleteConfirm, this, idx);
448
            }
449
            break;
450
        }
451
452
        case InputAction::GUI_SELECT:
453
        {
454
            if (blocked)
455
                return;
456
            event.consume();
457
            use(mCharacterView->getSelected());
458
            break;
459
        }
460
        default:
461
            break;
462
    }
463
    PRAGMA45(GCC diagnostic pop)
464
}
465
466
/**
467
 * Communicate character deletion to the server.
468
 */
469
void CharSelectDialog::attemptCharacterDelete(const int index,
470
                                              const std::string &email)
471
{
472
    if (mLocked)
473
        return;
474
475
    if (mCharacterEntries[index] != nullptr)
476
    {
477
        mCharServerHandler->deleteCharacter(
478
            mCharacterEntries[index]->getCharacter(),
479
            email);
480
    }
481
    lock();
482
}
483
484
void CharSelectDialog::askPasswordForDeletion(const int index)
485
{
486
    mDeleteIndex = index;
487
    if (serverFeatures->haveEmailOnDelete())
488
    {
489
        CREATEWIDGETV(mDeleteDialog, TextDialog,
490
            // TRANSLATORS: char deletion question.
491
            _("Enter your email for deleting character"),
492
            // TRANSLATORS: email label.
493
            _("Enter email:"),
494
            this,
495
            false);
496
    }
497
    else
498
    {
499
        CREATEWIDGETV(mDeleteDialog, TextDialog,
500
            // TRANSLATORS: char deletion question.
501
            _("Enter password for deleting character"),
502
            // TRANSLATORS: email label.
503
            _("Enter password:"),
504
            this,
505
            true);
506
    }
507
    mDeleteDialog->setActionEventId("try delete character");
508
    mDeleteDialog->addActionListener(this);
509
}
510
511
/**
512
 * Communicate character selection to the server.
513
 */
514
void CharSelectDialog::attemptCharacterSelect(const int index)
515
{
516
    if (mLocked || (mCharacterEntries[index] == nullptr))
517
        return;
518
519
    setVisible(Visible_false);
520
    if (mCharServerHandler != nullptr)
521
    {
522
        mCharServerHandler->chooseCharacter(
523
            mCharacterEntries[index]->getCharacter());
524
    }
525
    lock();
526
}
527
528
1
void CharSelectDialog::setCharacters(const Net::Characters &characters)
529
{
530
    // Reset previous characters
531
15
    FOR_EACH (STD_VECTOR<CharacterDisplay*>::const_iterator,
532
              iter, mCharacterEntries)
533
    {
534
9
        if (*iter != nullptr)
535
9
            (*iter)->setCharacter(nullptr);
536
    }
537
538
3
    FOR_EACH (Net::Characters::const_iterator, i, characters)
539
        setCharacter(*i);
540
1
    updateState();
541
1
}
542
543
void CharSelectDialog::setCharacter(Net::Character *const character)
544
{
545
    if (character == nullptr)
546
        return;
547
    const int characterSlot = character->slot;
548
    if (characterSlot >= CAST_S32(mCharacterEntries.size()))
549
    {
550
        logger->log("Warning: slot out of range: %d", character->slot);
551
        return;
552
    }
553
554
    if (mCharacterEntries[characterSlot] != nullptr)
555
        mCharacterEntries[characterSlot]->setCharacter(character);
556
}
557
558
void CharSelectDialog::lock()
559
{
560
    if (!mLocked)
561
        setLocked(true);
562
}
563
564
void CharSelectDialog::unlock()
565
{
566
    setLocked(false);
567
}
568
569
void CharSelectDialog::setLocked(const bool locked)
570
{
571
    mLocked = locked;
572
573
    if (mSwitchLoginButton != nullptr)
574
        mSwitchLoginButton->setEnabled(!locked);
575
    if (mChangePasswordButton != nullptr)
576
        mChangePasswordButton->setEnabled(!locked);
577
    mPlayButton->setEnabled(!locked);
578
    if (mDeleteButton != nullptr)
579
        mDeleteButton->setEnabled(!locked);
580
581
    for (size_t i = 0, sz = mCharacterEntries.size(); i < sz; ++i)
582
    {
583
        if (mCharacterEntries[i] != nullptr)
584
            mCharacterEntries[i]->setActive(!mLocked);
585
    }
586
}
587
588
bool CharSelectDialog::selectByName(const std::string &name,
589
                                    const SelectAction selAction)
590
{
591
    if (mLocked)
592
        return false;
593
594
    for (size_t i = 0, sz = mCharacterEntries.size(); i < sz; ++i)
595
    {
596
        if (mCharacterEntries[i] != nullptr)
597
        {
598
            const Net::Character *const character
599
                = mCharacterEntries[i]->getCharacter();
600
            if (character != nullptr)
601
            {
602
                if (character->dummy != nullptr &&
603
                    character->dummy->getName() == name)
604
                {
605
                    mCharacterView->show(CAST_S32(i));
606
                    updateState();
607
                    if (selAction == Choose)
608
                        attemptCharacterSelect(CAST_S32(i));
609
                    return true;
610
                }
611
            }
612
        }
613
    }
614
615
    return false;
616
}
617
618
void CharSelectDialog::close()
619
{
620
    client->setState(State::SWITCH_LOGIN);
621
    Window::close();
622
}
623
624
1
void CharSelectDialog::widgetResized(const Event &event)
625
{
626
1
    Window::widgetResized(event);
627
1
    mCharacterView->resize();
628
1
}
629
630
2
void CharSelectDialog::updateState()
631
{
632
2
    const int idx = mCharacterView->getSelected();
633
2
    if (idx == -1)
634
    {
635
        mPlayButton->setEnabled(false);
636
        return;
637
    }
638
4
    mPlayButton->setEnabled(true);
639
640

6
    if (mCharacterEntries[idx] != nullptr &&
641
2
        mCharacterEntries[idx]->getCharacter() != nullptr)
642
    {
643
        // TRANSLATORS: char select dialog. button.
644
        mPlayButton->setCaption(_("Play"));
645
646
        const LocalPlayer *const player = mCharacterEntries[
647
            idx]->getCharacter()->dummy;
648
        if ((player != nullptr) && (mRenameButton != nullptr))
649
            mRenameButton->setEnabled(player->getRename() ? true : false);
650
    }
651
    else
652
    {
653
        // TRANSLATORS: char select dialog. button.
654
10
        mPlayButton->setCaption(_("Create"));
655
    }
656
}
657
658
void CharSelectDialog::setName(const BeingId id, const std::string &newName)
659
{
660
    for (unsigned int i = 0, fsz = CAST_U32(
661
         mCharacterEntries.size());
662
         i < fsz;
663
         ++i)
664
    {
665
        CharacterDisplay *const character = mCharacterEntries[i];
666
        if (character == nullptr)
667
            continue;
668
        LocalPlayer *const player = character->getCharacter()->dummy;
669
        if ((player != nullptr) && player->getId() == id)
670
        {
671
            player->setName(newName);
672
            character->update();
673
            return;
674
        }
675
    }
676

3
}