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

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


Generated on Sat Sep 26 2020 05:01:14 for ResidualVM by doxygen 1.7.1
curved edge   curved edge