GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/widgets/dropdown.cpp Lines: 129 264 48.9 %
Date: 2020-06-04 Branches: 85 228 37.3 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2006-2009  The Mana World Development Team
4
 *  Copyright (C) 2009-2010  The Mana Developers
5
 *  Copyright (C) 2011-2019  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/dropdown.h"
24
25
#include "settings.h"
26
27
#include "gui/gui.h"
28
#include "gui/skin.h"
29
30
#include "gui/fonts/font.h"
31
32
#include "gui/models/extendedlistmodel.h"
33
34
#include "gui/widgets/createwidget.h"
35
#include "gui/widgets/popuplist.h"
36
37
#include "render/graphics.h"
38
39
#include "resources/imagerect.h"
40
41
#include "debug.h"
42
43
int DropDown::instances = 0;
44
Image *DropDown::buttons[2][2];
45
1
ImageRect DropDown::skinRect;
46
float DropDown::mAlpha = 1.0;
47
Skin *DropDown::mSkin = nullptr;
48
49
3
static std::string const dropdownFiles[2] =
50
{
51
    "dropdown.xml",
52
    "dropdown_pressed.xml"
53

5
};
54
55
95
DropDown::DropDown(const Widget2 *const widget,
56
                   ListModel *const listModel,
57
                   const bool isExtended,
58
                   const Modal modal,
59
                   ActionListener *const listener,
60
95
                   const std::string &eventId) :
61
    ActionListener(),
62
    BasicContainer(widget),
63
    KeyListener(),
64
    MouseListener(),
65
    FocusListener(),
66
    SelectionListener(),
67

95
    mPopup(CREATEWIDGETR(PopupList, this, listModel, isExtended, modal)),
68
190
    mShadowColor(getThemeColor(ThemeColorId::DROPDOWN_SHADOW, 255U)),
69
190
    mHighlightColor(getThemeColor(ThemeColorId::HIGHLIGHT, 255U)),
70
    mPadding(1),
71
    mImagePadding(2),
72
    mSpacing(0),
73
    mFoldedUpHeight(0),
74
    mSelectionListeners(),
75
    mExtended(isExtended),
76
    mDroppedDown(false),
77
    mPushed(false),
78
1045
    mIsDragged(false)
