GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/gui.cpp Lines: 152 633 24.0 %
Date: 2018-06-18 21:15:20 Branches: 130 740 17.6 %

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


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


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

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

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

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

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

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

1260
        fontFile = branding.getStringValue("particleFont");
214
215

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

2016
    fontFile = config.getValue("boldFont", "");
219
252
    if (fontFile.empty())
220

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

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

2016
    fontFile = config.getValue("helpFont", "");
226
252
    if (fontFile.empty())
227

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

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

2016
    fontFile = config.getValue("secureFont", "");
233
252
    if (fontFile.empty())
234

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

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

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

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

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

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

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

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

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

1008
    config.addListener("doubleClick", mConfigListener);
265
252
}
266
267
1260
Gui::~Gui()
268
{
269
252
    config.removeListeners(mConfigListener);
270
504
    delete2(mConfigListener);
271
272
252
    if (mMouseCursors != nullptr)
273
    {
274
252
        mMouseCursors->decRef();
275
252
        mMouseCursors = nullptr;
276
    }
277
278
252
    if (windowContainer != nullptr)
279
252
        windowContainer->slowLogic();
280
252
    Widget *top = mTop;
281
252
    if (Widget::widgetExists(mTop))
282
        setTop(nullptr);
283
252
    Window::setWindowContainer(nullptr);
284
252
    delete top;
285
286
252
    delete2(mGuiFont);
287
252
    delete2(boldFont);
288
252
    delete2(mHelpFont);
289
252
    delete2(mSecureFont);
290
252
    delete2(mInfoParticleFont);
291
252
    delete2(mNpcFont);
292
504
    delete2(guiInput);
293
252
    delete2(theme);
294
295
252
    delete2(mFocusHandler);
296
252
    Label::finalCleanup();
297
252
    Tab::finalCleanup();
298
    Widget::cleanGlobalFont();
299
252
}
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
134
void Gui::draw()
470
{
471
    BLOCK_START("Gui::draw 1")
472
134
    Widget *const top = getTop();
473
134
    if (top == nullptr)
474
        return;
475
268
    mGraphics->pushClipArea(top->getDimension());
476
477
134
    if (isBatchDrawRenders(openGLMode))
478
    {
479
134
        top->draw(mGraphics);
480
134
        touchManager.draw();
481
    }
482
    else
483
    {
484
        top->safeDraw(mGraphics);
485
        touchManager.safeDraw();
486
    }
487
488
    int mouseX;
489
    int mouseY;
490
134
    const MouseStateType button = getMouseState(mouseX, mouseY);
491
492

134
    if ((settings.mouseFocused ||
493
134
        ((button & SDL_BUTTON(1)) != 0)) &&
494
268
        mMouseCursors != nullptr &&
495
268
        mCustomCursor &&
496
134
        mMouseCursorAlpha > 0.0F)
497
    {
498
#ifndef DYECMD
499
134
        const Image *const image = dragDrop.getItemImage();
500
134
        if (mGuiFont != nullptr)
501
        {
502
134
            const std::string &str = dragDrop.getText();
503
134
            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
134
        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
268
        Image *const mouseCursor = mMouseCursors->get(
523
268
            CAST_SIZE(mCursorType));
524
134
        if (mouseCursor != nullptr)
525
        {
526
134
            mouseCursor->setAlpha(mMouseCursorAlpha);
527
134
            mGraphics->drawImage(mouseCursor, mouseX - 15, mouseY - 17);
528
        }
529
    }
530
531
134
    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
252
void Gui::setUseCustomCursor(const bool customCursor)
554
{
555
252
    if (settings.options.hideCursor)
556
    {
557
        SDL::showCursor(false);
558
        return;
559
    }
560
252
    if (customCursor != mCustomCursor)
561
    {
562
252
        mCustomCursor = customCursor;
563
564
252
        if (mCustomCursor)
565
        {
566
            // Hide the SDL mouse cursor
567
252
            SDL::showCursor(false);
568
569
            // Load the mouse cursor
570
252
            if (mMouseCursors != nullptr)
571
                mMouseCursors->decRef();
572

1008
            mMouseCursors = Theme::getImageSetFromTheme("mouse.png", 40, 40);
573
574
252
            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, y;
702
        widget->getAbsolutePosition(x, y);
703
704
        if (x > mouseX || y > mouseY
705
            || x + widget->getWidth() <= mouseX
706
            || y + widget->getHeight() <= mouseY)
707
        {
708
            parent = nullptr;
709
        }
710
    }
711
712
    while (parent != nullptr)
713
    {
714
        parent = widget->getParent();
715
716
        // Check if the widget is present in the "widget with mouse" queue.
717
        bool widgetIsPresentInQueue = false;
718
        FOR_EACH (std::deque<Widget*>::const_iterator,
719
                  iter, mWidgetWithMouseQueue)
720
        {
721
            if (*iter == widget)
722
            {
723
                widgetIsPresentInQueue = true;
724
                break;
725
            }
726
        }
727
728
        // Widget is not present, send an entered event and add
729
        // it to the "widget with mouse" queue.
730
        if (!widgetIsPresentInQueue
731
            && Widget::widgetExists(widget))
732
        {
733
            distributeMouseEvent(widget,
734
                MouseEventType::ENTERED,
735
                button,
736
                mouseX,
737
                mouseY,
738
                true,
739
                true);
740
            mWidgetWithMouseQueue.push_front(widget);
741
        }
742
743
        const Widget *const swap = widget;
744
        widget = parent;
745
        parent = swap->getParent();
746
    }
747
748
    if (mFocusHandler->getDraggedWidget() != nullptr)
749
    {
750
        distributeMouseEvent(mFocusHandler->getDraggedWidget(),
751
            MouseEventType::DRAGGED,
752
            mLastMouseDragButton,
753
            mouseX,
754
            mouseY,
755
            false,
756
            false);
757
    }
758
    else
759
    {
760
        Widget *const sourceWidget = getMouseEventSource(mouseX, mouseY);
761
        distributeMouseEvent(sourceWidget,
762
            MouseEventType::MOVED,
763
            button,
764
            mouseX,
765
            mouseY,
766
            false,
767
            false);
768
    }
769
    mMouseInactivityTimer = 0;
770
}
771
772
void Gui::handleMousePressed(const MouseInput &mouseInput)
773
{
774
    const int x = mouseInput.getX();
775
    const int y = mouseInput.getY();
776
    const MouseButtonT button = mouseInput.getButton();
777
    const unsigned int timeStamp = mouseInput.getTimeStamp();
778
779
    Widget *sourceWidget = getMouseEventSource(x, y);
780
781
    if (mFocusHandler->getDraggedWidget() != nullptr)
782
        sourceWidget = mFocusHandler->getDraggedWidget();
783
784
    if (sourceWidget == nullptr)
785
        return;
786
    int sourceWidgetX;
787
    int sourceWidgetY;
788
    sourceWidget->getAbsolutePosition(sourceWidgetX, sourceWidgetY);
789
790
    if (((mFocusHandler->getModalFocused() != nullptr)
791
        && sourceWidget->isModalFocused())
792
        || (mFocusHandler->getModalFocused() == nullptr))
793
    {
794
        sourceWidget->requestFocus();
795
    }
796
797
    if (mDoubleClick &&
798
        timeStamp - mLastMousePressTimeStamp < 250U &&
799
        mLastMousePressButton == button)
800
    {
801
        mClickCount ++;
802
    }
803
    else
804
    {
805
        mClickCount = 1;
806
    }
807
808
    distributeMouseEvent(sourceWidget,
809
        MouseEventType::PRESSED,
810
        button,
811
        x,
812
        y,
813
        false,
814
        false);
815
    mFocusHandler->setLastWidgetPressed(sourceWidget);
816
    mFocusHandler->setDraggedWidget(sourceWidget);
817
    mLastMouseDragButton = button;
818
    mLastMousePressButton = button;
819
    mLastMousePressTimeStamp = timeStamp;
820
}
821
822
void Gui::updateFonts()
823
{
824
    const int fontSize = config.getIntValue("fontSize");
825
    std::string fontFile = config.getValue("font", "");
826
    if (fontFile.empty())
827
        fontFile = branding.getStringValue("font");
828
829
    mGuiFont->loadFont(fontFile, fontSize, TTF_STYLE_NORMAL);
830
831
    fontFile = config.getValue("particleFont", "");
832
    if (fontFile.empty())
833
        fontFile = branding.getStringValue("particleFont");
834
835
    mInfoParticleFont->loadFont(fontFile, fontSize, TTF_STYLE_BOLD);
836
837
    fontFile = config.getValue("boldFont", "");
838
    if (fontFile.empty())
839
        fontFile = branding.getStringValue("boldFont");
840
841
    boldFont->loadFont(fontFile, fontSize, TTF_STYLE_NORMAL);
842
843
    const int npcFontSize = config.getIntValue("npcfontSize");
844
845
    fontFile = config.getValue("npcFont", "");
846
    if (fontFile.empty())
847
        fontFile = branding.getStringValue("npcFont");
848
849
    mNpcFont->loadFont(fontFile, npcFontSize, TTF_STYLE_NORMAL);
850
}
851
852
void Gui::distributeMouseEvent(Widget *const source,
853
                               const MouseEventTypeT type,
854
                               const MouseButtonT button,
855
                               const int x, const int y,
856
                               const bool force,
857
                               const bool toSourceOnly)
858
{
859
    if ((source == nullptr) || (mFocusHandler == nullptr))
860
        return;
861
862
    Widget *widget = source;
863
864
    if (!force)
865
    {
866
        if (mFocusHandler->getModalFocused() != nullptr
867
            && !widget->isModalFocused())
868
        {
869
            return;
870
        }
871
        if (mFocusHandler->getModalMouseInputFocused() != nullptr
872
            && !widget->isModalMouseInputFocused())
873
        {
874
            return;
875
        }
876
    }
877
878
    MouseEvent event(source,
879
        type, button,
880
        x, y, mClickCount);
881
882
    Widget* parent = source;
883
    while (parent != nullptr)
884
    {
885
        // If the widget has been removed due to input
886
        // cancel the distribution.
887
        if (!Widget::widgetExists(widget))
888
            break;
889
890
        parent = widget->getParent();
891
892
        if (widget->isEnabled() || force)
893
        {
894
            int widgetX;
895
            int widgetY;
896
            widget->getAbsolutePosition(widgetX, widgetY);
897
898
            event.setX(x - widgetX);
899
            event.setY(y - widgetY);
900
901
            std::list<MouseListener*> mouseListeners
902
                = widget->getMouseListeners();
903
904
            const MouseEventTypeT mouseType = event.getType();
905
            // Send the event to all mouse listeners of the widget.
906
            FOR_EACH (std::list<MouseListener*>::const_iterator,
907
                 it, mouseListeners)
908
            {
909
                switch (mouseType)
910
                {
911
                    case MouseEventType::ENTERED:
912
                        (*it)->mouseEntered(event);
913
                        break;
914
                    case MouseEventType::EXITED:
915
                        (*it)->mouseExited(event);
916
                        break;
917
                    case MouseEventType::MOVED:
918
                        (*it)->mouseMoved(event);
919
                        break;
920
                    case MouseEventType::PRESSED:
921
                        (*it)->mousePressed(event);
922
                        break;
923
                    case MouseEventType::RELEASED:
924
                    case MouseEventType::RELEASED2:
925
                        (*it)->mouseReleased(event);
926
                        break;
927
                    case MouseEventType::WHEEL_MOVED_UP:
928
                        (*it)->mouseWheelMovedUp(event);
929
                        break;
930
                    case MouseEventType::WHEEL_MOVED_DOWN:
931
                        (*it)->mouseWheelMovedDown(event);
932
                        break;
933
                    case MouseEventType::DRAGGED:
934
                        (*it)->mouseDragged(event);
935
                        break;
936
                    case MouseEventType::CLICKED:
937
                        (*it)->mouseClicked(event);
938
                        break;
939
                    default:
940
                        break;
941
                }
942
            }
943
944
            if (toSourceOnly)
945
                break;
946
        }
947
948
        const Widget *const swap = widget;
949
        widget = parent;
950
        parent = swap->getParent();
951
952
#ifndef DYECMD
953
        if (type == MouseEventType::RELEASED)
954
            dragDrop.clear();
955
#endif  // DYECMD
956
957
        if ((widget == nullptr) || event.isConsumed())
958
            break;
959
960
        // If a non modal focused widget has been reach
961
        // and we have modal focus cancel the distribution.
962
        if ((mFocusHandler->getModalFocused() != nullptr)
963
            && !widget->isModalFocused())
964
        {
965
            break;
966
        }
967
968
        // If a non modal mouse input focused widget has been reach
969
        // and we have modal mouse input focus cancel the distribution.
970
        if ((mFocusHandler->getModalMouseInputFocused() != nullptr)
971
            && !widget->isModalMouseInputFocused())
972
        {
973
            break;
974
        }
975
    }
976
}
977
978
void Gui::resetClickCount()
979
{
980
    mClickCount = 1;
981
    mLastMousePressTimeStamp = 0;
982
}
983
984
69
MouseEvent *Gui::createMouseEvent(Window *const widget)
985
{
986

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

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