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