GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/widgets/dropdown.cpp Lines: 128 266 48.1 %
Date: 2017-11-29 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-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/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
188
DropDown::DropDown(const Widget2 *const widget,
56
                   ListModel *const listModel,
57
                   const bool extended,
58
                   const Modal modal,
59
                   ActionListener *const listener,
60
188
                   const std::string &eventId) :
61
    ActionListener(),
62
    BasicContainer(widget),
63
    KeyListener(),
64
    MouseListener(),
65
    FocusListener(),
66
    SelectionListener(),
67

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

188
    if (instances == 0 && (theme != nullptr))
88
    {
89
        // Load the background skin
90
90
        for (int i = 0; i < 2; i ++)
91
        {
92

216
            Skin *const skin = theme->load(dropdownFiles[i], "dropdown.xml");
93
36
            if (skin != nullptr)
94
            {
95
36
                if (i == 0)
96
18
                    mSkin = skin;
97
36
                const ImageRect &rect = skin->getBorder();
98
108
                for (int f = 0; f < 2; f ++)
99
                {
100
72
                    if (rect.grid[f] != nullptr)
101
                    {
102
72
                        rect.grid[f]->incRef();
103
72
                        buttons[f][i] = rect.grid[f];
104
72
                        buttons[f][i]->setAlpha(mAlpha);
105
                    }
106
                    else
107
                    {
108
                        buttons[f][i] = nullptr;
109
                    }
110
                }
111
36
                if (i != 0)
112
18
                    theme->unload(skin);
113
            }
114
            else
115
            {
116
                for (int f = 0; f < 2; f ++)
117
                    buttons[f][i] = nullptr;
118
            }
119
        }
120
121
        // get the border skin
122

126
        theme->loadRect(skinRect, "dropdown_background.xml", "");
123
    }
124
125
188
    instances++;
126
127
188
    setWidth(100);
128
188
    setFocusable(true);
129
188
    setListModel(listModel);
130
131

188
    if (mPopup->getSelected() < 0)
132
4
        mPopup->setSelected(0);
133
134
188
    addMouseListener(this);
135
188
    addKeyListener(this);
136
188
    addFocusListener(this);
137
138
188
    adjustHeight();
139
//    mPopup->setForegroundColorAll(getThemeColor(ThemeColorId::DROPDOWN),
140
//        getThemeColor(ThemeColorId::DROPDOWN_OUTLINE));
141
376
    mForegroundColor = getThemeColor(ThemeColorId::DROPDOWN);
142
376
    mForegroundColor2 = getThemeColor(ThemeColorId::DROPDOWN_OUTLINE);
143
144
188
    if (!eventId.empty())
145
6
        setActionEventId(eventId);
146
147
188
    if (listener != nullptr)
148
6
        addActionListener(listener);
149
150
188
    mPopup->adjustSize();
151
152
188
    if (mSkin != nullptr)
153
    {
154

752
        mSpacing = mSkin->getOption("spacing");
155

752
        mFrameSize = CAST_U32(mSkin->getOption("frameSize"));
156
376
        mPadding = mSkin->getPadding();
157

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

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



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

6
}