1 /*
2  *  The ManaPlus Client
3  *  Copyright (C) 2011-2019  The ManaPlus Developers
4  *  Copyright (C) 2019-2021  Andrei Karas
5  *
6  *  This file is part of The ManaPlus Client.
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
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 "gui/widgets/widget.h"
70 
71 #include "gui/focushandler.h"
72 
73 #include "listeners/actionlistener.h"
74 #include "listeners/widgetdeathlistener.h"
75 #include "listeners/widgetlistener.h"
76 
77 #include "utils/foreach.h"
78 
79 #include "debug.h"
80 
81 Font* Widget::mGlobalFont = nullptr;
82 std::list<Widget*> Widget::mAllWidgets;
83 std::set<Widget*> Widget::mAllWidgetsSet;
84 
Widget(const Widget2 * const widget)85 Widget::Widget(const Widget2 *const widget) :
86     Widget2(widget),
87     mVisible(Visible_true),
88     mMouseListeners(),
89     mKeyListeners(),
90     mActionListeners(),
91     mDeathListeners(),
92     mFocusListeners(),
93     mWidgetListeners(),
94     mForegroundColor(0x000000),
95     mBackgroundColor(0xffffff),
96     mBaseColor(0x808090),
97     mDimension(),
98     mActionEventId(),
99     mId(),
100     mFocusHandler(nullptr),
101     mInternalFocusHandler(nullptr),
102     mParent(nullptr),
103     mCurrentFont(nullptr),
104     mFrameSize(0),
105     mFocusable(false),
106     mTabIn(true),
107     mTabOut(true),
108     mEnabled(true),
109     mAllowLogic(true),
110     mMouseConsume(true),
111     mRedraw(true),
112     mSelectable(true)
113 {
114     mAllWidgets.push_back(this);
115     mAllWidgetsSet.insert(this);
116 }
117 
~Widget()118 Widget::~Widget()
119 {
120     FOR_EACH (WidgetDeathListenerIterator, iter, mDeathListeners)
121     {
122         Event event(this);
123         (*iter)->death(event);
124     }
125 
126     // +++ call to virtual member
127     setFocusHandler(nullptr);
128 
129     mAllWidgets.remove(this);
130     mAllWidgetsSet.erase(this);
131 }
132 
setWidth(const int width)133 void Widget::setWidth(const int width)
134 {
135     Rect newDimension = mDimension;
136     newDimension.width = width;
137     setDimension(newDimension);
138 }
139 
setHeight(const int height)140 void Widget::setHeight(const int height)
141 {
142     Rect newDimension = mDimension;
143     newDimension.height = height;
144     setDimension(newDimension);
145 }
146 
setX(const int x)147 void Widget::setX(const int x)
148 {
149     Rect newDimension = mDimension;
150     newDimension.x = x;
151     setDimension(newDimension);
152 }
153 
setY(const int y)154 void Widget::setY(const int y)
155 {
156     Rect newDimension = mDimension;
157     newDimension.y = y;
158     setDimension(newDimension);
159 }
160 
setPosition(const int x,const int y)161 void Widget::setPosition(const int x, const int y)
162 {
163     Rect newDimension = mDimension;
164     newDimension.x = x;
165     newDimension.y = y;
166     setDimension(newDimension);
167 }
168 
setDimension(const Rect & dimension)169 void Widget::setDimension(const Rect& dimension)
170 {
171     const Rect oldDimension = mDimension;
172     mDimension = dimension;
173 
174     if (mDimension.width != oldDimension.width
175         || mDimension.height != oldDimension.height)
176     {
177         distributeResizedEvent();
178     }
179 
180     if (mDimension.x != oldDimension.x || mDimension.y != oldDimension.y)
181         distributeMovedEvent();
182 }
183 
isFocused() const184 bool Widget::isFocused() const
185 {
186     if (mFocusHandler == nullptr)
187         return false;
188 
189     return mFocusHandler->isFocused(this);
190 }
191 
setFocusable(const bool focusable)192 void Widget::setFocusable(const bool focusable)
193 {
194     if (!focusable && isFocused() && (mFocusHandler != nullptr))
195         mFocusHandler->focusNone();
196     mFocusable = focusable;
197 }
198 
isFocusable() const199 bool Widget::isFocusable() const
200 {
201     return mFocusable && isVisible() && isEnabled();
202 }
203 
requestFocus()204 void Widget::requestFocus()
205 {
206     if (mFocusHandler == nullptr)
207         return;
208 
209     if (isFocusable())
210         mFocusHandler->requestFocus(this);
211 }
212 
requestMoveToTop()213 void Widget::requestMoveToTop()
214 {
215     if (mParent != nullptr)
216         mParent->moveToTop(this);
217 }
218 
requestMoveToBottom()219 void Widget::requestMoveToBottom()
220 {
221     if (mParent != nullptr)
222         mParent->moveToBottom(this);
223 }
224 
setVisible(Visible visible)225 void Widget::setVisible(Visible visible)
226 {
227     if (visible == Visible_false && isFocused() && (mFocusHandler != nullptr))
228         mFocusHandler->focusNone();
229 
230     if (visible == Visible_true)
231         distributeShownEvent();
232     else
233         distributeHiddenEvent();
234 
235     mVisible = visible;
236 }
237 
setFocusHandler(FocusHandler * const focusHandler)238 void Widget::setFocusHandler(FocusHandler *const focusHandler)
239 {
240     if (mFocusHandler != nullptr)
241     {
242         releaseModalFocus();
243         mFocusHandler->remove(this);
244     }
245 
246     if (focusHandler != nullptr)
247         focusHandler->add(this);
248 
249     mFocusHandler = focusHandler;
250 }
251 
addActionListener(ActionListener * const actionListener)252 void Widget::addActionListener(ActionListener *const actionListener)
253 {
254     mActionListeners.push_back(actionListener);
255 }
256 
removeActionListener(ActionListener * const actionListener)257 void Widget::removeActionListener(ActionListener *const actionListener)
258 {
259     mActionListeners.remove(actionListener);
260 }
261 
addDeathListener(WidgetDeathListener * const deathListener)262 void Widget::addDeathListener(WidgetDeathListener *const deathListener)
263 {
264     mDeathListeners.push_back(deathListener);
265 }
266 
removeDeathListener(WidgetDeathListener * const deathListener)267 void Widget::removeDeathListener(WidgetDeathListener *const deathListener)
268 {
269     mDeathListeners.remove(deathListener);
270 }
271 
addKeyListener(KeyListener * const keyListener)272 void Widget::addKeyListener(KeyListener *const keyListener)
273 {
274     mKeyListeners.push_back(keyListener);
275 }
276 
removeKeyListener(KeyListener * const keyListener)277 void Widget::removeKeyListener(KeyListener *const keyListener)
278 {
279     mKeyListeners.remove(keyListener);
280 }
281 
addFocusListener(FocusListener * const focusListener)282 void Widget::addFocusListener(FocusListener *const focusListener)
283 {
284     mFocusListeners.push_back(focusListener);
285 }
286 
removeFocusListener(FocusListener * const focusListener)287 void Widget::removeFocusListener(FocusListener *const focusListener)
288 {
289     mFocusListeners.remove(focusListener);
290 }
291 
addMouseListener(MouseListener * const mouseListener)292 void Widget::addMouseListener(MouseListener *const mouseListener)
293 {
294     mMouseListeners.push_back(mouseListener);
295 }
296 
removeMouseListener(MouseListener * const mouseListener)297 void Widget::removeMouseListener(MouseListener *const mouseListener)
298 {
299     mMouseListeners.remove(mouseListener);
300 }
301 
addWidgetListener(WidgetListener * const widgetListener)302 void Widget::addWidgetListener(WidgetListener *const widgetListener)
303 {
304     mWidgetListeners.push_back(widgetListener);
305 }
306 
removeWidgetListener(WidgetListener * const widgetListener)307 void Widget::removeWidgetListener(WidgetListener *const widgetListener)
308 {
309     mWidgetListeners.remove(widgetListener);
310 }
311 
getAbsolutePosition(int & x,int & y) const312 void Widget::getAbsolutePosition(int& x, int& y) const
313 {
314     if (mParent == nullptr)
315     {
316         x = mDimension.x;
317         y = mDimension.y;
318         return;
319     }
320 
321     int parentX;
322     int parentY;
323 
324     mParent->getAbsolutePosition(parentX, parentY);
325 
326     const Rect &rect = mParent->getChildrenArea();
327     x = parentX + mDimension.x + rect.x;
328     y = parentY + mDimension.y + rect.y;
329 }
330 
getFont() const331 Font* Widget::getFont() const
332 {
333     if (mCurrentFont == nullptr)
334         return mGlobalFont;
335     return mCurrentFont;
336 }
337 
setGlobalFont(Font * const font)338 void Widget::setGlobalFont(Font *const font)
339 {
340     mGlobalFont = font;
341 
342     FOR_EACH (std::list<Widget*>::const_iterator, iter, mAllWidgets)
343     {
344         if ((*iter)->mCurrentFont == nullptr)
345             (*iter)->fontChanged();
346     }
347 }
348 
setFont(Font * const font)349 void Widget::setFont(Font *const font)
350 {
351     mCurrentFont = font;
352     fontChanged();
353 }
354 
distributeWindowResizeEvent()355 void Widget::distributeWindowResizeEvent()
356 {
357     FOR_EACH (std::list<Widget*>::const_iterator, iter, mAllWidgets)
358         (*iter)->windowResized();
359 }
360 
widgetExists(const Widget * const widget)361 bool Widget::widgetExists(const Widget *const widget)
362 {
363     return mAllWidgetsSet.find(const_cast<Widget*>(widget))
364         != mAllWidgetsSet.end();
365 }
366 
setSize(const int width,const int height)367 void Widget::setSize(const int width, const int height)
368 {
369     Rect newDimension = mDimension;
370     newDimension.width = width;
371     newDimension.height = height;
372     setDimension(newDimension);
373 }
374 
isEnabled() const375 bool Widget::isEnabled() const
376 {
377     return mEnabled && isVisible();
378 }
379 
requestModalFocus()380 void Widget::requestModalFocus()
381 {
382     if (mFocusHandler == nullptr)
383         return;
384     mFocusHandler->requestModalFocus(this);
385 }
386 
requestModalMouseInputFocus()387 void Widget::requestModalMouseInputFocus()
388 {
389     if (mFocusHandler == nullptr)
390         return;
391     mFocusHandler->requestModalMouseInputFocus(this);
392 }
393 
releaseModalFocus()394 void Widget::releaseModalFocus()
395 {
396     if (mFocusHandler == nullptr)
397         return;
398     mFocusHandler->releaseModalFocus(this);
399 }
400 
releaseModalMouseInputFocus()401 void Widget::releaseModalMouseInputFocus()
402 {
403     if (mFocusHandler == nullptr)
404         return;
405     mFocusHandler->releaseModalMouseInputFocus(this);
406 }
407 
isModalFocused() const408 bool Widget::isModalFocused() const
409 {
410     if (mFocusHandler == nullptr)
411         return false;
412 
413     if (mParent != nullptr)
414     {
415         return (mFocusHandler->getModalFocused() == this)
416             || mParent->isModalFocused();
417     }
418 
419     return mFocusHandler->getModalFocused() == this;
420 }
421 
isModalMouseInputFocused() const422 bool Widget::isModalMouseInputFocused() const
423 {
424     if (mFocusHandler == nullptr)
425         return false;
426 
427     if (mParent != nullptr)
428     {
429         return (mFocusHandler->getModalMouseInputFocused() == this)
430             || mParent->isModalMouseInputFocused();
431     }
432 
433     return mFocusHandler->getModalMouseInputFocused() == this;
434 }
435 
getMouseListeners() const436 const std::list<MouseListener*> &Widget::getMouseListeners() const
437 {
438     return mMouseListeners;
439 }
440 
getKeyListeners() const441 const std::list<KeyListener*> &Widget::getKeyListeners() const
442 {
443     return mKeyListeners;
444 }
445 
getFocusListeners() const446 const std::list<FocusListener*> &Widget::getFocusListeners() const
447 {
448     return mFocusListeners;
449 }
450 
getChildrenArea()451 Rect Widget::getChildrenArea()
452 {
453     return Rect(0, 0, 0, 0);
454 }
455 
getInternalFocusHandler()456 FocusHandler* Widget::getInternalFocusHandler()
457 {
458     return mInternalFocusHandler;
459 }
460 
setInternalFocusHandler(FocusHandler * const focusHandler)461 void Widget::setInternalFocusHandler(FocusHandler *const focusHandler)
462 {
463     mInternalFocusHandler = focusHandler;
464 }
465 
distributeResizedEvent()466 void Widget::distributeResizedEvent()
467 {
468     FOR_EACH (WidgetListenerIterator, iter, mWidgetListeners)
469     {
470         Event event(this);
471         (*iter)->widgetResized(event);
472     }
473 }
474 
distributeMovedEvent()475 void Widget::distributeMovedEvent()
476 {
477     FOR_EACH (WidgetListenerIterator, iter, mWidgetListeners)
478     {
479         Event event(this);
480         (*iter)->widgetMoved(event);
481     }
482 }
483 
distributeHiddenEvent()484 void Widget::distributeHiddenEvent()
485 {
486     FOR_EACH (WidgetListenerIterator, iter, mWidgetListeners)
487     {
488         Event event(this);
489         (*iter)->widgetHidden(event);
490     }
491 }
492 
distributeActionEvent()493 void Widget::distributeActionEvent()
494 {
495     FOR_EACH (ActionListenerIterator, iter, mActionListeners)
496     {
497         ActionEvent actionEvent(this, mActionEventId);
498         (*iter)->action(actionEvent);
499     }
500 }
501 
distributeShownEvent()502 void Widget::distributeShownEvent()
503 {
504     FOR_EACH (WidgetListenerIterator, iter, mWidgetListeners)
505     {
506         Event event(this);
507         (*iter)->widgetShown(event);
508     }
509 }
510 
showPart(const Rect & rectangle)511 void Widget::showPart(const Rect &rectangle)
512 {
513     if (mParent != nullptr)
514         mParent->showWidgetPart(this, rectangle);
515 }
516 
windowResized()517 void Widget::windowResized()
518 {
519     mRedraw = true;
520 }
521 
callPostInit(Widget * const widget)522 Widget *Widget::callPostInit(Widget *const widget)
523 {
524     if (widget != nullptr)
525         widget->postInit();
526     return widget;
527 }
528