MyGUI  3.2.0
MyGUI_ListBox.cpp
Go to the documentation of this file.
00001 
00006 /*
00007     This file is part of MyGUI.
00008 
00009     MyGUI is free software: you can redistribute it and/or modify
00010     it under the terms of the GNU Lesser General Public License as published by
00011     the Free Software Foundation, either version 3 of the License, or
00012     (at your option) any later version.
00013 
00014     MyGUI is distributed in the hope that it will be useful,
00015     but WITHOUT ANY WARRANTY; without even the implied warranty of
00016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017     GNU Lesser General Public License for more details.
00018 
00019     You should have received a copy of the GNU Lesser General Public License
00020     along with MyGUI.  If not, see <http://www.gnu.org/licenses/>.
00021 */
00022 #include "MyGUI_Precompiled.h"
00023 #include "MyGUI_ListBox.h"
00024 #include "MyGUI_Button.h"
00025 #include "MyGUI_ScrollBar.h"
00026 #include "MyGUI_ResourceSkin.h"
00027 #include "MyGUI_InputManager.h"
00028 #include "MyGUI_WidgetManager.h"
00029 
00030 namespace MyGUI
00031 {
00032 
00033     ListBox::ListBox() :
00034         mWidgetScroll(nullptr),
00035         mHeightLine(1),
00036         mTopIndex(0),
00037         mOffsetTop(0),
00038         mRangeIndex(-1),
00039         mLastRedrawLine(0),
00040         mIndexSelect(ITEM_NONE),
00041         mLineActive(ITEM_NONE),
00042         mNeedVisibleScroll(true),
00043         mClient(nullptr)
00044     {
00045     }
00046 
00047     void ListBox::initialiseOverride()
00048     {
00049         Base::initialiseOverride();
00050 
00051         // FIXME нам нужен фокус клавы
00052         setNeedKeyFocus(true);
00053 
00054         // парсим свойства
00055         if (isUserString("SkinLine"))
00056             mSkinLine = getUserString("SkinLine");
00057 
00058         if (isUserString("HeightLine"))
00059             mHeightLine = utility::parseInt(getUserString("HeightLine"));
00060 
00061         if (mHeightLine < 1)
00062             mHeightLine = 1;
00063 
00064         assignWidget(mClient, "Client");
00065         if (mClient != nullptr)
00066         {
00067             mClient->eventMouseButtonPressed += newDelegate(this, &ListBox::notifyMousePressed);
00068             setWidgetClient(mClient);
00069         }
00070 
00071         assignWidget(mWidgetScroll, "VScroll");
00072         if (mWidgetScroll != nullptr)
00073         {
00074             mWidgetScroll->eventScrollChangePosition += newDelegate(this, &ListBox::notifyScrollChangePosition);
00075             mWidgetScroll->eventMouseButtonPressed += newDelegate(this, &ListBox::notifyMousePressed);
00076             mWidgetScroll->setScrollPage((size_t)mHeightLine);
00077             mWidgetScroll->setScrollViewPage((size_t)mHeightLine);
00078         }
00079 
00080         updateScroll();
00081         updateLine();
00082     }
00083 
00084     void ListBox::shutdownOverride()
00085     {
00086         mWidgetScroll = nullptr;
00087         mClient = nullptr;
00088 
00089         Base::shutdownOverride();
00090     }
00091 
00092     void ListBox::onMouseWheel(int _rel)
00093     {
00094         notifyMouseWheel(nullptr, _rel);
00095 
00096         Base::onMouseWheel(_rel);
00097     }
00098 
00099     void ListBox::onKeyButtonPressed(KeyCode _key, Char _char)
00100     {
00101         if (getItemCount() == 0)
00102         {
00103             Base::onKeyButtonPressed(_key, _char);
00104             return;
00105         }
00106 
00107         // очень секретный метод, запатентованный механизм движения курсора
00108         size_t sel = mIndexSelect;
00109 
00110         if (_key == KeyCode::ArrowUp)
00111         {
00112             if (sel != 0)
00113             {
00114                 if (sel == ITEM_NONE)
00115                     sel = 0;
00116                 else
00117                     sel --;
00118             }
00119         }
00120         else if (_key == KeyCode::ArrowDown)
00121         {
00122             if (sel == ITEM_NONE)
00123                 sel = 0;
00124             else
00125                 sel ++;
00126 
00127             if (sel >= getItemCount())
00128             {
00129                 // старое значение
00130                 sel = mIndexSelect;
00131             }
00132         }
00133         else if (_key == KeyCode::Home)
00134         {
00135             if (sel != 0)
00136                 sel = 0;
00137         }
00138         else if (_key == KeyCode::End)
00139         {
00140             if (sel != (getItemCount() - 1))
00141             {
00142                 sel = getItemCount() - 1;
00143             }
00144         }
00145         else if (_key == KeyCode::PageUp)
00146         {
00147             if (sel != 0)
00148             {
00149                 if (sel == ITEM_NONE)
00150                 {
00151                     sel = 0;
00152                 }
00153                 else
00154                 {
00155                     size_t page = _getClientWidget()->getHeight() / mHeightLine;
00156                     if (sel <= page)
00157                         sel = 0;
00158                     else
00159                         sel -= page;
00160                 }
00161             }
00162         }
00163         else if (_key == KeyCode::PageDown)
00164         {
00165             if (sel != (getItemCount() - 1))
00166             {
00167                 if (sel == ITEM_NONE)
00168                 {
00169                     sel = 0;
00170                 }
00171                 else
00172                 {
00173                     sel += _getClientWidget()->getHeight() / mHeightLine;
00174                     if (sel >= getItemCount())
00175                         sel = getItemCount() - 1;
00176                 }
00177             }
00178         }
00179         else if ((_key == KeyCode::Return) || (_key == KeyCode::NumpadEnter))
00180         {
00181             if (sel != ITEM_NONE)
00182             {
00183                 //FIXME нас могут удалить
00184                 eventListSelectAccept(this, sel);
00185 
00186                 Base::onKeyButtonPressed(_key, _char);
00187                 // выходим, так как изменили колличество строк
00188                 return;
00189             }
00190         }
00191 
00192         if (sel != mIndexSelect)
00193         {
00194             _resetContainer(true);
00195 
00196             if (!isItemVisibleAt(sel))
00197             {
00198                 beginToItemAt(sel);
00199                 if (mWidgetScroll != nullptr)
00200                     _sendEventChangeScroll(mWidgetScroll->getScrollPosition());
00201             }
00202             setIndexSelected(sel);
00203 
00204             // изменилась позиция
00205             // FIXME нас могут удалить
00206             eventListChangePosition(this, mIndexSelect);
00207         }
00208 
00209         Base::onKeyButtonPressed(_key, _char);
00210     }
00211 
00212     void ListBox::notifyMouseWheel(Widget* _sender, int _rel)
00213     {
00214         if (mRangeIndex <= 0)
00215             return;
00216 
00217         if (mWidgetScroll == nullptr)
00218             return;
00219 
00220         int offset = (int)mWidgetScroll->getScrollPosition();
00221         if (_rel < 0)
00222             offset += mHeightLine;
00223         else
00224             offset -= mHeightLine;
00225 
00226         if (offset >= mRangeIndex)
00227             offset = mRangeIndex;
00228         else if (offset < 0)
00229             offset = 0;
00230 
00231         if ((int)mWidgetScroll->getScrollPosition() == offset)
00232             return;
00233 
00234         mWidgetScroll->setScrollPosition(offset);
00235         _setScrollView(offset);
00236         _sendEventChangeScroll(offset);
00237 
00238         _resetContainer(true);
00239     }
00240 
00241     void ListBox::notifyScrollChangePosition(ScrollBar* _sender, size_t _position)
00242     {
00243         _setScrollView(_position);
00244         _sendEventChangeScroll(_position);
00245     }
00246 
00247     void ListBox::notifyMousePressed(Widget* _sender, int _left, int _top, MouseButton _id)
00248     {
00249         if (MouseButton::Left != _id)
00250             return;
00251 
00252         if (_sender == mWidgetScroll)
00253             return;
00254 
00255         // если выделен клиент, то сбрасываем
00256         if (_sender == _getClientWidget())
00257         {
00258             if (mIndexSelect != ITEM_NONE)
00259             {
00260                 _selectIndex(mIndexSelect, false);
00261                 mIndexSelect = ITEM_NONE;
00262                 eventListChangePosition(this, mIndexSelect);
00263             }
00264             eventListMouseItemActivate(this, mIndexSelect);
00265 
00266             // если не клиент, то просчитывам
00267         }
00268         // ячейка может быть скрыта
00269         else if (_sender->getVisible())
00270         {
00271 
00272 #if MYGUI_DEBUG_MODE == 1
00273             _checkMapping("ListBox::notifyMousePressed");
00274             MYGUI_ASSERT_RANGE(*_sender->_getInternalData<size_t>(), mWidgetLines.size(), "ListBox::notifyMousePressed");
00275             MYGUI_ASSERT_RANGE(*_sender->_getInternalData<size_t>() + mTopIndex, mItemsInfo.size(), "ListBox::notifyMousePressed");
00276 #endif
00277 
00278             size_t index = *_sender->_getInternalData<size_t>() + mTopIndex;
00279 
00280             if (mIndexSelect != index)
00281             {
00282                 _selectIndex(mIndexSelect, false);
00283                 _selectIndex(index, true);
00284                 mIndexSelect = index;
00285                 eventListChangePosition(this, mIndexSelect);
00286             }
00287             eventListMouseItemActivate(this, mIndexSelect);
00288         }
00289 
00290         _resetContainer(true);
00291     }
00292 
00293     void ListBox::notifyMouseDoubleClick(Widget* _sender)
00294     {
00295         if (mIndexSelect != ITEM_NONE)
00296             eventListSelectAccept(this, mIndexSelect);
00297     }
00298 
00299     void ListBox::setPosition(const IntPoint& _point)
00300     {
00301         Base::setPosition(_point);
00302     }
00303 
00304     void ListBox::setSize(const IntSize& _size)
00305     {
00306         Base::setSize(_size);
00307 
00308         updateScroll();
00309         updateLine();
00310     }
00311 
00312     void ListBox::setCoord(const IntCoord& _coord)
00313     {
00314         Base::setCoord(_coord);
00315 
00316         updateScroll();
00317         updateLine();
00318     }
00319 
00320     void ListBox::updateScroll()
00321     {
00322         mRangeIndex = (mHeightLine * (int)mItemsInfo.size()) - _getClientWidget()->getHeight();
00323 
00324         if (mWidgetScroll == nullptr)
00325             return;
00326 
00327         if ((!mNeedVisibleScroll) || (mRangeIndex < 1) || (mWidgetScroll->getLeft() <= _getClientWidget()->getLeft()))
00328         {
00329             if (mWidgetScroll->getVisible())
00330             {
00331                 mWidgetScroll->setVisible(false);
00332                 // увеличиваем клиентскую зону на ширину скрола
00333                 if (mClient != nullptr)
00334                     mClient->setSize(mClient->getWidth() + mWidgetScroll->getWidth(), mClient->getHeight());
00335             }
00336         }
00337         else if (!mWidgetScroll->getVisible())
00338         {
00339             if (mClient != nullptr)
00340                 mClient->setSize(mClient->getWidth() - mWidgetScroll->getWidth(), mClient->getHeight());
00341             mWidgetScroll->setVisible(true);
00342         }
00343 
00344         mWidgetScroll->setScrollRange(mRangeIndex + 1);
00345         if (!mItemsInfo.empty())
00346             mWidgetScroll->setTrackSize(mWidgetScroll->getLineSize() * _getClientWidget()->getHeight() / mHeightLine / (int)mItemsInfo.size());
00347     }
00348 
00349     void ListBox::updateLine(bool _reset)
00350     {
00351         // сбрасываем
00352         if (_reset)
00353         {
00354             mOldSize.clear();
00355             mLastRedrawLine = 0;
00356             _resetContainer(false);
00357         }
00358 
00359         // позиция скролла
00360         int position = mTopIndex * mHeightLine + mOffsetTop;
00361 
00362         // если высота увеличивалась то добавляем виджеты
00363         if (mOldSize.height < mCoord.height)
00364         {
00365             int height = (int)mWidgetLines.size() * mHeightLine - mOffsetTop;
00366 
00367             // до тех пор, пока не достигнем максимального колличества, и всегда на одну больше
00368             while ( (height <= (_getClientWidget()->getHeight() + mHeightLine)) && (mWidgetLines.size() < mItemsInfo.size()) )
00369             {
00370                 // создаем линию
00371                 Widget* widget = _getClientWidget()->createWidgetT("Button", mSkinLine, 0, height, _getClientWidget()->getWidth(), mHeightLine, Align::Top | Align::HStretch);
00372                 Button* line = widget->castType<Button>();
00373                 // подписываемся на всякие там события
00374                 line->eventMouseButtonPressed += newDelegate(this, &ListBox::notifyMousePressed);
00375                 line->eventMouseButtonDoubleClick += newDelegate(this, &ListBox::notifyMouseDoubleClick);
00376                 line->eventMouseWheel += newDelegate(this, &ListBox::notifyMouseWheel);
00377                 line->eventMouseSetFocus += newDelegate(this, &ListBox::notifyMouseSetFocus);
00378                 line->eventMouseLostFocus += newDelegate(this, &ListBox::notifyMouseLostFocus);
00379                 line->_setContainer(this);
00380                 // присваиваем порядковый номер, для простоты просчета
00381                 line->_setInternalData((size_t)mWidgetLines.size());
00382                 // и сохраняем
00383                 mWidgetLines.push_back(line);
00384                 height += mHeightLine;
00385             }
00386 
00387             // проверяем на возможность не менять положение списка
00388             if (position >= mRangeIndex)
00389             {
00390                 // размер всех помещается в клиент
00391                 if (mRangeIndex <= 0)
00392                 {
00393                     // обнуляем, если надо
00394                     if (position || mOffsetTop || mTopIndex)
00395                     {
00396                         position = 0;
00397                         mTopIndex = 0;
00398                         mOffsetTop = 0;
00399                         mLastRedrawLine = 0; // чтобы все перерисовалось
00400 
00401                         // выравниваем
00402                         int offset = 0;
00403                         for (size_t pos = 0; pos < mWidgetLines.size(); pos++)
00404                         {
00405                             mWidgetLines[pos]->setPosition(0, offset);
00406                             offset += mHeightLine;
00407                         }
00408                     }
00409                 }
00410                 else
00411                 {
00412                     // прижимаем список к нижней границе
00413                     int count = _getClientWidget()->getHeight() / mHeightLine;
00414                     mOffsetTop = mHeightLine - (_getClientWidget()->getHeight() % mHeightLine);
00415 
00416                     if (mOffsetTop == mHeightLine)
00417                     {
00418                         mOffsetTop = 0;
00419                         count --;
00420                     }
00421 
00422                     int top = (int)mItemsInfo.size() - count - 1;
00423 
00424                     // выравниваем
00425                     int offset = 0 - mOffsetTop;
00426                     for (size_t pos = 0; pos < mWidgetLines.size(); pos++)
00427                     {
00428                         mWidgetLines[pos]->setPosition(0, offset);
00429                         offset += mHeightLine;
00430                     }
00431 
00432                     // высчитываем положение, должно быть максимальным
00433                     position = top * mHeightLine + mOffsetTop;
00434 
00435                     // если индех изменился, то перерисовываем линии
00436                     if (top != mTopIndex)
00437                     {
00438                         mTopIndex = top;
00439                         _redrawItemRange();
00440                     }
00441                 }
00442             }
00443 
00444             // увеличился размер, но прокрутки вниз небыло, обновляем линии снизу
00445             _redrawItemRange(mLastRedrawLine);
00446 
00447         } // if (old_cy < mCoord.height)
00448 
00449         // просчитываем положение скролла
00450         if (mWidgetScroll != nullptr)
00451             mWidgetScroll->setScrollPosition(position);
00452 
00453         mOldSize.width = mCoord.width;
00454         mOldSize.height = mCoord.height;
00455 
00456 #if MYGUI_DEBUG_MODE == 1
00457         _checkMapping("ListBox::updateLine");
00458 #endif
00459     }
00460 
00461     void ListBox::_redrawItemRange(size_t _start)
00462     {
00463         // перерисовываем линии, только те, что видны
00464         size_t pos = _start;
00465         for (; pos < mWidgetLines.size(); pos++)
00466         {
00467             // индекс в нашем массиве
00468             size_t index = pos + (size_t)mTopIndex;
00469 
00470             // не будем заходить слишком далеко
00471             if (index >= mItemsInfo.size())
00472             {
00473                 // запоминаем последнюю перерисованную линию
00474                 mLastRedrawLine = pos;
00475                 break;
00476             }
00477             if (mWidgetLines[pos]->getTop() > _getClientWidget()->getHeight())
00478             {
00479                 // запоминаем последнюю перерисованную линию
00480                 mLastRedrawLine = pos;
00481                 break;
00482             }
00483 
00484             // если был скрыт, то покажем
00485             mWidgetLines[pos]->setVisible(true);
00486             // обновляем текст
00487             mWidgetLines[pos]->setCaption(mItemsInfo[index].first);
00488 
00489             // если нужно выделить ,то выделим
00490             static_cast<Button*>(mWidgetLines[pos])->setStateSelected(index == mIndexSelect);
00491         }
00492 
00493         // если цикл весь прошли, то ставим максимальную линию
00494         if (pos >= mWidgetLines.size())
00495         {
00496             mLastRedrawLine = pos;
00497         }
00498         else
00499         {
00500             //Widget* focus = InputManager::getInstance().getMouseFocusWidget();
00501             for (; pos < mWidgetLines.size(); pos++)
00502             {
00503                 static_cast<Button*>(mWidgetLines[pos])->setStateSelected(false);
00504                 static_cast<Button*>(mWidgetLines[pos])->setVisible(false);
00505                 //if (focus == mWidgetLines[pos]) InputManager::getInstance()._unlinkWidget(focus);
00506             }
00507         }
00508 
00509 #if MYGUI_DEBUG_MODE == 1
00510         _checkMapping("ListBox::_redrawItemRange");
00511 #endif
00512     }
00513 
00514     // перерисовывает индекс
00515     void ListBox::_redrawItem(size_t _index)
00516     {
00517         // невидно
00518         if (_index < (size_t)mTopIndex)
00519             return;
00520         _index -= (size_t)mTopIndex;
00521         // тоже невидно
00522         if (_index >= mLastRedrawLine)
00523             return;
00524 
00525         MYGUI_ASSERT_RANGE(_index, mItemsInfo.size(), "ListBox::_redrawItem");
00526         // перерисовываем
00527         mWidgetLines[_index]->setCaption(mItemsInfo[_index + mTopIndex].first);
00528 
00529 #if MYGUI_DEBUG_MODE == 1
00530         _checkMapping("ListBox::_redrawItem");
00531 #endif
00532     }
00533 
00534     void ListBox::insertItemAt(size_t _index, const UString& _name, Any _data)
00535     {
00536         MYGUI_ASSERT_RANGE_INSERT(_index, mItemsInfo.size(), "ListBox::insertItemAt");
00537         if (_index == ITEM_NONE)
00538             _index = mItemsInfo.size();
00539 
00540         // вставляем физически
00541         mItemsInfo.insert(mItemsInfo.begin() + _index, PairItem(_name, _data));
00542 
00543         // если надо, то меняем выделенный элемент
00544         if ((mIndexSelect != ITEM_NONE) && (_index <= mIndexSelect))
00545             mIndexSelect++;
00546 
00547         // строка, до первого видимого элемента
00548         if ((_index <= (size_t)mTopIndex) && (mRangeIndex > 0))
00549         {
00550             mTopIndex ++;
00551             // просчитываем положение скролла
00552             if (mWidgetScroll != nullptr)
00553             {
00554                 mWidgetScroll->setScrollRange(mWidgetScroll->getScrollRange() + mHeightLine);
00555                 if (!mItemsInfo.empty())
00556                     mWidgetScroll->setTrackSize( mWidgetScroll->getLineSize() * _getClientWidget()->getHeight() / mHeightLine / (int)mItemsInfo.size() );
00557                 mWidgetScroll->setScrollPosition(mTopIndex * mHeightLine + mOffsetTop);
00558             }
00559             mRangeIndex += mHeightLine;
00560         }
00561         else
00562         {
00563             // высчитывам положение строки
00564             int offset = ((int)_index - mTopIndex) * mHeightLine - mOffsetTop;
00565 
00566             // строка, после последнего видимого элемента, плюс одна строка (потому что для прокрутки нужно на одну строчку больше)
00567             if (_getClientWidget()->getHeight() < (offset - mHeightLine))
00568             {
00569                 // просчитываем положение скролла
00570                 if (mWidgetScroll != nullptr)
00571                 {
00572                     mWidgetScroll->setScrollRange(mWidgetScroll->getScrollRange() + mHeightLine);
00573                     if (!mItemsInfo.empty())
00574                         mWidgetScroll->setTrackSize( mWidgetScroll->getLineSize() * _getClientWidget()->getHeight() / mHeightLine / (int)mItemsInfo.size() );
00575                     mWidgetScroll->setScrollPosition(mTopIndex * mHeightLine + mOffsetTop);
00576                 }
00577                 mRangeIndex += mHeightLine;
00578 
00579                 // строка в видимой области
00580             }
00581             else
00582             {
00583                 // обновляем все
00584                 updateScroll();
00585                 updateLine(true);
00586 
00587                 // позже сюда еще оптимизацию по колличеству перерисовок
00588             }
00589         }
00590 
00591 #if MYGUI_DEBUG_MODE == 1
00592         _checkMapping("ListBox::insertItemAt");
00593 #endif
00594     }
00595 
00596     void ListBox::removeItemAt(size_t _index)
00597     {
00598         MYGUI_ASSERT_RANGE(_index, mItemsInfo.size(), "ListBox::removeItemAt");
00599 
00600         // удяляем физически строку
00601         mItemsInfo.erase(mItemsInfo.begin() + _index);
00602 
00603         // если надо, то меняем выделенный элемент
00604         if (mItemsInfo.empty()) mIndexSelect = ITEM_NONE;
00605         else if (mIndexSelect != ITEM_NONE)
00606         {
00607             if (_index < mIndexSelect)
00608                 mIndexSelect--;
00609             else if ((_index == mIndexSelect) && (mIndexSelect == (mItemsInfo.size())))
00610                 mIndexSelect--;
00611         }
00612 
00613         // если виджетов стало больше , то скрываем крайний
00614         if (mWidgetLines.size() > mItemsInfo.size())
00615         {
00616             mWidgetLines[mItemsInfo.size()]->setVisible(false);
00617         }
00618 
00619         // строка, до первого видимого элемента
00620         if (_index < (size_t)mTopIndex)
00621         {
00622             mTopIndex --;
00623             // просчитываем положение скролла
00624             if (mWidgetScroll != nullptr)
00625             {
00626                 mWidgetScroll->setScrollRange(mWidgetScroll->getScrollRange() - mHeightLine);
00627                 if (!mItemsInfo.empty())
00628                     mWidgetScroll->setTrackSize( mWidgetScroll->getLineSize() * _getClientWidget()->getHeight() / mHeightLine / (int)mItemsInfo.size() );
00629                 mWidgetScroll->setScrollPosition(mTopIndex * mHeightLine + mOffsetTop);
00630             }
00631             mRangeIndex -= mHeightLine;
00632         }
00633         else
00634         {
00635             // высчитывам положение удаляемой строки
00636             int offset = ((int)_index - mTopIndex) * mHeightLine - mOffsetTop;
00637 
00638             // строка, после последнего видимого элемента
00639             if (_getClientWidget()->getHeight() < offset)
00640             {
00641                 // просчитываем положение скролла
00642                 if (mWidgetScroll != nullptr)
00643                 {
00644                     mWidgetScroll->setScrollRange(mWidgetScroll->getScrollRange() - mHeightLine);
00645                     if (!mItemsInfo.empty())
00646                         mWidgetScroll->setTrackSize( mWidgetScroll->getLineSize() * _getClientWidget()->getHeight() / mHeightLine / (int)mItemsInfo.size() );
00647                     mWidgetScroll->setScrollPosition(mTopIndex * mHeightLine + mOffsetTop);
00648                 }
00649                 mRangeIndex -= mHeightLine;
00650 
00651                 // строка в видимой области
00652             }
00653             else
00654             {
00655                 // обновляем все
00656                 updateScroll();
00657                 updateLine(true);
00658 
00659                 // позже сюда еще оптимизацию по колличеству перерисовок
00660             }
00661         }
00662 
00663 #if MYGUI_DEBUG_MODE == 1
00664         _checkMapping("ListBox::removeItemAt");
00665 #endif
00666     }
00667 
00668     void ListBox::setIndexSelected(size_t _index)
00669     {
00670         MYGUI_ASSERT_RANGE_AND_NONE(_index, mItemsInfo.size(), "ListBox::setIndexSelected");
00671         if (mIndexSelect != _index)
00672         {
00673             _selectIndex(mIndexSelect, false);
00674             _selectIndex(_index, true);
00675             mIndexSelect = _index;
00676         }
00677     }
00678 
00679     void ListBox::_selectIndex(size_t _index, bool _select)
00680     {
00681         if (_index == ITEM_NONE)
00682             return;
00683         // не видно строки
00684         if (_index < (size_t)mTopIndex)
00685             return;
00686         // высчитывам положение строки
00687         int offset = ((int)_index - mTopIndex) * mHeightLine - mOffsetTop;
00688         // строка, после последнего видимого элемента
00689         if (_getClientWidget()->getHeight() < offset)
00690             return;
00691 
00692         size_t index = _index - mTopIndex;
00693         if (index < mWidgetLines.size())
00694             static_cast<Button*>(mWidgetLines[index])->setStateSelected(_select);
00695 
00696 #if MYGUI_DEBUG_MODE == 1
00697         _checkMapping("ListBox::_selectIndex");
00698 #endif
00699     }
00700 
00701     void ListBox::beginToItemAt(size_t _index)
00702     {
00703         MYGUI_ASSERT_RANGE(_index, mItemsInfo.size(), "ListBox::beginToItemAt");
00704         if (mRangeIndex <= 0)
00705             return;
00706 
00707         int offset = (int)_index * mHeightLine;
00708         if (offset >= mRangeIndex) offset = mRangeIndex;
00709 
00710         if (mWidgetScroll != nullptr)
00711         {
00712             if ((int)mWidgetScroll->getScrollPosition() == offset)
00713                 return;
00714             mWidgetScroll->setScrollPosition(offset);
00715         }
00716         notifyScrollChangePosition(nullptr, offset);
00717 
00718 #if MYGUI_DEBUG_MODE == 1
00719         _checkMapping("ListBox::beginToItemAt");
00720 #endif
00721     }
00722 
00723     // видим ли мы элемент, полностью или нет
00724     bool ListBox::isItemVisibleAt(size_t _index, bool _fill)
00725     {
00726         // если элемента нет, то мы его не видим (в том числе когда их вообще нет)
00727         if (_index >= mItemsInfo.size())
00728             return false;
00729         // если скрола нет, то мы палюбак видим
00730         if (mRangeIndex <= 0)
00731             return true;
00732 
00733         // строка, до первого видимого элемента
00734         if (_index < (size_t)mTopIndex)
00735             return false;
00736 
00737         // строка это верхний выделенный
00738         if (_index == (size_t)mTopIndex)
00739         {
00740             if ((mOffsetTop != 0) && (_fill))
00741                 return false; // нам нужна полностью видимость
00742             return true;
00743         }
00744 
00745         // высчитывам положение строки
00746         int offset = ((int)_index - mTopIndex) * mHeightLine - mOffsetTop;
00747 
00748         // строка, после последнего видимого элемента
00749         if (_getClientWidget()->getHeight() < offset)
00750             return false;
00751 
00752         // если мы внизу и нам нужен целый
00753         if ((_getClientWidget()->getHeight() < (offset + mHeightLine)) && (_fill))
00754             return false;
00755 
00756         return true;
00757     }
00758 
00759     void ListBox::removeAllItems()
00760     {
00761         mTopIndex = 0;
00762         mIndexSelect = ITEM_NONE;
00763         mOffsetTop = 0;
00764 
00765         mItemsInfo.clear();
00766 
00767         int offset = 0;
00768         for (size_t pos = 0; pos < mWidgetLines.size(); pos++)
00769         {
00770             mWidgetLines[pos]->setVisible(false);
00771             mWidgetLines[pos]->setPosition(0, offset);
00772             offset += mHeightLine;
00773         }
00774 
00775         // обновляем все
00776         updateScroll();
00777         updateLine(true);
00778 
00779 #if MYGUI_DEBUG_MODE == 1
00780         _checkMapping("ListBox::removeAllItems");
00781 #endif
00782     }
00783 
00784     void ListBox::setItemNameAt(size_t _index, const UString& _name)
00785     {
00786         MYGUI_ASSERT_RANGE(_index, mItemsInfo.size(), "ListBox::setItemNameAt");
00787         mItemsInfo[_index].first = _name;
00788         _redrawItem(_index);
00789     }
00790 
00791     void ListBox::setItemDataAt(size_t _index, Any _data)
00792     {
00793         MYGUI_ASSERT_RANGE(_index, mItemsInfo.size(), "ListBox::setItemDataAt");
00794         mItemsInfo[_index].second = _data;
00795         _redrawItem(_index);
00796     }
00797 
00798     const UString& ListBox::getItemNameAt(size_t _index)
00799     {
00800         MYGUI_ASSERT_RANGE(_index, mItemsInfo.size(), "ListBox::getItemNameAt");
00801         return mItemsInfo[_index].first;
00802     }
00803 
00804     void ListBox::notifyMouseSetFocus(Widget* _sender, Widget* _old)
00805     {
00806 
00807 #if MYGUI_DEBUG_MODE == 1
00808         MYGUI_ASSERT_RANGE(*_sender->_getInternalData<size_t>(), mWidgetLines.size(), "ListBox::notifyMouseSetFocus");
00809 #endif
00810 
00811         mLineActive = *_sender->_getInternalData<size_t>();
00812         eventListMouseItemFocus(this, mLineActive);
00813     }
00814 
00815     void ListBox::notifyMouseLostFocus(Widget* _sender, Widget* _new)
00816     {
00817         if ((nullptr == _new) || (_new->getParent() != _getClientWidget()))
00818         {
00819             mLineActive = ITEM_NONE;
00820             eventListMouseItemFocus(this, ITEM_NONE);
00821         }
00822     }
00823 
00824     void ListBox::_setItemFocus(size_t _index, bool _focus)
00825     {
00826         MYGUI_ASSERT_RANGE(_index, mWidgetLines.size(), "ListBox::_setItemFocus");
00827         static_cast<Button*>(mWidgetLines[_index])->_setMouseFocus(_focus);
00828     }
00829 
00830     void ListBox::setScrollVisible(bool _visible)
00831     {
00832         if (mNeedVisibleScroll == _visible)
00833             return;
00834         mNeedVisibleScroll = _visible;
00835         updateScroll();
00836     }
00837 
00838     void ListBox::setScrollPosition(size_t _position)
00839     {
00840         if (mWidgetScroll != nullptr)
00841         {
00842             if (mWidgetScroll->getScrollRange() > _position)
00843             {
00844                 mWidgetScroll->setScrollPosition(_position);
00845                 _setScrollView(_position);
00846             }
00847         }
00848     }
00849 
00850     void ListBox::_setScrollView(size_t _position)
00851     {
00852         mOffsetTop = ((int)_position % mHeightLine);
00853 
00854         // смещение с отрицательной стороны
00855         int offset = 0 - mOffsetTop;
00856 
00857         for (size_t pos = 0; pos < mWidgetLines.size(); pos++)
00858         {
00859             mWidgetLines[pos]->setPosition(IntPoint(0, offset));
00860             offset += mHeightLine;
00861         }
00862 
00863         // если индех изменился, то перерисовываем линии
00864         int top = ((int)_position / mHeightLine);
00865         if (top != mTopIndex)
00866         {
00867             mTopIndex = top;
00868             _redrawItemRange();
00869         }
00870 
00871         // прорисовываем все нижние строки, если они появились
00872         _redrawItemRange(mLastRedrawLine);
00873     }
00874 
00875     void ListBox::_sendEventChangeScroll(size_t _position)
00876     {
00877         eventListChangeScroll(this, _position);
00878         if (ITEM_NONE != mLineActive)
00879             eventListMouseItemFocus(this, mLineActive);
00880     }
00881 
00882     void ListBox::swapItemsAt(size_t _index1, size_t _index2)
00883     {
00884         MYGUI_ASSERT_RANGE(_index1, mItemsInfo.size(), "ListBox::swapItemsAt");
00885         MYGUI_ASSERT_RANGE(_index2, mItemsInfo.size(), "ListBox::swapItemsAt");
00886 
00887         if (_index1 == _index2)
00888             return;
00889 
00890         std::swap(mItemsInfo[_index1], mItemsInfo[_index2]);
00891 
00892         _redrawItem(_index1);
00893         _redrawItem(_index2);
00894     }
00895 
00896     void ListBox::_checkMapping(const std::string& _owner)
00897     {
00898         size_t count_pressed = 0;
00899         size_t count_show = 0;
00900 
00901         for (size_t pos = 0; pos < mWidgetLines.size(); pos++)
00902         {
00903             MYGUI_ASSERT(pos == *mWidgetLines[pos]->_getInternalData<size_t>(), _owner);
00904             static_cast<Button*>(mWidgetLines[pos])->getStateSelected() ? count_pressed ++ : 0;
00905             static_cast<Button*>(mWidgetLines[pos])->getVisible() ? count_show ++ : 0;
00906         }
00907         //MYGUI_ASSERT(count_pressed < 2, _owner);
00908         //MYGUI_ASSERT((count_show + mOffsetTop) <= mItemsInfo.size(), _owner);
00909     }
00910 
00911     void ListBox::_checkAlign()
00912     {
00913         // максимальная высота всех строк
00914         int max_height = mItemsInfo.size() * mHeightLine;
00915         // видимая высота
00916         int visible_height = _getClientWidget()->getHeight();
00917 
00918         // все строки помещаются
00919         if (visible_height >= max_height)
00920         {
00921             MYGUI_ASSERT(mTopIndex == 0, "mTopIndex == 0");
00922             MYGUI_ASSERT(mOffsetTop == 0, "mOffsetTop == 0");
00923             int height = 0;
00924             for (size_t pos = 0; pos < mWidgetLines.size(); pos++)
00925             {
00926                 if (pos >= mItemsInfo.size())
00927                     break;
00928                 MYGUI_ASSERT(mWidgetLines[pos]->getTop() == height, "mWidgetLines[pos]->getTop() == height");
00929                 height += mWidgetLines[pos]->getHeight();
00930             }
00931         }
00932     }
00933 
00934     size_t ListBox::findItemIndexWith(const UString& _name)
00935     {
00936         for (size_t pos = 0; pos < mItemsInfo.size(); pos++)
00937         {
00938             if (mItemsInfo[pos].first == _name)
00939                 return pos;
00940         }
00941         return ITEM_NONE;
00942     }
00943 
00944     int ListBox::getOptimalHeight()
00945     {
00946         return (int)((mCoord.height - _getClientWidget()->getHeight()) + (mItemsInfo.size() * mHeightLine));
00947     }
00948 
00949     Widget* ListBox::_getClientWidget()
00950     {
00951         return mClient == nullptr ? this : mClient;
00952     }
00953 
00954     size_t ListBox::getItemCount() const
00955     {
00956         return mItemsInfo.size();
00957     }
00958 
00959     void ListBox::addItem(const UString& _name, Any _data)
00960     {
00961         insertItemAt(ITEM_NONE, _name, _data);
00962     }
00963 
00964     size_t ListBox::getIndexSelected() const
00965     {
00966         return mIndexSelect;
00967     }
00968 
00969     void ListBox::clearIndexSelected()
00970     {
00971         setIndexSelected(ITEM_NONE);
00972     }
00973 
00974     void ListBox::clearItemDataAt(size_t _index)
00975     {
00976         setItemDataAt(_index, Any::Null);
00977     }
00978 
00979     void ListBox::beginToItemFirst()
00980     {
00981         if (getItemCount())
00982             beginToItemAt(0);
00983     }
00984 
00985     void ListBox::beginToItemLast()
00986     {
00987         if (getItemCount())
00988             beginToItemAt(getItemCount() - 1);
00989     }
00990 
00991     void ListBox::beginToItemSelected()
00992     {
00993         if (getIndexSelected() != ITEM_NONE)
00994             beginToItemAt(getIndexSelected());
00995     }
00996 
00997     bool ListBox::isItemSelectedVisible(bool _fill)
00998     {
00999         return isItemVisibleAt(mIndexSelect, _fill);
01000     }
01001 
01002     void ListBox::setPosition(int _left, int _top)
01003     {
01004         setPosition(IntPoint(_left, _top));
01005     }
01006 
01007     void ListBox::setSize(int _width, int _height)
01008     {
01009         setSize(IntSize(_width, _height));
01010     }
01011 
01012     void ListBox::setCoord(int _left, int _top, int _width, int _height)
01013     {
01014         setCoord(IntCoord(_left, _top, _width, _height));
01015     }
01016 
01017     size_t ListBox::_getItemIndex(Widget* _item)
01018     {
01019         for (VectorButton::iterator iter = mWidgetLines.begin(); iter != mWidgetLines.end(); ++iter)
01020         {
01021             if ((*iter) == _item)
01022                 return *(*iter)->_getInternalData<size_t>() + mTopIndex;
01023         }
01024         return ITEM_NONE;
01025     }
01026 
01027     void ListBox::_resetContainer(bool _update)
01028     {
01029         // обязательно у базового
01030         Base::_resetContainer(_update);
01031 
01032         if (!_update)
01033         {
01034             WidgetManager& instance = WidgetManager::getInstance();
01035             for (VectorButton::iterator iter = mWidgetLines.begin(); iter != mWidgetLines.end(); ++iter)
01036                 instance.unlinkFromUnlinkers(*iter);
01037         }
01038     }
01039 
01040     void ListBox::setPropertyOverride(const std::string& _key, const std::string& _value)
01041     {
01042         if (_key == "AddItem")
01043             addItem(_value);
01044         else
01045         {
01046             Base::setPropertyOverride(_key, _value);
01047             return;
01048         }
01049         eventChangeProperty(this, _key, _value);
01050     }
01051 
01052     size_t ListBox::_getItemCount()
01053     {
01054         return getItemCount();
01055     }
01056 
01057     void ListBox::_addItem(const MyGUI::UString& _name)
01058     {
01059         addItem(_name);
01060     }
01061 
01062     void ListBox::_removeItemAt(size_t _index)
01063     {
01064         removeItemAt(_index);
01065     }
01066 
01067     void ListBox::_setItemNameAt(size_t _index, const UString& _name)
01068     {
01069         setItemNameAt(_index, _name);
01070     }
01071 
01072     const UString& ListBox::_getItemNameAt(size_t _index)
01073     {
01074         return getItemNameAt(_index);
01075     }
01076 
01077 } // namespace MyGUI