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