GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/gui.cpp Lines: 152 629 24.2 %
Date: 2018-11-12 Branches: 127 778 16.3 %

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
/*      _______   __   __   __   ______   __   __   _______   __   __
24
 *     / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___  /\ /  |\/ /\
25
 *    / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
26
 *   / / /__   / / // / // / // / /    / ___  / // ___  / // /| ' / /
27
 *  / /_// /\ / /_// / // / // /_/_   / / // / // /\_/ / // / |  / /
28
 * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
29
 * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
30
 *
31
 * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
32
 *
33
 *
34
 * Per Larsson a.k.a finalman
35
 * Olof Naessén a.k.a jansem/yakslem
36
 *
37
 * Visit: http://guichan.sourceforge.net
38
 *
39
 * License: (BSD)
40
 * Redistribution and use in source and binary forms, with or without
41
 * modification, are permitted provided that the following conditions
42
 * are met:
43
 * 1. Redistributions of source code must retain the above copyright
44
 *    notice, this list of conditions and the following disclaimer.
45
 * 2. Redistributions in binary form must reproduce the above copyright
46
 *    notice, this list of conditions and the following disclaimer in
47
 *    the documentation and/or other materials provided with the
48
 *    distribution.
49
 * 3. Neither the name of Guichan nor the names of its contributors may
50
 *    be used to endorse or promote products derived from this software
51
 *    without specific prior written permission.
52
 *
53
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
54
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
55
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
56
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
57
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
58
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
59
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
60
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
61
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
62
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
63
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
64
 */
65
66
#include "gui/gui.h"
67
68
#include "gui/focushandler.h"
69
#include "gui/sdlinput.h"
70
#include "gui/viewport.h"
71
72
#include "gui/fonts/font.h"
73
74
#include "gui/widgets/label.h"
75
#include "gui/widgets/window.h"
76
77
#include "gui/widgets/tabs/tab.h"
78
79
#ifndef DYECMD
80
#include "dragdrop.h"
81
#else  // DYECMD
82
#include "resources/image/image.h"
83
#endif  // DYECMD
84
#include "settings.h"
85
86
#include "listeners/focuslistener.h"
87
#include "listeners/guiconfiglistener.h"
88
#include "listeners/keylistener.h"
89
90
#include "input/inputmanager.h"
91
92
#include "input/touch/touchmanager.h"
93
94
#include "render/renderers.h"
95
96
#include "resources/imageset.h"
97
98
#include "resources/resourcemanager/resourcemanager.h"
99
100
#include "utils/delete2.h"
101
#include "utils/foreach.h"
102
#include "utils/langs.h"
103
#include "utils/sdlsharedhelper.h"
104
#include "utils/timer.h"
105
106
#include "net/ipc.h"
107
108
#include "debug.h"
109
110
Gui *gui = nullptr;
111
Font *boldFont = nullptr;
112
113
127
Gui::Gui() :
114
    mTop(nullptr),
115
    mGraphics(nullptr),
116
    mInput(nullptr),
117
127
    mFocusHandler(new FocusHandler),
118
    mKeyListeners(),
119
    mLastMousePressButton(MouseButton::EMPTY),
120
    mLastMousePressTimeStamp(0U),
121
    mLastMouseX(0),
122
    mLastMouseY(0),
123
    mClickCount(1),
124
    mLastMouseDragButton(MouseButton::EMPTY),
125
    mWidgetWithMouseQueue(),
126
254
    mConfigListener(new GuiConfigListener(this)),
127
    mGuiFont(nullptr),
128
    mInfoParticleFont(nullptr),
129
    mHelpFont(nullptr),
130
    mSecureFont(nullptr),
131
    mNpcFont(nullptr),
132
    mMouseCursors(nullptr),
133
    mMouseCursorAlpha(1.0F),
134
    mMouseInactivityTimer(0),
135
    mCursorType(Cursor::CURSOR_POINTER),
136
#ifdef ANDROID
137
    mLastMouseRealX(0),
138
    mLastMouseRealY(0),
139
#endif  // ANDROID
140
    mFocusListeners(),
141
127
    mForegroundColor(theme->getColor(ThemeColorId::TEXT, 255)),
142
127
    mForegroundColor2(theme->getColor(ThemeColorId::TEXT_OUTLINE, 255)),
143
    mTime(0),
144
    mTime10(0),
145
    mCustomCursor(false),
146
1016
    mDoubleClick(true)
