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

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2004-2009  The Mana World Development Team
4
 *  Copyright (C) 2009-2010  The Mana Developers
5
 *  Copyright (C) 2011-2019  The ManaPlus Developers
6
 *  Copyright (C) 2019-2021  Andrei Karas
7
 *
8
 *  This file is part of The ManaPlus Client.
9
 *
10
 *  This program is free software; you can redistribute it and/or modify
11
 *  it under the terms of the GNU General Public License as published by
12
 *  the Free Software Foundation; either version 2 of the License, or
13
 *  any later version.
14
 *
15
 *  This program is distributed in the hope that it will be useful,
16
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 *  GNU General Public License for more details.
19
 *
20
 *  You should have received a copy of the GNU General Public License
21
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
22
 */
23
24
/*      _______   __   __   __   ______   __   __   _______   __   __
25
 *     / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___  /\ /  |\/ /\
26
 *    / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
27
 *   / / /__   / / // / // / // / /    / ___  / // ___  / // /| ' / /
28
 *  / /_// /\ / /_// / // / // /_/_   / / // / // /\_/ / // / |  / /
29
 * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
30
 * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
31
 *
32
 * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
33
 *
34
 *
35
 * Per Larsson a.k.a finalman
36
 * Olof Naessén a.k.a jansem/yakslem
37
 *
38
 * Visit: http://guichan.sourceforge.net
39
 *
40
 * License: (BSD)
41
 * Redistribution and use in source and binary forms, with or without
42
 * modification, are permitted provided that the following conditions
43
 * are met:
44
 * 1. Redistributions of source code must retain the above copyright
45
 *    notice, this list of conditions and the following disclaimer.
46
 * 2. Redistributions in binary form must reproduce the above copyright
47
 *    notice, this list of conditions and the following disclaimer in
48
 *    the documentation and/or other materials provided with the
49
 *    distribution.
50
 * 3. Neither the name of Guichan nor the names of its contributors may
51
 *    be used to endorse or promote products derived from this software
52
 *    without specific prior written permission.
53
 *
54
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
55
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
56
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
57
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
58
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
59
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
60
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
61
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
62
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
63
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
64
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
65
 */
66
67
#include "gui/gui.h"
68
69
#include "gui/focushandler.h"
70
#include "gui/sdlinput.h"
71
#include "gui/viewport.h"
72
73
#include "gui/fonts/font.h"
74
75
#include "gui/widgets/label.h"
76
#include "gui/widgets/window.h"
77
78
#include "gui/widgets/tabs/tab.h"
79
80
#ifndef DYECMD
81
#include "dragdrop.h"
82
#else  // DYECMD
83
#include "resources/image/image.h"
84
#endif  // DYECMD
85
#include "settings.h"
86
87
#include "listeners/focuslistener.h"
88
#include "listeners/guiconfiglistener.h"
89
#include "listeners/keylistener.h"
90
91
#include "input/inputmanager.h"
92
93
#include "input/touch/touchmanager.h"
94
95
#include "render/renderers.h"
96
97
#include "resources/imageset.h"
98
99
#include "resources/resourcemanager/resourcemanager.h"
100
101
#include "utils/delete2.h"
102
#include "utils/foreach.h"
103
#include "utils/langs.h"
104
#include "utils/sdlsharedhelper.h"
105
#include "utils/timer.h"
106
107
#include "net/ipc.h"
108
109
#include "debug.h"
110
111
Gui *gui = nullptr;
112
Font *boldFont = nullptr;
113
114
127
Gui::Gui() :
115
    mTop(nullptr),
116
    mGraphics(nullptr),
117
    mInput(nullptr),
118
✓✗
127
    mFocusHandler(new FocusHandler),
119
    mKeyListeners(),
120
    mLastMousePressButton(MouseButton::EMPTY),
121
    mLastMousePressTimeStamp(0U),
122
    mLastMouseX(0),
123
    mLastMouseY(0),
124
    mClickCount(1),
125
    mLastMouseDragButton(MouseButton::EMPTY),
126
    mWidgetWithMouseQueue(),
127
✓✗
254
    mConfigListener(new GuiConfigListener(this)),
128
    mGuiFont(nullptr),
129
    mInfoParticleFont(nullptr),
130
    mHelpFont(nullptr),
131
    mSecureFont(nullptr),
132
    mNpcFont(nullptr),
133
    mMouseCursors(nullptr),
134
    mMouseCursorAlpha(1.0F),
135
    mMouseInactivityTimer(0),
136
    mCursorType(Cursor::CURSOR_POINTER),
137
#ifdef ANDROID
138
    mLastMouseRealX(0),
139
    mLastMouseRealY(0),
140
#endif  // ANDROID
141
    mFocusListeners(),
142
✓✗
127
    mForegroundColor(theme->getColor(ThemeColorId::TEXT, 255)),
143
✓✗
127
    mForegroundColor2(theme->getColor(ThemeColorId::TEXT_OUTLINE, 255)),
144
    mTime(0),
145
    mTime10(0),
146
    mCustomCursor(false),
147
1016
    mDoubleClick(true)
