1
//============================================================================
5
// SS tttttt eeee ll ll aaaa
6
// SSSS tt ee ee ll ll aa
7
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
8
// SS SS tt ee ll ll aa aa
9
// SSSS ttt eeeee llll llll aaaaa
11
// Copyright (c) 1995-2010 by Bradford W. Mott, Stephen Anthony
12
// and the Stella Team
14
// See the file "License.txt" for information on usage and redistribution of
15
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
17
// $Id: ContextMenu.cxx 2001 2010-04-10 21:37:23Z stephena $
19
// Based on code from ScummVM - Scumm Interpreter
20
// Copyright (C) 2002-2004 The ScummVM project
21
//============================================================================
23
#include "OSystem.hxx"
24
#include "FrameBuffer.hxx"
26
#include "DialogContainer.hxx"
27
#include "ContextMenu.hxx"
29
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
30
ContextMenu::ContextMenu(GuiObject* boss, const GUI::Font& font,
31
const StringMap& items, int cmd)
32
: Dialog(&boss->instance(), &boss->parent(), 0, 0, 16, 16),
34
_rowHeight(font.getLineHeight()),
48
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
49
ContextMenu::~ContextMenu()
54
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
55
void ContextMenu::addItems(const StringMap& items)
60
// Resize to largest string
62
for(unsigned int i = 0; i < _entries.size(); ++i)
64
int length = _font->getStringWidth(_entries[i].first);
71
_h = 1; // recalculate this in ::recalc()
74
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
75
void ContextMenu::show(uInt32 x, uInt32 y, int item)
80
recalc(instance().frameBuffer().imageRect());
81
parent().addDialog(this);
85
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
86
void ContextMenu::center()
88
// Make sure the menu is exactly where it should be, in case the image
90
const GUI::Rect& image = instance().frameBuffer().imageRect();
92
uInt32 x = image.x() + _xorig;
93
uInt32 y = image.y() + _yorig;
94
uInt32 tx = image.x() + image.width();
95
uInt32 ty = image.y() + image.height();
96
if(x + _w > tx) x -= (x + _w - tx);
97
if(y + _h > ty) y -= (y + _h - ty);
99
surface().setPos(x, y);
102
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
103
void ContextMenu::recalc(const GUI::Rect& image)
105
// Now is the time to adjust the height
106
// If it's higher than the screen, we need to scroll through
107
int maxentries = (image.height() - 4) / _rowHeight;
108
if((int)_entries.size() > maxentries)
110
// We show two less than the max, so we have room for two scroll buttons
111
_numEntries = maxentries - 2;
112
_h = maxentries * _rowHeight + 4;
117
_numEntries = _entries.size();
118
_h = _entries.size() * _rowHeight + 4;
123
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
124
void ContextMenu::setSelected(int item)
126
if(item >= 0 && item < (int)_entries.size())
127
_selectedItem = item;
132
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
133
void ContextMenu::setSelected(const string& tag, const string& defaultTag)
135
for(unsigned int item = 0; item < _entries.size(); ++item)
137
if(BSPF_strcasecmp(_entries[item].second.c_str(), tag.c_str()) == 0)
144
// If we get this far, the value wasn't found; use the default value
145
for(unsigned int item = 0; item < _entries.size(); ++item)
147
if(BSPF_strcasecmp(_entries[item].second.c_str(), defaultTag.c_str()) == 0)
155
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
156
void ContextMenu::setSelectedMax()
158
setSelected(_entries.size() - 1);
161
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
162
void ContextMenu::clearSelection()
167
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
168
int ContextMenu::getSelected() const
170
return _selectedItem;
173
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
174
const string& ContextMenu::getSelectedName() const
176
return (_selectedItem >= 0) ? _entries[_selectedItem].first : EmptyString;
179
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
180
const string& ContextMenu::getSelectedTag() const
182
return (_selectedItem >= 0) ? _entries[_selectedItem].second : EmptyString;
185
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
186
void ContextMenu::handleMouseDown(int x, int y, int button, int clickCount)
188
// Only do a selection when the left button is in the dialog
191
x += getAbsX(); y += getAbsY();
192
if(x >= _x && x <= _x+_w && y >= _y && y <= _y+_h)
199
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
200
void ContextMenu::handleMouseMoved(int x, int y, int button)
202
// Compute over which item the mouse is...
203
int item = findItem(x, y);
207
// ...and update the selection accordingly
208
drawCurrentSelection(item);
211
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
212
void ContextMenu::handleKeyDown(int ascii, int keycode, int modifiers)
214
handleEvent(instance().eventHandler().eventForKey(keycode, kMenuMode));
217
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
218
void ContextMenu::handleJoyDown(int stick, int button)
220
handleEvent(instance().eventHandler().eventForJoyButton(stick, button, kMenuMode));
223
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
224
void ContextMenu::handleJoyAxis(int stick, int axis, int value)
226
if(value != 0) // we don't care about 'axis up' events
227
handleEvent(instance().eventHandler().eventForJoyAxis(stick, axis, value, kMenuMode));
230
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
231
bool ContextMenu::handleJoyHat(int stick, int hat, int value)
233
handleEvent(instance().eventHandler().eventForJoyHat(stick, hat, value, kMenuMode));
237
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
238
void ContextMenu::handleEvent(Event::Type e)
242
case Event::UISelect:
253
case Event::UICancel:
261
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
262
int ContextMenu::findItem(int x, int y) const
264
if(x >= 0 && x < _w && y >= 0 && y < _h)
265
return (y - 4) / _rowHeight;
270
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
271
void ContextMenu::drawCurrentSelection(int item)
274
_selectedOffset = item;
278
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
279
void ContextMenu::sendSelection()
281
// Select the correct item when scrolling; we have to take into account
282
// that the viewable items are no longer 1-to-1 with the entries
283
int item = _firstEntry + _selectedOffset;
287
if(_selectedOffset == 0) // scroll up
289
else if(_selectedOffset == _numEntries+1) // scroll down
295
// We remove the dialog when the user has selected an item
296
// Make sure the dialog is removed before sending any commands,
297
// since one consequence of sending a command may be to add another
301
// Send any command associated with the selection
302
_selectedItem = item;
303
sendCommand(_cmd ? _cmd : kCMenuItemSelectedCmd, _selectedItem, -1);
306
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
307
void ContextMenu::moveUp()
311
// Reaching the top of the list means we have to scroll up, but keep the
312
// current item offset
313
// Otherwise, the offset should decrease by 1
314
if(_selectedOffset == 1)
316
else if(_selectedOffset > 1)
317
drawCurrentSelection(_selectedOffset-1);
321
if(_selectedOffset > 0)
322
drawCurrentSelection(_selectedOffset-1);
326
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
327
void ContextMenu::moveDown()
331
// Reaching the bottom of the list means we have to scroll down, but keep the
332
// current item offset
333
// Otherwise, the offset should increase by 1
334
if(_selectedOffset == _numEntries)
336
else if(_selectedOffset < (int)_entries.size())
337
drawCurrentSelection(_selectedOffset+1);
341
if(_selectedOffset < (int)_entries.size() - 1)
342
drawCurrentSelection(_selectedOffset+1);
346
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
347
void ContextMenu::scrollUp()
356
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
357
void ContextMenu::scrollDown()
359
if(_firstEntry + _numEntries < (int)_entries.size())
366
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
367
void ContextMenu::drawDialog()
369
static uInt32 up_arrow[8] = {
379
static uInt32 down_arrow[8] = {
390
// Normally we add widgets and let Dialog::draw() take care of this
391
// logic. But for some reason, this Dialog was written differently
392
// by the ScummVM guys, so I'm not going to mess with it.
393
FBSurface& s = surface();
397
// Draw menu border and background
398
s.fillRect(_x+1, _y+1, _w-2, _h-2, kWidColor);
399
s.box(_x, _y, _w, _h, kColor, kShadowColor);
401
// Draw the entries, taking scroll buttons into account
402
int x = _x + 2, y = _y + 2, w = _w - 4;
404
// Show top scroll area
405
int offset = _selectedOffset;
408
s.hLine(x, y+_rowHeight-1, w+2, kShadowColor);
409
s.drawBitmap(up_arrow, ((_w-_x)>>1)-4, (_rowHeight>>1)+y-4, kScrollColor, 8);
414
for(int i = _firstEntry, current = 0; i < _firstEntry + _numEntries; ++i, ++current)
416
bool hilite = offset == current;
417
if(hilite) s.fillRect(x, y, w, _rowHeight, kTextColorHi);
418
s.drawString(_font, _entries[i].first, x + 1, y + 2, w,
419
!hilite ? kTextColor : kWidColor);
423
// Show bottom scroll area
426
s.hLine(x, y, w+2, kShadowColor);
427
s.drawBitmap(down_arrow, ((_w-_x)>>1)-4, (_rowHeight>>1)+y-4, kScrollColor, 8);
430
s.addDirtyRect(_x, _y, _w, _h);
434
// Commit surface changes to screen