GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/gui.cpp Lines: 152 633 24.0 %
Date: 2017-11-29 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-2017  The ManaPlus Developers
6
 *
7
 *  This file is part of The ManaPlus Client.
8
 *
9
 *  This program is free software; you can redistribute it and/or modify
10
 *  it under the terms of the GNU General Public License as published by
11
 *  the Free Software Foundation; either version 2 of the License, or
12
 *  any later version.
13
 *
14
 *  This program is distributed in the hope that it will be useful,
15
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 *  GNU General Public License for more details.
18
 *
19
 *  You should have received a copy of the GNU General Public License
20
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 */
22
23
/*      _______   __   __   __   ______   __   __   _______   __   __
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
248
Gui::Gui() :
114
    mTop(nullptr),
115
    mGraphics(nullptr),
116
    mInput(nullptr),
117
248
    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
248
    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
248
    mForegroundColor(theme->getColor(ThemeColorId::TEXT, 255)),
142
248
    mForegroundColor2(theme->getColor(ThemeColorId::TEXT_OUTLINE, 255)),
143
    mTime(0),
144
    mTime10(0),
145
    mCustomCursor(false),
146
1984
    mDoubleClick(true)
147
{
148
248
}
149
150
248
void Gui::postInit(Graphics *const graphics)
151
{
152
248
    logger->log1("Initializing GUI...");
153
    // Set graphics
154
248
    setGraphics(graphics);
155
156
    // Set input
157
248
    guiInput = new SDLInput;
158
496
    setInput(guiInput);
159
160
    // Set focus handler
161
248
    delete mFocusHandler;
162
248
    mFocusHandler = new FocusHandler;
163
164
    // Initialize top GUI widget
165
248
    WindowContainer *const guiTop = new WindowContainer(nullptr);
166
248
    guiTop->setFocusable(true);
167
248
    guiTop->setSize(graphics->mWidth, graphics->mHeight);
168
496
    guiTop->setOpaque(Opaque_false);
169
248
    Window::setWindowContainer(guiTop);
170
248
    setTop(guiTop);
171
172
496
    const StringVect langs = getLang();
173
466
    const bool isJapan = (!langs.empty() && langs[0].size() > 3
174


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


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

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

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

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

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

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

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

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

1984
    fontFile = config.getValue("boldFont", "");
219
248
    if (fontFile.empty())
220

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

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

1984
    fontFile = config.getValue("helpFont", "");
226
248
    if (fontFile.empty())
227

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

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

1984
    fontFile = config.getValue("secureFont", "");
233
248
    if (fontFile.empty())
234

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

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

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

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

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

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

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

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

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

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

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

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

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

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