ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

gui/dialog.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "common/rect.h"
00024 
00025 #ifdef ENABLE_KEYMAPPER
00026 #include "common/events.h"
00027 #endif
00028 
00029 #include "gui/gui-manager.h"
00030 #include "gui/dialog.h"
00031 #include "gui/widget.h"
00032 
00033 namespace GUI {
00034 
00035 /*
00036  * TODO list
00037  * - add some sense of the window being "active" (i.e. in front) or not. If it
00038  *   was inactive and just became active, reset certain vars (like who is focused).
00039  *   Maybe we should just add lostFocus and receivedFocus methods to Dialog, just
00040  *   like we have for class Widget?
00041  * ...
00042  */
00043 
00044 Dialog::Dialog(int x, int y, int w, int h)
00045     : GuiObject(x, y, w, h),
00046       _mouseWidget(0), _focusedWidget(0), _dragWidget(0), _tickleWidget(0), _visible(false),
00047     _backgroundType(GUI::ThemeEngine::kDialogBackgroundDefault) {
00048     // Some dialogs like LauncherDialog use internally a fixed size, even though
00049     // their widgets rely on the layout to be initialized correctly by the theme.
00050     // Thus we need to catch screen changes here too. If we do not do that, it
00051     // will for example crash after returning to the launcher when the user
00052     // started a 640x480 game with a non 1x scaler.
00053     g_gui.checkScreenChange();
00054 
00055     _result = -1;
00056 }
00057 
00058 Dialog::Dialog(const Common::String &name)
00059     : GuiObject(name),
00060       _mouseWidget(0), _focusedWidget(0), _dragWidget(0), _tickleWidget(0), _visible(false),
00061     _backgroundType(GUI::ThemeEngine::kDialogBackgroundDefault) {
00062 
00063     // It may happen that we have 3x scaler in launcher (960xY) and then 640x480
00064     // game will be forced to 1x. At this stage GUI will not be aware of
00065     // resolution change, so widgets will be off screen. This forces it to
00066     // recompute
00067     //
00068     // Fixes bug #1590596: "HE: When 3x graphics are choosen, F5 crashes game"
00069     // and bug #1595627: "SCUMM: F5 crashes game (640x480)"
00070     g_gui.checkScreenChange();
00071 
00072     _result = -1;
00073 }
00074 
00075 int Dialog::runModal() {
00076     // Open up
00077     open();
00078 
00079     // Start processing events
00080     g_gui.runLoop();
00081 
00082     // Return the result code
00083     return _result;
00084 }
00085 
00086 void Dialog::open() {
00087     _result = 0;
00088     _visible = true;
00089     g_gui.openDialog(this);
00090 
00091     setDefaultFocusedWidget();
00092 }
00093 
00094 void Dialog::close() {
00095     _visible = false;
00096 
00097     if (_mouseWidget) {
00098         _mouseWidget->handleMouseLeft(0);
00099         _mouseWidget = 0;
00100     }
00101     releaseFocus();
00102     g_gui.closeTopDialog();
00103 }
00104 
00105 void Dialog::reflowLayout() {
00106     // The screen has changed. That means the screen visual may also have
00107     // changed, so any cached image may be invalid. The subsequent redraw
00108     // should be treated as the very first draw.
00109 
00110     GuiObject::reflowLayout();
00111 
00112     Widget *w = _firstWidget;
00113     while (w) {
00114         w->reflowLayout();
00115         w = w->_next;
00116     }
00117 }
00118 
00119 void Dialog::lostFocus() {
00120     _dragWidget = NULL;
00121 
00122     if (_tickleWidget) {
00123         _tickleWidget->lostFocus();
00124     }
00125 }
00126 
00127 void Dialog::setFocusWidget(Widget *widget) {
00128     // The focus will change. Tell the old focused widget (if any)
00129     // that it lost the focus.
00130     releaseFocus();
00131 
00132     // Tell the new focused widget (if any) that it just gained the focus.
00133     if (widget)
00134         widget->receivedFocus();
00135 
00136     _focusedWidget = widget;
00137 }
00138 
00139 void Dialog::setDefaultFocusedWidget() {
00140     Widget *w = _firstWidget;
00141     // Search for the first objects that wantsFocus() (if any) and give it the focus
00142     while (w && !w->wantsFocus()) {
00143         w = w->_next;
00144     }
00145 
00146     setFocusWidget(w);
00147 }
00148 
00149 void Dialog::releaseFocus() {
00150     if (_focusedWidget) {
00151         _focusedWidget->lostFocus();
00152         _focusedWidget = 0;
00153     }
00154 }
00155 
00156 void Dialog::markWidgetsAsDirty() {
00157     Widget *w = _firstWidget;
00158     while (w) {
00159         w->markAsDirty();
00160         w = w->_next;
00161     }
00162 }
00163 
00164 void Dialog::drawDialog(DrawLayer layerToDraw) {
00165 
00166     if (!isVisible())
00167         return;
00168 
00169     g_gui.theme()->_layerToDraw = layerToDraw;
00170     g_gui.theme()->drawDialogBackground(Common::Rect(_x, _y, _x + _w, _y + _h), _backgroundType);
00171 
00172     markWidgetsAsDirty();
00173     drawWidgets();
00174 }
00175 
00176 void Dialog::drawWidgets() {
00177 
00178     if (!isVisible())
00179         return;
00180 
00181     // Draw all children
00182     Widget *w = _firstWidget;
00183     while (w) {
00184         //if (w->_debugVisible)
00185         w->draw();
00186         w = w->_next;
00187     }
00188 }
00189 
00190 void Dialog::handleMouseDown(int x, int y, int button, int clickCount) {
00191     Widget *w;
00192 
00193     w = findWidget(x, y);
00194 
00195     if (w && !(w->getFlags() & WIDGET_IGNORE_DRAG))
00196         _dragWidget = w;
00197 
00198     // If the click occurred inside a widget which is not the currently
00199     // focused one, change the focus to that widget.
00200     if (w && w != _focusedWidget && w->wantsFocus()) {
00201         setFocusWidget(w);
00202     }
00203 
00204     if (w)
00205         w->handleMouseDown(x - (w->getAbsX() - _x), y - (w->getAbsY() - _y), button, clickCount);
00206 }
00207 
00208 void Dialog::handleMouseUp(int x, int y, int button, int clickCount) {
00209     Widget *w;
00210 
00211     if (_focusedWidget) {
00212         //w = _focusedWidget;
00213 
00214         // Lose focus on mouseup unless the widget requested to retain the focus
00215         if (! (_focusedWidget->getFlags() & WIDGET_RETAIN_FOCUS )) {
00216             releaseFocus();
00217         }
00218     }
00219 
00220     if (_dragWidget)
00221         w = _dragWidget;
00222     else
00223         w = findWidget(x, y);
00224 
00225     if (w)
00226         w->handleMouseUp(x - (w->getAbsX() - _x), y - (w->getAbsY() - _y), button, clickCount);
00227 
00228     _dragWidget = 0;
00229 }
00230 
00231 void Dialog::handleMouseWheel(int x, int y, int direction) {
00232     Widget *w;
00233 
00234     // This may look a bit backwards, but I think it makes more sense for
00235     // the mouse wheel to primarily affect the widget the mouse is at than
00236     // the widget that happens to be focused.
00237 
00238     w = findWidget(x, y);
00239     if (!w)
00240         w = _focusedWidget;
00241     if (w)
00242         w->handleMouseWheel(x - (w->getAbsX() - _x), y - (w->getAbsY() - _y), direction);
00243 }
00244 
00245 void Dialog::handleKeyDown(Common::KeyState state) {
00246     if (_focusedWidget) {
00247         if (_focusedWidget->handleKeyDown(state))
00248             return;
00249     }
00250 
00251     // Hotkey handling
00252     if (state.ascii != 0) {
00253         Widget *w = _firstWidget;
00254         state.ascii = toupper(state.ascii);
00255         while (w) {
00256             if (w->_type == kButtonWidget && state.ascii == toupper(((ButtonWidget *)w)->_hotkey)) {
00257                 // The hotkey for widget w was pressed. We fake a mouse click into the
00258                 // button by invoking the appropriate methods.
00259                 w->handleMouseDown(0, 0, 1, 1);
00260                 w->handleMouseUp(0, 0, 1, 1);
00261                 return;
00262             }
00263             w = w->_next;
00264         }
00265     }
00266 
00267     // ESC closes all dialogs by default
00268     if (state.keycode == Common::KEYCODE_ESCAPE) {
00269         setResult(-1);
00270         close();
00271     }
00272 
00273     if (state.keycode == Common::KEYCODE_TAB) {
00274         // TODO: Maybe add Tab behaviours for all widgets too.
00275         // searches through widgets on screen for tab widget
00276         Widget *w = _firstWidget;
00277         while (w) {
00278             if (w->_type == kTabWidget)
00279                 if (w->handleKeyDown(state))
00280                     return;
00281 
00282             w = w->_next;
00283         }
00284     }
00285 }
00286 
00287 void Dialog::handleKeyUp(Common::KeyState state) {
00288     // Focused widget receives keyup events
00289     if (_focusedWidget)
00290         _focusedWidget->handleKeyUp(state);
00291 }
00292 
00293 void Dialog::handleMouseMoved(int x, int y, int button) {
00294     Widget *w;
00295 
00296     if (_focusedWidget && !_dragWidget) {
00297         w = _focusedWidget;
00298         int wx = w->getAbsX() - _x;
00299         int wy = w->getAbsY() - _y;
00300 
00301         // We still send mouseEntered/Left messages to the focused item
00302         // (but to no other items).
00303         bool mouseInFocusedWidget = (x >= wx && x < wx + w->_w && y >= wy && y < wy + w->_h);
00304         if (mouseInFocusedWidget && _mouseWidget != w) {
00305             if (_mouseWidget)
00306                 _mouseWidget->handleMouseLeft(button);
00307             _mouseWidget = w;
00308             w->handleMouseEntered(button);
00309         } else if (!mouseInFocusedWidget && _mouseWidget == w) {
00310             _mouseWidget = 0;
00311             w->handleMouseLeft(button);
00312         }
00313 
00314         if (w->getFlags() & WIDGET_TRACK_MOUSE)
00315             w->handleMouseMoved(x - wx, y - wy, button);
00316     }
00317 
00318     // We process mouseEntered/Left events if we don't have any
00319     // currently active dragged widget or if the currently dragged widget
00320     // does not want to be informed about the mouse mouse events.
00321     if (!_dragWidget || !(_dragWidget->getFlags() & WIDGET_TRACK_MOUSE))
00322         w = findWidget(x, y);
00323     else
00324         w = _dragWidget;
00325 
00326     if (_mouseWidget != w) {
00327         if (_mouseWidget)
00328             _mouseWidget->handleMouseLeft(button);
00329 
00330         // If we have a widget in drag mode we prevent mouseEntered
00331         // events from being sent to other widgets.
00332         if (_dragWidget && w != _dragWidget)
00333             w = 0;
00334 
00335         if (w)
00336             w->handleMouseEntered(button);
00337         _mouseWidget = w;
00338     }
00339 
00340     // We only sent mouse move events when the widget requests to be informed about them.
00341     if (w && (w->getFlags() & WIDGET_TRACK_MOUSE))
00342         w->handleMouseMoved(x - (w->getAbsX() - _x), y - (w->getAbsY() - _y), button);
00343 }
00344 
00345 void Dialog::handleTickle() {
00346     // Focused widget receives tickle notifications
00347     if (_focusedWidget && _focusedWidget->getFlags() & WIDGET_WANT_TICKLE)
00348         _focusedWidget->handleTickle();
00349 
00350     if (_tickleWidget && _tickleWidget->getFlags() & WIDGET_WANT_TICKLE)
00351         _tickleWidget->handleTickle();
00352 }
00353 
00354 void Dialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
00355     switch (cmd) {
00356     case kCloseCmd:
00357         close();
00358         break;
00359     }
00360 }
00361 
00362 #ifdef ENABLE_KEYMAPPER
00363 void Dialog::handleOtherEvent(Common::Event evt) { }
00364 #endif
00365 /*
00366  * Determine the widget at location (x,y) if any. Assumes the coordinates are
00367  * in the local coordinate system, i.e. relative to the top left of the dialog.
00368  */
00369 Widget *Dialog::findWidget(int x, int y) {
00370     return Widget::findWidgetInChain(_firstWidget, x, y);
00371 }
00372 
00373 Widget *Dialog::findWidget(const char *name) {
00374     return Widget::findWidgetInChain(_firstWidget, name);
00375 }
00376 
00377 void Dialog::removeWidget(Widget *del) {
00378     if (del == _mouseWidget || del->containsWidget(_mouseWidget))
00379         _mouseWidget = NULL;
00380     if (del == _focusedWidget || del->containsWidget(_focusedWidget))
00381         _focusedWidget = NULL;
00382     if (del == _dragWidget || del->containsWidget(_dragWidget))
00383         _dragWidget = NULL;
00384 
00385     GuiObject::removeWidget(del);
00386 }
00387 
00388 } // End of namespace GUI


Generated on Sat Feb 16 2019 05:00:49 for ResidualVM by doxygen 1.7.1
curved edge   curved edge