148
{
149
127
}
150
151
127
void Gui::postInit(Graphics *const graphics)
152
{
153
127
    logger->log1("Initializing GUI...");
154
    // Set graphics
155
127
    setGraphics(graphics);
156
157
    // Set input
158
✓✗
127
    guiInput = new SDLInput;
159
254
    setInput(guiInput);
160
161
    // Set focus handler
162
✓✗
127
    delete mFocusHandler;
163
✓✗
127
    mFocusHandler = new FocusHandler;
164
165
    // Initialize top GUI widget
166
✓✗
127
    WindowContainer *const guiTop = new WindowContainer(nullptr);
167
127
    guiTop->setFocusable(true);
168
127
    guiTop->setSize(graphics->mWidth, graphics->mHeight);
169
254
    guiTop->setOpaque(Opaque_false);
170
127
    Window::setWindowContainer(guiTop);
171
127
    setTop(guiTop);
172
173
254
    const StringVect langs = getLang();
174
✓✗
238
    const bool isJapan = (!langs.empty() && langs[0].size() > 3
175
✓✓✓✗
✓✗✓✓
✗✗
587
        && langs[0].substr(0, 3) == "ja_");
176
✓✗
238
    const bool isChinese = (!langs.empty() && langs[0].size() > 3
177
✓✓✓✗
✓✗✓✓
✗✗
587
        && langs[0].substr(0, 3) == "zh_");
178
179
    // Set global font
180
✓✗✓✗
508
    const int fontSize = config.getIntValue("fontSize");
181
✓✗✓✗
✓✗
1016
    std::string fontFile = config.getValue("font", "");
182
✗✓
127
    if (isJapan)
183
    {
184
        fontFile = config.getValue("japanFont", "");
185
        if (fontFile.empty())
186
            fontFile = branding.getStringValue("japanFont");
187
    }
188
✗✓
127
    else if (isChinese)
189
    {
190
        fontFile = config.getValue("chinaFont", "");
191
        if (fontFile.empty())
192
            fontFile = branding.getStringValue("chinaFont");
193
    }
194
✓✗
127
    if (fontFile.empty())
195
✓✗✓✗
635
        fontFile = branding.getStringValue("font");
196
197
✓✗✓✗
254
    mGuiFont = new Font(fontFile, fontSize, TTF_STYLE_NORMAL);
198
199
    // Set particle font
200
✓✗✓✗
✓✗
1016
    fontFile = config.getValue("particleFont", "");
201
✗✓
127
    if (isJapan)
202
    {
203
        fontFile = config.getValue("japanFont", "");
204
        if (fontFile.empty())
205
            fontFile = branding.getStringValue("japanFont");
206
    }
207
✗✓
127
    else if (isChinese)
208
    {
209
        fontFile = config.getValue("chinaFont", "");
210
        if (fontFile.empty())
211
            fontFile = branding.getStringValue("chinaFont");
212
    }
213
✓✗
127
    if (fontFile.empty())
214
✓✗✓✗
635
        fontFile = branding.getStringValue("particleFont");
215
216
✓✗✓✗
254
    mInfoParticleFont = new Font(fontFile, fontSize, TTF_STYLE_BOLD);
217
218
    // Set bold font
219
✓✗✓✗
✓✗
1016
    fontFile = config.getValue("boldFont", "");
220
✓✗
127
    if (fontFile.empty())
221
✓✗✓✗
635
        fontFile = branding.getStringValue("boldFont");
222
223
✓✗✓✗
254
    boldFont = new Font(fontFile, fontSize, TTF_STYLE_NORMAL);
224
225
    // Set help font
226
✓✗✓✗
✓✗
1016
    fontFile = config.getValue("helpFont", "");
227
✓✗
127
    if (fontFile.empty())
228
✓✗✓✗
635
        fontFile = branding.getStringValue("helpFont");
229
230
✓✗✓✗
254
    mHelpFont = new Font(fontFile, fontSize, TTF_STYLE_NORMAL);
231
232
    // Set secure font
233
✓✗✓✗
✓✗
1016
    fontFile = config.getValue("secureFont", "");
234
✓✗
127
    if (fontFile.empty())
235
✓✗✓✗
635
        fontFile = branding.getStringValue("secureFont");
236
237
✓✗✓✗
254
    mSecureFont = new Font(fontFile, fontSize, TTF_STYLE_NORMAL);
238
239
    // Set npc font
240
✓✗✓✗
508
    const int npcFontSize = config.getIntValue("npcfontSize");
241
✓✗✓✗
✓✗
1016
    fontFile = config.getValue("npcFont", "");
242
✗✓
127
    if (isJapan)
243
    {
244
        fontFile = config.getValue("japanFont", "");
245
        if (fontFile.empty())
246
            fontFile = branding.getStringValue("japanFont");
247
    }
248
✗✓
127
    else if (isChinese)
249
    {
250
        fontFile = config.getValue("chinaFont", "");
251
        if (fontFile.empty())
252
            fontFile = branding.getStringValue("chinaFont");
253
    }
254
✓✗
127
    if (fontFile.empty())
255
✓✗✓✗
635
        fontFile = branding.getStringValue("npcFont");
256
257
✓✗✓✗
254
    mNpcFont = new Font(fontFile, npcFontSize, TTF_STYLE_NORMAL);
258
259
✓✗
127
    Widget::setGlobalFont(mGuiFont);
260
261
    // Initialize mouse cursor and listen for changes to the option
262
✓✗✓✗
✓✗
508
    setUseCustomCursor(config.getBoolValue("customcursor"));
263
✓✗✓✗
635
    setDoubleClick(config.getBoolValue("doubleClick"));
264
✓✗✓✗
508
    config.addListener("customcursor", mConfigListener);
265
✓✗✓✗
508
    config.addListener("doubleClick", mConfigListener);
266
127
}
267
268
635
Gui::~Gui()
269
{
270
127
    config.removeListeners(mConfigListener);
271
✓✗
254
    delete2(mConfigListener)
272
273
✓✗
127
    if (mMouseCursors != nullptr)
274
    {
275
127
        mMouseCursors->decRef();
276
127
        mMouseCursors = nullptr;
277
    }
278
279
✓✗
127
    if (windowContainer != nullptr)
280
127
        windowContainer->slowLogic();
281
127
    Widget *top = mTop;
282
✓✗
127
    if (Widget::widgetExists(mTop))
283
        setTop(nullptr);
284
127
    Window::setWindowContainer(nullptr);
285
✓✗
127
    delete top;
286
287
✓✗
127
    delete2(mGuiFont)
288
✓✗
127
    delete2(boldFont)
289
✓✗
127
    delete2(mHelpFont)
290
✓✗
127
    delete2(mSecureFont)
291
✓✗
127
    delete2(mInfoParticleFont)
292
✓✗
127
    delete2(mNpcFont)
293
✓✗
254
    delete2(guiInput)
294
✓✓
127
    delete2(theme)
295
296
✓✗
127
    delete2(mFocusHandler)
297
127
    Label::finalCleanup();
298
127
    Tab::finalCleanup();
299
    Widget::cleanGlobalFont();
300
127
}
301
302
void Gui::logic()
303
{
304
    BLOCK_START("Gui::logic")
305
    ResourceManager::clearScheduled();
306
307
    if (mTop == nullptr)
308
    {
309
        BLOCK_END("Gui::logic")
310
        return;
311
    }
312
313
    handleModalFocus();
314
    handleModalMouseInputFocus();
315
316
    if (guiInput != nullptr)
317
        handleMouseInput();
318
319
    mTop->logic();
320
    BLOCK_END("Gui::logic")
321
}
322
323
void Gui::slowLogic()
324
{
325
    BLOCK_START("Gui::slowLogic")
326
    Palette::advanceGradients();
327
328
    // Fade out mouse cursor after extended inactivity
329
    if (mMouseInactivityTimer < 100 * 15)
330
    {
331
        ++mMouseInactivityTimer;
332
        mMouseCursorAlpha = std::min(1.0F, mMouseCursorAlpha + 0.05F);
333
    }
334
    else
335
    {
336
        mMouseCursorAlpha = std::max(0.0F, mMouseCursorAlpha - 0.005F);
337
    }
338
    if (mGuiFont != nullptr)
339
        mGuiFont->slowLogic(0);
340
    if (mInfoParticleFont != nullptr)
341
        mInfoParticleFont->slowLogic(1);
342
    if (mHelpFont != nullptr)
343
        mHelpFont->slowLogic(2);
344
    if (mSecureFont != nullptr)
345
        mSecureFont->slowLogic(3);
346
    if (boldFont != nullptr)
347
        boldFont->slowLogic(4);
348
    if (mNpcFont != nullptr)
349
        mNpcFont->slowLogic(5);
350
    if (windowContainer != nullptr)
351
        windowContainer->slowLogic();
352
353
    const time_t time = cur_time;
354
    if (mTime != time)
355
    {
356
        logger->flush();
357
        if (ipc != nullptr)
358
            ipc->flush();
359
        mTime = time;
360
361
        if (time > mTime10 || mTime10 - time > 10)
362
        {
363
            mTime10 = time + 10;
364
            ResourceManager::cleanOrphans(false);
365
            guiInput->simulateMouseMove();
366
        }
367
    }
368
369
    BLOCK_END("Gui::slowLogic")
370
}
371
372
void Gui::clearFonts()
373
{
374
    if (mGuiFont != nullptr)
375
        mGuiFont->clear();
376
    if (mInfoParticleFont != nullptr)
377
        mInfoParticleFont->clear();
378
    if (mHelpFont != nullptr)
379
        mHelpFont->clear();
380
    if (mSecureFont != nullptr)
381
        mSecureFont->clear();
382
    if (boldFont != nullptr)
383
        boldFont->clear();
384
    if (mNpcFont != nullptr)
385
        mNpcFont->clear();
386
}
387
388
bool Gui::handleInput()
389
{
390
    if (mInput != nullptr)
391
        return handleKeyInput();
392
    return false;
393
}
394
395
bool Gui::handleKeyInput()
396
{
397
    if (guiInput == nullptr)
398
        return false;
399
400
    BLOCK_START("Gui::handleKeyInput")
401
    bool consumed(false);
402
403
    while (!mInput->isKeyQueueEmpty())
404
    {
405
        const KeyInput keyInput = guiInput->dequeueKeyInput();
406
407
        KeyEvent eventToGlobalKeyListeners(nullptr,
408
            keyInput.getType(),
409
            keyInput.getActionId(), keyInput.getKey());
410
411
#ifdef USE_SDL2
412
        if (!keyInput.getText().empty())
413
            eventToGlobalKeyListeners.setText(keyInput.getText());
414
#endif  // USE_SDL2
415
416
        distributeKeyEventToGlobalKeyListeners(
417
            eventToGlobalKeyListeners);
418
419
        // If a global key listener consumes the event it will not be
420
        // sent further to the source of the event.
421
        if (eventToGlobalKeyListeners.isConsumed())
422
        {
423
            consumed = true;
424
            continue;
425
        }
426
427
        if (mFocusHandler != nullptr)
428
        {
429
            bool eventConsumed = false;
430
431
            // Send key inputs to the focused widgets
432
            if (mFocusHandler->getFocused() != nullptr)
433
            {
434
                KeyEvent event(getKeyEventSource(),
435
                    keyInput.getType(),
436
                    keyInput.getActionId(), keyInput.getKey());
437
#ifdef USE_SDL2
438
                if (!keyInput.getText().empty())
439
                    event.setText(keyInput.getText());
440
#endif  // USE_SDL2
441
442
                if (!mFocusHandler->getFocused()->isFocusable())
443
                    mFocusHandler->focusNone();
444
                else
445
                    distributeKeyEvent(event);
446
447
                eventConsumed = event.isConsumed();
448
                if (eventConsumed)
449
                    consumed = true;
450
            }
451
452
            // If the key event hasn't been consumed and
453
            // tabbing is enable check for tab press and
454
            // change focus.
455
            if (!eventConsumed && keyInput.getActionId()
456
                == InputAction::GUI_TAB &&
457
                keyInput.getType() == KeyEventType::PRESSED)
458
            {
459
                if (inputManager.isActionActive(InputAction::GUI_MOD))
460
                    mFocusHandler->tabPrevious();
461
                else
462
                    mFocusHandler->tabNext();
463
            }
464
        }
465
    }  // end while
466
    BLOCK_END("Gui::handleKeyInput")
467
    return consumed;
468
}
469
470
67
void Gui::draw()
471
{
472
    BLOCK_START("Gui::draw 1")
473
67
    Widget *const top = getTop();
474
✓✗
67
    if (top == nullptr)
475
        return;
476
134
    mGraphics->pushClipArea(top->getDimension());
477
478
✓✗
67
    if (isBatchDrawRenders(openGLMode))
479
    {
480
67
        top->draw(mGraphics);
481
67
        touchManager.draw();
482
    }
483
    else
484
    {
485
        top->safeDraw(mGraphics);
486
        touchManager.safeDraw();
487
    }
488
489
    int mouseX;
490
    int mouseY;
491
67
    const MouseStateType button = getMouseState(mouseX, mouseY);
492
493
✗✓✗✗
67
    if ((settings.mouseFocused ||
494
✓✗
67
        ((button & SDL_BUTTON(1)) != 0)) &&
495
✓✗
134
        mMouseCursors != nullptr &&
496
✓✗
134
        mCustomCursor &&
497
67
        mMouseCursorAlpha > 0.0F)
498
    {
499
#ifndef DYECMD
500
67
        const Image *const image = dragDrop.getItemImage();
501
✓✗
67
        if (mGuiFont != nullptr)
502
        {
503
67
            const std::string &str = dragDrop.getText();
504
✗✓
67
            if (!str.empty())
505
            {
506
                const int posX = mouseX - mGuiFont->getWidth(str) / 2;
507
                const int posY = mouseY +
508
                    (image != nullptr ? image->mBounds.h / 2 : 0);
509
                mGuiFont->drawString(mGraphics,
510
                    mForegroundColor, mForegroundColor2,
511
                    str,
512
                    posX, posY);
513
            }
514
        }
515
✗✓
67
        if (image != nullptr)
516
        {
517
            const int posX = mouseX - (image->mBounds.w / 2);
518
            const int posY = mouseY - (image->mBounds.h / 2);
519
            mGraphics->drawImage(image, posX, posY);
520
        }
521
#endif  // DYECMD
522
523
67
        Image *const mouseCursor = mMouseCursors->get(
524
134
            CAST_SIZE(mCursorType));
525
✓✗
67
        if (mouseCursor != nullptr)
526
        {
527
67
            mouseCursor->setAlpha(mMouseCursorAlpha);
528
67
            mGraphics->drawImage(mouseCursor, mouseX - 15, mouseY - 17);
529
        }
530
    }
531
532
67
    mGraphics->popClipArea();
533
    BLOCK_END("Gui::draw 1")
534
}
535
536
void Gui::videoResized() const
537
{
538
    WindowContainer *const top = static_cast<WindowContainer *>(getTop());
539
540
    if (top != nullptr)
541
    {
542
        const int oldWidth = top->getWidth();
543
        const int oldHeight = top->getHeight();
544
545
        top->setSize(mainGraphics->mWidth, mainGraphics->mHeight);
546
        top->adjustAfterResize(oldWidth, oldHeight);
547
    }
548
549
    if (viewport != nullptr)
550
        viewport->videoResized();
551
    Widget::distributeWindowResizeEvent();
552
}
553
554
127
void Gui::setUseCustomCursor(const bool customCursor)
555
{
556
✗✓
127
    if (settings.options.hideCursor)
557
    {
558
        SDL::showCursor(false);
559
        return;
560
    }
561
✓✗
127
    if (customCursor != mCustomCursor)
562
    {
563
127
        mCustomCursor = customCursor;
564
565
✓✗
127
        if (mCustomCursor)
566
        {
567
            // Hide the SDL mouse cursor
568
127
            SDL::showCursor(false);
569
570
            // Load the mouse cursor
571
✗✓
127
            if (mMouseCursors != nullptr)
572
                mMouseCursors->decRef();
573
✓✗
508
            mMouseCursors = Theme::getImageSetFromTheme("mouse.png", 40, 40);
574
575
✗✓
127
            if (mMouseCursors == nullptr)
576
                logger->log("Error: Unable to load mouse cursors.");
577
        }
578
        else
579
        {
580
            // Show the SDL mouse cursor
581
            SDL::showCursor(true);
582
583
            // Unload the mouse cursor
584
            if (mMouseCursors != nullptr)
585
            {
586
                mMouseCursors->decRef();
587
                mMouseCursors = nullptr;
588
            }
589
        }
590
    }
591
}
592
593
void Gui::handleMouseMoved(const MouseInput &mouseInput)
594
{
595
    // Check if the mouse leaves the application window.
596
    if (mTop != nullptr &&
597
        !mWidgetWithMouseQueue.empty() &&
598
        (mouseInput.getX() < 0 ||
599
        mouseInput.getY() < 0 ||
600
        !mTop->getDimension().isPointInRect(mouseInput.getX(),
601
        mouseInput.getY())))
602
    {
603
        // Distribute an event to all widgets in the
604
        // "widget with mouse" queue.
605
        while (!mWidgetWithMouseQueue.empty())
606
        {
607
            Widget *const widget = mWidgetWithMouseQueue.front();
608
609
            if (Widget::widgetExists(widget))
610
            {
611
                distributeMouseEvent(widget,
612
                    MouseEventType::EXITED,
613
                    mouseInput.getButton(),
614
                    mouseInput.getX(),
615
                    mouseInput.getY(),
616
                    true,
617
                    true);
618
            }
619
620
            mWidgetWithMouseQueue.pop_front();
621
        }
622
623
        mMouseInactivityTimer = 0;
624
        return;
625
    }
626
627
    const int mouseX = mouseInput.getX();
628
    const int mouseY = mouseInput.getY();
629
    const MouseButtonT button = mouseInput.getButton();
630
631
    // Check if there is a need to send mouse exited events by
632
    // traversing the "widget with mouse" queue.
633
    bool widgetWithMouseQueueCheckDone = mWidgetWithMouseQueue.empty();
634
    while (!widgetWithMouseQueueCheckDone)
635
    {
636
        unsigned int iterations = 0;
637
        for (std::deque<Widget*>::iterator
638
             iter = mWidgetWithMouseQueue.begin();
639
             iter != mWidgetWithMouseQueue.end();
640
             ++ iter)
641
        {
642
            Widget *const widget = *iter;
643
644
            // If a widget in the "widget with mouse queue" doesn't
645
            // exists anymore it should be removed from the queue.
646
            if (!Widget::widgetExists(widget))
647
            {
648
                mWidgetWithMouseQueue.erase(iter);
649
                break;
650
            }
651
            else
652
            {
653
                int x;
654
                int y;
655
                widget->getAbsolutePosition(x, y);
656
657
                if (x > mouseX
658
                    || y > mouseY
659
                    || x + widget->getWidth() <= mouseX
660
                    || y + widget->getHeight() <= mouseY
661
                    || !widget->isVisible())
662
                {
663
                    distributeMouseEvent(widget,
664
                        MouseEventType::EXITED,
665
                        button,
666
                        mouseX,
667
                        mouseY,
668
                        true,
669
                        true);
670
                    mClickCount = 1;
671
                    mLastMousePressTimeStamp = 0U;
672
                    mWidgetWithMouseQueue.erase(iter);
673
                    break;
674
                }
675
            }
676
677
            iterations++;
678
        }
679
680
        widgetWithMouseQueueCheckDone =
681
            (CAST_SIZE(iterations) == mWidgetWithMouseQueue.size());
682
    }
683
684
    // Check all widgets below the mouse to see if they are
685
    // present in the "widget with mouse" queue. If a widget
686
    // is not then it should be added and an entered event should
687
    // be sent to it.
688
    Widget* parent = getMouseEventSource(mouseX, mouseY);
689
    Widget* widget = parent;
690
691
    // If a widget has modal mouse input focus then it will
692
    // always be returned from getMouseEventSource, but we only wan't to
693
    // send mouse entered events if the mouse has actually entered the
694
    // widget with modal mouse input focus, hence we need to check if
695
    // that's the case. If it's not we should simply ignore to send any
696
    // mouse entered events.
697
    if ((mFocusHandler->getModalMouseInputFocused() != nullptr)
698
        && widget == mFocusHandler->getModalMouseInputFocused()
699
        && Widget::widgetExists(widget) &&
700
        (widget != nullptr))
701
    {
702
        int x;
703
        int y;
704
        widget->getAbsolutePosition(x, y);
705
706
        if (x > mouseX || y > mouseY
707
            || x + widget->getWidth() <= mouseX
708
            || y + widget->getHeight() <= mouseY)
709
        {
710
            parent = nullptr;
711
        }
712
    }
713
714
    while (parent != nullptr)
715
    {
716
        parent = widget->getParent();
717
718
        // Check if the widget is present in the "widget with mouse" queue.
719
        bool widgetIsPresentInQueue = false;
720
        FOR_EACH (std::deque<Widget*>::const_iterator,
721
                  iter, mWidgetWithMouseQueue)
722
        {
723
            if (*iter == widget)
724
            {
725
                widgetIsPresentInQueue = true;
726
                break;
727
            }
728
        }
729
730
        // Widget is not present, send an entered event and add
731
        // it to the "widget with mouse" queue.
732
        if (!widgetIsPresentInQueue
733
            && Widget::widgetExists(widget))
734
        {
735
            distributeMouseEvent(widget,
736
                MouseEventType::ENTERED,
737
                button,
738
                mouseX,
739
                mouseY,
740
                true,
741
                true);
742
            mWidgetWithMouseQueue.push_front(widget);
743
        }
744
745
        const Widget *const swap = widget;
746
        widget = parent;
747
        parent = swap->getParent();
748
    }
749
750
    if (mFocusHandler->getDraggedWidget() != nullptr)
751
    {
752
        distributeMouseEvent(mFocusHandler->getDraggedWidget(),
753
            MouseEventType::DRAGGED,
754
            mLastMouseDragButton,
755
            mouseX,
756
            mouseY,
757
            false,
758
            false);
759
    }
760
    else
761
    {
762
        Widget *const sourceWidget = getMouseEventSource(mouseX, mouseY);
763
        distributeMouseEvent(sourceWidget,
764
            MouseEventType::MOVED,
765
            button,
766
            mouseX,
767
            mouseY,
768
            false,
769
            false);
770
    }
771
    mMouseInactivityTimer = 0;
772
}
773
774
void Gui::handleMousePressed(const MouseInput &mouseInput)
775
{
776
    const int x = mouseInput.getX();
777
    const int y = mouseInput.getY();
778
    const MouseButtonT button = mouseInput.getButton();
779
    const unsigned int timeStamp = mouseInput.getTimeStamp();
780
781
    Widget *sourceWidget = getMouseEventSource(x, y);
782
783
    if (mFocusHandler->getDraggedWidget() != nullptr)
784
        sourceWidget = mFocusHandler->getDraggedWidget();
785
786
    if (sourceWidget == nullptr)
787
        return;
788
    int sourceWidgetX;
789
    int sourceWidgetY;
790
    sourceWidget->getAbsolutePosition(sourceWidgetX, sourceWidgetY);
791
792
    if (((mFocusHandler->getModalFocused() != nullptr)
793
        && sourceWidget->isModalFocused())
794
        || (mFocusHandler->getModalFocused() == nullptr))
795
    {
796
        sourceWidget->requestFocus();
797
    }
798
799
    if (mDoubleClick &&
800
        timeStamp - mLastMousePressTimeStamp < 250U &&
801
        mLastMousePressButton == button)
802
    {
803
        mClickCount ++;
804
    }
805
    else
806
    {
807
        mClickCount = 1;
808
    }
809
810
    distributeMouseEvent(sourceWidget,
811
        MouseEventType::PRESSED,
812
        button,
813
        x,
814
        y,
815
        false,
816
        false);
817
    mFocusHandler->setLastWidgetPressed(sourceWidget);
818
    mFocusHandler->setDraggedWidget(sourceWidget);
819
    mLastMouseDragButton = button;
820
    mLastMousePressButton = button;
821
    mLastMousePressTimeStamp = timeStamp;
822
}
823
824
void Gui::updateFonts()
825
{
826
    const int fontSize = config.getIntValue("fontSize");
827
    std::string fontFile = config.getValue("font", "");
828
    if (fontFile.empty())
829
        fontFile = branding.getStringValue("font");
830
831
    mGuiFont->loadFont(fontFile, fontSize, TTF_STYLE_NORMAL);
832
833
    fontFile = config.getValue("particleFont", "");
834
    if (fontFile.empty())
835
        fontFile = branding.getStringValue("particleFont");
836
837
    mInfoParticleFont->loadFont(fontFile, fontSize, TTF_STYLE_BOLD);
838
839
    fontFile = config.getValue("boldFont", "");
840
    if (fontFile.empty())
841
        fontFile = branding.getStringValue("boldFont");
842
843
    boldFont->loadFont(fontFile, fontSize, TTF_STYLE_NORMAL);
844
845
    const int npcFontSize = config.getIntValue("npcfontSize");
846
847
    fontFile = config.getValue("npcFont", "");
848
    if (fontFile.empty())
849
        fontFile = branding.getStringValue("npcFont");
850
851
    mNpcFont->loadFont(fontFile, npcFontSize, TTF_STYLE_NORMAL);
852
}
853
854
void Gui::distributeMouseEvent(Widget *const source,
855
                               const MouseEventTypeT type,
856
                               const MouseButtonT button,
857
                               const int x, const int y,
858
                               const bool force,
859
                               const bool toSourceOnly)
