1 namespace gui::controls;
5 static define branchesColor = Color { 85, 85, 85 };
6 static define headerCollapseForeground = Color { 135, 135, 135 };
7 static define dataBoxBackground = Color { 67, 134, 198 };
8 static define dataBoxForeground = lightYellow;
10 #define SNAPDOWN(x, d) \
13 if(x < 0) x -= ((d) - Abs(x) % (d)); else x -= x % (d); \
18 static class ListBoxCell : struct
20 ListBoxCell prev, next;
26 #define RESIZE_BORDER 5
32 bool header:1, freeSelect:1 /*Exclusive to ELS_MULTISELECT*/, fullRowSelect:1, multiSelect:1, autoScroll:1, alwaysHL : 1, moveRows:1, resizable:1;
33 bool moveFields:1, clearHeader:1, alwaysEdit:1, collapse:1, treeBranch:1, rootCollapse:1, heightSet:1;
34 bool sortable:1, noDragging:1, fillLastField:1, expandOnAdd:1;
37 public class DataDisplayFlags
39 public bool selected:1, fullRow:1, current:1, active:1, dropBox:1, header:1, firstField:1;
42 enum SelectedFlag { unselected, selected, tempSelected, tempUnselected };
45 extern int __ecereVMethodID_class_OnEdit;
46 extern int __ecereVMethodID_class_OnDisplay;
47 extern int __ecereVMethodID_class_OnGetString;
48 extern int __ecereVMethodID_class_OnFree;
49 extern int __ecereVMethodID_class_OnCompare;
50 extern int __ecereVMethodID_class_OnCopy;
51 extern int __ecereVMethodID_class_OnSaveEdit;
55 public class DataField
59 property Class dataType
61 set { dataType = value; if(value) { alignment = (Alignment)value.defaultAlignment; } } // NOTE: Class::defaultAlignment does not seem to be used anywhere
62 get { return dataType; }
64 property bool editable { set { editable = value; } };
65 property bool fixed { set { fixed = value; } get { return fixed; } };
66 property Alignment alignment
71 if(headButton) headButton.alignment = value;
72 if(listBox) listBox.Update(null);
74 get { return alignment; }
80 width = Max(value, -EXTRA_SPACE);
82 listBox.AdaptToFieldWidth(this, false);
86 property int index { get { return this ? index : 0; } };
95 for(field = listBox.fields.first; field; field = field.next)
97 if(index == value - 2)
110 for(field = listBox.fields.first; field; field = field.next)
122 property int sortOrder { get { return this ? sortOrder : 0; } };
123 property const char * header
129 headButton.text = header;
132 property void * userData
140 return this ? userData : null;
143 property bool freeData
145 set { freeData = value; } get { return freeData; }
147 property DataField prev { get { return prev; } };
148 property DataField next { get { return next; } };
150 void Move(DataField after)
154 listBox.fields.Move(this, after);
157 listBox.OnResize(listBox.clientSize.w, listBox.clientSize.h);
158 listBox.Update(null);
164 if(listBox && dataType)
166 Display display = listBox.display;
167 Font boldFont = listBox.boldFont.font;
168 Font font = listBox.fontObject;
172 display.FontExtent(boldFont, header, strlen(header), &width, null);
173 width += EXTRA_SPACE;
174 for(row = listBox.firstRow; row; row = row.GetNextRow())
178 for(i = 0, cell = row.cells.first; i != index; i++, cell = cell.next);
179 if(cell && cell.isSet && dataType)
181 static char tempString[4096];
184 if(dataType.type == normalClass || dataType.type == noHeadClass)
185 string = ((const char *(*)(void *, void *, char *, void *, bool *))(void *)dataType._vTbl[__ecereVMethodID_class_OnGetString])(dataType, cell.data[0], tempString, userData, null);
187 string = ((const char *(*)(void *, void *, char *, void *, bool *))(void *)dataType._vTbl[__ecereVMethodID_class_OnGetString])(dataType, cell.data, tempString, userData, null);
189 display.FontExtent(row.header ? boldFont : font, string, strlen(string), &tw, null);
191 display.FontExtent(row.header ? boldFont : font, "", 0, &tw, null);
192 if(tw > width) width = tw;
196 property::width = width;
203 // property::dataType = "String";
204 property::dataType = "char *";
216 headButton.Destroy(0);
221 listBox.fields.Remove(this);
222 for(field = listBox.fields.first; field; field = field.next)
224 if(field.index >= index)
227 if(listBox.currentField == this)
228 listBox.currentField = null;
230 listBox.OnResize(listBox.clientSize.w, listBox.clientSize.h);
235 DataField prev, next;
259 property int64 tag { set { if(this) tag = value; } get { return this ? tag : 0; } };
260 property DataRow previous { get { return prev; } };
261 property DataRow next { get { return next; } };
262 property int index { get { return (this && (!parent || parent.IsExpanded())) ? index : -1; } };
263 property const char * string
265 set { SetData(listBox.fields.first, value); }
266 get { return GetData(listBox.fields.first); }
268 property bool isHeader { set { header = value; } get { return this ? header : false; } };
269 property BitmapResource icon { set { if(this) icon = value; } get { return this ? icon : null; } };
270 property bool collapsed
274 if(this && collapsed != value)
277 if(parent.IsExpanded())
284 for(search = subRows.first; search; search = search.next)
285 search.selectedFlag = 0;
287 if(listBox.clickedRow && !listBox.clickedRow.parent.IsExpanded())
289 listBox.clickedRow = GetNextRow();
290 if(!listBox.clickedRow)
291 listBox.clickedRow = this;
293 if(listBox.currentRow && !listBox.currentRow.parent.IsExpanded())
295 listBox.SetCurrentRow(this, true);
297 if(listBox.firstRowShown && !listBox.firstRowShown.parent.IsExpanded())
299 listBox.firstRowShown = GetPrevRow();
300 if(!listBox.firstRowShown)
301 listBox.firstRowShown = this;
305 index = this.index+1;
306 for(search = GetNextRow(); search; search = search.GetNextRow())
307 search.index = index++;
308 listBox.rowCount = index;
310 listBox.HideEditBox(false, false, true);
312 listBox.SetScrollArea(
314 (listBox.rowCount * listBox.rowHeight) +
315 ((listBox.style.header) ? listBox.rowHeight : 0) -
316 ((!((listBox.clientSize.h+1) % listBox.rowHeight)) ? listBox.rowHeight : 0), true);
317 listBox.Update(null);
318 listBox.NotifyCollapse(listBox.master, listBox, this, value);
322 if(this && !skipCheck)
323 listBox.CheckConsistency();
326 get { return this ? collapsed : false; }
328 property bool selected
334 selectedFlag = value ? selected : unselected;
335 listBox.Update(null);
338 get { return selectedFlag == selected || selectedFlag == tempSelected; }
340 property DataRow parent
344 if(this && (value != this))
347 DataRow after = value ? value.subRows.last : listBox.rows.last;
348 int ixCount = (!collapsed && subRows.count) ? GetLastRow().index - index + 1 : 1;
349 if(parent.IsExpanded())
351 for(search = GetNextRow(); search; search = search.GetNextRow())
352 search.index -= ixCount;
353 listBox.rowCount -= ixCount;
356 listBox.HideEditBox(false, false, true);
359 if(this == listBox.clickedRow)
361 listBox.clickedRow = GetNextRow();
362 if(!listBox.clickedRow)
363 listBox.clickedRow = GetPrevRow();
366 if(this == listBox.currentRow)
368 DataRow newCurrentRow = GetNextRow();
369 if(!listBox.newCurrentRow)
370 listBox.newCurrentRow = GetPrevRow();
371 listBox.SetCurrentRow(newCurrentRow, true);
374 if(this == listBox.firstRowShown)
376 listBox.firstRowShown = GetPrevRow();
377 if(!listBox.firstRowShown)
378 listBox.firstRowShown = GetNextRow();
382 (parent ? parent.subRows : listBox.rows).Remove(this);
385 value.subRows.Insert(after, this);
387 listBox.rows.Insert(after, this);
391 if(value && listBox.style.expandOnAdd)
394 value.skipCheck = true;
396 value.collapsed = false;
398 value.skipCheck = false;
402 if(value.IsExpanded())
406 if(after && after.subRows.first && !after.collapsed)
408 search = after.GetLastRow();
409 index = search.index + 1;
412 index = after ? (after.index + 1) : (index + 1);
414 listBox.rowCount += ixCount;
418 for(search = GetNextRow(); search; search = search.GetNextRow())
422 listBox.SetScrollArea(
424 (listBox.rowCount * listBox.rowHeight) +
425 ((listBox.style.header) ? listBox.rowHeight : 0) -
426 ((!((listBox.clientSize.h+1) % listBox.rowHeight)) ? listBox.rowHeight : 0), true);
427 if(listBox.style.autoScroll)
428 listBox.SetScrollPosition(0, MAXINT - listBox.rowHeight);
430 // TESTING THIS HERE...
432 listBox.Sort(listBox.sortField, listBox.sortField.sortOrder);
435 int headerSize = ((listBox.style.header) ? listBox.rowHeight : 0);
436 int height = listBox.clientSize.h + 1 - headerSize;
437 if(listBox.currentRow && listBox.currentRow.index * listBox.rowHeight > listBox.scroll.y + height - listBox.rowHeight)
438 listBox.SetScrollPosition(listBox.scroll.x, listBox.currentRow.index * listBox.rowHeight - height + listBox.rowHeight);
439 else if(!listBox.currentRow || listBox.currentRow.index * listBox.rowHeight < listBox.scroll.y)
441 int line = listBox.currentRow ? listBox.currentRow.index * listBox.rowHeight : 0;
442 listBox.SetScrollPosition(listBox.scroll.x, line);
445 listBox.OnVScroll(0, listBox.scroll.y, 0);
447 listBox.Update(null);
455 property DataRow lastRow { get { return this ? subRows.last : null; } };
456 property DataRow firstRow { get { return this ? subRows.first : null; } };
458 void Edit(DataField field)
464 if(!field || !field.editable)
466 for(field = listBox.fields.first; field; field = field.next)
467 if(field.editable) break;
469 if(field && field.editable)
471 listBox.SetCurrentRow(this, true);
472 listBox.PopupEditBox(field, false);
478 // NOTE: This does not support reparenting (Use row.parent = first)
479 void Move(DataRow after)
484 if(listBox && prev != after)
487 int headerSize = ((listBox.style.header) ? listBox.rowHeight : 0);
488 int height = listBox.clientSize.h + 1 - headerSize;
489 int ixCount = (!collapsed && subRows.count) ? GetLastRow().index - index + 1 : 1;
491 if(!after || after.index < index)
493 if(after == listBox.firstRowShown.prev)
494 listBox.firstRowShown = this;
496 // All rows between AFTER (exclusive) and ROW (exclusive) are incremented by one
497 // ROW is equal to AFTER's index + 1
499 for(search = after ? after.next : listBox.rows.first; search && search != this; search = search.GetNextRow())
500 search.index += ixCount;
502 if(after && after.subRows.first && !after.collapsed)
504 search = after.GetLastRow();
505 index = search.index + 1;
508 index = after ? (after.index + 1) : 0;
510 // Fix indices of sub rows
514 for(c = 1, search = GetNextRow(); search && c < ixCount; c++, search = search.GetNextRow())
520 DataRow nextRow = GetNextRow();
521 if(this == listBox.firstRowShown)
522 listBox.firstRowShown = nextRow;
525 // All rows between ROW (exclusive) and AFTER (inclusive) are decremented by one
526 // ROW is equal to AFTER's index
528 for(search = nextRow; search; search = search.GetNextRow())
530 search.index -= ixCount;
531 if(search == after) break;
534 listBox.rows.Move(this, after);
537 listBox.CheckConsistency();
540 listBox.HideEditBox(true, false, true);
543 if(listBox.currentRow && listBox.currentRow.index * listBox.rowHeight > listBox.scroll.y + height - listBox.rowHeight)
544 listBox.SetScrollPosition(listBox.scroll.x, listBox.currentRow.index * listBox.rowHeight - height + listBox.rowHeight);
545 else if(!listBox.currentRow || listBox.currentRow.index * listBox.rowHeight < listBox.scroll.y)
547 int line = listBox.currentRow ? listBox.currentRow.index * listBox.rowHeight : 0;
548 //SNAPUP(line, listBox.rowHeight);
549 listBox.SetScrollPosition(listBox.scroll.x, line);
552 listBox.OnVScroll(0, listBox.scroll.y, 0);
554 listBox.modifiedDocument = true;
556 listBox.Update(null);
563 any_object GetData(DataField field)
567 ListBoxCell cell = listBox.GetCell(&this, &field);
568 if(cell && cell.isSet && cell.data)
570 if((field.dataType.type == normalClass || field.dataType.type == noHeadClass))
579 void * SetData(DataField field, any_object newData)
583 ListBoxCell cell = listBox.GetCell(&this, &field);
586 Class dataType = field.dataType;
590 if(dataType.type == normalClass || dataType.type == noHeadClass)
592 if(cell.data[0] && field.freeData)
593 ((void (*)(void *, void *))(void *)dataType._vTbl[__ecereVMethodID_class_OnFree])(dataType, cell.data[0]);
595 if(eClass_IsDerived(dataType, class(char *)) && field.freeData)
596 ((void (*)(void *, void *, void *))(void *)dataType._vTbl[__ecereVMethodID_class_OnCopy])(dataType, cell.data, newData);
598 cell.data[0] = (void *)newData;
602 // Free old data first
603 ((void (*)(void *, void *))(void *)dataType._vTbl[__ecereVMethodID_class_OnFree])(dataType, cell.data);
604 ((void (*)(void *, void *, void *))(void *)dataType._vTbl[__ecereVMethodID_class_OnCopy])(dataType, cell.data, newData);
608 listBox.modifiedDocument = true;
609 listBox.Update(null);
610 if(dataType && (dataType.type == normalClass || dataType.type == noHeadClass))
619 void UnsetData(DataField field)
623 ListBoxCell cell = listBox.GetCell(&this, &field);
626 Class dataType = field.dataType;
630 if(dataType.type == normalClass || dataType.type == noHeadClass)
632 if(cell.data[0] && field.freeData)
633 ((void (*)(void *, void *))(void *)dataType._vTbl[__ecereVMethodID_class_OnFree])(dataType, cell.data[0]);
638 // Free old data first
639 ((void (*)(void *, void *))(void *)dataType._vTbl[__ecereVMethodID_class_OnFree])(dataType, cell.data);
643 listBox.Update(null);
648 DataRow FindRow(int64 tag)
651 for(row = subRows.first; row; row = row.next)
653 if(!row.noneRow && row.tag == tag)
659 DataRow FindSubRow(int64 tag)
662 for(row = subRows.first; row; row = row.next)
664 if(!row.noneRow && row.tag == tag)
666 if(row.subRows.first)
668 DataRow subRow = row.FindSubRow(tag);
676 DataRow AddRowAfter(DataRow after)
686 subRows.Insert(after, row);
687 row.listBox = listBox;
691 for(c = 0; c<listBox.fields.count; c++)
693 for(field = listBox.fields.first; field; field = field.next)
694 if((int)field.index == c)
698 int size = (field.dataType && field.dataType.typeSize) ?
699 (sizeof(class ListBoxCell) + field.dataType.typeSize - sizeof(void *)) : sizeof(class ListBoxCell);
700 ListBoxCell cell = (ListBoxCell)new0 byte[size];
702 FillBytes(cell.data, 0, size - (uint)(uintptr)&((ListBoxCell)0).data);
711 if(after && after.subRows.first && !after.collapsed)
713 for(search = after.subRows.last; !search.collapsed && search.subRows.last; )
714 search = search.subRows.last;
715 row.index = search.index + 1;
718 row.index = after ? (after.index + 1) : (index + 1);
722 for(search = row.GetNextRow(); search; search = search.GetNextRow())
725 listBox.SetScrollArea(
727 (listBox.rowCount * listBox.rowHeight) +
728 ((listBox.style.header) ? listBox.rowHeight : 0) -
729 ((!((listBox.clientSize.h+1) % listBox.rowHeight)) ? listBox.rowHeight : 0), true);
730 if(listBox.style.autoScroll)
731 listBox.SetScrollPosition(0, MAXINT - listBox.rowHeight);
734 listBox.modifiedDocument = true;
737 listBox.CheckConsistency();
747 return AddRowAfter(subRows.last);
751 DataRow AddStringf(const char * format, ...)
756 char string[MAX_F_STRING];
758 va_start(args, format);
759 vsnprintf(string, sizeof(string), format, args);
760 string[sizeof(string)-1] = 0;
764 row.SetData(null, string);
770 DataRow AddString(const char * string)
776 row.SetData(listBox.fields.first, string);
786 subRows.offset = (uint)(uintptr)&((DataRow)0).prev;
791 ListBoxCell cell, next;
796 while((subRow = subRows.first))
798 subRows.Remove(subRow);
802 for(cell = cells.first; cell; cell = next, cellIndex++)
807 for(field = listBox.fields.first; field && field.index != cellIndex; field = field.next);
808 if(field && field.dataType)
810 // TOCHECK: Is this check good? Will need incref/decref sometime?
811 if(field.dataType.type == normalClass || field.dataType.type == noHeadClass)
813 if(cell.data[0] && field.freeData)
814 ((void (*)(void *, void *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnFree])(field.dataType, cell.data[0]);
817 ((void (*)(void *, void *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnFree])(field.dataType, cell.data);
827 return !this || (!collapsed && (!parent || parent.IsExpanded()));
830 int Compare(DataRow b, DataField sortField)
833 ListBoxCell cell1, cell2;
835 for(index = 0, cell1 = cells.first, cell2 = b.cells.first;
836 index != sortField.index;
837 index++, cell1 = cell1.next, cell2 = cell2.next);
839 if(!cell1.isSet && !cell2.isSet)
841 else if(!cell1.isSet)
843 else if(!cell2.isSet)
846 if(noneRow && !b.noneRow) return -1;
847 else if(!noneRow && b.noneRow) return 1;
848 else if(noneRow && b.noneRow) return 0;
850 if(sortField.dataType._vTbl[__ecereVMethodID_class_OnCompare])
852 if(sortField.dataType.type == normalClass || sortField.dataType.type == noHeadClass)
854 result = ((int (*)(void *, void *, void *))(void *)sortField.dataType._vTbl[__ecereVMethodID_class_OnCompare])(sortField.dataType,
855 (cell1.isSet && cell1.data) ? cell1.data[0] : null,
856 (cell2.isSet && cell2.data) ? cell2.data[0] : null);
860 result = ((int (*)(void *, void *, void *))(void *)sortField.dataType._vTbl[__ecereVMethodID_class_OnCompare])(sortField.dataType,
861 cell1.isSet ? cell1.data : null,
862 cell2.isSet ? cell2.data : null);
865 return sortField.sortOrder * result;
868 void _SortSubRows(DataField field, int order)
871 for(search = subRows.first; search; search = search.next)
872 search._SortSubRows(field, order);
873 subRows.Sort(Compare, field);
876 public void SortSubRows(bool scrollToCurrent)
878 if(this && listBox && listBox.sortField)
880 _SortSubRows(listBox.sortField, listBox.sortField.sortOrder);
884 int index = this.index;
885 for(search = this; search; search = search.GetNextRow())
886 search.index = index++;
890 int headerSize = ((listBox.style.header) ? listBox.rowHeight : 0);
891 int height = listBox.clientSize.h + 1 - headerSize;
892 if(listBox.currentRow && listBox.currentRow.index * listBox.rowHeight > listBox.scroll.y + height - listBox.rowHeight)
893 listBox.SetScrollPosition(listBox.scroll.x, listBox.currentRow.index * listBox.rowHeight - height + listBox.rowHeight);
894 else if(!listBox.currentRow || listBox.currentRow.index * listBox.rowHeight < listBox.scroll.y)
895 listBox.SetScrollPosition(listBox.scroll.x, listBox.currentRow ? listBox.currentRow.index * listBox.rowHeight : 0);
896 listBox.OnVScroll(0, listBox.scroll.y, 0);
901 public DataRow GetPrevRow()
907 for(row = prev; row && !row.collapsed && row.subRows.last; row = row.subRows.last);
914 public DataRow GetNextRow()
918 if(subRows.first && !collapsed)
922 for(row = this; row; row = row.parent)
924 if(row.next) { row = row.next; break; }
930 private DataRow GetLastRow()
933 while(row && !row.collapsed && row.subRows.last)
934 row = row.subRows.last;
941 SelectedFlag selectedFlag;
952 public class ListBox : CommonControl
954 hasVertScroll = true;
955 // background = white;
957 snapVertScroll = true;
959 class_property(icon) = "<:ecere>controls/listBox.png";
963 property bool freeSelect { property_category $"Behavior" set { style.freeSelect = value; } get { return style.freeSelect; } };
964 property DataRow currentRow { property_category $"Private" /*"Behavior"*/ set { if(this) SetCurrentRow(value, false); } get { return this ? currentRow : null; } };
965 property DataField currentField
967 get { return currentField; }
968 // TODO: Document what this does
971 currentField = value;
972 HideEditBox(true, true, false);
973 if(value && value.editable)
974 PopupEditBox(currentField, false);
978 property int rowHeight
980 property_category $"Appearance"
981 isset { return style.heightSet; }
986 style.heightSet = true;
988 SetScrollLineStep(8, value);
992 style.heightSet = false;
997 get { return this ? rowHeight : 0; }
999 property Seconds typingTimeout
1001 property_category $"Behavior"
1004 typedString[0] = '\0';
1005 typingTimer.delay = value;
1006 typingTimeOut = value;
1008 get { return typingTimeOut; }
1010 property bool moveRows { property_category $"Behavior" set { style.moveRows = value; } get { return style.moveRows; } };
1011 property bool moveFields { property_category $"Behavior" set { style.moveFields = value; } get { return style.moveFields; } };
1012 property bool resizable { property_category $"Behavior" set { style.resizable = value; } get { return style.resizable; } };
1013 property bool autoScroll { property_category $"Behavior" set { style.autoScroll = value; } get { return style.autoScroll; } };
1014 property bool alwaysHighLight { property_category $"Appearance" set { style.alwaysHL = value; } get { return style.alwaysHL; } };
1015 property bool hasClearHeader { property_category $"Appearance" set { style.clearHeader = value; if(value) property::hasHeader = true; } get { return style.clearHeader; } };
1016 property bool hasHeader
1018 property_category $"Appearance"
1021 if(value && !style.header)
1027 bevel = !guiApp.textMode && !style.clearHeader;
1028 dontScrollVert = true;
1031 NotifyPushed = HeaderPushed;
1032 NotifyClicked = HeaderClicked;
1033 NotifyDoubleClick = HeaderDoubleClicked;
1034 NotifyReleased = HeaderReleased;
1035 NotifyMouseMove = HeaderMouseMove;
1039 endBevel.visible = false;
1041 style.header = value;
1043 get { return style.header; }
1045 property bool multiSelect { property_category $"Behavior" set { style.multiSelect = value; } get { return style.multiSelect; } };
1046 property bool alwaysEdit { property_category $"Behavior" set { style.alwaysEdit = value; } get { return style.alwaysEdit; } };
1047 property bool fullRowSelect { property_category $"Appearance" set { style.fullRowSelect = value; } get { return style.fullRowSelect; } };
1048 property bool collapseControl { property_category $"Appearance" set { style.collapse = value; } get { return style.collapse; } };
1049 property bool treeBranches { property_category $"Appearance" set { style.treeBranch = value; } get { return style.treeBranch; } };
1050 property bool rootCollapseButton { property_category $"Appearance" set { style.rootCollapse = value; } get { return style.rootCollapse; } };
1051 property bool sortable { property_category $"Behavior" set { style.sortable = value; } get { return style.sortable; } };
1052 property bool noDragging { property_category $"Behavior" set { style.noDragging = value; } get { return style.noDragging; } };
1053 property bool fillLastField
1055 property_category $"Behavior"
1058 style.fillLastField = value;
1060 get { return style.fillLastField; }
1062 property int numSelections
1066 int numSelections = 0;
1067 if(this && style.multiSelect)
1070 for(row = rows.first; row; row = row.GetNextRow())
1071 if(row.selectedFlag == selected || row.selectedFlag == tempSelected)
1074 return numSelections;
1077 property int currentIndex
1079 get { return currentRow ? currentRow.index : -1; }
1081 property DataRow lastRow { get { return this ? rows.last : null; } };
1082 property DataRow firstRow { get { return this ? rows.first : null; } };
1083 property int rowCount { get { return rowCount; } };
1084 property DataField firstField { get { return this ? fields.first : null; } };
1085 property Color selectionColor { set { selectionColor = value; } get { return selectionColor; } isset { return selectionColor ? true : false; } };
1086 property Color selectionText { set { selectionText = value; } get { return selectionText; } isset { return selectionText ? true : false; } };
1087 property Color stippleColor { set { stippleColor = value; } get { return stippleColor; } };
1088 property bool expandOnAdd { set { style.expandOnAdd = value; } get { return style.expandOnAdd; } };
1091 virtual bool Window::NotifySelect(ListBox listBox, DataRow row, Modifiers mods);
1092 virtual bool Window::NotifyHighlight(ListBox listBox, DataRow row, Modifiers mods);
1093 virtual bool Window::NotifyReclick(ListBox listBox, DataRow row, Modifiers mods);
1094 virtual bool Window::NotifySort(ListBox listBox, DataField field, Modifiers mods);
1095 virtual bool Window::NotifyChanged(ListBox listBox, DataRow row);
1096 virtual bool Window::NotifyKeyDown(ListBox listBox, DataRow row, Key key, unichar ch);
1097 virtual bool Window::NotifyEdited(ListBox listBox, DataRow row);
1098 virtual bool Window::NotifyEditDone(ListBox listBox, DataRow row);
1099 virtual bool Window::NotifyMovedField(ListBox listBox, DataField field, Modifiers mods);
1100 virtual bool Window::NotifyMove(ListBox listBox, DataRow row, Modifiers mods);
1101 virtual bool Window::NotifyDoubleClick(ListBox listBox, int x, int y, Modifiers mods);
1102 virtual bool Window::NotifyRightClick(ListBox listBox, int x, int y, Modifiers mods);
1103 virtual bool Window::NotifyResized(ListBox listBox, DataField field, Modifiers mods);
1104 virtual bool Window::NotifyCollapse(ListBox listBox, DataRow row, bool collapsed);
1105 virtual bool Window::NotifyKeyHit(ListBox listBox, DataRow row, Key key, unichar ch);
1106 virtual bool Window::NotifyModified(ListBox listBox, DataRow row);
1107 virtual bool Window::NotifyEditing(ListBox listBox, DataRow row);
1110 private void CheckConsistency()
1115 for(r = rows.first; r; r = r.GetNextRow())
1117 if(r.index != index++)
1125 void AddField(DataField addedField)
1130 if(fields.first && ((DataField)fields.first).defaultField)
1132 DataField defaultField = fields.first;
1133 defaultField.Free();
1134 delete defaultField;
1138 addedField = DataField { };
1142 addedField.listBox = this;
1143 fields.Add(addedField);
1145 addedField.sortOrder = 1;
1146 addedField.index = numFields;
1150 addedField.headButton.Destroy(0);
1151 delete addedField.headButton;
1152 addedField.headButton = Button
1157 dontScrollVert = true;
1158 id = (int64)(intptr)addedField;
1159 text = addedField.header;
1160 bevel = (!guiApp.textMode && !style.clearHeader);
1162 alignment = addedField.alignment;
1163 NotifyPushed = HeaderPushed;
1164 NotifyClicked = HeaderClicked;
1165 NotifyDoubleClick = HeaderDoubleClicked;
1166 NotifyReleased = HeaderReleased;
1167 NotifyMouseMove = HeaderMouseMove;
1169 incref addedField.headButton;
1170 addedField.headButton.Create();
1173 addedField.headButton.background = Color { 0, 170, 0 };
1179 for(row = rows.first; row; )
1181 int size = (field.dataType && field.dataType.typeSize) ?
1182 (sizeof(class ListBoxCell) + field.dataType.typeSize - sizeof(void *)) : sizeof(class ListBoxCell);
1183 ListBoxCell cell = (ListBoxCell)new0 byte[size];
1184 row.cells.Add(cell);
1185 FillBytes(cell.data, 0, size - (uint)(uintptr)&((ListBoxCell)0).data);
1188 if(row.subRows.first)
1189 row = row.subRows.first;
1192 for(; row; row = row.parent)
1194 if(row.next) { row = row.next; break; }
1199 OnResize(clientSize.w, clientSize.h);
1208 Clear(); // Ensure data is cleared first
1209 while((field = fields.first))
1214 endBevel.visible = false;
1219 void RemoveField(DataField field)
1225 int index = field.index;
1228 if(sortField == field)
1231 for(row = rows.first; row; )
1236 for(cell = row.cells.first, c = 0; c < index && cell; c++, cell = cell.next);
1237 if(cell && index == c)
1241 // TOCHECK: Is this check good? Will need incref/decref sometime?
1242 if(field.dataType.type == normalClass || field.dataType.type == noHeadClass)
1244 if(cell.data[0] && field.freeData)
1245 ((void (*)(void *, void *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnFree])(field.dataType, cell.data[0]);
1248 ((void (*)(void *, void *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnFree])(field.dataType, cell.data);
1251 row.cells.Remove(cell);
1255 if(row.subRows.first)
1256 row = row.subRows.first;
1259 for(; row; row = row.parent)
1261 if(row.next) { row = row.next; break; }
1270 endBevel.visible = false;
1274 DataRow AddRowNone()
1276 DataRow row { noneRow = true };
1283 rows.Insert(null, row);
1286 for(search = row.GetNextRow(); search; search = search.GetNextRow())
1292 firstRowShown = row;
1296 (rowCount * rowHeight) +
1297 ((style.header) ? rowHeight : 0) -
1298 ((!((clientSize.h+1) % rowHeight)) ? rowHeight : 0), true);
1299 if(style.autoScroll)
1300 SetScrollPosition(0, MAXINT - rowHeight);
1301 modifiedDocument = true;
1320 // Find very last row
1323 for(lastRow = rows.last; lastRow && !lastRow.collapsed && lastRow.subRows.last; lastRow = lastRow.subRows.last);
1324 row.index = lastRow ? (lastRow.index + 1) : 0;
1332 for(c = 0; c<fields.count; c++)
1334 for(field = fields.first; field; field = field.next)
1335 if((int)field.index == c)
1339 int size = (field.dataType && field.dataType.typeSize) ?
1340 (sizeof(class ListBoxCell) + field.dataType.typeSize - sizeof(void *)) : sizeof(class ListBoxCell);
1341 ListBoxCell cell = (ListBoxCell) new0 byte[size];
1342 row.cells.Add(cell);
1343 FillBytes(cell.data, 0, size - (uint)(uintptr)&((ListBoxCell)0).data);
1350 firstRowShown = row;
1356 (rowCount * rowHeight) +
1357 ((style.header) ? rowHeight : 0) -
1358 ((!((clientSize.h+1) % rowHeight)) ? rowHeight : 0), true);
1359 if(style.autoScroll)
1360 SetScrollPosition(0, MAXINT - rowHeight);
1361 modifiedDocument = true;
1372 DataRow AddRowAfter(DataRow after)
1384 if(after && after.subRows.first && !after.collapsed)
1386 for(search = after.subRows.last; !search.collapsed && search.subRows.last; )
1387 search = search.subRows.last;
1388 row.index = search.index + 1;
1391 row.index = after ? (after.index + 1) : 0;
1392 rows.Insert(after, row);
1395 for(search = row.GetNextRow(); search; search = search.GetNextRow())
1400 for(c = 0; c<fields.count; c++)
1402 for(field = fields.first; field; field = field.next)
1403 if((int)field.index == c)
1407 int size = (field.dataType && field.dataType.typeSize) ?
1408 (sizeof(class ListBoxCell) + field.dataType.typeSize - sizeof(void *)) : sizeof(class ListBoxCell);
1409 ListBoxCell cell = (ListBoxCell) new0 byte[size];
1410 row.cells.Add(cell);
1411 FillBytes(cell.data, 0, size - (uint)(uintptr)&((ListBoxCell)0).data);
1415 if(!firstRowShown || !after)
1417 firstRowShown = row;
1422 (rowCount * rowHeight) +
1423 ((style.header) ? rowHeight : 0) -
1424 ((!((clientSize.h+1) % rowHeight)) ? rowHeight : 0), true);
1425 if(style.autoScroll)
1426 SetScrollPosition(0, MAXINT - rowHeight);
1427 modifiedDocument = true;
1437 DataRow AddStringf(const char * format, ...)
1443 char string[MAX_F_STRING];
1446 va_start(args, format);
1447 vsnprintf(string, sizeof(string), format ? format : "", args);
1448 string[sizeof(string)-1] = 0;
1452 row.SetData(fields.first, string);
1458 DataRow AddString(const char * string)
1464 row.SetData(fields.first, string);
1470 void SelectRow(DataRow row)
1472 SetCurrentRow(row, true);
1475 void DeleteRow(DataRow row)
1477 if(!row) row = currentRow;
1480 DataRow sub, next, search;
1481 // Trying to move this here (Messed up deleting watches)
1482 //HideEditBox(false, false, true);
1485 for(sub = row.subRows.first; sub; sub = next)
1491 if(row.parent.IsExpanded())
1493 for(search = row.GetNextRow(); search; search = search.GetNextRow())
1498 HideEditBox(false, false, true);
1500 if(row == clickedRow)
1502 clickedRow = row.GetNextRow();
1504 clickedRow = row.GetPrevRow();
1507 if(row == currentRow)
1509 DataRow newCurrentRow = row.GetNextRow();
1511 newCurrentRow = row.GetPrevRow();
1512 SetCurrentRow(newCurrentRow, true);
1515 if(row == firstRowShown)
1517 firstRowShown = row.GetPrevRow();
1519 firstRowShown = row.GetNextRow();
1522 (row.parent ? row.parent.subRows: rows).Remove(row);
1525 //HideEditBox(false, false, true);
1529 (this.rowCount * rowHeight) +
1530 ((style.header) ? rowHeight : 0) -
1531 ((!((clientSize.h+1) % rowHeight)) ? rowHeight : 0), true);
1533 modifiedDocument = true;
1542 DataRow FindRow(int64 tag)
1547 for(row = rows.first; row; row = row.next)
1549 if(!row.noneRow && row.tag == tag)
1557 DataRow FindString(const char * searchedString)
1560 bool checkNextField = true;
1562 for(field = fields.first; field; field = field.next)
1564 if(field.dataType._vTbl[__ecereVMethodID_class_OnGetString])
1567 for(row = rows.first; row; row = row.GetNextRow())
1571 void * data = row.GetData(field);
1572 char tempString[1024] = "";
1573 bool needClass = false;
1574 const char * string = ((const char *(*)(void *, void *, char *, void *, bool *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString])(field.dataType, data, tempString, null, &needClass);
1576 if(string && string[0])
1577 checkNextField = false;
1578 if(string && string[0] && !strcmp(string, searchedString))
1583 if(!checkNextField) break;
1588 DataRow FindSubString(const char * subString)
1591 bool checkNextField = true;
1592 int len = subString ? strlen(subString) : 0;
1596 for(field = fields.first; field; field = field.next)
1598 if(field.dataType._vTbl[__ecereVMethodID_class_OnGetString])
1601 for(row = rows.first; row; row = row.GetNextRow())
1605 void * data = row.GetData(field);
1606 char tempString[1024] = "";
1607 bool needClass = false;
1608 const char * string = ((const char *(*)(void *, void *, char *, void *, bool *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString])(field.dataType, data, tempString, null, &needClass);
1610 if(string && string[0])
1611 checkNextField = false;
1612 if(string && string[0] && !strncmp(string, subString, len))
1617 if(!checkNextField) break;
1623 DataRow FindSubStringi(const char * subString)
1626 bool checkNextField = true;
1627 int len = subString ? strlen(subString) : 0;
1628 DataRow result = null;
1629 const char * bestResult = null;
1634 for(field = fields.first; field; field = field.next)
1636 if(field.dataType._vTbl[__ecereVMethodID_class_OnGetString])
1639 for(row = rows.first; row; row = row.GetNextRow())
1643 void * data = row.GetData(field);
1644 char tempString[1024] = "";
1645 bool needClass = false;
1646 const char * string = ((const char *(*)(void *, void *, char *, void *, bool *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString])(field.dataType, data, tempString, null, &needClass);
1648 if(string && string[0])
1649 checkNextField = false;
1650 if(string && string[0])
1652 int stringLen = strlen(string);
1654 if(!strnicmp(string, subString, Min(len, stringLen)))
1656 if(bestLen < Min(len, stringLen))
1658 if(!bestResult || strcmpi(string, bestResult) < 0)
1660 bestLen = Min(len, stringLen);
1661 bestResult = string;
1669 if(!checkNextField) break;
1675 DataRow FindSubStringAfter(DataRow after, const char * subString)
1678 bool checkNextField = true;
1679 int len = subString ? strlen(subString) : 0;
1683 for(field = fields.first; field; field = field.next)
1685 if(field.dataType._vTbl[__ecereVMethodID_class_OnGetString])
1688 for(row = after.GetNextRow(); row && row != after; row = row.GetNextRow(), (!row) ? (row = rows.first) : null)
1692 void * data = row.GetData(field);
1693 char tempString[1024] = "";
1694 bool needClass = false;
1695 const char * string = ((const char *(*)(void *, void *, char *, void *, bool *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString])(field.dataType, data, tempString, null, &needClass);
1697 if(string && string[0])
1698 checkNextField = false;
1699 if(string && string[0] && !strncmp(string, subString, len))
1704 if(!checkNextField) break;
1710 DataRow FindSubRow(int64 tag)
1716 for(row = rows.first; row; row = row.next)
1718 if(!row.noneRow && row.tag == tag)
1720 if(!row.noneRow && row.subRows.first)
1722 DataRow subRow = row.FindSubRow(tag);
1736 Window master = this.master;
1738 HideEditBox(false, true, false);
1739 editData.Destroy(0);
1741 firstRowShown = currentRow = null;
1746 if(style.freeSelect)
1747 NotifyHighlight(master, this, currentRow ? currentRow : null, 0);
1749 NotifySelect(master, this, currentRow ? currentRow : null, 0);
1752 if(style.alwaysEdit && currentRow)
1753 currentRow.Edit(currentField);
1760 (this.rowCount * rowHeight) +
1761 ((style.header) ? rowHeight : 0) -
1762 ((rowHeight && !((clientSize.h+1) % rowHeight)) ? rowHeight : 0), true);
1767 void Sort(DataField field, int order)
1772 int headerSize = ((style.header) ? rowHeight : 0);
1773 int height = clientSize.h + 1 - headerSize;
1775 if(!field) field = fields.first;
1777 field.sortOrder = order ? order : 1;
1778 rows.Sort(DataRow::Compare, field);
1780 for(search = rows.first; search; search = search.next)
1781 search._SortSubRows(field, order);
1785 for(search = rows.first; search; search = search.GetNextRow())
1786 search.index = index++;
1789 if(currentRow && currentRow.index * rowHeight > scroll.y + height - rowHeight)
1790 SetScrollPosition(scroll.x, currentRow.index * rowHeight - height + rowHeight);
1791 else if(!currentRow || currentRow.index * rowHeight < scroll.y)
1792 SetScrollPosition(scroll.x, currentRow ? currentRow.index * rowHeight : 0);
1794 OnVScroll(0, scroll.y, 0);
1796 // SetScrollPosition(0, scroll.y);
1801 void StopEditing(bool save)
1803 HideEditBox(save, false, true);
1806 void GetMultiSelection(OldList list)
1809 if(this && style.multiSelect)
1813 for(row = rows.first; row; row = row.GetNextRow())
1815 if(row.selectedFlag == selected || row.selectedFlag == tempSelected)
1817 list.Add(OldLink { data = row });
1823 // Convenience Current Row Methods
1824 void * SetData(DataField field, any_object data)
1826 return currentRow.SetData(field, data);
1829 any_object GetData(DataField field)
1831 return (void *)currentRow.GetData(field);
1836 return currentRow ? currentRow.tag : 0;
1842 DataField defaultField { };
1843 rows.offset = (uint)(uintptr)&((DataRow)0).prev;
1844 fields.offset = (uint)(uintptr)&((DataField)0).prev;
1845 style.fullRowSelect = true;
1846 style.fillLastField = true;
1847 style.expandOnAdd = true;
1848 typingTimeOut = 0.5;
1849 rowHeight = 16; // Stuff depending on creation and default property checking
1852 defaultField.defaultField = true;
1854 AddField(defaultField);
1856 typedString = new char[1];
1857 typedString[0] = '\0';
1872 while((field = fields.first))
1874 // fields.Remove(field);
1884 while((row = rows.first))
1891 ListBoxCell GetCell(DataRow * row, DataField * field)
1893 ListBoxCell cell = null;
1894 if(!*row) *row = currentRow;
1897 if(!*field) *field = this ? currentField : null;
1900 for(*field = fields.first; (*field).index != 0; *field = (*field).next);
1905 if(field->listBox == this)
1907 for(index = 0, cell = (*row).cells.first; cell && index != (*field).index; index++, cell = cell.next);
1914 void HideEditBox(bool save, bool alwaysStopEdit, bool repositionOnly)
1916 if(editData && editData.visible)
1919 editData.SaveData();
1921 editData.visible = false;
1922 NotifyEditDone(master, this, currentRow);
1924 // ENSURE DATA BOX IS NOT VISIBLE
1925 editData.visible = false;
1927 if(style.alwaysEdit && !alwaysStopEdit)
1930 int height = rowHeight - (style.alwaysEdit ? 1 : 0);
1931 int y = currentRow.index * rowHeight + (style.header ? rowHeight : 0);
1932 int x = currentField.x;
1933 int width = (!currentField.next && style.fillLastField && (!hasHorzScroll || clientSize.w - currentField.x > currentField.width + EXTRA_SPACE)) ?
1934 clientSize.w - currentField.x : (currentField.width + EXTRA_SPACE);
1936 if(!style.alwaysEdit)
1938 editData.position = { x, y };
1939 editData.size = { width, height };
1943 editData.position = { x, y - editData.clientStart.y };
1944 editData.size = { width, height + editData.clientStart.y * 2 };
1946 editData.visible = true;
1947 if(style.alwaysEdit)
1948 editData.Deactivate();
1950 PopupEditBox(currentField, repositionOnly);
1954 currentField = null;*/
1958 void SetCurrentRow(DataRow row, bool notify)
1960 if(this && (currentRow != row || (currentRow && currentRow.selectedFlag == unselected)))
1962 int headerSize = ((style.header) ? rowHeight : 0);
1963 int height = clientSize.h + 1 - headerSize;
1965 // true: destroy edit box
1966 HideEditBox(true, true, false);
1968 if(!(style.multiSelect) && currentRow)
1969 currentRow.selectedFlag = unselected;
1973 if(style.multiSelect)
1977 for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
1978 selRow.selectedFlag = unselected;
1980 currentRow.selectedFlag = selected;
1985 currentRow.selectedFlag = selected;
1987 if(currentRow && currentRow.index * rowHeight > scroll.y + height - rowHeight)
1988 SetScrollPosition(scroll.x,
1989 currentRow.index * rowHeight - height + rowHeight);
1990 else if(!currentRow || currentRow.index * rowHeight < scroll.y)
1992 int line = currentRow ? currentRow.index * rowHeight : 0;
1993 //SNAPUP(line, rowHeight);
1994 SetScrollPosition(scroll.x, line);
1999 Window master = this.master;
2002 if(style.freeSelect && visible)
2003 NotifyHighlight(master, this, currentRow ? currentRow : null, 0);
2005 NotifySelect(master, this, currentRow ? currentRow : null, 0);
2006 if(style.alwaysEdit && currentRow)
2007 currentRow.Edit(currentField);
2015 void RepositionFieldEditor()
2017 if(editData && editData.visible)
2019 int height = rowHeight - (style.alwaysEdit ? 1 : 0);
2021 int y = currentRow.index * rowHeight + (style.header ? rowHeight : 0);
2025 if(style.collapse && !(style.treeBranch))
2027 for(field = fields.first; field; field = field.next)
2029 width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ?
2030 clientSize.w - field.x : (field.width + EXTRA_SPACE);
2031 if(field == currentField) break;
2034 if(!style.alwaysEdit)
2036 editData.position = { x, y - editData.clientStart.y };
2037 editData.size = { width, height + editData.clientStart.y * 2 };
2041 editData.position = { x, y };
2042 editData.size = { width, height };
2047 void PopupEditBox(DataField whichField, bool repositionOnly)
2049 if((!editData || !editData.visible || currentField != whichField) && currentRow)
2051 // true: destroy edit box
2052 HideEditBox(true, true, false);
2055 int height = rowHeight - (style.alwaysEdit ? 1 : 0);
2057 int y = currentRow.index * rowHeight + (style.header ? rowHeight : 0);
2059 //void * data = currentRow.GetData(whichField);
2064 if(style.collapse && !(style.treeBranch))
2067 for(field = fields.first; field; field = field.next)
2069 width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ?
2070 clientSize.w - field.x : (field.width + EXTRA_SPACE);
2071 if(field == whichField) break;
2075 currentField = whichField;
2076 cell = GetCell(&row, ¤tField);
2083 background = dataBoxBackground;
2084 foreground = dataBoxForeground;
2086 bool NotifyChanged(DataBox dataBox, bool closingDropDown)
2089 DataField field = null;
2090 ListBoxCell cell = GetCell(&row, &field);
2094 modifiedDocument = true;
2096 NotifyChanged(master, this, currentRow);
2101 bool NotifyModified()
2103 //DataRow row = null;
2104 //DataField field = null;
2105 //ListBoxCell cell = GetCell(&row, &field);
2106 //cell.isSet = true;
2107 modifiedDocument = true;
2109 NotifyModified(master, this, currentRow);
2113 bool OnKeyDown(Key key, unichar ch)
2115 bool result = DataBox::OnKeyDown(key, ch);
2116 if(visible && active) // Added this check here, because we will not use enter/escape otherwise, and lose DataBox's result
2118 if((SmartKey)key == enter || (SmartKey)key == escape)
2127 editData.Destroy(0);
2128 editData.type = whichField.dataType;
2129 editData.fieldData = whichField.userData;
2130 editData.borderStyle = style.alwaysEdit ? 0 : deep;
2131 editData.data = cell ? cell.data : null;
2134 // Might not really need this anymore...
2135 NotifyEditing(master, this, currentRow);
2138 if(!style.alwaysEdit)
2140 editData.position = { x, y - editData.clientStart.y };
2141 editData.size = { width, height + editData.clientStart.y * 2 };
2145 editData.position = { x, y };
2146 editData.size = { width, height };
2150 editData.visible = true;
2152 if(style.alwaysEdit)
2153 editData.Deactivate();
2155 // MOVED THIS HIGHER FOR DATALIST EDITOR
2157 // Might not really need this anymore...
2158 NotifyEdited(master, this, currentRow);
2163 void OnRedraw(Surface surface)
2166 int y = (style.header) ? rowHeight : 0;
2167 bool isActive = active;
2168 Font font = fontObject;
2169 Font boldFont = this.boldFont.font;
2173 if(style.alwaysEdit && style.fullRowSelect)
2176 int y = (style.header) ? rowHeight : 0;
2177 int x = -scroll.x - 1;// + EXTRA_SPACE;// / 2 - 1;
2178 int w = clientSize.w;
2179 int h = clientSize.h;
2183 // Fill out indent column
2184 if(style.collapse && !(style.treeBranch) && (style.header || rows.first))
2187 surface.SetBackground(formColor);
2188 surface.Area(-scroll.x, 0, x, clientSize.h);
2191 surface.SetForeground(formColor);
2192 for(row = firstRowShown; row; row = row.GetNextRow())
2195 surface.HLine(x + 1, w-1, y-1);
2201 for(field = fields.first; field; field = field.next)
2203 int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ?
2204 clientSize.w - field.x : (field.width + EXTRA_SPACE);
2205 if(field.prev && y > 0)
2206 surface.VLine(0, y-1, x);
2211 surface.foreground = this.foreground;
2212 surface.TextOpacity(false);
2214 // Draw the tree branches
2215 if(style.treeBranch)
2217 int y = -scroll.y + ((style.header) ? rowHeight : 0);
2218 surface.LineStipple(0x5555);
2219 surface.SetForeground(branchesColor);
2220 for(row = rows.first; row; row = row.GetNextRow() )
2222 int x = -scroll.x + EXTRA_SPACE / 2-1;
2223 int rowStart = -scroll.x;
2228 for(parent = row.parent; parent; parent = parent.parent)
2229 if(!parent.header) indent += 20;
2230 if(style.rootCollapse) indent += 20;
2232 plusIndent = (style.treeBranch) ? (indent - 20 + 4) : indent;
2237 if(row.subRows.first)
2240 int y1 = y + PLUSY + 4;
2244 for(child = row.collapsed ? null : row.subRows.first; child && child != row; )
2247 if(child.subRows.first && !child.collapsed && child != row.subRows.last)
2248 child = child.subRows.first;
2253 for(; child && child != row; child = child.parent)
2263 y2 = y + numRows * rowHeight + PLUSY + 4;
2264 surface.VLine(y1, y2, rowStart + plusIndent + 7 + 20);
2266 surface.HLine(rowStart + plusIndent + 7, rowStart + indent, y + PLUSY + 4);
2269 if(y >= clientSize.h)
2272 // Root Vertical Lines
2273 if(style.rootCollapse && rows.first)
2278 y = -scroll.y + ((style.header) ? rowHeight : 0);
2280 for(child = rows.first; child && child != rows.last; )
2283 if(child.subRows.first && !child.collapsed && child != rows.last)
2284 child = child.subRows.first;
2289 for(; child; child = child.parent)
2299 y2 = y + numRows * rowHeight + PLUSY + 4;
2300 surface.VLine(y1, y2, -scroll.x + 11);
2302 surface.LineStipple(0);
2305 for(row = firstRowShown; row; row = row.GetNextRow() )
2307 int x = -scroll.x + EXTRA_SPACE / 2-1;
2310 Color foreground = this.foreground /*black*/, background = this.background /*white*/;
2311 DataDisplayFlags dataDisplayFlags = 0;
2312 int rowStart = -scroll.x;
2315 Bitmap icon = row.icon ? row.icon.bitmap : null;
2316 int collapseRowStart = 0;
2317 bool lastWasHeader = row.header;
2319 for(parent = row.parent; parent; parent = parent.parent)
2321 if(!parent.header || lastWasHeader)
2323 if(style.treeBranch)
2329 if(style.rootCollapse) indent += 20;
2332 dataDisplayFlags.fullRow = style.fullRowSelect;
2333 dataDisplayFlags.active = isActive;
2334 dataDisplayFlags.header = row.header;
2339 collapseRowStart = rowStart;
2341 if(!(style.treeBranch))
2348 if(style.multiSelect)
2350 dataDisplayFlags.selected = row.selectedFlag == selected || row.selectedFlag == tempSelected;
2351 dataDisplayFlags.current = row == currentRow || (!currentRow && row == firstRowShown);
2355 dataDisplayFlags.selected = row.selectedFlag == selected || row.selectedFlag == tempSelected;
2356 dataDisplayFlags.current = row == currentRow || (!currentRow && row == firstRowShown);
2358 if(row == currentRow)
2360 dataDisplayFlags.current = true;
2361 dataDisplayFlags.selectedFlag = true;
2363 else if(!currentRow && row == firstRowShown)
2365 dataDisplayFlags.current = true;
2369 surface.TextOpacity(true);
2371 background = this.background;
2372 foreground = this.foreground;
2374 // Draw the current row background
2377 Color colors[] = { formColor, azure, mistyRose, linen, floralWhite, lavender, lavenderBlush, lemonChiffon };
2380 while((p = p.parent)) level++;
2381 background = colors[level % (sizeof(colors)/sizeof(colors[0]))];
2382 surface.SetBackground(background);
2383 surface.Area(rowStart, y, clientSize.w, (y + rowHeight) - 1);
2384 foreground = branchesColor;
2386 else if(dataDisplayFlags.selected)
2388 if(dataDisplayFlags.selected && (isActive || style.alwaysHL || (style.alwaysEdit && style.fullRowSelect)))
2390 if(!isActive && style.alwaysEdit)
2391 background = formColor;
2393 background = selectionColor ? selectionColor : SELECTION_COLOR;
2394 if(style.fullRowSelect)
2396 int offset = (style.alwaysEdit) ? 2 : 1;
2397 surface.SetBackground(background);
2398 surface.Area(rowStart, y, clientSize.w, (y + rowHeight) - offset);
2400 if(isActive || !(style.alwaysEdit))
2401 foreground = selectionText ? selectionText : SELECTION_TEXT;
2403 foreground = branchesColor;
2409 surface.Blit(icon, x + (20 - icon.width) /2,y + 2,0,0, icon.width, icon.height);
2415 int width = clientSize.w;
2417 dataDisplayFlags.firstField = true;
2418 clip.left = x - EXTRA_SPACE / 2+1;
2420 clip.right = x + width - EXTRA_SPACE/2 - 0;
2421 clip.bottom = y + rowHeight - 1;
2422 surface.Clip(&clip);
2424 surface.TextFont(font);
2426 surface.SetForeground(foreground);
2427 surface.SetBackground(background);
2429 ((void (*)(void *, const void *, void *, int, int, int, void *, uint, uint))(void *)class(String)._vTbl[__ecereVMethodID_class_OnDisplay])(class(String), "(none)", surface, x, y - 1 + (rowHeight - fontH)/2, width - EXTRA_SPACE/2, null, Alignment::left, dataDisplayFlags);
2433 if(opacity < 1) surface.TextOpacity(false);
2435 for(field = fields.first; field; field = field.next)
2438 int width = ((!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) || row.header) ?
2439 clientSize.w - field.x : (field.width + EXTRA_SPACE);
2441 // Box clip = { x, y+1, x + field.width - EXTRA_SPACE - 1, y + rowHeight - 2 };
2444 //width -= EXTRA_SPACE;
2446 if(!field.prev) width -= indent;
2449 dataDisplayFlags.firstField = field.prev ? false : true;
2451 if(!dataDisplayFlags.firstField && !dataDisplayFlags.fullRow)
2453 background = this.background;
2454 foreground = this.foreground;
2457 if(!isActive && dataDisplayFlags.selected && style.alwaysEdit && field.editable && opacity)
2460 surface.SetBackground(background);
2461 surface.Area(x-3, y, x+width-1, y + rowHeight-2);
2464 clip.left = x - EXTRA_SPACE / 2+1;
2466 clip.right = x + width - EXTRA_SPACE/2 - 0;
2467 clip.bottom = y + rowHeight - 1;
2468 surface.Clip(&clip);
2470 for(index = 0, cell = row.cells.first; cell && index != field.index; index++, cell = cell.next);
2471 // Should always be as many cells in the row as fields in the listbox
2472 if(cell && cell.isSet && field.dataType)
2475 surface.TextFont(boldFont);
2477 surface.TextFont(font);
2479 surface.SetForeground(foreground);
2480 surface.SetBackground(background);
2482 if(field.dataType.type == noHeadClass || field.dataType.type == normalClass)
2483 ((void (*)(void *, void *, void *, int, int, int, void *, uint, uint))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnDisplay])(field.dataType, cell.data[0], surface, x, y - 1 + (rowHeight - fontH)/2, width - EXTRA_SPACE/2, field.userData, field.alignment, dataDisplayFlags);
2485 ((void (*)(void *, void *, void *, int, int, int, void *, uint, uint))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnDisplay])(field.dataType, cell.data, surface, x, y - 1 + (rowHeight - fontH)/2, width - EXTRA_SPACE/2, field.userData, field.alignment, dataDisplayFlags);
2488 if(!isActive && dataDisplayFlags.selected && style.alwaysEdit && field.editable)
2489 background = formColor;
2491 if(!dataDisplayFlags.firstField && !dataDisplayFlags.fullRow)
2493 background = formColor;
2494 foreground = this.background;
2497 x += width;// + EXTRA_SPACE;
2499 if(row.header) break;
2505 int plusIndent = (style.treeBranch) ? (indent - 20 + 4) : indent;
2507 if(row.subRows.first && (row.parent || !(style.treeBranch) || (style.rootCollapse)))
2509 surface.SetForeground(row.header ? headerCollapseForeground : this.foreground);
2510 surface.Rectangle(collapseRowStart + 3 + plusIndent, y + PLUSY, collapseRowStart + 11 + plusIndent, y + PLUSY + 8);
2512 surface.SetBackground(row.header ? (formColor) : (this.background)); //white
2513 surface.Area(collapseRowStart + 4 + plusIndent, y + PLUSY + 1, collapseRowStart + 10 + plusIndent, y + PLUSY + 7);
2515 surface.HLine(collapseRowStart + 5 + plusIndent, collapseRowStart + 9 + plusIndent, y+PLUSY+4);
2517 surface.VLine(y + PLUSY + 2, y + PLUSY + 6, collapseRowStart + 7 + plusIndent);
2522 // Draw the current row stipple
2523 if(style.fullRowSelect && !(style.alwaysEdit) && (dataDisplayFlags.current) && isActive)
2528 surface.LineStipple(0x5555);
2529 if(dataDisplayFlags.selected)
2530 surface.SetForeground(stippleColor);
2532 surface.SetForeground(this.foreground);
2535 surface.SetForeground(selectionColor ? selectionColor : SELECTION_COLOR);
2536 surface.Rectangle(0, y, clientSize.w-1, (y + rowHeight) - 1);
2537 surface.LineStipple(0);
2541 if(y >= clientSize.h)
2544 if(firstRowShown) surface.Clip(null);
2545 if(this.dragRow && this.dropIndex != -1)
2547 int dropIndex = this.dropIndex;
2550 if(!style.multiSelect && currentRow.index < this.dropIndex)
2552 surface.DrawingChar(223);
2554 y = style.header ? rowHeight : 0;
2555 y += dropIndex * rowHeight - scroll.y;
2557 surface.SetForeground(Color { 85, 85, 255 });
2558 surface.HLine(0, clientSize.w-1, y);
2559 surface.HLine(0, clientSize.w-1, y + 1);
2563 void OnDrawOverChildren(Surface surface)
2565 if(draggingField && this.dropField)
2567 int position = this.dropField.x;
2568 if(draggingField.x < position)
2569 position += this.dropField.width + EXTRA_SPACE;
2571 surface.SetForeground(Color { 85, 85, 255 });
2572 surface.VLine(0, rowHeight - 1, position - scroll.x - 2);
2573 surface.VLine(0, rowHeight - 1, position - scroll.x);
2575 if(sortField && !style.clearHeader && style.header)
2577 DataField field = sortField;
2578 int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ?
2579 clientSize.w - field.x : (field.width + EXTRA_SPACE);
2582 surface.TextExtent(field.header, strlen(field.header), &tw, &th);
2583 if(tw < width - EXTRA_SPACE)
2585 bool up = field.sortOrder == 1;
2589 field.x + 2 - scroll.x, 0,
2590 field.x + width + EXTRA_SPACE - 1 - scroll.x, rowHeight
2592 surface.Clip(&clip);
2593 if(field.alignment == left || field.alignment == center)
2595 if(field.alignment == center)
2596 x = field.x + (width + EXTRA_SPACE - tw) / 2 + tw + EXTRA_SPACE + 4;
2598 x = field.x + tw + EXTRA_SPACE + 4;
2600 x = Min(x, field.x + width - 4);
2602 else if(field.alignment == right)
2604 x = field.x + width - tw - 2*EXTRA_SPACE - 4;
2605 x = Max(x, field.x + 2);
2611 // surface.SetForeground((wmenu.selectedFlag == item) ? white : black);
2612 // surface.WriteText(clientSize.w-8, y+(wmenu.rh - 8)/2, "\020", 1);
2618 surface.SetForeground(Color { 128,128,128 } );
2619 surface.DrawLine(x + 3, y, x, y + 5);
2620 surface.PutPixel(x + 1, y + 5);
2621 surface.PutPixel(x + 1, y + 3);
2622 surface.PutPixel(x + 2, y + 1);
2624 surface.SetForeground(white);
2625 surface.DrawLine(x + 4, y, x + 7, y + 5);
2626 surface.PutPixel(x + 6, y + 5);
2627 surface.PutPixel(x + 6, y + 3);
2628 surface.PutPixel(x + 5, y + 1);
2630 surface.DrawLine(x, y + 6, x + 7, y + 6);
2634 surface.SetForeground(Color { 128,128,128 });
2635 surface.DrawLine(x + 3, y+6, x, y+1);
2636 surface.PutPixel(x + 1, y+1);
2637 surface.PutPixel(x + 1, y+3);
2638 surface.PutPixel(x + 2, y+5);
2640 surface.SetForeground(white);
2641 surface.DrawLine(x + 4, y+6, x + 7, y+1);
2642 surface.PutPixel(x + 6, y+1);
2643 surface.PutPixel(x + 6, y+3);
2644 surface.PutPixel(x + 5, y+5);
2646 surface.DrawLine(x, y, x + 7, y);
2655 void OnResize(int w, int h)
2658 bool showEndBevel = false;
2660 if(style.collapse && !style.treeBranch)
2662 for(field = fields.first; field; field = field.next)
2664 int width = field.width + EXTRA_SPACE;
2672 (rowCount * rowHeight) +
2673 ((style.header) ? rowHeight : 0) -
2674 ((!((clientSize.h+1) % rowHeight)) ? rowHeight : 0), true);
2676 for(field = fields.first; field; field = field.next)
2678 int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ?
2679 clientSize.w - field.x : (field.width + EXTRA_SPACE);
2680 if(style.header && field.headButton)
2682 showEndBevel = true;
2685 field.headButton.position = { field.x, 0 };
2686 field.headButton.size = { width, rowHeight };
2687 field.headButton.visible = true;
2690 field.headButton.visible = false;
2694 if(!style.fillLastField && showEndBevel && endBevel)
2696 endBevel.position = { x, 0 };
2697 endBevel.size = { clientSize.w + 2 - x, rowHeight };
2698 endBevel.visible = true;
2701 endBevel.visible = false;
2703 if(style.alwaysEdit && editData && editData.visible)
2705 HideEditBox(true, false, true);
2707 else if(editData && editData.visible)
2708 RepositionFieldEditor();
2711 void AdaptToFieldWidth(DataField field, bool doScroll)
2713 OnResize(clientSize.w, clientSize.h);
2715 // Scroll appropriately
2718 if(field.x + field.width + EXTRA_SPACE - clientSize.w > scroll.x &&
2719 field.x >= field.x + field.width + EXTRA_SPACE - clientSize.w)
2721 SetScrollPosition(field.x + field.width + EXTRA_SPACE - clientSize.w, scroll.y);
2723 else if(field.x + field.width + EXTRA_SPACE - clientSize.w > scroll.x ||
2725 SetScrollPosition(field.x, scroll.y);
2730 bool HeaderPushed(Button control, int x, int y, Modifiers mods)
2732 DataField field = (DataField)(intptr)control.id;
2733 // false: dont destroy edit box
2734 HideEditBox(true, false, true);
2735 if(style.resizable && ((!field && x < RESIZE_BORDER && fields.last) ||
2736 (field && x < RESIZE_BORDER && field.prev) ||
2737 (field && x >= control.clientSize.w - RESIZE_BORDER)))
2740 field = fields.last;
2741 else if(x < RESIZE_BORDER && field.prev)
2744 if(field.fixed) return false;
2745 resizingField = field;
2746 this.resizeX = x + control.position.x;
2747 this.startWidth = field.width;
2748 this.oldX = x - scroll.x;
2753 if(field.fixed) return false;
2754 draggingField = field;
2755 if(style.moveFields)
2756 field.headButton.stayDown = true;
2757 else if(!style.sortable)
2765 bool HeaderMouseMove(Button control, int x, int y, Modifiers mods)
2770 DataField field = resizingField;
2772 x += control.position.x;
2774 // Tweak to prevent shrinking field if we're actually moving right
2775 if(x - scroll.x > this.oldX &&
2776 this.startWidth + x - this.resizeX < field.width)
2778 this.oldX = x - scroll.x;
2781 this.oldX = x - scroll.x;
2783 field.width = this.startWidth + x - this.resizeX;
2784 field.width = Max(field.width, - EXTRA_SPACE);
2786 AdaptToFieldWidth(field, true);
2788 else if(draggingField)
2790 x += control.position.x;
2791 if(style.moveFields)
2793 DataField field = fields.last;
2795 for(field = fields.first; field; field = field.next)
2797 fieldX += ((field.width || style.resizable) ?
2798 field.width : clientSize.w) + EXTRA_SPACE;
2802 if(draggingField == field)
2804 // Reset scroll position
2805 if(field.x + field.width + EXTRA_SPACE - clientSize.w > scroll.x &&
2806 field.x >= field.x + field.width + EXTRA_SPACE - clientSize.w)
2809 field.x + field.width + EXTRA_SPACE - clientSize.w,
2812 else if(field.x + field.width + EXTRA_SPACE - clientSize.w > scroll.x ||
2814 SetScrollPosition(field.x, scroll.y);
2817 if(this.dropField != field)
2819 this.dropField = field;
2822 int position = field.x;
2824 if(draggingField.x < position)
2826 position += field.width + EXTRA_SPACE - clientSize.w;
2827 if(position > scroll.x)
2828 SetScrollPosition(position, scroll.y);
2833 if(position < scroll.x)
2834 SetScrollPosition(position, scroll.y);
2837 this.movingFields = true;
2843 else if(style.resizable)
2845 DataField field = (DataField)(intptr)control.id;
2848 if(x < RESIZE_BORDER && field.prev)
2850 if(!field.prev.fixed)
2851 control.cursor = guiApp.GetCursor(sizeWE);
2853 else if(x >= control.clientSize.w - RESIZE_BORDER)
2854 control.cursor = guiApp.GetCursor(sizeWE);
2856 control.cursor = null;
2860 if(x < RESIZE_BORDER && fields.last)
2861 control.cursor = guiApp.GetCursor(sizeWE);
2863 control.cursor = null;
2869 bool HeaderReleased(Button control, int x, int y, Modifiers mods)
2873 NotifyResized(master, this, resizingField, mods);
2874 resizingField = null;
2881 if(style.moveFields)
2886 DataField switchField = fields.last;
2890 x += draggingField.x;
2891 for(field = fields.first; field; field = field.next)
2893 fieldX += ((field.width || style.resizable) ?
2894 field.width : clientSize.w) + EXTRA_SPACE;
2897 switchField = field;
2901 if(switchField && draggingField != switchField && this.dropField)
2903 for(field = fields.first; field; field = field.next)
2905 if(field == switchField || field == draggingField)
2909 // Switch field first: move before
2910 if(field == switchField)
2911 draggingField.Move(switchField.prev);
2912 // Dragged field first: move after
2914 draggingField.Move(switchField);
2916 NotifyMovedField(master, this, draggingField, mods);
2918 draggingField.headButton.stayDown = false;
2923 movingFields = false;
2925 draggingField = null;
2931 bool HeaderClicked(Button control, int x, int y, Modifiers mods)
2933 if(style.header && !this.dropField && style.sortable)
2935 DataField field = (DataField)(intptr)control.id;
2936 if(sortField == field)
2937 field.sortOrder *= -1;
2942 Sort(sortField, field.sortOrder);
2943 NotifySort(master, this, field, mods);
2949 bool HeaderDoubleClicked(Button control, int x, int y, Modifiers mods)
2953 DataField field = (DataField)(intptr)control.id;
2956 if(x < RESIZE_BORDER && field.prev)
2958 else if(x >= control.clientSize.w - RESIZE_BORDER);
2964 if(x < RESIZE_BORDER && fields.last)
2965 field = fields.last;
2977 if(style.freeSelect)
2982 this.rolledOver = this.dragging = false;
2989 bool OnLoadGraphics()
2991 display.FontExtent(fontObject, "W", 1, null, &fontH);
2992 if(!style.heightSet)
2994 rowHeight = Max(fontH + 2, 16) + (style.alwaysEdit ? 1 : 0);
2995 SetScrollLineStep(8, rowHeight);
3000 void OnApplyGraphics()
3002 SetScrollLineStep(8, rowHeight);
3006 for(field = fields.first; field; field = field.next)
3008 if(field.headButton)
3010 field.headButton.bevel = (!guiApp.textMode && !style.clearHeader);
3012 field.headButton.background = Color { 0, 170, 0 };
3016 OnResize(clientSize.w, clientSize.h);
3019 bool OnResizing(int *w, int *h)
3023 if(!initSize.w && (!anchor.left.type || !anchor.right.type) && !*w)
3028 Font font = fontObject;
3029 Font boldFont = this.boldFont.font;
3030 Display display = this.display;
3032 for(row = rows.first; row; row = row.GetNextRow())
3034 Bitmap icon = row.icon ? row.icon.bitmap : null;
3035 int x = -scroll.x + EXTRA_SPACE / 2-1;
3039 for(parent = row.parent; parent; parent = parent.parent)
3043 if(style.treeBranch)
3049 if(style.rootCollapse) indent += 20;
3051 if(style.collapse && !(style.treeBranch)) x += 15;
3055 // Compute the rows size
3056 for(field = fields.first; field; field = field.next)
3058 if(((style.resizable && (!(style.alwaysEdit) || field.next)) || field.width) && !row.header)
3059 x += field.width - (field.prev ? 0 : indent);
3064 for(index = 0, cell = row.cells.first; index != field.index; index++, cell = cell.next);
3066 // Should always be as many cells in the row as fields in the listbox
3067 if(cell && cell.isSet && field.dataType)
3069 static char tempString[4096];
3070 const char * string;
3072 if(field.dataType.type == normalClass || field.dataType.type == noHeadClass)
3073 string = ((const char *(*)(void *, void *, char *, void *, bool *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString])(field.dataType, cell.data[0], tempString, field.userData, null);
3075 string = ((const char *(*)(void *, void *, char *, void *, bool *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString])(field.dataType, cell.data, tempString, field.userData, null);
3077 if(!string) string = "";
3078 display.FontExtent(row.header ? boldFont : font, string, strlen(string), &tw, &th);
3081 display.FontExtent(row.header ? boldFont : font, string, strlen(string), &tw, &th);
3083 display.FontExtent(row.header ? boldFont : font, "", 0, &tw, &th);
3086 if(row.header) break;
3090 maxWidth = Max(maxWidth, x);
3096 *h = Min(this.maxShown, this.rowCount) * rowHeight;
3101 if(!*w) *w = rowHeight * 5;
3102 if(!*h) *h = rowHeight * 5;
3109 FontResource font = this.font;
3110 FontResource boldFont
3112 faceName = font.faceName, size = font.size, bold = true
3114 AddResource(boldFont);
3115 RemoveResource(this.boldFont);
3116 this.boldFont = boldFont;
3120 SetInitSize(initSize);
3123 bool OnMouseMove(int x, int y, Modifiers mods)
3125 bool isTimer = false;
3126 int realX = x, realY = y;
3128 if(insideNotifySelect) return true;
3130 if(style.alwaysEdit && style.resizable &&
3131 resizingField && !(mods.isSideEffect))
3134 DataField field = resizingField;
3135 field.width = this.startWidth + x - this.resizeX;
3136 field.width = Max(field.width, - EXTRA_SPACE);
3138 AdaptToFieldWidth(field, true);
3142 if(style.alwaysEdit && style.resizable)
3144 int vx = -scroll.x - 1;
3147 if(style.collapse && !(style.treeBranch))
3150 for(field = fields.first; field; field = field.next)
3152 int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ?
3153 clientSize.w - field.x : (field.width + EXTRA_SPACE);
3159 cursor = guiApp.GetCursor(sizeWE);
3163 vx += width + EXTRA_SPACE;
3167 if((editData && editData.visible) || (style.alwaysEdit))
3170 if(x == MAXINT && y == MAXINT)
3177 // ADDED THIS CHECK FOR FieldDropBox LEAKS
3178 if(/*!mods.isSideEffect && */(this.rolledOver || !this.dragging))
3180 int rowY = (style.header) ? rowHeight : 0;
3188 ((vertScroll && vertScroll.visible &&
3189 (y < 0 || y >= clientSize.h)) ||
3190 (horzScroll && horzScroll.visible &&
3191 (x < 0 || x >= clientSize.w))))
3196 if(vertScroll && vertScroll.visible &&
3197 (y < 0 || y >= clientSize.h))
3198 vertScroll.Action((y<0)?up:down, 0, 0);
3199 if(horzScroll && horzScroll.visible &&
3200 (x < 0 || x >= clientSize.w))
3201 horzScroll.Action((x<0)?up:down, 0, 0);
3207 // This must be done after the scrolling took place
3208 rowIndex = firstRowShown ? firstRowShown.index : -1;
3210 y = Min(y, clientSize.h-1);
3211 for(row = firstRowShown; row; row = row.GetNextRow(), rowIndex ++)
3220 if(row && currentRow != row)
3222 if(this.dragRow && style.moveRows)
3224 if(row.selectedFlag == selected || row.selectedFlag == tempSelected)
3226 else if(style.multiSelect)
3229 for(thisRow = rows.first; thisRow; thisRow = thisRow.GetNextRow())
3230 if((thisRow.selectedFlag == selected || thisRow.selectedFlag == tempSelected) ||
3236 if(this.dropIndex != rowIndex)
3238 this.dropIndex = rowIndex;
3239 this.editRow = null;
3241 this.movedRow = true;
3244 else if((style.freeSelect || this.dragging) && ((realX>= 0 && realY >= 0 && realX< clientSize.w && realY < clientSize.h) || this.rolledOver))
3246 if(!(style.multiSelect))
3248 if(currentRow)currentRow.selectedFlag = unselected;
3249 if(row)row.selectedFlag = selected;
3253 if(style.multiSelect)
3257 for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3259 if(selRow.selectedFlag == tempUnselected) selRow.selectedFlag = selected;
3260 else if(selRow.selectedFlag == tempSelected) selRow.selectedFlag = unselected;
3263 if(rowIndex >= clickedRow.index)
3265 for(selRow = clickedRow; selRow; selRow = selRow.GetNextRow())
3267 selRow.selectedFlag = (selRow.selectedFlag == selected) ? tempUnselected : tempSelected;
3274 for(selRow = row; selRow; selRow = selRow.GetNextRow())
3276 selRow.selectedFlag = (selRow.selectedFlag == selected) ? tempUnselected : tempSelected;
3277 if(selRow == clickedRow)
3283 if(style.freeSelect)
3284 NotifyHighlight(master, this, currentRow, mods);
3287 insideNotifySelect = true;
3288 NotifySelect(master, this, currentRow, mods);
3289 insideNotifySelect = false;
3292 if(style.alwaysEdit && currentRow)
3293 currentRow.Edit(currentField);
3300 bool OnMouseOver(int x, int y, Modifiers mods)
3303 this.rolledOver = true;
3307 bool OnActivate(bool active, Window swap, bool * goOnWithActivation, bool direct)
3309 // TOCHECK: WAS THIS MISSING ? CHECK FOR SLOWDOWN
3310 if(!active) Update(null);
3312 if(!active && (!swap || !swap.isModal))
3314 // true: destroy edit box
3315 HideEditBox(true, true, false);
3317 else if(!swap || !swap.isModal)
3319 // Bring back edit box
3320 if(currentRow && style.alwaysEdit)
3322 currentRow.Edit(currentField ? currentField : null);
3326 return true; //NotifyActivate(master, this, active, swap, 0);
3330 bool OnButtonDown(int x, int y, Modifiers mods, bool right)
3333 // Check to see if we're dragging the vertical divider
3334 if(style.alwaysEdit && style.resizable && !right)
3336 int vx = -scroll.x - 1;// + EXTRA_SPACE;// / 2 - 1;
3339 if(style.collapse && !(style.treeBranch))
3342 for(field = fields.first; field; field = field.next)
3344 int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ?
3345 clientSize.w - field.x : (field.width + EXTRA_SPACE);
3351 resizingField = field.prev;
3353 this.startWidth = resizingField.width;
3355 SetMouseRangeToClient();
3359 vx += width + EXTRA_SPACE;
3363 if(!(style.freeSelect))
3365 int rowY = (style.header) ? rowHeight : 0;
3367 int rowIndex = firstRowShown ? firstRowShown.index : -1;
3368 DataRow previousRow = currentRow;
3369 DataRow newCurrentRow = null;
3370 bool moveMultiple = false;
3371 int numSelected = 0;
3372 int rowStart = -scroll.x;
3374 if(style.multiSelect)
3381 for(row = rows.first; row; row = row.GetNextRow())
3383 if(row.selectedFlag == tempSelected)
3384 row.selectedFlag = selected;
3385 else if(row.selectedFlag == tempUnselected)
3386 row.selectedFlag = unselected;
3387 if(row.selectedFlag == selected)
3394 for(row = firstRowShown; row; row = row.GetNextRow(), rowIndex ++)
3397 if(rowY > y || (style.multiSelect && !row.GetNextRow()))
3400 if(style.treeBranch)
3403 for(parent = (style.rootCollapse) ? row.parent : (row.parent ? row.parent.parent : null); parent; parent = parent.parent)
3409 /* THIS WAS TOO STRICT:
3410 if(style.collapse && row.subRows.first && (row.parent || !(style.treeBranch) || (style.rootCollapse)) &&
3411 (x >= rowStart + 3 + plusIndent && y >= rowY - rowHeight + PLUSY && x <= rowStart + 11 + plusIndent && y <= rowY - rowHeight + PLUSY + 8))
3413 if(style.collapse &&
3414 (x >= rowStart && y >= rowY - rowHeight && x <= rowStart + 18 + plusIndent && y <= rowY + rowHeight-1))
3416 if(row.subRows.first && (row.parent || !(style.treeBranch) || (style.rootCollapse)) && x >= plusIndent)
3417 row.collapsed = !row.collapsed;
3424 newCurrentRow = row;
3426 if(style.multiSelect)
3428 // Deselect everything if user didn't clicked on a row
3432 for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3433 selRow.selectedFlag = unselected;
3435 //this.clickedRowIndex = rowIndex;
3437 else if(style.moveRows && !(mods.shift) &&
3438 (row.selectedFlag == selected || row.selectedFlag == tempSelected) &&
3439 !right && !(mods.isActivate))
3440 moveMultiple = true;
3446 if(row.selectedFlag == tempUnselected || row.selectedFlag == unselected)
3448 for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3449 selRow.selectedFlag = unselected;
3450 row.selectedFlag = selected;
3453 //this.clickedRowIndex = rowIndex;
3459 for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3460 selRow.selectedFlag = unselected;
3464 for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3466 if(selRow != clickedRow)
3468 if(selRow.selectedFlag == tempUnselected) selRow.selectedFlag = selected;
3469 else if(selRow.selectedFlag == tempSelected) selRow.selectedFlag = unselected;
3476 if(rowIndex >= clickedRow.index)
3478 for(selRow = clickedRow; selRow; selRow = selRow.GetNextRow())
3482 if(selRow != clickedRow)
3484 if(selRow.selectedFlag)
3485 selRow.selectedFlag = tempUnselected;
3487 selRow.selectedFlag = tempSelected;
3491 selRow.selectedFlag = selected;
3498 for(selRow = row; selRow; selRow = selRow.GetNextRow())
3502 if(selRow != clickedRow)
3504 if(selRow.selectedFlag)
3505 selRow.selectedFlag = tempUnselected;
3507 selRow.selectedFlag = tempSelected;
3511 selRow.selectedFlag = selected;
3512 if(selRow == clickedRow)
3521 if(row.selectedFlag)
3522 row.selectedFlag = tempUnselected;
3523 else row.selectedFlag = tempSelected;
3526 row.selectedFlag = tempSelected;
3528 //this.clickedRowIndex = rowIndex;
3538 // true: destroy edit box
3541 incref newCurrentRow;
3544 if(currentRow != newCurrentRow)
3545 HideEditBox(true, true, false);
3549 if(newCurrentRow._refCount <= 1)
3550 delete newCurrentRow;
3552 newCurrentRow._refCount--;
3557 if(!(style.multiSelect))
3559 if(currentRow) currentRow.selectedFlag = unselected;
3560 if(newCurrentRow) newCurrentRow.selectedFlag = selected;
3564 if(currentRow != newCurrentRow)
3567 // true: destroy edit box
3570 //incref newCurrentRow;
3571 incref newCurrentRow;
3574 HideEditBox(true, true, false);
3579 int headerSize = ((style.header) ? rowHeight : 0);
3580 int height = clientSize.h + 1 - headerSize;
3582 /*if(newCurrentRow._refCount <= 1)
3583 delete newCurrentRow;
3586 newCurrentRow._refCount--;
3587 //newCurrentRow._refCount--;
3590 currentRow = newCurrentRow;
3592 if(currentRow && currentRow.index * rowHeight > scroll.y + height - rowHeight)
3593 SetScrollPosition(scroll.x,
3594 currentRow.index * rowHeight - height + rowHeight);
3595 else if(!currentRow || currentRow.index * rowHeight < scroll.y)
3597 int line = currentRow ? currentRow.index * rowHeight : 0;
3598 //SNAPUP(line, rowHeight);
3599 SetScrollPosition(scroll.x, line);
3602 // GO THROUGH SetCurrentRow eventually?
3603 // SetCurrentRow(newCurrentRow, true);
3607 if(style.freeSelect)
3608 NotifyHighlight(master, this, currentRow, mods);
3609 else if((moveMultiple || (!(style.multiSelect) && previousRow == currentRow)) &&
3610 newCurrentRow && !(mods.shift))
3614 if(!(mods.isActivate))
3618 this.dragRow = currentRow;
3619 this.dropIndex = -1;
3620 this.movedRow = false;
3622 if(editData && editData.visible && style.alwaysEdit)
3629 if(style.collapse && !style.treeBranch)
3632 for(parent = newCurrentRow.parent; parent; parent = parent.parent)
3634 indent += (style.treeBranch) ? 20 : 15;
3637 for(field = fields.first; field; field = field.next)
3639 int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ?
3640 clientSize.w - field.x : (field.width + EXTRA_SPACE);
3642 if(!field.prev) width -= indent;
3643 if(x >= sx && x < sx + width)
3647 if(field == currentField)
3648 editData.Deactivate();
3651 currentRow.Edit(field);
3652 editData.Activate();
3655 else if(!(mods.ctrl) && numSelected <= 1)
3656 this.editRow = currentRow;
3658 if(style.noDragging && newCurrentRow)
3659 NotifyReclick(master, this, newCurrentRow, mods);
3663 // If the user clicked exactly on the edited field,
3665 if(editData && editData.visible && newCurrentRow)
3671 if(style.collapse && !(style.treeBranch))
3676 for(parent = newCurrentRow.parent; parent; parent = parent.parent)
3678 indent += (style.treeBranch) ? 20 : 15;
3682 for(field = fields.first; field; field = field.next)
3684 int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ?
3685 clientSize.w - field.x : (field.width + EXTRA_SPACE);
3686 if(!field.prev) width -= indent;
3687 if(x >= sx && x < sx + width && newCurrentRow)
3692 if(field) //x >= sx && x < sx + width && newCurrentRow)
3694 if(field == currentField)
3695 editData.Activate();
3697 newCurrentRow.Edit(currentField);*/
3699 else if(style.noDragging && newCurrentRow)
3700 NotifyReclick(master, this, newCurrentRow, mods);
3702 else if(style.noDragging && newCurrentRow)
3703 NotifyReclick(master, this, newCurrentRow, mods);
3710 if(result && style.alwaysEdit && currentRow)
3714 DataField field = null;
3719 if(style.collapse && !style.treeBranch)
3722 for(parent = newCurrentRow.parent; parent; parent = parent.parent)
3724 indent += (style.treeBranch) ? 20 : 15;
3727 for(field = fields.first; field; field = field.next)
3729 int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ?
3730 clientSize.w - field.x : (field.width + EXTRA_SPACE);
3732 if(!field.prev) width -= indent;
3733 if(x >= sx && x < sx + width)
3735 f = currentField = field;
3742 // Moved NotifySelect after setting currentField for the NotifySelect implementation to be aware of which field is now selected (e.g. WatchesView)
3743 result = NotifySelect(master, this, currentRow, mods);
3744 if(result && style.alwaysEdit && currentRow)
3746 // In case the user specifically clicked on a field (f is set), override any change to currentField that NotifySelect could have done
3747 currentRow.Edit(f ? f : currentField);
3749 // If the user clicked exactly on the edited field,
3751 if(editData && editData.visible && newCurrentRow)
3755 editData.Activate();
3757 else if(style.noDragging && newCurrentRow)
3758 NotifyReclick(master, this, newCurrentRow, mods);
3761 else if(style.noDragging && newCurrentRow)
3762 NotifyReclick(master, this, newCurrentRow, mods);
3766 For drop box to capture...
3769 if(x < 0 || y < 0 || x >= clientSize.w || y >= clientSize.h)
3772 master.parent.Activate();
3781 if(!style.noDragging)
3783 this.dragging = true;
3786 if(x >= 0 && y >= 0 && x < clientSize.w && y < clientSize.h)
3787 this.rolledOver = true;
3792 this.dragging = false;
3793 OnLeftButtonUp(x, y, mods);
3798 bool OnLeftButtonDown(int x, int y, Modifiers mods)
3800 return OnButtonDown(x,y, mods, false);
3803 bool OnLeftButtonUp(int x, int y, Modifiers mods)
3805 if(resizingField && style.alwaysEdit)
3807 Window::FreeMouseRange();
3809 resizingField = null;
3812 if(dragRow || editRow)
3814 DataRow row, switchRow = rows.last;
3815 int rowY = (style.header) ? rowHeight : 0;
3816 for(row = firstRowShown; row; row = row.GetNextRow())
3825 if(this.editRow == switchRow && y >= 0)
3830 for(field = fields.first; field; field = field.next)
3832 int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ?
3833 clientSize.w - field.x : (field.width + EXTRA_SPACE);
3836 if(fieldX > x + scroll.x)
3840 if(field && field.editable)
3842 // true: destroy edit box
3843 HideEditBox(true, true, false);
3844 PopupEditBox(field, false);
3846 else if(!style.noDragging)
3847 NotifyReclick(master, this, currentRow, mods);
3849 else if(style.moveRows && switchRow)
3851 if(this.dragRow == switchRow && this.movedRow == false)
3853 DataRow row = this.dragRow;
3857 for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3858 selRow.selectedFlag = unselected;
3862 for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3864 if(selRow != clickedRow)
3866 if(selRow.selectedFlag == tempUnselected) selRow.selectedFlag = selected;
3867 else if(selRow.selectedFlag == tempSelected) selRow.selectedFlag = unselected;
3873 if(row.selectedFlag)
3874 row.selectedFlag = tempUnselected;
3875 else row.selectedFlag = tempSelected;
3878 row.selectedFlag = tempSelected;
3883 if(style.multiSelect)
3885 if(!switchRow.selectedFlag)
3887 bool foundSwitch = false;
3890 DataRow afterRow = switchRow.prev;
3891 for(row = rows.first; row; row = next)
3893 next = row.GetNextRow();
3894 if(row.selectedFlag == selected || row.selectedFlag == tempSelected)
3896 if(!foundSwitch && !after)
3899 afterRow = switchRow;
3901 if(!after || !(row.selectedFlag == selected || row.selectedFlag == tempSelected) ||
3908 else if(row == switchRow)
3915 for(row = rows.first; row; row = row.GetNextRow())
3917 if(row == switchRow || row == this.dragRow)
3921 // Switch row first: move before
3922 if(row == switchRow)
3924 if(NotifyMove(master, this, switchRow.prev, mods))
3925 dragRow.Move(switchRow.prev);
3927 // Dragged row first: move after
3930 if(NotifyMove(master, this, switchRow, mods))
3931 dragRow.Move(switchRow);
3944 if(this.dragging || style.freeSelect)
3948 this.rolledOver = this.dragging = false;
3950 if(x >= 0 && y >= 0 && x < clientSize.w && y < clientSize.h && currentRow && style.freeSelect)
3954 result = NotifySelect(master, this, currentRow, mods);
3955 if(style.alwaysEdit)
3956 currentRow.Edit(currentField);
3960 // if(!(style.freeSelect))
3966 bool OnLeftDoubleClick(int x, int y, Modifiers mods)
3968 int rowStart = -scroll.x;
3970 int rowY = (style.header) ? rowHeight : 0;
3973 OnLeftButtonUp(x,y,mods);
3974 if(style.alwaysEdit)
3976 if(!(style.collapse) || x > 15)
3977 if(editData && editData.visible)
3979 editData.Activate();
3980 NotifyDoubleClick(master, this, x, y, mods);
3984 for(row = firstRowShown; row; row = row.GetNextRow())
3987 if(rowY > y || (style.multiSelect && !row.GetNextRow()))
3989 if(style.treeBranch)
3992 for(parent = (style.rootCollapse) ? row.parent : (row.parent ? row.parent.parent : null); parent; parent = parent.parent)
4001 if((row && style.collapse && (x >= rowStart && x <= rowStart + 18 + plusIndent)) ||
4002 NotifyDoubleClick(master, this, x, y, mods))
4006 if(row && row.subRows.first)
4008 row.collapsed = !row.collapsed;
4012 // We need to return true here so that OnLeftButtonDown can popup the DataBox Editors
4018 bool OnRightButtonDown(int x, int y, Modifiers mods)
4020 return OnButtonDown(x,y, mods, true);
4023 bool OnRightButtonUp(int x, int y, Modifiers mods)
4025 OnLeftButtonUp(x,y,mods);
4026 return NotifyRightClick(master, this, x, y, mods);
4029 bool GoToLetter(unichar ch, bool keyHit)
4031 bool result = false;
4033 bool checkNextField = true;
4034 int len = keyHit ? 0 : strlen(typedString);
4036 typedString = renew typedString char[len + 2];
4037 typedString[len++] = (char)tolower(ch); // TODO: FIX UNICODE
4038 typedString[len] = '\0';
4040 for(field = fields.first; field; field = field.next)
4042 DataRow startRow = currentRow ? currentRow : rows.first;
4044 if(startRow && field.dataType && field.dataType._vTbl[__ecereVMethodID_class_OnGetString] && ch)
4047 bool looped = false;
4048 if(len == 1 && currentRow)
4049 startRow = (next = startRow.GetNextRow(), (next ? next : rows.first));
4051 for(row = startRow; row != startRow || !looped; next = row.GetNextRow(), row = next ? next : rows.first)
4053 void * data = row.GetData(field);
4054 char tempString[1024] = "";
4055 bool needClass = false;
4056 const char * string = data ? ((const char *(*)(void *, void *, char *, void *, bool *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString])(field.dataType, data, tempString, null, &needClass) : null;
4058 if(string && string[0])
4059 checkNextField = false;
4060 if(string && string[0] && !strnicmp(string, typedString, len))
4062 if(style.multiSelect)
4067 for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
4068 selRow.selectedFlag = unselected;
4069 row.selectedFlag = selected;
4071 SetCurrentRow(row, true);
4078 if(this.typingTimeOut && !keyHit)
4079 typingTimer.Start();
4080 if(!result || !this.typingTimeOut || keyHit)
4081 typedString[len-1] = '\0';
4083 if(!checkNextField || result) break;
4088 bool OnKeyDown(Key key, unichar ch)
4092 if(key == enter || key == keyPadEnter)
4094 if(editData && editData.visible && editData.active)
4096 HideEditBox(true, false, false);
4100 else if(key == escape)
4102 if(resizingField || this.movingFields || (editData && editData.visible) || this.dragRow)
4104 if(editData && editData.visible && style.alwaysEdit && !editData.active)
4106 // false: dont destroy edit box
4107 HideEditBox(false, false, false);
4110 resizingField.width = this.startWidth;
4111 AdaptToFieldWidth(resizingField, true);
4112 resizingField = null;
4115 this.dragRow = null;
4118 this.dragging = false;
4122 this.movingFields = false;
4123 draggingField = null;
4129 if(!currentField || !currentField.editable)
4130 for(field = fields.first; field; field = field.next)
4134 currentField = field;
4138 if((key == f2 || (style.alwaysEdit && (key == ctrlV || key == ctrlC || key == ctrlX || key == shiftInsert || key == ctrlInsert || key == shiftDel))) &&
4139 currentField && currentField.editable)
4141 PopupEditBox(currentField, false);
4142 if(editData && editData.visible)
4144 if(style.alwaysEdit)
4146 editData.Activate();
4147 if(key == ctrlV || key == ctrlC || key == ctrlX || key == shiftInsert || key == ctrlInsert || key == shiftDel)
4149 editData.OnKeyHit(key, ch);
4153 // For Installer to pop up file dialog
4154 NotifyKeyDown(master, this, currentRow, key, ch);
4160 if(!NotifyKeyDown(master, this, currentRow, key, ch))
4163 // Editable fields...
4166 if(style.alwaysEdit && editData && editData.visible)
4167 return editData.OnKeyDown(key, ch);
4168 return true; // We want to pick up the OnKeyHit to replace contents, but skip GoToLetter
4171 if(ch >=32 && ch != 128 && !key.alt && !key.ctrl && GoToLetter(ch, false))
4173 /*if(inactive && window.state != Hidden)
4174 NotifyHighlight(master, this, currentRow, 0);
4177 NotifySelect(master, this, currentRow, 0);
4184 bool OnKeyHit(Key key, unichar ch)
4186 if(!ch && !key.alt && !key.ctrl)
4188 key.code = (SmartKey)key.code;
4190 if(ch >= 32 && !key.alt && !key.ctrl && ch != 128)
4193 if(!currentField || !currentField.editable)
4194 for(field = fields.first; field; field = field.next)
4198 currentField = field;
4202 if(currentField && currentField.editable)
4204 if((!editData || !editData.visible) || !editData.active)
4206 PopupEditBox(currentField, false);
4207 if(editData && editData.visible)
4209 editData.Activate();
4210 editData.OnKeyHit(key, ch);
4217 if(editData && editData.visible && ch && !key.alt && !key.ctrl && editData.active && (key.code != tab || (editData._class == class(EditBox) && ((EditBox)editData).tabKey)))
4220 if(!key.alt && (style.multiSelect || !key.ctrl))
4225 if(style.alwaysEdit)
4230 for(field = currentField.prev; field; field = field.prev)
4234 currentField = field;
4235 HideEditBox(true, true, false);
4236 PopupEditBox(currentField, false);
4242 if(style.collapse && currentRow /*&& !currentField*/) // THIS PREVENTED COLLAPSING THE PROPERTY SHEET
4244 if(currentRow.subRows.first && !currentRow.collapsed)
4246 currentRow.collapsed = true;
4248 else if(currentRow.parent)
4249 SetCurrentRow(currentRow.parent, true);
4254 if(style.collapse && currentRow && currentRow.subRows.first)
4256 if(currentRow.collapsed)
4257 currentRow.collapsed = false;
4259 SetCurrentRow(currentRow.subRows.first, true);
4262 else if(style.alwaysEdit)
4267 for(field = currentField.next; field; field = field.next)
4271 currentField = field;
4272 HideEditBox(true, true, false);
4273 PopupEditBox(currentField, false);
4281 case pageDown: case pageUp:
4282 case end: case home:
4284 int headerSize = ((style.header) ? rowHeight : 0);
4285 int height = clientSize.h + 1 - headerSize;
4288 // true: destroy edit box
4289 // !!! TESTING true HERE !!!
4290 HideEditBox(true, true, false);
4291 // HideEditBox(false, true, false);
4293 oldRow = currentRow;
4295 SNAPDOWN(height, rowHeight);
4296 if((!currentRow || key.code == home) && key.code != end)
4298 currentRow = rows.first;
4306 if(!(style.multiSelect) && currentRow && !currentRow.selectedFlag)
4309 next = currentRow.GetNextRow();
4316 if(!(style.multiSelect) && currentRow && !currentRow.selectedFlag)
4319 next = currentRow.GetPrevRow();
4327 currentRow = lastRow.GetLastRow();
4333 currentRow && (next = currentRow.GetPrevRow()) && c < height / rowHeight;
4334 c++, currentRow = next);
4341 currentRow && (next = currentRow.GetNextRow()) && c < height / rowHeight;
4342 c++, currentRow = next);
4347 if(currentRow && currentRow.index * rowHeight > scroll.y + height - rowHeight)
4348 SetScrollPosition(scroll.x, currentRow.index * rowHeight - height + rowHeight);
4349 else if(!currentRow || currentRow.index * rowHeight < scroll.y)
4350 SetScrollPosition(scroll.x, currentRow ? currentRow.index * rowHeight : 0);
4352 if(style.multiSelect)
4356 if(!(key.shift) && (key.ctrl))
4359 for(row = rows.first; row; row = row.GetNextRow())
4361 if(row.selectedFlag == tempSelected)
4362 row.selectedFlag = selected;
4363 else if(row.selectedFlag == tempUnselected)
4364 row.selectedFlag = unselected;
4370 for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
4371 selRow.selectedFlag = unselected;
4375 for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
4377 if(selRow.selectedFlag == tempUnselected) selRow.selectedFlag = selected;
4378 else if(selRow.selectedFlag == tempSelected) selRow.selectedFlag = unselected;
4384 if(currentRow.index >= clickedRow.index)
4386 for(selRow = clickedRow; selRow; selRow = selRow.GetNextRow())
4390 if(selRow.selectedFlag) selRow.selectedFlag = tempUnselected; else selRow.selectedFlag = tempSelected;
4393 selRow.selectedFlag = selected;
4394 if(selRow == currentRow)
4400 for(selRow = currentRow; selRow; selRow = selRow.GetNextRow())
4404 if(selRow.selectedFlag) selRow.selectedFlag = tempUnselected; else selRow.selectedFlag = tempSelected;
4407 selRow.selectedFlag = selected;
4408 if(selRow == clickedRow)
4415 if(!(key.ctrl) && currentRow)
4417 currentRow.selectedFlag = selected;
4420 clickedRow = currentRow;
4425 if(oldRow) oldRow.selectedFlag = unselected;
4426 if(currentRow) currentRow.selectedFlag = selected;
4431 if(style.freeSelect)
4432 NotifyHighlight(master, this, currentRow, 0);
4434 NotifySelect(master, this, currentRow, 0);
4436 if(style.alwaysEdit && currentRow)
4437 currentRow.Edit(currentField /*null*/);
4444 if(style.multiSelect && currentRow)
4446 if(currentRow.selectedFlag)
4449 currentRow.selectedFlag = unselected;
4452 currentRow.selectedFlag = selected;
4455 if(style.freeSelect)
4456 NotifyHighlight(master, this, currentRow, 0);
4458 NotifySelect(master, this, currentRow, 0);
4465 if(!NotifyKeyHit(master, this, currentRow, key, ch))
4468 if(ch >=32 && ch != 128 && !key.alt && !key.ctrl && GoToLetter(ch, true))
4470 /*if(inactive && window.state != Hidden)
4471 return NotifyHighlight(master, this, currentRow, 0);
4474 return NotifySelect(master, this, currentRow, 0);
4481 void OnHScroll(ScrollBarAction action, int position, Key key)
4486 void OnVScroll(ScrollBarAction action, int position, Key key)
4491 for(firstRowShown = rows.first; firstRowShown; firstRowShown = next)
4493 next = firstRowShown.GetNextRow();
4494 if(y >= position || !next) break;
4504 DataRow firstRowShown;
4508 DataField sortField;
4512 double typingTimeOut;
4525 if(guiApp.GetKeyState(shift)) mods.shift = true;
4526 if(guiApp.GetKeyState(alt)) mods.alt = true;
4527 if(guiApp.GetKeyState(control)) mods.ctrl = true;
4528 OnMouseMove(MAXINT, MAXINT, mods);
4536 delay = 0.5; // typingTimeOut
4541 typedString[0] = '\0';
4543 // The next line was commented... Why? When commented typing words stops working ( only first character jumps )
4549 bool dragging, rolledOver;
4558 // For editing fields
4560 DataField currentField;
4563 // For moving fields
4564 DataField draggingField, dropField;
4567 // For resizing fields
4568 DataField resizingField;
4569 int resizeX, oldX, startWidth;
4572 FontResource boldFont;
4575 // Only used for OnMouseMove so far, for avoiding problems with consequential mouse moves
4576 bool insideNotifySelect;
4577 ColorAlpha selectionColor, selectionText, stippleColor;
4578 stippleColor = 0xFFFFFF80;