1 /***********************************************************************
2 created: 13/4/2004
3 author: Paul D Turner
4
5 purpose: Implementation of MultiColumnList widget base class
6 *************************************************************************/
7 /***************************************************************************
8 * Copyright (C) 2004 - 2006 Paul D Turner & The CEGUI Development Team
9 *
10 * Permission is hereby granted, free of charge, to any person obtaining
11 * a copy of this software and associated documentation files (the
12 * "Software"), to deal in the Software without restriction, including
13 * without limitation the rights to use, copy, modify, merge, publish,
14 * distribute, sublicense, and/or sell copies of the Software, and to
15 * permit persons to whom the Software is furnished to do so, subject to
16 * the following conditions:
17 *
18 * The above copyright notice and this permission notice shall be
19 * included in all copies or substantial portions of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
24 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
25 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
26 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 * OTHER DEALINGS IN THE SOFTWARE.
28 ***************************************************************************/
29 #include "CEGUI/widgets/MultiColumnList.h"
30 #include "CEGUI/Exceptions.h"
31 #include "CEGUI/widgets/Scrollbar.h"
32 #include "CEGUI/widgets/ListHeader.h"
33 #include "CEGUI/widgets/ListboxItem.h"
34 #include "CEGUI/Logger.h"
35 #include "CEGUI/TextUtils.h"
36 #include "CEGUI/PropertyHelper.h"
37 #include "CEGUI/CoordConverter.h"
38 #include "CEGUI/WindowManager.h"
39 #include <algorithm>
40
41
42 // Start of CEGUI namespace section
43 namespace CEGUI
44 {
45 const String MultiColumnList::EventNamespace("MultiColumnList");
46 const String MultiColumnList::WidgetTypeName("CEGUI/MultiColumnList");
47
48 /*************************************************************************
49 MultiColumnListWindowRenderer
50 *************************************************************************/
MultiColumnListWindowRenderer(const String & name)51 MultiColumnListWindowRenderer::MultiColumnListWindowRenderer(const String& name) :
52 WindowRenderer(name, MultiColumnList::EventNamespace)
53 {
54 }
55 /*************************************************************************
56 Constants
57 *************************************************************************/
58 // Event names
59 const String MultiColumnList::EventSelectionModeChanged( "SelectionModeChanged" );
60 const String MultiColumnList::EventNominatedSelectColumnChanged( "NominatedSelectColumnChanged" );
61 const String MultiColumnList::EventNominatedSelectRowChanged( "NominatedSelectRowChanged" );
62 const String MultiColumnList::EventVertScrollbarModeChanged( "VertScrollbarModeChanged" );
63 const String MultiColumnList::EventHorzScrollbarModeChanged( "HorzScrollbarModeChanged" );
64 const String MultiColumnList::EventSelectionChanged( "SelectionChanged" );
65 const String MultiColumnList::EventListContentsChanged( "ListContentsChanged" );
66 const String MultiColumnList::EventSortColumnChanged( "SortColumnChanged" );
67 const String MultiColumnList::EventSortDirectionChanged( "SortDirectionChanged" );
68 const String MultiColumnList::EventListColumnSized( "ListColumnSized" );
69 const String MultiColumnList::EventListColumnMoved( "ListColumnMoved" );
70
71 /*************************************************************************
72 Child Widget name suffix constants
73 *************************************************************************/
74 const String MultiColumnList::VertScrollbarName( "__auto_vscrollbar__" );
75 const String MultiColumnList::HorzScrollbarName( "__auto_hscrollbar__" );
76 const String MultiColumnList::ListHeaderName( "__auto_listheader__" );
77
78
79 /*************************************************************************
80 Constructor for the Multi-column list base class
81 *************************************************************************/
MultiColumnList(const String & type,const String & name)82 MultiColumnList::MultiColumnList(const String& type, const String& name) :
83 Window(type, name),
84 d_forceVertScroll(false),
85 d_forceHorzScroll(false),
86 d_nominatedSelectCol(0),
87 d_nominatedSelectRow(0),
88 d_lastSelected(0),
89 d_columnCount(0),
90 d_autoSizeColumnUsesHeader(false)
91 {
92 // add properties
93 addMultiColumnListProperties();
94
95 // set default selection mode
96 d_selectMode = CellSingle; // hack to ensure call below does what it should.
97 setSelectionMode(RowSingle);
98 }
99
100
101 /*************************************************************************
102 Destructor for the multi-column list base class.
103 *************************************************************************/
~MultiColumnList(void)104 MultiColumnList::~MultiColumnList(void)
105 {
106 // delete any items we are supposed to
107 resetList_impl();
108 }
109
110
111 /*************************************************************************
112 Return whether user manipulation of the sort column and direction
113 is enabled.
114 *************************************************************************/
isUserSortControlEnabled(void) const115 bool MultiColumnList::isUserSortControlEnabled(void) const
116 {
117 return getListHeader()->isSortingEnabled();
118 }
119
120
121 /*************************************************************************
122 Return whether the user may size column segments.
123 *************************************************************************/
isUserColumnSizingEnabled(void) const124 bool MultiColumnList::isUserColumnSizingEnabled(void) const
125 {
126 return getListHeader()->isColumnSizingEnabled();
127 }
128
129
130 /*************************************************************************
131 Return whether the user may modify the order of the columns.
132 *************************************************************************/
isUserColumnDraggingEnabled(void) const133 bool MultiColumnList::isUserColumnDraggingEnabled(void) const
134 {
135 return getListHeader()->isColumnDraggingEnabled();
136 }
137
138
139 /*************************************************************************
140 Return the number of columns in the multi-column list
141 *************************************************************************/
getColumnCount(void) const142 uint MultiColumnList::getColumnCount(void) const
143 {
144 return d_columnCount;
145 }
146
147
148 /*************************************************************************
149 Return the number of rows in the multi-column list.
150 *************************************************************************/
getRowCount(void) const151 uint MultiColumnList::getRowCount(void) const
152 {
153 return (uint)d_grid.size();
154 }
155
156
157 /*************************************************************************
158 Return the zero based index of the current sort column. There must
159 be at least one column to successfully call this method.
160 *************************************************************************/
getSortColumn(void) const161 uint MultiColumnList::getSortColumn(void) const
162 {
163 return getListHeader()->getSortColumn();
164 }
165
getSortColumnID(void) const166 uint MultiColumnList::getSortColumnID(void) const
167 {
168 if (getColumnCount() > 0)
169 {
170 return getColumnID(getSortColumn());
171 }
172 return 0;
173 }
174
175 /*************************************************************************
176 Return the zero based column index of the column with the specified ID.
177 *************************************************************************/
getColumnWithID(uint col_id) const178 uint MultiColumnList::getColumnWithID(uint col_id) const
179 {
180 return getListHeader()->getColumnFromID(col_id);
181 }
182
183
184 /*************************************************************************
185 Return the zero based index of the column whos header text matches
186 the specified text.
187 *************************************************************************/
getColumnWithHeaderText(const String & text) const188 uint MultiColumnList::getColumnWithHeaderText(const String& text) const
189 {
190 return getListHeader()->getColumnWithText(text);
191 }
192
193
194 /*************************************************************************
195 Return the total width of all column headers.
196 *************************************************************************/
getTotalColumnHeadersWidth(void) const197 UDim MultiColumnList::getTotalColumnHeadersWidth(void) const
198 {
199 const ListHeader* header = getListHeader();
200 UDim width(0,0);
201
202 for (uint i = 0; i < getColumnCount(); ++i)
203 width += header->getColumnWidth(i);
204
205 return width;
206 }
207
208
209 /*************************************************************************
210 Return the width of the specified column header.
211 *************************************************************************/
getColumnHeaderWidth(uint col_idx) const212 UDim MultiColumnList::getColumnHeaderWidth(uint col_idx) const
213 {
214 return getListHeader()->getColumnWidth(col_idx);
215 }
216
217
218 /*************************************************************************
219 Return the currently set sort direction.
220 *************************************************************************/
getSortDirection(void) const221 ListHeaderSegment::SortDirection MultiColumnList::getSortDirection(void) const
222 {
223 return getListHeader()->getSortDirection();
224 }
225
226
227 /*************************************************************************
228 Return the ListHeaderSegment object for the specified column
229 *************************************************************************/
getHeaderSegmentForColumn(uint col_idx) const230 ListHeaderSegment& MultiColumnList::getHeaderSegmentForColumn(uint col_idx) const
231 {
232 return getListHeader()->getSegmentFromColumn(col_idx);
233 }
234
235
236 /*************************************************************************
237 return index of row containing the given ListboxItem
238 *************************************************************************/
getItemRowIndex(const ListboxItem * item) const239 uint MultiColumnList::getItemRowIndex(const ListboxItem* item) const
240 {
241 for (uint i = 0; i < getRowCount(); ++i)
242 {
243 if (isListboxItemInRow(item, i))
244 {
245 return i;
246 }
247
248 }
249
250 // item is not attached to the list box, throw...
251 CEGUI_THROW(InvalidRequestException(
252 "the given ListboxItem is not attached to this MultiColumnList."));
253 }
254
255
256 /*************************************************************************
257 return the index of the column containing the given ListboxItem
258 *************************************************************************/
getItemColumnIndex(const ListboxItem * item) const259 uint MultiColumnList::getItemColumnIndex(const ListboxItem* item) const
260 {
261 for (uint i = 0; i < getColumnCount(); ++i)
262 {
263 if (isListboxItemInColumn(item, i))
264 {
265 return i;
266 }
267
268 }
269
270 // item is not attached to the list box, throw...
271 CEGUI_THROW(InvalidRequestException(
272 "the given ListboxItem is not attached to this MultiColumnList."));
273 }
274
275
276 /*************************************************************************
277 return an MCLGridRef for the given ListboxItem.
278 *************************************************************************/
getItemGridReference(const ListboxItem * item) const279 MCLGridRef MultiColumnList::getItemGridReference(const ListboxItem* item) const
280 {
281 return MCLGridRef(getItemRowIndex(item), getItemColumnIndex(item));
282 }
283
284
285 /*************************************************************************
286 return a ListboxItem pointer for the given reference.
287 May be NULL if position is blank. Throws on out of range.
288 *************************************************************************/
getItemAtGridReference(const MCLGridRef & grid_ref) const289 ListboxItem* MultiColumnList::getItemAtGridReference(const MCLGridRef& grid_ref) const
290 {
291 // check for invalid grid ref
292 if (grid_ref.column >= getColumnCount())
293 {
294 CEGUI_THROW(InvalidRequestException(
295 "the column given in the grid reference is out of range."));
296 }
297 else if (grid_ref.row >= getRowCount())
298 {
299 CEGUI_THROW(InvalidRequestException(
300 "the row given in the grid reference is out of range."));
301 }
302 else
303 {
304 return d_grid[grid_ref.row][grid_ref.column];
305 }
306
307 }
308
309
310 /*************************************************************************
311 return true if the given item is in the given column.
312 *************************************************************************/
isListboxItemInColumn(const ListboxItem * item,uint col_idx) const313 bool MultiColumnList::isListboxItemInColumn(const ListboxItem* item, uint col_idx) const
314 {
315 // check for invalid index
316 if (col_idx >= getColumnCount())
317 {
318 CEGUI_THROW(InvalidRequestException(
319 "the column index given is out of range."));
320 }
321 else
322 {
323 for (uint i = 0; i < getRowCount(); ++i)
324 {
325 if (d_grid[i][col_idx] == item)
326 {
327 return true;
328 }
329
330 }
331
332 // Item was not in the column.
333 return false;
334 }
335
336 }
337
338
339 /*************************************************************************
340 return true if the given item is in the given row.
341 *************************************************************************/
isListboxItemInRow(const ListboxItem * item,uint row_idx) const342 bool MultiColumnList::isListboxItemInRow(const ListboxItem* item, uint row_idx) const
343 {
344 // check for invalid index
345 if (row_idx >= getRowCount())
346 {
347 CEGUI_THROW(InvalidRequestException(
348 "the row index given is out of range."));
349 }
350 else
351 {
352 for (uint i = 0; i < getColumnCount(); ++i)
353 {
354 if (d_grid[row_idx][i] == item)
355 {
356 return true;
357 }
358
359 }
360
361 // Item was not in the row.
362 return false;
363 }
364
365 }
366
367
368 /*************************************************************************
369 return true if the given item is somewhere in the list box.
370 *************************************************************************/
isListboxItemInList(const ListboxItem * item) const371 bool MultiColumnList::isListboxItemInList(const ListboxItem* item) const
372 {
373 for (uint i = 0; i < getRowCount(); ++i)
374 {
375 for (uint j = 0; j < getColumnCount(); ++j)
376 {
377 if (d_grid[i][j] == item)
378 {
379 return true;
380 }
381
382 }
383
384 }
385
386 return false;
387 }
388
389
390 /*************************************************************************
391 Search the items in column 'col_idx' and return the first item after
392 'start_item' that matches the specified text. If 'start_item' is NULL
393 searching start at (and includes) the first item in the column.
394 *************************************************************************/
findColumnItemWithText(const String & text,uint col_idx,const ListboxItem * start_item) const395 ListboxItem* MultiColumnList::findColumnItemWithText(const String& text, uint col_idx, const ListboxItem* start_item) const
396 {
397 // ensure column is valid
398 if (col_idx >= getColumnCount())
399 {
400 CEGUI_THROW(InvalidRequestException(
401 "specified column index is out of range."));
402 }
403
404 // find start position for search
405 uint i = (!start_item) ? 0 : getItemRowIndex(start_item) + 1;
406
407 for ( ; i < getRowCount(); ++i)
408 {
409 // does this item match?
410 if (d_grid[i][col_idx]->getText() == text)
411 {
412 return d_grid[i][col_idx];
413 }
414
415 }
416
417 // no matching item.
418 return 0;
419 }
420
421
422 /*************************************************************************
423 Search the items in row 'row_idx' and return the first item after
424 'start_item' that matches the specified text. If 'start_item' is NULL
425 searching start at (and includes) the first item in the row.
426 *************************************************************************/
findRowItemWithText(const String & text,uint row_idx,const ListboxItem * start_item) const427 ListboxItem* MultiColumnList::findRowItemWithText(const String& text, uint row_idx, const ListboxItem* start_item) const
428 {
429 // ensure row is valid
430 if (row_idx >= getRowCount())
431 {
432 CEGUI_THROW(InvalidRequestException(
433 "specified row index is out of range."));
434 }
435
436 // find start position for search
437 uint i = (!start_item) ? 0 : getItemColumnIndex(start_item) + 1;
438
439 for ( ; i < getColumnCount(); ++i)
440 {
441 // does this item match?
442 if (d_grid[row_idx][i]->getText() == text)
443 {
444 return d_grid[row_idx][i];
445 }
446
447 }
448
449 // no matching item.
450 return 0;
451 }
452
453
454 /*************************************************************************
455 Search the list and return the first item after 'start_item' that
456 matches the specified text. If 'start_item' is NULL searching starts
457 at (and includes) the first item in the list.
458
459 Searching proceeds across the columns and down the rows.
460 *************************************************************************/
findListItemWithText(const String & text,const ListboxItem * start_item) const461 ListboxItem* MultiColumnList::findListItemWithText(const String& text, const ListboxItem* start_item) const
462 {
463 MCLGridRef startRef(0, 0);
464
465 // get position of start_item if it's not NULL
466 if (start_item)
467 {
468 startRef = getItemGridReference(start_item);
469 ++startRef.column;
470 }
471
472 // perform the search
473 for (uint i = startRef.row; i < getRowCount(); ++i)
474 {
475 for (uint j = startRef.column; j < getColumnCount(); ++j)
476 {
477 // does this item match?
478 if (d_grid[i][j]->getText() == text)
479 {
480 return d_grid[i][j];
481 }
482
483 }
484
485 }
486
487 // No match
488 return 0;
489 }
490
491
492 /*************************************************************************
493 Scan the list (across columns for each row) and return the first
494 selected item.
495 *************************************************************************/
getFirstSelectedItem(void) const496 ListboxItem* MultiColumnList::getFirstSelectedItem(void) const
497 {
498 return getNextSelected(0);
499 }
500
501
502 /*************************************************************************
503 Search the list and return the first item after 'start_item' that
504 is selected. If 'start_item' is NULL searching starts at (and
505 includes) the first item in the list.
506
507 Searching proceeds across the columns and down the rows.
508 *************************************************************************/
getNextSelected(const ListboxItem * start_item) const509 ListboxItem* MultiColumnList::getNextSelected(const ListboxItem* start_item) const
510 {
511 MCLGridRef startRef(0, 0);
512
513 // get position of start_item if it's not NULL
514 if (start_item)
515 {
516 startRef = getItemGridReference(start_item);
517 if (++startRef.column == getColumnCount())
518 {
519 startRef.column = 0;
520 ++startRef.row;
521 }
522 }
523
524 // perform the search
525 for (uint i = startRef.row; i < getRowCount(); ++i)
526 {
527 for (uint j = startRef.column; j < getColumnCount(); ++j)
528 {
529 // does this item match?
530 ListboxItem* item = d_grid[i][j];
531
532 if ((item != 0) && item->isSelected())
533 {
534 return d_grid[i][j];
535 }
536
537 }
538
539 }
540
541 // No match
542 return 0;
543 }
544
545
546 /*************************************************************************
547 Return the total number of selected items
548 *************************************************************************/
getSelectedCount(void) const549 uint MultiColumnList::getSelectedCount(void) const
550 {
551 uint count = 0;
552
553 for (uint i = 0; i < getRowCount(); ++i)
554 {
555 for (uint j = 0; j < getColumnCount(); ++j)
556 {
557 ListboxItem* item = d_grid[i][j];
558
559 if ((item != 0) && item->isSelected())
560 {
561 ++count;
562 }
563
564 }
565
566 }
567
568 return count;
569 }
570
571
572 /*************************************************************************
573 Return whether the item at the given grid position is selected.
574 *************************************************************************/
isItemSelected(const MCLGridRef & grid_ref) const575 bool MultiColumnList::isItemSelected(const MCLGridRef& grid_ref) const
576 {
577 ListboxItem* item = getItemAtGridReference(grid_ref);
578
579 if (item)
580 {
581 return item->isSelected();
582 }
583
584 // if no item exists here, then it can't be selected.
585 return false;
586 }
587
588
589 /*************************************************************************
590 Return the ID of the nominated selection column
591 *************************************************************************/
getNominatedSelectionColumnID(void) const592 uint MultiColumnList::getNominatedSelectionColumnID(void) const
593 {
594 if (getColumnCount() > 0)
595 {
596 return getListHeader()->getSegmentFromColumn(d_nominatedSelectCol).getID();
597 }
598 return 0;
599 }
600
601
602 /*************************************************************************
603 Return the nominated selection column
604 *************************************************************************/
getNominatedSelectionColumn(void) const605 uint MultiColumnList::getNominatedSelectionColumn(void) const
606 {
607 return d_nominatedSelectCol;
608 }
609
610
611 /*************************************************************************
612 Return the nominated selection row.
613 *************************************************************************/
getNominatedSelectionRow(void) const614 uint MultiColumnList::getNominatedSelectionRow(void) const
615 {
616 return d_nominatedSelectRow;
617 }
618
619
620 /*************************************************************************
621 Return the active selection mode.
622 *************************************************************************/
getSelectionMode(void) const623 MultiColumnList::SelectionMode MultiColumnList::getSelectionMode(void) const
624 {
625 return d_selectMode;
626 }
627
628
629 /*************************************************************************
630 Initialise the Window based object ready for use.
631 *************************************************************************/
initialiseComponents(void)632 void MultiColumnList::initialiseComponents(void)
633 {
634 // get the component sub-widgets
635 Scrollbar* vertScrollbar = getVertScrollbar();
636 Scrollbar* horzScrollbar = getHorzScrollbar();
637 ListHeader* header = getListHeader();
638
639 // subscribe some events
640 header->subscribeEvent(ListHeader::EventSegmentRenderOffsetChanged, Event::Subscriber(&CEGUI::MultiColumnList::handleHeaderScroll, this));
641 header->subscribeEvent(ListHeader::EventSegmentSequenceChanged, Event::Subscriber(&CEGUI::MultiColumnList::handleHeaderSegMove, this));
642 header->subscribeEvent(ListHeader::EventSegmentSized, Event::Subscriber(&CEGUI::MultiColumnList::handleColumnSizeChange, this));
643 header->subscribeEvent(ListHeader::EventSortColumnChanged , Event::Subscriber(&CEGUI::MultiColumnList::handleSortColumnChange, this));
644 header->subscribeEvent(ListHeader::EventSortDirectionChanged, Event::Subscriber(&CEGUI::MultiColumnList::handleSortDirectionChange, this));
645 header->subscribeEvent(ListHeader::EventSplitterDoubleClicked, Event::Subscriber(&CEGUI::MultiColumnList::handleHeaderSegDblClick, this));
646 horzScrollbar->subscribeEvent(Scrollbar::EventScrollPositionChanged, Event::Subscriber(&CEGUI::MultiColumnList::handleHorzScrollbar, this));
647 vertScrollbar->subscribeEvent(Scrollbar::EventScrollPositionChanged, Event::Subscriber(&CEGUI::MultiColumnList::handleVertScrollbar, this));
648
649
650 // final initialisation now widget is complete
651 setSortDirection(ListHeaderSegment::None);
652
653 // Perform initial layout
654 configureScrollbars();
655 performChildWindowLayout();
656 }
657
658
659 /*************************************************************************
660 Remove all items from the list.
661 *************************************************************************/
resetList(void)662 void MultiColumnList::resetList(void)
663 {
664 if (resetList_impl())
665 {
666 WindowEventArgs args(this);
667 onListContentsChanged(args);
668 }
669
670 }
671
672
673 /*************************************************************************
674 Add a column to the table.
675 *************************************************************************/
addColumn(const String & text,uint col_id,const UDim & width)676 void MultiColumnList::addColumn(const String& text, uint col_id, const UDim& width)
677 {
678 insertColumn(text, col_id, width, getColumnCount());
679 }
680
681 /*************************************************************************
682 Add a column to the table.
683 *************************************************************************/
addColumn(const String & value)684 void MultiColumnList::addColumn(const String& value)
685 {
686 size_t idstart = value.rfind("id:");
687 size_t wstart = value.rfind("width:");
688 size_t capstart = value.find("text:");
689
690 // some defaults in case of missing data
691 String caption, id("0"), width("{0.33,0}");
692
693 // extract the caption field
694 if (capstart != String::npos)
695 {
696 capstart = value.find_first_of(":") + 1;
697
698 if (wstart == String::npos)
699 if (idstart == String::npos)
700 caption = value.substr(capstart);
701 else
702 caption = value.substr(capstart, idstart - capstart);
703 else
704 caption = value.substr(capstart, wstart - capstart);
705
706 // trim trailing whitespace
707 TextUtils::trimTrailingChars(caption, TextUtils::DefaultWhitespace);
708 }
709
710 // extract the width field
711 if (wstart != String::npos)
712 {
713 width = value.substr(wstart);
714 width = width.substr(width.find_first_of("{"));
715 width = width.substr(0, width.find_last_of("}") + 1);
716 }
717
718 // extract the id field.
719 if (idstart != String::npos)
720 {
721 id = value.substr(idstart);
722 id = id.substr(id.find_first_of(":") + 1);
723 }
724
725 // add the column accordingly
726 addColumn(caption, PropertyHelper<uint>::fromString(id), PropertyHelper<UDim>::fromString(width));
727 }
728 /*************************************************************************
729 Insert a column into the table.
730 *************************************************************************/
insertColumn(const String & text,uint col_id,const UDim & width,uint position)731 void MultiColumnList::insertColumn(const String& text, uint col_id, const UDim& width, uint position)
732 {
733 // if position is out of range, add item to end of current columns.
734 if (position > getColumnCount())
735 {
736 position = getColumnCount();
737 }
738
739 // set-up the header for the new column.
740 getListHeader()->insertColumn(text, col_id, width, position);
741 ++d_columnCount;
742
743 // Set the font equal to that of our list
744 ListHeaderSegment& segment = getHeaderSegmentForColumn(position);
745 segment.setFont(d_font);
746 // ban properties from segment that we control from here.
747 segment.banPropertyFromXML("ID");
748 segment.banPropertyFromXML("Text");
749 segment.banPropertyFromXML("Font");
750
751 // Insert a blank entry at the appropriate position in each row.
752 for (uint i = 0; i < getRowCount(); ++i)
753 {
754 d_grid[i].d_items.insert(
755 d_grid[i].d_items.begin() + position,
756 static_cast<ListboxItem*>(0));
757 }
758
759 // update stored nominated selection column if that has changed.
760 if ((d_nominatedSelectCol >= position) && (getColumnCount() > 1))
761 {
762 d_nominatedSelectCol++;
763 }
764
765 // signal a change to the list contents
766 WindowEventArgs args(this);
767 onListContentsChanged(args);
768 }
769
770
771 /*************************************************************************
772 Remove a column from the table.
773 *************************************************************************/
removeColumn(uint col_idx)774 void MultiColumnList::removeColumn(uint col_idx)
775 {
776 // ensure index is valid, and throw if not.
777 if (col_idx >= getColumnCount())
778 {
779 CEGUI_THROW(InvalidRequestException(
780 "the specified column index is out of range."));
781 }
782 else
783 {
784 // update stored column index values
785 if (d_nominatedSelectCol == col_idx)
786 {
787 d_nominatedSelectCol = 0;
788 }
789
790 // remove the column from each row
791 for (uint i = 0; i < getRowCount(); ++i)
792 {
793 // extract the item pointer.
794 ListboxItem* item = d_grid[i][col_idx];
795
796 // remove the column entry from the row
797 d_grid[i].d_items.erase(d_grid[i].d_items.begin() + col_idx);
798
799 // delete the ListboxItem as needed.
800 if ((item != 0) && item->isAutoDeleted())
801 {
802 CEGUI_DELETE_AO item;
803 }
804
805 }
806
807 // remove header segment
808 getListHeader()->removeColumn(col_idx);
809 --d_columnCount;
810
811 // signal a change to the list contents
812 WindowEventArgs args(this);
813 onListContentsChanged(args);
814 }
815
816 }
817
818
819 /*************************************************************************
820 Remove a column from the table (via ID)
821 *************************************************************************/
removeColumnWithID(uint col_id)822 void MultiColumnList::removeColumnWithID(uint col_id)
823 {
824 removeColumn(getColumnWithID(col_id));
825 }
826
827
828 /*************************************************************************
829 Move a column within the table
830 *************************************************************************/
moveColumn(uint col_idx,uint position)831 void MultiColumnList::moveColumn(uint col_idx, uint position)
832 {
833 // move the segment on the header, events will ensure the items get moved also.
834 getListHeader()->moveColumn(col_idx, position);
835 }
836
837
838 /*************************************************************************
839 Move a column (with specified ID) within the table.
840 *************************************************************************/
moveColumnWithID(uint col_id,uint position)841 void MultiColumnList::moveColumnWithID(uint col_id, uint position)
842 {
843 moveColumn(getColumnWithID(col_id), position);
844 }
845
846
847 /*************************************************************************
848 Add a row to the bottom of the table
849 *************************************************************************/
addRow(uint row_id)850 uint MultiColumnList::addRow(uint row_id)
851 {
852 return addRow(0, 0, row_id);
853 }
854
855
856 /*************************************************************************
857 Add a row to the bottom of the table
858 *************************************************************************/
addRow(ListboxItem * item,uint col_id,uint row_id)859 uint MultiColumnList::addRow(ListboxItem* item, uint col_id, uint row_id)
860 {
861 uint col_idx = 0;
862
863 // Build the new row
864 ListRow row;
865 row.d_sortColumn = getSortColumn();
866 row.d_items.resize(getColumnCount(), 0);
867 row.d_rowID = row_id;
868
869 if (item)
870 {
871 // discover which column to initially set
872 col_idx = getColumnWithID(col_id);
873
874 // establish item ownership & enter item into column
875 item->setOwnerWindow(this);
876 row[col_idx] = item;
877 }
878
879 uint pos;
880
881 // if sorting is enabled, insert at an appropriate position
882 const ListHeaderSegment::SortDirection dir = getSortDirection();
883 if (dir != ListHeaderSegment::None)
884 {
885 // calculate where the row should be inserted
886 ListItemGrid::iterator ins_pos = dir == ListHeaderSegment::Descending ?
887 std::upper_bound(d_grid.begin(), d_grid.end(), row, pred_descend) :
888 std::upper_bound(d_grid.begin(), d_grid.end(), row);
889 // insert item and get final inserted position.
890 ListItemGrid::iterator final_pos = d_grid.insert(ins_pos, row);
891 // get final inserted position as an uint.
892 pos = (uint)std::distance(d_grid.begin(), final_pos);
893 }
894 // not sorted, just stick it on the end.
895 else
896 {
897 pos = getRowCount();
898 d_grid.push_back(row);
899 }
900
901 // signal a change to the list contents
902 WindowEventArgs args(this);
903 onListContentsChanged(args);
904
905 return pos;
906 }
907
908
909 /*************************************************************************
910 Insert a row into the table
911 *************************************************************************/
insertRow(uint row_idx,uint row_id)912 uint MultiColumnList::insertRow(uint row_idx, uint row_id)
913 {
914 return insertRow(0, 0, row_idx, row_id);
915 }
916
917
918 /*************************************************************************
919 Insert a row into the table
920 *************************************************************************/
insertRow(ListboxItem * item,uint col_id,uint row_idx,uint row_id)921 uint MultiColumnList::insertRow(ListboxItem* item, uint col_id, uint row_idx, uint row_id)
922 {
923 // if sorting is enabled, use add instead of insert
924 if (getSortDirection() != ListHeaderSegment::None)
925 {
926 return addRow(item, col_id, row_id);
927 }
928 else
929 {
930 // Build the new row (empty)
931 ListRow row;
932 row.d_sortColumn = getSortColumn();
933 row.d_items.resize(getColumnCount(), 0);
934 row.d_rowID = row_id;
935
936 // if row index is too big, just insert at end.
937 if (row_idx > getRowCount())
938 {
939 row_idx = getRowCount();
940 }
941
942 d_grid.insert(d_grid.begin() + row_idx, row);
943
944 // set the initial item in the new row
945 setItem(item, col_id, row_idx);
946
947 // signal a change to the list contents
948 WindowEventArgs args(this);
949 onListContentsChanged(args);
950
951 return row_idx;
952 }
953
954 }
955
956
957 /*************************************************************************
958 Remove a row from the list.
959 *************************************************************************/
removeRow(uint row_idx)960 void MultiColumnList::removeRow(uint row_idx)
961 {
962 // ensure row exists
963 if (row_idx >= getRowCount())
964 {
965 CEGUI_THROW(InvalidRequestException(
966 "The specified row index is out of range."));
967 }
968 else
969 {
970 // delete items we are supposed to
971 for (uint i = 0; i < getColumnCount(); ++i)
972 {
973 ListboxItem* item = d_grid[row_idx][i];
974
975 if ((item != 0) && item->isAutoDeleted())
976 {
977 CEGUI_DELETE_AO item;
978 }
979
980 }
981
982 // erase the row from the grid.
983 d_grid.erase(d_grid.begin() + row_idx);
984
985 // if we have erased the selection row, reset that to 0
986 if (d_nominatedSelectRow == row_idx)
987 {
988 d_nominatedSelectRow = 0;
989 }
990
991 // signal a change to the list contents
992 WindowEventArgs args(this);
993 onListContentsChanged(args);
994 }
995
996 }
997
998
999 /*************************************************************************
1000 Replace the item at grid-ref 'position' with 'item'.
1001 The old item is deleted according to the items auto-delete setting
1002 *************************************************************************/
setItem(ListboxItem * item,const MCLGridRef & position)1003 void MultiColumnList::setItem(ListboxItem* item, const MCLGridRef& position)
1004 {
1005 // validate grid ref
1006 if (position.column >= getColumnCount())
1007 {
1008 CEGUI_THROW(InvalidRequestException(
1009 "the specified column index is invalid."));
1010 }
1011 else if (position.row >= getRowCount())
1012 {
1013 CEGUI_THROW(InvalidRequestException(
1014 "the specified row index is invalid."));
1015 }
1016
1017 // delete old item as required
1018 ListboxItem* oldItem = d_grid[position.row][position.column];
1019
1020 if ((oldItem != 0) && oldItem->isAutoDeleted())
1021 {
1022 CEGUI_DELETE_AO oldItem;
1023 }
1024
1025 // set new item.
1026 if (item)
1027 item->setOwnerWindow(this);
1028
1029 d_grid[position.row][position.column] = item;
1030
1031
1032 // signal a change to the list contents
1033 WindowEventArgs args(this);
1034 onListContentsChanged(args);
1035 }
1036
1037
1038 /*************************************************************************
1039 Replace the item in row 'row_idx', in the column with ID 'col_id'
1040 with 'item'. The old item is deleted as required.
1041 *************************************************************************/
setItem(ListboxItem * item,uint col_id,uint row_idx)1042 void MultiColumnList::setItem(ListboxItem* item, uint col_id, uint row_idx)
1043 {
1044 setItem(item, MCLGridRef(row_idx, getColumnWithID(col_id)));
1045 }
1046
1047
1048 /*************************************************************************
1049 Set the current selection mode.
1050 *************************************************************************/
setSelectionMode(MultiColumnList::SelectionMode sel_mode)1051 void MultiColumnList::setSelectionMode(MultiColumnList::SelectionMode sel_mode)
1052 {
1053 if (d_selectMode != sel_mode)
1054 {
1055 d_selectMode = sel_mode;
1056
1057 clearAllSelections();
1058
1059 switch(d_selectMode)
1060 {
1061 case RowSingle:
1062 d_multiSelect = false;
1063 d_fullRowSelect = true;
1064 d_fullColSelect = false;
1065 d_useNominatedCol = false;
1066 d_useNominatedRow = false;
1067 break;
1068
1069 case RowMultiple:
1070 d_multiSelect = true;
1071 d_fullRowSelect = true;
1072 d_fullColSelect = false;
1073 d_useNominatedCol = false;
1074 d_useNominatedRow = false;
1075 break;
1076
1077 case CellSingle:
1078 d_multiSelect = false;
1079 d_fullRowSelect = false;
1080 d_fullColSelect = false;
1081 d_useNominatedCol = false;
1082 d_useNominatedRow = false;
1083 break;
1084
1085 case CellMultiple:
1086 d_multiSelect = true;
1087 d_fullRowSelect = false;
1088 d_fullColSelect = false;
1089 d_useNominatedCol = false;
1090 d_useNominatedRow = false;
1091 break;
1092
1093 case NominatedColumnSingle:
1094 d_multiSelect = false;
1095 d_fullRowSelect = false;
1096 d_fullColSelect = false;
1097 d_useNominatedCol = true;
1098 d_useNominatedRow = false;
1099 break;
1100
1101 case NominatedColumnMultiple:
1102 d_multiSelect = true;
1103 d_fullRowSelect = false;
1104 d_fullColSelect = false;
1105 d_useNominatedCol = true;
1106 d_useNominatedRow = false;
1107 break;
1108
1109 case ColumnSingle:
1110 d_multiSelect = false;
1111 d_fullRowSelect = false;
1112 d_fullColSelect = true;
1113 d_useNominatedCol = false;
1114 d_useNominatedRow = false;
1115 break;
1116
1117 case ColumnMultiple:
1118 d_multiSelect = true;
1119 d_fullRowSelect = false;
1120 d_fullColSelect = true;
1121 d_useNominatedCol = false;
1122 d_useNominatedRow = false;
1123 break;
1124
1125 case NominatedRowSingle:
1126 d_multiSelect = false;
1127 d_fullRowSelect = false;
1128 d_fullColSelect = false;
1129 d_useNominatedCol = false;
1130 d_useNominatedRow = true;
1131 break;
1132
1133 case NominatedRowMultiple:
1134 d_multiSelect = true;
1135 d_fullRowSelect = false;
1136 d_fullColSelect = false;
1137 d_useNominatedCol = false;
1138 d_useNominatedRow = true;
1139 break;
1140
1141 default:
1142 CEGUI_THROW(InvalidRequestException(
1143 "invalid or unknown SelectionMode value supplied."));
1144 break;
1145
1146 }
1147
1148 // Fire event.
1149 WindowEventArgs args(this);
1150 onSelectionModeChanged(args);
1151 }
1152
1153 }
1154
1155
1156 /*************************************************************************
1157 Set the nominated column for 'NominatedColumn*' selections (by ID)
1158 *************************************************************************/
setNominatedSelectionColumnID(uint col_id)1159 void MultiColumnList::setNominatedSelectionColumnID(uint col_id)
1160 {
1161 setNominatedSelectionColumn(getColumnWithID(col_id));
1162 }
1163
1164
1165 /*************************************************************************
1166 Set the nominated column for 'NominatedColumn*' selections (by Index)
1167 *************************************************************************/
setNominatedSelectionColumn(uint col_idx)1168 void MultiColumnList::setNominatedSelectionColumn(uint col_idx)
1169 {
1170 if (d_nominatedSelectCol != col_idx)
1171 {
1172 clearAllSelections();
1173
1174 d_nominatedSelectCol = col_idx;
1175
1176 // Fire event.
1177 WindowEventArgs args(this);
1178 onNominatedSelectColumnChanged(args);
1179 }
1180
1181 }
1182
1183
1184 /*************************************************************************
1185 Set the nominated row for 'NominatedRow*' selections
1186 *************************************************************************/
setNominatedSelectionRow(uint row_idx)1187 void MultiColumnList::setNominatedSelectionRow(uint row_idx)
1188 {
1189 if (d_nominatedSelectRow != row_idx)
1190 {
1191 clearAllSelections();
1192
1193 d_nominatedSelectRow = row_idx;
1194
1195 // Fire event.
1196 WindowEventArgs args(this);
1197 onNominatedSelectRowChanged(args);
1198 }
1199
1200 }
1201
1202
1203 /*************************************************************************
1204 Set the current sort direction.
1205 *************************************************************************/
setSortDirection(ListHeaderSegment::SortDirection direction)1206 void MultiColumnList::setSortDirection(ListHeaderSegment::SortDirection direction)
1207 {
1208 if (getSortDirection() != direction)
1209 {
1210 // set the sort direction on the header, events will make sure everything else is updated.
1211 getListHeader()->setSortDirection(direction);
1212 }
1213
1214 }
1215
1216
1217 /*************************************************************************
1218 Set the current sort column (by index)
1219 *************************************************************************/
setSortColumn(uint col_idx)1220 void MultiColumnList::setSortColumn(uint col_idx)
1221 {
1222 if (getSortColumn() != col_idx)
1223 {
1224 // set the sort column on the header, events will make sure everything else is updated.
1225 getListHeader()->setSortColumn(col_idx);
1226 }
1227
1228 }
1229
1230
1231 /*************************************************************************
1232 Set the current sort column (by ID)
1233 *************************************************************************/
setSortColumnByID(uint col_id)1234 void MultiColumnList::setSortColumnByID(uint col_id)
1235 {
1236 ListHeader* header = getListHeader();
1237
1238 if (header->getSegmentFromColumn(getSortColumn()).getID() != col_id)
1239 {
1240 // set the sort column on the header, events will make sure everything else is updated.
1241 header->setSortColumnFromID(col_id);
1242 }
1243
1244 }
1245
1246
1247 /*************************************************************************
1248 Enable / Disable forced display of the vertical scroll bar
1249 *************************************************************************/
setShowVertScrollbar(bool setting)1250 void MultiColumnList::setShowVertScrollbar(bool setting)
1251 {
1252 if (d_forceVertScroll != setting)
1253 {
1254 d_forceVertScroll = setting;
1255
1256 configureScrollbars();
1257
1258 // Event firing.
1259 WindowEventArgs args(this);
1260 onVertScrollbarModeChanged(args);
1261 }
1262
1263 }
1264
1265
1266 /*************************************************************************
1267 Enable / Disable forced display of the horizontal scroll bar
1268 *************************************************************************/
setShowHorzScrollbar(bool setting)1269 void MultiColumnList::setShowHorzScrollbar(bool setting)
1270 {
1271 if (d_forceHorzScroll != setting)
1272 {
1273 d_forceHorzScroll = setting;
1274
1275 configureScrollbars();
1276
1277 // Event firing.
1278 WindowEventArgs args(this);
1279 onHorzScrollbarModeChanged(args);
1280 }
1281
1282 }
1283
1284
1285 /*************************************************************************
1286 Clear the selected state from all currently selected items.
1287 *************************************************************************/
clearAllSelections(void)1288 void MultiColumnList::clearAllSelections(void)
1289 {
1290 // only fire events and update if we actually made any changes
1291 if (clearAllSelections_impl())
1292 {
1293 // Fire event.
1294 WindowEventArgs args(this);
1295 onSelectionChanged(args);
1296 }
1297
1298
1299 }
1300
1301
1302 /*************************************************************************
1303 Set the selected state of 'item' (must be attached to the list box)
1304 *************************************************************************/
setItemSelectState(ListboxItem * item,bool state)1305 void MultiColumnList::setItemSelectState(ListboxItem* item, bool state)
1306 {
1307 setItemSelectState(getItemGridReference(item), state);
1308 }
1309
1310
1311 /*************************************************************************
1312 Set the selected state of the item at 'grid_ref'.
1313 *************************************************************************/
setItemSelectState(const MCLGridRef & grid_ref,bool state)1314 void MultiColumnList::setItemSelectState(const MCLGridRef& grid_ref, bool state)
1315 {
1316 if (setItemSelectState_impl(grid_ref, state))
1317 {
1318 // Fire event.
1319 WindowEventArgs args(this);
1320 onSelectionChanged(args);
1321 }
1322
1323 }
1324
1325
1326 /*************************************************************************
1327 Update the list box internal state after attached items have been
1328 externally modified.
1329 *************************************************************************/
handleUpdatedItemData(void)1330 void MultiColumnList::handleUpdatedItemData(void)
1331 {
1332 resortList();
1333 configureScrollbars();
1334 invalidate();
1335 }
1336
1337
1338 /*************************************************************************
1339 Set the width of the specified column header (and therefore the
1340 column itself).
1341 *************************************************************************/
setColumnHeaderWidth(uint col_idx,const UDim & width)1342 void MultiColumnList::setColumnHeaderWidth(uint col_idx, const UDim& width)
1343 {
1344 getListHeader()->setColumnWidth(col_idx, width);
1345 }
1346
1347
1348 /*************************************************************************
1349 display required integrated scroll bars according to current state
1350 of the list box and update their values.
1351 *************************************************************************/
configureScrollbars(void)1352 void MultiColumnList::configureScrollbars(void)
1353 {
1354 Scrollbar* vertScrollbar = getVertScrollbar();
1355 Scrollbar* horzScrollbar = getHorzScrollbar();
1356 float totalHeight = getTotalRowsHeight();
1357 float fullWidth = getListHeader()->getTotalSegmentsPixelExtent();
1358
1359 //
1360 // First show or hide the scroll bars as needed (or requested)
1361 //
1362 // show or hide vertical scroll bar as required (or as specified by option)
1363 if ((totalHeight > getListRenderArea().getHeight()) || d_forceVertScroll)
1364 {
1365 vertScrollbar->show();
1366
1367 // show or hide horizontal scroll bar as required (or as specified by option)
1368 if ((fullWidth > getListRenderArea().getWidth()) || d_forceHorzScroll)
1369 {
1370 horzScrollbar->show();
1371 }
1372 else
1373 {
1374 horzScrollbar->hide();
1375 }
1376
1377 }
1378 else
1379 {
1380 // show or hide horizontal scroll bar as required (or as specified by option)
1381 if ((fullWidth > getListRenderArea().getWidth()) || d_forceHorzScroll)
1382 {
1383 horzScrollbar->show();
1384
1385 // show or hide vertical scroll bar as required (or as specified by option)
1386 if ((totalHeight > getListRenderArea().getHeight()) || d_forceVertScroll)
1387 {
1388 vertScrollbar->show();
1389 }
1390 else
1391 {
1392 vertScrollbar->hide();
1393 }
1394
1395 }
1396 else
1397 {
1398 vertScrollbar->hide();
1399 horzScrollbar->hide();
1400 }
1401
1402 }
1403
1404 //
1405 // Set up scroll bar values
1406 //
1407 Rectf renderArea(getListRenderArea());
1408
1409 vertScrollbar->setDocumentSize(totalHeight);
1410 vertScrollbar->setPageSize(renderArea.getHeight());
1411 vertScrollbar->setStepSize(ceguimax(1.0f, renderArea.getHeight() / 10.0f));
1412 vertScrollbar->setScrollPosition(vertScrollbar->getScrollPosition());
1413
1414 horzScrollbar->setDocumentSize(fullWidth);
1415 horzScrollbar->setPageSize(renderArea.getWidth());
1416 horzScrollbar->setStepSize(ceguimax(1.0f, renderArea.getWidth() / 10.0f));
1417 horzScrollbar->setScrollPosition(horzScrollbar->getScrollPosition());
1418 }
1419
1420
1421 /*************************************************************************
1422 select all strings between positions 'start' and 'end'. (inclusive)
1423 *************************************************************************/
selectRange(const MCLGridRef & start,const MCLGridRef & end)1424 bool MultiColumnList::selectRange(const MCLGridRef& start, const MCLGridRef& end)
1425 {
1426 MCLGridRef tmpStart(start);
1427 MCLGridRef tmpEnd(end);
1428
1429 // ensure start is before end
1430 if (tmpStart.column > tmpEnd.column)
1431 {
1432 tmpStart.column = tmpEnd.column;
1433 tmpEnd.column = start.column;
1434 }
1435
1436 if (tmpStart.row > tmpEnd.row)
1437 {
1438 tmpStart.row = tmpEnd.row;
1439 tmpEnd.row = start.row;
1440 }
1441
1442 bool modified = false;
1443
1444 // loop through all items selecting them.
1445 for (uint i = tmpStart.row; i <= tmpEnd.row; ++i)
1446 {
1447 for (uint j = tmpStart.column; j <= tmpEnd.column; ++j)
1448 {
1449 ListboxItem* item = d_grid[i][j];
1450
1451 if (item)
1452 {
1453 modified |= setItemSelectState_impl(getItemGridReference(item), true);
1454 }
1455
1456 }
1457
1458 }
1459
1460 return modified;
1461 }
1462
1463
1464 /*************************************************************************
1465 Return the sum of all row heights
1466 *************************************************************************/
getTotalRowsHeight(void) const1467 float MultiColumnList::getTotalRowsHeight(void) const
1468 {
1469 float height = 0.0f;
1470
1471 for (uint i = 0; i < getRowCount(); ++i)
1472 {
1473 height += getHighestRowItemHeight(i);
1474 }
1475
1476 return height;
1477 }
1478
1479
1480 /*************************************************************************
1481 Return the width of the widest item in the given column
1482 *************************************************************************/
getWidestColumnItemWidth(uint col_idx) const1483 float MultiColumnList::getWidestColumnItemWidth(uint col_idx) const
1484 {
1485 if (col_idx >= getColumnCount())
1486 {
1487 CEGUI_THROW(InvalidRequestException(
1488 "specified column is out of range."));
1489 }
1490 else
1491 {
1492 float width = 0.0f;
1493
1494 // check each item in the column
1495 for (uint i = 0; i < getRowCount(); ++i)
1496 {
1497 ListboxItem* item = d_grid[i][col_idx];
1498
1499 // if the slot has an item in it
1500 if (item)
1501 {
1502 Sizef sz(item->getPixelSize());
1503
1504 // see if this item is wider than the previous widest
1505 if (sz.d_width > width)
1506 {
1507 // update current widest
1508 width = sz.d_width;
1509 }
1510
1511 }
1512
1513 }
1514
1515 // return the widest item.
1516 return width;
1517 }
1518
1519 }
1520
1521
1522 /*************************************************************************
1523 Return the height of the highest item in the given row.
1524 *************************************************************************/
getHighestRowItemHeight(uint row_idx) const1525 float MultiColumnList::getHighestRowItemHeight(uint row_idx) const
1526 {
1527 if (row_idx >= getRowCount())
1528 {
1529 CEGUI_THROW(InvalidRequestException(
1530 "specified row is out of range."));
1531 }
1532 else
1533 {
1534 float height = 0.0f;
1535
1536 // check each item in the column
1537 for (uint i = 0; i < getColumnCount(); ++i)
1538 {
1539 ListboxItem* item = d_grid[row_idx][i];
1540
1541 // if the slot has an item in it
1542 if (item)
1543 {
1544 Sizef sz(item->getPixelSize());
1545
1546 // see if this item is higher than the previous highest
1547 if (sz.d_height > height)
1548 {
1549 // update current highest
1550 height = sz.d_height;
1551 }
1552
1553 }
1554
1555 }
1556
1557 // return the hightest item.
1558 return height;
1559 }
1560
1561 }
1562
1563
1564 /*************************************************************************
1565 Clear the selected state for all items (implementation)
1566 *************************************************************************/
clearAllSelections_impl(void)1567 bool MultiColumnList::clearAllSelections_impl(void)
1568 {
1569 // flag used so we can track if we did anything.
1570 bool modified = false;
1571
1572 for (uint i = 0; i < getRowCount(); ++i)
1573 {
1574 for (uint j = 0; j < getColumnCount(); ++j)
1575 {
1576 ListboxItem* item = d_grid[i][j];
1577
1578 // if slot has an item, and item is selected
1579 if ((item != 0) && item->isSelected())
1580 {
1581 // clear selection state and set modified flag
1582 item->setSelected(false);
1583 modified = true;
1584 }
1585
1586 }
1587
1588 }
1589
1590 // signal whether or not we did anything.
1591 return modified;
1592 }
1593
1594
1595 /*************************************************************************
1596 Return the ListboxItem under the given window local pixel co-ordinate.
1597 *************************************************************************/
getItemAtPoint(const Vector2f & pt) const1598 ListboxItem* MultiColumnList::getItemAtPoint(const Vector2f& pt) const
1599 {
1600 const ListHeader* header = getListHeader();
1601 Rectf listArea(getListRenderArea());
1602
1603 float y = listArea.d_min.d_y - getVertScrollbar()->getScrollPosition();
1604 float x = listArea.d_min.d_x - getHorzScrollbar()->getScrollPosition();
1605
1606 for (uint i = 0; i < getRowCount(); ++i)
1607 {
1608 y += getHighestRowItemHeight(i);
1609
1610 // have we located the row?
1611 if (pt.d_y < y)
1612 {
1613 // scan across to find column that was clicked
1614 for (uint j = 0; j < getColumnCount(); ++j)
1615 {
1616 const ListHeaderSegment& seg = header->getSegmentFromColumn(j);
1617 x += CoordConverter::asAbsolute(seg.getWidth(), header->getPixelSize().d_width);
1618
1619 // was this the column?
1620 if (pt.d_x < x)
1621 {
1622 // return contents of grid element that was clicked.
1623 return d_grid[i][j];
1624 }
1625 }
1626 }
1627 }
1628
1629 return 0;
1630 }
1631
1632
1633 /*************************************************************************
1634 Set select state for the given item. This appropriately selects other
1635 items depending upon the select mode. Returns true if something is
1636 changed, else false.
1637 *************************************************************************/
setItemSelectState_impl(const MCLGridRef grid_ref,bool state)1638 bool MultiColumnList::setItemSelectState_impl(const MCLGridRef grid_ref, bool state)
1639 {
1640 // validate grid ref
1641 if (grid_ref.column >= getColumnCount())
1642 {
1643 CEGUI_THROW(InvalidRequestException(
1644 "the specified column index is invalid."));
1645 }
1646 else if (grid_ref.row >= getRowCount())
1647 {
1648 CEGUI_THROW(InvalidRequestException(
1649 "the specified row index is invalid."));
1650 }
1651
1652 // only do this if the setting is changing
1653 if (d_grid[grid_ref.row][grid_ref.column]->isSelected() != state)
1654 {
1655 // if using nominated selection row and/ or column, check that they match.
1656 if ((!d_useNominatedCol || (d_nominatedSelectCol == grid_ref.column)) &&
1657 (!d_useNominatedRow || (d_nominatedSelectRow == grid_ref.row)))
1658 {
1659 // clear current selection if not multi-select box
1660 if (state && (!d_multiSelect))
1661 {
1662 clearAllSelections_impl();
1663 }
1664
1665 // full row?
1666 if (d_fullRowSelect)
1667 {
1668 // clear selection on all items in the row
1669 setSelectForItemsInRow(grid_ref.row, state);
1670 }
1671 // full column?
1672 else if (d_fullColSelect)
1673 {
1674 // clear selection on all items in the column
1675 setSelectForItemsInColumn(grid_ref.column, state);
1676
1677 }
1678 // single item to be affected
1679 else
1680 {
1681 d_grid[grid_ref.row][grid_ref.column]->setSelected(state);
1682 }
1683
1684 return true;
1685 }
1686
1687 }
1688
1689 return false;
1690 }
1691
1692
1693 /*************************************************************************
1694 Select all items in the given row
1695 *************************************************************************/
setSelectForItemsInRow(uint row_idx,bool state)1696 void MultiColumnList::setSelectForItemsInRow(uint row_idx, bool state)
1697 {
1698 for (uint i = 0; i < getColumnCount(); ++i)
1699 {
1700 ListboxItem* item = d_grid[row_idx][i];
1701
1702 if (item)
1703 {
1704 item->setSelected(state);
1705 }
1706
1707 }
1708
1709 }
1710
1711
1712 /*************************************************************************
1713 Select all items in the given column
1714 *************************************************************************/
setSelectForItemsInColumn(uint col_idx,bool state)1715 void MultiColumnList::setSelectForItemsInColumn(uint col_idx, bool state)
1716 {
1717 for (uint i = 0; i < getRowCount(); ++i)
1718 {
1719 ListboxItem* item = d_grid[i][col_idx];
1720
1721 if (item)
1722 {
1723 item->setSelected(state);
1724 }
1725
1726 }
1727
1728 }
1729
1730 /*************************************************************************
1731 Move the column at index 'col_idx' so it is at index 'position'.
1732
1733 Implementation version which does not move the header segment
1734 (since that may have already happned).
1735 *************************************************************************/
moveColumn_impl(uint col_idx,uint position)1736 void MultiColumnList::moveColumn_impl(uint col_idx, uint position)
1737 {
1738 // ensure index is valid, and throw if not.
1739 if (col_idx >= getColumnCount())
1740 {
1741 CEGUI_THROW(InvalidRequestException(
1742 "the specified source column index is out of range."));
1743 }
1744 else
1745 {
1746 // if position is too big, insert at end.
1747 if (position > getColumnCount())
1748 {
1749 position = getColumnCount();
1750 }
1751
1752 // update select column index value if needed
1753 if (d_nominatedSelectCol == col_idx)
1754 {
1755 d_nominatedSelectCol = position;
1756 }
1757 else if ((col_idx < d_nominatedSelectCol) && (position >= d_nominatedSelectCol))
1758 {
1759 d_nominatedSelectCol--;
1760 }
1761 else if ((col_idx > d_nominatedSelectCol) && (position <= d_nominatedSelectCol))
1762 {
1763 d_nominatedSelectCol++;
1764 }
1765
1766 // move column entry in each row.
1767 for (uint i = 0; i < getRowCount(); ++i)
1768 {
1769 // store entry.
1770 ListboxItem* item = d_grid[i][col_idx];
1771
1772 // remove the original column for this row.
1773 d_grid[i].d_items.erase(d_grid[i].d_items.begin() + col_idx);
1774
1775 // insert entry at its new position
1776 d_grid[i].d_items.insert(d_grid[i].d_items.begin() + position, item);
1777 }
1778
1779 }
1780
1781 }
1782
1783
1784 /*************************************************************************
1785 Handler called when selection mode changes
1786 *************************************************************************/
onSelectionModeChanged(WindowEventArgs & e)1787 void MultiColumnList::onSelectionModeChanged(WindowEventArgs& e)
1788 {
1789 fireEvent(EventSelectionModeChanged, e, EventNamespace);
1790 }
1791
1792
1793 /*************************************************************************
1794 Handler called when the nominated selection column changes
1795 *************************************************************************/
onNominatedSelectColumnChanged(WindowEventArgs & e)1796 void MultiColumnList::onNominatedSelectColumnChanged(WindowEventArgs& e)
1797 {
1798 fireEvent(EventNominatedSelectColumnChanged, e, EventNamespace);
1799 }
1800
1801
1802 /*************************************************************************
1803 Handler called when nominated selection row changes
1804 *************************************************************************/
onNominatedSelectRowChanged(WindowEventArgs & e)1805 void MultiColumnList::onNominatedSelectRowChanged(WindowEventArgs& e)
1806 {
1807 fireEvent(EventNominatedSelectRowChanged, e, EventNamespace);
1808 }
1809
1810
1811 /*************************************************************************
1812 Handler called when vertcial scroll bar 'force' mode changes
1813 *************************************************************************/
onVertScrollbarModeChanged(WindowEventArgs & e)1814 void MultiColumnList::onVertScrollbarModeChanged(WindowEventArgs& e)
1815 {
1816 fireEvent(EventVertScrollbarModeChanged, e, EventNamespace);
1817 }
1818
1819
1820 /*************************************************************************
1821 Handler called when horizontal scroll bar 'force' mode changes
1822 *************************************************************************/
onHorzScrollbarModeChanged(WindowEventArgs & e)1823 void MultiColumnList::onHorzScrollbarModeChanged(WindowEventArgs& e)
1824 {
1825 fireEvent(EventHorzScrollbarModeChanged, e, EventNamespace);
1826 }
1827
1828
1829 /*************************************************************************
1830 Handler called when the current selection in the list changes.
1831 *************************************************************************/
onSelectionChanged(WindowEventArgs & e)1832 void MultiColumnList::onSelectionChanged(WindowEventArgs& e)
1833 {
1834 invalidate();
1835 fireEvent(EventSelectionChanged, e, EventNamespace);
1836 }
1837
1838
1839 /*************************************************************************
1840 Handler called when list contents changes
1841 *************************************************************************/
onListContentsChanged(WindowEventArgs & e)1842 void MultiColumnList::onListContentsChanged(WindowEventArgs& e)
1843 {
1844 configureScrollbars();
1845 invalidate();
1846 fireEvent(EventListContentsChanged, e, EventNamespace);
1847 }
1848
1849
1850 /*************************************************************************
1851 Handler called when the sort column changes
1852 *************************************************************************/
onSortColumnChanged(WindowEventArgs & e)1853 void MultiColumnList::onSortColumnChanged(WindowEventArgs& e)
1854 {
1855 invalidate();
1856 fireEvent(EventSortColumnChanged, e, EventNamespace);
1857 }
1858
1859
1860 /*************************************************************************
1861 Handler called when the sort direction changes
1862 *************************************************************************/
onSortDirectionChanged(WindowEventArgs & e)1863 void MultiColumnList::onSortDirectionChanged(WindowEventArgs& e)
1864 {
1865 invalidate();
1866 fireEvent(EventSortDirectionChanged, e, EventNamespace);
1867 }
1868
1869
1870 /*************************************************************************
1871 Handler called when a column is sized
1872 *************************************************************************/
onListColumnSized(WindowEventArgs & e)1873 void MultiColumnList::onListColumnSized(WindowEventArgs& e)
1874 {
1875 configureScrollbars();
1876 invalidate();
1877 fireEvent(EventListColumnSized, e, EventNamespace);
1878 }
1879
1880
1881 /*************************************************************************
1882 Handler called when a column is moved
1883 *************************************************************************/
onListColumnMoved(WindowEventArgs & e)1884 void MultiColumnList::onListColumnMoved(WindowEventArgs& e)
1885 {
1886 invalidate();
1887 fireEvent(EventListColumnMoved, e, EventNamespace);
1888 }
1889
1890 /*************************************************************************
1891 Handler for when widget font is changed
1892 *************************************************************************/
onFontChanged(WindowEventArgs & e)1893 void MultiColumnList::onFontChanged(WindowEventArgs& e)
1894 {
1895 // Propagate to children
1896 // Set the font equal to that of our list
1897 for (uint col = 0; col < getColumnCount(); col++)
1898 {
1899 getHeaderSegmentForColumn(col).setFont(d_font);
1900 }
1901
1902 // Call base class handler
1903 Window::onFontChanged(e);
1904 }
1905
1906 /*************************************************************************
1907 Handler for when we are sized
1908 *************************************************************************/
onSized(ElementEventArgs & e)1909 void MultiColumnList::onSized(ElementEventArgs& e)
1910 {
1911 // base class handling
1912 Window::onSized(e);
1913
1914 configureScrollbars();
1915
1916 ++e.handled;
1917 }
1918
1919
1920 /*************************************************************************
1921 Handler for when mouse button is pressed
1922 *************************************************************************/
onMouseButtonDown(MouseEventArgs & e)1923 void MultiColumnList::onMouseButtonDown(MouseEventArgs& e)
1924 {
1925 // base class processing
1926 Window::onMouseButtonDown(e);
1927
1928 if (e.button == LeftButton)
1929 {
1930 bool modified = false;
1931
1932 Vector2f localPos(CoordConverter::screenToWindow(*this, e.position));
1933 ListboxItem* item = getItemAtPoint(localPos);
1934
1935 if (item)
1936 {
1937 // clear old selections if no control key is pressed or if multi-select is off
1938 if (!(e.sysKeys & Control) || !d_multiSelect)
1939 {
1940 modified = clearAllSelections_impl();
1941 }
1942
1943 modified = true;
1944
1945 // select range or item, depending upon keys and last selected item
1946 if (((e.sysKeys & Shift) && (d_lastSelected != 0)) && d_multiSelect)
1947 {
1948 modified |= selectRange(getItemGridReference(item), getItemGridReference(d_lastSelected));
1949 }
1950 else
1951 {
1952 modified |= setItemSelectState_impl(getItemGridReference(item), item->isSelected() ^ true);
1953 }
1954
1955 // update last selected item
1956 d_lastSelected = item->isSelected() ? item : 0;
1957 }
1958
1959 // fire event if needed
1960 if (modified)
1961 {
1962 WindowEventArgs args(this);
1963 onSelectionChanged(args);
1964 }
1965
1966 ++e.handled;
1967 }
1968
1969 }
1970
1971
1972 /*************************************************************************
1973 Handler for mouse wheel changes
1974 *************************************************************************/
onMouseWheel(MouseEventArgs & e)1975 void MultiColumnList::onMouseWheel(MouseEventArgs& e)
1976 {
1977 // base class processing.
1978 Window::onMouseWheel(e);
1979
1980 Scrollbar* vertScrollbar = getVertScrollbar();
1981 Scrollbar* horzScrollbar = getHorzScrollbar();
1982
1983 if (vertScrollbar->isEffectiveVisible() && (vertScrollbar->getDocumentSize() > vertScrollbar->getPageSize()))
1984 {
1985 vertScrollbar->setScrollPosition(vertScrollbar->getScrollPosition() + vertScrollbar->getStepSize() * -e.wheelChange);
1986 }
1987 else if (horzScrollbar->isEffectiveVisible() && (horzScrollbar->getDocumentSize() > horzScrollbar->getPageSize()))
1988 {
1989 horzScrollbar->setScrollPosition(horzScrollbar->getScrollPosition() + horzScrollbar->getStepSize() * -e.wheelChange);
1990 }
1991
1992 ++e.handled;
1993 }
1994
1995
1996 /*************************************************************************
1997 Event handler for header offset changes (scrolling)
1998 *************************************************************************/
handleHeaderScroll(const EventArgs &)1999 bool MultiColumnList::handleHeaderScroll(const EventArgs&)
2000 {
2001 // grab the header scroll value, convert to pixels, and set the scroll bar to match.
2002 getHorzScrollbar()->setScrollPosition(getListHeader()->getSegmentOffset());
2003
2004 return true;
2005 }
2006
2007
2008 /*************************************************************************
2009 Event handler for drag & drop of header segments
2010 *************************************************************************/
handleHeaderSegMove(const EventArgs & e)2011 bool MultiColumnList::handleHeaderSegMove(const EventArgs& e)
2012 {
2013 moveColumn_impl(((HeaderSequenceEventArgs&)e).d_oldIdx, ((HeaderSequenceEventArgs&)e).d_newIdx);
2014
2015 // signal change to our clients
2016 WindowEventArgs args(this);
2017 onListColumnMoved(args);
2018
2019 return true;
2020 }
2021
2022
2023
2024 /*************************************************************************
2025 Event handler for when header segment size (column width) changes
2026 *************************************************************************/
handleColumnSizeChange(const EventArgs &)2027 bool MultiColumnList::handleColumnSizeChange(const EventArgs&)
2028 {
2029 configureScrollbars();
2030
2031 // signal change to our clients
2032 WindowEventArgs args(this);
2033 onListColumnSized(args);
2034
2035 return true;
2036 }
2037
2038
2039 /*************************************************************************
2040 Event handler for when horizontal scroll bar is moved.
2041 *************************************************************************/
handleHorzScrollbar(const EventArgs &)2042 bool MultiColumnList::handleHorzScrollbar(const EventArgs&)
2043 {
2044 // set header offset to match scroll position
2045 getListHeader()->setSegmentOffset(getHorzScrollbar()->getScrollPosition());
2046 invalidate();
2047 return true;
2048 }
2049
2050 /*************************************************************************
2051 Event handler for when vertical scroll bar is moved.
2052 *************************************************************************/
handleVertScrollbar(const EventArgs &)2053 bool MultiColumnList::handleVertScrollbar(const EventArgs&)
2054 {
2055 invalidate();
2056 return true;
2057 }
2058
2059
2060 /*************************************************************************
2061 Handler for when sort column in header is changed
2062 *************************************************************************/
handleSortColumnChange(const EventArgs &)2063 bool MultiColumnList::handleSortColumnChange(const EventArgs&)
2064 {
2065 uint col = getSortColumn();
2066
2067 // set new sort column on all rows
2068 for (uint i = 0; i < getRowCount(); ++i)
2069 {
2070 d_grid[i].d_sortColumn = col;
2071 }
2072
2073 resortList();
2074
2075 // signal change to our clients
2076 WindowEventArgs args(this);
2077 onSortColumnChanged(args);
2078
2079 return true;
2080 }
2081
2082
2083 /*************************************************************************
2084 Handler for when sort direction in header is changed
2085 *************************************************************************/
handleSortDirectionChange(const EventArgs &)2086 bool MultiColumnList::handleSortDirectionChange(const EventArgs&)
2087 {
2088 resortList();
2089 // signal change to our clients
2090 WindowEventArgs args(this);
2091 onSortDirectionChanged(args);
2092
2093 return true;
2094 }
2095
2096
2097 /*************************************************************************
2098 Handler for when user double-clicks on header segment splitter
2099 *************************************************************************/
handleHeaderSegDblClick(const EventArgs & e)2100 bool MultiColumnList::handleHeaderSegDblClick(const EventArgs& e)
2101 {
2102 // get the column index for the segment that was double-clicked
2103 uint col = getListHeader()->getColumnFromSegment((ListHeaderSegment&)*((WindowEventArgs&)e).window);
2104
2105 autoSizeColumnHeader(col);
2106
2107 return true;
2108 }
2109
2110
2111 /*************************************************************************
2112 Set whether user manipulation of the sort column and direction are
2113 enabled.
2114 *************************************************************************/
setUserSortControlEnabled(bool setting)2115 void MultiColumnList::setUserSortControlEnabled(bool setting)
2116 {
2117 getListHeader()->setSortingEnabled(setting);
2118 }
2119
2120
2121 /*************************************************************************
2122 Set whether the user may size column segments.
2123 *************************************************************************/
setUserColumnSizingEnabled(bool setting)2124 void MultiColumnList::setUserColumnSizingEnabled(bool setting)
2125 {
2126 getListHeader()->setColumnSizingEnabled(setting);
2127 }
2128
2129
2130 /*************************************************************************
2131 Set whether the user may modify the order of the columns.
2132 *************************************************************************/
setUserColumnDraggingEnabled(bool setting)2133 void MultiColumnList::setUserColumnDraggingEnabled(bool setting)
2134 {
2135 getListHeader()->setColumnDraggingEnabled(setting);
2136 }
2137
2138
2139 /*************************************************************************
2140 Return the ID code assigned to the requested column.
2141 *************************************************************************/
getColumnID(uint col_idx) const2142 uint MultiColumnList::getColumnID(uint col_idx) const
2143 {
2144 return getListHeader()->getSegmentFromColumn(col_idx).getID();
2145 }
2146
2147
2148 /*************************************************************************
2149 Return the ID code assigned to the requested row.
2150 *************************************************************************/
getRowID(uint row_idx) const2151 uint MultiColumnList::getRowID(uint row_idx) const
2152 {
2153 // check for invalid index
2154 if (row_idx >= getRowCount())
2155 {
2156 CEGUI_THROW(InvalidRequestException(
2157 "the row index given is out of range."));
2158 }
2159 else
2160 {
2161 return d_grid[row_idx].d_rowID;
2162 }
2163 }
2164
2165
2166 /*************************************************************************
2167 Return the zero based row index of the row with the specified ID.
2168 *************************************************************************/
getRowWithID(uint row_id) const2169 uint MultiColumnList::getRowWithID(uint row_id) const
2170 {
2171 for (uint i = 0; i < getRowCount(); ++i)
2172 {
2173 if (d_grid[i].d_rowID == row_id)
2174 {
2175 return i;
2176 }
2177 }
2178
2179 // No such row found, throw exception
2180 CEGUI_THROW(InvalidRequestException(
2181 "no row with the requested ID is present."));
2182 }
2183
2184
2185 /*************************************************************************
2186 std algorithm predicate used for sorting in descending order (static)
2187 *************************************************************************/
pred_descend(const ListRow & a,const ListRow & b)2188 bool MultiColumnList::pred_descend(const ListRow& a, const ListRow& b)
2189 {
2190 return a > b;
2191 }
2192
2193
2194 /*************************************************************************
2195 Return whether the vertical scroll bar is always shown.
2196 *************************************************************************/
isVertScrollbarAlwaysShown(void) const2197 bool MultiColumnList::isVertScrollbarAlwaysShown(void) const
2198 {
2199 return d_forceVertScroll;
2200 }
2201
2202
2203 /*************************************************************************
2204 Return whether the horizontal scroll bar is always shown.
2205 *************************************************************************/
isHorzScrollbarAlwaysShown(void) const2206 bool MultiColumnList::isHorzScrollbarAlwaysShown(void) const
2207 {
2208 return d_forceHorzScroll;
2209 }
2210
2211
2212 /*************************************************************************
2213 Adds properties for MCL
2214 *************************************************************************/
addMultiColumnListProperties(void)2215 void MultiColumnList::addMultiColumnListProperties(void)
2216 {
2217 const String& propertyOrigin = WidgetTypeName;
2218
2219 CEGUI_DEFINE_PROPERTY(MultiColumnList, bool,
2220 "ColumnsSizable", "Property to get/set the setting for user sizing of the column headers. Value is either \"true\" or \"false\".",
2221 &MultiColumnList::setUserColumnSizingEnabled, &MultiColumnList::isUserColumnSizingEnabled, true /* TODO: Inconsistency */
2222 );
2223
2224 CEGUI_DEFINE_PROPERTY(MultiColumnList, bool,
2225 "ColumnsMovable", "Property to get/set the setting for user moving of the column headers. Value is either \"true\" or \"false\".",
2226 &MultiColumnList::setUserColumnDraggingEnabled, &MultiColumnList::isUserColumnDraggingEnabled, true /* TODO: Inconsistency */
2227 );
2228
2229 CEGUI_DEFINE_PROPERTY(MultiColumnList, bool,
2230 "SortSettingEnabled", "Property to get/set the setting for for user modification of the sort column & direction."
2231 " Value is either \"true\" or \"false\".",
2232 &MultiColumnList::setUserSortControlEnabled, &MultiColumnList::isUserSortControlEnabled, true /* TODO: Inconsistency */
2233 );
2234
2235 CEGUI_DEFINE_PROPERTY(MultiColumnList, ListHeaderSegment::SortDirection,
2236 "SortDirection", "Property to get/set the sort direction setting of the list."
2237 " Value is the text of one of the SortDirection enumerated value names.",
2238 &MultiColumnList::setSortDirection, &MultiColumnList::getSortDirection, ListHeaderSegment::None
2239 );
2240
2241 CEGUI_DEFINE_PROPERTY(MultiColumnList, bool,
2242 "ForceVertScrollbar", "Property to get/set the 'always show' setting for the vertical scroll bar of the list box."
2243 " Value is either \"true\" or \"false\".",
2244 &MultiColumnList::setShowVertScrollbar, &MultiColumnList::isVertScrollbarAlwaysShown, false /* TODO: Inconsistency */
2245 );
2246
2247 CEGUI_DEFINE_PROPERTY(MultiColumnList, bool,
2248 "ForceHorzScrollbar", "Property to get/set the 'always show' setting for the horizontal scroll bar of the list box."
2249 " Value is either \"true\" or \"false\".",
2250 &MultiColumnList::setShowHorzScrollbar, &MultiColumnList::isHorzScrollbarAlwaysShown, false /* TODO: Inconsistency */
2251 );
2252
2253 CEGUI_DEFINE_PROPERTY(MultiColumnList, uint,
2254 "NominatedSelectionColumnID", "Property to get/set the nominated selection column (via ID). Value is an unsigned integer number.",
2255 &MultiColumnList::setNominatedSelectionColumn, &MultiColumnList::getNominatedSelectionColumnID, 0 /* TODO: Inconsistency */
2256 );
2257
2258 CEGUI_DEFINE_PROPERTY(MultiColumnList, uint,
2259 "NominatedSelectionRow", "Property to get/set the nominated selection row. Value is an unsigned integer number.",
2260 &MultiColumnList::setNominatedSelectionRow, &MultiColumnList::getNominatedSelectionRow, 0
2261 );
2262
2263 CEGUI_DEFINE_PROPERTY_NO_XML(MultiColumnList, uint,
2264 "RowCount", "Property to access the number of rows in the list (read only)",
2265 0, &MultiColumnList::getRowCount, 0
2266 );
2267
2268 CEGUI_DEFINE_PROPERTY(MultiColumnList, MultiColumnList::SelectionMode,
2269 "SelectionMode", "Property to get/set the selection mode setting of the list."
2270 " Value is the text of one of the SelectionMode enumerated value names.",
2271 &MultiColumnList::setSelectionMode, &MultiColumnList::getSelectionMode, MultiColumnList::RowSingle
2272 );
2273
2274 CEGUI_DEFINE_PROPERTY(MultiColumnList, bool,
2275 "AutoSizeColumnUsesHeader", "Property to get/set the 'use header size' flag when auto-sizing a column."
2276 " Value is either \"true\" or \"false\".",
2277 &MultiColumnList::setAutoSizeColumnUsesHeader, &MultiColumnList::getAutoSizeColumnUsesHeader, false
2278 );
2279
2280 CEGUI_DEFINE_PROPERTY_NO_XML(MultiColumnList, String,
2281 "ColumnHeader", "Property to set up a column (there is no getter for this property)",
2282 &MultiColumnList::addColumn, 0, "" /* TODO: This is quite a hack, isn't it? */
2283 );
2284 }
2285
2286
2287 /*************************************************************************
2288 Remove all items from the list.
2289 *************************************************************************/
resetList_impl(void)2290 bool MultiColumnList::resetList_impl(void)
2291 {
2292 // just return false if the list is already empty (no rows == empty)
2293 if (getRowCount() == 0)
2294 {
2295 return false;
2296 }
2297 // we have items to be removed and possible deleted
2298 else
2299 {
2300 for (uint i = 0; i < getRowCount(); ++i)
2301 {
2302 for (uint j = 0; j < getColumnCount(); ++j)
2303 {
2304 ListboxItem* item = d_grid[i][j];
2305
2306 // delete item as needed.
2307 if ((item != 0) && item->isAutoDeleted())
2308 {
2309 CEGUI_DELETE_AO item;
2310 }
2311
2312 }
2313
2314 }
2315
2316 // clear all items from the grid.
2317 d_grid.clear();
2318
2319 // reset other affected fields
2320 d_nominatedSelectRow = 0;
2321 d_lastSelected = 0;
2322
2323 return true;
2324 }
2325
2326 }
2327
2328
2329 /*************************************************************************
2330 Automatically determines the "best fit" size for the specified column
2331 and sets the column width to the same.
2332 *************************************************************************/
autoSizeColumnHeader(uint col_idx)2333 void MultiColumnList::autoSizeColumnHeader(uint col_idx)
2334 {
2335 // check for invalid index
2336 if (col_idx >= getColumnCount())
2337 {
2338 CEGUI_THROW(InvalidRequestException(
2339 "the column index given is out of range."));
2340 }
2341 else
2342 {
2343 // get the width of the widest item in the column.
2344 float width = ceguimax(getWidestColumnItemWidth(col_idx), ListHeader::MinimumSegmentPixelWidth);
2345
2346 // set new column width
2347 setColumnHeaderWidth(col_idx, cegui_absdim(width));
2348 }
2349
2350 }
2351
2352
2353 /*************************************************************************
2354 Set the ID code assigned to a given row.
2355 *************************************************************************/
setRowID(uint row_idx,uint row_id)2356 void MultiColumnList::setRowID(uint row_idx, uint row_id)
2357 {
2358 // check for invalid index
2359 if (row_idx >= getRowCount())
2360 {
2361 CEGUI_THROW(InvalidRequestException(
2362 "the row index given is out of range."));
2363 }
2364 else
2365 {
2366 d_grid[row_idx].d_rowID = row_id;
2367 }
2368 }
2369
2370
2371 /*************************************************************************
2372 Return a pointer to the vertical scrollbar component widget for this
2373 MultiColumnList.
2374 *************************************************************************/
getVertScrollbar() const2375 Scrollbar* MultiColumnList::getVertScrollbar() const
2376 {
2377 return static_cast<Scrollbar*>(getChild(VertScrollbarName));
2378 }
2379
2380
2381 /*************************************************************************
2382 Return a pointer to the horizontal scrollbar component widget for this
2383 MultiColumnList.
2384 *************************************************************************/
getHorzScrollbar() const2385 Scrollbar* MultiColumnList::getHorzScrollbar() const
2386 {
2387 return static_cast<Scrollbar*>(getChild(HorzScrollbarName));
2388 }
2389
2390
2391 /*************************************************************************
2392 Return a pointer to the list header component widget for this
2393 MultiColumnList.
2394 *************************************************************************/
getListHeader() const2395 ListHeader* MultiColumnList::getListHeader() const
2396 {
2397 return static_cast<ListHeader*>(getChild(ListHeaderName));
2398 }
2399
validateWindowRenderer(const WindowRenderer * renderer) const2400 bool MultiColumnList::validateWindowRenderer(const WindowRenderer* renderer) const
2401 {
2402 return dynamic_cast<const MultiColumnListWindowRenderer*>(renderer) != 0;
2403 }
2404
2405 /*************************************************************************
2406 Write xml properties for this MultiColumnList to a stream.
2407 *************************************************************************/
writePropertiesXML(XMLSerializer & xml_stream) const2408 int MultiColumnList::writePropertiesXML(XMLSerializer& xml_stream) const
2409 {
2410 // basically this is here to translate the columns in the list into
2411 // instances of the <ColumnHeader> element. Because the SortColumnID
2412 // property requires the column to exist, we also write that out manually.
2413
2414 // Dump all other properties first
2415 int propCnt = Window::writePropertiesXML(xml_stream);
2416
2417 // create an dump <ColumnHeader> elements
2418 for (uint i = 0; i < getColumnCount(); ++i)
2419 {
2420 ListHeaderSegment& seg = getHeaderSegmentForColumn(i);
2421
2422 // column text
2423 String propString = "text:";
2424 propString += seg.getText();
2425 // column width
2426 propString += " width:";
2427 propString += PropertyHelper<UDim>::toString(seg.getWidth());
2428 // column id
2429 propString += " id:";
2430 propString += PropertyHelper<uint>::toString(seg.getID());
2431 // create the tag
2432 xml_stream.openTag(Property::XMLElementName)
2433 .attribute(Property::NameXMLAttributeName, "ColumnHeader")
2434 .attribute(Property::ValueXMLAttributeName, propString)
2435 .closeTag();
2436 ++propCnt;
2437 }
2438
2439 // write out SortColumnID property, if any(!)
2440 CEGUI_TRY
2441 {
2442 uint sortColumnID = getColumnWithID(getSortColumn());
2443 if (sortColumnID != 0)
2444 {
2445 xml_stream.openTag(Property::XMLElementName)
2446 .attribute(Property::NameXMLAttributeName, "SortColumnID")
2447 .attribute(Property::ValueXMLAttributeName,
2448 PropertyHelper<uint>::toString(sortColumnID))
2449 .closeTag();
2450 ++propCnt;
2451 }
2452 }
2453 CEGUI_CATCH (InvalidRequestException&)
2454 {
2455 // This catches error(s) from the MultiLineColumnList for example
2456 Logger::getSingleton().logEvent("MultiColumnList::writePropertiesXML - invalid sort column requested. Continuing...", Errors);
2457 }
2458
2459 return propCnt;
2460 }
2461
2462
2463 /*************************************************************************
2464 Return a Rect object describing, in un-clipped pixels, the window
2465 relative area that is to be used for rendering list items.
2466 *************************************************************************/
getListRenderArea() const2467 Rectf MultiColumnList::getListRenderArea() const
2468 {
2469 if (d_windowRenderer != 0)
2470 {
2471 MultiColumnListWindowRenderer* wr = (MultiColumnListWindowRenderer*)d_windowRenderer;
2472 return wr->getListRenderArea();
2473 }
2474 else
2475 {
2476 //return getListRenderArea_impl();
2477 CEGUI_THROW(InvalidRequestException(
2478 "This function must be implemented by the window renderer module"));
2479 }
2480 }
2481
2482 //----------------------------------------------------------------------------//
ensureItemIsVisible(const ListboxItem * item)2483 void MultiColumnList::ensureItemIsVisible(const ListboxItem* item)
2484 {
2485 // NB: throws InvalidRequestException if non-existant item
2486 ensureItemIsVisible(getItemGridReference(item));
2487 }
2488
2489 //----------------------------------------------------------------------------//
ensureItemIsVisible(const MCLGridRef & grid_ref)2490 void MultiColumnList::ensureItemIsVisible(const MCLGridRef& grid_ref)
2491 {
2492 ensureRowIsVisible(grid_ref.row);
2493 ensureColumnIsVisible(grid_ref.column);
2494 }
2495
2496 //----------------------------------------------------------------------------//
ensureItemRowIsVisible(const ListboxItem * item)2497 void MultiColumnList::ensureItemRowIsVisible(const ListboxItem* item)
2498 {
2499 // NB: throws InvalidRequestException if non-existant item
2500 ensureRowIsVisible(getItemGridReference(item).row);
2501 }
2502
2503 //----------------------------------------------------------------------------//
ensureItemColumnIsVisible(const ListboxItem * item)2504 void MultiColumnList::ensureItemColumnIsVisible(const ListboxItem* item)
2505 {
2506 // NB: throws InvalidRequestException if non-existant item
2507 ensureColumnIsVisible(getItemGridReference(item).column);
2508 }
2509
2510 //----------------------------------------------------------------------------//
ensureRowIsVisible(uint row_idx)2511 void MultiColumnList::ensureRowIsVisible(uint row_idx)
2512 {
2513 uint rows = getRowCount();
2514
2515 Scrollbar* vertScrollbar = getVertScrollbar();
2516
2517 // handle horizontal scrolling
2518 // handle simple "scroll to the bottom" case
2519 if (row_idx >= rows)
2520 {
2521 vertScrollbar->setScrollPosition(
2522 vertScrollbar->getDocumentSize() - vertScrollbar->getPageSize());
2523 }
2524 else
2525 {
2526 float bottom;
2527 float top = 0.0f;
2528 float listHeight = getListRenderArea().getHeight();
2529
2530 // get distance to top of item
2531 uint row;
2532 for (row = 0; row < row_idx; ++row)
2533 top += getHighestRowItemHeight(row);
2534
2535 // calculate distance to bottom of item
2536 bottom = top + getHighestRowItemHeight(row);
2537
2538 // account for current scrollbar value
2539 float currPos = vertScrollbar->getScrollPosition();
2540 top -= currPos;
2541 bottom -= currPos;
2542
2543 // if top edge is above the view area, or if the item is too big to fit
2544 if ((top < 0.0f) || ((bottom - top) > listHeight))
2545 {
2546 // scroll top of item to top of box.
2547 vertScrollbar->setScrollPosition(currPos + top);
2548 }
2549 // if the bottom edge is below the view area
2550 else if (bottom >= listHeight)
2551 {
2552 // position bottom of item at the bottom of the list
2553 vertScrollbar->setScrollPosition(currPos + bottom - listHeight);
2554 }
2555 }
2556 }
2557
2558 //----------------------------------------------------------------------------//
ensureColumnIsVisible(uint col_idx)2559 void MultiColumnList::ensureColumnIsVisible(uint col_idx)
2560 {
2561 uint cols = getColumnCount();
2562 Scrollbar* horzScrollbar = getHorzScrollbar();
2563
2564 // handle horizontal scrolling
2565 // first the simple "scroll to the right edge" case
2566 if (col_idx >= cols)
2567 {
2568 horzScrollbar->setScrollPosition(
2569 horzScrollbar->getDocumentSize() - horzScrollbar->getPageSize());
2570 }
2571 else
2572 {
2573 float right;
2574 float left = 0.0f;
2575 float listWidth = getListRenderArea().getWidth();
2576
2577 // get distance to left edge of item
2578 uint col;
2579 for (col = 0; col < col_idx; ++col)
2580 left += CoordConverter::asAbsolute(getColumnHeaderWidth(col),
2581 getParentPixelSize().d_width);
2582
2583 // get the distance to the right edge of the item
2584 right = left + CoordConverter::asAbsolute(getColumnHeaderWidth(col),
2585 getParentPixelSize().d_width);
2586
2587 // account for current scrollbar value
2588 float currPos = horzScrollbar->getScrollPosition();
2589 left -= currPos;
2590 right -= currPos;
2591
2592 // if the left edge is to the left of the view area, or if the item is
2593 // too big to fit
2594 if ((left < 0.0f) || ((right - left) > listWidth))
2595 {
2596 // scroll left edge of item to left edge of box.
2597 horzScrollbar->setScrollPosition(currPos + left);
2598 }
2599 // if right edge is to the right of the view area
2600 else if (right >= listWidth)
2601 {
2602 // position the right edge of the item at the right edge of the list
2603 horzScrollbar->setScrollPosition(currPos + right - listWidth);
2604 }
2605 }
2606 }
2607
2608 //----------------------------------------------------------------------------//
setAutoSizeColumnUsesHeader(bool include_header)2609 void MultiColumnList::setAutoSizeColumnUsesHeader(bool include_header)
2610 {
2611 d_autoSizeColumnUsesHeader = include_header;
2612 }
2613
2614 //----------------------------------------------------------------------------//
getAutoSizeColumnUsesHeader() const2615 bool MultiColumnList::getAutoSizeColumnUsesHeader() const
2616 {
2617 return d_autoSizeColumnUsesHeader;
2618 }
2619
2620 //----------------------------------------------------------------------------//
2621
2622 //////////////////////////////////////////////////////////////////////////
2623 /*************************************************************************
2624 Operators for ListRow
2625 *************************************************************************/
2626 //////////////////////////////////////////////////////////////////////////
2627 /*************************************************************************
2628 Less-than operator
2629 *************************************************************************/
operator <(const ListRow & rhs) const2630 bool MultiColumnList::ListRow::operator<(const ListRow& rhs) const
2631 {
2632 ListboxItem* a = d_items[d_sortColumn];
2633 ListboxItem* b = rhs.d_items[d_sortColumn];
2634
2635 // handle cases with empty slots
2636 if (!b)
2637 {
2638 return false;
2639 }
2640 else if (!a)
2641 {
2642 return true;
2643 }
2644 else
2645 {
2646 return *a < *b;
2647 }
2648
2649 }
2650
2651
2652 /*************************************************************************
2653 Greater-than operator
2654 *************************************************************************/
operator >(const ListRow & rhs) const2655 bool MultiColumnList::ListRow::operator>(const ListRow& rhs) const
2656 {
2657 ListboxItem* a = d_items[d_sortColumn];
2658 ListboxItem* b = rhs.d_items[d_sortColumn];
2659
2660 // handle cases with empty slots
2661 if (!a)
2662 {
2663 return false;
2664 }
2665 else if (!b)
2666 {
2667 return true;
2668 }
2669 else
2670 {
2671 return *a > *b;
2672 }
2673
2674 }
2675
2676 /*************************************************************************
2677 Cause list content to be (re)sorted
2678 *************************************************************************/
resortList()2679 void MultiColumnList::resortList()
2680 {
2681 // re-sort list according to direction
2682 ListHeaderSegment::SortDirection dir = getSortDirection();
2683
2684 if (dir == ListHeaderSegment::Descending)
2685 {
2686 std::sort(d_grid.begin(), d_grid.end(), pred_descend);
2687 }
2688 else if (dir == ListHeaderSegment::Ascending)
2689 {
2690 std::sort(d_grid.begin(), d_grid.end());
2691 }
2692 // else no (or invalid) direction, so do not sort.
2693 }
2694
2695
2696 //////////////////////////////////////////////////////////////////////////
2697 /*************************************************************************
2698 Operators for MCLGridRef
2699 *************************************************************************/
2700 //////////////////////////////////////////////////////////////////////////
2701 /*************************************************************************
2702 Assignment operator
2703 *************************************************************************/
operator =(const MCLGridRef & rhs)2704 MCLGridRef& MCLGridRef::operator=(const MCLGridRef& rhs)
2705 {
2706 column = rhs.column;
2707 row = rhs.row;
2708 return *this;
2709 }
2710
2711
2712 /*************************************************************************
2713 return true if this is less than (appears before) 'rhs' in grid.
2714 *************************************************************************/
operator <(const MCLGridRef & rhs) const2715 bool MCLGridRef::operator<(const MCLGridRef& rhs) const
2716 {
2717 if ((row < rhs.row) ||
2718 ((row == rhs.row) && (column < rhs.column)))
2719 {
2720 return true;
2721 }
2722 else
2723 {
2724 return false;
2725 }
2726 }
2727
2728
2729 /*************************************************************************
2730 return true if this is less than or equal to 'rhs'
2731 *************************************************************************/
operator <=(const MCLGridRef & rhs) const2732 bool MCLGridRef::operator<=(const MCLGridRef& rhs) const
2733 {
2734 return !operator>(rhs);
2735 }
2736
2737
2738 /*************************************************************************
2739 return true if this is greater than (appears after) 'rhs' in grid.
2740 *************************************************************************/
operator >(const MCLGridRef & rhs) const2741 bool MCLGridRef::operator>(const MCLGridRef& rhs) const
2742 {
2743 return (operator<(rhs) || operator==(rhs)) ? false : true;
2744 }
2745
2746
2747 /*************************************************************************
2748 return true if this is greater than or equal to 'rhs'
2749 *************************************************************************/
operator >=(const MCLGridRef & rhs) const2750 bool MCLGridRef::operator>=(const MCLGridRef& rhs) const
2751 {
2752 return !operator<(rhs);
2753 }
2754
2755
2756 /*************************************************************************
2757 return true if this grid ref is equal to that in 'rhs'
2758 *************************************************************************/
operator ==(const MCLGridRef & rhs) const2759 bool MCLGridRef::operator==(const MCLGridRef& rhs) const
2760 {
2761 return ((column == rhs.column) && (row == rhs.row)) ? true : false;
2762 }
2763
2764
2765 /*************************************************************************
2766 return true if this grid ref is different to that in 'rhs'
2767 *************************************************************************/
operator !=(const MCLGridRef & rhs) const2768 bool MCLGridRef::operator!=(const MCLGridRef& rhs) const
2769 {
2770 return !operator==(rhs);
2771 }
2772
2773 } // End of CEGUI namespace section
2774