MyGUI  3.0.1
MyGUI_MultiList.cpp
Go to the documentation of this file.
1 
7 /*
8  This file is part of MyGUI.
9 
10  MyGUI is free software: you can redistribute it and/or modify
11  it under the terms of the GNU Lesser General Public License as published by
12  the Free Software Foundation, either version 3 of the License, or
13  (at your option) any later version.
14 
15  MyGUI is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  GNU Lesser General Public License for more details.
19 
20  You should have received a copy of the GNU Lesser General Public License
21  along with MyGUI. If not, see <http://www.gnu.org/licenses/>.
22 */
23 #include "MyGUI_Precompiled.h"
24 #include "MyGUI_MultiList.h"
25 #include "MyGUI_ResourceSkin.h"
26 #include "MyGUI_Button.h"
27 #include "MyGUI_StaticImage.h"
28 #include "MyGUI_List.h"
29 #include "MyGUI_Gui.h"
30 #include "MyGUI_WidgetManager.h"
31 
32 namespace MyGUI
33 {
34 
36  mHeightButton(0),
37  mWidthBar(0),
38  mButtonMain(nullptr),
39  mLastMouseFocusIndex(ITEM_NONE),
40  mSortUp(true),
41  mSortColumnIndex(ITEM_NONE),
42  mWidthSeparator(0),
43  mOffsetButtonSeparator(2),
44  mItemSelected(ITEM_NONE),
45  mFrameAdvise(false),
46  mClient(nullptr)
47  {
48  }
49 
50  void MultiList::_initialise(WidgetStyle _style, const IntCoord& _coord, Align _align, ResourceSkin* _info, Widget* _parent, ICroppedRectangle * _croppedParent, IWidgetCreator * _creator, const std::string& _name)
51  {
52  Base::_initialise(_style, _coord, _align, _info, _parent, _croppedParent, _creator, _name);
53 
54  initialiseWidgetSkin(_info);
55  }
56 
58  {
59  frameAdvise(false);
60  shutdownWidgetSkin();
61  }
62 
64  {
65  shutdownWidgetSkin();
67  initialiseWidgetSkin(_info);
68  }
69 
70  void MultiList::initialiseWidgetSkin(ResourceSkin* _info)
71  {
72  // парсим свойства
73  const MapString& properties = _info->getProperties();
74  if (!properties.empty())
75  {
76  MapString::const_iterator iter = properties.find("SkinButton");
77  if (iter != properties.end()) mSkinButton = iter->second;
78  iter = properties.find("HeightButton");
79  if (iter != properties.end()) mHeightButton = utility::parseInt(iter->second);
80  if (mHeightButton < 0) mHeightButton = 0;
81 
82  iter = properties.find("SkinList");
83  if (iter != properties.end()) mSkinList = iter->second;
84 
85  iter = properties.find("SkinButtonEmpty");
86  if (iter != properties.end())
87  {
88  mButtonMain = mClient->createWidget<Button>(iter->second,
89  IntCoord(0, 0, mClient->getWidth(), mHeightButton), Align::Default);
90  }
91 
92  iter = properties.find("WidthSeparator");
93  if (iter != properties.end()) mWidthSeparator = utility::parseInt(iter->second);
94  iter = properties.find("SkinSeparator");
95  if (iter != properties.end()) mSkinSeparator = iter->second;
96  }
97 
98  for (VectorWidgetPtr::iterator iter=mWidgetChildSkin.begin(); iter!=mWidgetChildSkin.end(); ++iter)
99  {
100  if (*(*iter)->_getInternalData<std::string>() == "Client")
101  {
102  MYGUI_DEBUG_ASSERT( ! mClient, "widget already assigned");
103  mClient = (*iter);
104  mWidgetClient = (*iter); // чтобы размер возвращался клиентской зоны
105  }
106  }
107  // мона и без клиента
108  if (nullptr == mClient) mClient = this;
109  }
110 
111  void MultiList::shutdownWidgetSkin()
112  {
113  mWidgetClient = nullptr;
114  mClient = nullptr;
115  }
116 
117  //----------------------------------------------------------------------------------//
118  // методы для работы со столбцами
119  void MultiList::insertColumnAt(size_t _column, const UString& _name, int _width, Any _data)
120  {
121  MYGUI_ASSERT_RANGE_INSERT(_column, mVectorColumnInfo.size(), "MultiList::insertColumnAt");
122  if (_column == ITEM_NONE) _column = mVectorColumnInfo.size();
123 
124  // скрываем у крайнего скролл
125  if (!mVectorColumnInfo.empty())
126  mVectorColumnInfo.back().list->setScrollVisible(false);
127  else mSortColumnIndex = 0;
128 
129  ColumnInfo column;
130  column.width = _width < 0 ? 0 : _width;
131 
132  column.list = mClient->createWidget<List>(mSkinList, IntCoord(), Align::Left | Align::VStretch);
134  column.list->eventListMouseItemFocus = newDelegate(this, &MultiList::notifyListChangeFocus);
135  column.list->eventListChangeScroll = newDelegate(this, &MultiList::notifyListChangeScrollPosition);
136  column.list->eventListSelectAccept = newDelegate(this, &MultiList::notifyListSelectAccept);
137 
138  column.button = mClient->createWidget<Button>(mSkinButton, IntCoord(), Align::Default);
140  column.name = _name;
141  column.data = _data;
142 
143  // если уже были столбики, то делаем то же колличество полей
144  if (!mVectorColumnInfo.empty())
145  {
146  size_t count = mVectorColumnInfo.front().list->getItemCount();
147  for (size_t pos=0; pos<count; ++pos)
148  column.list->addItem("");
149  }
150 
151  mVectorColumnInfo.insert(mVectorColumnInfo.begin() + _column, column);
152 
153  updateColumns();
154 
155  // показываем скролл нового крайнего
156  mVectorColumnInfo.back().list->setScrollVisible(true);
157  }
158 
159  void MultiList::setColumnNameAt(size_t _column, const UString& _name)
160  {
161  MYGUI_ASSERT_RANGE(_column, mVectorColumnInfo.size(), "MultiList::setColumnNameAt");
162  mVectorColumnInfo[_column].name = _name;
163  redrawButtons();
164  }
165 
166  void MultiList::setColumnWidthAt(size_t _column, int _width)
167  {
168  MYGUI_ASSERT_RANGE(_column, mVectorColumnInfo.size(), "MultiList::setColumnWidthAt");
169  mVectorColumnInfo[_column].width = _width < 0 ? 0 : _width;
170  updateColumns();
171  }
172 
173  const UString& MultiList::getColumnNameAt(size_t _column)
174  {
175  MYGUI_ASSERT_RANGE(_column, mVectorColumnInfo.size(), "MultiList::getColumnNameAt");
176  return mVectorColumnInfo[_column].name;
177  }
178 
179  int MultiList::getColumnWidthAt(size_t _column)
180  {
181  MYGUI_ASSERT_RANGE(_column, mVectorColumnInfo.size(), "MultiList::getColumnWidthAt");
182  return mVectorColumnInfo[_column].width;
183  }
184 
185  void MultiList::removeColumnAt(size_t _column)
186  {
187  MYGUI_ASSERT_RANGE(_column, mVectorColumnInfo.size(), "MultiList::removeColumnAt");
188 
189  ColumnInfo& info = mVectorColumnInfo[_column];
190 
192  manager.destroyWidget(info.button);
193  manager.destroyWidget(info.list);
194 
195  mVectorColumnInfo.erase(mVectorColumnInfo.begin() + _column);
196 
197  if (mVectorColumnInfo.empty())
198  {
199  mSortColumnIndex = ITEM_NONE;
200  mItemSelected = ITEM_NONE;
201  }
202  else
203  {
204  mSortColumnIndex = 0;
205  mSortUp = true;
206  sortList();
207  }
208 
209  updateColumns();
210  }
211 
213  {
215  for (VectorColumnInfo::iterator iter=mVectorColumnInfo.begin(); iter!=mVectorColumnInfo.end(); ++iter)
216  {
217  manager.destroyWidget((*iter).button);
218  manager.destroyWidget((*iter).list);
219  }
220  mVectorColumnInfo.clear();
221  mSortColumnIndex = ITEM_NONE;
222 
223  updateColumns();
224 
225  mItemSelected = ITEM_NONE;
226  }
227 
228  void MultiList::sortByColumn(size_t _column, bool _backward)
229  {
230  mSortColumnIndex = _column;
231  if (_backward)
232  {
233  mSortUp = !mSortUp;
234  redrawButtons();
235  // если было недосортированно то сортируем
236  if (mFrameAdvise) sortList();
237 
238  flipList();
239  }
240  else
241  {
242  mSortUp = true;
243  redrawButtons();
244  sortList();
245  }
246  }
247 
248  size_t MultiList::getItemCount() const
249  {
250  if (mVectorColumnInfo.empty()) return 0;
251  return mVectorColumnInfo.front().list->getItemCount();
252  }
253 
255  {
257  for (VectorColumnInfo::iterator iter=mVectorColumnInfo.begin(); iter!=mVectorColumnInfo.end(); ++iter)
258  {
259  (*iter).list->removeAllItems();
260  }
261 
262  mItemSelected = ITEM_NONE;
263  }
264 
265  /*size_t MultiList::getItemIndexSelected()
266  {
267  if (mVectorColumnInfo.empty()) return ITEM_NONE;
268  size_t item = mVectorColumnInfo.front().list->getItemIndexSelected();
269  return (ITEM_NONE == item) ? ITEM_NONE : BiIndexBase::convertToFace(item);
270  }*/
271 
272  void MultiList::updateBackSelected(size_t _index)
273  {
274  if (_index == ITEM_NONE)
275  {
276  for (VectorColumnInfo::iterator iter=mVectorColumnInfo.begin(); iter!=mVectorColumnInfo.end(); ++iter)
277  {
278  (*iter).list->clearIndexSelected();
279  }
280  }
281  else
282  {
283  //size_t index = BiIndexBase::convertToBack(_index);
284  for (VectorColumnInfo::iterator iter=mVectorColumnInfo.begin(); iter!=mVectorColumnInfo.end(); ++iter)
285  {
286  (*iter).list->setIndexSelected(_index);
287  }
288  }
289  }
290 
291  void MultiList::setIndexSelected(size_t _index)
292  {
293  if (_index == mItemSelected) return;
294 
295  MYGUI_ASSERT_RANGE(0, mVectorColumnInfo.size(), "MultiList::setIndexSelected");
296  MYGUI_ASSERT_RANGE_AND_NONE(_index, mVectorColumnInfo.begin()->list->getItemCount(), "MultiList::setIndexSelected");
297 
298  mItemSelected = _index;
300  }
301 
302  void MultiList::setSubItemNameAt(size_t _column, size_t _index, const UString& _name)
303  {
304  MYGUI_ASSERT_RANGE(_column, mVectorColumnInfo.size(), "MultiList::setSubItemAt");
305  MYGUI_ASSERT_RANGE(_index, mVectorColumnInfo.begin()->list->getItemCount(), "MultiList::setSubItemAt");
306 
307  size_t index = BiIndexBase::convertToBack(_index);
308  mVectorColumnInfo[_column].list->setItemNameAt(index, _name);
309 
310  // если мы попортили список с активным сортом, надо пересчитывать
311  if (_column == mSortColumnIndex) frameAdvise(true);
312  }
313 
314  const UString& MultiList::getSubItemNameAt(size_t _column, size_t _index)
315  {
316  MYGUI_ASSERT_RANGE(_column, mVectorColumnInfo.size(), "MultiList::getSubItemNameAt");
317  MYGUI_ASSERT_RANGE(_index, mVectorColumnInfo.begin()->list->getItemCount(), "MultiList::getSubItemNameAt");
318 
319  size_t index = BiIndexBase::convertToBack(_index);
320  return mVectorColumnInfo[_column].list->getItemNameAt(index);
321  }
322 
323  size_t MultiList::findSubItemWith(size_t _column, const UString& _name)
324  {
325  MYGUI_ASSERT_RANGE(_column, mVectorColumnInfo.size(), "MultiList::findSubItemWith");
326 
327  size_t index = mVectorColumnInfo[_column].list->findItemIndexWith(_name);
328  return BiIndexBase::convertToFace(index);
329  }
330  //----------------------------------------------------------------------------------//
331 
333  {
334  if (nullptr == mButtonMain) return;
335  // кнопка, для заполнения пустоты
336  if (mWidthBar >= mClient->getWidth()) mButtonMain->setVisible(false);
337  else
338  {
339  mButtonMain->setCoord(mWidthBar, 0, mClient->getWidth()-mWidthBar, mHeightButton);
340  mButtonMain->setVisible(true);
341  }
342  }
343 
344  void MultiList::notifyListChangePosition(List* _sender, size_t _position)
345  {
346  for (VectorColumnInfo::iterator iter=mVectorColumnInfo.begin(); iter!=mVectorColumnInfo.end(); ++iter)
347  {
348  if (_sender != (*iter).list) (*iter).list->setIndexSelected(_position);
349  }
350 
351  updateBackSelected(_position);
352 
353  mItemSelected = BiIndexBase::convertToFace(_position);
354 
355  // наш евент
356  eventListChangePosition(this, mItemSelected);
357  }
358 
359  void MultiList::notifyListSelectAccept(List* _sender, size_t _position)
360  {
361  // наш евент
363  }
364 
365  void MultiList::notifyListChangeFocus(List* _sender, size_t _position)
366  {
367  for (VectorColumnInfo::iterator iter=mVectorColumnInfo.begin(); iter!=mVectorColumnInfo.end(); ++iter)
368  {
369  if (_sender != (*iter).list)
370  {
371  if (ITEM_NONE != mLastMouseFocusIndex) (*iter).list->_setItemFocus(mLastMouseFocusIndex, false);
372  if (ITEM_NONE != _position) (*iter).list->_setItemFocus(_position, true);
373  }
374  }
375  mLastMouseFocusIndex = _position;
376  }
377 
378  void MultiList::notifyListChangeScrollPosition(List* _sender, size_t _position)
379  {
380  for (VectorColumnInfo::iterator iter=mVectorColumnInfo.begin(); iter!=mVectorColumnInfo.end(); ++iter)
381  {
382  if (_sender != (*iter).list)
383  (*iter).list->setScrollPosition(_position);
384  }
385  }
386 
388  {
389  size_t index = *_sender->_getInternalData<size_t>();
390  sortByColumn(index, index == mSortColumnIndex);
391  }
392 
394  {
395  size_t pos = 0;
396  for (VectorColumnInfo::iterator iter=mVectorColumnInfo.begin(); iter!=mVectorColumnInfo.end(); ++iter)
397  {
398  if (pos == mSortColumnIndex)
399  {
400  if (mSortUp) setButtonImageIndex((*iter).button, SORT_UP);
401  else setButtonImageIndex((*iter).button, SORT_DOWN);
402  }
403  else setButtonImageIndex((*iter).button, SORT_NONE);
404  (*iter).button->setCaption((*iter).name);
405  pos++;
406  }
407  }
408 
409  void MultiList::setButtonImageIndex(Button* _button, size_t _index)
410  {
411  StaticImage* image = _button->getStaticImage();
412  if ( nullptr == image ) return;
413  if (image->getItemResource())
414  {
415  static const size_t CountIcons = 3;
416  static const char * IconNames[CountIcons + 1] = { "None", "Up", "Down", "" };
417  if (_index >= CountIcons) _index = CountIcons;
418  image->setItemName(IconNames[_index]);
419  }
420  else
421  {
422  image->setItemSelect(_index);
423  }
424  }
425 
426  void MultiList::frameEntered(float _frame)
427  {
428  sortList();
429  }
430 
431  void MultiList::frameAdvise(bool _advise)
432  {
433  if ( _advise )
434  {
435  if ( ! mFrameAdvise )
436  {
437  MyGUI::Gui::getInstance().eventFrameStart += MyGUI::newDelegate( this, &MultiList::frameEntered );
438  mFrameAdvise = true;
439  }
440  }
441  else
442  {
443  if ( mFrameAdvise )
444  {
445  MyGUI::Gui::getInstance().eventFrameStart -= MyGUI::newDelegate( this, &MultiList::frameEntered );
446  mFrameAdvise = false;
447  }
448  }
449  }
450 
452  {
453  if (!mWidthSeparator || mSkinSeparator.empty()) return nullptr;
454  // последний столбик
455  if (_index == mVectorColumnInfo.size()-1) return nullptr;
456 
457  while (_index >= mSeparators.size())
458  {
459  Widget* separator = mClient->createWidget<Widget>(mSkinSeparator, IntCoord(), Align::Default);
460  mSeparators.push_back(separator);
461  }
462 
463  return mSeparators[_index];
464  }
465 
467  {
468  mWidthBar = 0;
469  size_t index = 0;
470  for (VectorColumnInfo::iterator iter=mVectorColumnInfo.begin(); iter!=mVectorColumnInfo.end(); ++iter)
471  {
472  (*iter).list->setCoord(mWidthBar, mHeightButton, (*iter).width, mClient->getHeight() - mHeightButton);
473  (*iter).button->setCoord(mWidthBar, 0, (*iter).width, mHeightButton);
474  (*iter).button->_setInternalData(index);
475 
476  mWidthBar += (*iter).width;
477 
478  // промежуток между листами
479  Widget* separator = getSeparator(index);
480  if (separator)
481  {
482  separator->setCoord(mWidthBar, 0, mWidthSeparator, mClient->getHeight());
483  }
484 
485  mWidthBar += mWidthSeparator;
486  index++;
487  }
488 
489  redrawButtons();
490  updateOnlyEmpty();
491  }
492 
494  {
495  if (ITEM_NONE == mSortColumnIndex) return;
496 
497  size_t last = mVectorColumnInfo.front().list->getItemCount();
498  if (0 == last) return;
499  last --;
500  size_t first = 0;
501 
502  while (first < last)
503  {
504  BiIndexBase::swapItemsBackAt(first, last);
505  for (VectorColumnInfo::iterator iter=mVectorColumnInfo.begin(); iter!=mVectorColumnInfo.end(); ++iter)
506  {
507  (*iter).list->swapItemsAt(first, last);
508  }
509 
510  first++;
511  last--;
512  }
513 
515  }
516 
517  bool MultiList::compare(List* _list, size_t _left, size_t _right)
518  {
519  bool result = false;
520  if (mSortUp) std::swap(_left, _right);
521  if (requestOperatorLess.empty()) result = _list->getItemNameAt(_left) < _list->getItemNameAt(_right);
522  else requestOperatorLess(this, mSortColumnIndex, _list->getItemNameAt(_left), _list->getItemNameAt(_right), result);
523  return result;
524  }
525 
527  {
528  if (ITEM_NONE == mSortColumnIndex) return;
529 
530  List* list = mVectorColumnInfo[mSortColumnIndex].list;
531 
532  size_t count = list->getItemCount();
533  if (0 == count) return;
534 
535  // shell sort
536  int first, last;
537  for (size_t step = count>>1; step>0 ; step >>= 1)
538  {
539  for (size_t i=0;i<(count-step);i++)
540  {
541  first=i;
542  while (first>=0)
543  {
544  last = first+step;
545  if (compare(list, first, last))
546  {
547  BiIndexBase::swapItemsBackAt(first, last);
548  for (VectorColumnInfo::iterator iter=mVectorColumnInfo.begin(); iter!=mVectorColumnInfo.end(); ++iter)
549  {
550  (*iter).list->swapItemsAt(first, last);
551  }
552  }
553  first--;
554  }
555  }
556  }
557 
558  frameAdvise(false);
559 
561  }
562 
563  void MultiList::insertItemAt(size_t _index, const UString& _name, Any _data)
564  {
565  MYGUI_ASSERT_RANGE(0, mVectorColumnInfo.size(), "MultiList::insertItemAt");
566  MYGUI_ASSERT_RANGE_INSERT(_index, mVectorColumnInfo.front().list->getItemCount(), "MultiList::insertItemAt");
567  if (ITEM_NONE == _index) _index = mVectorColumnInfo.front().list->getItemCount();
568 
569  // если надо, то меняем выделенный элемент
570  // при сортировке, обновится
571  if ((mItemSelected != ITEM_NONE) && (_index <= mItemSelected)) mItemSelected ++;
572 
573  size_t index = BiIndexBase::insertItemAt(_index);
574 
575  // вставляем во все поля пустые, а потом присваиваем первому
576  for (VectorColumnInfo::iterator iter=mVectorColumnInfo.begin(); iter!=mVectorColumnInfo.end(); ++iter)
577  {
578  (*iter).list->insertItemAt(index, "");
579  }
580  mVectorColumnInfo.front().list->setItemNameAt(index, _name);
581  mVectorColumnInfo.front().list->setItemDataAt(index, _data);
582 
583  frameAdvise(true);
584  }
585 
586  void MultiList::removeItemAt(size_t _index)
587  {
588  MYGUI_ASSERT_RANGE(0, mVectorColumnInfo.size(), "MultiList::removeItemAt");
589  MYGUI_ASSERT_RANGE(_index, mVectorColumnInfo.begin()->list->getItemCount(), "MultiList::removeItemAt");
590 
591  size_t index = BiIndexBase::removeItemAt(_index);
592 
593  for (VectorColumnInfo::iterator iter=mVectorColumnInfo.begin(); iter!=mVectorColumnInfo.end(); ++iter)
594  {
595  (*iter).list->removeItemAt(index);
596  }
597 
598  // если надо, то меняем выделенный элемент
599  size_t count = mVectorColumnInfo.begin()->list->getItemCount();
600  if (count == 0) mItemSelected = ITEM_NONE;
601  else if (mItemSelected != ITEM_NONE)
602  {
603  if (_index < mItemSelected) mItemSelected --;
604  else if ((_index == mItemSelected) && (mItemSelected == count)) mItemSelected --;
605  }
607  }
608 
609  void MultiList::swapItemsAt(size_t _index1, size_t _index2)
610  {
611  MYGUI_ASSERT_RANGE(0, mVectorColumnInfo.size(), "MultiList::removeItemAt");
612  MYGUI_ASSERT_RANGE(_index1, mVectorColumnInfo.begin()->list->getItemCount(), "MultiList::swapItemsAt");
613  MYGUI_ASSERT_RANGE(_index2, mVectorColumnInfo.begin()->list->getItemCount(), "MultiList::swapItemsAt");
614 
615  // при сортированном, меняем только индексы
616  BiIndexBase::swapItemsFaceAt(_index1, _index2);
617 
618  // при несортированном, нужно наоборот, поменять только данные
619  // FIXME
620  }
621 
622  void MultiList::setColumnDataAt(size_t _index, Any _data)
623  {
624  MYGUI_ASSERT_RANGE(_index, mVectorColumnInfo.size(), "MultiList::setColumnDataAt");
625  mVectorColumnInfo[_index].data = _data;
626  }
627 
628  void MultiList::setSubItemDataAt(size_t _column, size_t _index, Any _data)
629  {
630  MYGUI_ASSERT_RANGE(_column, mVectorColumnInfo.size(), "MultiList::setSubItemDataAt");
631  MYGUI_ASSERT_RANGE(_index, mVectorColumnInfo.begin()->list->getItemCount(), "MultiList::setSubItemDataAt");
632 
633  size_t index = BiIndexBase::convertToBack(_index);
634  mVectorColumnInfo[_column].list->setItemDataAt(index, _data);
635  }
636 
637 } // namespace MyGUI