~ubuntu-branches/debian/jessie/scummvm/jessie

« back to all changes in this revision

Viewing changes to gui/widgets/popup.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Moritz Muehlenhoff
  • Date: 2011-05-25 19:02:23 UTC
  • mto: This revision was merged to the branch mainline in revision 23.
  • Revision ID: james.westby@ubuntu.com-20110525190223-fiqm0oaec714xk31
Tags: upstream-1.3.0
ImportĀ upstreamĀ versionĀ 1.3.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* ScummVM - Graphic Adventure Engine
 
2
 *
 
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.
 
6
 *
 
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.
 
11
 *
 
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.
 
16
 *
 
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.
 
20
 *
 
21
 * $URL$
 
22
 * $Id$
 
23
 */
 
24
 
 
25
#include "common/system.h"
 
26
#include "gui/dialog.h"
 
27
#include "gui/gui-manager.h"
 
28
#include "gui/widgets/popup.h"
 
29
 
 
30
#include "gui/ThemeEval.h"
 
31
 
 
32
namespace GUI {
 
33
 
 
34
//
 
35
// PopUpDialog
 
36
//
 
37
 
 
38
class PopUpDialog : public Dialog {
 
39
protected:
 
40
        PopUpWidget     *_popUpBoss;
 
41
        int                     _clickX, _clickY;
 
42
        byte            *_buffer;
 
43
        int                     _selection;
 
44
        uint32          _openTime;
 
45
        bool            _twoColumns;
 
46
        int                     _entriesPerColumn;
 
47
 
 
48
        int                     _leftPadding;
 
49
        int                     _rightPadding;
 
50
 
 
51
public:
 
52
        PopUpDialog(PopUpWidget *boss, int clickX, int clickY);
 
53
 
 
54
        void drawDialog();
 
55
 
 
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.
 
60
 
 
61
protected:
 
62
        void drawMenuEntry(int entry, bool hilite);
 
63
 
 
64
        int findItem(int x, int y) const;
 
65
        void setSelection(int item);
 
66
        bool isMouseDown();
 
67
 
 
68
        void moveUp();
 
69
        void moveDown();
 
70
};
 
71
 
 
72
PopUpDialog::PopUpDialog(PopUpWidget *boss, int clickX, int clickY)
 
73
        : Dialog(0, 0, 16, 16),
 
74
        _popUpBoss(boss) {
 
75
 
 
76
        // Copy the selection index
 
77
        _selection = _popUpBoss->_selectedItem;
 
78
 
 
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;
 
84
 
 
85
        _leftPadding = _popUpBoss->_leftPadding;
 
86
        _rightPadding = _popUpBoss->_rightPadding;
 
87
 
 
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.
 
91
 
 
92
        const int screenH = g_system->getOverlayHeight();
 
93
 
 
94
        // HACK: For now, we do not do scrolling. Instead, we draw the dialog
 
95
        // in two columns if it's too tall.
 
96
 
 
97
        if (_h >= screenH) {
 
98
                const int screenW = g_system->getOverlayWidth();
 
99
 
 
100
                _twoColumns = true;
 
101
                _entriesPerColumn = _popUpBoss->_entries.size() / 2;
 
102
 
 
103
                if (_popUpBoss->_entries.size() & 1)
 
104
                        _entriesPerColumn++;
 
105
 
 
106
                _h = _entriesPerColumn * kLineHeight + 2;
 
107
                _w = 0;
 
108
 
 
109
                for (uint i = 0; i < _popUpBoss->_entries.size(); i++) {
 
110
                        int width = g_gui.getStringWidth(_popUpBoss->_entries[i].name);
 
111
 
 
112
                        if (width > _w)
 
113
                                _w = width;
 
114
                }
 
115
 
 
116
                _w = 2 * _w + 10;
 
117
 
 
118
                if (!(_w & 1))
 
119
                        _w++;
 
120
 
 
121
                if (_popUpBoss->_selectedItem >= _entriesPerColumn) {
 
122
                        _x -= _w / 2;
 
123
                        _y = _popUpBoss->getAbsY() - (_popUpBoss->_selectedItem - _entriesPerColumn) * kLineHeight;
 
124
                }
 
125
 
 
126
                if (_w >= screenW)
 
127
                        _w = screenW - 1;
 
128
                if (_x < 0)
 
129
                        _x = 0;
 
130
                if (_x + _w >= screenW)
 
131
                        _x = screenW - 1 - _w;
 
132
        } else
 
133
                _twoColumns = false;
 
134
 
 
135
        if (_h >= screenH)
 
136
                _h = screenH - 1;
 
137
        if (_y < 0)
 
138
                _y = 0;
 
139
        else if (_y + _h >= screenH)
 
140
                _y = screenH - 1 - _h;
 
141
 
 
142
        // TODO - implement scrolling if we had to move the menu, or if there are too many entries
 
143
 
 
144
        // Remember original mouse position
 
145
        _clickX = clickX - _x;
 
146
        _clickY = clickY - _y;
 
147
 
 
148
        _openTime = 0;
 
149
}
 
150
 
 
151
void PopUpDialog::drawDialog() {
 
152
        // Draw the menu border
 
153
        g_gui.theme()->drawWidgetBackground(Common::Rect(_x, _y, _x+_w, _y+_h), 0);
 
154
 
 
155
        /*if (_twoColumns)
 
156
                g_gui.vLine(_x + _w / 2, _y, _y + _h - 2, g_gui._color);*/
 
157
 
 
158
        // Draw the entries
 
159
        int count = _popUpBoss->_entries.size();
 
160
        for (int i = 0; i < count; i++) {
 
161
                drawMenuEntry(i, i == _selection);
 
162
        }
 
163
 
 
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);
 
167
        }*/
 
