GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/gui.cpp Lines: 152 629 24.2 %
Date: 2021-03-17 Branches: 129 782 16.5 %

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


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


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

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

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

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

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

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

635
        fontFile = branding.getStringValue("particleFont");
215
216

254
    mInfoParticleFont = new Font(fontFile, fontSize, TTF_STYLE_BOLD);
217
218
    // Set bold font
219

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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