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 "gui/dialog.h"
27
#include "gui/gui-manager.h"
28
#include "gui/widgets/popup.h"
30
#include "gui/ThemeEval.h"
38
class PopUpDialog : public Dialog {
40
PopUpWidget *_popUpBoss;
46
int _entriesPerColumn;
52
PopUpDialog(PopUpWidget *boss, int clickX, int clickY);
56
void handleMouseUp(int x, int y, int button, int clickCount);
57
void handleMouseWheel(int x, int y, int direction); // Scroll through entries with scroll wheel
58
void handleMouseMoved(int x, int y, int button); // Redraw selections depending on mouse position
59
void handleKeyDown(Common::KeyState state); // Scroll through entries with arrow keys etc.
62
void drawMenuEntry(int entry, bool hilite);
64
int findItem(int x, int y) const;
65
void setSelection(int item);
72
PopUpDialog::PopUpDialog(PopUpWidget *boss, int clickX, int clickY)
73
: Dialog(0, 0, 16, 16),
76
// Copy the selection index
77
_selection = _popUpBoss->_selectedItem;
79
// Calculate real popup dimensions
80
_x = _popUpBoss->getAbsX();
81
_y = _popUpBoss->getAbsY() - _popUpBoss->_selectedItem * kLineHeight;
82
_h = _popUpBoss->_entries.size() * kLineHeight + 2;
83
_w = _popUpBoss->_w - kLineHeight + 2;
85
_leftPadding = _popUpBoss->_leftPadding;
86
_rightPadding = _popUpBoss->_rightPadding;
88
// Perform clipping / switch to scrolling mode if we don't fit on the screen
89
// FIXME - OSystem should send out notification messages when the screen
90
// resolution changes... we could generalize CommandReceiver and CommandSender.
92
const int screenH = g_system->getOverlayHeight();
94
// HACK: For now, we do not do scrolling. Instead, we draw the dialog
95
// in two columns if it's too tall.
98
const int screenW = g_system->getOverlayWidth();
101
_entriesPerColumn = _popUpBoss->_entries.size() / 2;
103
if (_popUpBoss->_entries.size() & 1)
106
_h = _entriesPerColumn * kLineHeight + 2;
109
for (uint i = 0; i < _popUpBoss->_entries.size(); i++) {
110
int width = g_gui.getStringWidth(_popUpBoss->_entries[i].name);
121
if (_popUpBoss->_selectedItem >= _entriesPerColumn) {
123
_y = _popUpBoss->getAbsY() - (_popUpBoss->_selectedItem - _entriesPerColumn) * kLineHeight;
130
if (_x + _w >= screenW)
131
_x = screenW - 1 - _w;
139
else if (_y + _h >= screenH)
140
_y = screenH - 1 - _h;
142
// TODO - implement scrolling if we had to move the menu, or if there are too many entries
144
// Remember original mouse position
145
_clickX = clickX - _x;
146
_clickY = clickY - _y;
151
void PopUpDialog::drawDialog() {
152
// Draw the menu border
153
g_gui.theme()->drawWidgetBackground(Common::Rect(_x, _y, _x+_w, _y+_h), 0);
156
g_gui.vLine(_x + _w / 2, _y, _y + _h - 2, g_gui._color);*/
159
int count = _popUpBoss->_entries.size();
160
for (int i = 0; i < count; i++) {
161
drawMenuEntry(i, i == _selection);
164
// The last entry may be empty. Fill it with black.
165
/*if (_twoColumns && (count & 1)) {
166
g_gui.fillRect(_x + 1 + _w / 2, _y + 1 + kLineHeight * (_entriesPerColumn - 1), _w / 2 - 1, kLineHeight, g_gui._bgcolor);
169
if (_openTime == 0) {
170
// Time the popup was opened
171
_openTime = g_system->getMillis();
175
void PopUpDialog::handleMouseUp(int x, int y, int button, int clickCount) {
176
// Mouse was released. If it wasn't moved much since the original mouse down,
177
// let the popup stay open. If it did move, assume the user made his selection.
178
int dist = (_clickX - x) * (_clickX - x) + (_clickY - y) * (_clickY - y);
179
if (dist > 3 * 3 || g_system->getMillis() - _openTime > 300) {
180
setResult(_selection);
185
_openTime = (uint32)-1;
188
void PopUpDialog::handleMouseWheel(int x, int y, int direction) {
191
else if (direction > 0)
195
void PopUpDialog::handleMouseMoved(int x, int y, int button) {
196
// Compute over which item the mouse is...
197
int item = findItem(x, y);
199
if (item >= 0 && _popUpBoss->_entries[item].name.size() == 0)
202
if (item == -1 && !isMouseDown()) {
203
setSelection(_popUpBoss->_selectedItem);
207
// ...and update the selection accordingly
211
void PopUpDialog::handleKeyDown(Common::KeyState state) {
212
if (state.keycode == Common::KEYCODE_ESCAPE) {
213
// Don't change the previous selection
222
switch (state.keycode) {
224
case Common::KEYCODE_RETURN:
225
case Common::KEYCODE_KP_ENTER:
226
setResult(_selection);
230
// Keypad & special keys
231
// - if num lock is set, we ignore the keypress
232
// - if num lock is not set, we fall down to the special key case
234
case Common::KEYCODE_KP1:
235
if (state.flags & Common::KBD_NUM)
237
case Common::KEYCODE_END:
238
setSelection(_popUpBoss->_entries.size()-1);
241
case Common::KEYCODE_KP2:
242
if (state.flags & Common::KBD_NUM)
244
case Common::KEYCODE_DOWN:
248
case Common::KEYCODE_KP7:
249
if (state.flags & Common::KBD_NUM)
251
case Common::KEYCODE_HOME:
255
case Common::KEYCODE_KP8:
256
if (state.flags & Common::KBD_NUM)
258
case Common::KEYCODE_UP:
267
int PopUpDialog::findItem(int x, int y) const {
268
if (x >= 0 && x < _w && y >= 0 && y < _h) {
270
uint entry = (y - 2) / kLineHeight;
272
entry += _entriesPerColumn;
274
if (entry >= _popUpBoss->_entries.size())
279
return (y - 2) / kLineHeight;
284
void PopUpDialog::setSelection(int item) {
285
if (item != _selection) {
286
// Undraw old selection
288
drawMenuEntry(_selection, false);
293
// Draw new selection
295
drawMenuEntry(item, true);
299
bool PopUpDialog::isMouseDown() {
300
// TODO/FIXME - need a way to determine whether any mouse buttons are pressed or not.
301
// Sure, we could just count mouse button up/down events, but that is cumbersome and
302
// error prone. Would be much nicer to add an API to OSystem for this...
307
void PopUpDialog::moveUp() {
308
if (_selection < 0) {
309
setSelection(_popUpBoss->_entries.size() - 1);
310
} else if (_selection > 0) {
311
int item = _selection;
314
} while (item >= 0 && _popUpBoss->_entries[item].name.size() == 0);
320
void PopUpDialog::moveDown() {
321
int lastItem = _popUpBoss->_entries.size() - 1;
323
if (_selection < 0) {
325
} else if (_selection < lastItem) {
326
int item = _selection;
329
} while (item <= lastItem && _popUpBoss->_entries[item].name.size() == 0);
330
if (item <= lastItem)
335
void PopUpDialog::drawMenuEntry(int entry, bool hilite) {
336
// Draw one entry of the popup menu, including selection
341
int n = _popUpBoss->_entries.size() / 2;
343
if (_popUpBoss->_entries.size() & 1)
348
y = _y + 1 + kLineHeight * (entry - n);
351
y = _y + 1 + kLineHeight * entry;
357
y = _y + 1 + kLineHeight * entry;
361
Common::String &name(_popUpBoss->_entries[entry].name);
363
if (name.size() == 0) {
365
g_gui.theme()->drawLineSeparator(Common::Rect(x, y, x+w, y+kLineHeight));
367
g_gui.theme()->drawText(Common::Rect(x+1, y+2, x+w, y+2+kLineHeight), name, hilite ? ThemeEngine::kStateHighlight : ThemeEngine::kStateEnabled,
368
Graphics::kTextAlignLeft, ThemeEngine::kTextInversionNone, _leftPadding);
379
PopUpWidget::PopUpWidget(GuiObject *boss, const String &name, const char *tooltip)
380
: Widget(boss, name, tooltip), CommandSender(boss) {
381
setFlags(WIDGET_ENABLED | WIDGET_CLEARBG | WIDGET_RETAIN_FOCUS | WIDGET_IGNORE_DRAG);
382
_type = kPopUpWidget;
387
void PopUpWidget::handleMouseDown(int x, int y, int button, int clickCount) {
389
PopUpDialog popupDialog(this, x + getAbsX(), y + getAbsY());
390
int newSel = popupDialog.runModal();
391
if (newSel != -1 && _selectedItem != newSel) {
392
_selectedItem = newSel;
393
sendCommand(kPopUpItemSelectedCmd, _entries[_selectedItem].tag);
398
void PopUpWidget::handleMouseWheel(int x, int y, int direction) {
399
int newSelection = _selectedItem + direction;
401
// Skip separator entries
402
while ((newSelection >= 0) && (newSelection < (int)_entries.size()) &&
403
_entries[newSelection].name.equals("")) {
404
newSelection += direction;
407
// Just update the selected item when we're in range
408
if ((newSelection >= 0) && (newSelection < (int)_entries.size()) &&
409
(newSelection != _selectedItem)) {
410
_selectedItem = newSelection;
415
void PopUpWidget::reflowLayout() {
416
_leftPadding = g_gui.xmlEval()->getVar("Globals.PopUpWidget.Padding.Left", 0);
417
_rightPadding = g_gui.xmlEval()->getVar("Globals.PopUpWidget.Padding.Right", 0);
419
Widget::reflowLayout();
422
void PopUpWidget::appendEntry(const String &entry, uint32 tag) {
426
_entries.push_back(e);
429
void PopUpWidget::clearEntries() {
434
void PopUpWidget::setSelected(int item) {
435
if (item != _selectedItem) {
436
if (item >= 0 && item < (int)_entries.size()) {
437
_selectedItem = item;
444
void PopUpWidget::setSelectedTag(uint32 tag) {
446
for (item = 0; item < _entries.size(); ++item) {
447
if (_entries[item].tag == tag) {
454
void PopUpWidget::drawWidget() {
456
if (_selectedItem >= 0)
457
sel = _entries[_selectedItem].name;
458
g_gui.theme()->drawPopUpWidget(Common::Rect(_x, _y, _x + _w, _y + _h), sel, _leftPadding, _state, Graphics::kTextAlignLeft);
461
} // End of namespace GUI