aboutsummaryrefslogtreecommitdiff
path: root/gui/ListWidget.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gui/ListWidget.cpp')
-rw-r--r--gui/ListWidget.cpp148
1 files changed, 123 insertions, 25 deletions
diff --git a/gui/ListWidget.cpp b/gui/ListWidget.cpp
index 06cbcdcbd9..07d22973ac 100644
--- a/gui/ListWidget.cpp
+++ b/gui/ListWidget.cpp
@@ -33,8 +33,8 @@
namespace GUI {
-ListWidget::ListWidget(GuiObject *boss, const String &name)
- : EditableWidget(boss, name), CommandSender(boss) {
+ListWidget::ListWidget(GuiObject *boss, const String &name, uint32 cmd)
+ : EditableWidget(boss, name), _cmd(cmd) {
_scrollBar = NULL;
_textWidth = NULL;
@@ -60,10 +60,12 @@ ListWidget::ListWidget(GuiObject *boss, const String &name)
// FIXME: This flag should come from widget definition
_editable = true;
+
+ _quickSelect = true;
}
-ListWidget::ListWidget(GuiObject *boss, int x, int y, int w, int h)
- : EditableWidget(boss, x, y, w, h), CommandSender(boss) {
+ListWidget::ListWidget(GuiObject *boss, int x, int y, int w, int h, uint32 cmd)
+ : EditableWidget(boss, x, y, w, h), _cmd(cmd) {
_scrollBar = NULL;
_textWidth = NULL;
@@ -105,11 +107,14 @@ Widget *ListWidget::findWidget(int x, int y) {
void ListWidget::setSelected(int item) {
assert(item >= -1 && item < (int)_list.size());
+ // We only have to do something if the widget is enabled and the selection actually changes
if (isEnabled() && _selectedItem != item) {
if (_editMode)
abortEditMode();
_selectedItem = item;
+
+ // Notify clients that the selection changed.
sendCommand(kListSelectionChangedCmd, _selectedItem);
_currentPos = _selectedItem - _entriesPerPage / 2;
@@ -121,8 +126,13 @@ void ListWidget::setSelected(int item) {
void ListWidget::setList(const StringList &list) {
if (_editMode && _caretVisible)
drawCaret(true);
- int size = list.size();
+
+ // Copy everything
+ _dataList = list;
_list = list;
+ _filter.clear();
+
+ int size = list.size();
if (_currentPos >= size)
_currentPos = size - 1;
if (_currentPos < 0)
@@ -133,6 +143,15 @@ void ListWidget::setList(const StringList &list) {
scrollBarRecalc();
}
+void ListWidget::append(const String &s) {
+ _dataList.push_back(s);
+ _list.push_back(s);
+
+ setFilter(_filter, false);
+
+ scrollBarRecalc();
+}
+
void ListWidget::scrollTo(int item) {
int size = _list.size();
if (item >= size)
@@ -223,8 +242,6 @@ bool ListWidget::handleKeyDown(Common::KeyState state) {
// Quick selection mode: Go to first list item starting with this key
// (or a substring accumulated from the last couple key presses).
// Only works in a useful fashion if the list entries are sorted.
- // TODO: Maybe this should be off by default, and instead we add a
- // method "enableQuickSelect()" or so ?
uint32 time = getMillis();
if (_quickSelectTime < time) {
_quickSelectStr = (char)state.ascii;
@@ -233,26 +250,29 @@ bool ListWidget::handleKeyDown(Common::KeyState state) {
}
_quickSelectTime = time + 300; // TODO: Turn this into a proper constant (kQuickSelectDelay ?)
-
- // FIXME: This is bad slow code (it scans the list linearly each time a
- // key is pressed); it could be much faster. Only of importance if we have
- // quite big lists to deal with -- so for now we can live with this lazy
- // implementation :-)
- int newSelectedItem = 0;
- int bestMatch = 0;
- bool stop;
- for (StringList::const_iterator i = _list.begin(); i != _list.end(); ++i) {
- const int match = matchingCharsIgnoringCase(i->c_str(), _quickSelectStr.c_str(), stop);
- if (match > bestMatch || stop) {
- _selectedItem = newSelectedItem;
- bestMatch = match;
- if (stop)
- break;
+ if (_quickSelect) {
+ // FIXME: This is bad slow code (it scans the list linearly each time a
+ // key is pressed); it could be much faster. Only of importance if we have
+ // quite big lists to deal with -- so for now we can live with this lazy
+ // implementation :-)
+ int newSelectedItem = 0;
+ int bestMatch = 0;
+ bool stop;
+ for (StringList::const_iterator i = _list.begin(); i != _list.end(); ++i) {
+ const int match = matchingCharsIgnoringCase(i->c_str(), _quickSelectStr.c_str(), stop);
+ if (match > bestMatch || stop) {
+ _selectedItem = newSelectedItem;
+ bestMatch = match;
+ if (stop)
+ break;
+ }
+ newSelectedItem++;
}
- newSelectedItem++;
- }
- scrollToCurrent();
+ scrollToCurrent();
+ } else {
+ sendCommand(_cmd, 0);
+ }
} else if (_editMode) {
// Class EditableWidget handles all text editing related key presses for us
handled = EditableWidget::handleKeyDown(state);
@@ -445,6 +465,18 @@ void ListWidget::scrollToCurrent() {
_scrollBar->recalc();
}
+void ListWidget::scrollToEnd() {
+ if (_currentPos + _entriesPerPage < (int)_list.size()) {
+ _currentPos = _list.size() - _entriesPerPage;
+ } else {
+ return;
+ }
+
+ _scrollBar->_currentPos = _currentPos;
+ _scrollBar->recalc();
+ _scrollBar->draw();
+}
+
void ListWidget::startEditMode() {
if (_editable && !_editMode && _selectedItem >= 0) {
_editMode = true;
@@ -512,4 +544,70 @@ void ListWidget::reflowLayout() {
}
}
+void ListWidget::setFilter(const String &filter, bool redraw) {
+ // FIXME: This method does not deal correctly with edit mode!
+ // Until we fix that, let's make sure it isn't called while editing takes place
+ assert(!_editMode);
+
+ String filt = filter;
+ filt.toLowercase();
+
+ if (_filter == filt) // Filter was not changed
+ return;
+
+ _filter = filt;
+
+ if (_filter.empty()) {
+ // No filter -> display everything
+ _list = _dataList;
+ } else {
+ // Restrict the list to everything which contains all words in _filter
+ // as substrings, ignoring case.
+
+ Common::StringTokenizer tok(filter);
+ String tmp;
+ int n = 0;
+
+ _list.clear();
+ _listIndex.clear();
+
+ for (StringList::iterator i = _dataList.begin(); i != _dataList.end(); ++i, ++n) {
+ tmp = *i;
+ tmp.toLowercase();
+ bool matches = true;
+ tok.reset();
+ while (!tok.empty()) {
+ if (!tmp.contains(tok.nextToken())) {
+ matches = false;
+ break;
+ }
+ }
+
+ if (matches) {
+ _list.push_back(*i);
+ _listIndex.push_back(n);
+ }
+ }
+ }
+
+ _currentPos = 0;
+ _selectedItem = -1;
+
+ if (redraw) {
+ scrollBarRecalc();
+ // Redraw the whole dialog. This is annoying, as this might be rather
+ // expensive when really only the list widget and its scroll bar area
+ // to be redrawn. However, since the scrollbar might change its
+ // visibility status, and the list its width, we cannot just redraw
+ // the two.
+ // TODO: A more efficient (and elegant?) way to handle this would be to
+ // introduce a kind of "BoxWidget" or "GroupWidget" which defines a
+ // rectangular region and subwidgets can be placed within it.
+ // Such a widget could also (optionally) draw a border (or even different
+ // kinds of borders) around the objects it groups; and also a 'title'
+ // (I am borrowing these "ideas" from the NSBox class in Cocoa :).
+ _boss->draw();
+ }
+}
+
} // End of namespace GUI