GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/focushandler.cpp Lines: 73 234 31.2 %
Date: 2021-03-17 Branches: 38 170 22.4 %

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/focushandler.h"
68
69
#include "gui/gui.h"
70
71
#include "gui/widgets/window.h"
72
73
#include "listeners/focuslistener.h"
74
75
#include "utils/foreach.h"
76
77
#include "debug.h"
78
79
254
FocusHandler::FocusHandler() :
80
    mWidgets(),
81
    mFocusedWidget(nullptr),
82
    mModalFocusedWidget(nullptr),
83
    mModalMouseInputFocusedWidget(nullptr),
84
    mDraggedWidget(nullptr),
85
    mLastWidgetWithMouse(nullptr),
86
    mLastWidgetWithModalFocus(nullptr),
87
    mLastWidgetWithModalMouseInputFocus(nullptr),
88
    mLastWidgetPressed(nullptr),
89
762
    mModalStack()
90
{
91
254
}
92
93
7
void FocusHandler::requestModalFocus(Widget *const widget)
94
{
95
    /* If there is another widget with modal focus, remove its modal focus
96
     * and put it on the modal widget stack.
97
     */
98

7
    if ((mModalFocusedWidget != nullptr) && mModalFocusedWidget != widget)
99
    {
100
        mModalStack.push_front(mModalFocusedWidget);
101
        mModalFocusedWidget = nullptr;
102
    }
103
104
7
    mModalFocusedWidget = widget;
105

7
    if ((mFocusedWidget != nullptr) && !mFocusedWidget->isModalFocused())
106
        focusNone();
107
7
}
108
109
2492
void FocusHandler::releaseModalFocus(Widget *const widget)
110
{
111
2492
    mModalStack.remove(widget);
112
113
2492
    if (mModalFocusedWidget == widget)
114
    {
115
5
        mModalFocusedWidget = nullptr;
116
117
        /* Check if there were any previously modal widgets that'd still like
118
         * to regain their modal focus.
119
         */
120
10
        if (!mModalStack.empty())
121
        {
122
            requestModalFocus(mModalStack.front());
123
            mModalStack.pop_front();
124
        }
125
    }
126
2492
}
127
128
1246
void FocusHandler::remove(Widget *const widget)
129
{
130
1246
    releaseModalFocus(widget);
131
132
1246
    if (isFocused(widget))
133
23
        mFocusedWidget = nullptr;
134
135
35891
    FOR_EACH (WidgetIterator, iter, mWidgets)
136
    {
137
32153
        if ((*iter) == widget)
138
        {
139
2492
            mWidgets.erase(iter);
140
1246
            break;
141
        }
142
    }
143
144
1246
    if (mDraggedWidget == widget)
145
    {
146
        mDraggedWidget = nullptr;
147
        return;
148
    }
149
150
1246
    if (mLastWidgetWithMouse == widget)
151
    {
152
        mLastWidgetWithMouse = nullptr;
153
        return;
154
    }
155
156
1246
    if (mLastWidgetWithModalFocus == widget)
157
    {
158
        mLastWidgetWithModalFocus = nullptr;
159
        return;
160
    }
161
162
1246
    if (mLastWidgetWithModalMouseInputFocus == widget)
163
    {
164
        mLastWidgetWithModalMouseInputFocus = nullptr;
165
        return;
166
    }
167
168
1246
    if (mLastWidgetPressed == widget)
169
    {
170
        mLastWidgetPressed = nullptr;
171
        return;
172
    }
173
}
174
175
void FocusHandler::tabNext()
176
{
177
    if (mFocusedWidget != nullptr)
178
    {
179
        if (!mFocusedWidget->isTabOutEnabled())
180
            return;
181
    }
182
183
    if (mWidgets.empty())
184
    {
185
        mFocusedWidget = nullptr;
186
        return;
187
    }
188
189
    int i;
190
    int focusedWidget = -1;
191
    const int sz = CAST_S32(mWidgets.size());
192
    for (i = 0; i < sz; ++ i)
193
    {
194
        if (mWidgets[i] == mFocusedWidget)
195
            focusedWidget = i;
196
    }
197
    const int focused = focusedWidget;
198
    bool done = false;
199
200
    // i is a counter that ensures that the following loop
201
    // won't get stuck in an infinite loop
202
    i = sz;
203
    do
204
    {
205
        ++ focusedWidget;
206
207
        if (i == 0)
208
        {
209
            focusedWidget = -1;
210
            break;
211
        }
212
213
        -- i;
214
215
        if (focusedWidget >= sz)
216
            focusedWidget = 0;
217
218
        if (focusedWidget == focused)
219
            return;
220
221
        const Widget *const widget = mWidgets.at(focusedWidget);
222
        if (widget->isFocusable() && widget->isTabInEnabled() &&
223
            ((mModalFocusedWidget == nullptr) || widget->isModalFocused()))
224
        {
225
            done = true;
226
        }
227
    }
228
    while (!done);
229
230
    if (focusedWidget >= 0)
231
    {
232
        mFocusedWidget = mWidgets.at(focusedWidget);
233
        Event focusEvent(mFocusedWidget);
234
        distributeFocusGainedEvent(focusEvent);
235
    }
236
237
    if (focused >= 0)
238
    {
239
        Event focusEvent(mWidgets.at(focused));
240
        distributeFocusLostEvent(focusEvent);
241
    }
242
243
    checkForWindow();
244
}
245
246
void FocusHandler::tabPrevious()
247
{
248
    if (mFocusedWidget != nullptr)
249
    {
250
        if (!mFocusedWidget->isTabOutEnabled())
251
            return;
252
    }
253
254
    if (mWidgets.empty())
255
    {
256
        mFocusedWidget = nullptr;
257
        return;
258
    }
259
260
    int i;
261
    int focusedWidget = -1;
262
    const int sz = CAST_S32(mWidgets.size());
263
    for (i = 0; i < sz; ++ i)
264
    {
265
        if (mWidgets[i] == mFocusedWidget)
266
            focusedWidget = i;
267
    }
268
    const int focused = focusedWidget;
269
    bool done = false;
270
271
    // i is a counter that ensures that the following loop
272
    // won't get stuck in an infinite loop
273
    i = sz;
274
    do
275
    {
276
        -- focusedWidget;
277
278
        if (i == 0)
279
        {
280
            focusedWidget = -1;
281
            break;
282
        }
283
284
        -- i;
285
286
        if (focusedWidget <= 0)
287
            focusedWidget = sz - 1;
288
289
        if (focusedWidget == focused)
290
            return;
291
292
        const Widget *const widget = mWidgets.at(focusedWidget);
293
        if (widget->isFocusable() && widget->isTabInEnabled() &&
294
            ((mModalFocusedWidget == nullptr) || widget->isModalFocused()))
295
        {
296
            done = true;
297
        }
298
    }
299
    while (!done);
300
301
    if (focusedWidget >= 0)
302
    {
303
        mFocusedWidget = mWidgets.at(focusedWidget);
304
        Event focusEvent(mFocusedWidget);
305
        distributeFocusGainedEvent(focusEvent);
306
    }
307
308
    if (focused >= 0)
309
    {
310
        Event focusEvent(mWidgets.at(focused));
311
        distributeFocusLostEvent(focusEvent);
312
    }
313
314
    checkForWindow();
315
}
316
317
void FocusHandler::checkForWindow() const
318
{
319
    if (mFocusedWidget != nullptr)
320
    {
321
        Widget *widget = mFocusedWidget->getParent();
322
323
        while (widget != nullptr)
324
        {
325
            Window *const window = dynamic_cast<Window*>(widget);
326
327
            if (window != nullptr)
328
            {
329
                window->requestMoveToTop();
330
                break;
331
            }
332
333
            widget = widget->getParent();
334
        }
335
    }
336
}
337
338
24
void FocusHandler::distributeFocusGainedEvent(const Event &focusEvent)
339
{
340
24
    if (gui != nullptr)
341
24
        gui->distributeGlobalFocusGainedEvent(focusEvent);
342
343
24
    const Widget *const sourceWidget = focusEvent.getSource();
344
345
24
    if (sourceWidget == nullptr)
346
        return;
347
    std::list<FocusListener*> focusListeners
348
48
        = sourceWidget->getFocusListeners();
349
350
    // Send the event to all focus listeners of the widget.
351
37
    for (std::list<FocusListener*>::const_iterator
352
72
          it = focusListeners.begin();
353
37
          it != focusListeners.end();
354
          ++ it)
355
    {
356
13
        (*it)->focusGained(focusEvent);
357
    }
358
}
359
360
24
void FocusHandler::requestFocus(const Widget *const widget)
361
{
362

24
    if ((widget == nullptr) || widget == mFocusedWidget)
363
        return;
364
365
24
    int toBeFocusedIndex = -1;
366
142
    for (unsigned int i = 0, fsz = CAST_U32(
367
48
         mWidgets.size()); i < fsz; ++i)
368
    {
369
284
        if (mWidgets[i] == widget)
370
        {
371
24
            toBeFocusedIndex = i;
372
24
            break;
373
        }
374
    }
375
376
24
    if (toBeFocusedIndex < 0)
377
        return;
378
379
24
    Widget *const oldFocused = mFocusedWidget;
380
381
    if (oldFocused != widget)
382
    {
383
48
        mFocusedWidget = mWidgets.at(toBeFocusedIndex);
384
385
72
        Event focusEvent(mFocusedWidget);
386
24
        distributeFocusGainedEvent(focusEvent);
387
388
24
        if (oldFocused != nullptr)
389
        {
390
            Event oldFocusEvent(oldFocused);
391
            distributeFocusLostEvent(oldFocusEvent);
392
        }
393
    }
394
}
395
396
void FocusHandler::requestModalMouseInputFocus(Widget *const widget)
397
{
398
    if ((mModalMouseInputFocusedWidget != nullptr)
399
        && mModalMouseInputFocusedWidget != widget)
400
    {
401
        return;
402
    }
403
404
    mModalMouseInputFocusedWidget = widget;
405
}
406
407
void FocusHandler::releaseModalMouseInputFocus(const Widget *const widget)
408
{
409
    if (mModalMouseInputFocusedWidget == widget)
410
        mModalMouseInputFocusedWidget = nullptr;
411
}
412
413
Widget* FocusHandler::getFocused() const
414
{
415
    return mFocusedWidget;
416
}
417
418
3
Widget* FocusHandler::getModalFocused() const
419
{
420
3
    return mModalFocusedWidget;
421
}
422
423
Widget* FocusHandler::getModalMouseInputFocused() const
424
{
425
    return mModalMouseInputFocusedWidget;
426
}
427
428
void FocusHandler::focusNext()
429
{
430
    int i;
431
    int focusedWidget = -1;
432
    const int sz = CAST_S32(mWidgets.size());
433
    for (i = 0; i < sz; ++i)
434
    {
435
        if (mWidgets[i] == mFocusedWidget)
436
            focusedWidget = i;
437
    }
438
    const int focused = focusedWidget;
439
440
    // i is a counter that ensures that the following loop
441
    // won't get stuck in an infinite loop
442
    i = sz;
443
    do
444
    {
445
        ++ focusedWidget;
446
447
        if (i == 0)
448
        {
449
            focusedWidget = -1;
450
            break;
451
        }
452
453
        -- i;
454
455
        if (focusedWidget >= sz)
456
            focusedWidget = 0;
457
458
        if (focusedWidget == focused)
459
            return;
460
    }
461
    while (!mWidgets.at(focusedWidget)->isFocusable());
462
463
    if (focusedWidget >= 0)
464
    {
465
        mFocusedWidget = mWidgets.at(focusedWidget);
466
467
        Event focusEvent(mFocusedWidget);
468
        distributeFocusGainedEvent(focusEvent);
469
    }
470
471
    if (focused >= 0)
472
    {
473
        Event focusEvent(mWidgets.at(focused));
474
        distributeFocusLostEvent(focusEvent);
475
    }
476
}
477
478
void FocusHandler::focusPrevious()
479
{
480
    if (mWidgets.empty())
481
    {
482
        mFocusedWidget = nullptr;
483
        return;
484
    }
485
486
    int i;
487
    int focusedWidget = -1;
488
    const int sz = CAST_S32(mWidgets.size());
489
    for (i = 0; i < sz; ++ i)
490
    {
491
        if (mWidgets[i] == mFocusedWidget)
492
            focusedWidget = i;
493
    }
494
    const int focused = focusedWidget;
495
496
    // i is a counter that ensures that the following loop
497
    // won't get stuck in an infinite loop
498
    i = sz;
499
    do
500
    {
501
        -- focusedWidget;
502
503
        if (i == 0)
504
        {
505
            focusedWidget = -1;
506
            break;
507
        }
508
509
        -- i;
510
511
        if (focusedWidget <= 0)
512
            focusedWidget = sz - 1;
513
514
        if (focusedWidget == focused)
515
            return;
516
    }
517
    while (!mWidgets.at(focusedWidget)->isFocusable());
518
519
    if (focusedWidget >= 0)
520
    {
521
        mFocusedWidget = mWidgets.at(focusedWidget);
522
        Event focusEvent(mFocusedWidget);
523
        distributeFocusGainedEvent(focusEvent);
524
    }
525
526
    if (focused >= 0)
527
    {
528
        Event focusEvent(mWidgets.at(focused));
529
        distributeFocusLostEvent(focusEvent);
530
    }
531
}
532
533
239
bool FocusHandler::isFocused(const Widget *const widget) const
534
{
535
1485
    return mFocusedWidget == widget;
536
}
537
538
1246
void FocusHandler::add(Widget *const widget)
539
{
540
1246
    mWidgets.push_back(widget);
541
1246
}
542
543
void FocusHandler::focusNone()
544
{
545

1
    if (mFocusedWidget != nullptr)
546
    {
547
1
        Widget *const focused = mFocusedWidget;
548
1
        mFocusedWidget = nullptr;
549
550
2
        Event focusEvent(focused);
551
1
        distributeFocusLostEvent(focusEvent);
552
    }
553
}
554
555
1
void FocusHandler::distributeFocusLostEvent(const Event& focusEvent)
556
{
557
1
    const Widget *const sourceWidget = focusEvent.getSource();
558
1
    if (sourceWidget == nullptr)
559
        return;
560
561
    std::list<FocusListener*> focusListeners
562
2
        = sourceWidget->getFocusListeners();
563
564
    // Send the event to all focus listeners of the widget.
565
2
    for (std::list<FocusListener*>::const_iterator
566
3
          it = focusListeners.begin();
567
2
          it != focusListeners.end();
568
          ++ it)
569
    {
570
1
        (*it)->focusLost(focusEvent);
571
    }
572
}
573
574
2487
Widget* FocusHandler::getDraggedWidget() const
575
{
576
2487
    return mDraggedWidget;
577
}
578
579
void FocusHandler::setDraggedWidget(Widget *const draggedWidget)
580
{
581
    mDraggedWidget = draggedWidget;
582
}
583
584
Widget* FocusHandler::getLastWidgetWithMouse() const
585
{
586
    return mLastWidgetWithMouse;
587
}
588
589
void FocusHandler::setLastWidgetWithMouse(Widget *const lastWidgetWithMouse)
590
{
591
    mLastWidgetWithMouse = lastWidgetWithMouse;
592
}
593
594
Widget* FocusHandler::getLastWidgetWithModalFocus() const
595
{
596
    return mLastWidgetWithModalFocus;
597
}
598
599
void FocusHandler::setLastWidgetWithModalFocus(Widget *const widget)
600
{
601
    mLastWidgetWithModalFocus = widget;
602
}
603
604
Widget* FocusHandler::getLastWidgetWithModalMouseInputFocus() const
605
{
606
    return mLastWidgetWithModalMouseInputFocus;
607
}
608
609
void FocusHandler::setLastWidgetWithModalMouseInputFocus(Widget *const widget)
610
{
611
    mLastWidgetWithModalMouseInputFocus = widget;
612
}
613
614
Widget* FocusHandler::getLastWidgetPressed() const
615
{
616
    return mLastWidgetPressed;
617
}
618
619
void FocusHandler::setLastWidgetPressed(Widget *const lastWidgetPressed)
620
{
621
    mLastWidgetPressed = lastWidgetPressed;
622
}