860
{
861
    if ((source == nullptr) || (mFocusHandler == nullptr))
862
        return;
863
864
    Widget *widget = source;
865
866
    if (!force)
867
    {
868
        if (mFocusHandler->getModalFocused() != nullptr
869
            && !widget->isModalFocused())
870
        {
871
            return;
872
        }
873
        if (mFocusHandler->getModalMouseInputFocused() != nullptr
874
            && !widget->isModalMouseInputFocused())
875
        {
876
            return;
877
        }
878
    }
879
880
    MouseEvent event(source,
881
        type, button,
882
        x, y, mClickCount);
883
884
    Widget* parent = source;
885
    while (parent != nullptr)
886
    {
887
        // If the widget has been removed due to input
888
        // cancel the distribution.
889
        if (!Widget::widgetExists(widget))
890
            break;
891
892
        parent = widget->getParent();
893
894
        if (widget->isEnabled() || force)
895
        {
896
            int widgetX;
897
            int widgetY;
898
            widget->getAbsolutePosition(widgetX, widgetY);
899
900
            event.setX(x - widgetX);
901
            event.setY(y - widgetY);
902
903
            std::list<MouseListener*> mouseListeners
904
                = widget->getMouseListeners();
905
906
            const MouseEventTypeT mouseType = event.getType();
907
            // Send the event to all mouse listeners of the widget.
908
            FOR_EACH (std::list<MouseListener*>::const_iterator,
909
                 it, mouseListeners)
910
            {
911
                switch (mouseType)
912
                {
913
                    case MouseEventType::ENTERED:
914
                        (*it)->mouseEntered(event);
915
                        break;
916
                    case MouseEventType::EXITED:
917
                        (*it)->mouseExited(event);
918
                        break;
919
                    case MouseEventType::MOVED:
920
                        (*it)->mouseMoved(event);
921
                        break;
922
                    case MouseEventType::PRESSED:
923
                        (*it)->mousePressed(event);
924
                        break;
925
                    case MouseEventType::RELEASED:
926
                    case MouseEventType::RELEASED2:
927
                        (*it)->mouseReleased(event);
928
                        break;
929
                    case MouseEventType::WHEEL_MOVED_UP:
930
                        (*it)->mouseWheelMovedUp(event);
931
                        break;
932
                    case MouseEventType::WHEEL_MOVED_DOWN:
933
                        (*it)->mouseWheelMovedDown(event);
934
                        break;
935
                    case MouseEventType::DRAGGED:
936
                        (*it)->mouseDragged(event);
937
                        break;
938
                    case MouseEventType::CLICKED:
939
                        (*it)->mouseClicked(event);
940
                        break;
941
                    default:
942
                        break;
943
                }
944
            }
945
946
            if (toSourceOnly)
947
                break;
948
        }
949
950
        const Widget *const swap = widget;
951
        widget = parent;
952
        parent = swap->getParent();
953
954
#ifndef DYECMD
955
        if (type == MouseEventType::RELEASED)
956
            dragDrop.clear();
957
#endif  // DYECMD
958
959
        if ((widget == nullptr) || event.isConsumed())
960
            break;
961
962
        // If a non modal focused widget has been reach
963
        // and we have modal focus cancel the distribution.
964
        if ((mFocusHandler->getModalFocused() != nullptr)
965
            && !widget->isModalFocused())
966
        {
967
            break;
968
        }
969
970
        // If a non modal mouse input focused widget has been reach
971
        // and we have modal mouse input focus cancel the distribution.
972
        if ((mFocusHandler->getModalMouseInputFocused() != nullptr)
973
            && !widget->isModalMouseInputFocused())
974
        {
975
            break;
976
        }
977
    }
978
}
979
980
void Gui::resetClickCount()
981
{
982
    mClickCount = 1;
983
    mLastMousePressTimeStamp = 0;
984
}
985
986
35
MouseEvent *Gui::createMouseEvent(Window *const widget)
987
{
988
✗✓✗✗
35
    if ((viewport == nullptr) || (widget == nullptr))
989
        return nullptr;
990
991
    int x = 0;
992
    int y = 0;
993
    int mouseX = 0;
994
    int mouseY = 0;
995
996
    getAbsolutePosition(widget, x, y);
997
    getMouseState(mouseX, mouseY);
998
999
    return new MouseEvent(widget,
1000
        MouseEventType::MOVED,
1001
        MouseButton::EMPTY,
1002
        mouseX - x,
1003
        mouseY - y,
1004
        mClickCount);
1005
}
1006
1007
void Gui::getAbsolutePosition(Widget *restrict widget,
1008
                              int &restrict x,
1009
                              int &restrict y)
