1 /* ScummVM - Graphic Adventure Engine
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 #include "common/system.h"
26 #include "common/events.h"
27 #include "gui/dialog.h"
28 #include "gui/GuiManager.h"
29 #include "gui/PopUpWidget.h"
30 #include "engines/engine.h"
32 #include "gui/ThemeEval.h"
40 class PopUpDialog
: public Dialog
{
42 PopUpWidget
*_popUpBoss
;
48 int _entriesPerColumn
;
54 PopUpDialog(PopUpWidget
*boss
, int clickX
, int clickY
);
58 void handleMouseUp(int x
, int y
, int button
, int clickCount
);
59 void handleMouseWheel(int x
, int y
, int direction
); // Scroll through entries with scroll wheel
60 void handleMouseMoved(int x
, int y
, int button
); // Redraw selections depending on mouse position
61 void handleKeyDown(Common::KeyState state
); // Scroll through entries with arrow keys etc.
64 void drawMenuEntry(int entry
, bool hilite
);
66 int findItem(int x
, int y
) const;
67 void setSelection(int item
);
74 PopUpDialog::PopUpDialog(PopUpWidget
*boss
, int clickX
, int clickY
)
75 : Dialog(0, 0, 16, 16),
78 // Copy the selection index
79 _selection
= _popUpBoss
->_selectedItem
;
81 // Calculate real popup dimensions
82 _x
= _popUpBoss
->getAbsX();
83 _y
= _popUpBoss
->getAbsY() - _popUpBoss
->_selectedItem
* kLineHeight
;
84 _h
= _popUpBoss
->_entries
.size() * kLineHeight
+ 2;
85 _w
= _popUpBoss
->_w
- kLineHeight
+ 2;
87 _leftPadding
= _popUpBoss
->_leftPadding
;
88 _rightPadding
= _popUpBoss
->_rightPadding
;
90 // Perform clipping / switch to scrolling mode if we don't fit on the screen
91 // FIXME - OSystem should send out notification messages when the screen
92 // resolution changes... we could generalize CommandReceiver and CommandSender.
94 const int screenH
= g_system
->getOverlayHeight();
96 // HACK: For now, we do not do scrolling. Instead, we draw the dialog
97 // in two columns if it's too tall.
100 const int screenW
= g_system
->getOverlayWidth();
103 _entriesPerColumn
= _popUpBoss
->_entries
.size() / 2;
105 if (_popUpBoss
->_entries
.size() & 1)
108 _h
= _entriesPerColumn
* kLineHeight
+ 2;
111 for (uint i
= 0; i
< _popUpBoss
->_entries
.size(); i
++) {
112 int width
= g_gui
.getStringWidth(_popUpBoss
->_entries
[i
].name
);
123 if (_popUpBoss
->_selectedItem
>= _entriesPerColumn
) {
125 _y
= _popUpBoss
->getAbsY() - (_popUpBoss
->_selectedItem
- _entriesPerColumn
) * kLineHeight
;
132 if (_x
+ _w
>= screenW
)
133 _x
= screenW
- 1 - _w
;
141 else if (_y
+ _h
>= screenH
)
142 _y
= screenH
- 1 - _h
;
144 // TODO - implement scrolling if we had to move the menu, or if there are too many entries
146 // Remember original mouse position
147 _clickX
= clickX
- _x
;
148 _clickY
= clickY
- _y
;
153 void PopUpDialog::drawDialog() {
154 // Draw the menu border
155 g_gui
.theme()->drawWidgetBackground(Common::Rect(_x
, _y
, _x
+_w
, _y
+_h
), 0);
158 g_gui.vLine(_x + _w / 2, _y, _y + _h - 2, g_gui._color);*/
161 int count
= _popUpBoss
->_entries
.size();
162 for (int i
= 0; i
< count
; i
++) {
163 drawMenuEntry(i
, i
== _selection
);
166 // The last entry may be empty. Fill it with black.
167 /*if (_twoColumns && (count & 1)) {
168 g_gui.fillRect(_x + 1 + _w / 2, _y + 1 + kLineHeight * (_entriesPerColumn - 1), _w / 2 - 1, kLineHeight, g_gui._bgcolor);
171 if (_openTime
== 0) {
172 // Time the popup was opened
173 _openTime
= getMillis();
177 void PopUpDialog::handleMouseUp(int x
, int y
, int button
, int clickCount
) {
178 // Mouse was released. If it wasn't moved much since the original mouse down,
179 // let the popup stay open. If it did move, assume the user made his selection.
180 int dist
= (_clickX
- x
) * (_clickX
- x
) + (_clickY
- y
) * (_clickY
- y
);
181 if (dist
> 3 * 3 || getMillis() - _openTime
> 300) {
182 setResult(_selection
);
187 _openTime
= (uint32
)-1;
190 void PopUpDialog::handleMouseWheel(int x
, int y
, int direction
) {
193 else if (direction
> 0)
197 void PopUpDialog::handleMouseMoved(int x
, int y
, int button
) {
198 // Compute over which item the mouse is...
199 int item
= findItem(x
, y
);
201 if (item
>= 0 && _popUpBoss
->_entries
[item
].name
.size() == 0)
204 if (item
== -1 && !isMouseDown()) {
205 setSelection(_popUpBoss
->_selectedItem
);
209 // ...and update the selection accordingly
213 void PopUpDialog::handleKeyDown(Common::KeyState state
) {
214 if (state
.keycode
== Common::KEYCODE_ESCAPE
) {
215 // Don't change the previous selection
224 switch (state
.keycode
) {
225 case Common::KEYCODE_RETURN
:
226 case Common::KEYCODE_KP_ENTER
:
227 setResult(_selection
);
230 case Common::KEYCODE_UP
:
233 case Common::KEYCODE_DOWN
:
236 case Common::KEYCODE_HOME
:
239 case Common::KEYCODE_END
:
240 setSelection(_popUpBoss
->_entries
.size()-1);
247 int PopUpDialog::findItem(int x
, int y
) const {
248 if (x
>= 0 && x
< _w
&& y
>= 0 && y
< _h
) {
250 uint entry
= (y
- 2) / kLineHeight
;
252 entry
+= _entriesPerColumn
;
254 if (entry
>= _popUpBoss
->_entries
.size())
259 return (y
- 2) / kLineHeight
;
264 void PopUpDialog::setSelection(int item
) {
265 if (item
!= _selection
) {
266 // Undraw old selection
268 drawMenuEntry(_selection
, false);
273 // Draw new selection
275 drawMenuEntry(item
, true);
279 bool PopUpDialog::isMouseDown() {
280 // TODO/FIXME - need a way to determine whether any mouse buttons are pressed or not.
281 // Sure, we could just count mouse button up/down events, but that is cumbersome and
282 // error prone. Would be much nicer to add an API to OSystem for this...
287 void PopUpDialog::moveUp() {
288 if (_selection
< 0) {
289 setSelection(_popUpBoss
->_entries
.size() - 1);
290 } else if (_selection
> 0) {
291 int item
= _selection
;
294 } while (item
>= 0 && _popUpBoss
->_entries
[item
].name
.size() == 0);
300 void PopUpDialog::moveDown() {
301 int lastItem
= _popUpBoss
->_entries
.size() - 1;
303 if (_selection
< 0) {
305 } else if (_selection
< lastItem
) {
306 int item
= _selection
;
309 } while (item
<= lastItem
&& _popUpBoss
->_entries
[item
].name
.size() == 0);
310 if (item
<= lastItem
)
315 void PopUpDialog::drawMenuEntry(int entry
, bool hilite
) {
316 // Draw one entry of the popup menu, including selection
321 int n
= _popUpBoss
->_entries
.size() / 2;
323 if (_popUpBoss
->_entries
.size() & 1)
328 y
= _y
+ 1 + kLineHeight
* (entry
- n
);
331 y
= _y
+ 1 + kLineHeight
* entry
;
337 y
= _y
+ 1 + kLineHeight
* entry
;
341 Common::String
&name(_popUpBoss
->_entries
[entry
].name
);
343 if (name
.size() == 0) {
345 g_gui
.theme()->drawLineSeparator(Common::Rect(x
, y
, x
+w
, y
+kLineHeight
));
347 g_gui
.theme()->drawText(Common::Rect(x
+1, y
+2, x
+w
, y
+2+kLineHeight
), name
, hilite
? ThemeEngine::kStateHighlight
: ThemeEngine::kStateEnabled
,
348 Graphics::kTextAlignLeft
, ThemeEngine::kTextInversionNone
, _leftPadding
);
359 PopUpWidget::PopUpWidget(GuiObject
*boss
, const String
&name
)
360 : Widget(boss
, name
), CommandSender(boss
) {
361 setFlags(WIDGET_ENABLED
| WIDGET_CLEARBG
| WIDGET_RETAIN_FOCUS
| WIDGET_IGNORE_DRAG
);
362 _type
= kPopUpWidget
;
367 void PopUpWidget::handleMouseDown(int x
, int y
, int button
, int clickCount
) {
369 PopUpDialog
popupDialog(this, x
+ getAbsX(), y
+ getAbsY());
370 int newSel
= popupDialog
.runModal();
371 if (newSel
!= -1 && _selectedItem
!= newSel
) {
372 _selectedItem
= newSel
;
373 sendCommand(kPopUpItemSelectedCmd
, _entries
[_selectedItem
].tag
);
378 void PopUpWidget::handleMouseWheel(int x
, int y
, int direction
) {
379 int newSelection
= _selectedItem
+ direction
;
381 // Skip separator entries
382 while ((newSelection
>= 0) && (newSelection
< (int)_entries
.size()) &&
383 _entries
[newSelection
].name
.equals("")) {
384 newSelection
+= direction
;
387 // Just update the selected item when we're in range
388 if ((newSelection
>= 0) && (newSelection
< (int)_entries
.size()) &&
389 (newSelection
!= _selectedItem
)) {
390 _selectedItem
= newSelection
;
395 void PopUpWidget::reflowLayout() {
396 _leftPadding
= g_gui
.xmlEval()->getVar("Globals.PopUpWidget.Padding.Left", 0);
397 _rightPadding
= g_gui
.xmlEval()->getVar("Globals.PopUpWidget.Padding.Right", 0);
399 Widget::reflowLayout();
402 void PopUpWidget::appendEntry(const String
&entry
, uint32 tag
) {
406 _entries
.push_back(e
);
409 void PopUpWidget::clearEntries() {
414 void PopUpWidget::setSelected(int item
) {
415 if (item
!= _selectedItem
) {
416 if (item
>= 0 && item
< (int)_entries
.size()) {
417 _selectedItem
= item
;
424 void PopUpWidget::setSelectedTag(uint32 tag
) {
426 for (item
= 0; item
< _entries
.size(); ++item
) {
427 if (_entries
[item
].tag
== tag
) {
434 void PopUpWidget::drawWidget() {
436 if (_selectedItem
>= 0)
437 sel
= _entries
[_selectedItem
].name
;
438 g_gui
.theme()->drawPopUpWidget(Common::Rect(_x
, _y
, _x
+ _w
, _y
+ _h
), sel
, _leftPadding
, _state
, Graphics::kTextAlignLeft
);
441 } // End of namespace GUI