147
{
148
127
}
149
150
127
void Gui::postInit(Graphics *const graphics)
151
{
152
127
    logger->log1("Initializing GUI...");
153
    // Set graphics
154
127
    setGraphics(graphics);
155
156
    // Set input
157
127
    guiInput = new SDLInput;
158
254
    setInput(guiInput);
159
160
    // Set focus handler
161
127
    delete mFocusHandler;
162
127
    mFocusHandler = new FocusHandler;
163
164
    // Initialize top GUI widget
165
127
    WindowContainer *const guiTop = new WindowContainer(nullptr);
166
127
    guiTop->setFocusable(true);
167
127
    guiTop->setSize(graphics->mWidth, graphics->mHeight);
168
254
    guiTop->setOpaque(Opaque_false);
169
127
    Window::setWindowContainer(guiTop);
170
127
    setTop(guiTop);
171
172
254
    const StringVect langs = getLang();
173
238
    const bool isJapan = (!langs.empty() && langs[0].size() > 3
174


587
        && langs[0].substr(0, 3) == "ja_");
175
238
    const bool isChinese = (!langs.empty() && langs[0].size() > 3
176


587
        && langs[0].substr(0, 3) == "zh_");
177
178
    // Set global font
179

508
    const int fontSize = config.getIntValue("fontSize");
180

1016
    std::string fontFile = config.getValue("font", "");
181
127
    if (isJapan)
182
    {
183
        fontFile = config.getValue("japanFont", "");
184
        if (fontFile.empty())
185
            fontFile = branding.getStringValue("japanFont");
186
    }
187
127
    else if (isChinese)
188
    {
189
        fontFile = config.getValue("chinaFont", "");
190
        if (fontFile.empty())
191
            fontFile = branding.getStringValue("chinaFont");
192
    }
193
127
    if (fontFile.empty())
194

635
        fontFile = branding.getStringValue("font");
195
196

254
    mGuiFont = new Font(fontFile, fontSize, TTF_STYLE_NORMAL);
197
198
    // Set particle font
199

1016
    fontFile = config.getValue("particleFont", "");
200
127
    if (isJapan)
201
    {
202
        fontFile = config.getValue("japanFont", "");
203
        if (fontFile.empty())
204
            fontFile = branding.getStringValue("japanFont");
205
    }
206
127
    else if (isChinese)
207
    {
208
        fontFile = config.getValue("chinaFont", "");
209
        if (fontFile.empty())
210
            fontFile = branding.getStringValue("chinaFont");
211
    }
212
127
    if (fontFile.empty())
213

635
        fontFile = branding.getStringValue("particleFont");
214
215
127
    mInfoParticleFont = new Font(fontFile, fontSize, TTF_STYLE_BOLD);
216
217
    // Set bold font
218

1016
    fontFile = config.getValue("boldFont", "");
219
127
    if (fontFile.empty())
220

635
        fontFile = branding.getStringValue("boldFont");
221
222

254
    boldFont = new Font(fontFile, fontSize, TTF_STYLE_NORMAL);
223
224
    // Set help font
225

1016
    fontFile = config.getValue("helpFont", "");
226
127
    if (fontFile.empty())
227

635
        fontFile = branding.getStringValue("helpFont");
228
229

254
    mHelpFont = new Font(fontFile, fontSize, TTF_STYLE_NORMAL);
230
231
    // Set secure font
232

1016
    fontFile = config.getValue("secureFont", "");
233
127
    if (fontFile.empty())
234

635
        fontFile = branding.getStringValue("secureFont");
235
236

254
    mSecureFont = new Font(fontFile, fontSize, TTF_STYLE_NORMAL);
237
238
    // Set npc font
239

508
    const int npcFontSize = config.getIntValue("npcfontSize");
240

1016
    fontFile = config.getValue("npcFont", "");
241
127
    if (isJapan)
242
    {
243
        fontFile = config.getValue("japanFont", "");
244
        if (fontFile.empty())
245
            fontFile = branding.getStringValue("japanFont");
246
    }
247
127
    else if (isChinese)
248
    {
249
        fontFile = config.getValue("chinaFont", "");
250
        if (fontFile.empty())
251
            fontFile = branding.getStringValue("chinaFont");
252
    }
253
127
    if (fontFile.empty())
254

635
        fontFile = branding.getStringValue("npcFont");
255
256

254
    mNpcFont = new Font(fontFile, npcFontSize, TTF_STYLE_NORMAL);
257
258
127
    Widget::setGlobalFont(mGuiFont);
259
260
    // Initialize mouse cursor and listen for changes to the option
261

508
    setUseCustomCursor(config.getBoolValue("customcursor"));
262

635
    setDoubleClick(config.getBoolValue("doubleClick"));
263

508
    config.addListener("customcursor", mConfigListener);
264

508
    config.addListener("doubleClick", mConfigListener);
265
127
}
266
267
635
Gui::~Gui()
268
{
269
127
    config.removeListeners(mConfigListener);
270
254
    delete2(mConfigListener);
271
272
127
    if (mMouseCursors != nullptr)
273
    {
274
127
        mMouseCursors->decRef();
275
127
        mMouseCursors = nullptr;
276
    }
277
278
127
    if (windowContainer != nullptr)
279
127
        windowContainer->slowLogic();
280
127
    Widget *top = mTop;
281
127
    if (Widget::widgetExists(mTop))
282
        setTop(nullptr);
283
127
    Window::setWindowContainer(nullptr);
284
127
    delete top;
285
286
127
    delete2(mGuiFont);
287
127
    delete2(boldFont);
288
127
    delete2(mHelpFont);
289
127
    delete2(mSecureFont);
290
127
    delete2(mInfoParticleFont);
291
127
    delete2(mNpcFont);
292
254
    delete2(guiInput);
293
127
    delete2(theme);
294
295
127
    delete2(mFocusHandler);
296
127
    Label::finalCleanup();
297
127
    Tab::finalCleanup();
298
    Widget::cleanGlobalFont();
299
127
}
300
301
void Gui::logic()
302
{
303
    BLOCK_START("Gui::logic")
304
    ResourceManager::clearScheduled();
305
306
    if (mTop == nullptr)
307
    {
308
        BLOCK_END("Gui::logic")
309
        return;
310
    }
311
312
    handleModalFocus();
313
    handleModalMouseInputFocus();
314
315
    if (guiInput != nullptr)
316
        handleMouseInput();
317
318
    mTop->logic();
319
    BLOCK_END("Gui::logic")
320
}
321
322
void Gui::slowLogic()
323
{
324
    BLOCK_START("Gui::slowLogic")
325
    Palette::advanceGradients();
326
327
    // Fade out mouse cursor after extended inactivity
328
    if (mMouseInactivityTimer < 100 * 15)
329
    {
330
        ++mMouseInactivityTimer;
331
        mMouseCursorAlpha = std::min(1.0F, mMouseCursorAlpha + 0.05F);
332
    }
333
    else
334
    {
335
        mMouseCursorAlpha = std::max(0.0F, mMouseCursorAlpha - 0.005F);
336
    }
337
    if (mGuiFont != nullptr)
338
        mGuiFont->slowLogic(0);
339
    if (mInfoParticleFont != nullptr)
340
        mInfoParticleFont->slowLogic(1);
341
    if (mHelpFont != nullptr)
342
        mHelpFont->slowLogic(2);
343
    if (mSecureFont != nullptr)
344
        mSecureFont->slowLogic(3);
345
    if (boldFont != nullptr)
346
        boldFont->slowLogic(4);
347
    if (mNpcFont != nullptr)
348
        mNpcFont->slowLogic(5);
349
    if (windowContainer != nullptr)
350
        windowContainer->slowLogic();
351
352
    const time_t time = cur_time;
353
    if (mTime != time)
354
    {
355
        logger->flush();
356
        if (ipc != nullptr)
357
            ipc->flush();
358
        mTime = time;
359
360
        if (time > mTime10 || mTime10 - time > 10)
361
        {
362
            mTime10 = time + 10;
363
            ResourceManager::cleanOrphans(false);
364
            guiInput->simulateMouseMove();
365
        }
366
    }
367
368
    BLOCK_END("Gui::slowLogic")
369
}
370
371
void Gui::clearFonts()
372
{
373
    if (mGuiFont != nullptr)
374
        mGuiFont->clear();
375
    if (mInfoParticleFont != nullptr)
376
        mInfoParticleFont->clear();
377
    if (mHelpFont != nullptr)
378
        mHelpFont->clear();
379
    if (mSecureFont != nullptr)
380
        mSecureFont->clear();
381
    if (boldFont != nullptr)
382
        boldFont->clear();
383
    if (mNpcFont != nullptr)
384
        mNpcFont->clear();
385
}
386
387
bool Gui::handleInput()
388
{
389
    if (mInput != nullptr)
390
        return handleKeyInput();
391
    return false;
392
}
393
394
bool Gui::handleKeyInput()
395
{
396
    if (guiInput == nullptr)
397
        return false;
398
399
    BLOCK_START("Gui::handleKeyInput")
400
    bool consumed(false);
401
402
    while (!mInput->isKeyQueueEmpty())
403
    {
404
        const KeyInput keyInput = guiInput->dequeueKeyInput();
405
406
        KeyEvent eventToGlobalKeyListeners(nullptr,
407
            keyInput.getType(),
408
            keyInput.getActionId(), keyInput.getKey());
409
410
#ifdef USE_SDL2
411
        if (!keyInput.getText().empty())
412
            eventToGlobalKeyListeners.setText(keyInput.getText());
413
#endif  // USE_SDL2
414
415
        distributeKeyEventToGlobalKeyListeners(
416
            eventToGlobalKeyListeners);
417
418
        // If a global key listener consumes the event it will not be
419
        // sent further to the source of the event.
420
        if (eventToGlobalKeyListeners.isConsumed())
421
        {
422
            consumed = true;
423
            continue;
424
        }
425
426
        if (mFocusHandler != nullptr)
427
        {
428
            bool eventConsumed = false;
429
430
            // Send key inputs to the focused widgets
431
            if (mFocusHandler->getFocused() != nullptr)
432
            {
433
                KeyEvent event(getKeyEventSource(),
434
                    keyInput.getType(),
435
                    keyInput.getActionId(), keyInput.getKey());
436
#ifdef USE_SDL2
437
                if (!keyInput.getText().empty())
438
                    event.setText(keyInput.getText());
439
#endif  // USE_SDL2
440
441
                if (!mFocusHandler->getFocused()->isFocusable())
442
                    mFocusHandler->focusNone();
443
                else
444
                    distributeKeyEvent(event);
445
446
                eventConsumed = event.isConsumed();
447
                if (eventConsumed)
448
                    consumed = true;
449
            }
450
451
            // If the key event hasn't been consumed and
452
            // tabbing is enable check for tab press and
453
            // change focus.
454
            if (!eventConsumed && keyInput.getActionId()
455
                == InputAction::GUI_TAB &&
456
                keyInput.getType() == KeyEventType::PRESSED)
457
            {
458
                if (inputManager.isActionActive(InputAction::GUI_MOD))
459
                    mFocusHandler->tabPrevious();
460
                else
461
                    mFocusHandler->tabNext();
462
            }
463
        }
464
    }  // end while
465
    BLOCK_END("Gui::handleKeyInput")
466
    return consumed;
467
}
468
469
67
void Gui::draw()
470
{
471
    BLOCK_START("Gui::draw 1")
472
67
    Widget *const top = getTop();
473
67
    if (top == nullptr)
474
        return;
475
134
    mGraphics->pushClipArea(top->getDimension());
476
477
67
    if (isBatchDrawRenders(openGLMode))
478
    {
479
67
        top->draw(mGraphics);
480
67
        touchManager.draw();
481
    }
482
    else
483
    {
484
        top->safeDraw(mGraphics);
485
        touchManager.safeDraw();
486
    }
487
488
    int mouseX;
489
    int mouseY;
490
67
    const MouseStateType button = getMouseState(mouseX, mouseY);
491
492

67
    if ((settings.mouseFocused ||
493
67
        ((button & SDL_BUTTON(1)) != 0)) &&
494
134
        mMouseCursors != nullptr &&
495
134
        mCustomCursor &&
496
67
        mMouseCursorAlpha > 0.0F)
497
    {
498
#ifndef DYECMD
499
67
        const Image *const image = dragDrop.getItemImage();
500
67
        if (mGuiFont != nullptr)
501
        {
502
67
            const std::string &str = dragDrop.getText();
503
67
            if (!str.empty())
504
            {
505
                const int posX = mouseX - mGuiFont->getWidth(str) / 2;
506
                const int posY = mouseY +
507
                    (image != nullptr ? image->mBounds.h / 2 : 0);
508
                mGuiFont->drawString(mGraphics,
509
                    mForegroundColor, mForegroundColor2,
510
                    str,
511
                    posX, posY);
512
            }
513
        }
514
67
        if (image != nullptr)
515
        {
516
            const int posX = mouseX - (image->mBounds.w / 2);
517
            const int posY = mouseY - (image->mBounds.h / 2);
518
            mGraphics->drawImage(image, posX, posY);
519
        }
520
#endif  // DYECMD
521
522
67
        Image *const mouseCursor = mMouseCursors->get(
523
134
            CAST_SIZE(mCursorType));
524
67
        if (mouseCursor != nullptr)
525
        {
526
67
            mouseCursor->setAlpha(mMouseCursorAlpha);
527
67
            mGraphics->drawImage(mouseCursor, mouseX - 15, mouseY - 17);
528
        }
529
    }
530
531
67
    mGraphics->popClipArea();
532
    BLOCK_END("Gui::draw 1")
533
}
534
535
void Gui::videoResized() const
536
{
537
    WindowContainer *const top = static_cast<WindowContainer *>(getTop());
538
539
    if (top != nullptr)
540
    {
541
        const int oldWidth = top->getWidth();
542
        const int oldHeight = top->getHeight();
543
544
        top->setSize(mainGraphics->mWidth, mainGraphics->mHeight);
545
        top->adjustAfterResize(oldWidth, oldHeight);
546
    }
547
548
    if (viewport != nullptr)
549
        viewport->videoResized();
550
    Widget::distributeWindowResizeEvent();
551
}
552
553
127
void Gui::setUseCustomCursor(const bool customCursor)
554
{
555
127
    if (settings.options.hideCursor)
556
    {
557
        SDL::showCursor(false);
558
        return;
559
    }
560
127
    if (customCursor != mCustomCursor)
561
    {
562
127
        mCustomCursor = customCursor;
563
564
127
        if (mCustomCursor)
565
        {
566
            // Hide the SDL mouse cursor
567
127
            SDL::showCursor(false);
568
569
            // Load the mouse cursor
570
127
            if (mMouseCursors != nullptr)
571
                mMouseCursors->decRef();
572
508
            mMouseCursors = Theme::getImageSetFromTheme("mouse.png", 40, 40);
573
574
127
            if (mMouseCursors == nullptr)
575
                logger->log("Error: Unable to load mouse cursors.");
576
        }
577
        else
578
        {
579
            // Show the SDL mouse cursor
580
            SDL::showCursor(true);
581
582
            // Unload the mouse cursor
583
            if (mMouseCursors != nullptr)
584
            {
585
                mMouseCursors->decRef();
586
                mMouseCursors = nullptr;
587
            }
588
        }
589
    }
590
}
591
592
void Gui::handleMouseMoved(const MouseInput &mouseInput)
593
{
594
    // Check if the mouse leaves the application window.
595
    if (mTop != nullptr &&
596
        !mWidgetWithMouseQueue.empty() &&
597
        (mouseInput.getX() < 0 ||
598
        mouseInput.getY() < 0 ||
599
        !mTop->getDimension().isPointInRect(mouseInput.getX(),
600
        mouseInput.getY())))
601
    {
602
        // Distribute an event to all widgets in the
603
        // "widget with mouse" queue.
604
        while (!mWidgetWithMouseQueue.empty())
605
        {
606
            Widget *const widget = mWidgetWithMouseQueue.front();
607
608
            if (Widget::widgetExists(widget))
609
            {
610
                distributeMouseEvent(widget,
611
                    MouseEventType::EXITED,
612
                    mouseInput.getButton(),
613
                    mouseInput.getX(),
614
                    mouseInput.getY(),
615
                    true,
616
                    true);
617
            }
618
619
            mWidgetWithMouseQueue.pop_front();
620
        }
621
622
        mMouseInactivityTimer = 0;
623
        return;
624
    }
625
626
    const int mouseX = mouseInput.getX();
627
    const int mouseY = mouseInput.getY();
628
    const MouseButtonT button = mouseInput.getButton();
629
630
    // Check if there is a need to send mouse exited events by
631
    // traversing the "widget with mouse" queue.
632
    bool widgetWithMouseQueueCheckDone = mWidgetWithMouseQueue.empty();
633
    while (!widgetWithMouseQueueCheckDone)
634
    {
635
        unsigned int iterations = 0;
636
        for (std::deque<Widget*>::iterator
637
             iter = mWidgetWithMouseQueue.begin();
638
             iter != mWidgetWithMouseQueue.end();
639
             ++ iter)
640
        {
641
            Widget *const widget = *iter;
642
643
            // If a widget in the "widget with mouse queue" doesn't
644
            // exists anymore it should be removed from the queue.
645
            if (!Widget::widgetExists(widget))
646
            {
647
                mWidgetWithMouseQueue.erase(iter);
648
                break;
649
            }
650
            else
651
            {
652
                int x;
653
                int y;
654
                widget->getAbsolutePosition(x, y);
655
656
                if (x > mouseX
657
                    || y > mouseY
658
                    || x + widget->getWidth() <= mouseX
659
                    || y + widget->getHeight() <= mouseY
660
                    || !widget->isVisible())
661
                {
662
                    distributeMouseEvent(widget,
663
                        MouseEventType::EXITED,
664
                        button,
665
                        mouseX,
666
                        mouseY,
667
                        true,
668
                        true);
669
                    mClickCount = 1;
670
                    mLastMousePressTimeStamp = 0U;
671
                    mWidgetWithMouseQueue.erase(iter);
672
                    break;
673
                }
674
            }
675
676
            iterations++;
677
        }
678
679
        widgetWithMouseQueueCheckDone =
680
            (CAST_SIZE(iterations) == mWidgetWithMouseQueue.size());
681
    }
682
683
    // Check all widgets below the mouse to see if they are
684
    // present in the "widget with mouse" queue. If a widget
685
    // is not then it should be added and an entered event should
686
    // be sent to it.
687
    Widget* parent = getMouseEventSource(mouseX, mouseY);
688
    Widget* widget = parent;
689
690
    // If a widget has modal mouse input focus then it will
691
    // always be returned from getMouseEventSource, but we only wan't to
692
    // send mouse entered events if the mouse has actually entered the
693
    // widget with modal mouse input focus, hence we need to check if
694
    // that's the case. If it's not we should simply ignore to send any
695
    // mouse entered events.
696
    if ((mFocusHandler->getModalMouseInputFocused() != nullptr)
697
        && widget == mFocusHandler->getModalMouseInputFocused()
698
        && Widget::widgetExists(widget) &&
699
        (widget != nullptr))
700
    {
701
        int x;
702
        int y;
703
        widget->getAbsolutePosition(x, y);
704
705
        if (x > mouseX || y > mouseY
706
            || x + widget->getWidth() <= mouseX
707
            || y + widget->getHeight() <= mouseY)
708
        {
709
            parent = nullptr;
710
        }
711
    }
712
713
    while (parent != nullptr)
714
    {
715
        parent = widget->getParent();
716
717
        // Check if the widget is present in the "widget with mouse" queue.
718
        bool widgetIsPresentInQueue = false;
719
        FOR_EACH (std::deque<Widget*>::const_iterator,
720
                  iter, mWidgetWithMouseQueue)
721
        {
722
            if (*iter == widget)
723
            {
724
                widgetIsPresentInQueue = true;
725
                break;
726
            }
727
        }
728
729
        // Widget is not present, send an entered event and add
730
        // it to the "widget with mouse" queue.
731
        if (!widgetIsPresentInQueue
732
            && Widget::widgetExists(widget))
733
        {
734
            distributeMouseEvent(widget,
735
                MouseEventType::ENTERED,
736
                button,
737
                mouseX,
738
                mouseY,
739
                true,
740
                true);
741
            mWidgetWithMouseQueue.push_front(widget);
742
        }
743
744
        const Widget *const swap = widget;
745
        widget = parent;
746
        parent = swap->getParent();
747
    }
748
749
    if (mFocusHandler->getDraggedWidget() != nullptr)
750
    {
751
        distributeMouseEvent(mFocusHandler->getDraggedWidget(),
752
            MouseEventType::DRAGGED,
753
            mLastMouseDragButton,
754
            mouseX,
755
            mouseY,
756
            false,
757
            false);
758
    }
759
    else
760
    {
761
        Widget *const sourceWidget = getMouseEventSource(mouseX, mouseY);
762
        distributeMouseEvent(sourceWidget,
763
            MouseEventType::MOVED,
764
            button,
765
            mouseX,
766
            mouseY,
767
            false,
768
            false);
769
    }
770
    mMouseInactivityTimer = 0;
771
}
772
773
void Gui::handleMousePressed(const MouseInput &mouseInput)
774
{
775
    const int x = mouseInput.getX();
776
    const int y = mouseInput.getY();
777
    const MouseButtonT button = mouseInput.getButton();
778
    const unsigned int timeStamp = mouseInput.getTimeStamp();
779
780
    Widget *sourceWidget = getMouseEventSource(x, y);
781
782
    if (mFocusHandler->getDraggedWidget() != nullptr)
783
        sourceWidget = mFocusHandler->getDraggedWidget();
784
785
    if (sourceWidget == nullptr)
786
        return;
787
    int sourceWidgetX;
788
    int sourceWidgetY;
789
    sourceWidget->getAbsolutePosition(sourceWidgetX, sourceWidgetY);
790
791
    if (((mFocusHandler->getModalFocused() != nullptr)
792
        && sourceWidget->isModalFocused())
793
        || (mFocusHandler->getModalFocused() == nullptr))
794
    {
795
        sourceWidget->requestFocus();
796
    }
797
798
    if (mDoubleClick &&
799
        timeStamp - mLastMousePressTimeStamp < 250U &&
800
        mLastMousePressButton == button)
801
    {
802
        mClickCount ++;
803
    }
804
    else
805
    {
806
        mClickCount = 1;
807
    }
808
809
    distributeMouseEvent(sourceWidget,
810
        MouseEventType::PRESSED,
811
        button,
812
        x,
813
        y,
814
        false,
815
        false);
816
    mFocusHandler->setLastWidgetPressed(sourceWidget);
817
    mFocusHandler->setDraggedWidget(sourceWidget);
818
    mLastMouseDragButton = button;
819
    mLastMousePressButton = button;
820
    mLastMousePressTimeStamp = timeStamp;
821
}
822
823
void Gui::updateFonts()
824
{
825
    const int fontSize = config.getIntValue("fontSize");
826
    std::string fontFile = config.getValue("font", "");
827
    if (fontFile.empty())
828
        fontFile = branding.getStringValue("font");
829
830
    mGuiFont->loadFont(fontFile, fontSize, TTF_STYLE_NORMAL);
831
832
    fontFile = config.getValue("particleFont", "");
833
    if (fontFile.empty())
834
        fontFile = branding.getStringValue("particleFont");
835
836
    mInfoParticleFont->loadFont(fontFile, fontSize, TTF_STYLE_BOLD);
837
838
    fontFile = config.getValue("boldFont", "");
839
    if (fontFile.empty())
840
        fontFile = branding.getStringValue("boldFont");
841
842
    boldFont->loadFont(fontFile, fontSize, TTF_STYLE_NORMAL);
843
844
    const int npcFontSize = config.getIntValue("npcfontSize");
845
846
    fontFile = config.getValue("npcFont", "");
847
    if (fontFile.empty())
848
        fontFile = branding.getStringValue("npcFont");
849
850
    mNpcFont->loadFont(fontFile, npcFontSize, TTF_STYLE_NORMAL);
851
}
852
853
void Gui::distributeMouseEvent(Widget *const source,
854
                               const MouseEventTypeT type,
855
                               const MouseButtonT button,
856
                               const int x, const int y,
857
                               const bool force,
858
                               const bool toSourceOnly)