1010
{
1011
    if (widget == nullptr)
1012
        return;
1013
    x = 0;
1014
    y = 0;
1015
    while (widget->getParent() != nullptr)
1016
    {
1017
        x += widget->getX();
1018
        y += widget->getY();
1019
        widget = widget->getParent();
1020
    }
1021
}
1022
1023
void Gui::handleMouseInput()
1024
{
1025
    BLOCK_START("Gui::handleMouseInput")
1026
    while (!mInput->isMouseQueueEmpty())
1027
    {
1028
        const MouseInput mouseInput = guiInput->dequeueMouseInput();
1029
1030
        if (touchManager.processEvent(mouseInput))
1031
        {
1032
#ifdef ANDROID
1033
#ifndef USE_SDL2
1034
            SDL_WarpMouse(mLastMouseX, mLastMouseY,
1035
                mLastMouseRealX, mLastMouseRealY);
1036
#endif  // USE_SDL2
1037
#endif  // ANDROID
1038
1039
            mMouseInactivityTimer = 0;
1040
            continue;
1041
        }
1042
1043
        // Save the current mouse state. It will be needed if modal focus
1044
        // changes or modal mouse input focus changes.
1045
        mLastMouseX = mouseInput.getX();
1046
        mLastMouseY = mouseInput.getY();
1047
#ifdef ANDROID
1048
        mLastMouseRealX = mouseInput.getRealX();
1049
        mLastMouseRealY = mouseInput.getRealY();
1050
#endif  // ANDROID
1051
1052
        switch (mouseInput.getType())
1053
        {
1054
            case MouseEventType::PRESSED:
1055
                handleMousePressed(mouseInput);
1056
                break;
1057
            case MouseEventType::RELEASED:
1058
                handleMouseReleased(mouseInput);
1059
                break;
1060
            case MouseEventType::MOVED:
1061
                handleMouseMoved(mouseInput);
1062
                break;
1063
            case MouseEventType::WHEEL_MOVED_DOWN:
1064
                handleMouseWheelMovedDown(mouseInput);
1065
                break;
1066
            case MouseEventType::WHEEL_MOVED_UP:
1067
                handleMouseWheelMovedUp(mouseInput);
1068
                break;
1069
            case MouseEventType::CLICKED:
1070
            case MouseEventType::ENTERED:
1071
            case MouseEventType::EXITED:
1072
            case MouseEventType::DRAGGED:
1073
            case MouseEventType::RELEASED2:
1074
            default:
1075
                break;
1076
        }
1077
    }
1078
    BLOCK_END("Gui::handleMouseInput")
1079
}
1080
1081
void Gui::handleMouseReleased(const MouseInput &mouseInput)
1082
{
1083
    Widget *sourceWidget = getMouseEventSource(
1084
        mouseInput.getX(), mouseInput.getY());
1085
1086
    int sourceWidgetX;
1087
    int sourceWidgetY;
1088
    if (mFocusHandler->getDraggedWidget() != nullptr)
1089
    {
1090
        if (sourceWidget != mFocusHandler->getLastWidgetPressed())
1091
            mFocusHandler->setLastWidgetPressed(nullptr);
1092
1093
        Widget *const oldWidget = sourceWidget;
1094
        sourceWidget = mFocusHandler->getDraggedWidget();
1095
        if ((oldWidget != nullptr) && oldWidget != sourceWidget)
1096
        {
1097
            oldWidget->getAbsolutePosition(sourceWidgetX, sourceWidgetY);
1098
            distributeMouseEvent(oldWidget,
1099
                MouseEventType::RELEASED2,
1100
                mouseInput.getButton(),
1101
                mouseInput.getX(),
1102
                mouseInput.getY(),
1103
                false,
1104
                false);
1105
        }
1106
    }
1107
1108
    if (sourceWidget == nullptr)
1109
        return;
1110
    sourceWidget->getAbsolutePosition(sourceWidgetX, sourceWidgetY);
1111
    distributeMouseEvent(sourceWidget,
1112
        MouseEventType::RELEASED,
1113
        mouseInput.getButton(),
1114
        mouseInput.getX(),
1115
        mouseInput.getY(),
1116
        false,
1117
        false);
1118
1119
    if (mouseInput.getButton() == mLastMousePressButton
1120
        && mFocusHandler->getLastWidgetPressed() == sourceWidget)
1121
    {
1122
        distributeMouseEvent(sourceWidget,
1123
            MouseEventType::CLICKED,
1124
            mouseInput.getButton(),
1125
            mouseInput.getX(),
1126
            mouseInput.getY(),
1127
            false,
1128
            false);
1129
1130
        mFocusHandler->setLastWidgetPressed(nullptr);
1131
    }
1132
    else
1133
    {
1134
        mLastMousePressButton = MouseButton::EMPTY;
1135
        mClickCount = 0;
1136
    }
1137
1138
    if (mFocusHandler->getDraggedWidget() != nullptr)
1139
        mFocusHandler->setDraggedWidget(nullptr);
1140
}
1141
1142
95
void Gui::addGlobalFocusListener(FocusListener* focusListener)
1143
{
1144
190
    mFocusListeners.push_back(focusListener);
1145
95
}
1146
1147
95
void Gui::removeGlobalFocusListener(FocusListener* focusListener)
1148
{
1149
95
    mFocusListeners.remove(focusListener);
1150
95
}
1151
1152
24
void Gui::distributeGlobalFocusGainedEvent(const Event &focusEvent)
1153
{
1154
✓✓
77
    for (FocusListenerIterator iter = mFocusListeners.begin();
1155
58
         iter != mFocusListeners.end();
1156
         ++ iter)
1157
    {
1158
5
        (*iter)->focusGained(focusEvent);
1159
    }
1160
24
}
1161
1162
2467
void Gui::removeDragged(const Widget *const widget)
1163
{
1164
✓✗
2467
    if (mFocusHandler == nullptr)
1165
        return;
1166
1167
✗✓
2467
    if (mFocusHandler->getDraggedWidget() == widget)
1168
        mFocusHandler->setDraggedWidget(nullptr);
1169
}
1170
1171
2
MouseStateType Gui::getMouseState(int &x, int &y)
1172
{
1173
69
    const MouseStateType res = SDL_GetMouseState(&x, &y);
1174
69
    const int scale = mainGraphics->getScale();
1175
69
    x /= scale;
1176
69
    y /= scale;
1177
2
    return res;
1178
}
1179
1180
127
void Gui::setTop(Widget *const top)
1181
{
1182
✗✓✓✗
254
    if (mTop != nullptr)
1183
127
        mTop->setFocusHandler(nullptr);
1184
✓✗
127
    if (top != nullptr)
1185
127
        top->setFocusHandler(mFocusHandler);
1186
1187
254
    mTop = top;
1188
127
}
1189
1190
void Gui::setGraphics(Graphics *const graphics)
1191
{
1192
127
    mGraphics = graphics;
1193
}
1194
1195
Graphics* Gui::getGraphics() const
1196
{
1197
    return mGraphics;
1198
}
1199
1200
void Gui::setInput(SDLInput *const input)
1201
{
1202
127
    mInput = input;
1203
}
1204
1205
SDLInput* Gui::getInput() const
1206
{
1207
    return mInput;
1208
}
1209
1210
void Gui::addGlobalKeyListener(KeyListener *const keyListener)
1211
{
1212
    mKeyListeners.push_back(keyListener);
1213
}
1214
1215
void Gui::removeGlobalKeyListener(KeyListener *const keyListener)
1216
{
1217
    mKeyListeners.remove(keyListener);
1218
}
1219
1220
void Gui::handleMouseWheelMovedDown(const MouseInput& mouseInput)
1221
{
1222
    Widget* sourceWidget = getMouseEventSource(
1223
        mouseInput.getX(), mouseInput.getY());
1224
1225
    if (mFocusHandler->getDraggedWidget() != nullptr)
1226
        sourceWidget = mFocusHandler->getDraggedWidget();
1227
1228
    if (sourceWidget != nullptr)
1229
    {
1230
        int sourceWidgetX = 0;
1231
        int sourceWidgetY = 0;
1232
1233
        sourceWidget->getAbsolutePosition(sourceWidgetX, sourceWidgetY);
1234
        distributeMouseEvent(sourceWidget,
1235
            MouseEventType::WHEEL_MOVED_DOWN,
1236
            mouseInput.getButton(),
1237
            mouseInput.getX(),
1238
            mouseInput.getY(),
1239
            false,
1240
            false);
1241
    }
1242
}
1243
1244
void Gui::handleMouseWheelMovedUp(const MouseInput& mouseInput)
1245
{
1246
    Widget* sourceWidget = getMouseEventSource(
1247
        mouseInput.getX(), mouseInput.getY());
1248
1249
    if (mFocusHandler->getDraggedWidget() != nullptr)
1250
        sourceWidget = mFocusHandler->getDraggedWidget();
1251
1252
    if (sourceWidget != nullptr)
1253
    {
1254
        int sourceWidgetX;
1255
        int sourceWidgetY;
1256
1257
        sourceWidget->getAbsolutePosition(sourceWidgetX, sourceWidgetY);
1258
        distributeMouseEvent(sourceWidget,
1259
            MouseEventType::WHEEL_MOVED_UP,
1260
            mouseInput.getButton(),
1261
            mouseInput.getX(),
1262
            mouseInput.getY(),
1263
            false,
1264
            false);
1265
    }
1266
}
1267
1268
Widget* Gui::getWidgetAt(const int x, const int y) const
1269
{
1270
    // If the widget's parent has no child then we have found the widget..
1271
    Widget* parent = mTop;
1272
    Widget* child = mTop;
1273
    Widget* selectable = mTop;
1274
1275
    while (child != nullptr)
1276
    {
1277
        Widget *const swap = child;
1278
        int parentX;
1279
        int parentY;
1280
        parent->getAbsolutePosition(parentX, parentY);
1281
        child = parent->getWidgetAt(x - parentX, y - parentY);
1282
        parent = swap;
1283
        if (parent->isSelectable())
1284
            selectable = parent;
1285
    }
1286
1287
    return selectable;
1288
}
1289
1290
Widget* Gui::getMouseEventSource(const int x, const int y) const
1291
{
1292
    Widget *const widget = getWidgetAt(x, y);
1293
    if (widget == nullptr)
1294
        return nullptr;
1295
1296
    if (mFocusHandler != nullptr &&
1297
        mFocusHandler->getModalMouseInputFocused() != nullptr &&
1298
        !widget->isModalMouseInputFocused())
1299
    {
1300
        return mFocusHandler->getModalMouseInputFocused();
1301
    }
1302
1303
    return widget;
1304
}
1305
1306
Widget* Gui::getKeyEventSource() const
1307
{
1308
    Widget* widget = mFocusHandler->getFocused();
1309
1310
    while (widget != nullptr &&
1311
           widget->getInternalFocusHandler() != nullptr &&
1312
           widget->getInternalFocusHandler()->getFocused() != nullptr)
1313
    {
1314
        widget = widget->getInternalFocusHandler()->getFocused();
1315
    }
1316
1317
    return widget;
1318
}
1319
1320
void Gui::distributeKeyEvent(KeyEvent &event) const
1321
{
1322
    Widget* parent = event.getSource();
1323
    Widget* widget = parent;
1324
1325
    if (parent == nullptr)
1326
        return;
1327
    if (mFocusHandler->getModalFocused() != nullptr &&
1328
        !widget->isModalFocused())
1329
    {
1330
        return;
1331
    }
1332
    if (mFocusHandler->getModalMouseInputFocused() != nullptr &&
1333
        !widget->isModalMouseInputFocused())
1334
    {
1335
        return;
1336
    }
1337
1338
    while (parent != nullptr)
1339
    {
1340
        // If the widget has been removed due to input
1341
        // cancel the distribution.
1342
        if (!Widget::widgetExists(widget))
1343
            break;
1344
1345
        parent = widget->getParent();
1346
1347
        if (widget->isEnabled())
1348
        {
1349
            std::list<KeyListener*> keyListeners
1350
                = widget->getKeyListeners();
1351
1352
            const KeyEventTypeT eventType = event.getType();
1353
            // Send the event to all key listeners of the source widget.
1354
            FOR_EACH (std::list<KeyListener*>::const_iterator,
1355
                 it, keyListeners)
1356
            {
1357
                switch (eventType)
1358
                {
1359
                    case KeyEventType::PRESSED:
1360
                        (*it)->keyPressed(event);
1361
                        break;
1362
                    case KeyEventType::RELEASED:
1363
                        (*it)->keyReleased(event);
1364
                        break;
1365
                    default:
1366
                        break;
1367
                }
1368
            }
1369
        }
1370
1371
        const Widget *const swap = widget;
1372
        widget = parent;
1373
        parent = swap->getParent();
1374
1375
        // If a non modal focused widget has been reach
1376
        // and we have modal focus cancel the distribution.
1377
        if ((widget != nullptr) &&
1378
            (mFocusHandler->getModalFocused() != nullptr) &&
1379
            !widget->isModalFocused())
1380
        {
1381
            break;
1382
        }
1383
    }
1384
}
1385
1386
void Gui::distributeKeyEventToGlobalKeyListeners(KeyEvent& event)
1387
{
1388
    BLOCK_START("Gui::distributeKeyEventToGlobalKeyListeners")
1389
    const KeyEventTypeT eventType = event.getType();
1390
    FOR_EACH (KeyListenerListIterator, it, mKeyListeners)
1391
    {
1392
        switch (eventType)
1393
        {
1394
            case KeyEventType::PRESSED:
1395
                (*it)->keyPressed(event);
1396
                break;
1397
            case KeyEventType::RELEASED:
1398
                (*it)->keyReleased(event);
1399
                break;
1400
            default:
1401
                break;
1402
        }
1403
1404
        if (event.isConsumed())
1405
            break;
1406
    }
1407
    BLOCK_END("Gui::distributeKeyEventToGlobalKeyListeners")
1408
}
1409
1410
void Gui::handleModalMouseInputFocus()
1411
{
1412
    BLOCK_START("Gui::handleModalMouseInputFocus")
1413
    Widget *const lastModalWidget
1414
        = mFocusHandler->getLastWidgetWithModalMouseInputFocus();
1415
    Widget *const modalWidget = mFocusHandler->getModalMouseInputFocused();
1416
    if (lastModalWidget != modalWidget)
1417
    {
1418
        // Check if modal mouse input focus has been gained by a widget.
1419
        if (lastModalWidget == nullptr)
1420
        {
1421
            handleModalFocusGained();
1422
            mFocusHandler->setLastWidgetWithModalMouseInputFocus(modalWidget);
1423
        }
1424
        // Check if modal mouse input focus has been released.
1425
        else
1426
        {
1427
            handleModalFocusReleased();
1428
            mFocusHandler->setLastWidgetWithModalMouseInputFocus(nullptr);
1429
        }
1430
    }
1431
    BLOCK_END("Gui::handleModalMouseInputFocus")
1432
}
1433
1434
void Gui::handleModalFocus()
1435
{
1436
    BLOCK_START("Gui::handleModalFocus")
1437
    Widget *const lastModalWidget
1438
        = mFocusHandler->getLastWidgetWithModalFocus();
1439
    Widget *const modalWidget = mFocusHandler->getModalFocused();
1440
1441
    if (lastModalWidget != modalWidget)
1442
    {
1443
        // Check if modal focus has been gained by a widget.
1444
        if (lastModalWidget == nullptr)
1445
        {
1446
            handleModalFocusGained();
1447
            mFocusHandler->setLastWidgetWithModalFocus(modalWidget);
1448
        }
1449
        // Check if modal focus has been released.
1450
        else
1451
        {
1452
            handleModalFocusReleased();
1453
            mFocusHandler->setLastWidgetWithModalFocus(nullptr);
1454
        }
1455
    }
1456
    BLOCK_END("Gui::handleModalFocus")
1457
}
1458
1459
void Gui::handleModalFocusGained()
1460
{
1461
    // Distribute an event to all widgets in the "widget with mouse" queue.
1462
    while (!mWidgetWithMouseQueue.empty())
1463
    {
1464
        Widget *const widget = mWidgetWithMouseQueue.front();
1465
1466
        if (Widget::widgetExists(widget))
1467
        {
1468
            distributeMouseEvent(widget,
1469
                MouseEventType::EXITED,
1470
                mLastMousePressButton,
1471
                mLastMouseX,
1472
                mLastMouseY,
1473
                true,
1474
                true);
1475
        }
1476
1477
        mWidgetWithMouseQueue.pop_front();
1478
    }
1479
1480
    mFocusHandler->setLastWidgetWithModalMouseInputFocus(
1481
        mFocusHandler->getModalMouseInputFocused());
1482
}
1483
1484
void Gui::handleModalFocusReleased()
1485
{
1486
      // Check all widgets below the mouse to see if they are
1487
    // present in the "widget with mouse" queue. If a widget
1488
    // is not then it should be added and an entered event should
1489
    // be sent to it.
1490
    Widget* widget = getMouseEventSource(mLastMouseX, mLastMouseY);
1491
    Widget* parent = widget;
1492
1493
    while (parent != nullptr &&
1494
           widget != nullptr)
1495
    {
1496
        parent = widget->getParent();
1497
1498
        // Check if the widget is present in the "widget with mouse" queue.
1499
        bool widgetIsPresentInQueue = false;
1500
        FOR_EACH (std::deque<Widget*>::const_iterator,
1501
                  iter, mWidgetWithMouseQueue)
1502
        {
1503
            if (*iter == widget)
1504
            {
1505
                widgetIsPresentInQueue = true;
1506
                break;
1507
            }
1508
        }
1509
1510
        // Widget is not present, send an entered event and add
1511
        // it to the "widget with mouse" queue.
1512
        if (!widgetIsPresentInQueue && Widget::widgetExists(widget))
1513
        {
1514
            distributeMouseEvent(widget,
1515
                MouseEventType::ENTERED,
1516
                mLastMousePressButton,
1517
                mLastMouseX,
1518
                mLastMouseY,
1519
                false,
1520
                true);
1521
            mWidgetWithMouseQueue.push_front(widget);
1522
        }
1523
1524
        const Widget *const swap = widget;
1525
        widget = parent;
1526
        parent = swap->getParent();
1527
    }
1528
}
1529
1530
int Gui::getMousePressLength() const
1531
{
1532
    if (mLastMousePressTimeStamp == 0U)
1533
        return 0;
1534
    unsigned int ticks = SDL_GetTicks();
1535
    if (ticks > mLastMousePressTimeStamp)
1536
        return ticks - mLastMousePressTimeStamp;
1537
    return mLastMousePressTimeStamp - ticks;
1538
2
}