168
 
 
169
        if (_openTime == 0) {
 
170
                // Time the popup was opened
 
171
                _openTime = g_system->getMillis();
 
172
        }
 
173
}
 
174
 
 
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);
 
181
                close();
 
182
        }
 
183
        _clickX = -1;
 
184
        _clickY = -1;
 
185
        _openTime = (uint32)-1;
 
186
}
 
187
 
 
188
void PopUpDialog::handleMouseWheel(int x, int y, int direction) {
 
189
        if (direction < 0)
 
190
                moveUp();
 
191
        else if (direction > 0)
 
192
                moveDown();
 
193
}
 
194
 
 
195
void PopUpDialog::handleMouseMoved(int x, int y, int button) {
 
196
        // Compute over which item the mouse is...
 
197
        int item = findItem(x, y);
 
198
 
 
199
        if (item >= 0 && _popUpBoss->_entries[item].name.size() == 0)
 
200
                item = -1;
 
201
 
 
202
        if (item == -1 && !isMouseDown()) {
 
203
                setSelection(_popUpBoss->_selectedItem);
 
204
                return;
 
205
        }
 
206
 
 
207
        // ...and update the selection accordingly
 
208
        setSelection(item);
 
209
}
 
210
 
 
211
void PopUpDialog::handleKeyDown(Common::KeyState state) {
 
212
        if (state.keycode == Common::KEYCODE_ESCAPE) {
 
213
                // Don't change the previous selection
 
214
                setResult(-1);
 
215
                close();
 
216
                return;
 
217
        }
 
218
 
 
219
        if (isMouseDown())
 
220
                return;
 
221
 
 
222
        switch (state.keycode) {
 
223
 
 
224
        case Common::KEYCODE_RETURN:
 
225
        case Common::KEYCODE_KP_ENTER:
 
226
                setResult(_selection);
 
227
                close();
 
228
                break;
 
229
 
 
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
 
233
 
 
234
        case Common::KEYCODE_KP1:
 
235
                if (state.flags & Common::KBD_NUM)
 
236
                        break;
 
237
        case Common::KEYCODE_END:
 
238
                setSelection(_popUpBoss->_entries.size()-1);
 
239
                break;
 
240
 
 
241
        case Common::KEYCODE_KP2:
 
242
                if (state.flags & Common::KBD_NUM)
 
243
                        break;
 
244
        case Common::KEYCODE_DOWN:
 
245
                moveDown();
 
246
                break;
 
247
 
 
248
        case Common::KEYCODE_KP7:
 
249
                if (state.flags & Common::KBD_NUM)
 
250
                        break;
 
251
        case Common::KEYCODE_HOME:
 
252
                setSelection(0);
 
253
                break;
 
254
 
 
255
        case Common::KEYCODE_KP8:
 
256
                if (state.flags & Common::KBD_NUM)
 
257
                        break;
 
258
        case Common::KEYCODE_UP:
 
259
                moveUp();
 
260
                break;
 
261
 
 
262
        default:
 
263
                break;
 
264
        }
 
265
}
 
266
 
 
267
int PopUpDialog::findItem(int x, int y) const {
 
268
        if (x >= 0 && x < _w && y >= 0 && y < _h) {
 
269
                if (_twoColumns) {
 
270
                        uint entry = (y - 2) / kLineHeight;
 
271
                        if (x > _w / 2) {
 
272
                                entry += _entriesPerColumn;
 
273
 
 
274
                                if (entry >= _popUpBoss->_entries.size())
 
275
                                        return -1;
 
276
                        }
 
277
                        return entry;
 
278
                }
 
279
                return (y - 2) / kLineHeight;
 
280
        }
 
281
        return -1;
 
282
}
 
283
 
 
284
void PopUpDialog::setSelection(int item) {
 
285
        if (item != _selection) {
 
286
                // Undraw old selection
 
287
                if (_selection >= 0)
 
288
                        drawMenuEntry(_selection, false);
 
289
 
 
290
                // Change selection
 
291
                _selection = item;
 
292
 
 
293
                // Draw new selection
 
294
                if (item >= 0)
 
295
                        drawMenuEntry(item, true);
 
296
        }
 
297
}
 
