GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/gui.cpp Lines: 152 633 24.0 %
Date: 2019-03-24 Branches: 127 782 16.2 %

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-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
/*      _______   __   __   __   ______   __   __   _______   __   __
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
99
void Gui::addGlobalFocusListener(FocusListener* focusListener)
1142
{
1143
198
    mFocusListeners.push_back(focusListener);
1144
99
}
1145
1146
99
void Gui::removeGlobalFocusListener(FocusListener* focusListener)
1147
{
1148
99
    mFocusListeners.remove(focusListener);
1149
99
}
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
2479
void Gui::removeDragged(const Widget *const widget)
1162
{
1163
2479
    if (mFocusHandler == nullptr)
1164
        return;
1165
1166
2479
    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
}