GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/widgets/dropdown.cpp Lines: 129 267 48.3 %
Date: 2018-06-18 21:15:20 Branches: 84 224 37.5 %

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-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/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
2
ImageRect DropDown::skinRect;
46
float DropDown::mAlpha = 1.0;
47
Skin *DropDown::mSkin = nullptr;
48
49
6
static std::string const dropdownFiles[2] =
50
{
51
    "dropdown.xml",
52
    "dropdown_pressed.xml"
53

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

190
    mPopup(CREATEWIDGETR(PopupList, this, listModel, isExtended, modal)),
68
380
    mShadowColor(getThemeColor(ThemeColorId::DROPDOWN_SHADOW, 255U)),
69
380
    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
2090
    mIsDragged(false)
79
{
80
190
    mAllowLogic = false;
81
190
    mFrameSize = 2;
82
380
    mForegroundColor2 = getThemeColor(ThemeColorId::DROPDOWN_OUTLINE, 255U);
83
84
190
    mPopup->setHeight(100);
85
86
    // Initialize graphics
87

190
    if (instances == 0 && (theme != nullptr))
88
    {
89
        // Load the background skin
90
100
        for (int i = 0; i < 2; i ++)
91
        {
92
200
            Skin *const skin = theme->load(dropdownFiles[i],
93
                "dropdown.xml",
94
                true,
95
80
                Theme::getThemePath());
96
40
            if (skin != nullptr)
97
            {
98
40
                if (i == 0)
99
20
                    mSkin = skin;
100
40
                const ImageRect &rect = skin->getBorder();
101
120
                for (int f = 0; f < 2; f ++)
102
                {
103
80
                    if (rect.grid[f] != nullptr)
104
                    {
105
80
                        rect.grid[f]->incRef();
106
80
                        buttons[f][i] = rect.grid[f];
107
80
                        buttons[f][i]->setAlpha(mAlpha);
108
                    }
109
                    else
110
                    {
111
                        buttons[f][i] = nullptr;
112
                    }
113
                }
114
40
                if (i != 0)
115
20
                    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

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

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

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

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

760
        mImagePadding = mSkin->getOption("imagePadding");
162
    }
163
190
    adjustHeight();
164
190
}
165
166
1520
DropDown::~DropDown()
167
{
168
190
    if (gui != nullptr)
169
190
        gui->removeDragged(this);
170
171
190
    instances--;
172
190
    if (instances == 0)
173
    {
174
100
        for (int f = 0; f < 2; f ++)
175
        {
176
200
            for (int i = 0; i < 2; i ++)
177
            {
178
80
                if (buttons[f][i] != nullptr)
179
80
                    buttons[f][i]->decRef();
180
            }
181
        }
182
20
        if (theme != nullptr)
183
        {
184
20
            theme->unload(mSkin);
185
20
            Theme::unloadRect(skinRect, 0, 8);
186
        }
187
    }
188
380
}
189
190
16
void DropDown::updateAlpha()
191
{
192
    const float alpha = std::max(settings.guiAlpha,
193
48
        theme->getMinimumOpacity());
194
195
16
    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
16
}
215
216
16
void DropDown::draw(Graphics *const graphics)
217
{
218
    BLOCK_START("DropDown::draw")
219
    int h;
220
221
16
    if (mDroppedDown)
222
        h = mFoldedUpHeight;
223
    else
224
16
        h = mDimension.height;
225
226
16
    updateAlpha();
227
228
16
    const unsigned int alpha = CAST_U32(mAlpha * 255.0F);
229
16
    const int pad = 2 * mPadding;
230
16
    mHighlightColor.a = alpha;
231
16
    mShadowColor.a = alpha;
232
233
16
    ListModel *const model = mPopup->getListModel();
234

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



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

6
}