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