79
{
80
95
    mAllowLogic = false;
81
95
    mFrameSize = 2;
82
190
    mForegroundColor2 = getThemeColor(ThemeColorId::DROPDOWN_OUTLINE, 255U);
83
84
95
    mPopup->setHeight(100);
85
86
    // Initialize graphics
87

95
    if (instances == 0 && (theme != nullptr))
88
    {
89
        // Load the background skin
90
50
        for (int i = 0; i < 2; i ++)
91
        {
92
80
            Skin *const skin = theme->load(dropdownFiles[i],
93
                "dropdown.xml",
94
                true,
95
40
                Theme::getThemePath());
96
20
            if (skin != nullptr)
97
            {
98
20
                if (i == 0)
99
10
                    mSkin = skin;
100
20
                const ImageRect &rect = skin->getBorder();
101
60
                for (int f = 0; f < 2; f ++)
102
                {
103
40
                    if (rect.grid[f] != nullptr)
104
                    {
105
40
                        rect.grid[f]->incRef();
106
40
                        buttons[f][i] = rect.grid[f];
107
40
                        buttons[f][i]->setAlpha(mAlpha);
108
                    }
109
                    else
110
                    {
111
                        buttons[f][i] = nullptr;
112
                    }
113
                }
114
20
                if (i != 0)
115
10
                    theme->unload(skin);
116
            }
117
            else
118
            {
119
                for (int f = 0; f < 2; f ++)
120
                    buttons[f][i] = nullptr;
121
            }
122
        }
123
124
        // get the border skin
125

70
        theme->loadRect(skinRect, "dropdown_background.xml", "", 0, 8);
126
    }
127
128
95
    instances++;
129
130
95
    setWidth(100);
131
95
    setFocusable(true);
132
95
    setListModel(listModel);
133
134

95
    if (mPopup->getSelected() < 0)
135
2
        mPopup->setSelected(0);
136
137
95
    addMouseListener(this);
138
95
    addKeyListener(this);
139
95
    addFocusListener(this);
140
141
95
    adjustHeight();
142
//    mPopup->setForegroundColorAll(getThemeColor(ThemeColorId::DROPDOWN,
143
//        255U),
144
//        getThemeColor(ThemeColorId::DROPDOWN_OUTLINE, 255U));
145
190
    mForegroundColor = getThemeColor(ThemeColorId::DROPDOWN, 255U);
146
190
    mForegroundColor2 = getThemeColor(ThemeColorId::DROPDOWN_OUTLINE, 255U);
147
148
95
    if (!eventId.empty())
149
4
        setActionEventId(eventId);
150
151
95
    if (listener != nullptr)
152
4
        addActionListener(listener);
153
154
95
    mPopup->adjustSize();
155
156
95
    if (mSkin != nullptr)
157
    {
158

380
        mSpacing = mSkin->getOption("spacing");
159

380
        mFrameSize = CAST_U32(mSkin->getOption("frameSize"));
160
190
        mPadding = mSkin->getPadding();
161

380
        mImagePadding = mSkin->getOption("imagePadding");
162
    }
163
95
    adjustHeight();
164
95
}
165
166
760
DropDown::~DropDown()
167
{
168
95
    if (gui != nullptr)
169
95
        gui->removeDragged(this);
170
171
95
    instances--;
172
95
    if (instances == 0)
173
    {
174
50
        for (int f = 0; f < 2; f ++)
175
        {
176
100
            for (int i = 0; i < 2; i ++)
177
            {
178
40
                if (buttons[f][i] != nullptr)
179
40
                    buttons[f][i]->decRef();
180
            }
181
        }
182
10
        if (theme != nullptr)
183
        {
184
10
            theme->unload(mSkin);
185
10
            Theme::unloadRect(skinRect, 0, 8);
186
        }
187
    }
188
190
}
189
190
8
void DropDown::updateAlpha()
191
{
192
    const float alpha = std::max(settings.guiAlpha,
193
24
        theme->getMinimumOpacity());
194
195
8
    if (mAlpha != alpha)
196
    {
197
        mAlpha = alpha;
198
199
        if (buttons[0][0] != nullptr)
200
            buttons[0][0]->setAlpha(mAlpha);
201
        if (buttons[0][1] != nullptr)
202
            buttons[0][1]->setAlpha(mAlpha);
203
        if (buttons[1][0] != nullptr)
204
            buttons[1][0]->setAlpha(mAlpha);
205
        if (buttons[1][1] != nullptr)
206
            buttons[1][1]->setAlpha(mAlpha);
207
208
        for (int a = 0; a < 9; a++)
209
        {
210
            if (skinRect.grid[a] != nullptr)
211
                skinRect.grid[a]->setAlpha(mAlpha);
212
        }
213
    }
214
8
}
215
216
8
void DropDown::draw(Graphics *const graphics)
217
{
218
    BLOCK_START("DropDown::draw")
219
    int h;
220
221
8
    if (mDroppedDown)
222
        h = mFoldedUpHeight;
223
    else
224
8
        h = mDimension.height;
225
226
8
    updateAlpha();
227
228
8
    const unsigned int alpha = CAST_U32(mAlpha * 255.0F);
229
8
    mHighlightColor.a = alpha;
230
8
    mShadowColor.a = alpha;
231
232
8
    ListModel *const model = mPopup->getListModel();
233

8
    if ((model != nullptr) && mPopup->getSelected() >= 0)
234
    {
235
8
        Font *const font = getFont();
236
8
        if (mExtended)
237
        {
238
            const int sel = mPopup->getSelected();
239
            ExtendedListModel *const model2
240
                = static_cast<ExtendedListModel *>(model);
241
            const Image *const image = model2->getImageAt(sel);
242
            if (image == nullptr)
243
            {
244
                font->drawString(graphics,
245
                    mForegroundColor,
246
                    mForegroundColor2,
247
                    model->getElementAt(sel),
248
                    mPadding, mPadding);
249
            }
250
            else
251
            {
252
                graphics->drawImage(image,
253
                    mImagePadding,
254
                    (mDimension.height - image->getHeight()) / 2 + mPadding);
255
                font->drawString(graphics,
256
                    mForegroundColor,
257
                    mForegroundColor2,
258
                    model->getElementAt(sel),
259
                    image->getWidth() + mImagePadding + mSpacing, mPadding);
260
            }
261
        }
262
        else
263
        {
264
16
            font->drawString(graphics,
265
                mForegroundColor,
266
                mForegroundColor2,
267
16
                model->getElementAt(mPopup->getSelected()),
268
8
                mPadding, mPadding);
269
        }
270
    }
271
272
8
    if (isFocused())
273
    {
274
        const int pad = 2 * mPadding;
275
        graphics->setColor(mHighlightColor);
276
        graphics->drawRectangle(Rect(mPadding, mPadding,
277
            mDimension.width - h - pad, h - pad));
278
    }
279
280
8
    drawButton(graphics);
281
282
8
    if (mDroppedDown)
283
    {
284
        // Draw two lines separating the ListBox with selected
285
        // element view.
286
        const int w = mDimension.width;
287
        graphics->setColor(mHighlightColor);
288
        graphics->drawLine(0, h, w, h);
289
        graphics->setColor(mShadowColor);
290
        graphics->drawLine(0, h + 1, w, h + 1);
291
    }
292
    BLOCK_END("DropDown::draw")
293
8
}
294
295
void DropDown::safeDraw(Graphics *const graphics)
296
{
297
    DropDown::draw(graphics);
298
}
299
300
8
void DropDown::drawFrame(Graphics *const graphics)
301
{
302
    BLOCK_START("DropDown::drawFrame")
303
16
    const int bs2 = CAST_S32(getFrameSize());
304
8
    const Rect &rect = mDimension;
305
16
    graphics->drawImageRect(0, 0,
306
16
        rect.width + bs2, rect.height + bs2,
307
16
        skinRect);
308
    BLOCK_END("DropDown::drawFrame")
309
8
}
310
311
void DropDown::safeDrawFrame(Graphics *const graphics)
312
{
313
    BLOCK_START("DropDown::drawFrame")
314
    const int bs2 = CAST_S32(getFrameSize());
315
    const Rect &rect = mDimension;
316
    graphics->drawImageRect(0, 0,
317
        rect.width + bs2, rect.height + bs2,
318
        skinRect);
319
    BLOCK_END("DropDown::drawFrame")
320
}
321
322
8
void DropDown::drawButton(Graphics *const graphics)
323
{
324
8
    Image *const image = buttons[mDroppedDown][mPushed];
325
8
    if (image != nullptr)
326
    {
327
8
        const int height = mDroppedDown ? mFoldedUpHeight : mDimension.height;
328
16
        graphics->drawImage(image,
329
16
            mDimension.width - image->getWidth() - mImagePadding,
330
24
            (height - image->getHeight()) / 2);
331
    }
332
8
}
333
334
void DropDown::keyPressed(KeyEvent& event)
335
{
336
    if (event.isConsumed())
337
        return;
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_SELECT:
345
        case InputAction::GUI_SELECT2:
346
            dropDown();
347
            break;
348
349
        case InputAction::GUI_UP:
350
            setSelected(getSelected() - 1);
351
            break;
352
353
        case InputAction::GUI_DOWN:
354
            setSelected(getSelected() + 1);
355
            break;
356
357
        case InputAction::GUI_HOME:
358
            setSelected(0);
359
            break;
360
361
        case InputAction::GUI_END:
362
            if (mPopup->getListModel() != nullptr)
363
            {
364
                setSelected(mPopup->getListModel()->
365
                    getNumberOfElements() - 1);
366
            }
367
            break;
368
369
        default:
370
            return;
371
    }
