ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

gui/widgets/list.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "common/system.h"
00024 #include "common/frac.h"
00025 #include "common/tokenizer.h"
00026 
00027 #include "gui/widgets/list.h"
00028 #include "gui/widgets/scrollbar.h"
00029 #include "gui/dialog.h"
00030 #include "gui/gui-manager.h"
00031 
00032 #include "gui/ThemeEval.h"
00033 
00034 namespace GUI {
00035 
00036 ListWidget::ListWidget(Dialog *boss, const String &name, const char *tooltip, uint32 cmd)
00037     : EditableWidget(boss, name, tooltip), _cmd(cmd) {
00038 
00039     _scrollBar = NULL;
00040 
00041     // This ensures that _entriesPerPage is properly initialized.
00042     reflowLayout();
00043 
00044     _scrollBar = new ScrollBarWidget(this, _w - _scrollBarWidth + 1, 0, _scrollBarWidth, _h);
00045     _scrollBar->setTarget(this);
00046 
00047     setFlags(WIDGET_ENABLED | WIDGET_CLEARBG | WIDGET_RETAIN_FOCUS | WIDGET_WANT_TICKLE);
00048     _type = kListWidget;
00049     _editMode = false;
00050     _numberingMode = kListNumberingOne;
00051     _currentPos = 0;
00052     _selectedItem = -1;
00053     _currentKeyDown = 0;
00054 
00055     _quickSelectTime = 0;
00056 
00057     // The item is selected, thus _bgcolor is used to draw the caret and _textcolorhi to erase it
00058     _caretInverse = true;
00059 
00060     // FIXME: This flag should come from widget definition
00061     _editable = true;
00062 
00063     _quickSelect = true;
00064     _editColor = ThemeEngine::kFontColorNormal;
00065 }
00066 
00067 ListWidget::ListWidget(Dialog *boss, int x, int y, int w, int h, const char *tooltip, uint32 cmd)
00068     : EditableWidget(boss, x, y, w, h, tooltip), _cmd(cmd) {
00069 
00070     _scrollBar = NULL;
00071 
00072     // This ensures that _entriesPerPage is properly initialized.
00073     reflowLayout();
00074 
00075     _scrollBar = new ScrollBarWidget(this, _w - _scrollBarWidth + 1, 0, _scrollBarWidth, _h);
00076     _scrollBar->setTarget(this);
00077 
00078     setFlags(WIDGET_ENABLED | WIDGET_CLEARBG | WIDGET_RETAIN_FOCUS | WIDGET_WANT_TICKLE);
00079     _type = kListWidget;
00080     _editMode = false;
00081     _numberingMode = kListNumberingOne;
00082     _currentPos = 0;
00083     _selectedItem = -1;
00084     _currentKeyDown = 0;
00085 
00086     _quickSelectTime = 0;
00087 
00088     // The item is selected, thus _bgcolor is used to draw the caret and _textcolorhi to erase it
00089     _caretInverse = true;
00090 
00091     // FIXME: This flag should come from widget definition
00092     _editable = true;
00093 
00094     _quickSelect = true;
00095     _editColor = ThemeEngine::kFontColorNormal;
00096 }
00097 
00098 bool ListWidget::containsWidget(Widget *w) const {
00099     if (w == _scrollBar || _scrollBar->containsWidget(w))
00100         return true;
00101     return false;
00102 }
00103 
00104 Widget *ListWidget::findWidget(int x, int y) {
00105     if (x >= _w - _scrollBarWidth)
00106         return _scrollBar;
00107 
00108     return this;
00109 }
00110 
00111 void ListWidget::setSelected(int item) {
00112     // HACK/FIXME: If our _listIndex has a non zero size,
00113     // we will need to look up, whether the user selected
00114     // item is present in that list
00115     if (_listIndex.size()) {
00116         int filteredItem = -1;
00117 
00118         for (uint i = 0; i < _listIndex.size(); ++i) {
00119             if (_listIndex[i] == item) {
00120                 filteredItem = i;
00121                 break;
00122             }
00123         }
00124 
00125         item = filteredItem;
00126     }
00127 
00128     assert(item >= -1 && item < (int)_list.size());
00129 
00130     // We only have to do something if the widget is enabled and the selection actually changes
00131     if (isEnabled() && _selectedItem != item) {
00132         if (_editMode)
00133             abortEditMode();
00134 
00135         _selectedItem = item;
00136 
00137         // Notify clients that the selection changed.
00138         sendCommand(kListSelectionChangedCmd, _selectedItem);
00139 
00140         _currentPos = _selectedItem - _entriesPerPage / 2;
00141         scrollToCurrent();
00142         markAsDirty();
00143     }
00144 }
00145 
00146 ThemeEngine::FontColor ListWidget::getSelectionColor() const {
00147     if (_listColors.empty())
00148         return ThemeEngine::kFontColorNormal;
00149 
00150     if (_filter.empty())
00151         return _listColors[_selectedItem];
00152     else
00153         return _listColors[_listIndex[_selectedItem]];
00154 }
00155 
00156 void ListWidget::setList(const StringArray &list, const ColorList *colors) {
00157     if (_editMode && _caretVisible)
00158         drawCaret(true);
00159 
00160     // Copy everything
00161     _dataList = list;
00162     _list = list;
00163     _filter.clear();
00164     _listIndex.clear();
00165     _listColors.clear();
00166 
00167     if (colors) {
00168         _listColors = *colors;
00169         assert(_listColors.size() == _dataList.size());
00170     }
00171 
00172     int size = list.size();
00173     if (_currentPos >= size)
00174         _currentPos = size - 1;
00175     if (_currentPos < 0)
00176         _currentPos = 0;
00177     _selectedItem = -1;
00178     _editMode = false;
00179     g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
00180     scrollBarRecalc();
00181 }
00182 
00183 void ListWidget::append(const String &s, ThemeEngine::FontColor color) {
00184     if (_dataList.size() == _listColors.size()) {
00185         // If the color list has the size of the data list, we append the color.
00186         _listColors.push_back(color);
00187     } else if (!_listColors.size() && color != ThemeEngine::kFontColorNormal) {
00188         // If it's the first entry to use a non default color, we will fill
00189         // up all other entries of the color list with the default color and
00190         // add the requested color for the new entry.
00191         for (uint i = 0; i < _dataList.size(); ++i)
00192             _listColors.push_back(ThemeEngine::kFontColorNormal);
00193         _listColors.push_back(color);
00194     }
00195 
00196     _dataList.push_back(s);
00197     _list.push_back(s);
00198 
00199     setFilter(_filter, false);
00200 
00201     scrollBarRecalc();
00202 }
00203 
00204 void ListWidget::scrollTo(int item) {
00205     int size = _list.size();
00206     if (item >= size)
00207         item = size - 1;
00208     if (item < 0)
00209         item = 0;
00210 
00211     if (_currentPos != item) {
00212         _currentPos = item;
00213         checkBounds();
00214         scrollBarRecalc();
00215     }
00216 }
00217 
00218 void ListWidget::scrollBarRecalc() {
00219     _scrollBar->_numEntries = _list.size();
00220     _scrollBar->_entriesPerPage = _entriesPerPage;
00221     _scrollBar->_currentPos = _currentPos;
00222     _scrollBar->recalc();
00223 }
00224 
00225 void ListWidget::handleTickle() {
00226     if (_editMode)
00227         EditableWidget::handleTickle();
00228     _scrollBar->handleTickle();
00229 }
00230 
00231 void ListWidget::handleMouseDown(int x, int y, int button, int clickCount) {
00232     if (!isEnabled())
00233         return;
00234 
00235     // First check whether the selection changed
00236     int newSelectedItem = findItem(x, y);
00237 
00238     if (_selectedItem != newSelectedItem && newSelectedItem != -1) {
00239         if (_editMode)
00240             abortEditMode();
00241         _selectedItem = newSelectedItem;
00242         sendCommand(kListSelectionChangedCmd, _selectedItem);
00243     }
00244 
00245     // TODO: Determine where inside the string the user clicked and place the
00246     // caret accordingly.
00247     // See _editScrollOffset and EditTextWidget::handleMouseDown.
00248     markAsDirty();
00249 
00250 }
00251 
00252 void ListWidget::handleMouseUp(int x, int y, int button, int clickCount) {
00253     // If this was a double click and the mouse is still over
00254     // the selected item, send the double click command
00255     if (clickCount == 2 && (_selectedItem == findItem(x, y)) &&
00256         _selectedItem >= 0) {
00257         sendCommand(kListItemDoubleClickedCmd, _selectedItem);
00258     }
00259 }
00260 
00261 void ListWidget::handleMouseWheel(int x, int y, int direction) {
00262     _scrollBar->handleMouseWheel(x, y, direction);
00263 }
00264 
00265 
00266 int ListWidget::findItem(int x, int y) const {
00267     if (y < _topPadding) return -1;
00268     int item = (y - _topPadding) / kLineHeight + _currentPos;
00269     if (item >= _currentPos && item < _currentPos + _entriesPerPage &&
00270         item < (int)_list.size())
00271         return item;
00272     else
00273         return -1;
00274 }
00275 
00276 static int matchingCharsIgnoringCase(const char *x, const char *y, bool &stop) {
00277     int match = 0;
00278     while (*x && *y && tolower(*x) == tolower(*y)) {
00279         ++x;
00280         ++y;
00281         ++match;
00282     }
00283     stop = !*y || (*x && (tolower(*x) >= tolower(*y)));
00284     return match;
00285 }
00286 
00287 bool ListWidget::handleKeyDown(Common::KeyState state) {
00288     bool handled = true;
00289     bool dirty = false;
00290     int oldSelectedItem = _selectedItem;
00291 
00292     if (!_editMode && state.keycode <= Common::KEYCODE_z && Common::isPrint(state.ascii)) {
00293         // Quick selection mode: Go to first list item starting with this key
00294         // (or a substring accumulated from the last couple key presses).
00295         // Only works in a useful fashion if the list entries are sorted.
00296         uint32 time = g_system->getMillis();
00297         if (_quickSelectTime < time) {
00298             _quickSelectStr = (char)state.ascii;
00299         } else {
00300             _quickSelectStr += (char)state.ascii;
00301         }
00302         _quickSelectTime = time + 300;  // TODO: Turn this into a proper constant (kQuickSelectDelay ?)
00303 
00304         if (_quickSelect) {
00305             // FIXME: This is bad slow code (it scans the list linearly each time a
00306             // key is pressed); it could be much faster. Only of importance if we have
00307             // quite big lists to deal with -- so for now we can live with this lazy
00308             // implementation :-)
00309             int newSelectedItem = 0;
00310             int bestMatch = 0;
00311             bool stop;
00312             for (StringArray::const_iterator i = _list.begin(); i != _list.end(); ++i) {
00313                 const int match = matchingCharsIgnoringCase(i->c_str(), _quickSelectStr.c_str(), stop);
00314                 if (match > bestMatch || stop) {
00315                     _selectedItem = newSelectedItem;
00316                     bestMatch = match;
00317                     if (stop)
00318                         break;
00319                 }
00320                 newSelectedItem++;
00321             }
00322 
00323             scrollToCurrent();
00324         } else {
00325             sendCommand(_cmd, 0);
00326         }
00327     } else if (_editMode) {
00328         // Class EditableWidget handles all text editing related key presses for us
00329         handled = EditableWidget::handleKeyDown(state);
00330     } else {
00331         // not editmode
00332 
00333         switch (state.keycode) {
00334         case Common::KEYCODE_RETURN:
00335         case Common::KEYCODE_KP_ENTER:
00336             if (_selectedItem >= 0) {
00337                 // override continuous enter keydown
00338                 if (_editable && (_currentKeyDown != Common::KEYCODE_RETURN && _currentKeyDown != Common::KEYCODE_KP_ENTER)) {
00339                     dirty = true;
00340                     startEditMode();
00341                 } else
00342                     sendCommand(kListItemActivatedCmd, _selectedItem);
00343             }
00344             break;
00345 
00346         // Keypad & special keys
00347         //   - if num lock is set, we do not handle the keypress
00348         //   - if num lock is not set, we either fall down to the special key case
00349         //     or ignore the key press for 0, 4, 5 and 6
00350 
00351         case Common::KEYCODE_KP_PERIOD:
00352             if (state.flags & Common::KBD_NUM) {
00353                 handled = false;
00354                 break;
00355             }
00356             // fall through
00357         case Common::KEYCODE_BACKSPACE:
00358         case Common::KEYCODE_DELETE:
00359             if (_selectedItem >= 0) {
00360                 if (_editable) {
00361                     // Ignore delete and backspace when the list item is editable
00362                 } else {
00363                     sendCommand(kListItemRemovalRequestCmd, _selectedItem);
00364                 }
00365             }
00366             break;
00367 
00368         case Common::KEYCODE_KP1:
00369             if (state.flags & Common::KBD_NUM) {
00370                 handled = false;
00371                 break;
00372             }
00373             // fall through
00374         case Common::KEYCODE_END:
00375             _selectedItem = _list.size() - 1;
00376             break;
00377 
00378 
00379         case Common::KEYCODE_KP2:
00380             if (state.flags & Common::KBD_NUM) {
00381                 handled = false;
00382                 break;
00383             }
00384             // fall through
00385         case Common::KEYCODE_DOWN:
00386             if (_selectedItem < (int)_list.size() - 1)
00387                 _selectedItem++;
00388             break;
00389 
00390         case Common::KEYCODE_KP3:
00391             if (state.flags & Common::KBD_NUM) {
00392                 handled = false;
00393                 break;
00394             }
00395             // fall through
00396         case Common::KEYCODE_PAGEDOWN:
00397             _selectedItem += _entriesPerPage - 1;
00398             if (_selectedItem >= (int)_list.size() )
00399                 _selectedItem = _list.size() - 1;
00400             break;
00401 
00402         case Common::KEYCODE_KP7:
00403             if (state.flags & Common::KBD_NUM) {
00404                 handled = false;
00405                 break;
00406             }
00407             // fall through
00408         case Common::KEYCODE_HOME:
00409             _selectedItem = 0;
00410             break;
00411 
00412         case Common::KEYCODE_KP8:
00413             if (state.flags & Common::KBD_NUM) {
00414                 handled = false;
00415                 break;
00416             }
00417             // fall through
00418         case Common::KEYCODE_UP:
00419             if (_selectedItem > 0)
00420                 _selectedItem--;
00421             break;
00422 
00423         case Common::KEYCODE_KP9:
00424             if (state.flags & Common::KBD_NUM) {
00425                 handled = false;
00426                 break;
00427             }
00428             // fall through
00429         case Common::KEYCODE_PAGEUP:
00430             _selectedItem -= _entriesPerPage - 1;
00431             if (_selectedItem < 0)
00432                 _selectedItem = 0;
00433             break;
00434 
00435         default:
00436             handled = false;
00437         }
00438 
00439         scrollToCurrent();
00440     }
00441 
00442     if (dirty || _selectedItem != oldSelectedItem)
00443         markAsDirty();
00444 
00445     if (_selectedItem != oldSelectedItem) {
00446         sendCommand(kListSelectionChangedCmd, _selectedItem);
00447         // also draw scrollbar
00448         _scrollBar->markAsDirty();
00449     }
00450 
00451     return handled;
00452 }
00453 
00454 bool ListWidget::handleKeyUp(Common::KeyState state) {
00455     if (state.keycode == _currentKeyDown)
00456         _currentKeyDown = 0;
00457     return true;
00458 }
00459 
00460 void ListWidget::receivedFocusWidget() {
00461     _inversion = ThemeEngine::kTextInversionFocus;
00462 
00463     // Redraw the widget so the selection color will change
00464     markAsDirty();
00465 }
00466 
00467 void ListWidget::lostFocusWidget() {
00468     _inversion = ThemeEngine::kTextInversion;
00469 
00470     // If we lose focus, we simply forget the user changes
00471     _editMode = false;
00472     g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
00473     drawCaret(true);
00474     markAsDirty();
00475 }
00476 
00477 void ListWidget::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
00478     switch (cmd) {
00479     case kSetPositionCmd:
00480         if (_currentPos != (int)data) {
00481             _currentPos = data;
00482             checkBounds();
00483             markAsDirty();
00484 
00485             // Scrollbar actions cause list focus (which triggers a redraw)
00486             // NOTE: ListWidget's boss is always GUI::Dialog
00487             ((GUI::Dialog *)_boss)->setFocusWidget(this);
00488         }
00489         break;
00490     }
00491 }
00492 
00493 void ListWidget::drawWidget() {
00494     int i, pos, len = _list.size();
00495     Common::String buffer;
00496 
00497     // Draw a thin frame around the list.
00498     g_gui.theme()->drawWidgetBackground(Common::Rect(_x, _y, _x + _w, _y + _h), 0,
00499                                         ThemeEngine::kWidgetBackgroundBorder);
00500 
00501     // Draw the list items
00502     for (i = 0, pos = _currentPos; i < _entriesPerPage && pos < len; i++, pos++) {
00503         const int y = _y + _topPadding + kLineHeight * i;
00504         const int fontHeight = kLineHeight;
00505         ThemeEngine::TextInversionState inverted = ThemeEngine::kTextInversionNone;
00506 
00507         // Draw the selected item inverted, on a highlighted background.
00508         if (_selectedItem == pos)
00509             inverted = _inversion;
00510 
00511         Common::Rect r(getEditRect());
00512         int pad = _leftPadding;
00513 
00514         // If in numbering mode, we first print a number prefix
00515         if (_numberingMode != kListNumberingOff) {
00516             buffer = Common::String::format("%2d. ", (pos + _numberingMode));
00517             g_gui.theme()->drawText(Common::Rect(_x + _hlLeftPadding, y, _x + r.left + _leftPadding, y + fontHeight - 2),
00518                                     buffer, _state, Graphics::kTextAlignLeft, inverted, _leftPadding, true);
00519             pad = 0;
00520         }
00521 
00522         ThemeEngine::FontColor color = ThemeEngine::kFontColorNormal;
00523 
00524         if (!_listColors.empty()) {
00525             if (_filter.empty() || _selectedItem == -1)
00526                 color = _listColors[pos];
00527             else
00528                 color = _listColors[_listIndex[pos]];
00529         }
00530 
00531         if (_selectedItem == pos && _editMode) {
00532             buffer = _editString;
00533             color = _editColor;
00534             adjustOffset();
00535             g_gui.theme()->drawText(Common::Rect(_x + r.left, y, _x + r.right, y + fontHeight - 2), buffer, _state,
00536                                     Graphics::kTextAlignLeft, inverted, pad, true, ThemeEngine::kFontStyleBold, color);
00537         } else {
00538             buffer = _list[pos];
00539             g_gui.theme()->drawText(Common::Rect(_x + r.left, y, _x + r.right, y + fontHeight - 2), buffer, _state,
00540                                     Graphics::kTextAlignLeft, inverted, pad, true, ThemeEngine::kFontStyleBold, color);
00541         }
00542     }
00543 }
00544 
00545 Common::Rect ListWidget::getEditRect() const {
00546     const int scrollbarW = (_scrollBar && _scrollBar->isVisible()) ? _scrollBarWidth : 0;
00547     Common::Rect r(_hlLeftPadding, 0, _w - _hlRightPadding - scrollbarW, kLineHeight - 2);
00548     const int offset = (_selectedItem - _currentPos) * kLineHeight + _topPadding;
00549     r.top += offset;
00550     r.bottom += offset;
00551 
00552     if (_numberingMode != kListNumberingOff) {
00553         // FIXME: Assumes that all digits have the same width.
00554         Common::String temp = Common::String::format("%2d. ", (_list.size() - 1 + _numberingMode));
00555         r.left += g_gui.getStringWidth(temp) + _leftPadding;
00556     }
00557 
00558     return r;
00559 }
00560 
00561 void ListWidget::checkBounds() {
00562     if (_currentPos < 0 || _entriesPerPage > (int)_list.size())
00563         _currentPos = 0;
00564     else if (_currentPos + _entriesPerPage > (int)_list.size())
00565         _currentPos = _list.size() - _entriesPerPage;
00566 }
00567 
00568 void ListWidget::scrollToCurrent() {
00569     // Only do something if the current item is not in our view port
00570     if (_selectedItem < _currentPos) {
00571         // it's above our view
00572         _currentPos = _selectedItem;
00573     } else if (_selectedItem >= _currentPos + _entriesPerPage ) {
00574         // it's below our view
00575         _currentPos = _selectedItem - _entriesPerPage + 1;
00576     }
00577 
00578     checkBounds();
00579     _scrollBar->_currentPos = _currentPos;
00580     _scrollBar->recalc();
00581 }
00582 
00583 void ListWidget::scrollToEnd() {
00584     if (_currentPos + _entriesPerPage < (int)_list.size()) {
00585         _currentPos = _list.size() - _entriesPerPage;
00586     } else {
00587         return;
00588     }
00589 
00590     _scrollBar->_currentPos = _currentPos;
00591     _scrollBar->recalc();
00592     _scrollBar->markAsDirty();
00593 }
00594 
00595 void ListWidget::startEditMode() {
00596     if (_editable && !_editMode && _selectedItem >= 0) {
00597         _editMode = true;
00598         setEditString(_list[_selectedItem]);
00599         _caretPos = _editString.size(); // Force caret to the *end* of the selection.
00600         if (_listColors.empty()) {
00601             _editColor = ThemeEngine::kFontColorNormal;
00602         } else {
00603             if (_filter.empty())
00604                 _editColor = _listColors[_selectedItem];
00605             else
00606                 _editColor = _listColors[_listIndex[_selectedItem]];
00607         }
00608         markAsDirty();
00609         g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
00610     }
00611 }
00612 
00613 void ListWidget::endEditMode() {
00614     if (!_editMode)
00615         return;
00616     // send a message that editing finished with a return/enter key press
00617     _editMode = false;
00618     _list[_selectedItem] = _editString;
00619     g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
00620     sendCommand(kListItemActivatedCmd, _selectedItem);
00621 }
00622 
00623 void ListWidget::abortEditMode() {
00624     // undo any changes made
00625     assert(_selectedItem >= 0);
00626     _editMode = false;
00627     //drawCaret(true);
00628     //markAsDirty();
00629     g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
00630 }
00631 
00632 void ListWidget::reflowLayout() {
00633     Widget::reflowLayout();
00634 
00635     _leftPadding = g_gui.xmlEval()->getVar("Globals.ListWidget.Padding.Left", 0);
00636     _rightPadding = g_gui.xmlEval()->getVar("Globals.ListWidget.Padding.Right", 0);
00637     _topPadding = g_gui.xmlEval()->getVar("Globals.ListWidget.Padding.Top", 0);
00638     _bottomPadding = g_gui.xmlEval()->getVar("Globals.ListWidget.Padding.Bottom", 0);
00639     _hlLeftPadding = g_gui.xmlEval()->getVar("Globals.ListWidget.hlLeftPadding", 0);
00640     _hlRightPadding = g_gui.xmlEval()->getVar("Globals.ListWidget.hlRightPadding", 0);
00641 
00642     _scrollBarWidth = g_gui.xmlEval()->getVar("Globals.Scrollbar.Width", 0);
00643 
00644     // HACK: Once we take padding into account, there are times where
00645     // integer rounding leaves a big chunk of white space in the bottom
00646     // of the list.
00647     // We do a rough rounding on the decimal places of Entries Per Page,
00648     // to add another entry even if it goes a tad over the padding.
00649     frac_t entriesPerPage = intToFrac(_h - _topPadding - _bottomPadding) / kLineHeight;
00650 
00651     // Our threshold before we add another entry is 0.9375 (0xF000 with FRAC_BITS being 16).
00652     const frac_t threshold = intToFrac(15) / 16;
00653 
00654     if ((frac_t)(entriesPerPage & FRAC_LO_MASK) >= threshold)
00655         entriesPerPage += FRAC_ONE;
00656 
00657     _entriesPerPage = fracToInt(entriesPerPage);
00658     assert(_entriesPerPage > 0);
00659 
00660     if (_scrollBar) {
00661         _scrollBar->resize(_w - _scrollBarWidth + 1, 0, _scrollBarWidth, _h);
00662         scrollBarRecalc();
00663         scrollToCurrent();
00664     }
00665 }
00666 
00667 void ListWidget::setFilter(const String &filter, bool redraw) {
00668     // FIXME: This method does not deal correctly with edit mode!
00669     // Until we fix that, let's make sure it isn't called while editing takes place
00670     assert(!_editMode);
00671 
00672     String filt = filter;
00673     filt.toLowercase();
00674 
00675     if (_filter == filt) // Filter was not changed
00676         return;
00677 
00678     _filter = filt;
00679 
00680     if (_filter.empty()) {
00681         // No filter -> display everything
00682         _list = _dataList;
00683         _listIndex.clear();
00684     } else {
00685         // Restrict the list to everything which contains all words in _filter
00686         // as substrings, ignoring case.
00687 
00688         Common::StringTokenizer tok(_filter);
00689         String tmp;
00690         int n = 0;
00691 
00692         _list.clear();
00693         _listIndex.clear();
00694 
00695         for (StringArray::iterator i = _dataList.begin(); i != _dataList.end(); ++i, ++n) {
00696             tmp = *i;
00697             tmp.toLowercase();
00698             bool matches = true;
00699             tok.reset();
00700             while (!tok.empty()) {
00701                 if (!tmp.contains(tok.nextToken())) {
00702                     matches = false;
00703                     break;
00704                 }
00705             }
00706 
00707             if (matches) {
00708                 _list.push_back(*i);
00709                 _listIndex.push_back(n);
00710             }
00711         }
00712     }
00713 
00714     _currentPos = 0;
00715     _selectedItem = -1;
00716 
00717     if (redraw) {
00718         scrollBarRecalc();
00719         // Redraw the whole dialog. This is annoying, as this might be rather
00720         // expensive when really only the list widget and its scroll bar area
00721         // to be redrawn. However, since the scrollbar might change its
00722         // visibility status, and the list its width, we cannot just redraw
00723         // the two.
00724         // TODO: A more efficient (and elegant?) way to handle this would be to
00725         // introduce a kind of "BoxWidget" or "GroupWidget" which defines a
00726         // rectangular region and subwidgets can be placed within it.
00727         // Such a widget could also (optionally) draw a border (or even different
00728         // kinds of borders) around the objects it groups; and also a 'title'
00729         // (I am borrowing these "ideas" from the NSBox class in Cocoa :).
00730         g_gui.scheduleTopDialogRedraw();
00731     }
00732 }
00733 
00734 } // End of namespace GUI


Generated on Sat May 25 2019 05:00:48 for ResidualVM by doxygen 1.7.1
curved edge   curved edge