859
{
860
    if ((source == nullptr) || (mFocusHandler == nullptr))
861
        return;
862
863
    Widget *widget = source;
864
865
    if (!force)
866
    {
867
        if (mFocusHandler->getModalFocused() != nullptr
868
            && !widget->isModalFocused())
869
        {
870
            return;
871
        }
872
        if (mFocusHandler->getModalMouseInputFocused() != nullptr
873
            && !widget->isModalMouseInputFocused())
874
        {
875
            return;
876
        }
877
    }
878
879
    MouseEvent event(source,
880
        type, button,
881
        x, y, mClickCount);
882
883
    Widget* parent = source;
884
    while (parent != nullptr)
885
    {
886
        // If the widget has been removed due to input
887
        // cancel the distribution.
888
        if (!Widget::widgetExists(widget))
889
            break;
890
891
        parent = widget->getParent();
892
893
        if (widget->isEnabled() || force)
894
        {
895
            int widgetX;
896
            int widgetY;
897
            widget->getAbsolutePosition(widgetX, widgetY);
898
899
            event.setX(x - widgetX);
900
            event.setY(y - widgetY);
901
902
            std::list<MouseListener*> mouseListeners
903
                = widget->getMouseListeners();
904
905
            const MouseEventTypeT mouseType = event.getType();
906
            // Send the event to all mouse listeners of the widget.
907
            FOR_EACH (std::list<MouseListener*>::const_iterator,
908
                 it, mouseListeners)
909
            {
910
                switch (mouseType)
911
                {
912
                    case MouseEventType::ENTERED:
913
                        (*it)->mouseEntered(event);
914
                        break;
915
                    case MouseEventType::EXITED:
916
                        (*it)->mouseExited(event);
917
                        break;
918
                    case MouseEventType::MOVED:
919
                        (*it)->mouseMoved(event);
920
                        break;
921
                    case MouseEventType::PRESSED:
922
                        (*it)->mousePressed(event);
923
                        break;
924
                    case MouseEventType::RELEASED:
925
                    case MouseEventType::RELEASED2:
926
                        (*it)->mouseReleased(event);
927
                        break;
928
                    case MouseEventType::WHEEL_MOVED_UP:
929
                        (*it)->mouseWheelMovedUp(event);
930
                        break;
931
                    case MouseEventType::WHEEL_MOVED_DOWN:
932
                        (*it)->mouseWheelMovedDown(event);
933
                        break;
934
                    case MouseEventType::DRAGGED:
935
                        (*it)->mouseDragged(event);
936
                        break;
937
                    case MouseEventType::CLICKED:
938
                        (*it)->mouseClicked(event);
939
                        break;
940
                    default:
941
                        break;
942
                }
943
            }
944
945
            if (toSourceOnly)
946
                break;
947
        }
948
949
        const Widget *const swap = widget;
950
        widget = parent;
951
        parent = swap->getParent();
952
953
#ifndef DYECMD
954
        if (type == MouseEventType::RELEASED)
955
            dragDrop.clear();
956
#endif  // DYECMD
957
958
        if ((widget == nullptr) || event.isConsumed())
959
            break;
960
961
        // If a non modal focused widget has been reach
962
        // and we have modal focus cancel the distribution.
963
        if ((mFocusHandler->getModalFocused() != nullptr)
964
            && !widget->isModalFocused())
965
        {
966
            break;
967
        }
968
969
        // If a non modal mouse input focused widget has been reach
970
        // and we have modal mouse input focus cancel the distribution.
971
        if ((mFocusHandler->getModalMouseInputFocused() != nullptr)
972
            && !widget->isModalMouseInputFocused())
973
        {
974
            break;
975
        }
976
    }
977
}
978
979
void Gui::resetClickCount()
980
{
981
    mClickCount = 1;
982
    mLastMousePressTimeStamp = 0;
983
}
984
985
35
MouseEvent *Gui::createMouseEvent(Window *const widget)
986
{
987

35
    if ((viewport == nullptr) || (widget == nullptr))
988
        return nullptr;
989
990
    int x = 0;
991
    int y = 0;
992
    int mouseX = 0;
993
    int mouseY = 0;
994
995
    getAbsolutePosition(widget, x, y);
996
    getMouseState(mouseX, mouseY);
997
998
    return new MouseEvent(widget,
999
        MouseEventType::MOVED,
1000
        MouseButton::EMPTY,
1001
        mouseX - x,
1002
        mouseY - y,
1003
        mClickCount);
1004
}
1005
1006
void Gui::getAbsolutePosition(Widget *restrict widget,
1007
                              int &restrict x,
1008
                              int &restrict y)