372
    PRAGMA45(GCC diagnostic pop)
373
374
    event.consume();
375
}
376
377
3
void DropDown::hideDrop(bool event)
378
{
379
3
    if (event)
380
1
        distributeActionEvent();
381
3
    mPopup->setVisible(Visible_false);
382
3
}
383
384
void DropDown::mousePressed(MouseEvent& event)
385
{
386
    event.consume();
387
    // If we have a mouse press on the widget.
388
    if (event.getButton() == MouseButton::LEFT
389
        && !mDroppedDown && event.getSource() == this)
390
    {
391
        mPushed = true;
392
        dropDown();
393
    }
394
    else
395
    {
396
        mPushed = false;
397
        foldUp();
398
        hideDrop(true);
399
    }
400
}
401
402
void DropDown::mouseReleased(MouseEvent &event)
403
{
404
    if (mIsDragged)
405
        mPushed = false;
406
407
    const MouseButtonT button = event.getButton();
408
    const int x = event.getX();
409
    const int y = event.getY();
410
    // Released outside of widget. Can happen when we have modal
411
    // input focus.
412
    if ((0 > y || y >= mDimension.height || x < 0 || x >= mDimension.width)
413
        && button == MouseButton::LEFT)
414
    {
415
        if (mIsDragged)
416
            foldUp();
417
    }
418
    else if (button == MouseButton::LEFT)
419
    {
420
        mPushed = false;
421
    }
422
423
    mIsDragged = false;
424
}
425
426
void DropDown::mouseDragged(MouseEvent &event)
427
{
428
    mIsDragged = true;
429
    event.consume();
430
}
431
432
void DropDown::mouseWheelMovedUp(MouseEvent& event)
433
{
434
    setSelected(getSelected() - 1);
435
    event.consume();
436
}
437
438
void DropDown::mouseWheelMovedDown(MouseEvent& event)
439
{
440
    setSelected(getSelected() + 1);
441
    event.consume();
442
}
443
444
19
void DropDown::setSelectedString(const std::string &str)
445
{
446
19
    ListModel *const listModel = mPopup->getListModel();
447
19
    if (listModel == nullptr)
448
        return;
449
450
299
    for (int f = 0; f < listModel->getNumberOfElements(); f ++)
451
    {
452
318
        if (listModel->getElementAt(f) == str)
453
        {
454
            setSelected(f);
455
            break;
456
        }
457
    }
458
}
459
460
std::string DropDown::getSelectedString() const
461
{
462
    ListModel *const listModel = mPopup->getListModel();
463
    if (listModel == nullptr)
464
        return "";
465
466
    return listModel->getElementAt(getSelected());
467
}
468
469
291
void DropDown::adjustHeight()
470
{
471
291
    setHeight(getFont()->getHeight() + 2 * mPadding);
472
291
}
473
474
void DropDown::dropDown()
475
{
476
    if (!mDroppedDown)
477
    {
478
        if (mParent == nullptr)
479
            return;
480
        mDroppedDown = true;
481
        mFoldedUpHeight = getHeight();
482
        adjustHeight();
483
484
        int x = 0;
485
        int y = 0;
486
        getAbsolutePosition(x, y);
487
        const int frame = CAST_S32(mParent->getFrameSize());
488
        const int pad = mPopup->getPadding();
489
        const int pad2 = pad * 2;
490
491
        // here width should be adjusted on some other parameters
492
        mPopup->setWidth(mDimension.width - pad2 + 8);
493
        mPopup->show(x - mPadding - frame - 1, y + mDimension.height);
494
        mPopup->requestMoveToTop();
495
        mPopup->requestFocus();
496
    }
497
}
498
499
void DropDown::foldUp()
500
{
501
    if (mDroppedDown)
502
    {
503
        mDroppedDown = false;
504
        adjustHeight();
505
    }
506
}
507
508
3
int DropDown::getSelected() const
509
{
510
3
    return mPopup->getSelected();
511
}
512
513
80
void DropDown::setSelected(int selected)
514
{
515



99
    if (selected >= 0)
516
99
        mPopup->setSelected(selected);
517
80
}
518
519
95
void DropDown::setListModel(ListModel *const listModel)
520
{
521
95
    mPopup->setListModel(listModel);
522
523
95
    if (mPopup->getSelected() < 0)
524
95
        mPopup->setSelected(0);
525
526
95
    adjustHeight();
527
95
}
528
529
ListModel *DropDown::getListModel()
530
{
531
    return mPopup->getListModel();
532
}
533
534
void DropDown::action(const ActionEvent &actionEvent A_UNUSED)
535
{
536
    foldUp();
537
    distributeActionEvent();
538
}
539
540
Rect DropDown::getChildrenArea()
541
{
542
    if (mDroppedDown)
543
    {
544
        // Calculate the children area (with the one pixel border in mind)
545
        return Rect(1, mFoldedUpHeight + 1,
546
            mDimension.width - 2, mDimension.height - mFoldedUpHeight - 2);
547
    }
548
549
    return Rect();
550
}
551
552
void DropDown::valueChanged(const SelectionEvent& event A_UNUSED)
553
{
554
}
555
556
void DropDown::updateSelection()
557
{
558
    mDroppedDown = false;
559
    mPushed = false;
560
    distributeActionEvent();
561
    distributeValueChangedEvent();
562
}
563
564
void DropDown::addSelectionListener(SelectionListener* selectionListener)
565
{
566
    mSelectionListeners.push_back(selectionListener);
567
}
568
569
void DropDown::removeSelectionListener(SelectionListener* listener)
570
{
571
    mSelectionListeners.remove(listener);
572
}
573
574
void DropDown::distributeValueChangedEvent()
575
{
576
    for (SelectionListenerIterator iter = mSelectionListeners.begin();
577
          iter != mSelectionListeners.end();
578
          ++iter)
579
    {
580
        SelectionEvent event(this);
581
        (*iter)->valueChanged(event);
582
    }
583

3
}