298
 
 
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...
 
303
 
 
304
        return false;
 
305
}
 
306
 
 
307
void PopUpDialog::moveUp() {
 
308
        if (_selection < 0) {
 
309
                setSelection(_popUpBoss->_entries.size() - 1);
 
310
        } else if (_selection > 0) {
 
311
                int item = _selection;
 
312
                do {
 
313
                        item--;
 
314
                } while (item >= 0 && _popUpBoss->_entries[item].name.size() == 0);
 
315
                if (item >= 0)
 
316
                        setSelection(item);
 
317
        }
 
318
}
 
319
 
 
320
void PopUpDialog::moveDown() {
 
321
        int lastItem = _popUpBoss->_entries.size() - 1;
 
322
 
 
323
        if (_selection < 0) {
 
324
                setSelection(0);
 
325
        } else if (_selection < lastItem) {
 
326
                int item = _selection;
 
327
                do {
 
328
                        item++;
 
329
                } while (item <= lastItem && _popUpBoss->_entries[item].name.size() == 0);
 
330
                if (item <= lastItem)
 
331
                        setSelection(item);
 
332
        }
 
333
}
 
334
 
 
335
void PopUpDialog::drawMenuEntry(int entry, bool hilite) {
 
336
        // Draw one entry of the popup menu, including selection
 
337
        assert(entry >= 0);
 
338
        int x, y, w;
 
339
 
 
340
        if (_twoColumns) {
 
341
                int n = _popUpBoss->_entries.size() / 2;
 
342
 
 
343
                if (_popUpBoss->_entries.size() & 1)
 
344
                        n++;
 
345
 
 
346
                if (entry >= n) {
 
347
                        x = _x + 1 + _w / 2;
 
348
                        y = _y + 1 + kLineHeight * (entry - n);
 
349
                } else {
 
350
                        x = _x + 1;
 
351
                        y = _y + 1 + kLineHeight * entry;
 
352
                }
 
353
 
 
354
                w = _w / 2 - 1;
 
355
        } else {
 
356
                x = _x + 1;
 
357
                y = _y + 1 + kLineHeight * entry;
 
358
                w = _w - 2;
 
359
        }
 
360
 
 
361
        Common::String &name(_popUpBoss->_entries[entry].name);
 
362
 
 
363
        if (name.size() == 0) {
 
364
                // Draw a separator
 
365
                g_gui.theme()->drawLineSeparator(Common::Rect(x, y, x+w, y+kLineHeight));
 
366
        } else {
 
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);
 
369
        }
 
370
}
 
371
 
 
372
 
 
373
#pragma mark -
 
374
 
 
375
//
 
376
// PopUpWidget
 
377
//
 
378
 
 
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;
 
383
 
 
384
        _selectedItem = -1;
 
385
}
 
386
 
 
387
void PopUpWidget::handleMouseDown(int x, int y, int button, int clickCount) {
 
388
        if (isEnabled()) {
 
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);
 
394
                }
 
395
        }
 
396
}
 
397
 
 
398
void PopUpWidget::handleMouseWheel(int x, int y, int direction) {
 
399
        int newSelection = _selectedItem + direction;
 
400
 
 
401
        // Skip separator entries
 
402
        while ((newSelection >= 0) && (newSelection < (int)_entries.size()) &&
 
403
                _entries[newSelection].name.equals("")) {
 
404
                newSelection += direction;
 
405
        }
 
406
 
 
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;
 
411
                draw();
 
412
        }
 
413
}
 
414
 
 
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);
 
418
 
 
419
        Widget::reflowLayout();
 
420
}
 
421
 
 
422
void PopUpWidget::appendEntry(const String &entry, uint32 tag) {
 
423
        Entry e;
 
424
        e.name = entry;
 
425
        e.tag = tag;
 
426
        _entries.push_back(e);
 
427
}
 
428
 
 
429
void PopUpWidget::clearEntries() {
 
430
        _entries.clear();
 
431
        _selectedItem = -1;
 
432
}
 
433
 
 
434
void PopUpWidget::setSelected(int item) {
 
435
        if (item != _selectedItem) {
 
436
                if (item >= 0 && item < (int)_entries.size()) {
 
437
                        _selectedItem = item;
 
438
                } else {
 
439
                        _selectedItem = -1;
 
440
                }
 
441
        }
 
442
}
 
443
 
 
444
void PopUpWidget::setSelectedTag(uint32 tag) {
 
445
        uint item;
 
446
        for (item = 0; item < _entries.size(); ++item) {
 
447
                if (_entries[item].tag == tag) {
 
448
                        setSelected(item);
 
449
                        return;
 
450
                }
 
451
        }
 
452
}
 
453
 
 
454
void PopUpWidget::drawWidget() {
 
455
        Common::String sel;
 
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);
 
459
}
 
460
 
 
461
} // End of namespace GUI