1009
{
1010
    if (widget == nullptr)
1011
        return;
1012
    x = 0;
1013
    y = 0;
1014
    while (widget->getParent() != nullptr)
1015
    {
1016
        x += widget->getX();
1017
        y += widget->getY();
1018
        widget = widget->getParent();
1019
    }
1020
}
1021
1022
void Gui::handleMouseInput()
1023
{
1024
    BLOCK_START("Gui::handleMouseInput")
1025
    while (!mInput->isMouseQueueEmpty())
1026
    {
1027
        const MouseInput mouseInput = guiInput->dequeueMouseInput();
1028
1029
        if (touchManager.processEvent(mouseInput))
1030
        {
1031
#ifdef ANDROID
1032
#ifndef USE_SDL2
1033
            SDL_WarpMouse(mLastMouseX, mLastMouseY,
1034
                mLastMouseRealX, mLastMouseRealY);
1035
#endif  // USE_SDL2
1036
#endif  // ANDROID
1037
1038
            mMouseInactivityTimer = 0;
1039
            continue;
1040
        }
1041
1042
        // Save the current mouse state. It will be needed if modal focus
1043
        // changes or modal mouse input focus changes.
1044
        mLastMouseX = mouseInput.getX();
1045
        mLastMouseY = mouseInput.getY();
1046
#ifdef ANDROID
1047
        mLastMouseRealX = mouseInput.getRealX();
1048
        mLastMouseRealY = mouseInput.getRealY();
1049
#endif  // ANDROID
1050
1051
        switch (mouseInput.getType())
1052
        {
1053
            case MouseEventType::PRESSED:
1054
                handleMousePressed(mouseInput);
1055
                break;
1056
            case MouseEventType::RELEASED:
1057
                handleMouseReleased(mouseInput);
1058
                break;
1059
            case MouseEventType::MOVED:
1060
                handleMouseMoved(mouseInput);
1061
                break;
1062
            case MouseEventType::WHEEL_MOVED_DOWN:
1063
                handleMouseWheelMovedDown(mouseInput);
1064
                break;
1065
            case MouseEventType::WHEEL_MOVED_UP:
1066
                handleMouseWheelMovedUp(mouseInput);
1067
                break;
1068
            case MouseEventType::CLICKED:
1069
            case MouseEventType::ENTERED:
1070
            case MouseEventType::EXITED:
1071
            case MouseEventType::DRAGGED:
1072
            case MouseEventType::RELEASED2:
1073
            default:
1074
                break;
1075
        }
1076
    }
1077
    BLOCK_END("Gui::handleMouseInput")
1078
}
1079
1080
void Gui::handleMouseReleased(const MouseInput &mouseInput)
1081
{
1082
    Widget *sourceWidget = getMouseEventSource(
1083
        mouseInput.getX(), mouseInput.getY());
1084
1085
    int sourceWidgetX;
1086
    int sourceWidgetY;
1087
    if (mFocusHandler->getDraggedWidget() != nullptr)
1088
    {
1089
        if (sourceWidget != mFocusHandler->getLastWidgetPressed())
1090
            mFocusHandler->setLastWidgetPressed(nullptr);
1091
1092
        Widget *const oldWidget = sourceWidget;
1093
        sourceWidget = mFocusHandler->getDraggedWidget();
1094
        if ((oldWidget != nullptr) && oldWidget != sourceWidget)
1095
        {
1096
            oldWidget->getAbsolutePosition(sourceWidgetX, sourceWidgetY);
1097
            distributeMouseEvent(oldWidget,
1098
                MouseEventType::RELEASED2,
1099
                mouseInput.getButton(),
1100
                mouseInput.getX(),
1101
                mouseInput.getY(),
1102
                false,
1103
                false);
1104
        }
1105
    }
1106
1107
    if (sourceWidget == nullptr)
1108
        return;
1109
    sourceWidget->getAbsolutePosition(sourceWidgetX, sourceWidgetY);
1110
    distributeMouseEvent(sourceWidget,
1111
        MouseEventType::RELEASED,
1112
        mouseInput.getButton(),
1113
        mouseInput.getX(),
1114
        mouseInput.getY(),
1115
        false,
1116
        false);
1117
1118
    if (mouseInput.getButton() == mLastMousePressButton
1119
        && mFocusHandler->getLastWidgetPressed() == sourceWidget)
1120
    {
1121
        distributeMouseEvent(sourceWidget,
1122
            MouseEventType::CLICKED,
1123
            mouseInput.getButton(),
1124
            mouseInput.getX(),
1125
            mouseInput.getY(),
1126
            false,
1127
            false);
1128
1129
        mFocusHandler->setLastWidgetPressed(nullptr);
1130
    }
1131
    else
1132
    {
1133
        mLastMousePressButton = MouseButton::EMPTY;
1134
        mClickCount = 0;
1135
    }
1136
1137
    if (mFocusHandler->getDraggedWidget() != nullptr)
1138
        mFocusHandler->setDraggedWidget(nullptr);
1139
}
1140
1141
95
void Gui::addGlobalFocusListener(FocusListener* focusListener)
1142
{
1143
190
    mFocusListeners.push_back(focusListener);
1144
95
}
1145
1146
95
void Gui::removeGlobalFocusListener(FocusListener* focusListener)
1147
{
1148
95
    mFocusListeners.remove(focusListener);
1149
95
}
1150
1151
24
void Gui::distributeGlobalFocusGainedEvent(const Event &focusEvent)
1152
{
1153
77
    for (FocusListenerIterator iter = mFocusListeners.begin();
1154
58
         iter != mFocusListeners.end();
1155
         ++ iter)
1156
    {
1157
5
        (*iter)->focusGained(focusEvent);
1158
    }
1159
24
}
1160
1161
2459
void Gui::removeDragged(const Widget *const widget)
1162
{
1163
2459
    if (mFocusHandler == nullptr)
1164
        return;
1165
1166
2459
    if (mFocusHandler->getDraggedWidget() == widget)
1167
        mFocusHandler->setDraggedWidget(nullptr);
1168
}
1169
1170
2
MouseStateType Gui::getMouseState(int &x, int &y)
1171
{
1172
69
    const MouseStateType res = SDL_GetMouseState(&x, &y);
1173
69
    const int scale = mainGraphics->getScale();
1174
69
    x /= scale;
1175
69
    y /= scale;
1176
2
    return res;
1177
}
1178
1179
127
void Gui::setTop(Widget *const top)
1180
{
1181

254
    if (mTop != nullptr)
1182
127
        mTop->setFocusHandler(nullptr);
1183
127
    if (top != nullptr)
1184
127
        top->setFocusHandler(mFocusHandler);
1185
1186
254
    mTop = top;
1187
127
}
1188
1189
void Gui::setGraphics(Graphics *const graphics)
1190
{
1191
127
    mGraphics = graphics;
1192
}
1193
1194
Graphics* Gui::getGraphics() const
1195
{
1196
    return mGraphics;
1197
}
1198
1199
void Gui::setInput(SDLInput *const input)
1200
{
1201
127
    mInput = input;
1202
}
1203
1204
SDLInput* Gui::getInput() const
1205
{
1206
    return mInput;
1207
}
1208
1209
void Gui::addGlobalKeyListener(KeyListener *const keyListener)
1210
{
1211
    mKeyListeners.push_back(keyListener);
1212
}
1213
1214
void Gui::removeGlobalKeyListener(KeyListener *const keyListener)
1215
{
1216
    mKeyListeners.remove(keyListener);
1217
}
1218
1219
void Gui::handleMouseWheelMovedDown(const MouseInput& mouseInput)
1220
{
1221
    Widget* sourceWidget = getMouseEventSource(
1222
        mouseInput.getX(), mouseInput.getY());
1223
1224
    if (mFocusHandler->getDraggedWidget() != nullptr)
1225
        sourceWidget = mFocusHandler->getDraggedWidget();
1226
1227
    if (sourceWidget != nullptr)
1228
    {
1229
        int sourceWidgetX = 0;
1230
        int sourceWidgetY = 0;
1231
1232
        sourceWidget->getAbsolutePosition(sourceWidgetX, sourceWidgetY);
1233
        distributeMouseEvent(sourceWidget,
1234
            MouseEventType::WHEEL_MOVED_DOWN,
1235
            mouseInput.getButton(),
1236
            mouseInput.getX(),
1237
            mouseInput.getY(),
1238
            false,
1239
            false);
1240
    }
1241
}
1242
1243
void Gui::handleMouseWheelMovedUp(const MouseInput& mouseInput)
1244
{
1245
    Widget* sourceWidget = getMouseEventSource(
1246
        mouseInput.getX(), mouseInput.getY());
1247
1248
    if (mFocusHandler->getDraggedWidget() != nullptr)
1249
        sourceWidget = mFocusHandler->getDraggedWidget();
1250
1251
    if (sourceWidget != nullptr)
1252
    {
1253
        int sourceWidgetX;
1254
        int sourceWidgetY;
1255
1256
        sourceWidget->getAbsolutePosition(sourceWidgetX, sourceWidgetY);
1257
        distributeMouseEvent(sourceWidget,
1258
            MouseEventType::WHEEL_MOVED_UP,
1259
            mouseInput.getButton(),
1260
            mouseInput.getX(),
1261
            mouseInput.getY(),
1262
            false,
1263
            false);
1264
    }
1265
}
1266
1267
Widget* Gui::getWidgetAt(const int x, const int y) const
1268
{
1269
    // If the widget's parent has no child then we have found the widget..
1270
    Widget* parent = mTop;
1271
    Widget* child = mTop;
1272
    Widget* selectable = mTop;
1273
1274
    while (child != nullptr)
1275
    {
1276
        Widget *const swap = child;
1277
        int parentX;
1278
        int parentY;
1279
        parent->getAbsolutePosition(parentX, parentY);
1280
        child = parent->getWidgetAt(x - parentX, y - parentY);
1281
        parent = swap;
1282
        if (parent->isSelectable())
1283
            selectable = parent;
1284
    }
1285
1286
    return selectable;
1287
}
1288
1289
Widget* Gui::getMouseEventSource(const int x, const int y) const
1290
{
1291
    Widget *const widget = getWidgetAt(x, y);
1292
    if (widget == nullptr)
1293
        return nullptr;
1294
1295
    if (mFocusHandler != nullptr &&
1296
        mFocusHandler->getModalMouseInputFocused() != nullptr &&
1297
        !widget->isModalMouseInputFocused())
1298
    {
1299
        return mFocusHandler->getModalMouseInputFocused();
1300
    }
1301
1302
    return widget;
1303
}
1304
1305
Widget* Gui::getKeyEventSource() const
1306
{
1307
    Widget* widget = mFocusHandler->getFocused();
1308
1309
    while (widget != nullptr &&
1310
           widget->getInternalFocusHandler() != nullptr &&
1311
           widget->getInternalFocusHandler()->getFocused() != nullptr)
1312
    {
1313
        widget = widget->getInternalFocusHandler()->getFocused();
1314
    }
1315
1316
    return widget;
1317
}
1318
1319
void Gui::distributeKeyEvent(KeyEvent &event) const
1320
{
1321
    Widget* parent = event.getSource();
1322
    Widget* widget = parent;
1323
1324
    if (parent == nullptr)
1325
        return;
1326
    if (mFocusHandler->getModalFocused() != nullptr &&
1327
        !widget->isModalFocused())
1328
    {
1329
        return;
1330
    }
1331
    if (mFocusHandler->getModalMouseInputFocused() != nullptr &&
1332
        !widget->isModalMouseInputFocused())
1333
    {
1334
        return;
1335
    }
1336
1337
    while (parent != nullptr)
1338
    {
1339
        // If the widget has been removed due to input
1340
        // cancel the distribution.
1341
        if (!Widget::widgetExists(widget))
1342
            break;
1343
1344
        parent = widget->getParent();
1345
1346
        if (widget->isEnabled())
1347
        {
1348
            std::list<KeyListener*> keyListeners
1349
                = widget->getKeyListeners();
1350
1351
            const KeyEventTypeT eventType = event.getType();
1352
            // Send the event to all key listeners of the source widget.
1353
            FOR_EACH (std::list<KeyListener*>::const_iterator,
1354
                 it, keyListeners)
1355
            {
1356
                switch (eventType)
1357
                {
1358
                    case KeyEventType::PRESSED:
1359
                        (*it)->keyPressed(event);
1360
                        break;
1361
                    case KeyEventType::RELEASED:
1362
                        (*it)->keyReleased(event);
1363
                        break;
1364
                    default:
1365
                        break;
1366
                }
1367
            }
1368
        }
1369
1370
        const Widget *const swap = widget;
1371
        widget = parent;
1372
        parent = swap->getParent();
1373
1374
        // If a non modal focused widget has been reach
1375
        // and we have modal focus cancel the distribution.
1376
        if ((widget != nullptr) &&
1377
            (mFocusHandler->getModalFocused() != nullptr) &&
1378
            !widget->isModalFocused())
1379
        {
1380
            break;
1381
        }
1382
    }
1383
}
1384
1385
void Gui::distributeKeyEventToGlobalKeyListeners(KeyEvent& event)
1386
{
1387
    BLOCK_START("Gui::distributeKeyEventToGlobalKeyListeners")
1388
    const KeyEventTypeT eventType = event.getType();
1389
    FOR_EACH (KeyListenerListIterator, it, mKeyListeners)
1390
    {
1391
        switch (eventType)
1392
        {
1393
            case KeyEventType::PRESSED:
1394
                (*it)->keyPressed(event);
1395
                break;
1396
            case KeyEventType::RELEASED:
1397
                (*it)->keyReleased(event);
1398
                break;
1399
            default:
1400
                break;
1401
        }
1402
1403
        if (event.isConsumed())
1404
            break;
1405
    }
1406
    BLOCK_END("Gui::distributeKeyEventToGlobalKeyListeners")
1407
}
1408
1409
void Gui::handleModalMouseInputFocus()
1410
{
1411
    BLOCK_START("Gui::handleModalMouseInputFocus")
1412
    Widget *const lastModalWidget
1413
        = mFocusHandler->getLastWidgetWithModalMouseInputFocus();
1414
    Widget *const modalWidget = mFocusHandler->getModalMouseInputFocused();
1415
    if (lastModalWidget != modalWidget)
1416
    {
1417
        // Check if modal mouse input focus has been gained by a widget.
1418
        if (lastModalWidget == nullptr)
1419
        {
1420
            handleModalFocusGained();
1421
            mFocusHandler->setLastWidgetWithModalMouseInputFocus(modalWidget);
1422
        }
1423
        // Check if modal mouse input focus has been released.
1424
        else
1425
        {
1426
            handleModalFocusReleased();
1427
            mFocusHandler->setLastWidgetWithModalMouseInputFocus(nullptr);
1428
        }
1429
    }
1430
    BLOCK_END("Gui::handleModalMouseInputFocus")
1431
}
1432
1433
void Gui::handleModalFocus()
1434
{
1435
    BLOCK_START("Gui::handleModalFocus")
1436
    Widget *const lastModalWidget
1437
        = mFocusHandler->getLastWidgetWithModalFocus();
1438
    Widget *const modalWidget = mFocusHandler->getModalFocused();
1439
1440
    if (lastModalWidget != modalWidget)
1441
    {
1442
        // Check if modal focus has been gained by a widget.
1443
        if (lastModalWidget == nullptr)
1444
        {
1445
            handleModalFocusGained();
1446
            mFocusHandler->setLastWidgetWithModalFocus(modalWidget);
1447
        }
1448
        // Check if modal focus has been released.
1449
        else
1450
        {
1451
            handleModalFocusReleased();
1452
            mFocusHandler->setLastWidgetWithModalFocus(nullptr);
1453
        }
1454
    }
1455
    BLOCK_END("Gui::handleModalFocus")
1456
}
1457
1458
void Gui::handleModalFocusGained()
1459
{
1460
    // Distribute an event to all widgets in the "widget with mouse" queue.
1461
    while (!mWidgetWithMouseQueue.empty())
1462
    {
1463
        Widget *const widget = mWidgetWithMouseQueue.front();
1464
1465
        if (Widget::widgetExists(widget))
1466
        {
1467
            distributeMouseEvent(widget,
1468
                MouseEventType::EXITED,
1469
                mLastMousePressButton,
1470
                mLastMouseX,
1471
                mLastMouseY,
1472
                true,
1473
                true);
1474
        }
1475
1476
        mWidgetWithMouseQueue.pop_front();
1477
    }
1478
1479
    mFocusHandler->setLastWidgetWithModalMouseInputFocus(
1480
        mFocusHandler->getModalMouseInputFocused());
1481
}
1482
1483
void Gui::handleModalFocusReleased()
1484
{
1485
      // Check all widgets below the mouse to see if they are
1486
    // present in the "widget with mouse" queue. If a widget
1487
    // is not then it should be added and an entered event should
1488
    // be sent to it.
1489
    Widget* widget = getMouseEventSource(mLastMouseX, mLastMouseY);
1490
    Widget* parent = widget;
1491
1492
    while (parent != nullptr &&
1493
           widget != nullptr)
1494
    {
1495
        parent = widget->getParent();
1496
1497
        // Check if the widget is present in the "widget with mouse" queue.
1498
        bool widgetIsPresentInQueue = false;
1499
        FOR_EACH (std::deque<Widget*>::const_iterator,
1500
                  iter, mWidgetWithMouseQueue)
1501
        {
1502
            if (*iter == widget)
1503
            {
1504
                widgetIsPresentInQueue = true;
1505
                break;
1506
            }
1507
        }
1508
1509
        // Widget is not present, send an entered event and add
1510
        // it to the "widget with mouse" queue.
1511
        if (!widgetIsPresentInQueue && Widget::widgetExists(widget))
1512
        {
1513
            distributeMouseEvent(widget,
1514
                MouseEventType::ENTERED,
1515
                mLastMousePressButton,
1516
                mLastMouseX,
1517
                mLastMouseY,
1518
                false,
1519
                true);
1520
            mWidgetWithMouseQueue.push_front(widget);
1521
        }
1522
1523
        const Widget *const swap = widget;
1524
        widget = parent;
1525
        parent = swap->getParent();
1526
    }
1527
}
1528
1529
int Gui::getMousePressLength() const
1530
{
1531
    if (mLastMousePressTimeStamp == 0U)
1532
        return 0;
1533
    unsigned int ticks = SDL_GetTicks();
1534
    if (ticks > mLastMousePressTimeStamp)
1535
        return ticks - mLastMousePressTimeStamp;
1536
    return mLastMousePressTimeStamp - ticks;
1537
2
}