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
58 property Class dataType
60 set { dataType = value; if(value) { alignment = (Alignment)value.defaultAlignment; } } // NOTE: Class::defaultAlignment does not seem to be used anywhere
61 get { return dataType; }
63 property bool editable { set { editable = value; } };
64 property bool fixed { set { fixed = value; } get { return fixed; } };
65 property Alignment alignment
70 if(headButton) headButton.alignment = value;
71 if(listBox) listBox.Update(null);
73 get { return alignment; }
79 width = Max(value, -EXTRA_SPACE);
81 listBox.AdaptToFieldWidth(this, false);
85 property int index { get { return this ? index : 0; } };
94 for(field = listBox.fields.first; field; field = field.next)
96 if(index == value - 2)
109 for(field = listBox.fields.first; field; field = field.next)
121 property int sortOrder { get { return this ? sortOrder : 0; } };
122 property const char * header
128 headButton.text = header;
131 property void * userData
139 return this ? userData : null;
142 property bool freeData
144 set { freeData = value; } get { return freeData; }
146 property DataField prev { get { return prev; } };
147 property DataField next { get { return next; } };
149 void Move(DataField after)
153 listBox.fields.Move(this, after);
156 listBox.OnResize(listBox.clientSize.w, listBox.clientSize.h);
157 listBox.Update(null);
163 if(listBox && dataType)
165 Display display = listBox.display;
166 Font boldFont = listBox.boldFont.font;
167 Font font = listBox.fontObject;
171 display.FontExtent(boldFont, header, strlen(header), &width, null);
172 width += EXTRA_SPACE;
173 for(row = listBox.firstRow; row; row = row.GetNextRow())
177 for(i = 0, cell = row.cells.first; i != index; i++, cell = cell.next);
178 if(cell && cell.isSet && dataType)
180 static char tempString[4096];
183 if(dataType.type == normalClass || dataType.type == noHeadClass)
184 string = ((const char *(*)(void *, void *, char *, void *, bool *))(void *)dataType._vTbl[__ecereVMethodID_class_OnGetString])(dataType, cell.data[0], tempString, userData, null);
186 string = ((const char *(*)(void *, void *, char *, void *, bool *))(void *)dataType._vTbl[__ecereVMethodID_class_OnGetString])(dataType, cell.data, tempString, userData, null);
188 display.FontExtent(row.header ? boldFont : font, string, strlen(string), &tw, null);
190 display.FontExtent(row.header ? boldFont : font, "", 0, &tw, null);
191 if(tw > width) width = tw;
195 property::width = width;
202 // property::dataType = "String";
203 property::dataType = "char *";
215 headButton.Destroy(0);
220 listBox.fields.Remove(this);
221 for(field = listBox.fields.first; field; field = field.next)
223 if(field.index >= index)
226 if(listBox.currentField == this)
227 listBox.currentField = null;
229 listBox.OnResize(listBox.clientSize.w, listBox.clientSize.h);
234 DataField prev, next;
258 property int64 tag { set { if(this) tag = value; } get { return this ? tag : 0; } };
259 property DataRow previous { get { return prev; } };
260 property DataRow next { get { return next; } };
261 property int index { get { return (this && (!parent || parent.IsExpanded())) ? index : -1; } };
262 property const char * string
264 set { SetData(listBox.fields.first, value); }
265 get { return GetData(listBox.fields.first); }
267 property bool isHeader { set { header = value; } get { return this ? header : false; } };
268 property BitmapResource icon { set { if(this) icon = value; } get { return this ? icon : null; } };
269 property bool collapsed
273 if(this && collapsed != value)
276 if(parent.IsExpanded())
283 for(search = subRows.first; search; search = search.next)
284 search.selectedFlag = 0;
286 if(listBox.clickedRow && !listBox.clickedRow.parent.IsExpanded())
288 listBox.clickedRow = GetNextRow();
289 if(!listBox.clickedRow)
290 listBox.clickedRow = this;
292 if(listBox.currentRow && !listBox.currentRow.parent.IsExpanded())
294 listBox.SetCurrentRow(this, true);
296 if(listBox.firstRowShown && !listBox.firstRowShown.parent.IsExpanded())
298 listBox.firstRowShown = GetPrevRow();
299 if(!listBox.firstRowShown)
300 listBox.firstRowShown = this;
304 index = this.index+1;
305 for(search = GetNextRow(); search; search = search.GetNextRow())
306 search.index = index++;
307 listBox.rowCount = index;
309 listBox.HideEditBox(false, false, true);
311 listBox.SetScrollArea(
313 (listBox.rowCount * listBox.rowHeight) +
314 ((listBox.style.header) ? listBox.rowHeight : 0) -
315 ((!((listBox.clientSize.h+1) % listBox.rowHeight)) ? listBox.rowHeight : 0), true);
316 listBox.Update(null);
317 listBox.NotifyCollapse(listBox.master, listBox, this, value);
321 if(this && !skipCheck)
322 listBox.CheckConsistency();
325 get { return this ? collapsed : false; }
327 property bool selected
333 selectedFlag = value ? selected : unselected;
334 listBox.Update(null);
337 get { return selectedFlag == selected || selectedFlag == tempSelected; }
339 property DataRow parent
343 if(this && (value != this))
346 DataRow after = value ? value.subRows.last : listBox.rows.last;
347 int ixCount = (!collapsed && subRows.count) ? GetLastRow().index - index + 1 : 1;
348 if(parent.IsExpanded())
350 for(search = GetNextRow(); search; search = search.GetNextRow())
351 search.index -= ixCount;
352 listBox.rowCount -= ixCount;
355 listBox.HideEditBox(false, false, true);
358 if(this == listBox.clickedRow)
360 listBox.clickedRow = GetNextRow();
361 if(!listBox.clickedRow)
362 listBox.clickedRow = GetPrevRow();
365 if(this == listBox.currentRow)
367 DataRow newCurrentRow = GetNextRow();
368 if(!listBox.newCurrentRow)
369 listBox.newCurrentRow = GetPrevRow();
370 listBox.SetCurrentRow(newCurrentRow, true);
373 if(this == listBox.firstRowShown)
375 listBox.firstRowShown = GetPrevRow();
376 if(!listBox.firstRowShown)
377 listBox.firstRowShown = GetNextRow();
381 (parent ? parent.subRows : listBox.rows).Remove(this);
384 value.subRows.Insert(after, this);
386 listBox.rows.Insert(after, this);
390 if(value && listBox.style.expandOnAdd)
393 value.skipCheck = true;
395 value.collapsed = false;
397 value.skipCheck = false;
401 if(value.IsExpanded())
405 if(after && after.subRows.first && !after.collapsed)
407 search = after.GetLastRow();
408 index = search.index + 1;
411 index = after ? (after.index + 1) : (index + 1);
413 listBox.rowCount += ixCount;
417 for(search = GetNextRow(); search; search = search.GetNextRow())
421 listBox.SetScrollArea(
423 (listBox.rowCount * listBox.rowHeight) +
424 ((listBox.style.header) ? listBox.rowHeight : 0) -
425 ((!((listBox.clientSize.h+1) % listBox.rowHeight)) ? listBox.rowHeight : 0), true);
426 if(listBox.style.autoScroll)
427 listBox.SetScrollPosition(0, MAXINT - listBox.rowHeight);
429 // TESTING THIS HERE...
431 listBox.Sort(listBox.sortField, listBox.sortField.sortOrder);
434 int headerSize = ((listBox.style.header) ? listBox.rowHeight : 0);
435 int height = listBox.clientSize.h + 1 - headerSize;
436 if(listBox.currentRow && listBox.currentRow.index * listBox.rowHeight > listBox.scroll.y + height - listBox.rowHeight)
437 listBox.SetScrollPosition(listBox.scroll.x, listBox.currentRow.index * listBox.rowHeight - height + listBox.rowHeight);
438 else if(!listBox.currentRow || listBox.currentRow.index * listBox.rowHeight < listBox.scroll.y)
440 int line = listBox.currentRow ? listBox.currentRow.index * listBox.rowHeight : 0;
441 listBox.SetScrollPosition(listBox.scroll.x, line);
444 listBox.OnVScroll(0, listBox.scroll.y, 0);
446 listBox.Update(null);
454 property DataRow lastRow { get { return this ? subRows.last : null; } };
455 property DataRow firstRow { get { return this ? subRows.first : null; } };
457 void Edit(DataField field)
463 if(!field || !field.editable)
465 for(field = listBox.fields.first; field; field = field.next)
466 if(field.editable) break;
468 if(field && field.editable)
470 listBox.SetCurrentRow(this, true);
471 listBox.PopupEditBox(field, false);
477 // NOTE: This does not support reparenting (Use row.parent = first)
478 void Move(DataRow after)
483 if(listBox && prev != after)
486 int headerSize = ((listBox.style.header) ? listBox.rowHeight : 0);
487 int height = listBox.clientSize.h + 1 - headerSize;
488 int ixCount = (!collapsed && subRows.count) ? GetLastRow().index - index + 1 : 1;
490 if(!after || after.index < index)
492 if(after == listBox.firstRowShown.prev)
493 listBox.firstRowShown = this;
495 // All rows between AFTER (exclusive) and ROW (exclusive) are incremented by one
496 // ROW is equal to AFTER's index + 1
498 for(search = after ? after.next : listBox.rows.first; search && search != this; search = search.GetNextRow())
499 search.index += ixCount;
501 if(after && after.subRows.first && !after.collapsed)
503 search = after.GetLastRow();
504 index = search.index + 1;
507 index = after ? (after.index + 1) : 0;
509 // Fix indices of sub rows
513 for(c = 1, search = GetNextRow(); search && c < ixCount; c++, search = search.GetNextRow())
519 DataRow nextRow = GetNextRow();
520 if(this == listBox.firstRowShown)
521 listBox.firstRowShown = nextRow;
524 // All rows between ROW (exclusive) and AFTER (inclusive) are decremented by one
525 // ROW is equal to AFTER's index
527 for(search = nextRow; search; search = search.GetNextRow())
529 search.index -= ixCount;
530 if(search == after) break;
533 listBox.rows.Move(this, after);
536 listBox.CheckConsistency();
539 listBox.HideEditBox(true, false, true);
542 if(listBox.currentRow && listBox.currentRow.index * listBox.rowHeight > listBox.scroll.y + height - listBox.rowHeight)
543 listBox.SetScrollPosition(listBox.scroll.x, listBox.currentRow.index * listBox.rowHeight - height + listBox.rowHeight);
544 else if(!listBox.currentRow || listBox.currentRow.index * listBox.rowHeight < listBox.scroll.y)
546 int line = listBox.currentRow ? listBox.currentRow.index * listBox.rowHeight : 0;
547 //SNAPUP(line, listBox.rowHeight);
548 listBox.SetScrollPosition(listBox.scroll.x, line);
551 listBox.OnVScroll(0, listBox.scroll.y, 0);
553 listBox.modifiedDocument = true;
555 listBox.Update(null);
562 any_object GetData(DataField field)
566 ListBoxCell cell = listBox.GetCell(&this, &field);
567 if(cell && cell.isSet && cell.data)
569 if((field.dataType.type == normalClass || field.dataType.type == noHeadClass))
578 void * SetData(DataField field, any_object newData)
582 ListBoxCell cell = listBox.GetCell(&this, &field);
585 Class dataType = field.dataType;
589 if(dataType.type == normalClass || dataType.type == noHeadClass)
591 if(cell.data[0] && field.freeData)
592 ((void (*)(void *, void *))(void *)dataType._vTbl[__ecereVMethodID_class_OnFree])(dataType, cell.data[0]);
594 if(eClass_IsDerived(dataType, class(char *)) && field.freeData)
595 ((void (*)(void *, void *, void *))(void *)dataType._vTbl[__ecereVMethodID_class_OnCopy])(dataType, cell.data, newData);
597 cell.data[0] = (void *)newData;
601 // Free old data first
602 ((void (*)(void *, void *))(void *)dataType._vTbl[__ecereVMethodID_class_OnFree])(dataType, cell.data);
603 ((void (*)(void *, void *, void *))(void *)dataType._vTbl[__ecereVMethodID_class_OnCopy])(dataType, cell.data, newData);
607 listBox.modifiedDocument = true;
608 listBox.Update(null);
609 if(dataType && (dataType.type == normalClass || dataType.type == noHeadClass))
618 void UnsetData(DataField field)
622 ListBoxCell cell = listBox.GetCell(&this, &field);
625 Class dataType = field.dataType;
629 if(dataType.type == normalClass || dataType.type == noHeadClass)
631 if(cell.data[0] && field.freeData)
632 ((void (*)(void *, void *))(void *)dataType._vTbl[__ecereVMethodID_class_OnFree])(dataType, cell.data[0]);
637 // Free old data first
638 ((void (*)(void *, void *))(void *)dataType._vTbl[__ecereVMethodID_class_OnFree])(dataType, cell.data);
642 listBox.Update(null);
647 DataRow FindRow(int64 tag)
650 for(row = subRows.first; row; row = row.next)
652 if(!row.noneRow && row.tag == tag)
658 DataRow FindSubRow(int64 tag)
661 for(row = subRows.first; row; row = row.next)
663 if(!row.noneRow && row.tag == tag)
665 if(row.subRows.first)
667 DataRow subRow = row.FindSubRow(tag);
675 DataRow AddRowAfter(DataRow after)
685 subRows.Insert(after, row);
686 row.listBox = listBox;
690 for(c = 0; c<listBox.fields.count; c++)
692 for(field = listBox.fields.first; field; field = field.next)
693 if((int)field.index == c)
697 int size = (field.dataType && field.dataType.typeSize) ?
698 (sizeof(class ListBoxCell) + field.dataType.typeSize - sizeof(void *)) : sizeof(class ListBoxCell);
699 ListBoxCell cell = (ListBoxCell)new0 byte[size];
701 FillBytes(cell.data, 0, size - (uint)(uintptr)&((ListBoxCell)0).data);
710 if(after && after.subRows.first && !after.collapsed)
712 for(search = after.subRows.last; !search.collapsed && search.subRows.last; )
713 search = search.subRows.last;
714 row.index = search.index + 1;
717 row.index = after ? (after.index + 1) : (index + 1);
721 for(search = row.GetNextRow(); search; search = search.GetNextRow())
724 listBox.SetScrollArea(
726 (listBox.rowCount * listBox.rowHeight) +
727 ((listBox.style.header) ? listBox.rowHeight : 0) -
728 ((!((listBox.clientSize.h+1) % listBox.rowHeight)) ? listBox.rowHeight : 0), true);
729 if(listBox.style.autoScroll)
730 listBox.SetScrollPosition(0, MAXINT - listBox.rowHeight);
733 listBox.modifiedDocument = true;
736 listBox.CheckConsistency();
746 return AddRowAfter(subRows.last);
750 DataRow AddStringf(const char * format, ...)
755 char string[MAX_F_STRING];
757 va_start(args, format);
758 vsnprintf(string, sizeof(string), format, args);
759 string[sizeof(string)-1] = 0;
763 row.SetData(null, string);
769 DataRow AddString(const char * string)
775 row.SetData(listBox.fields.first, string);
785 subRows.offset = (uint)(uintptr)&((DataRow)0).prev;
790 ListBoxCell cell, next;
795 while((subRow = subRows.first))
797 subRows.Remove(subRow);
801 for(cell = cells.first; cell; cell = next, cellIndex++)
806 for(field = listBox.fields.first; field && field.index != cellIndex; field = field.next);
807 if(field && field.dataType)
809 // TOCHECK: Is this check good? Will need incref/decref sometime?
810 if(field.dataType.type == normalClass || field.dataType.type == noHeadClass)
812 if(cell.data[0] && field.freeData)
813 ((void (*)(void *, void *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnFree])(field.dataType, cell.data[0]);
816 ((void (*)(void *, void *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnFree])(field.dataType, cell.data);
826 return !this || (!collapsed && (!parent || parent.IsExpanded()));
829 int Compare(DataRow b, DataField sortField)
832 ListBoxCell cell1, cell2;
834 for(index = 0, cell1 = cells.first, cell2 = b.cells.first;
835 index != sortField.index;
836 index++, cell1 = cell1.next, cell2 = cell2.next);
838 if(!cell1.isSet && !cell2.isSet)
840 else if(!cell1.isSet)
842 else if(!cell2.isSet)
845 if(noneRow && !b.noneRow) return -1;
846 else if(!noneRow && b.noneRow) return 1;
847 else if(noneRow && b.noneRow) return 0;
849 if(sortField.dataType._vTbl[__ecereVMethodID_class_OnCompare])
851 if(sortField.dataType.type == normalClass || sortField.dataType.type == noHeadClass)
853 result = ((int (*)(void *, void *, void *))(void *)sortField.dataType._vTbl[__ecereVMethodID_class_OnCompare])(sortField.dataType,
854 (cell1.isSet && cell1.data) ? cell1.data[0] : null,
855 (cell2.isSet && cell2.data) ? cell2.data[0] : null);
859 result = ((int (*)(void *, void *, void *))(void *)sortField.dataType._vTbl[__ecereVMethodID_class_OnCompare])(sortField.dataType,
860 cell1.isSet ? cell1.data : null,
861 cell2.isSet ? cell2.data : null);
864 return sortField.sortOrder * result;
867 void _SortSubRows(DataField field, int order)
870 for(search = subRows.first; search; search = search.next)
871 search._SortSubRows(field, order);
872 subRows.Sort(Compare, field);
875 public void SortSubRows(bool scrollToCurrent)
877 if(this && listBox && listBox.sortField)
879 _SortSubRows(listBox.sortField, listBox.sortField.sortOrder);
883 int index = this.index;
884 for(search = this; search; search = search.GetNextRow())
885 search.index = index++;
889 int headerSize = ((listBox.style.header) ? listBox.rowHeight : 0);
890 int height = listBox.clientSize.h + 1 - headerSize;
891 if(listBox.currentRow && listBox.currentRow.index * listBox.rowHeight > listBox.scroll.y + height - listBox.rowHeight)
892 listBox.SetScrollPosition(listBox.scroll.x, listBox.currentRow.index * listBox.rowHeight - height + listBox.rowHeight);
893 else if(!listBox.currentRow || listBox.currentRow.index * listBox.rowHeight < listBox.scroll.y)
894 listBox.SetScrollPosition(listBox.scroll.x, listBox.currentRow ? listBox.currentRow.index * listBox.rowHeight : 0);
895 listBox.OnVScroll(0, listBox.scroll.y, 0);
900 public DataRow GetPrevRow()
906 for(row = prev; row && !row.collapsed && row.subRows.last; row = row.subRows.last);
913 public DataRow GetNextRow()
917 if(subRows.first && !collapsed)
921 for(row = this; row; row = row.parent)
923 if(row.next) { row = row.next; break; }
929 private DataRow GetLastRow()
932 while(row && !row.collapsed && row.subRows.last)
933 row = row.subRows.last;
940 SelectedFlag selectedFlag;
951 public class ListBox : CommonControl
953 hasVertScroll = true;
954 // background = white;
956 snapVertScroll = true;
958 class_property(icon) = "<:ecere>controls/listBox.png";
962 property bool freeSelect { property_category $"Behavior" set { style.freeSelect = value; } get { return style.freeSelect; } };
963 property DataRow currentRow { property_category $"Private" /*"Behavior"*/ set { if(this) SetCurrentRow(value, false); } get { return this ? currentRow : null; } };
964 property DataField currentField
966 get { return currentField; }
967 // TODO: Document what this does
970 currentField = value;
971 HideEditBox(true, true, false);
972 if(value && value.editable)
973 PopupEditBox(currentField, false);
977 property int rowHeight
979 property_category $"Appearance"
980 isset { return style.heightSet; }
985 style.heightSet = true;
987 SetScrollLineStep(8, value);
991 style.heightSet = false;
996 get { return this ? rowHeight : 0; }
998 property Seconds typingTimeout
1000 property_category $"Behavior"
1003 typedString[0] = '\0';
1004 typingTimer.delay = value;
1005 typingTimeOut = value;
1007 get { return typingTimeOut; }
1009 property bool moveRows { property_category $"Behavior" set { style.moveRows = value; } get { return style.moveRows; } };
1010 property bool moveFields { property_category $"Behavior" set { style.moveFields = value; } get { return style.moveFields; } };
1011 property bool resizable { property_category $"Behavior" set { style.resizable = value; } get { return style.resizable; } };
1012 property bool autoScroll { property_category $"Behavior" set { style.autoScroll = value; } get { return style.autoScroll; } };
1013 property bool alwaysHighLight { property_category $"Appearance" set { style.alwaysHL = value; } get { return style.alwaysHL; } };
1014 property bool hasClearHeader { property_category $"Appearance" set { style.clearHeader = value; if(value) property::hasHeader = true; } get { return style.clearHeader; } };
1015 property bool hasHeader
1017 property_category $"Appearance"
1020 if(value && !style.header)
1026 bevel = !guiApp.textMode && !style.clearHeader;
1027 dontScrollVert = true;
1030 NotifyPushed = HeaderPushed;
1031 NotifyClicked = HeaderClicked;
1032 NotifyDoubleClick = HeaderDoubleClicked;
1033 NotifyReleased = HeaderReleased;
1034 NotifyMouseMove = HeaderMouseMove;
1038 endBevel.visible = false;
1040 style.header = value;
1042 get { return style.header; }
1044 property bool multiSelect { property_category $"Behavior" set { style.multiSelect = value; } get { return style.multiSelect; } };
1045 property bool alwaysEdit { property_category $"Behavior" set { style.alwaysEdit = value; } get { return style.alwaysEdit; } };
1046 property bool fullRowSelect { property_category $"Appearance" set { style.fullRowSelect = value; } get { return style.fullRowSelect; } };
1047 property bool collapseControl { property_category $"Appearance" set { style.collapse = value; } get { return style.collapse; } };
1048 property bool treeBranches { property_category $"Appearance" set { style.treeBranch = value; } get { return style.treeBranch; } };
1049 property bool rootCollapseButton { property_category $"Appearance" set { style.rootCollapse = value; } get { return style.rootCollapse; } };
1050 property bool sortable { property_category $"Behavior" set { style.sortable = value; } get { return style.sortable; } };
1051 property bool noDragging { property_category $"Behavior" set { style.noDragging = value; } get { return style.noDragging; } };
1052 property bool fillLastField
1054 property_category $"Behavior"
1057 style.fillLastField = value;
1059 get { return style.fillLastField; }
1061 property int numSelections
1065 int numSelections = 0;
1066 if(this && style.multiSelect)
1069 for(row = rows.first; row; row = row.GetNextRow())
1070 if(row.selectedFlag == selected || row.selectedFlag == tempSelected)
1073 return numSelections;
1076 property int currentIndex
1078 get { return currentRow ? currentRow.index : -1; }
1080 property DataRow lastRow { get { return this ? rows.last : null; } };
1081 property DataRow firstRow { get { return this ? rows.first : null; } };
1082 property int rowCount { get { return rowCount; } };
1083 property DataField firstField { get { return this ? fields.first : null; } };
1084 property Color selectionColor { set { selectionColor = value; } get { return selectionColor; } isset { return selectionColor ? true : false; } };
1085 property Color selectionText { set { selectionText = value; } get { return selectionText; } isset { return selectionText ? true : false; } };
1086 property Color stippleColor { set { stippleColor = value; } get { return stippleColor; } };
1087 property bool expandOnAdd { set { style.expandOnAdd = value; } get { return style.expandOnAdd; } };
1090 virtual bool Window::NotifySelect(ListBox listBox, DataRow row, Modifiers mods);
1091 virtual bool Window::NotifyHighlight(ListBox listBox, DataRow row, Modifiers mods);
1092 virtual bool Window::NotifyReclick(ListBox listBox, DataRow row, Modifiers mods);
1093 virtual bool Window::NotifySort(ListBox listBox, DataField field, Modifiers mods);
1094 virtual bool Window::NotifyChanged(ListBox listBox, DataRow row);
1095 virtual bool Window::NotifyKeyDown(ListBox listBox, DataRow row, Key key, unichar ch);
1096 virtual bool Window::NotifyEdited(ListBox listBox, DataRow row);
1097 virtual bool Window::NotifyEditDone(ListBox listBox, DataRow row);
1098 virtual bool Window::NotifyMovedField(ListBox listBox, DataField field, Modifiers mods);
1099 virtual bool Window::NotifyMove(ListBox listBox, DataRow row, Modifiers mods);
1100 virtual bool Window::NotifyDoubleClick(ListBox listBox, int x, int y, Modifiers mods);
1101 virtual bool Window::NotifyRightClick(ListBox listBox, int x, int y, Modifiers mods);
1102 virtual bool Window::NotifyResized(ListBox listBox, DataField field, Modifiers mods);
1103 virtual bool Window::NotifyCollapse(ListBox listBox, DataRow row, bool collapsed);
1104 virtual bool Window::NotifyKeyHit(ListBox listBox, DataRow row, Key key, unichar ch);
1105 virtual bool Window::NotifyModified(ListBox listBox, DataRow row);
1106 virtual bool Window::NotifyEditing(ListBox listBox, DataRow row);
1109 private void CheckConsistency()
1114 for(r = rows.first; r; r = r.GetNextRow())
1116 if(r.index != index++)
1124 void AddField(DataField addedField)
1129 if(fields.first && ((DataField)fields.first).defaultField)
1131 DataField defaultField = fields.first;
1132 defaultField.Free();
1133 delete defaultField;
1137 addedField = DataField { };
1141 addedField.listBox = this;
1142 fields.Add(addedField);
1144 addedField.sortOrder = 1;
1145 addedField.index = numFields;
1149 addedField.headButton.Destroy(0);
1150 delete addedField.headButton;
1151 addedField.headButton = Button
1156 dontScrollVert = true;
1157 id = (int64)(intptr)addedField;
1158 text = addedField.header;
1159 bevel = (!guiApp.textMode && !style.clearHeader);
1161 alignment = addedField.alignment;
1162 NotifyPushed = HeaderPushed;
1163 NotifyClicked = HeaderClicked;
1164 NotifyDoubleClick = HeaderDoubleClicked;
1165 NotifyReleased = HeaderReleased;
1166 NotifyMouseMove = HeaderMouseMove;
1168 incref addedField.headButton;
1169 addedField.headButton.Create();
1172 addedField.headButton.background = Color { 0, 170, 0 };
1178 for(row = rows.first; row; )
1180 int size = (field.dataType && field.dataType.typeSize) ?
1181 (sizeof(class ListBoxCell) + field.dataType.typeSize - sizeof(void *)) : sizeof(class ListBoxCell);
1182 ListBoxCell cell = (ListBoxCell)new0 byte[size];
1183 row.cells.Add(cell);
1184 FillBytes(cell.data, 0, size - (uint)(uintptr)&((ListBoxCell)0).data);
1187 if(row.subRows.first)
1188 row = row.subRows.first;
1191 for(; row; row = row.parent)
1193 if(row.next) { row = row.next; break; }
1198 OnResize(clientSize.w, clientSize.h);
1207 Clear(); // Ensure data is cleared first
1208 while((field = fields.first))
1213 endBevel.visible = false;
1218 void RemoveField(DataField field)
1224 int index = field.index;
1227 if(sortField == field)
1230 for(row = rows.first; row; )
1235 for(cell = row.cells.first, c = 0; c < index && cell; c++, cell = cell.next);
1236 if(cell && index == c)
1240 // TOCHECK: Is this check good? Will need incref/decref sometime?
1241 if(field.dataType.type == normalClass || field.dataType.type == noHeadClass)
1243 if(cell.data[0] && field.freeData)
1244 ((void (*)(void *, void *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnFree])(field.dataType, cell.data[0]);
1247 ((void (*)(void *, void *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnFree])(field.dataType, cell.data);
1250 row.cells.Remove(cell);
1254 if(row.subRows.first)
1255 row = row.subRows.first;
1258 for(; row; row = row.parent)
1260 if(row.next) { row = row.next; break; }
1269 endBevel.visible = false;
1273 DataRow AddRowNone()
1275 DataRow row { noneRow = true };
1282 rows.Insert(null, row);
1285 for(search = row.GetNextRow(); search; search = search.GetNextRow())
1291 firstRowShown = row;
1295 (rowCount * rowHeight) +
1296 ((style.header) ? rowHeight : 0) -
1297 ((!((clientSize.h+1) % rowHeight)) ? rowHeight : 0), true);
1298 if(style.autoScroll)
1299 SetScrollPosition(0, MAXINT - rowHeight);
1300 modifiedDocument = true;
1319 // Find very last row
1322 for(lastRow = rows.last; lastRow && !lastRow.collapsed && lastRow.subRows.last; lastRow = lastRow.subRows.last);
1323 row.index = lastRow ? (lastRow.index + 1) : 0;
1331 for(c = 0; c<fields.count; c++)
1333 for(field = fields.first; field; field = field.next)
1334 if((int)field.index == c)
1338 int size = (field.dataType && field.dataType.typeSize) ?
1339 (sizeof(class ListBoxCell) + field.dataType.typeSize - sizeof(void *)) : sizeof(class ListBoxCell);
1340 ListBoxCell cell = (ListBoxCell) new0 byte[size];
1341 row.cells.Add(cell);
1342 FillBytes(cell.data, 0, size - (uint)(uintptr)&((ListBoxCell)0).data);
1349 firstRowShown = row;
1355 (rowCount * rowHeight) +
1356 ((style.header) ? rowHeight : 0) -
1357 ((!((clientSize.h+1) % rowHeight)) ? rowHeight : 0), true);
1358 if(style.autoScroll)
1359 SetScrollPosition(0, MAXINT - rowHeight);
1360 modifiedDocument = true;
1371 DataRow AddRowAfter(DataRow after)
1383 if(after && after.subRows.first && !after.collapsed)
1385 for(search = after.subRows.last; !search.collapsed && search.subRows.last; )
1386 search = search.subRows.last;
1387 row.index = search.index + 1;
1390 row.index = after ? (after.index + 1) : 0;
1391 rows.Insert(after, row);
1394 for(search = row.GetNextRow(); search; search = search.GetNextRow())
1399 for(c = 0; c<fields.count; c++)
1401 for(field = fields.first; field; field = field.next)
1402 if((int)field.index == c)
1406 int size = (field.dataType && field.dataType.typeSize) ?
1407 (sizeof(class ListBoxCell) + field.dataType.typeSize - sizeof(void *)) : sizeof(class ListBoxCell);
1408 ListBoxCell cell = (ListBoxCell) new0 byte[size];
1409 row.cells.Add(cell);
1410 FillBytes(cell.data, 0, size - (uint)(uintptr)&((ListBoxCell)0).data);
1414 if(!firstRowShown || !after)
1416 firstRowShown = row;
1421 (rowCount * rowHeight) +
1422 ((style.header) ? rowHeight : 0) -
1423 ((!((clientSize.h+1) % rowHeight)) ? rowHeight : 0), true);
1424 if(style.autoScroll)
1425 SetScrollPosition(0, MAXINT - rowHeight);
1426 modifiedDocument = true;
1436 DataRow AddStringf(const char * format, ...)
1442 char string[MAX_F_STRING];
1445 va_start(args, format);
1446 vsnprintf(string, sizeof(string), format ? format : "", args);
1447 string[sizeof(string)-1] = 0;
1451 row.SetData(fields.first, string);
1457 DataRow AddString(const char * string)
1463 row.SetData(fields.first, string);
1469 void SelectRow(DataRow row)
1471 SetCurrentRow(row, true);
1474 void DeleteRow(DataRow row)
1476 if(!row) row = currentRow;
1479 DataRow sub, next, search;
1480 // Trying to move this here (Messed up deleting watches)
1481 //HideEditBox(false, false, true);
1484 for(sub = row.subRows.first; sub; sub = next)
1490 if(row.parent.IsExpanded())
1492 for(search = row.GetNextRow(); search; search = search.GetNextRow())
1497 HideEditBox(false, false, true);
1499 if(row == clickedRow)
1501 clickedRow = row.GetNextRow();
1503 clickedRow = row.GetPrevRow();
1506 if(row == currentRow)
1508 DataRow newCurrentRow = row.GetNextRow();
1510 newCurrentRow = row.GetPrevRow();
1511 SetCurrentRow(newCurrentRow, true);
1514 if(row == firstRowShown)
1516 firstRowShown = row.GetPrevRow();
1518 firstRowShown = row.GetNextRow();
1521 (row.parent ? row.parent.subRows: rows).Remove(row);
1524 //HideEditBox(false, false, true);
1528 (this.rowCount * rowHeight) +
1529 ((style.header) ? rowHeight : 0) -
1530 ((!((clientSize.h+1) % rowHeight)) ? rowHeight : 0), true);
1532 modifiedDocument = true;
1541 DataRow FindRow(int64 tag)
1546 for(row = rows.first; row; row = row.next)
1548 if(!row.noneRow && row.tag == tag)
1556 DataRow FindString(const char * searchedString)
1559 bool checkNextField = true;
1561 for(field = fields.first; field; field = field.next)
1563 if(field.dataType._vTbl[__ecereVMethodID_class_OnGetString])
1566 for(row = rows.first; row; row = row.GetNextRow())
1570 void * data = row.GetData(field);
1571 char tempString[1024] = "";
1572 bool needClass = false;
1573 const char * string = ((const char *(*)(void *, void *, char *, void *, bool *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString])(field.dataType, data, tempString, null, &needClass);
1575 if(string && string[0])
1576 checkNextField = false;
1577 if(string && string[0] && !strcmp(string, searchedString))
1582 if(!checkNextField) break;
1587 DataRow FindSubString(const char * subString)
1590 bool checkNextField = true;
1591 int len = subString ? strlen(subString) : 0;
1595 for(field = fields.first; field; field = field.next)
1597 if(field.dataType._vTbl[__ecereVMethodID_class_OnGetString])
1600 for(row = rows.first; row; row = row.GetNextRow())
1604 void * data = row.GetData(field);
1605 char tempString[1024] = "";
1606 bool needClass = false;
1607 const char * string = ((const char *(*)(void *, void *, char *, void *, bool *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString])(field.dataType, data, tempString, null, &needClass);
1609 if(string && string[0])
1610 checkNextField = false;
1611 if(string && string[0] && !strncmp(string, subString, len))
1616 if(!checkNextField) break;
1622 DataRow FindSubStringi(const char * subString)
1625 bool checkNextField = true;
1626 int len = subString ? strlen(subString) : 0;
1627 DataRow result = null;
1628 const char * bestResult = null;
1633 for(field = fields.first; field; field = field.next)
1635 if(field.dataType._vTbl[__ecereVMethodID_class_OnGetString])
1638 for(row = rows.first; row; row = row.GetNextRow())
1642 void * data = row.GetData(field);
1643 char tempString[1024] = "";
1644 bool needClass = false;
1645 const char * string = ((const char *(*)(void *, void *, char *, void *, bool *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString])(field.dataType, data, tempString, null, &needClass);
1647 if(string && string[0])
1648 checkNextField = false;
1649 if(string && string[0])
1651 int stringLen = strlen(string);
1653 if(!strnicmp(string, subString, Min(len, stringLen)))
1655 if(bestLen < Min(len, stringLen))
1657 if(!bestResult || strcmpi(string, bestResult) < 0)
1659 bestLen = Min(len, stringLen);
1660 bestResult = string;
1668 if(!checkNextField) break;
1674 DataRow FindSubStringAfter(DataRow after, const char * subString)
1677 bool checkNextField = true;
1678 int len = subString ? strlen(subString) : 0;
1682 for(field = fields.first; field; field = field.next)
1684 if(field.dataType._vTbl[__ecereVMethodID_class_OnGetString])
1687 for(row = after.GetNextRow(); row && row != after; row = row.GetNextRow(), (!row) ? (row = rows.first) : null)
1691 void * data = row.GetData(field);
1692 char tempString[1024] = "";
1693 bool needClass = false;
1694 const char * string = ((const char *(*)(void *, void *, char *, void *, bool *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString])(field.dataType, data, tempString, null, &needClass);
1696 if(string && string[0])
1697 checkNextField = false;
1698 if(string && string[0] && !strncmp(string, subString, len))
1703 if(!checkNextField) break;
1709 DataRow FindSubRow(int64 tag)
1715 for(row = rows.first; row; row = row.next)
1717 if(!row.noneRow && row.tag == tag)
1719 if(!row.noneRow && row.subRows.first)
1721 DataRow subRow = row.FindSubRow(tag);
1735 Window master = this.master;
1737 HideEditBox(false, true, false);
1738 editData.Destroy(0);
1740 firstRowShown = currentRow = null;
1745 if(style.freeSelect)
1746 NotifyHighlight(master, this, currentRow ? currentRow : null, 0);
1748 NotifySelect(master, this, currentRow ? currentRow : null, 0);
1751 if(style.alwaysEdit && currentRow)
1752 currentRow.Edit(currentField);
1759 (this.rowCount * rowHeight) +
1760 ((style.header) ? rowHeight : 0) -
1761 ((rowHeight && !((clientSize.h+1) % rowHeight)) ? rowHeight : 0), true);
1766 void Sort(DataField field, int order)
1771 int headerSize = ((style.header) ? rowHeight : 0);
1772 int height = clientSize.h + 1 - headerSize;
1774 if(!field) field = fields.first;
1776 field.sortOrder = order ? order : 1;
1777 rows.Sort(DataRow::Compare, field);
1779 for(search = rows.first; search; search = search.next)
1780 search._SortSubRows(field, order);
1784 for(search = rows.first; search; search = search.GetNextRow())
1785 search.index = index++;
1788 if(currentRow && currentRow.index * rowHeight > scroll.y + height - rowHeight)
1789 SetScrollPosition(scroll.x, currentRow.index * rowHeight - height + rowHeight);
1790 else if(!currentRow || currentRow.index * rowHeight < scroll.y)
1791 SetScrollPosition(scroll.x, currentRow ? currentRow.index * rowHeight : 0);
1793 OnVScroll(0, scroll.y, 0);
1795 // SetScrollPosition(0, scroll.y);
1800 void StopEditing(bool save)
1802 HideEditBox(save, false, true);
1805 void GetMultiSelection(OldList list)
1808 if(this && style.multiSelect)
1812 for(row = rows.first; row; row = row.GetNextRow())
1814 if(row.selectedFlag == selected || row.selectedFlag == tempSelected)
1816 list.Add(OldLink { data = row });
1822 // Convenience Current Row Methods
1823 void * SetData(DataField field, any_object data)
1825 return currentRow.SetData(field, data);
1828 any_object GetData(DataField field)
1830 return (void *)currentRow.GetData(field);
1835 return currentRow ? currentRow.tag : 0;
1841 DataField defaultField { };
1842 rows.offset = (uint)(uintptr)&((DataRow)0).prev;
1843 fields.offset = (uint)(uintptr)&((DataField)0).prev;
1844 style.fullRowSelect = true;
1845 style.fillLastField = true;
1846 style.expandOnAdd = true;
1847 typingTimeOut = 0.5;
1848 rowHeight = 16; // Stuff depending on creation and default property checking
1851 defaultField.defaultField = true;
1853 AddField(defaultField);
1855 typedString = new char[1];
1856 typedString[0] = '\0';
1871 while((field = fields.first))
1873 // fields.Remove(field);
1883 while((row = rows.first))
1890 ListBoxCell GetCell(DataRow * row, DataField * field)
1892 ListBoxCell cell = null;
1893 if(!*row) *row = currentRow;
1896 if(!*field) *field = this ? currentField : null;
1899 for(*field = fields.first; (*field).index != 0; *field = (*field).next);
1904 if(field->listBox == this)
1906 for(index = 0, cell = (*row).cells.first; cell && index != (*field).index; index++, cell = cell.next);
1913 void HideEditBox(bool save, bool alwaysStopEdit, bool repositionOnly)
1915 if(editData && editData.visible)
1918 editData.SaveData();
1920 editData.visible = false;
1921 NotifyEditDone(master, this, currentRow);
1923 // ENSURE DATA BOX IS NOT VISIBLE
1924 editData.visible = false;
1926 if(style.alwaysEdit && !alwaysStopEdit)
1929 int height = rowHeight - (style.alwaysEdit ? 1 : 0);
1930 int y = currentRow.index * rowHeight + (style.header ? rowHeight : 0);
1931 int x = currentField.x;
1932 int width = (!currentField.next && style.fillLastField && (!hasHorzScroll || clientSize.w - currentField.x > currentField.width + EXTRA_SPACE)) ?
1933 clientSize.w - currentField.x : (currentField.width + EXTRA_SPACE);
1935 if(!style.alwaysEdit)
1937 editData.position = { x, y };
1938 editData.size = { width, height };
1942 editData.position = { x, y - editData.clientStart.y };
1943 editData.size = { width, height + editData.clientStart.y * 2 };
1945 editData.visible = true;
1946 if(style.alwaysEdit)
1947 editData.Deactivate();
1949 PopupEditBox(currentField, repositionOnly);
1953 currentField = null;*/
1957 void SetCurrentRow(DataRow row, bool notify)
1959 if(this && (currentRow != row || (currentRow && currentRow.selectedFlag == unselected)))
1961 int headerSize = ((style.header) ? rowHeight : 0);
1962 int height = clientSize.h + 1 - headerSize;
1964 // true: destroy edit box
1965 HideEditBox(true, true, false);
1967 if(!(style.multiSelect) && currentRow)
1968 currentRow.selectedFlag = unselected;
1972 if(style.multiSelect)
1976 for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
1977 selRow.selectedFlag = unselected;
1979 currentRow.selectedFlag = selected;
1984 currentRow.selectedFlag = selected;
1986 if(currentRow && currentRow.index * rowHeight > scroll.y + height - rowHeight)
1987 SetScrollPosition(scroll.x,
1988 currentRow.index * rowHeight - height + rowHeight);
1989 else if(!currentRow || currentRow.index * rowHeight < scroll.y)
1991 int line = currentRow ? currentRow.index * rowHeight : 0;
1992 //SNAPUP(line, rowHeight);
1993 SetScrollPosition(scroll.x, line);
1998 Window master = this.master;
2001 if(style.freeSelect && visible)
2002 NotifyHighlight(master, this, currentRow ? currentRow : null, 0);
2004 NotifySelect(master, this, currentRow ? currentRow : null, 0);
2005 if(style.alwaysEdit && currentRow)
2006 currentRow.Edit(currentField);
2014 void RepositionFieldEditor()
2016 if(editData && editData.visible)
2018 int height = rowHeight - (style.alwaysEdit ? 1 : 0);
2020 int y = currentRow.index * rowHeight + (style.header ? rowHeight : 0);
2024 if(style.collapse && !(style.treeBranch))
2026 for(field = fields.first; field; field = field.next)
2028 width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ?
2029 clientSize.w - field.x : (field.width + EXTRA_SPACE);
2030 if(field == currentField) break;
2033 if(!style.alwaysEdit)
2035 editData.position = { x, y - editData.clientStart.y };
2036 editData.size = { width, height + editData.clientStart.y * 2 };
2040 editData.position = { x, y };
2041 editData.size = { width, height };
2046 void PopupEditBox(DataField whichField, bool repositionOnly)
2048 if((!editData || !editData.visible || currentField != whichField) && currentRow)
2050 // true: destroy edit box
2051 HideEditBox(true, true, false);
2054 int height = rowHeight - (style.alwaysEdit ? 1 : 0);
2056 int y = currentRow.index * rowHeight + (style.header ? rowHeight : 0);
2058 //void * data = currentRow.GetData(whichField);
2063 if(style.collapse && !(style.treeBranch))
2066 for(field = fields.first; field; field = field.next)
2068 width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ?
2069 clientSize.w - field.x : (field.width + EXTRA_SPACE);
2070 if(field == whichField) break;
2074 currentField = whichField;
2075 cell = GetCell(&row, ¤tField);
2082 background = dataBoxBackground;
2083 foreground = dataBoxForeground;
2085 bool NotifyChanged(DataBox dataBox, bool closingDropDown)
2088 DataField field = null;
2089 ListBoxCell cell = GetCell(&row, &field);
2093 modifiedDocument = true;
2095 NotifyChanged(master, this, currentRow);
2100 bool NotifyModified()
2102 //DataRow row = null;
2103 //DataField field = null;
2104 //ListBoxCell cell = GetCell(&row, &field);
2105 //cell.isSet = true;
2106 modifiedDocument = true;
2108 NotifyModified(master, this, currentRow);
2112 bool OnKeyDown(Key key, unichar ch)
2114 bool result = DataBox::OnKeyDown(key, ch);
2115 if(visible && active) // Added this check here, because we will not use enter/escape otherwise, and lose DataBox's result
2117 if((SmartKey)key == enter || (SmartKey)key == escape)
2126 editData.Destroy(0);
2127 editData.type = whichField.dataType;
2128 editData.fieldData = whichField.userData;
2129 editData.borderStyle = style.alwaysEdit ? 0 : deep;
2130 editData.data = cell ? cell.data : null;
2133 // Might not really need this anymore...
2134 NotifyEditing(master, this, currentRow);
2137 if(!style.alwaysEdit)
2139 editData.position = { x, y - editData.clientStart.y };
2140 editData.size = { width, height + editData.clientStart.y * 2 };
2144 editData.position = { x, y };
2145 editData.size = { width, height };
2149 editData.visible = true;
2151 if(style.alwaysEdit)
2152 editData.Deactivate();
2154 // MOVED THIS HIGHER FOR DATALIST EDITOR
2156 // Might not really need this anymore...
2157 NotifyEdited(master, this, currentRow);
2162 void OnRedraw(Surface surface)
2165 int y = (style.header) ? rowHeight : 0;
2166 bool isActive = active;
2167 Font font = fontObject;
2168 Font boldFont = this.boldFont.font;
2172 if(style.alwaysEdit && style.fullRowSelect)
2175 int y = (style.header) ? rowHeight : 0;
2176 int x = -scroll.x - 1;// + EXTRA_SPACE;// / 2 - 1;
2177 int w = clientSize.w;
2178 int h = clientSize.h;
2182 // Fill out indent column
2183 if(style.collapse && !(style.treeBranch) && (style.header || rows.first))
2186 surface.SetBackground(formColor);
2187 surface.Area(-scroll.x, 0, x, clientSize.h);
2190 surface.SetForeground(formColor);
2191 for(row = firstRowShown; row; row = row.GetNextRow())
2194 surface.HLine(x + 1, w-1, y-1);
2200 for(field = fields.first; field; field = field.next)
2202 int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ?
2203 clientSize.w - field.x : (field.width + EXTRA_SPACE);
2204 if(field.prev && y > 0)
2205 surface.VLine(0, y-1, x);
2210 surface.foreground = this.foreground;
2211 surface.TextOpacity(false);
2213 // Draw the tree branches
2214 if(style.treeBranch)
2216 int y = -scroll.y + ((style.header) ? rowHeight : 0);
2217 surface.LineStipple(0x5555);
2218 surface.SetForeground(branchesColor);
2219 for(row = rows.first; row; row = row.GetNextRow() )
2221 int x = -scroll.x + EXTRA_SPACE / 2-1;
2222 int rowStart = -scroll.x;
2227 for(parent = row.parent; parent; parent = parent.parent)
2228 if(!parent.header) indent += 20;
2229 if(style.rootCollapse) indent += 20;
2231 plusIndent = (style.treeBranch) ? (indent - 20 + 4) : indent;
2236 if(row.subRows.first)
2239 int y1 = y + PLUSY + 4;
2243 for(child = row.collapsed ? null : row.subRows.first; child && child != row; )
2246 if(child.subRows.first && !child.collapsed && child != row.subRows.last)
2247 child = child.subRows.first;
2252 for(; child && child != row; child = child.parent)
2262 y2 = y + numRows * rowHeight + PLUSY + 4;
2263 surface.VLine(y1, y2, rowStart + plusIndent + 7 + 20);
2265 surface.HLine(rowStart + plusIndent + 7, rowStart + indent, y + PLUSY + 4);
2268 if(y >= clientSize.h)
2271 // Root Vertical Lines
2272 if(style.rootCollapse && rows.first)
2277 y = -scroll.y + ((style.header) ? rowHeight : 0);
2279 for(child = rows.first; child && child != rows.last; )
2282 if(child.subRows.first && !child.collapsed && child != rows.last)
2283 child = child.subRows.first;
2288 for(; child; child = child.parent)
2298 y2 = y + numRows * rowHeight + PLUSY + 4;
2299 surface.VLine(y1, y2, -scroll.x + 11);
2301 surface.LineStipple(0);
2304 for(row = firstRowShown; row; row = row.GetNextRow() )
2306 int x = -scroll.x + EXTRA_SPACE / 2-1;
2309 Color foreground = this.foreground /*black*/, background = this.background /*white*/;
2310 DataDisplayFlags dataDisplayFlags = 0;
2311 int rowStart = -scroll.x;
2314 Bitmap icon = row.icon ? row.icon.bitmap : null;
2315 int collapseRowStart = 0;
2316 bool lastWasHeader = row.header;
2318 for(parent = row.parent; parent; parent = parent.parent)
2320 if(!parent.header || lastWasHeader)
2322 if(style.treeBranch)
2328 if(style.rootCollapse) indent += 20;
2331 dataDisplayFlags.fullRow = style.fullRowSelect;
2332 dataDisplayFlags.active = isActive;
2333 dataDisplayFlags.header = row.header;
2338 collapseRowStart = rowStart;
2340 if(!(style.treeBranch))
2347 if(style.multiSelect)
2349 dataDisplayFlags.selected = row.selectedFlag == selected || row.selectedFlag == tempSelected;
2350 dataDisplayFlags.current = row == currentRow || (!currentRow && row == firstRowShown);
2354 dataDisplayFlags.selected = row.selectedFlag == selected || row.selectedFlag == tempSelected;
2355 dataDisplayFlags.current = row == currentRow || (!currentRow && row == firstRowShown);
2357 if(row == currentRow)
2359 dataDisplayFlags.current = true;
2360 dataDisplayFlags.selectedFlag = true;
2362 else if(!currentRow && row == firstRowShown)
2364 dataDisplayFlags.current = true;
2368 surface.TextOpacity(true);
2370 background = this.background;
2371 foreground = this.foreground;
2373 // Draw the current row background
2376 Color colors[] = { formColor, azure, mistyRose, linen, floralWhite, lavender, lavenderBlush, lemonChiffon };
2379 while((p = p.parent)) level++;
2380 background = colors[level % (sizeof(colors)/sizeof(colors[0]))];
2381 surface.SetBackground(background);
2382 surface.Area(rowStart, y, clientSize.w, (y + rowHeight) - 1);
2383 foreground = branchesColor;
2385 else if(dataDisplayFlags.selected)
2387 if(dataDisplayFlags.selected && (isActive || style.alwaysHL || (style.alwaysEdit && style.fullRowSelect)))
2389 if(!isActive && style.alwaysEdit)
2390 background = formColor;
2392 background = selectionColor ? selectionColor : SELECTION_COLOR;
2393 if(style.fullRowSelect)
2395 int offset = (style.alwaysEdit) ? 2 : 1;
2396 surface.SetBackground(background);
2397 surface.Area(rowStart, y, clientSize.w, (y + rowHeight) - offset);
2399 if(isActive || !(style.alwaysEdit))
2400 foreground = selectionText ? selectionText : SELECTION_TEXT;
2402 foreground = branchesColor;
2408 surface.Blit(icon, x + (20 - icon.width) /2,y + 2,0,0, icon.width, icon.height);
2414 int width = clientSize.w;
2416 dataDisplayFlags.firstField = true;
2417 clip.left = x - EXTRA_SPACE / 2+1;
2419 clip.right = x + width - EXTRA_SPACE/2 - 0;
2420 clip.bottom = y + rowHeight - 1;
2421 surface.Clip(&clip);
2423 surface.TextFont(font);
2425 surface.SetForeground(foreground);
2426 surface.SetBackground(background);
2428 ((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);
2432 if(opacity < 1) surface.TextOpacity(false);
2434 for(field = fields.first; field; field = field.next)
2437 int width = ((!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) || row.header) ?
2438 clientSize.w - field.x : (field.width + EXTRA_SPACE);
2440 // Box clip = { x, y+1, x + field.width - EXTRA_SPACE - 1, y + rowHeight - 2 };
2443 //width -= EXTRA_SPACE;
2445 if(!field.prev) width -= indent;
2448 dataDisplayFlags.firstField = field.prev ? false : true;
2450 if(!dataDisplayFlags.firstField && !dataDisplayFlags.fullRow)
2452 background = this.background;
2453 foreground = this.foreground;
2456 if(!isActive && dataDisplayFlags.selected && style.alwaysEdit && field.editable && opacity)
2459 surface.SetBackground(background);
2460 surface.Area(x-3, y, x+width-1, y + rowHeight-2);
2463 clip.left = x - EXTRA_SPACE / 2+1;
2465 clip.right = x + width - EXTRA_SPACE/2 - 0;
2466 clip.bottom = y + rowHeight - 1;
2467 surface.Clip(&clip);
2469 for(index = 0, cell = row.cells.first; cell && index != field.index; index++, cell = cell.next);
2470 // Should always be as many cells in the row as fields in the listbox
2471 if(cell && cell.isSet && field.dataType)
2474 surface.TextFont(boldFont);
2476 surface.TextFont(font);
2478 surface.SetForeground(foreground);
2479 surface.SetBackground(background);
2481 if(field.dataType.type == noHeadClass || field.dataType.type == normalClass)
2482 ((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);
2484 ((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);
2487 if(!isActive && dataDisplayFlags.selected && style.alwaysEdit && field.editable)
2488 background = formColor;
2490 if(!dataDisplayFlags.firstField && !dataDisplayFlags.fullRow)
2492 background = formColor;
2493 foreground = this.background;
2496 x += width;// + EXTRA_SPACE;
2498 if(row.header) break;
2504 int plusIndent = (style.treeBranch) ? (indent - 20 + 4) : indent;
2506 if(row.subRows.first && (row.parent || !(style.treeBranch) || (style.rootCollapse)))
2508 surface.SetForeground(row.header ? headerCollapseForeground : this.foreground);
2509 surface.Rectangle(collapseRowStart + 3 + plusIndent, y + PLUSY, collapseRowStart + 11 + plusIndent, y + PLUSY + 8);
2511 surface.SetBackground(row.header ? (formColor) : (this.background)); //white
2512 surface.Area(collapseRowStart + 4 + plusIndent, y + PLUSY + 1, collapseRowStart + 10 + plusIndent, y + PLUSY + 7);
2514 surface.HLine(collapseRowStart + 5 + plusIndent, collapseRowStart + 9 + plusIndent, y+PLUSY+4);
2516 surface.VLine(y + PLUSY + 2, y + PLUSY + 6, collapseRowStart + 7 + plusIndent);
2521 // Draw the current row stipple
2522 if(style.fullRowSelect && !(style.alwaysEdit) && (dataDisplayFlags.current) && isActive)
2527 surface.LineStipple(0x5555);
2528 if(dataDisplayFlags.selected)
2529 surface.SetForeground(stippleColor);
2531 surface.SetForeground(this.foreground);
2534 surface.SetForeground(selectionColor ? selectionColor : SELECTION_COLOR);
2535 surface.Rectangle(0, y, clientSize.w-1, (y + rowHeight) - 1);
2536 surface.LineStipple(0);
2540 if(y >= clientSize.h)
2543 if(firstRowShown) surface.Clip(null);
2544 if(this.dragRow && this.dropIndex != -1)
2546 int dropIndex = this.dropIndex;
2549 if(!style.multiSelect && currentRow.index < this.dropIndex)
2551 surface.DrawingChar(223);
2553 y = style.header ? rowHeight : 0;
2554 y += dropIndex * rowHeight - scroll.y;
2556 surface.SetForeground(Color { 85, 85, 255 });
2557 surface.HLine(0, clientSize.w-1, y);
2558 surface.HLine(0, clientSize.w-1, y + 1);
2562 void OnDrawOverChildren(Surface surface)
2564 if(draggingField && this.dropField)
2566 int position = this.dropField.x;
2567 if(draggingField.x < position)
2568 position += this.dropField.width + EXTRA_SPACE;
2570 surface.SetForeground(Color { 85, 85, 255 });
2571 surface.VLine(0, rowHeight - 1, position - scroll.x - 2);
2572 surface.VLine(0, rowHeight - 1, position - scroll.x);
2574 if(sortField && !style.clearHeader && style.header)
2576 DataField field = sortField;
2577 int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ?
2578 clientSize.w - field.x : (field.width + EXTRA_SPACE);
2581 surface.TextExtent(field.header, strlen(field.header), &tw, &th);
2582 if(tw < width - EXTRA_SPACE)
2584 bool up = field.sortOrder == 1;
2588 field.x + 2 - scroll.x, 0,
2589 field.x + width + EXTRA_SPACE - 1 - scroll.x, rowHeight
2591 surface.Clip(&clip);
2592 if(field.alignment == left || field.alignment == center)
2594 if(field.alignment == center)
2595 x = field.x + (width + EXTRA_SPACE - tw) / 2 + tw + EXTRA_SPACE + 4;
2597 x = field.x + tw + EXTRA_SPACE + 4;
2599 x = Min(x, field.x + width - 4);
2601 else if(field.alignment == right)
2603 x = field.x + width - tw - 2*EXTRA_SPACE - 4;
2604 x = Max(x, field.x + 2);
2610 // surface.SetForeground((wmenu.selectedFlag == item) ? white : black);
2611 // surface.WriteText(clientSize.w-8, y+(wmenu.rh - 8)/2, "\020", 1);
2617 surface.SetForeground(Color { 128,128,128 } );
2618 surface.DrawLine(x + 3, y, x, y + 5);
2619 surface.PutPixel(x + 1, y + 5);
2620 surface.PutPixel(x + 1, y + 3);
2621 surface.PutPixel(x + 2, y + 1);
2623 surface.SetForeground(white);
2624 surface.DrawLine(x + 4, y, x + 7, y + 5);
2625 surface.PutPixel(x + 6, y + 5);
2626 surface.PutPixel(x + 6, y + 3);
2627 surface.PutPixel(x + 5, y + 1);
2629 surface.DrawLine(x, y + 6, x + 7, y + 6);
2633 surface.SetForeground(Color { 128,128,128 });
2634 surface.DrawLine(x + 3, y+6, x, y+1);
2635 surface.PutPixel(x + 1, y+1);
2636 surface.PutPixel(x + 1, y+3);
2637 surface.PutPixel(x + 2, y+5);
2639 surface.SetForeground(white);
2640 surface.DrawLine(x + 4, y+6, x + 7, y+1);
2641 surface.PutPixel(x + 6, y+1);
2642 surface.PutPixel(x + 6, y+3);
2643 surface.PutPixel(x + 5, y+5);
2645 surface.DrawLine(x, y, x + 7, y);
2654 void OnResize(int w, int h)
2657 bool showEndBevel = false;
2659 if(style.collapse && !style.treeBranch)
2661 for(field = fields.first; field; field = field.next)
2663 int width = field.width + EXTRA_SPACE;
2671 (rowCount * rowHeight) +
2672 ((style.header) ? rowHeight : 0) -
2673 ((!((clientSize.h+1) % rowHeight)) ? rowHeight : 0), true);
2675 for(field = fields.first; field; field = field.next)
2677 int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ?
2678 clientSize.w - field.x : (field.width + EXTRA_SPACE);
2679 if(style.header && field.headButton)
2681 showEndBevel = true;
2684 field.headButton.position = { field.x, 0 };
2685 field.headButton.size = { width, rowHeight };
2686 field.headButton.visible = true;
2689 field.headButton.visible = false;
2693 if(!style.fillLastField && showEndBevel && endBevel)
2695 endBevel.position = { x, 0 };
2696 endBevel.size = { clientSize.w + 2 - x, rowHeight };
2697 endBevel.visible = true;
2700 endBevel.visible = false;
2702 if(style.alwaysEdit && editData && editData.visible)
2704 HideEditBox(true, false, true);
2706 else if(editData && editData.visible)
2707 RepositionFieldEditor();
2710 void AdaptToFieldWidth(DataField field, bool doScroll)
2712 OnResize(clientSize.w, clientSize.h);
2714 // Scroll appropriately
2717 if(field.x + field.width + EXTRA_SPACE - clientSize.w > scroll.x &&
2718 field.x >= field.x + field.width + EXTRA_SPACE - clientSize.w)
2720 SetScrollPosition(field.x + field.width + EXTRA_SPACE - clientSize.w, scroll.y);
2722 else if(field.x + field.width + EXTRA_SPACE - clientSize.w > scroll.x ||
2724 SetScrollPosition(field.x, scroll.y);
2729 bool HeaderPushed(Button control, int x, int y, Modifiers mods)
2731 DataField field = (DataField)(intptr)control.id;
2732 // false: dont destroy edit box
2733 HideEditBox(true, false, true);
2734 if(style.resizable && ((!field && x < RESIZE_BORDER && fields.last) ||
2735 (field && x < RESIZE_BORDER && field.prev) ||
2736 (field && x >= control.clientSize.w - RESIZE_BORDER)))
2739 field = fields.last;
2740 else if(x < RESIZE_BORDER && field.prev)
2743 if(field.fixed) return false;
2744 resizingField = field;
2745 this.resizeX = x + control.position.x;
2746 this.startWidth = field.width;
2747 this.oldX = x - scroll.x;
2752 if(field.fixed) return false;
2753 draggingField = field;
2754 if(style.moveFields)
2755 field.headButton.stayDown = true;
2756 else if(!style.sortable)
2764 bool HeaderMouseMove(Button control, int x, int y, Modifiers mods)
2769 DataField field = resizingField;
2771 x += control.position.x;
2773 // Tweak to prevent shrinking field if we're actually moving right
2774 if(x - scroll.x > this.oldX &&
2775 this.startWidth + x - this.resizeX < field.width)
2777 this.oldX = x - scroll.x;
2780 this.oldX = x - scroll.x;
2782 field.width = this.startWidth + x - this.resizeX;
2783 field.width = Max(field.width, - EXTRA_SPACE);
2785 AdaptToFieldWidth(field, true);
2787 else if(draggingField)
2789 x += control.position.x;
2790 if(style.moveFields)
2792 DataField field = fields.last;
2794 for(field = fields.first; field; field = field.next)
2796 fieldX += ((field.width || style.resizable) ?
2797 field.width : clientSize.w) + EXTRA_SPACE;
2801 if(draggingField == field)
2803 // Reset scroll position
2804 if(field.x + field.width + EXTRA_SPACE - clientSize.w > scroll.x &&
2805 field.x >= field.x + field.width + EXTRA_SPACE - clientSize.w)
2808 field.x + field.width + EXTRA_SPACE - clientSize.w,
2811 else if(field.x + field.width + EXTRA_SPACE - clientSize.w > scroll.x ||
2813 SetScrollPosition(field.x, scroll.y);
2816 if(this.dropField != field)
2818 this.dropField = field;
2821 int position = field.x;
2823 if(draggingField.x < position)
2825 position += field.width + EXTRA_SPACE - clientSize.w;
2826 if(position > scroll.x)
2827 SetScrollPosition(position, scroll.y);
2832 if(position < scroll.x)
2833 SetScrollPosition(position, scroll.y);
2836 this.movingFields = true;
2842 else if(style.resizable)
2844 DataField field = (DataField)(intptr)control.id;
2847 if(x < RESIZE_BORDER && field.prev)
2849 if(!field.prev.fixed)
2850 control.cursor = guiApp.GetCursor(sizeWE);
2852 else if(x >= control.clientSize.w - RESIZE_BORDER)
2853 control.cursor = guiApp.GetCursor(sizeWE);
2855 control.cursor = null;
2859 if(x < RESIZE_BORDER && fields.last)
2860 control.cursor = guiApp.GetCursor(sizeWE);
2862 control.cursor = null;
2868 bool HeaderReleased(Button control, int x, int y, Modifiers mods)
2872 NotifyResized(master, this, resizingField, mods);
2873 resizingField = null;
2880 if(style.moveFields)
2885 DataField switchField = fields.last;
2889 x += draggingField.x;
2890 for(field = fields.first; field; field = field.next)
2892 fieldX += ((field.width || style.resizable) ?
2893 field.width : clientSize.w) + EXTRA_SPACE;
2896 switchField = field;
2900 if(switchField && draggingField != switchField && this.dropField)
2902 for(field = fields.first; field; field = field.next)
2904 if(field == switchField || field == draggingField)
2908 // Switch field first: move before
2909 if(field == switchField)
2910 draggingField.Move(switchField.prev);
2911 // Dragged field first: move after
2913 draggingField.Move(switchField);
2915 NotifyMovedField(master, this, draggingField, mods);
2917 draggingField.headButton.stayDown = false;
2922 movingFields = false;
2924 draggingField = null;
2930 bool HeaderClicked(Button control, int x, int y, Modifiers mods)
2932 if(style.header && !this.dropField && style.sortable)
2934 DataField field = (DataField)(intptr)control.id;
2935 if(sortField == field)
2936 field.sortOrder *= -1;
2941 Sort(sortField, field.sortOrder);
2942 NotifySort(master, this, field, mods);
2948 bool HeaderDoubleClicked(Button control, int x, int y, Modifiers mods)
2952 DataField field = (DataField)(intptr)control.id;
2955 if(x < RESIZE_BORDER && field.prev)
2957 else if(x >= control.clientSize.w - RESIZE_BORDER);
2963 if(x < RESIZE_BORDER && fields.last)
2964 field = fields.last;
2976 if(style.freeSelect)
2981 this.rolledOver = this.dragging = false;
2988 bool OnLoadGraphics()
2990 display.FontExtent(fontObject, "W", 1, null, &fontH);
2991 if(!style.heightSet)
2993 rowHeight = Max(fontH + 2, 16) + (style.alwaysEdit ? 1 : 0);
2994 SetScrollLineStep(8, rowHeight);
2999 void OnApplyGraphics()
3001 SetScrollLineStep(8, rowHeight);
3005 for(field = fields.first; field; field = field.next)
3007 if(field.headButton)
3009 field.headButton.bevel = (!guiApp.textMode && !style.clearHeader);
3011 field.headButton.background = Color { 0, 170, 0 };
3015 OnResize(clientSize.w, clientSize.h);
3018 bool OnResizing(int *w, int *h)
3022 if(!initSize.w && (!anchor.left.type || !anchor.right.type) && !*w)
3027 Font font = fontObject;
3028 Font boldFont = this.boldFont.font;
3029 Display display = this.display;
3031 for(row = rows.first; row; row = row.GetNextRow())
3033 Bitmap icon = row.icon ? row.icon.bitmap : null;
3034 int x = -scroll.x + EXTRA_SPACE / 2-1;
3038 for(parent = row.parent; parent; parent = parent.parent)
3042 if(style.treeBranch)
3048 if(style.rootCollapse) indent += 20;
3050 if(style.collapse && !(style.treeBranch)) x += 15;
3054 // Compute the rows size
3055 for(field = fields.first; field; field = field.next)
3057 if(((style.resizable && (!(style.alwaysEdit) || field.next)) || field.width) && !row.header)
3058 x += field.width - (field.prev ? 0 : indent);
3063 for(index = 0, cell = row.cells.first; index != field.index; index++, cell = cell.next);
3065 // Should always be as many cells in the row as fields in the listbox
3066 if(cell && cell.isSet && field.dataType)
3068 static char tempString[4096];
3069 const char * string;
3071 if(field.dataType.type == normalClass || field.dataType.type == noHeadClass)
3072 string = ((const char *(*)(void *, void *, char *, void *, bool *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString])(field.dataType, cell.data[0], tempString, field.userData, null);
3074 string = ((const char *(*)(void *, void *, char *, void *, bool *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString])(field.dataType, cell.data, tempString, field.userData, null);
3076 if(!string) string = "";
3077 display.FontExtent(row.header ? boldFont : font, string, strlen(string), &tw, &th);
3080 display.FontExtent(row.header ? boldFont : font, string, strlen(string), &tw, &th);
3082 display.FontExtent(row.header ? boldFont : font, "", 0, &tw, &th);
3085 if(row.header) break;
3089 maxWidth = Max(maxWidth, x);
3095 *h = Min(this.maxShown, this.rowCount) * rowHeight;
3100 if(!*w) *w = rowHeight * 5;
3101 if(!*h) *h = rowHeight * 5;
3108 FontResource font = this.font;
3109 FontResource boldFont
3111 faceName = font.faceName, size = font.size, bold = true
3113 AddResource(boldFont);
3114 RemoveResource(this.boldFont);
3115 this.boldFont = boldFont;
3119 SetInitSize(initSize);
3122 bool OnMouseMove(int x, int y, Modifiers mods)
3124 bool isTimer = false;
3125 int realX = x, realY = y;
3127 if(insideNotifySelect) return true;
3129 if(style.alwaysEdit && style.resizable &&
3130 resizingField && !(mods.isSideEffect))
3133 DataField field = resizingField;
3134 field.width = this.startWidth + x - this.resizeX;
3135 field.width = Max(field.width, - EXTRA_SPACE);
3137 AdaptToFieldWidth(field, true);
3141 if(style.alwaysEdit && style.resizable)
3143 int vx = -scroll.x - 1;
3146 if(style.collapse && !(style.treeBranch))
3149 for(field = fields.first; field; field = field.next)
3151 int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ?
3152 clientSize.w - field.x : (field.width + EXTRA_SPACE);
3158 cursor = guiApp.GetCursor(sizeWE);
3162 vx += width + EXTRA_SPACE;
3166 if((editData && editData.visible) || (style.alwaysEdit))
3169 if(x == MAXINT && y == MAXINT)
3176 // ADDED THIS CHECK FOR FieldDropBox LEAKS
3177 if(/*!mods.isSideEffect && */(this.rolledOver || !this.dragging))
3179 int rowY = (style.header) ? rowHeight : 0;
3187 ((vertScroll && vertScroll.visible &&
3188 (y < 0 || y >= clientSize.h)) ||
3189 (horzScroll && horzScroll.visible &&
3190 (x < 0 || x >= clientSize.w))))
3195 if(vertScroll && vertScroll.visible &&
3196 (y < 0 || y >= clientSize.h))
3197 vertScroll.Action((y<0)?up:down, 0, 0);
3198 if(horzScroll && horzScroll.visible &&
3199 (x < 0 || x >= clientSize.w))
3200 horzScroll.Action((x<0)?up:down, 0, 0);
3206 // This must be done after the scrolling took place
3207 rowIndex = firstRowShown ? firstRowShown.index : -1;
3209 y = Min(y, clientSize.h-1);
3210 for(row = firstRowShown; row; row = row.GetNextRow(), rowIndex ++)
3219 if(row && currentRow != row)
3221 if(this.dragRow && style.moveRows)
3223 if(row.selectedFlag == selected || row.selectedFlag == tempSelected)
3225 else if(style.multiSelect)
3228 for(thisRow = rows.first; thisRow; thisRow = thisRow.GetNextRow())
3229 if((thisRow.selectedFlag == selected || thisRow.selectedFlag == tempSelected) ||
3235 if(this.dropIndex != rowIndex)
3237 this.dropIndex = rowIndex;
3238 this.editRow = null;
3240 this.movedRow = true;
3243 else if((style.freeSelect || this.dragging) && ((realX>= 0 && realY >= 0 && realX< clientSize.w && realY < clientSize.h) || this.rolledOver))
3245 if(!(style.multiSelect))
3247 if(currentRow)currentRow.selectedFlag = unselected;
3248 if(row)row.selectedFlag = selected;
3252 if(style.multiSelect)
3256 for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3258 if(selRow.selectedFlag == tempUnselected) selRow.selectedFlag = selected;
3259 else if(selRow.selectedFlag == tempSelected) selRow.selectedFlag = unselected;
3262 if(rowIndex >= clickedRow.index)
3264 for(selRow = clickedRow; selRow; selRow = selRow.GetNextRow())
3266 selRow.selectedFlag = (selRow.selectedFlag == selected) ? tempUnselected : tempSelected;
3273 for(selRow = row; selRow; selRow = selRow.GetNextRow())
3275 selRow.selectedFlag = (selRow.selectedFlag == selected) ? tempUnselected : tempSelected;
3276 if(selRow == clickedRow)
3282 if(style.freeSelect)
3283 NotifyHighlight(master, this, currentRow, mods);
3286 insideNotifySelect = true;
3287 NotifySelect(master, this, currentRow, mods);
3288 insideNotifySelect = false;
3291 if(style.alwaysEdit && currentRow)
3292 currentRow.Edit(currentField);
3299 bool OnMouseOver(int x, int y, Modifiers mods)
3302 this.rolledOver = true;
3306 bool OnActivate(bool active, Window swap, bool * goOnWithActivation, bool direct)
3308 // TOCHECK: WAS THIS MISSING ? CHECK FOR SLOWDOWN
3309 if(!active) Update(null);
3311 if(!active && (!swap || !swap.isModal))
3313 // true: destroy edit box
3314 HideEditBox(true, true, false);
3316 else if(!swap || !swap.isModal)
3318 // Bring back edit box
3319 if(currentRow && style.alwaysEdit)
3321 currentRow.Edit(currentField ? currentField : null);
3325 return true; //NotifyActivate(master, this, active, swap, 0);
3329 bool OnButtonDown(int x, int y, Modifiers mods, bool right)
3332 // Check to see if we're dragging the vertical divider
3333 if(style.alwaysEdit && style.resizable && !right)
3335 int vx = -scroll.x - 1;// + EXTRA_SPACE;// / 2 - 1;
3338 if(style.collapse && !(style.treeBranch))
3341 for(field = fields.first; field; field = field.next)
3343 int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ?
3344 clientSize.w - field.x : (field.width + EXTRA_SPACE);
3350 resizingField = field.prev;
3352 this.startWidth = resizingField.width;
3354 SetMouseRangeToClient();
3358 vx += width + EXTRA_SPACE;
3362 if(!(style.freeSelect))
3364 int rowY = (style.header) ? rowHeight : 0;
3366 int rowIndex = firstRowShown ? firstRowShown.index : -1;
3367 DataRow previousRow = currentRow;
3368 DataRow newCurrentRow = null;
3369 bool moveMultiple = false;
3370 int numSelected = 0;
3371 int rowStart = -scroll.x;
3373 if(style.multiSelect)
3380 for(row = rows.first; row; row = row.GetNextRow())
3382 if(row.selectedFlag == tempSelected)
3383 row.selectedFlag = selected;
3384 else if(row.selectedFlag == tempUnselected)
3385 row.selectedFlag = unselected;
3386 if(row.selectedFlag == selected)
3393 for(row = firstRowShown; row; row = row.GetNextRow(), rowIndex ++)
3396 if(rowY > y || (style.multiSelect && !row.GetNextRow()))
3399 if(style.treeBranch)
3402 for(parent = (style.rootCollapse) ? row.parent : (row.parent ? row.parent.parent : null); parent; parent = parent.parent)
3408 /* THIS WAS TOO STRICT:
3409 if(style.collapse && row.subRows.first && (row.parent || !(style.treeBranch) || (style.rootCollapse)) &&
3410 (x >= rowStart + 3 + plusIndent && y >= rowY - rowHeight + PLUSY && x <= rowStart + 11 + plusIndent && y <= rowY - rowHeight + PLUSY + 8))
3412 if(style.collapse &&
3413 (x >= rowStart && y >= rowY - rowHeight && x <= rowStart + 18 + plusIndent && y <= rowY + rowHeight-1))
3415 if(row.subRows.first && (row.parent || !(style.treeBranch) || (style.rootCollapse)) && x >= plusIndent)
3416 row.collapsed = !row.collapsed;
3423 newCurrentRow = row;
3425 if(style.multiSelect)
3427 // Deselect everything if user didn't clicked on a row
3431 for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3432 selRow.selectedFlag = unselected;
3434 //this.clickedRowIndex = rowIndex;
3436 else if(style.moveRows && !(mods.shift) &&
3437 (row.selectedFlag == selected || row.selectedFlag == tempSelected) &&
3438 !right && !(mods.isActivate))
3439 moveMultiple = true;
3445 if(row.selectedFlag == tempUnselected || row.selectedFlag == unselected)
3447 for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3448 selRow.selectedFlag = unselected;
3449 row.selectedFlag = selected;
3452 //this.clickedRowIndex = rowIndex;
3458 for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3459 selRow.selectedFlag = unselected;
3463 for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3465 if(selRow != clickedRow)
3467 if(selRow.selectedFlag == tempUnselected) selRow.selectedFlag = selected;
3468 else if(selRow.selectedFlag == tempSelected) selRow.selectedFlag = unselected;
3475 if(rowIndex >= clickedRow.index)
3477 for(selRow = clickedRow; selRow; selRow = selRow.GetNextRow())
3481 if(selRow != clickedRow)
3483 if(selRow.selectedFlag)
3484 selRow.selectedFlag = tempUnselected;
3486 selRow.selectedFlag = tempSelected;
3490 selRow.selectedFlag = selected;
3497 for(selRow = row; selRow; selRow = selRow.GetNextRow())
3501 if(selRow != clickedRow)
3503 if(selRow.selectedFlag)
3504 selRow.selectedFlag = tempUnselected;
3506 selRow.selectedFlag = tempSelected;
3510 selRow.selectedFlag = selected;
3511 if(selRow == clickedRow)
3520 if(row.selectedFlag)
3521 row.selectedFlag = tempUnselected;
3522 else row.selectedFlag = tempSelected;
3525 row.selectedFlag = tempSelected;
3527 //this.clickedRowIndex = rowIndex;
3537 // true: destroy edit box
3540 incref newCurrentRow;
3543 if(currentRow != newCurrentRow)
3544 HideEditBox(true, true, false);
3548 if(newCurrentRow._refCount <= 1)
3549 delete newCurrentRow;
3551 newCurrentRow._refCount--;
3556 if(!(style.multiSelect))
3558 if(currentRow) currentRow.selectedFlag = unselected;
3559 if(newCurrentRow) newCurrentRow.selectedFlag = selected;
3563 if(currentRow != newCurrentRow)
3566 // true: destroy edit box
3569 //incref newCurrentRow;
3570 incref newCurrentRow;
3573 HideEditBox(true, true, false);
3578 int headerSize = ((style.header) ? rowHeight : 0);
3579 int height = clientSize.h + 1 - headerSize;
3581 /*if(newCurrentRow._refCount <= 1)
3582 delete newCurrentRow;
3585 newCurrentRow._refCount--;
3586 //newCurrentRow._refCount--;
3589 currentRow = newCurrentRow;
3591 if(currentRow && currentRow.index * rowHeight > scroll.y + height - rowHeight)
3592 SetScrollPosition(scroll.x,
3593 currentRow.index * rowHeight - height + rowHeight);
3594 else if(!currentRow || currentRow.index * rowHeight < scroll.y)
3596 int line = currentRow ? currentRow.index * rowHeight : 0;
3597 //SNAPUP(line, rowHeight);
3598 SetScrollPosition(scroll.x, line);
3601 // GO THROUGH SetCurrentRow eventually?
3602 // SetCurrentRow(newCurrentRow, true);
3606 if(style.freeSelect)
3607 NotifyHighlight(master, this, currentRow, mods);
3608 else if((moveMultiple || (!(style.multiSelect) && previousRow == currentRow)) &&
3609 newCurrentRow && !(mods.shift))
3613 if(!(mods.isActivate))
3617 this.dragRow = currentRow;
3618 this.dropIndex = -1;
3619 this.movedRow = false;
3621 if(editData && editData.visible && style.alwaysEdit)
3628 if(style.collapse && !style.treeBranch)
3631 for(parent = newCurrentRow.parent; parent; parent = parent.parent)
3633 indent += (style.treeBranch) ? 20 : 15;
3636 for(field = fields.first; field; field = field.next)
3638 int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ?
3639 clientSize.w - field.x : (field.width + EXTRA_SPACE);
3641 if(!field.prev) width -= indent;
3642 if(x >= sx && x < sx + width)
3646 if(field == currentField)
3647 editData.Deactivate();
3650 currentRow.Edit(field);
3651 editData.Activate();
3654 else if(!(mods.ctrl) && numSelected <= 1)
3655 this.editRow = currentRow;
3657 if(style.noDragging && newCurrentRow)
3658 NotifyReclick(master, this, newCurrentRow, mods);
3662 // If the user clicked exactly on the edited field,
3664 if(editData && editData.visible && newCurrentRow)
3670 if(style.collapse && !(style.treeBranch))
3675 for(parent = newCurrentRow.parent; parent; parent = parent.parent)
3677 indent += (style.treeBranch) ? 20 : 15;
3681 for(field = fields.first; field; field = field.next)
3683 int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ?
3684 clientSize.w - field.x : (field.width + EXTRA_SPACE);
3685 if(!field.prev) width -= indent;
3686 if(x >= sx && x < sx + width && newCurrentRow)
3691 if(field) //x >= sx && x < sx + width && newCurrentRow)
3693 if(field == currentField)
3694 editData.Activate();
3696 newCurrentRow.Edit(currentField);*/
3698 else if(style.noDragging && newCurrentRow)
3699 NotifyReclick(master, this, newCurrentRow, mods);
3701 else if(style.noDragging && newCurrentRow)
3702 NotifyReclick(master, this, newCurrentRow, mods);
3709 if(result && style.alwaysEdit && currentRow)
3713 DataField field = null;
3718 if(style.collapse && !style.treeBranch)
3721 for(parent = newCurrentRow.parent; parent; parent = parent.parent)
3723 indent += (style.treeBranch) ? 20 : 15;
3726 for(field = fields.first; field; field = field.next)
3728 int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ?
3729 clientSize.w - field.x : (field.width + EXTRA_SPACE);
3731 if(!field.prev) width -= indent;
3732 if(x >= sx && x < sx + width)
3734 f = currentField = field;
3741 // Moved NotifySelect after setting currentField for the NotifySelect implementation to be aware of which field is now selected (e.g. WatchesView)
3742 result = NotifySelect(master, this, currentRow, mods);
3743 if(result && style.alwaysEdit && currentRow)
3745 // In case the user specifically clicked on a field (f is set), override any change to currentField that NotifySelect could have done
3746 currentRow.Edit(f ? f : currentField);
3748 // If the user clicked exactly on the edited field,
3750 if(editData && editData.visible && newCurrentRow)
3754 editData.Activate();
3756 else if(style.noDragging && newCurrentRow)
3757 NotifyReclick(master, this, newCurrentRow, mods);
3760 else if(style.noDragging && newCurrentRow)
3761 NotifyReclick(master, this, newCurrentRow, mods);
3765 For drop box to capture...
3768 if(x < 0 || y < 0 || x >= clientSize.w || y >= clientSize.h)
3771 master.parent.Activate();
3780 if(!style.noDragging)
3782 this.dragging = true;
3785 if(x >= 0 && y >= 0 && x < clientSize.w && y < clientSize.h)
3786 this.rolledOver = true;
3791 this.dragging = false;
3792 OnLeftButtonUp(x, y, mods);
3797 bool OnLeftButtonDown(int x, int y, Modifiers mods)
3799 return OnButtonDown(x,y, mods, false);
3802 bool OnLeftButtonUp(int x, int y, Modifiers mods)
3804 if(resizingField && style.alwaysEdit)
3806 Window::FreeMouseRange();
3808 resizingField = null;
3811 if(dragRow || editRow)
3813 DataRow row, switchRow = rows.last;
3814 int rowY = (style.header) ? rowHeight : 0;
3815 for(row = firstRowShown; row; row = row.GetNextRow())
3824 if(this.editRow == switchRow && y >= 0)
3829 for(field = fields.first; field; field = field.next)
3831 int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ?
3832 clientSize.w - field.x : (field.width + EXTRA_SPACE);
3835 if(fieldX > x + scroll.x)
3839 if(field && field.editable)
3841 // true: destroy edit box
3842 HideEditBox(true, true, false);
3843 PopupEditBox(field, false);
3845 else if(!style.noDragging)
3846 NotifyReclick(master, this, currentRow, mods);
3848 else if(style.moveRows && switchRow)
3850 if(this.dragRow == switchRow && this.movedRow == false)
3852 DataRow row = this.dragRow;
3856 for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3857 selRow.selectedFlag = unselected;
3861 for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3863 if(selRow != clickedRow)
3865 if(selRow.selectedFlag == tempUnselected) selRow.selectedFlag = selected;
3866 else if(selRow.selectedFlag == tempSelected) selRow.selectedFlag = unselected;
3872 if(row.selectedFlag)
3873 row.selectedFlag = tempUnselected;
3874 else row.selectedFlag = tempSelected;
3877 row.selectedFlag = tempSelected;
3882 if(style.multiSelect)
3884 if(!switchRow.selectedFlag)
3886 bool foundSwitch = false;
3889 DataRow afterRow = switchRow.prev;
3890 for(row = rows.first; row; row = next)
3892 next = row.GetNextRow();
3893 if(row.selectedFlag == selected || row.selectedFlag == tempSelected)
3895 if(!foundSwitch && !after)
3898 afterRow = switchRow;
3900 if(!after || !(row.selectedFlag == selected || row.selectedFlag == tempSelected) ||
3907 else if(row == switchRow)
3914 for(row = rows.first; row; row = row.GetNextRow())
3916 if(row == switchRow || row == this.dragRow)
3920 // Switch row first: move before
3921 if(row == switchRow)
3923 if(NotifyMove(master, this, switchRow.prev, mods))
3924 dragRow.Move(switchRow.prev);
3926 // Dragged row first: move after
3929 if(NotifyMove(master, this, switchRow, mods))
3930 dragRow.Move(switchRow);
3943 if(this.dragging || style.freeSelect)
3947 this.rolledOver = this.dragging = false;
3949 if(x >= 0 && y >= 0 && x < clientSize.w && y < clientSize.h && currentRow && style.freeSelect)
3953 result = NotifySelect(master, this, currentRow, mods);
3954 if(style.alwaysEdit)
3955 currentRow.Edit(currentField);
3959 // if(!(style.freeSelect))
3965 bool OnLeftDoubleClick(int x, int y, Modifiers mods)
3967 int rowStart = -scroll.x;
3969 int rowY = (style.header) ? rowHeight : 0;
3972 OnLeftButtonUp(x,y,mods);
3973 if(style.alwaysEdit)
3975 if(!(style.collapse) || x > 15)
3976 if(editData && editData.visible)
3978 editData.Activate();
3979 NotifyDoubleClick(master, this, x, y, mods);
3983 for(row = firstRowShown; row; row = row.GetNextRow())
3986 if(rowY > y || (style.multiSelect && !row.GetNextRow()))
3988 if(style.treeBranch)
3991 for(parent = (style.rootCollapse) ? row.parent : (row.parent ? row.parent.parent : null); parent; parent = parent.parent)
4000 if((row && style.collapse && (x >= rowStart && x <= rowStart + 18 + plusIndent)) ||
4001 NotifyDoubleClick(master, this, x, y, mods))
4005 if(row && row.subRows.first)
4007 row.collapsed = !row.collapsed;
4011 // We need to return true here so that OnLeftButtonDown can popup the DataBox Editors
4017 bool OnRightButtonDown(int x, int y, Modifiers mods)
4019 return OnButtonDown(x,y, mods, true);
4022 bool OnRightButtonUp(int x, int y, Modifiers mods)
4024 OnLeftButtonUp(x,y,mods);
4025 return NotifyRightClick(master, this, x, y, mods);
4028 bool GoToLetter(unichar ch, bool keyHit)
4030 bool result = false;
4032 bool checkNextField = true;
4033 int len = keyHit ? 0 : strlen(typedString);
4035 typedString = renew typedString char[len + 2];
4036 typedString[len++] = (char)tolower(ch); // TODO: FIX UNICODE
4037 typedString[len] = '\0';
4039 for(field = fields.first; field; field = field.next)
4041 DataRow startRow = currentRow ? currentRow : rows.first;
4043 if(startRow && field.dataType && field.dataType._vTbl[__ecereVMethodID_class_OnGetString] && ch)
4046 bool looped = false;
4047 if(len == 1 && currentRow)
4048 startRow = (next = startRow.GetNextRow(), (next ? next : rows.first));
4050 for(row = startRow; row != startRow || !looped; next = row.GetNextRow(), row = next ? next : rows.first)
4052 void * data = row.GetData(field);
4053 char tempString[1024] = "";
4054 bool needClass = false;
4055 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;
4057 if(string && string[0])
4058 checkNextField = false;
4059 if(string && string[0] && !strnicmp(string, typedString, len))
4061 if(style.multiSelect)
4066 for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
4067 selRow.selectedFlag = unselected;
4068 row.selectedFlag = selected;
4070 SetCurrentRow(row, true);
4077 if(this.typingTimeOut && !keyHit)
4078 typingTimer.Start();
4079 if(!result || !this.typingTimeOut || keyHit)
4080 typedString[len-1] = '\0';
4082 if(!checkNextField || result) break;
4087 bool OnKeyDown(Key key, unichar ch)
4091 if(key == enter || key == keyPadEnter)
4093 if(editData && editData.visible && editData.active)
4095 HideEditBox(true, false, false);
4099 else if(key == escape)
4101 if(resizingField || this.movingFields || (editData && editData.visible) || this.dragRow)
4103 if(editData && editData.visible && style.alwaysEdit && !editData.active)
4105 // false: dont destroy edit box
4106 HideEditBox(false, false, false);
4109 resizingField.width = this.startWidth;
4110 AdaptToFieldWidth(resizingField, true);
4111 resizingField = null;
4114 this.dragRow = null;
4117 this.dragging = false;
4121 this.movingFields = false;
4122 draggingField = null;
4128 if(!currentField || !currentField.editable)
4129 for(field = fields.first; field; field = field.next)
4133 currentField = field;
4137 if((key == f2 || (style.alwaysEdit && (key == ctrlV || key == ctrlC || key == ctrlX || key == shiftInsert || key == ctrlInsert || key == shiftDel))) &&
4138 currentField && currentField.editable)
4140 PopupEditBox(currentField, false);
4141 if(editData && editData.visible)
4143 if(style.alwaysEdit)
4145 editData.Activate();
4146 if(key == ctrlV || key == ctrlC || key == ctrlX || key == shiftInsert || key == ctrlInsert || key == shiftDel)
4148 editData.OnKeyHit(key, ch);
4152 // For Installer to pop up file dialog
4153 NotifyKeyDown(master, this, currentRow, key, ch);
4159 if(!NotifyKeyDown(master, this, currentRow, key, ch))
4162 // Editable fields...
4165 if(style.alwaysEdit && editData && editData.visible)
4166 return editData.OnKeyDown(key, ch);
4167 return true; // We want to pick up the OnKeyHit to replace contents, but skip GoToLetter
4170 if(ch >=32 && ch != 128 && !key.alt && !key.ctrl && GoToLetter(ch, false))
4172 /*if(inactive && window.state != Hidden)
4173 NotifyHighlight(master, this, currentRow, 0);
4176 NotifySelect(master, this, currentRow, 0);
4183 bool OnKeyHit(Key key, unichar ch)
4185 if(!ch && !key.alt && !key.ctrl)
4187 key.code = (SmartKey)key.code;
4189 if(ch >= 32 && !key.alt && !key.ctrl && ch != 128)
4192 if(!currentField || !currentField.editable)
4193 for(field = fields.first; field; field = field.next)
4197 currentField = field;
4201 if(currentField && currentField.editable)
4203 if((!editData || !editData.visible) || !editData.active)
4205 PopupEditBox(currentField, false);
4206 if(editData && editData.visible)
4208 editData.Activate();
4209 editData.OnKeyHit(key, ch);
4216 if(editData && editData.visible && ch && !key.alt && !key.ctrl && editData.active && (key.code != tab || (editData._class == class(EditBox) && ((EditBox)editData).tabKey)))
4219 if(!key.alt && (style.multiSelect || !key.ctrl))
4224 if(style.alwaysEdit)
4229 for(field = currentField.prev; field; field = field.prev)
4233 currentField = field;
4234 HideEditBox(true, true, false);
4235 PopupEditBox(currentField, false);
4241 if(style.collapse && currentRow /*&& !currentField*/) // THIS PREVENTED COLLAPSING THE PROPERTY SHEET
4243 if(currentRow.subRows.first && !currentRow.collapsed)
4245 currentRow.collapsed = true;
4247 else if(currentRow.parent)
4248 SetCurrentRow(currentRow.parent, true);
4253 if(style.collapse && currentRow && currentRow.subRows.first)
4255 if(currentRow.collapsed)
4256 currentRow.collapsed = false;
4258 SetCurrentRow(currentRow.subRows.first, true);
4261 else if(style.alwaysEdit)
4266 for(field = currentField.next; field; field = field.next)
4270 currentField = field;
4271 HideEditBox(true, true, false);
4272 PopupEditBox(currentField, false);
4280 case pageDown: case pageUp:
4281 case end: case home:
4283 int headerSize = ((style.header) ? rowHeight : 0);
4284 int height = clientSize.h + 1 - headerSize;
4287 // true: destroy edit box
4288 // !!! TESTING true HERE !!!
4289 HideEditBox(true, true, false);
4290 // HideEditBox(false, true, false);
4292 oldRow = currentRow;
4294 SNAPDOWN(height, rowHeight);
4295 if((!currentRow || key.code == home) && key.code != end)
4297 currentRow = rows.first;
4305 if(!(style.multiSelect) && currentRow && !currentRow.selectedFlag)
4308 next = currentRow.GetNextRow();
4315 if(!(style.multiSelect) && currentRow && !currentRow.selectedFlag)
4318 next = currentRow.GetPrevRow();
4326 currentRow = lastRow.GetLastRow();
4332 currentRow && (next = currentRow.GetPrevRow()) && c < height / rowHeight;
4333 c++, currentRow = next);
4340 currentRow && (next = currentRow.GetNextRow()) && c < height / rowHeight;
4341 c++, currentRow = next);
4346 if(currentRow && currentRow.index * rowHeight > scroll.y + height - rowHeight)
4347 SetScrollPosition(scroll.x, currentRow.index * rowHeight - height + rowHeight);
4348 else if(!currentRow || currentRow.index * rowHeight < scroll.y)
4349 SetScrollPosition(scroll.x, currentRow ? currentRow.index * rowHeight : 0);
4351 if(style.multiSelect)
4355 if(!(key.shift) && (key.ctrl))
4358 for(row = rows.first; row; row = row.GetNextRow())
4360 if(row.selectedFlag == tempSelected)
4361 row.selectedFlag = selected;
4362 else if(row.selectedFlag == tempUnselected)
4363 row.selectedFlag = unselected;
4369 for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
4370 selRow.selectedFlag = unselected;
4374 for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
4376 if(selRow.selectedFlag == tempUnselected) selRow.selectedFlag = selected;
4377 else if(selRow.selectedFlag == tempSelected) selRow.selectedFlag = unselected;
4383 if(currentRow.index >= clickedRow.index)
4385 for(selRow = clickedRow; selRow; selRow = selRow.GetNextRow())
4389 if(selRow.selectedFlag) selRow.selectedFlag = tempUnselected; else selRow.selectedFlag = tempSelected;
4392 selRow.selectedFlag = selected;
4393 if(selRow == currentRow)
4399 for(selRow = currentRow; selRow; selRow = selRow.GetNextRow())
4403 if(selRow.selectedFlag) selRow.selectedFlag = tempUnselected; else selRow.selectedFlag = tempSelected;
4406 selRow.selectedFlag = selected;
4407 if(selRow == clickedRow)
4414 if(!(key.ctrl) && currentRow)
4416 currentRow.selectedFlag = selected;
4419 clickedRow = currentRow;
4424 if(oldRow) oldRow.selectedFlag = unselected;
4425 if(currentRow) currentRow.selectedFlag = selected;
4430 if(style.freeSelect)
4431 NotifyHighlight(master, this, currentRow, 0);
4433 NotifySelect(master, this, currentRow, 0);
4435 if(style.alwaysEdit && currentRow)
4436 currentRow.Edit(currentField /*null*/);
4443 if(style.multiSelect && currentRow)
4445 if(currentRow.selectedFlag)
4448 currentRow.selectedFlag = unselected;
4451 currentRow.selectedFlag = selected;
4454 if(style.freeSelect)
4455 NotifyHighlight(master, this, currentRow, 0);
4457 NotifySelect(master, this, currentRow, 0);
4464 if(!NotifyKeyHit(master, this, currentRow, key, ch))
4467 if(ch >=32 && ch != 128 && !key.alt && !key.ctrl && GoToLetter(ch, true))
4469 /*if(inactive && window.state != Hidden)
4470 return NotifyHighlight(master, this, currentRow, 0);
4473 return NotifySelect(master, this, currentRow, 0);
4480 void OnHScroll(ScrollBarAction action, int position, Key key)
4485 void OnVScroll(ScrollBarAction action, int position, Key key)
4490 for(firstRowShown = rows.first; firstRowShown; firstRowShown = next)
4492 next = firstRowShown.GetNextRow();
4493 if(y >= position || !next) break;
4503 DataRow firstRowShown;
4507 DataField sortField;
4511 public double typingTimeOut;
4524 if(guiApp.GetKeyState(shift)) mods.shift = true;
4525 if(guiApp.GetKeyState(alt)) mods.alt = true;
4526 if(guiApp.GetKeyState(control)) mods.ctrl = true;
4527 OnMouseMove(MAXINT, MAXINT, mods);
4535 delay = 0.5; // typingTimeOut
4540 typedString[0] = '\0';
4542 // The next line was commented... Why? When commented typing words stops working ( only first character jumps )
4548 bool dragging, rolledOver;
4557 // For editing fields
4559 DataField currentField;
4562 // For moving fields
4563 DataField draggingField, dropField;
4566 // For resizing fields
4567 DataField resizingField;
4568 int resizeX, oldX, startWidth;
4571 FontResource boldFont;
4574 // Only used for OnMouseMove so far, for avoiding problems with consequential mouse moves
4575 bool insideNotifySelect;
4576 ColorAlpha selectionColor, selectionText, stippleColor;
4577 stippleColor = 0xFFFFFF80;