1 /***************************************************************************
2  *   Copyright (c) 2017-2019 by the fifechan team                               *
3  *   https://github.com/fifengine/fifechan                                 *
4  *   This file is part of fifechan.                                        *
5  *                                                                         *
6  *   fifechan is free software; you can redistribute it and/or             *
7  *   modify it under the terms of the GNU Lesser General Public            *
8  *   License as published by the Free Software Foundation; either          *
9  *   version 2.1 of the License, or (at your option) any later version.    *
10  *                                                                         *
11  *   This library is distributed in the hope that it will be useful,       *
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
14  *   Lesser General Public License for more details.                       *
15  *                                                                         *
16  *   You should have received a copy of the GNU Lesser General Public      *
17  *   License along with this library; if not, write to the                 *
18  *   Free Software Foundation, Inc.,                                       *
19  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
20  ***************************************************************************/
21 
22 /*      _______   __   __   __   ______   __   __   _______   __   __
23  *     / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___  /\ /  |\/ /\
24  *    / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
25  *   / / /__   / / // / // / // / /    / ___  / // ___  / // /| ' / /
26  *  / /_// /\ / /_// / // / // /_/_   / / // / // /\_/ / // / |  / /
27  * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
28  * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
29  *
30  * Copyright (c) 2004 - 2008 Olof Naess�n and Per Larsson
31  *
32  *
33  * Per Larsson a.k.a finalman
34  * Olof Naess�n a.k.a jansem/yakslem
35  *
36  * Visit: http://guichan.sourceforge.net
37  *
38  * License: (BSD)
39  * Redistribution and use in source and binary forms, with or without
40  * modification, are permitted provided that the following conditions
41  * are met:
42  * 1. Redistributions of source code must retain the above copyright
43  *    notice, this list of conditions and the following disclaimer.
44  * 2. Redistributions in binary form must reproduce the above copyright
45  *    notice, this list of conditions and the following disclaimer in
46  *    the documentation and/or other materials provided with the
47  *    distribution.
48  * 3. Neither the name of Guichan nor the names of its contributors may
49  *    be used to endorse or promote products derived from this software
50  *    without specific prior written permission.
51  *
52  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
53  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
54  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
55  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
56  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
57  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
58  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
59  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
60  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
61  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
62  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
63  */
64 
65 /*
66  * For comments regarding functions please see the header file.
67  */
68 
69 #include "fifechan/gui.hpp"
70 
71 #include "fifechan/deathlistener.hpp"
72 #include "fifechan/exception.hpp"
73 #include "fifechan/focushandler.hpp"
74 #include "fifechan/graphics.hpp"
75 #include "fifechan/input.hpp"
76 #include "fifechan/keyinput.hpp"
77 #include "fifechan/keylistener.hpp"
78 #include "fifechan/mouseinput.hpp"
79 #include "fifechan/mouselistener.hpp"
80 #include "fifechan/rectangle.hpp"
81 #include "fifechan/visibilityeventhandler.hpp"
82 #include "fifechan/widget.hpp"
83 
84 #include <algorithm>
85 #include <iterator>
86 
87 namespace fcn
88 {
89     class GuiDeathListener : public DeathListener {
90     public:
GuiDeathListener(Gui * gui)91         GuiDeathListener(Gui* gui)	{
92             mGui = gui;
93         }
~GuiDeathListener()94         virtual ~GuiDeathListener() {
95         }
death(const Event & event)96         virtual void death(const Event& event) {
97             mGui->widgetDied(event.getSource());
98         }
99     private:
100         Gui* mGui;
101     };
102 
Gui()103     Gui::Gui()
104             :mTop(NULL),
105              mGraphics(NULL),
106              mInput(NULL),
107              mTabbing(true),
108              mShiftPressed(false),
109              mMetaPressed(false),
110              mControlPressed(false),
111              mAltPressed(false),
112              mLastMousePressButton(0),
113              mLastMousePressTimeStamp(0),
114              mLastMouseX(0),
115              mLastMouseY(0),
116              mClickCount(1),
117              mLastMouseDragButton(0)
118     {
119         mFocusHandler = new FocusHandler();
120         mVisibilityEventHandler = new VisibilityEventHandler(this);
121         mDeathListener = new GuiDeathListener(this);
122 
123         Widget::_setVisibilityEventHandler(mVisibilityEventHandler);
124 
125         Widget::_setGuiDeathListener(mDeathListener);
126     }
127 
~Gui()128     Gui::~Gui()
129     {
130         if (Widget::widgetExists(mTop))
131         {
132             setTop(NULL);
133         }
134         Widget::_setVisibilityEventHandler(NULL);
135         Widget::_setGuiDeathListener(NULL);
136 
137         delete mFocusHandler;
138         delete mVisibilityEventHandler;
139         delete mDeathListener;
140     }
141 
setTop(Widget * top)142     void Gui::setTop(Widget* top)
143     {
144         if (mTop != NULL)
145         {
146             mTop->_setFocusHandler(NULL);
147         }
148         if (top != NULL)
149         {
150             top->_setFocusHandler(mFocusHandler);
151         }
152 
153         mTop = top;
154     }
155 
getTop() const156     Widget* Gui::getTop() const
157     {
158         return mTop;
159     }
160 
setGraphics(Graphics * graphics)161     void Gui::setGraphics(Graphics* graphics)
162     {
163         mGraphics = graphics;
164     }
165 
getGraphics() const166     Graphics* Gui::getGraphics() const
167     {
168         return mGraphics;
169     }
170 
setInput(Input * input)171     void Gui::setInput(Input* input)
172     {
173         mInput = input;
174     }
175 
getInput() const176     Input* Gui::getInput() const
177     {
178         return mInput;
179     }
180 
logic()181     void Gui::logic()
182     {
183         if (mTop == NULL)
184             throw FCN_EXCEPTION("No top widget set");
185 
186         handleModalFocus();
187         handleModalMouseInputFocus();
188 
189         if (mInput != NULL)
190         {
191             mInput->_pollInput();
192 
193             handleKeyInput();
194             handleMouseInput();
195         }
196 
197         mTop->_logic();
198 
199         handleHiddenWidgets();
200         handleShownWidgets();
201     }
202 
draw()203     void Gui::draw()
204     {
205         if (mTop == NULL)
206             throw FCN_EXCEPTION("No top widget set");
207 
208         if (mGraphics == NULL)
209             throw FCN_EXCEPTION("No graphics set");
210 
211         if (!mTop->isVisible())
212             return;
213 
214         mGraphics->_beginDraw();
215         mTop->_draw(mGraphics);
216         mGraphics->_endDraw();
217     }
218 
focusNone()219     void Gui::focusNone()
220     {
221         mFocusHandler->focusNone();
222     }
223 
setTabbingEnabled(bool tabbing)224     void Gui::setTabbingEnabled(bool tabbing)
225     {
226         mTabbing = tabbing;
227     }
228 
isTabbingEnabled()229     bool Gui::isTabbingEnabled()
230     {
231         return mTabbing;
232     }
233 
addGlobalKeyListener(KeyListener * keyListener)234     void Gui::addGlobalKeyListener(KeyListener* keyListener)
235     {
236         mKeyListeners.push_back(keyListener);
237     }
238 
removeGlobalKeyListener(KeyListener * keyListener)239     void Gui::removeGlobalKeyListener(KeyListener* keyListener)
240     {
241         mKeyListeners.remove(keyListener);
242     }
243 
enqueueHiddenWidget(Widget * hidden)244     void Gui::enqueueHiddenWidget(Widget* hidden)
245     {
246         mHiddenWidgets.push(hidden);
247     }
248 
enqueueShownWidget(Widget * shown)249     void Gui::enqueueShownWidget(Widget* shown)
250     {
251         mShownWidgets.push(shown);
252     }
253 
widgetDied(Widget * widget)254     void Gui::widgetDied(Widget* widget)
255     {
256         std::queue<Widget*> tmp;
257         while(!mShownWidgets.empty())
258         {
259             Widget* shownWidget = mShownWidgets.front();
260             if (shownWidget != widget) {
261                 tmp.push(shownWidget);
262             }
263             mShownWidgets.pop();
264         }
265         mShownWidgets = tmp;
266 
267         tmp = std::queue<Widget*>();
268         while(!mHiddenWidgets.empty())
269         {
270             Widget* hiddenWidget = mHiddenWidgets.front();
271             if (hiddenWidget != widget) {
272                 tmp.push(hiddenWidget);
273             }
274             mHiddenWidgets.pop();
275         }
276         mHiddenWidgets = tmp;
277     }
278 
handleMouseInput()279     void Gui::handleMouseInput()
280     {
281         while (!mInput->isMouseQueueEmpty())
282         {
283             MouseInput mouseInput = mInput->dequeueMouseInput();
284 
285             switch (mouseInput.getType())
286             {
287             case MouseInput::Pressed:
288                handleMousePressed(mouseInput);
289                break;
290             case MouseInput::Released:
291                handleMouseReleased(mouseInput);
292                break;
293             case MouseInput::Moved:
294                handleMouseMoved(mouseInput);
295                break;
296             case MouseInput::WheelMovedDown:
297                handleMouseWheelMovedDown(mouseInput);
298                break;
299             case MouseInput::WheelMovedUp:
300                handleMouseWheelMovedUp(mouseInput);
301                break;
302             case MouseInput::WheelMovedRight:
303                handleMouseWheelMovedRight(mouseInput);
304                break;
305             case MouseInput::WheelMovedLeft:
306                handleMouseWheelMovedLeft(mouseInput);
307                break;
308             default:
309                throw FCN_EXCEPTION("Unknown mouse input type.");
310                break;
311             }
312 
313              // Save the current mouse state. It's needed to send
314              // mouse exited events and mouse entered events when
315              // the mouse exits a widget and when a widget releases
316              // modal mouse input focus.
317              mLastMouseX = mouseInput.getX();
318              mLastMouseY = mouseInput.getY();
319          }
320     }
321 
handleKeyInput()322     void Gui::handleKeyInput()
323     {
324         while (!mInput->isKeyQueueEmpty())
325         {
326             KeyInput keyInput = mInput->dequeueKeyInput();
327 
328             // Save modifiers state
329             mShiftPressed = keyInput.isShiftPressed();
330             mMetaPressed = keyInput.isMetaPressed();
331             mControlPressed = keyInput.isControlPressed();
332             mAltPressed = keyInput.isAltPressed();
333 
334             KeyEvent keyEventToGlobalKeyListeners(NULL,
335                                                   NULL,
336                                                   mShiftPressed,
337                                                   mControlPressed,
338                                                   mAltPressed,
339                                                   mMetaPressed,
340                                                   keyInput.getType(),
341                                                   keyInput.isNumericPad(),
342                                                   keyInput.getKey());
343 
344             distributeKeyEventToGlobalKeyListeners(keyEventToGlobalKeyListeners);
345 
346             // If a global key listener consumes the event it will not be
347             // sent further to the source of the event.
348             if (keyEventToGlobalKeyListeners.isConsumed())
349             {
350                 continue;
351             }
352 
353             bool keyEventConsumed = false;
354 
355             // Send key inputs to the focused widgets
356             if (mFocusHandler->getFocused() != NULL)
357             {
358                 Widget* source = getKeyEventSource();
359                 KeyEvent keyEvent(source,
360                                   source,
361                                   mShiftPressed,
362                                   mControlPressed,
363                                   mAltPressed,
364                                   mMetaPressed,
365                                   keyInput.getType(),
366                                   keyInput.isNumericPad(),
367                                   keyInput.getKey());
368 
369 
370                 if (!mFocusHandler->getFocused()->isFocusable())
371                     mFocusHandler->focusNone();
372                 else
373                     distributeKeyEvent(keyEvent);
374 
375                 keyEventConsumed = keyEvent.isConsumed();
376             }
377 
378             // If the key event hasn't been consumed and
379             // tabbing is enable check for tab press and
380             // change focus.
381             if (!keyEventConsumed
382                 && mTabbing
383                 && keyInput.getKey().getValue() == Key::Tab
384                 && keyInput.getType() == KeyInput::Pressed)
385             {
386                 if (keyInput.isShiftPressed())
387                     mFocusHandler->tabPrevious();
388                 else
389                     mFocusHandler->tabNext();
390             }
391         }
392     }
393 
handleMouseMoved(const MouseInput & mouseInput)394     void Gui::handleMouseMoved(const MouseInput& mouseInput)
395     {
396         // Get tha last widgets with the mouse using the
397         // last known mouse position.
398         std::set<Widget*> mLastWidgetsWithMouse = getWidgetsAt(mLastMouseX, mLastMouseY);
399 
400         // Check if the mouse has left the application window.
401         if (mouseInput.getX() < 0
402             || mouseInput.getY() < 0
403             || !mTop->getDimension().isContaining(mouseInput.getX(), mouseInput.getY()))
404         {
405             std::set<Widget*>::const_iterator iter;
406             for (iter = mLastWidgetsWithMouse.begin();
407                  iter != mLastWidgetsWithMouse.end();
408                  iter++)
409             {
410                 distributeMouseEvent((*iter),
411                                      MouseEvent::Exited,
412                                      mouseInput.getButton(),
413                                      mouseInput.getX(),
414                                      mouseInput.getY(),
415                                      true,
416                                      true);
417             }
418         }
419         // The mouse is in the application window.
420         else
421         {
422             // Calculate which widgets should receive a mouse exited event
423             // and which should receive a mouse entered event by using the
424             // last known mouse position and the latest mouse position.
425             std::set<Widget*> mWidgetsWithMouse = getWidgetsAt(mouseInput.getX(), mouseInput.getY());
426             std::set<Widget*> mWidgetsWithMouseExited;
427             std::set<Widget*> mWidgetsWithMouseEntered;
428             std::set_difference(mLastWidgetsWithMouse.begin(),
429                                 mLastWidgetsWithMouse.end(),
430                                 mWidgetsWithMouse.begin(),
431                                 mWidgetsWithMouse.end(),
432                                 std::inserter(mWidgetsWithMouseExited, mWidgetsWithMouseExited.begin()));
433             std::set_difference(mWidgetsWithMouse.begin(),
434                                 mWidgetsWithMouse.end(),
435                                 mLastWidgetsWithMouse.begin(),
436                                 mLastWidgetsWithMouse.end(),
437                                 std::inserter(mWidgetsWithMouseEntered, mWidgetsWithMouseEntered.begin()));
438 
439             std::set<Widget*>::const_iterator iter;
440             for (iter = mWidgetsWithMouseExited.begin();
441                  iter != mWidgetsWithMouseExited.end();
442                  iter++)
443             {
444                 distributeMouseEvent((*iter),
445                                      MouseEvent::Exited,
446                                      mouseInput.getButton(),
447                                      mouseInput.getX(),
448                                      mouseInput.getY(),
449                                      true,
450                                      true);
451                 // As the mouse has exited a widget we need
452                 // to reset the click count and the last mouse
453                 // press time stamp.
454                 mClickCount = 1;
455                 mLastMousePressTimeStamp = 0;
456             }
457 
458             for (iter = mWidgetsWithMouseEntered.begin();
459                  iter != mWidgetsWithMouseEntered.end();
460                  iter++)
461             {
462                 Widget* widget = (*iter);
463                 // If a widget has modal mouse input focus we
464                 // only want to send entered events to that widget
465                 // and the widget's parents.
466                 if ((mFocusHandler->getModalMouseInputFocused() != NULL
467                      && widget->isModalMouseInputFocused())
468                      || mFocusHandler->getModalMouseInputFocused() == NULL)
469                 {
470                     distributeMouseEvent(widget,
471                                          MouseEvent::Entered,
472                                          mouseInput.getButton(),
473                                          mouseInput.getX(),
474                                          mouseInput.getY(),
475                                          true,
476                                          true);
477                 }
478             }
479         }
480 
481         if (mFocusHandler->getDraggedWidget() != NULL)
482         {
483             distributeMouseEvent(mFocusHandler->getDraggedWidget(),
484                                  MouseEvent::Dragged,
485                                  mLastMouseDragButton,
486                                  mouseInput.getX(),
487                                  mouseInput.getY());
488         }
489         else
490         {
491             Widget* sourceWidget = getMouseEventSource(mouseInput.getX(), mouseInput.getY());
492             distributeMouseEvent(sourceWidget,
493                                  MouseEvent::Moved,
494                                  mouseInput.getButton(),
495                                  mouseInput.getX(),
496                                  mouseInput.getY());
497         }
498     }
499 
handleMousePressed(const MouseInput & mouseInput)500     void Gui::handleMousePressed(const MouseInput& mouseInput)
501     {
502         Widget* sourceWidget = getMouseEventSource(mouseInput.getX(), mouseInput.getY());
503 
504         if (mFocusHandler->getDraggedWidget() != NULL)
505             sourceWidget = mFocusHandler->getDraggedWidget();
506 
507         int sourceWidgetX, sourceWidgetY;
508         sourceWidget->getAbsolutePosition(sourceWidgetX, sourceWidgetY);
509 
510         if ((mFocusHandler->getModalFocused() != NULL
511             && sourceWidget->isModalFocused())
512             || mFocusHandler->getModalFocused() == NULL)
513         {
514             sourceWidget->requestFocus();
515         }
516 
517         if (mouseInput.getTimeStamp() - mLastMousePressTimeStamp < 250
518             && mLastMousePressButton == mouseInput.getButton())
519             mClickCount++;
520         else
521             mClickCount = 1;
522 
523         distributeMouseEvent(sourceWidget,
524                              MouseEvent::Pressed,
525                              mouseInput.getButton(),
526                              mouseInput.getX(),
527                              mouseInput.getY());
528 
529         mFocusHandler->setLastWidgetPressed(sourceWidget);
530 
531         mFocusHandler->setDraggedWidget(sourceWidget);
532         mLastMouseDragButton = mouseInput.getButton();
533 
534         mLastMousePressButton = mouseInput.getButton();
535         mLastMousePressTimeStamp = mouseInput.getTimeStamp();
536     }
537 
handleMouseWheelMovedDown(const MouseInput & mouseInput)538     void Gui::handleMouseWheelMovedDown(const MouseInput& mouseInput)
539     {
540         Widget* sourceWidget = getMouseEventSource(mouseInput.getX(), mouseInput.getY());
541 
542         if (mFocusHandler->getDraggedWidget() != NULL)
543             sourceWidget = mFocusHandler->getDraggedWidget();
544 
545         int sourceWidgetX, sourceWidgetY;
546         sourceWidget->getAbsolutePosition(sourceWidgetX, sourceWidgetY);
547 
548         distributeMouseEvent(sourceWidget,
549                              MouseEvent::WheelMovedDown,
550                              mouseInput.getButton(),
551                              mouseInput.getX(),
552                              mouseInput.getY());
553     }
554 
handleMouseWheelMovedUp(const MouseInput & mouseInput)555     void Gui::handleMouseWheelMovedUp(const MouseInput& mouseInput)
556     {
557         Widget* sourceWidget = getMouseEventSource(mouseInput.getX(), mouseInput.getY());
558 
559         if (mFocusHandler->getDraggedWidget() != NULL)
560             sourceWidget = mFocusHandler->getDraggedWidget();
561 
562         int sourceWidgetX, sourceWidgetY;
563         sourceWidget->getAbsolutePosition(sourceWidgetX, sourceWidgetY);
564 
565         distributeMouseEvent(sourceWidget,
566                              MouseEvent::WheelMovedUp,
567                              mouseInput.getButton(),
568                              mouseInput.getX(),
569                              mouseInput.getY());
570     }
571 
handleMouseWheelMovedRight(const MouseInput & mouseInput)572     void Gui::handleMouseWheelMovedRight(const MouseInput& mouseInput)
573     {
574         Widget* sourceWidget = getMouseEventSource(mouseInput.getX(), mouseInput.getY());
575 
576         if (mFocusHandler->getDraggedWidget() != NULL)
577             sourceWidget = mFocusHandler->getDraggedWidget();
578 
579         int sourceWidgetX, sourceWidgetY;
580         sourceWidget->getAbsolutePosition(sourceWidgetX, sourceWidgetY);
581 
582         distributeMouseEvent(sourceWidget,
583                              MouseEvent::WheelMovedRight,
584                              mouseInput.getButton(),
585                              mouseInput.getX(),
586                              mouseInput.getY());
587     }
588 
handleMouseWheelMovedLeft(const MouseInput & mouseInput)589     void Gui::handleMouseWheelMovedLeft(const MouseInput& mouseInput)
590     {
591         Widget* sourceWidget = getMouseEventSource(mouseInput.getX(), mouseInput.getY());
592 
593         if (mFocusHandler->getDraggedWidget() != NULL)
594             sourceWidget = mFocusHandler->getDraggedWidget();
595 
596         int sourceWidgetX, sourceWidgetY;
597         sourceWidget->getAbsolutePosition(sourceWidgetX, sourceWidgetY);
598 
599         distributeMouseEvent(sourceWidget,
600                              MouseEvent::WheelMovedLeft,
601                              mouseInput.getButton(),
602                              mouseInput.getX(),
603                              mouseInput.getY());
604     }
605 
handleMouseReleased(const MouseInput & mouseInput)606     void Gui::handleMouseReleased(const MouseInput& mouseInput)
607     {
608         Widget* sourceWidget = getMouseEventSource(mouseInput.getX(), mouseInput.getY());
609 
610         if (mFocusHandler->getDraggedWidget() != NULL)
611         {
612             if (sourceWidget != mFocusHandler->getLastWidgetPressed())
613                 mFocusHandler->setLastWidgetPressed(NULL);
614 
615             sourceWidget = mFocusHandler->getDraggedWidget();
616         }
617 
618         int sourceWidgetX, sourceWidgetY;
619         sourceWidget->getAbsolutePosition(sourceWidgetX, sourceWidgetY);
620 
621         distributeMouseEvent(sourceWidget,
622                              MouseEvent::Released,
623                              mouseInput.getButton(),
624                              mouseInput.getX(),
625                              mouseInput.getY());
626 
627         if (mouseInput.getButton() == mLastMousePressButton
628             && mFocusHandler->getLastWidgetPressed() == sourceWidget)
629         {
630             distributeMouseEvent(sourceWidget,
631                                  MouseEvent::Clicked,
632                                  mouseInput.getButton(),
633                                  mouseInput.getX(),
634                                  mouseInput.getY());
635 
636             mFocusHandler->setLastWidgetPressed(NULL);
637         }
638         else
639         {
640             mLastMousePressButton = 0;
641             mClickCount = 0;
642         }
643 
644         if (mFocusHandler->getDraggedWidget() != NULL)
645             mFocusHandler->setDraggedWidget(NULL);
646     }
647 
getWidgetAt(int x,int y,Widget * exclude)648     Widget* Gui::getWidgetAt(int x, int y, Widget* exclude)
649     {
650         // If the widget's parent has no child then we have found the widget..
651         Widget* parent = mTop;
652         Widget* child = mTop;
653 
654         while (child != NULL)
655         {
656             Widget* swap = child;
657             int parentX, parentY;
658             parent->getAbsolutePosition(parentX, parentY);
659             child = parent->getWidgetAt(x - parentX, y - parentY, exclude);
660             parent = swap;
661         }
662 
663         return parent;
664     }
665 
getWidgetsAt(int x,int y)666     std::set<Widget*> Gui::getWidgetsAt(int x, int y)
667     {
668         std::set<Widget*> result;
669 
670         Widget* widget = mTop;
671 
672         while (widget != NULL)
673         {
674             result.insert(widget);
675             int absoluteX, absoluteY;
676             widget->getAbsolutePosition(absoluteX, absoluteY);
677             widget = widget->getWidgetAt(x - absoluteX, y - absoluteY);
678         }
679 
680         return result;
681     }
682 
getMouseEventSource(int x,int y)683     Widget* Gui::getMouseEventSource(int x, int y)
684     {
685         Widget* widget = getWidgetAt(x, y);
686 
687         if (mFocusHandler->getModalMouseInputFocused() != NULL
688             && !widget->isModalMouseInputFocused())
689             return mFocusHandler->getModalMouseInputFocused();
690 
691         return widget;
692     }
693 
getKeyEventSource()694     Widget* Gui::getKeyEventSource()
695     {
696         Widget* widget = mFocusHandler->getFocused();
697 
698         while (widget->_getInternalFocusHandler() != NULL
699                && widget->_getInternalFocusHandler()->getFocused() != NULL)
700         {
701             widget = widget->_getInternalFocusHandler()->getFocused();
702         }
703 
704         return widget;
705     }
706 
distributeMouseEvent(Widget * source,int type,int button,int x,int y,bool force,bool toSourceOnly)707     void Gui::distributeMouseEvent(Widget* source,
708                                    int type,
709                                    int button,
710                                    int x,
711                                    int y,
712                                    bool force,
713                                    bool toSourceOnly)
714     {
715         Widget* parent = source;
716         Widget* widget = source;
717 
718         if (mFocusHandler->getModalFocused() != NULL
719             && !widget->isModalFocused()
720             && !force)
721             return;
722 
723         if (mFocusHandler->getModalMouseInputFocused() != NULL
724             && !widget->isModalMouseInputFocused()
725             && !force)
726             return;
727 
728         MouseEvent mouseEvent(source,
729                               source,
730                               mShiftPressed,
731                               mControlPressed,
732                               mAltPressed,
733                               mMetaPressed,
734                               type,
735                               button,
736                               x,
737                               y,
738                               mClickCount);
739 
740         while (parent != NULL)
741         {
742             // If the widget has been removed due to input
743             // cancel the distribution.
744             if (!Widget::widgetExists(widget))
745                 break;
746 
747             parent = widget->getParent();
748 
749             if (widget->isEnabled() || force)
750             {
751                 int widgetX, widgetY;
752                 widget->getAbsolutePosition(widgetX, widgetY);
753 
754                 mouseEvent.mX = x - widgetX;
755                 mouseEvent.mY = y - widgetY;
756                 mouseEvent.mDistributor = widget;
757                 std::list<MouseListener*> mouseListeners = widget->_getMouseListeners();
758 
759                 // Send the event to all mouse listeners of the widget.
760                 for (std::list<MouseListener*>::iterator it = mouseListeners.begin();
761                      it != mouseListeners.end();
762                      ++it)
763                 {
764                     switch (mouseEvent.getType())
765                     {
766                       case MouseEvent::Entered:
767                           (*it)->mouseEntered(mouseEvent);
768                           break;
769                       case MouseEvent::Exited:
770                           (*it)->mouseExited(mouseEvent);
771                           break;
772                       case MouseEvent::Moved:
773                           (*it)->mouseMoved(mouseEvent);
774                           break;
775                       case MouseEvent::Pressed:
776                           (*it)->mousePressed(mouseEvent);
777                           break;
778                       case MouseEvent::Released:
779                           (*it)->mouseReleased(mouseEvent);
780                           break;
781                       case MouseEvent::WheelMovedUp:
782                           (*it)->mouseWheelMovedUp(mouseEvent);
783                           break;
784                       case MouseEvent::WheelMovedDown:
785                           (*it)->mouseWheelMovedDown(mouseEvent);
786                           break;
787                       case MouseEvent::WheelMovedRight:
788                           (*it)->mouseWheelMovedRight(mouseEvent);
789                           break;
790                       case MouseEvent::WheelMovedLeft:
791                           (*it)->mouseWheelMovedLeft(mouseEvent);
792                           break;
793                       case MouseEvent::Dragged:
794                           (*it)->mouseDragged(mouseEvent);
795                           break;
796                       case MouseEvent::Clicked:
797                           (*it)->mouseClicked(mouseEvent);
798                           break;
799                       default:
800                           throw FCN_EXCEPTION("Unknown mouse event type.");
801                     }
802                 }
803 
804                 if (toSourceOnly)
805                     break;
806 
807             }
808 
809             Widget* swap = widget;
810             widget = parent;
811             parent = swap->getParent();
812 
813             // If a non modal focused widget has been reach
814             // and we have modal focus cancel the distribution.
815             if (mFocusHandler->getModalFocused() != NULL
816                 && widget != NULL
817                 && !widget->isModalFocused())
818                 break;
819 
820             // If a non modal mouse input focused widget has been reach
821             // and we have modal mouse input focus cancel the distribution.
822             if (mFocusHandler->getModalMouseInputFocused() != NULL
823                 && widget != NULL
824                 && !widget->isModalMouseInputFocused())
825                 break;
826         }
827     }
828 
distributeKeyEvent(KeyEvent & keyEvent)829     void Gui::distributeKeyEvent(KeyEvent& keyEvent)
830     {
831         Widget* parent = keyEvent.getSource();
832         Widget* widget = keyEvent.getSource();
833 
834         if (mFocusHandler->getModalFocused() != NULL
835             && !widget->isModalFocused())
836             return;
837 
838         while (parent != NULL)
839         {
840             // If the widget has been removed due to input
841             // cancel the distribution.
842             if (!Widget::widgetExists(widget))
843                 break;
844 
845             parent = widget->getParent();
846 
847             if (widget->isEnabled())
848             {
849                 keyEvent.mDistributor = widget;
850                 std::list<KeyListener*> keyListeners = widget->_getKeyListeners();
851 
852                 // Send the event to all key listeners of the source widget.
853                 for (std::list<KeyListener*>::iterator it = keyListeners.begin();
854                      it != keyListeners.end();
855                      ++it)
856                 {
857                     switch (keyEvent.getType())
858                     {
859                       case KeyEvent::Pressed:
860                           (*it)->keyPressed(keyEvent);
861                           break;
862                       case KeyEvent::Released:
863                           (*it)->keyReleased(keyEvent);
864                           break;
865                       default:
866                           throw FCN_EXCEPTION("Unknown key event type.");
867                     }
868                 }
869             }
870 
871             Widget* swap = widget;
872             widget = parent;
873             parent = swap->getParent();
874 
875             // If a non modal focused widget has been reach
876             // and we have modal focus cancel the distribution.
877             if (mFocusHandler->getModalFocused() != NULL
878                 && !widget->isModalFocused())
879                 break;
880         }
881     }
882 
distributeKeyEventToGlobalKeyListeners(KeyEvent & keyEvent)883     void Gui::distributeKeyEventToGlobalKeyListeners(KeyEvent& keyEvent)
884     {
885         KeyListenerListIterator it;
886 
887         for (it = mKeyListeners.begin(); it != mKeyListeners.end(); it++)
888         {
889             switch (keyEvent.getType())
890             {
891               case KeyEvent::Pressed:
892                   (*it)->keyPressed(keyEvent);
893                   break;
894               case KeyEvent::Released:
895                   (*it)->keyReleased(keyEvent);
896                   break;
897               default:
898                   throw FCN_EXCEPTION("Unknown key event type.");
899             }
900 
901             if (keyEvent.isConsumed())
902                 break;
903         }
904     }
905 
handleModalMouseInputFocus()906     void Gui::handleModalMouseInputFocus()
907     {
908         // Check if modal mouse input focus has been gained by a widget.
909         if ((mFocusHandler->getLastWidgetWithModalMouseInputFocus()
910                 != mFocusHandler->getModalMouseInputFocused())
911              && (mFocusHandler->getLastWidgetWithModalMouseInputFocus() == NULL))
912         {
913             handleModalMouseInputFocusGained();
914         }
915         // Check if modal mouse input focus has been released.
916         else if ((mFocusHandler->getLastWidgetWithModalMouseInputFocus()
917                     != mFocusHandler->getModalMouseInputFocused())
918                     && (mFocusHandler->getLastWidgetWithModalMouseInputFocus() != NULL))
919         {
920             handleModalMouseInputFocusReleased();
921         }
922     }
923 
handleModalFocus()924      void Gui::handleModalFocus()
925     {
926         // Check if modal focus has been gained by a widget.
927         if ((mFocusHandler->getLastWidgetWithModalFocus()
928                 != mFocusHandler->getModalFocused())
929              && (mFocusHandler->getLastWidgetWithModalFocus() == NULL))
930         {
931             handleModalFocusGained();
932         }
933         // Check if modal focus has been released.
934         else if ((mFocusHandler->getLastWidgetWithModalFocus()
935                     != mFocusHandler->getModalFocused())
936                     && (mFocusHandler->getLastWidgetWithModalFocus() != NULL))
937         {
938             handleModalFocusReleased();
939         }
940     }
941 
handleModalFocusGained()942     void Gui::handleModalFocusGained()
943     {
944         // Get all widgets at the last known mouse position
945         // and send them a mouse exited event.
946         std::set<Widget*> mWidgetsWithMouse = getWidgetsAt(mLastMouseX, mLastMouseY);
947 
948         std::set<Widget*>::const_iterator iter;
949         for (iter = mWidgetsWithMouse.begin();
950              iter != mWidgetsWithMouse.end();
951              iter++)
952         {
953             if ((*iter)->isModalFocused() || (*iter)->isModalMouseInputFocused()) {
954                 continue;
955             }
956             distributeMouseEvent((*iter),
957                                  MouseEvent::Exited,
958                                  mLastMousePressButton,
959                                  mLastMouseX,
960                                  mLastMouseY,
961                                  true,
962                                  true);
963         }
964         mFocusHandler->setLastWidgetWithModalFocus(mFocusHandler->getModalFocused());
965     }
966 
handleModalFocusReleased()967     void Gui::handleModalFocusReleased()
968     {
969         // Get all widgets at the last known mouse position
970         // and send them a mouse entered event.
971         std::set<Widget*> mWidgetsWithMouse = getWidgetsAt(mLastMouseX, mLastMouseY);
972 
973         std::set<Widget*>::const_iterator iter;
974         for (iter = mWidgetsWithMouse.begin();
975              iter != mWidgetsWithMouse.end();
976              iter++)
977         {
978             distributeMouseEvent((*iter),
979                                  MouseEvent::Entered,
980                                  mLastMousePressButton,
981                                  mLastMouseX,
982                                  mLastMouseY,
983                                  false,
984                                  true);
985         }
986         mFocusHandler->setLastWidgetWithModalFocus(NULL);
987     }
988 
handleModalMouseInputFocusGained()989     void Gui::handleModalMouseInputFocusGained()
990     {
991         // Get all widgets at the last known mouse position
992         // and send them a mouse exited event.
993         std::set<Widget*> mWidgetsWithMouse = getWidgetsAt(mLastMouseX, mLastMouseY);
994 
995         std::set<Widget*>::const_iterator iter;
996         for (iter = mWidgetsWithMouse.begin();
997              iter != mWidgetsWithMouse.end();
998              iter++)
999         {
1000             if ((*iter)->isModalMouseInputFocused()) {
1001                 continue;
1002             }
1003             distributeMouseEvent((*iter),
1004                                  MouseEvent::Exited,
1005                                  mLastMousePressButton,
1006                                  mLastMouseX,
1007                                  mLastMouseY,
1008                                  true,
1009                                  true);
1010         }
1011         mFocusHandler->setLastWidgetWithModalMouseInputFocus(mFocusHandler->getModalMouseInputFocused());
1012     }
1013 
handleModalMouseInputFocusReleased()1014     void Gui::handleModalMouseInputFocusReleased()
1015     {
1016         // Get all widgets at the last known mouse position
1017         // and send them a mouse entered event.
1018         std::set<Widget*> mWidgetsWithMouse = getWidgetsAt(mLastMouseX, mLastMouseY);
1019 
1020         std::set<Widget*>::const_iterator iter;
1021         for (iter = mWidgetsWithMouse.begin();
1022              iter != mWidgetsWithMouse.end();
1023              iter++)
1024         {
1025             distributeMouseEvent((*iter),
1026                                  MouseEvent::Entered,
1027                                  mLastMousePressButton,
1028                                  mLastMouseX,
1029                                  mLastMouseY,
1030                                  false,
1031                                  true);
1032         }
1033         mFocusHandler->setLastWidgetWithModalMouseInputFocus(NULL);
1034     }
1035 
handleHiddenWidgets()1036     void Gui::handleHiddenWidgets()
1037     {
1038         //process each hidden widget in queue
1039         while(!mHiddenWidgets.empty())
1040         {
1041             //if the hidden widget had the mouse cursor inside
1042             Widget* hiddenWidget = mHiddenWidgets.front();
1043 
1044             //make sure that the widget wasn't freed after hiding
1045             if(Widget::widgetExists(hiddenWidget) && hiddenWidget->isEnabled())
1046             {
1047                 int hiddenWidgetX, hiddenWidgetY;
1048                 hiddenWidget->getAbsolutePosition(hiddenWidgetX, hiddenWidgetY);
1049 
1050                 Rectangle r(hiddenWidgetX, hiddenWidgetY, hiddenWidget->getWidth(), hiddenWidget->getHeight());
1051 
1052                 if(r.isContaining(mLastMouseX, mLastMouseY))
1053                 {
1054                     //get the widget that has the cursor now and distribute that the mouse entered it
1055                     Widget* underMouseCursor = getWidgetAt(mLastMouseX, mLastMouseY);
1056 
1057                     distributeMouseEvent(underMouseCursor,
1058                                          MouseEvent::Entered,
1059                                          MouseEvent::Empty,
1060                                          mLastMouseX,
1061                                          mLastMouseY,
1062                                          true,
1063                                          true);
1064                 }
1065             }
1066 
1067             mHiddenWidgets.pop();
1068         }
1069 
1070     }
1071 
handleShownWidgets()1072     void Gui::handleShownWidgets()
1073     {
1074         //process each shown widget in queue
1075         while(!mShownWidgets.empty())
1076         {
1077             Widget* shownWidget = mShownWidgets.front();
1078 
1079             //if the shown widget has the mouse cursor inside it
1080             int shownWidgetX, shownWidgetY;
1081             shownWidget->getAbsolutePosition(shownWidgetX, shownWidgetY);
1082 
1083             Rectangle r(shownWidgetX, shownWidgetY, shownWidget->getWidth(), shownWidget->getHeight());
1084 
1085             if(r.isContaining(mLastMouseX, mLastMouseY) && shownWidget->isEnabled())
1086             {
1087                 //find which widget had the mouse before and distribute that the mouse exited it
1088                 Widget* underMouseCursorBefore = getWidgetAt(mLastMouseX, mLastMouseY, shownWidget);
1089 
1090                 distributeMouseEvent(underMouseCursorBefore,
1091                                      MouseEvent::Exited,
1092                                      MouseEvent::Empty,
1093                                      mLastMouseX,
1094                                      mLastMouseY,
1095                                      true,
1096                                      true
1097                                     );
1098 
1099                 //find which specific widget in the shown widget had the mouse before
1100                 //and distribute that the mouse exited it
1101                 Widget* underMouseCursorNow = getWidgetAt(mLastMouseX, mLastMouseY);
1102 
1103                 distributeMouseEvent(underMouseCursorNow,
1104                                      MouseEvent::Entered,
1105                                      MouseEvent::Empty,
1106                                      mLastMouseX,
1107                                      mLastMouseY,
1108                                      true,
1109                                      true
1110                                     );
1111             }
1112 
1113             mShownWidgets.pop();
1114         }
1115 
1116     }
1117 }
1118