GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/windows/charselectdialog.cpp Lines: 75 277 27.1 %
Date: 2017-11-29 Branches: 76 406 18.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/windows/charselectdialog.h"
24
25
#include "client.h"
26
#include "configuration.h"
27
28
#include "listeners/charrenamelistener.h"
29
#include "listeners/pincodelistener.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
2
CharSelectDialog::CharSelectDialog(LoginData &data) :
63
    // TRANSLATORS: char select dialog name
64
8
    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

4
    mSwitchLoginButton(new Button(this, _("Switch"), "switch", this)),
74
    // TRANSLATORS: char select dialog. button.
75
2
    mChangePasswordButton(new Button(this, _("Password"),
76

2
                          "change_password", this)),
77
    // TRANSLATORS: char select dialog. button.
78

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

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

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








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

2
    if (Net::getNetworkType() != ServerType::TMWATHENA)
109
#endif
110
    {
111

16
        mRenameButton = new Button(this,
112
            // TRANSLATORS: character rename button
113
2
            _("Rename"),
114
            "rename",
115

2
            this);
116
2
        placer(n, 0, mRenameButton);
117
        n ++;
118
    }
119
2
    placer(n, 0, mInfoButton);
120
    n ++;
121
122
38
    for (int i = 0; i < CAST_S32(mLoginData->characterSlots); i++)
123
    {
124

18
        CharacterDisplay *const character = new CharacterDisplay(this, this);
125
18
        character->setVisible(Visible_false);
126
18
        mCharacterEntries.push_back(character);
127
    }
128
129
2
    placer(0, 2, mPlayButton);
130
131
2
    if (!mSmallScreen)
132
    {
133
2
        mCharacterView = new CharacterViewNormal(
134

2
            this, &mCharacterEntries, mPadding);
135
2
        placer(0, 1, mCharacterView, 10);
136
2
        int sz = 410 + 2 * mPadding;
137

8
        if (config.getIntValue("fontSize") > 18)
138
            sz = 500 + 2 * mPadding;
139
4
        const int width = mCharacterView->getWidth() + 2 * mPadding;
140
2
        if (sz < width)
141
2
            sz = width;
142

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

12
    if (mCharacterEntries[idx] != nullptr &&
607
4
        mCharacterEntries[idx]->getCharacter() != nullptr)
608
    {
609
        // TRANSLATORS: char select dialog. button.
610
        mPlayButton->setCaption(_("Play"));
611
612
        const LocalPlayer *const player = mCharacterEntries[
613
            idx]->getCharacter()->dummy;
614
        if ((player != nullptr) && (mRenameButton != nullptr))
615
            mRenameButton->setEnabled(player->getRename() ? true : false);
616
    }
617
    else
618
    {
619
        // TRANSLATORS: char select dialog. button.
620
20
        mPlayButton->setCaption(_("Create"));
621
    }
622
}
623
624
void CharSelectDialog::setName(const BeingId id, const std::string &newName)
625
{
626
    for (unsigned int i = 0, fsz = CAST_U32(
627
         mCharacterEntries.size());
628
         i < fsz;
629
         ++i)
630
    {
631
        if (mCharacterEntries[i] == nullptr)
632
            continue;
633
        CharacterDisplay *const character = mCharacterEntries[i];
634
        if (character == nullptr)
635
            continue;
636
        LocalPlayer *const player = character->getCharacter()->dummy;
637
        if ((player != nullptr) && player->getId() == id)
638
        {
639
            player->setName(newName);
640
            character->update();
641
            return;
642
        }
643
    }
644

6
}