ecere/src/com; gfx; gui: Temporary patches for alignment issues
[sdk] / ecere / src / gui / controls / ListBox.ec
1 namespace gui::controls;
2
3 import "Window"
4
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;
9
10 #define SNAPDOWN(x, d) \
11       if(Abs(x) % (d)) \
12       { \
13          if(x < 0) x -= ((d) - Abs(x) % (d)); else x -= x % (d); \
14       }
15
16 #include <stdarg.h>
17
18 static class ListBoxCell : struct
19 {
20    ListBoxCell prev, next;
21    bool isSet;
22    // data follows
23    void * data[1];
24 };
25
26 #define RESIZE_BORDER   5
27 #define PLUSY  4
28 #define EXTRA_SPACE 8
29
30 class ListBoxBits
31 {
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;
35 };
36
37 public class DataDisplayFlags
38 {
39    public bool selected:1, fullRow:1, current:1, active:1, dropBox:1, header:1, firstField:1;
40 };
41
42 enum SelectedFlag { unselected, selected, tempSelected, tempUnselected };
43
44 default:
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;
52
53 private:
54
55 public class DataField
56 {
57 public:
58    property Class dataType
59    {
60       set { dataType = value; if(value) { alignment = (Alignment)value.defaultAlignment; } }  // NOTE: Class::defaultAlignment does not seem to be used anywhere
61       get { return dataType; }
62    }
63    property bool editable { set { editable = value; } };
64    property bool fixed { set { fixed = value; } get { return fixed; } };
65    property Alignment alignment
66    {
67       set
68       {
69          alignment = value;
70          if(headButton) headButton.alignment = value;
71          if(listBox) listBox.Update(null);
72       }
73       get { return alignment; }
74    };
75    property int width
76    {
77       set
78       {
79          width = Max(value, -EXTRA_SPACE);
80          if(listBox)
81             listBox.AdaptToFieldWidth(this, false);
82       }
83       get { return width; }
84    };
85    property int index { get { return this ? index : 0; } };
86    property int position
87    {
88       set
89       {
90          if(listBox)
91          {
92             int index = 0;
93             DataField field;
94             for(field = listBox.fields.first; field; field = field.next)
95             {
96                if(index == value - 2)
97                   break;
98                index++;
99             }
100             Move(field);
101          }
102       }
103       get
104       {
105          if(listBox)
106          {
107             int index = 0;
108             DataField field;
109             for(field = listBox.fields.first; field; field = field.next)
110             {
111                if(this == field)
112                {
113                   return index + 1;
114                }
115                index++;
116             }
117          }
118          return -1;
119       }
120    };
121    property int sortOrder { get { return this ? sortOrder : 0; } };
122    property const char * header
123    {
124       set
125       {
126          header = value;
127          if(headButton)
128             headButton.text = header;
129       }
130    };
131    property void * userData
132    {
133       set
134       {
135          userData = value;
136       }
137       get
138       {
139          return this ? userData : null;
140       }
141    };
142    property bool freeData
143    {
144       set { freeData = value; } get { return freeData; }
145    };
146    property DataField prev { get { return prev; } };
147    property DataField next { get { return next; } };
148
149    void Move(DataField after)
150    {
151       if(prev != after)
152       {
153          listBox.fields.Move(this, after);
154
155          // Fix up positions
156          listBox.OnResize(listBox.clientSize.w, listBox.clientSize.h);
157          listBox.Update(null);
158       }
159    }
160
161    void AutoSize()
162    {
163       if(listBox && dataType)
164       {
165          Display display = listBox.display;
166          Font boldFont = listBox.boldFont.font;
167          Font font = listBox.fontObject;
168          DataRow row;
169          int width = 0;
170          if(header)
171             display.FontExtent(boldFont, header, strlen(header), &width, null);
172          width += EXTRA_SPACE;
173          for(row = listBox.firstRow; row; row = row.GetNextRow())
174          {
175             ListBoxCell cell;
176             uint i;
177             for(i = 0, cell = row.cells.first; i != index; i++, cell = cell.next);
178             if(cell && cell.isSet && dataType)
179             {
180                static char tempString[4096];
181                const String string;
182                int tw = 0;
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);
185                else
186                   string = ((const char *(*)(void *, void *, char *, void *, bool *))(void *)dataType._vTbl[__ecereVMethodID_class_OnGetString])(dataType, cell.data, tempString, userData, null);
187                if(string)
188                   display.FontExtent(row.header ? boldFont : font, string, strlen(string), &tw, null);
189                else
190                   display.FontExtent(row.header ? boldFont : font, "", 0, &tw, null);
191                if(tw > width) width = tw;
192             }
193          }
194          if(width)
195             property::width = width;
196       }
197    }
198
199 private:
200    DataField()
201    {
202       // property::dataType = "String";
203       property::dataType = "char *";
204       // width = 80;
205       freeData = true;
206    }
207
208    ~DataField()
209    {
210       Free();
211    }
212
213    void Free()
214    {
215       headButton.Destroy(0);
216       delete headButton;
217       if(listBox)
218       {
219          DataField field;
220          listBox.fields.Remove(this);
221          for(field = listBox.fields.first; field; field = field.next)
222          {
223             if(field.index >= index)
224                field.index--;
225          }
226          if(listBox.currentField == this)
227             listBox.currentField = null;
228          listBox.numFields--;
229          listBox.OnResize(listBox.clientSize.w, listBox.clientSize.h);
230          listBox = null;
231       }
232    }
233
234    DataField prev, next;
235    const char * header;
236    Class dataType;
237    int width;
238    uint index;
239    int x;
240    Button headButton;
241    int sortOrder;
242    Alignment alignment;
243    bool editable;
244    ListBox listBox;
245    bool defaultField;
246    void * userData;
247    bool freeData;
248    bool fixed;
249 };
250
251 public class DataRow
252 {
253    class_no_expansion
254 #ifdef _DEBUG
255    bool skipCheck;
256 #endif
257 public:
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
263    {
264       set { SetData(listBox.fields.first, value); }
265       get { return GetData(listBox.fields.first); }
266    };
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
270    {
271       set
272       {
273          if(this && collapsed != value)
274          {
275             collapsed = value;
276             if(parent.IsExpanded())
277             {
278                DataRow search;
279                int index;
280
281                if(value)
282                {
283                   for(search = subRows.first; search; search = search.next)
284                      search.selectedFlag = 0;
285
286                   if(listBox.clickedRow && !listBox.clickedRow.parent.IsExpanded())
287                   {
288                      listBox.clickedRow = GetNextRow();
289                      if(!listBox.clickedRow)
290                         listBox.clickedRow = this;
291                   }
292                   if(listBox.currentRow && !listBox.currentRow.parent.IsExpanded())
293                   {
294                      listBox.SetCurrentRow(this, true);
295                   }
296                   if(listBox.firstRowShown && !listBox.firstRowShown.parent.IsExpanded())
297                   {
298                      listBox.firstRowShown = GetPrevRow();
299                      if(!listBox.firstRowShown)
300                         listBox.firstRowShown = this;
301                   }
302                }
303
304                index = this.index+1;
305                for(search = GetNextRow(); search; search = search.GetNextRow())
306                   search.index = index++;
307                listBox.rowCount = index;
308
309                listBox.HideEditBox(false, false, true);
310
311                listBox.SetScrollArea(
312                   listBox.width,
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);
318             }
319          }
320 #ifdef _DEBUG
321          if(this && !skipCheck)
322             listBox.CheckConsistency();
323 #endif
324       }
325       get { return this ? collapsed : false; }
326    };
327    property bool selected
328    {
329       set
330       {
331          if(this)
332          {
333             selectedFlag = value ? selected : unselected;
334             listBox.Update(null);
335          }
336       }
337       get { return selectedFlag == selected || selectedFlag == tempSelected; }
338    };
339    property DataRow parent
340    {
341       set
342       {
343          if(this && (value != this))
344          {
345             DataRow search;
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())
349             {
350                for(search = GetNextRow(); search; search = search.GetNextRow())
351                   search.index -= ixCount;
352                listBox.rowCount -= ixCount;
353             }
354
355             listBox.HideEditBox(false, false, true);
356
357             /*
358             if(this == listBox.clickedRow)
359             {
360                listBox.clickedRow = GetNextRow();
361                if(!listBox.clickedRow)
362                   listBox.clickedRow = GetPrevRow();
363             }
364
365             if(this == listBox.currentRow)
366             {
367                DataRow newCurrentRow = GetNextRow();
368                if(!listBox.newCurrentRow)
369                   listBox.newCurrentRow = GetPrevRow();
370                listBox.SetCurrentRow(newCurrentRow, true);
371             }
372
373             if(this == listBox.firstRowShown)
374             {
375                listBox.firstRowShown = GetPrevRow();
376                if(!listBox.firstRowShown)
377                   listBox.firstRowShown = GetNextRow();
378             }
379             */
380
381             (parent ? parent.subRows : listBox.rows).Remove(this);
382
383             if(value)
384                value.subRows.Insert(after, this);
385             else
386                listBox.rows.Insert(after, this);
387
388             parent = value;
389
390             if(value && listBox.style.expandOnAdd)
391             {
392 #ifdef _DEBUG
393                value.skipCheck = true;
394 #endif
395                value.collapsed = false;
396 #ifdef _DEBUG
397                value.skipCheck = false;
398 #endif
399             }
400
401             if(value.IsExpanded())
402             {
403                DataRow search;
404
405                if(after && after.subRows.first && !after.collapsed)
406                {
407                   search = after.GetLastRow();
408                   index = search.index + 1;
409                }
410                else
411                   index = after ? (after.index + 1) : (index + 1);
412
413                listBox.rowCount += ixCount;
414
415                {
416                   int ix = index+1;
417                   for(search = GetNextRow(); search; search = search.GetNextRow())
418                      search.index = ix++;
419                }
420
421                listBox.SetScrollArea(
422                   listBox.width,
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);
428             }
429             // TESTING THIS HERE...
430             if(listBox.created)
431                listBox.Sort(listBox.sortField, listBox.sortField.sortOrder);
432
433             {
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)
439                {
440                   int line = listBox.currentRow ? listBox.currentRow.index * listBox.rowHeight : 0;
441                   listBox.SetScrollPosition(listBox.scroll.x, line);
442                }
443                else
444                   listBox.OnVScroll(0, listBox.scroll.y, 0);
445             }
446             listBox.Update(null);
447          }
448       }
449       get
450       {
451          return parent;
452       }
453    };
454    property DataRow lastRow { get { return this ? subRows.last : null; } };
455    property DataRow firstRow { get { return this ? subRows.first : null; } };
456
457    void Edit(DataField field)
458    {
459       if(this)
460       {
461          if(listBox)
462          {
463             if(!field || !field.editable)
464             {
465                for(field = listBox.fields.first; field; field = field.next)
466                   if(field.editable) break;
467             }
468             if(field && field.editable)
469             {
470                listBox.SetCurrentRow(this, true);
471                listBox.PopupEditBox(field, false);
472             }
473          }
474       }
475    }
476
477    // NOTE: This does not support reparenting (Use row.parent = first)
478    void Move(DataRow after)
479    {
480       if(this)
481       {
482          incref this;
483          if(listBox && prev != after)
484          {
485             DataRow search;
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;
489
490             if(!after || after.index < index)
491             {
492                if(after == listBox.firstRowShown.prev)
493                   listBox.firstRowShown = this;
494
495                // All rows between AFTER (exclusive) and ROW (exclusive) are incremented by one
496                // ROW is equal to AFTER's index + 1
497
498                for(search = after ? after.next : listBox.rows.first; search && search != this; search = search.GetNextRow())
499                   search.index += ixCount;
500
501                if(after && after.subRows.first && !after.collapsed)
502                {
503                   search = after.GetLastRow();
504                   index = search.index + 1;
505                }
506                else
507                   index = after ? (after.index + 1) : 0;
508
509                // Fix indices of sub rows
510                if(!collapsed)
511                {
512                   int c, ix = index+1;
513                   for(c = 1, search = GetNextRow(); search && c < ixCount; c++, search = search.GetNextRow())
514                      search.index = ix++;
515                }
516             }
517             else
518             {
519                DataRow nextRow = GetNextRow();
520                if(this == listBox.firstRowShown)
521                   listBox.firstRowShown = nextRow;
522                index = after.index;
523
524                // All rows between ROW (exclusive) and AFTER (inclusive) are decremented by one
525                // ROW is equal to AFTER's index
526
527                for(search = nextRow; search; search = search.GetNextRow())
528                {
529                   search.index -= ixCount;
530                   if(search == after) break;
531                }
532             }
533             listBox.rows.Move(this, after);
534
535 #ifdef _DEBUG
536             listBox.CheckConsistency();
537 #endif
538
539             listBox.HideEditBox(true, false, true);
540             if(listBox)
541             {
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)
545                {
546                   int line = listBox.currentRow ? listBox.currentRow.index * listBox.rowHeight : 0;
547                   //SNAPUP(line, listBox.rowHeight);
548                   listBox.SetScrollPosition(listBox.scroll.x, line);
549                }
550                else
551                   listBox.OnVScroll(0, listBox.scroll.y, 0);
552
553                listBox.modifiedDocument = true;
554
555                listBox.Update(null);
556             }
557          }
558          delete this;
559       }
560    }
561
562    any_object GetData(DataField field)
563    {
564       if(this)
565       {
566          ListBoxCell cell = listBox.GetCell(&this, &field);
567          if(cell && cell.isSet && cell.data)
568          {
569             if((field.dataType.type == normalClass || field.dataType.type == noHeadClass))
570                return cell.data[0];
571             else
572                return cell.data;
573          }
574       }
575       return null;
576    }
577
578    void * SetData(DataField field, any_object newData)
579    {
580       if(this)
581       {
582          ListBoxCell cell = listBox.GetCell(&this, &field);
583          if(cell)
584          {
585             Class dataType = field.dataType;
586
587             if(dataType)
588             {
589                if(dataType.type == normalClass || dataType.type == noHeadClass)
590                {
591                   if(cell.data[0] && field.freeData)
592                      ((void (*)(void *, void *))(void *)dataType._vTbl[__ecereVMethodID_class_OnFree])(dataType, cell.data[0]);
593
594                   if(eClass_IsDerived(dataType, class(char *)) && field.freeData)
595                      ((void (*)(void *, void *, void *))(void *)dataType._vTbl[__ecereVMethodID_class_OnCopy])(dataType, cell.data, newData);
596                   else
597                      cell.data[0] = (void *)newData;
598                }
599                else
600                {
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);
604                }
605             }
606             cell.isSet = true;
607             listBox.modifiedDocument = true;
608             listBox.Update(null);
609             if(dataType && (dataType.type == normalClass || dataType.type == noHeadClass))
610                return cell.data;
611             else
612                return &cell.data;
613          }
614       }
615       return null;
616    }
617
618    void UnsetData(DataField field)
619    {
620       if(this)
621       {
622          ListBoxCell cell = listBox.GetCell(&this, &field);
623          if(cell)
624          {
625             Class dataType = field.dataType;
626
627             if(dataType)
628             {
629                if(dataType.type == normalClass || dataType.type == noHeadClass)
630                {
631                   if(cell.data[0] && field.freeData)
632                      ((void (*)(void *, void *))(void *)dataType._vTbl[__ecereVMethodID_class_OnFree])(dataType, cell.data[0]);
633                   cell.data[0] = null;
634                }
635                else
636                {
637                   // Free old data first
638                   ((void (*)(void *, void *))(void *)dataType._vTbl[__ecereVMethodID_class_OnFree])(dataType, cell.data);
639                }
640             }
641             cell.isSet = false;
642             listBox.Update(null);
643          }
644       }
645    }
646
647    DataRow FindRow(int64 tag)
648    {
649       DataRow row = null;
650       for(row = subRows.first; row; row = row.next)
651       {
652          if(!row.noneRow && row.tag == tag)
653             break;
654       }
655       return row;
656    }
657
658    DataRow FindSubRow(int64 tag)
659    {
660       DataRow row = null;
661       for(row = subRows.first; row; row = row.next)
662       {
663          if(!row.noneRow && row.tag == tag)
664             break;
665          if(row.subRows.first)
666          {
667             DataRow subRow = row.FindSubRow(tag);
668             if(subRow)
669                return subRow;
670          }
671       }
672       return row;
673    }
674
675    DataRow AddRowAfter(DataRow after)
676    {
677       if(this)
678       {
679          DataRow row { };
680          incref row;
681          if(row)
682          {
683             DataField field;
684             int c;
685             subRows.Insert(after, row);
686             row.listBox = listBox;
687             *&row.parent = this;
688
689             row.cells.Clear();
690             for(c = 0; c<listBox.fields.count; c++)
691             {
692                for(field = listBox.fields.first; field; field = field.next)
693                   if((int)field.index == c)
694                      break;
695                if(field)
696                {
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];
700                   row.cells.Add(cell);
701                   FillBytes(cell.data, 0, size - (uint)(uintptr)&((ListBoxCell)0).data);
702                   cell.isSet = false;
703                }
704             }
705
706             if(IsExpanded())
707             {
708                DataRow search;
709
710                if(after && after.subRows.first && !after.collapsed)
711                {
712                   for(search = after.subRows.last; !search.collapsed && search.subRows.last; )
713                      search = search.subRows.last;
714                   row.index = search.index + 1;
715                }
716                else
717                   row.index = after ? (after.index + 1) : (index + 1);
718
719                listBox.rowCount++;
720
721                for(search = row.GetNextRow(); search; search = search.GetNextRow())
722                   search.index++;
723
724                listBox.SetScrollArea(
725                   listBox.width,
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);
731             }
732
733             listBox.modifiedDocument = true;
734          }
735 #ifdef _DEBUG
736          listBox.CheckConsistency();
737 #endif
738          return row;
739       }
740       return null;
741    }
742
743    DataRow AddRow()
744    {
745       if(this)
746          return AddRowAfter(subRows.last);
747       return null;
748    }
749
750    DataRow AddStringf(const char * format, ...)
751    {
752       if(this)
753       {
754          DataRow row;
755          char string[MAX_F_STRING];
756          va_list args;
757          va_start(args, format);
758          vsnprintf(string, sizeof(string), format, args);
759          string[sizeof(string)-1] = 0;
760          va_end(args);
761
762          row = AddRow();
763          row.SetData(null, string);
764          return row;
765       }
766       return null;
767    }
768
769    DataRow AddString(const char * string)
770    {
771       if(this)
772       {
773          DataRow row;
774          row = AddRow();
775          row.SetData(listBox.fields.first, string);
776          return row;
777       }
778       return null;
779    }
780
781
782 private:
783    DataRow()
784    {
785       subRows.offset = (uint)(uintptr)&((DataRow)0).prev;
786    }
787
788    ~DataRow()
789    {
790       ListBoxCell cell, next;
791       uint cellIndex = 0;
792       DataRow subRow;
793
794       // Delete SubRows
795       while((subRow = subRows.first))
796       {
797          subRows.Remove(subRow);
798          delete subRow;
799       }
800
801       for(cell = cells.first; cell; cell = next, cellIndex++)
802       {
803          if(listBox)
804          {
805             DataField field;
806             for(field = listBox.fields.first; field && field.index != cellIndex; field = field.next);
807             if(field && field.dataType)
808             {
809                // TOCHECK: Is this check good? Will need incref/decref sometime?
810                if(field.dataType.type == normalClass || field.dataType.type == noHeadClass)
811                {
812                   if(cell.data[0] && field.freeData)
813                      ((void (*)(void *, void *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnFree])(field.dataType, cell.data[0]);
814                }
815                else
816                   ((void (*)(void *, void *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnFree])(field.dataType, cell.data);
817             }
818          }
819          next = cell.next;
820          delete cell;
821       }
822    }
823
824    bool IsExpanded()
825    {
826       return !this || (!collapsed && (!parent || parent.IsExpanded()));
827    }
828
829    int Compare(DataRow b, DataField sortField)
830    {
831       int result = 0;
832       ListBoxCell cell1, cell2;
833       uint index;
834       for(index = 0, cell1 = cells.first, cell2 = b.cells.first;
835           index != sortField.index;
836           index++, cell1 = cell1.next, cell2 = cell2.next);
837    /*
838       if(!cell1.isSet && !cell2.isSet)
839          result = 0;
840       else if(!cell1.isSet)
841          result = -1;
842       else if(!cell2.isSet)
843          result = 1;
844       else */
845       if(noneRow && !b.noneRow) return -1;
846       else if(!noneRow && b.noneRow) return 1;
847       else if(noneRow && b.noneRow) return 0;
848
849       if(sortField.dataType._vTbl[__ecereVMethodID_class_OnCompare])
850       {
851          if(sortField.dataType.type == normalClass || sortField.dataType.type == noHeadClass)
852          {
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);
856          }
857          else
858          {
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);
862          }
863       }
864       return sortField.sortOrder * result;
865    }
866
867    void _SortSubRows(DataField field, int order)
868    {
869       DataRow search;
870       for(search = subRows.first; search; search = search.next)
871          search._SortSubRows(field, order);
872       subRows.Sort(Compare, field);
873    }
874
875    public void SortSubRows(bool scrollToCurrent)
876    {
877       if(this && listBox && listBox.sortField)
878       {
879          _SortSubRows(listBox.sortField, listBox.sortField.sortOrder);
880
881          {
882             DataRow search;
883             int index = this.index;
884             for(search = this; search; search = search.GetNextRow())
885                search.index = index++;
886          }
887          if(scrollToCurrent)
888          {
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);
896          }
897       }
898    }
899
900    public DataRow GetPrevRow()
901    {
902       DataRow row;
903       // Find Previous row
904       if(prev)
905       {
906          for(row = prev; row && !row.collapsed && row.subRows.last; row = row.subRows.last);
907       }
908       else
909          row = parent;
910       return row;
911    }
912
913    public DataRow GetNextRow()
914    {
915       DataRow row;
916       // Find Next row
917       if(subRows.first && !collapsed)
918          row = subRows.first;
919       else
920       {
921          for(row = this; row; row = row.parent)
922          {
923             if(row.next) { row = row.next; break; }
924          }
925       }
926       return row;
927    }
928
929    private DataRow GetLastRow()
930    {
931       DataRow row = this;
932       while(row && !row.collapsed && row.subRows.last)
933          row = row.subRows.last;
934       return row;
935    }
936
937    DataRow prev, next;
938    OldList cells;
939    int64 tag;
940    SelectedFlag selectedFlag;
941    ListBox listBox;
942    bool header;
943    OldList subRows;
944    DataRow parent;
945    bool collapsed;
946    BitmapResource icon;
947    int index;
948    bool noneRow;
949 };
950
951 public class ListBox : CommonControl
952 {
953    hasVertScroll = true;
954    // background = white;
955    borderStyle = deep;
956    snapVertScroll = true;
957
958    class_property(icon) = "<:ecere>controls/listBox.png";
959
960 public:
961    // Properties
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
965    {
966       get { return currentField; }
967       // TODO: Document what this does
968       set
969       {
970          currentField = value;
971          HideEditBox(true, true, false);
972          if(value && value.editable)
973             PopupEditBox(currentField, false);
974       }
975    };
976
977    property int rowHeight
978    {
979       property_category $"Appearance"
980       isset { return style.heightSet; }
981       set
982       {
983          if(value)
984          {
985             style.heightSet = true;
986             rowHeight = value;
987             SetScrollLineStep(8, value);
988          }
989          else
990          {
991             style.heightSet = false;
992             OnLoadGraphics();
993             OnApplyGraphics();
994          }
995       }
996       get { return this ? rowHeight : 0; }
997    };
998    property Seconds typingTimeout
999    {
1000       property_category $"Behavior"
1001       set
1002       {
1003          typedString[0] = '\0';
1004          typingTimer.delay = value;
1005          typingTimeOut = value;
1006       }
1007       get { return typingTimeOut; }
1008    };
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
1016    {
1017       property_category $"Appearance"
1018       set
1019       {
1020          if(value && !style.header)
1021          {
1022             endBevel = Button
1023             {
1024                this;
1025                stayOnTop = true;
1026                bevel = !guiApp.textMode && !style.clearHeader;
1027                dontScrollVert = true;
1028                inactive = true;
1029                size.h = rowHeight;
1030                NotifyPushed = HeaderPushed;
1031                NotifyClicked = HeaderClicked;
1032                NotifyDoubleClick = HeaderDoubleClicked;
1033                NotifyReleased = HeaderReleased;
1034                NotifyMouseMove = HeaderMouseMove;
1035             };
1036             incref endBevel;
1037             endBevel.Create();
1038             endBevel.visible = false;
1039          }
1040          style.header = value;
1041       }
1042       get { return style.header; }
1043    };
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
1053    {
1054       property_category $"Behavior"
1055       set
1056       {
1057          style.fillLastField = value;
1058       }
1059       get { return style.fillLastField; }
1060    };
1061    property int numSelections
1062    {
1063       get
1064       {
1065          int numSelections = 0;
1066          if(this && style.multiSelect)
1067          {
1068             DataRow row;
1069             for(row = rows.first; row; row = row.GetNextRow())
1070                if(row.selectedFlag == selected || row.selectedFlag == tempSelected)
1071                   numSelections++;
1072          }
1073          return numSelections;
1074       }
1075    };
1076    property int currentIndex
1077    {
1078       get { return currentRow ? currentRow.index : -1; }
1079    };
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; } };
1088
1089    // Notifications
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);
1107
1108 #ifdef _DEBUG
1109    private void CheckConsistency()
1110    {
1111 #if 0
1112       DataRow r;
1113       int index = 0;
1114       for(r = rows.first; r; r = r.GetNextRow())
1115       {
1116          if(r.index != index++)
1117             PrintLn("bug");
1118       }
1119 #endif
1120    }
1121 #endif
1122
1123    // Methods
1124    void AddField(DataField addedField)
1125    {
1126       if(this)
1127       {
1128          DataField field;
1129          if(fields.first && ((DataField)fields.first).defaultField)
1130          {
1131             DataField defaultField = fields.first;
1132             defaultField.Free();
1133             delete defaultField;
1134          }
1135          if(!addedField)
1136          {
1137             addedField = DataField { };
1138          }
1139
1140          incref addedField;
1141          addedField.listBox = this;
1142          fields.Add(addedField);
1143
1144          addedField.sortOrder = 1;
1145          addedField.index = numFields;
1146          numFields++;
1147          if(style.header)
1148          {
1149             addedField.headButton.Destroy(0);
1150             delete addedField.headButton;
1151             addedField.headButton = Button
1152             {
1153                this;
1154                stayOnTop = true;
1155                inactive = true;
1156                dontScrollVert = true;
1157                id = (int64)(intptr)addedField;
1158                text = addedField.header;
1159                bevel = (!guiApp.textMode && !style.clearHeader);
1160                ellipsis = true;
1161                alignment = addedField.alignment;
1162                NotifyPushed = HeaderPushed;
1163                NotifyClicked = HeaderClicked;
1164                NotifyDoubleClick = HeaderDoubleClicked;
1165                NotifyReleased = HeaderReleased;
1166                NotifyMouseMove = HeaderMouseMove;
1167             };
1168             incref addedField.headButton;
1169             addedField.headButton.Create();
1170
1171             if(guiApp.textMode)
1172                addedField.headButton.background = Color { 0, 170, 0 };
1173          }
1174          if(rows.first)
1175          {
1176             DataRow row;
1177             field = addedField;
1178             for(row = rows.first; row; )
1179             {
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);
1185                cell.isSet = false;
1186
1187                if(row.subRows.first)
1188                   row = row.subRows.first;
1189                else
1190                {
1191                   for(; row; row = row.parent)
1192                   {
1193                      if(row.next) { row = row.next; break; }
1194                   }
1195                }
1196             }
1197          }
1198          OnResize(clientSize.w, clientSize.h);
1199       }
1200    }
1201
1202    void ClearFields()
1203    {
1204       if(this)
1205       {
1206          DataField field;
1207          Clear();    // Ensure data is cleared first
1208          while((field = fields.first))
1209          {
1210             field.Free();
1211             delete field;
1212          }
1213          endBevel.visible = false;
1214          sortField = null;
1215       }
1216    }
1217
1218    void RemoveField(DataField field)
1219    {
1220       if(this)
1221       {
1222          if(field)
1223          {
1224             int index = field.index;
1225             DataRow row;
1226
1227             if(sortField == field)
1228                sortField = null;
1229
1230             for(row = rows.first; row; )
1231             {
1232                int c;
1233                ListBoxCell cell;
1234
1235                for(cell = row.cells.first, c = 0; c < index && cell; c++, cell = cell.next);
1236                if(cell && index == c)
1237                {
1238                   if(field.dataType)
1239                   {
1240                      // TOCHECK: Is this check good? Will need incref/decref sometime?
1241                      if(field.dataType.type == normalClass || field.dataType.type == noHeadClass)
1242                      {
1243                         if(cell.data[0] && field.freeData)
1244                            ((void (*)(void *, void *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnFree])(field.dataType, cell.data[0]);
1245                      }
1246                      else
1247                         ((void (*)(void *, void *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnFree])(field.dataType, cell.data);
1248                   }
1249
1250                   row.cells.Remove(cell);
1251                   delete cell;
1252                }
1253
1254                if(row.subRows.first)
1255                   row = row.subRows.first;
1256                else
1257                {
1258                   for(; row; row = row.parent)
1259                   {
1260                      if(row.next) { row = row.next; break; }
1261                   }
1262                }
1263             }
1264
1265             field.Free();
1266             delete field;
1267          }
1268          if(!fields.count)
1269             endBevel.visible = false;
1270       }
1271    }
1272
1273    DataRow AddRowNone()
1274    {
1275       DataRow row { noneRow = true };
1276       incref row;
1277       if(row)
1278       {
1279          DataRow search;
1280
1281          row.index = 0;
1282          rows.Insert(null, row);
1283          row.listBox = this;
1284
1285          for(search = row.GetNextRow(); search; search = search.GetNextRow())
1286             search.index++;
1287
1288          this.rowCount++;
1289          row.cells.Clear();
1290
1291          firstRowShown = row;
1292
1293          SetScrollArea(
1294             width,
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;
1301          return row;
1302       }
1303       return null;
1304    }
1305
1306    DataRow AddRow()
1307    {
1308       if(this)
1309       {
1310          if(fields.first)
1311          {
1312             DataRow row { };
1313             incref row;
1314             if(row)
1315             {
1316                DataField field;
1317                int c;
1318
1319                // Find very last row
1320                {
1321                   DataRow lastRow;
1322                   for(lastRow = rows.last; lastRow && !lastRow.collapsed && lastRow.subRows.last; lastRow = lastRow.subRows.last);
1323                   row.index = lastRow ? (lastRow.index + 1) : 0;
1324                }
1325
1326                rows.Add(row);
1327                row.listBox = this;
1328                rowCount++;
1329
1330                row.cells.Clear();
1331                for(c = 0; c<fields.count; c++)
1332                {
1333                   for(field = fields.first; field; field = field.next)
1334                      if((int)field.index == c)
1335                         break;
1336                   if(field)
1337                   {
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);
1343                      cell.isSet = false;
1344                   }
1345                }
1346
1347                if(!firstRowShown)
1348                {
1349                   firstRowShown = row;
1350                }
1351
1352                if(rowHeight)
1353                   SetScrollArea(
1354                      width,
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;
1361             }
1362 #ifdef _DEBUG
1363             CheckConsistency();
1364 #endif
1365             return row;
1366          }
1367       }
1368       return null;
1369    }
1370
1371    DataRow AddRowAfter(DataRow after)
1372    {
1373       if(fields.first)
1374       {
1375          DataRow row { };
1376          incref row;
1377          if(row)
1378          {
1379             DataRow search;
1380             DataField field;
1381             int c;
1382
1383             if(after && after.subRows.first && !after.collapsed)
1384             {
1385                for(search = after.subRows.last; !search.collapsed && search.subRows.last; )
1386                   search = search.subRows.last;
1387                row.index = search.index + 1;
1388             }
1389             else
1390                row.index = after ? (after.index + 1) : 0;
1391             rows.Insert(after, row);
1392             row.listBox = this;
1393
1394             for(search = row.GetNextRow(); search; search = search.GetNextRow())
1395                search.index++;
1396
1397             this.rowCount++;
1398             row.cells.Clear();
1399             for(c = 0; c<fields.count; c++)
1400             {
1401                for(field = fields.first; field; field = field.next)
1402                   if((int)field.index == c)
1403                      break;
1404                if(field)
1405                {
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);
1411                   cell.isSet = false;
1412                }
1413             }
1414             if(!firstRowShown || !after)
1415             {
1416                firstRowShown = row;
1417             }
1418
1419             SetScrollArea(
1420                width,
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;
1427 #ifdef _DEBUG
1428             CheckConsistency();
1429 #endif
1430             return row;
1431          }
1432       }
1433       return null;
1434    }
1435
1436    DataRow AddStringf(const char * format, ...)
1437    {
1438       if(this)
1439       {
1440          DataRow row;
1441
1442          char string[MAX_F_STRING];
1443          va_list args;
1444
1445          va_start(args, format);
1446          vsnprintf(string, sizeof(string), format ? format : "", args);
1447          string[sizeof(string)-1] = 0;
1448          va_end(args);
1449
1450          row = AddRow();
1451          row.SetData(fields.first, string);
1452          return row;
1453       }
1454       return null;
1455    }
1456
1457    DataRow AddString(const char * string)
1458    {
1459       if(this)
1460       {
1461          DataRow row;
1462          row = AddRow();
1463          row.SetData(fields.first, string);
1464          return row;
1465       }
1466       return null;
1467    }
1468
1469    void SelectRow(DataRow row)
1470    {
1471       SetCurrentRow(row, true);
1472    }
1473
1474    void DeleteRow(DataRow row)
1475    {
1476       if(!row) row = currentRow;
1477       if(row)
1478       {
1479          DataRow sub, next, search;
1480          // Trying to move this here (Messed up deleting watches)
1481          //HideEditBox(false, false, true);
1482
1483          // Delete Sub Rows
1484          for(sub = row.subRows.first; sub; sub = next)
1485          {
1486             next = sub.next;
1487             DeleteRow(sub);
1488          }
1489
1490          if(row.parent.IsExpanded())
1491          {
1492             for(search = row.GetNextRow(); search; search = search.GetNextRow())
1493                search.index--;
1494             this.rowCount--;
1495          }
1496
1497          HideEditBox(false, false, true);
1498
1499          if(row == clickedRow)
1500          {
1501             clickedRow = row.GetNextRow();
1502             if(!clickedRow)
1503                clickedRow = row.GetPrevRow();
1504          }
1505
1506          if(row == currentRow)
1507          {
1508             DataRow newCurrentRow = row.GetNextRow();
1509             if(!newCurrentRow)
1510                newCurrentRow = row.GetPrevRow();
1511             SetCurrentRow(newCurrentRow, true);
1512          }
1513
1514          if(row == firstRowShown)
1515          {
1516             firstRowShown = row.GetPrevRow();
1517             if(!firstRowShown)
1518                firstRowShown = row.GetNextRow();
1519          }
1520
1521          (row.parent ? row.parent.subRows: rows).Remove(row);
1522          delete row;
1523
1524          //HideEditBox(false, false, true);
1525
1526          SetScrollArea(
1527             this.width,
1528             (this.rowCount * rowHeight) +
1529             ((style.header) ? rowHeight : 0) -
1530             ((!((clientSize.h+1) % rowHeight)) ? rowHeight : 0), true);
1531
1532          modifiedDocument = true;
1533
1534          Update(null);
1535 #ifdef _DEBUG
1536          CheckConsistency();
1537 #endif
1538       }
1539    }
1540
1541    DataRow FindRow(int64 tag)
1542    {
1543       if(this)
1544       {
1545          DataRow row = null;
1546          for(row = rows.first; row; row = row.next)
1547          {
1548             if(!row.noneRow && row.tag == tag)
1549                break;
1550          }
1551          return row;
1552       }
1553       return null;
1554    }
1555
1556    DataRow FindString(const char * searchedString)
1557    {
1558       DataField field;
1559       bool checkNextField = true;
1560
1561       for(field = fields.first; field; field = field.next)
1562       {
1563          if(field.dataType._vTbl[__ecereVMethodID_class_OnGetString])
1564          {
1565             DataRow row;
1566             for(row = rows.first; row; row = row.GetNextRow())
1567             {
1568                if(!row.noneRow)
1569                {
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);
1574
1575                   if(string && string[0])
1576                      checkNextField = false;
1577                   if(string && string[0] && !strcmp(string, searchedString))
1578                      return row;
1579                }
1580             }
1581          }
1582          if(!checkNextField) break;
1583       }
1584       return null;
1585    }
1586
1587    DataRow FindSubString(const char * subString)
1588    {
1589       DataField field;
1590       bool checkNextField = true;
1591       int len = subString ? strlen(subString) : 0;
1592
1593       if(len)
1594       {
1595          for(field = fields.first; field; field = field.next)
1596          {
1597             if(field.dataType._vTbl[__ecereVMethodID_class_OnGetString])
1598             {
1599                DataRow row;
1600                for(row = rows.first; row; row = row.GetNextRow())
1601                {
1602                   if(!row.noneRow)
1603                   {
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);
1608
1609                      if(string && string[0])
1610                         checkNextField = false;
1611                      if(string && string[0] && !strncmp(string, subString, len))
1612                         return row;
1613                   }
1614                }
1615             }
1616             if(!checkNextField) break;
1617          }
1618       }
1619       return null;
1620    }
1621
1622    DataRow FindSubStringi(const char * subString)
1623    {
1624       DataField field;
1625       bool checkNextField = true;
1626       int len = subString ? strlen(subString) : 0;
1627       DataRow result = null;
1628       const char * bestResult = null;
1629       int bestLen = 0;
1630
1631       if(len)
1632       {
1633          for(field = fields.first; field; field = field.next)
1634          {
1635             if(field.dataType._vTbl[__ecereVMethodID_class_OnGetString])
1636             {
1637                DataRow row;
1638                for(row = rows.first; row; row = row.GetNextRow())
1639                {
1640                   if(!row.noneRow)
1641                   {
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);
1646
1647                      if(string && string[0])
1648                         checkNextField = false;
1649                      if(string && string[0])
1650                      {
1651                         int stringLen = strlen(string);
1652
1653                         if(!strnicmp(string, subString, Min(len, stringLen)))
1654                         {
1655                            if(bestLen < Min(len, stringLen))
1656                               bestResult = 0;
1657                            if(!bestResult || strcmpi(string, bestResult) < 0)
1658                            {
1659                               bestLen = Min(len, stringLen);
1660                               bestResult = string;
1661                               result = row;
1662                            }
1663                         }
1664                      }
1665                   }
1666                }
1667             }
1668             if(!checkNextField) break;
1669          }
1670       }
1671       return result;
1672    }
1673
1674    DataRow FindSubStringAfter(DataRow after, const char * subString)
1675    {
1676       DataField field;
1677       bool checkNextField = true;
1678       int len = subString ? strlen(subString) : 0;
1679
1680       if(len)
1681       {
1682          for(field = fields.first; field; field = field.next)
1683          {
1684             if(field.dataType._vTbl[__ecereVMethodID_class_OnGetString])
1685             {
1686                DataRow row;
1687                for(row = after.GetNextRow(); row && row != after; row = row.GetNextRow(), (!row) ? (row = rows.first) : null)
1688                {
1689                   if(!row.noneRow)
1690                   {
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);
1695
1696                      if(string && string[0])
1697                         checkNextField = false;
1698                      if(string && string[0] && !strncmp(string, subString, len))
1699                         return row;
1700                   }
1701                }
1702             }
1703             if(!checkNextField) break;
1704          }
1705       }
1706       return null;
1707    }
1708
1709    DataRow FindSubRow(int64 tag)
1710    {
1711       if(this)
1712       {
1713          DataRow row = null;
1714
1715          for(row = rows.first; row; row = row.next)
1716          {
1717             if(!row.noneRow && row.tag == tag)
1718                break;
1719             if(!row.noneRow && row.subRows.first)
1720             {
1721                DataRow subRow = row.FindSubRow(tag);
1722                if(subRow)
1723                   return subRow;
1724             }
1725          }
1726          return row;
1727       }
1728       return null;
1729    }
1730
1731    void Clear()
1732    {
1733       if(this)
1734       {
1735          Window master = this.master;
1736
1737          HideEditBox(false, true, false);
1738          editData.Destroy(0);
1739
1740          firstRowShown = currentRow = null;
1741          ClearEx();
1742
1743          if(master)
1744          {
1745             if(style.freeSelect)
1746                NotifyHighlight(master, this, currentRow ? currentRow : null, 0);
1747             else
1748                NotifySelect(master, this, currentRow ? currentRow : null, 0);
1749          }
1750
1751          if(style.alwaysEdit && currentRow)
1752             currentRow.Edit(currentField);
1753
1754
1755          this.rowCount = 0;
1756
1757          SetScrollArea(
1758                this.width,
1759                (this.rowCount * rowHeight) +
1760                ((style.header) ? rowHeight : 0) -
1761                ((rowHeight && !((clientSize.h+1) % rowHeight)) ? rowHeight : 0), true);
1762          Update(null);
1763       }
1764    }
1765
1766    void Sort(DataField field, int order)
1767    {
1768       if(this)
1769       {
1770          DataRow search;
1771          int headerSize = ((style.header) ? rowHeight : 0);
1772          int height = clientSize.h + 1 - headerSize;
1773
1774          if(!field) field = fields.first;
1775          sortField = field;
1776          field.sortOrder = order ? order : 1;
1777          rows.Sort(DataRow::Compare, field);
1778
1779          for(search = rows.first; search; search = search.next)
1780             search._SortSubRows(field, order);
1781
1782          {
1783             int index = 0;
1784             for(search = rows.first; search; search = search.GetNextRow())
1785                search.index = index++;
1786          }
1787
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);
1792
1793          OnVScroll(0, scroll.y, 0);
1794
1795          // SetScrollPosition(0, scroll.y);
1796          // Update(null);
1797       }
1798    }
1799
1800    void StopEditing(bool save)
1801    {
1802       HideEditBox(save, false, true);
1803    }
1804
1805    void GetMultiSelection(OldList list)
1806    {
1807       list.Clear();
1808       if(this && style.multiSelect)
1809       {
1810          DataRow row;
1811
1812          for(row = rows.first; row; row = row.GetNextRow())
1813          {
1814             if(row.selectedFlag == selected || row.selectedFlag == tempSelected)
1815             {
1816                list.Add(OldLink { data = row });
1817             }
1818          }
1819       }
1820    }
1821
1822    // Convenience Current Row Methods
1823    void * SetData(DataField field, any_object data)
1824    {
1825       return currentRow.SetData(field, data);
1826    }
1827
1828    any_object GetData(DataField field)
1829    {
1830       return (void *)currentRow.GetData(field);
1831    }
1832
1833    int64 GetTag()
1834    {
1835       return currentRow ? currentRow.tag : 0;
1836    }
1837
1838 private:
1839    ListBox()
1840    {
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
1849       maxShown = 10;
1850
1851       defaultField.defaultField = true;
1852
1853       AddField(defaultField);
1854
1855       typedString = new char[1];
1856       typedString[0] = '\0';
1857       dropIndex = -1;
1858       return true;
1859    }
1860
1861    ~ListBox()
1862    {
1863       DataField field;
1864
1865       delete editData;
1866       delete typedString;
1867       delete endBevel;
1868
1869       ClearEx();
1870
1871       while((field = fields.first))
1872       {
1873          // fields.Remove(field);
1874          field.Free();
1875          delete field;
1876       }
1877    }
1878
1879    void ClearEx()
1880    {
1881       DataRow row;
1882       clickedRow = null;
1883       while((row = rows.first))
1884       {
1885          rows.Remove(row);
1886          delete row;
1887       }
1888    }
1889
1890    ListBoxCell GetCell(DataRow * row, DataField * field)
1891    {
1892       ListBoxCell cell = null;
1893       if(!*row) *row = currentRow;
1894       if(*row)
1895       {
1896          if(!*field) *field = this ? currentField : null;
1897          if(!*field && this)
1898          {
1899             for(*field = fields.first; (*field).index != 0; *field = (*field).next);
1900          }
1901          if(*field)
1902          {
1903             uint index;
1904             if(field->listBox == this)
1905             {
1906                for(index = 0, cell = (*row).cells.first; cell && index != (*field).index; index++, cell = cell.next);
1907             }
1908          }
1909       }
1910       return cell;
1911    }
1912
1913    void HideEditBox(bool save, bool alwaysStopEdit, bool repositionOnly)
1914    {
1915       if(editData && editData.visible)
1916       {
1917          if(save)
1918             editData.SaveData();
1919
1920          editData.visible = false;
1921          NotifyEditDone(master, this, currentRow);
1922
1923          // ENSURE DATA BOX IS NOT VISIBLE
1924          editData.visible = false;
1925
1926          if(style.alwaysEdit && !alwaysStopEdit)
1927          {
1928             /*
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);
1934
1935             if(!style.alwaysEdit)
1936             {
1937                editData.position = { x, y };
1938                editData.size = { width, height };
1939             }
1940             else
1941             {
1942                editData.position = { x, y - editData.clientStart.y };
1943                editData.size = { width, height + editData.clientStart.y * 2 };
1944             }
1945             editData.visible = true;
1946             if(style.alwaysEdit)
1947                editData.Deactivate();
1948             */
1949             PopupEditBox(currentField, repositionOnly);
1950          }
1951
1952          /*else
1953             currentField = null;*/
1954       }
1955    }
1956
1957    void SetCurrentRow(DataRow row, bool notify)
1958    {
1959       if(this && (currentRow != row || (currentRow && currentRow.selectedFlag == unselected)))
1960       {
1961          int headerSize = ((style.header) ? rowHeight : 0);
1962          int height = clientSize.h + 1 - headerSize;
1963
1964          // true: destroy edit box
1965          HideEditBox(true, true, false);
1966
1967          if(!(style.multiSelect) && currentRow)
1968             currentRow.selectedFlag = unselected;
1969
1970          currentRow = row;
1971
1972          if(style.multiSelect)
1973          {
1974             DataRow selRow;
1975
1976             for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
1977                selRow.selectedFlag = unselected;
1978             if(currentRow)
1979                currentRow.selectedFlag = selected;
1980
1981             clickedRow = row;
1982          }
1983          else if(currentRow)
1984             currentRow.selectedFlag = selected;
1985
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)
1990          {
1991             int line = currentRow ? currentRow.index * rowHeight : 0;
1992             //SNAPUP(line, rowHeight);
1993             SetScrollPosition(scroll.x, line);
1994          }
1995
1996          if(notify)
1997          {
1998             Window master = this.master;
1999             if(master)
2000             {
2001                if(style.freeSelect && visible)
2002                   NotifyHighlight(master, this, currentRow ? currentRow : null, 0);
2003                else
2004                   NotifySelect(master, this, currentRow ? currentRow : null, 0);
2005                if(style.alwaysEdit && currentRow)
2006                   currentRow.Edit(currentField);
2007             }
2008          }
2009
2010          Update(null);
2011       }
2012    }
2013
2014    void RepositionFieldEditor()
2015    {
2016       if(editData && editData.visible)
2017       {
2018          int height = rowHeight - (style.alwaysEdit ? 1 : 0);
2019          int x = 0;
2020          int y = currentRow.index * rowHeight + (style.header ? rowHeight : 0);
2021          int width = 0;
2022          DataField field;
2023
2024          if(style.collapse && !(style.treeBranch))
2025             x += 15;
2026          for(field = fields.first; field; field = field.next)
2027          {
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;
2031             x += width;
2032          }
2033          if(!style.alwaysEdit)
2034          {
2035             editData.position = { x, y - editData.clientStart.y };
2036             editData.size = { width, height + editData.clientStart.y * 2 };
2037          }
2038          else
2039          {
2040             editData.position = { x, y };
2041             editData.size = { width, height };
2042          }
2043       }
2044    }
2045
2046    void PopupEditBox(DataField whichField, bool repositionOnly)
2047    {
2048       if((!editData || !editData.visible || currentField != whichField) && currentRow)
2049       {
2050          // true: destroy edit box
2051          HideEditBox(true, true, false);
2052          if(whichField)
2053          {
2054             int height = rowHeight - (style.alwaysEdit ? 1 : 0);
2055             int x = 0;
2056             int y = currentRow.index * rowHeight + (style.header ? rowHeight : 0);
2057             int width = 0;
2058             //void * data = currentRow.GetData(whichField);
2059             DataField field;
2060             DataRow row = null;
2061             ListBoxCell cell;
2062
2063             if(style.collapse && !(style.treeBranch))
2064                x += 15;
2065
2066             for(field = fields.first; field; field = field.next)
2067             {
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;
2071                x += width;
2072             }
2073
2074             currentField = whichField;
2075             cell = GetCell(&row, &currentField);
2076
2077             if(!editData)
2078             {
2079                editData = DataBox
2080                {
2081                   this;
2082                   background = dataBoxBackground;
2083                   foreground = dataBoxForeground;
2084
2085                   bool NotifyChanged(DataBox dataBox, bool closingDropDown)
2086                   {
2087                      DataRow row = null;
2088                      DataField field = null;
2089                      ListBoxCell cell = GetCell(&row, &field);
2090                      if(cell)
2091                      {
2092                         cell.isSet = true;
2093                         modifiedDocument = true;
2094                         Update(null);
2095                         NotifyChanged(master, this, currentRow);
2096                      }
2097                      return true;
2098                   }
2099
2100                   bool NotifyModified()
2101                   {
2102                      //DataRow row = null;
2103                      //DataField field = null;
2104                      //ListBoxCell cell = GetCell(&row, &field);
2105                      //cell.isSet = true;
2106                      modifiedDocument = true;
2107                      //Update(null);
2108                      NotifyModified(master, this, currentRow);
2109                      return true;
2110                   }
2111
2112                   bool OnKeyDown(Key key, unichar ch)
2113                   {
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
2116                      {
2117                         if((SmartKey)key == enter || (SmartKey)key == escape)
2118                            return true;
2119                      }
2120                      return result;
2121                   }
2122                };
2123                incref editData;
2124             }
2125             else
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;
2131
2132             if(!repositionOnly)
2133                // Might not really need this anymore...
2134                NotifyEditing(master, this, currentRow);
2135
2136             editData.Create();
2137             if(!style.alwaysEdit)
2138             {
2139                editData.position = { x, y - editData.clientStart.y };
2140                editData.size = { width, height + editData.clientStart.y * 2 };
2141             }
2142             else
2143             {
2144                editData.position = { x, y };
2145                editData.size = { width, height };
2146             }
2147             if(!repositionOnly)
2148                editData.Refresh();
2149             editData.visible = true;
2150
2151             if(style.alwaysEdit)
2152                editData.Deactivate();
2153
2154             //   MOVED THIS HIGHER FOR DATALIST EDITOR
2155             if(!repositionOnly)
2156                // Might not really need this anymore...
2157                NotifyEdited(master, this, currentRow);
2158          }
2159       }
2160    }
2161
2162    void OnRedraw(Surface surface)
2163    {
2164       DataRow row;
2165       int y = (style.header) ? rowHeight : 0;
2166       bool isActive = active;
2167       Font font = fontObject;
2168       Font boldFont = this.boldFont.font;
2169
2170
2171       // Draw gray grid
2172       if(style.alwaysEdit && style.fullRowSelect)
2173       {
2174          // Horizontal lines
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;
2179          DataRow row;
2180          DataField field;
2181
2182          // Fill out indent column
2183          if(style.collapse && !(style.treeBranch) && (style.header || rows.first))
2184          {
2185             x += 15;
2186             surface.SetBackground(formColor);
2187             surface.Area(-scroll.x, 0, x, clientSize.h);
2188          }
2189
2190          surface.SetForeground(formColor);
2191          for(row = firstRowShown; row; row = row.GetNextRow())
2192          {
2193             y += rowHeight;
2194             surface.HLine(x + 1, w-1, y-1);
2195             if(y >= h)
2196                break;
2197          }
2198
2199          // Vertical lines
2200          for(field = fields.first; field; field = field.next)
2201          {
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);
2206             x += width;
2207          }
2208       }
2209
2210       surface.foreground = this.foreground;
2211       surface.TextOpacity(false);
2212
2213       // Draw the tree branches
2214       if(style.treeBranch)
2215       {
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() )
2220          {
2221             int x = -scroll.x + EXTRA_SPACE / 2-1;
2222             int rowStart = -scroll.x;
2223             int indent = 0;
2224             DataRow parent;
2225             int plusIndent;
2226
2227             for(parent = row.parent; parent; parent = parent.parent)
2228                if(!parent.header) indent += 20;
2229             if(style.rootCollapse) indent += 20;
2230
2231             plusIndent = (style.treeBranch) ? (indent - 20 + 4) : indent;
2232
2233             x += indent;
2234
2235             // Vertical line
2236             if(row.subRows.first)
2237             {
2238                int numRows = 0;
2239                int y1 = y + PLUSY + 4;
2240                int y2;
2241                DataRow child;
2242
2243                for(child = row.collapsed ? null : row.subRows.first; child && child != row; )
2244                {
2245                   numRows++;
2246                   if(child.subRows.first && !child.collapsed && child != row.subRows.last)
2247                      child = child.subRows.first;
2248                   else if(child.next)
2249                      child = child.next;
2250                   else
2251                   {
2252                      for(; child && child != row; child = child.parent)
2253                      {
2254                         if(child.next)
2255                         {
2256                            child = child.next;
2257                            break;
2258                         }
2259                      }
2260                   }
2261                }
2262                y2 = y + numRows * rowHeight + PLUSY + 4;
2263                surface.VLine(y1, y2, rowStart + plusIndent + 7 + 20);
2264             }
2265             surface.HLine(rowStart + plusIndent + 7, rowStart + indent, y + PLUSY + 4);
2266
2267             y += rowHeight;
2268             if(y >= clientSize.h)
2269                break;
2270          }
2271          // Root Vertical Lines
2272          if(style.rootCollapse && rows.first)
2273          {
2274             int numRows = 0;
2275             int y1, y2;
2276             DataRow child;
2277             y = -scroll.y + ((style.header) ? rowHeight : 0);
2278             y1 = y + PLUSY + 4;
2279             for(child = rows.first; child && child != rows.last; )
2280             {
2281                numRows++;
2282                if(child.subRows.first && !child.collapsed && child != rows.last)
2283                   child = child.subRows.first;
2284                else if(child.next)
2285                   child = child.next;
2286                else
2287                {
2288                   for(; child; child = child.parent)
2289                   {
2290                      if(child.next)
2291                      {
2292                         child = child.next;
2293                         break;
2294                      }
2295                   }
2296                }
2297             }
2298             y2 = y + numRows * rowHeight + PLUSY + 4;
2299             surface.VLine(y1, y2, -scroll.x + 11);
2300          }
2301          surface.LineStipple(0);
2302       }
2303
2304       for(row = firstRowShown; row; row = row.GetNextRow() )
2305       {
2306          int x = -scroll.x + EXTRA_SPACE / 2-1;
2307          DataField field;
2308          ListBoxCell cell;
2309          Color foreground = this.foreground /*black*/, background = this.background /*white*/;
2310          DataDisplayFlags dataDisplayFlags = 0;
2311          int rowStart = -scroll.x;
2312          int indent = 0;
2313          DataRow parent;
2314          Bitmap icon = row.icon ? row.icon.bitmap : null;
2315          int collapseRowStart = 0;
2316          bool lastWasHeader = row.header;
2317
2318          for(parent = row.parent; parent; parent = parent.parent)
2319          {
2320             if(!parent.header || lastWasHeader)
2321             {
2322                if(style.treeBranch)
2323                   indent += 20;
2324                else
2325                   indent += 15;
2326             }
2327          }
2328          if(style.rootCollapse) indent += 20;
2329          x += indent;
2330
2331          dataDisplayFlags.fullRow = style.fullRowSelect;
2332          dataDisplayFlags.active = isActive;
2333          dataDisplayFlags.header = row.header;
2334
2335          surface.Clip(null);
2336          if(style.collapse)
2337          {
2338             collapseRowStart = rowStart;
2339
2340             if(!(style.treeBranch))
2341             {
2342                x += 15;
2343                rowStart += 15;
2344             }
2345          }
2346
2347          if(style.multiSelect)
2348          {
2349             dataDisplayFlags.selected = row.selectedFlag == selected || row.selectedFlag == tempSelected;
2350             dataDisplayFlags.current = row == currentRow || (!currentRow && row == firstRowShown);
2351          }
2352          else
2353          {
2354             dataDisplayFlags.selected = row.selectedFlag == selected || row.selectedFlag == tempSelected;
2355             dataDisplayFlags.current = row == currentRow || (!currentRow && row == firstRowShown);
2356             /*
2357             if(row == currentRow)
2358             {
2359                dataDisplayFlags.current = true;
2360                dataDisplayFlags.selectedFlag = true;
2361             }
2362             else if(!currentRow && row == firstRowShown)
2363             {
2364                dataDisplayFlags.current = true;
2365             }*/
2366          }
2367
2368          surface.TextOpacity(true);
2369
2370          background = this.background;
2371          foreground = this.foreground;
2372
2373          // Draw the current row background
2374          if(row.header)
2375          {
2376             Color colors[] = { formColor, azure, mistyRose, linen, floralWhite, lavender, lavenderBlush, lemonChiffon };
2377             int level = 0;
2378             DataRow p = row;
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;
2384          }
2385          else if(dataDisplayFlags.selected)
2386          {
2387             if(dataDisplayFlags.selected && (isActive || style.alwaysHL || (style.alwaysEdit && style.fullRowSelect)))
2388             {
2389                if(!isActive && style.alwaysEdit)
2390                   background = formColor;
2391                else
2392                   background = selectionColor ? selectionColor : SELECTION_COLOR;
2393                if(style.fullRowSelect)
2394                {
2395                   int offset = (style.alwaysEdit) ? 2 : 1;
2396                   surface.SetBackground(background);
2397                   surface.Area(rowStart, y, clientSize.w, (y + rowHeight) - offset);
2398                }
2399                if(isActive || !(style.alwaysEdit))
2400                   foreground = selectionText ? selectionText : SELECTION_TEXT;
2401                else
2402                   foreground = branchesColor;
2403             }
2404          }
2405
2406          if(icon)
2407          {
2408             surface.Blit(icon, x + (20 - icon.width) /2,y + 2,0,0, icon.width, icon.height);
2409             x += 20;
2410          }
2411
2412          if(row.noneRow)
2413          {
2414             int width = clientSize.w;
2415             Box clip;
2416             dataDisplayFlags.firstField = true;
2417             clip.left = x - EXTRA_SPACE / 2+1;
2418             clip.top = y;
2419             clip.right = x + width - EXTRA_SPACE/2 - 0;
2420             clip.bottom = y + rowHeight - 1;
2421             surface.Clip(&clip);
2422
2423             surface.TextFont(font);
2424
2425             surface.SetForeground(foreground);
2426             surface.SetBackground(background);
2427
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);
2429          }
2430          else
2431          {
2432             if(opacity < 1) surface.TextOpacity(false);
2433             // Draw the rows
2434             for(field = fields.first; field; field = field.next)
2435             {
2436                uint index;
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);
2439
2440                // Box clip = { x, y+1, x + field.width - EXTRA_SPACE - 1, y + rowHeight - 2 };
2441                Box clip;
2442
2443                //width -= EXTRA_SPACE;
2444
2445                if(!field.prev) width -= indent;
2446
2447
2448                dataDisplayFlags.firstField = field.prev ? false : true;
2449
2450                if(!dataDisplayFlags.firstField && !dataDisplayFlags.fullRow)
2451                {
2452                   background = this.background;
2453                   foreground = this.foreground;
2454                }
2455
2456                if(!isActive && dataDisplayFlags.selected && style.alwaysEdit && field.editable && opacity)
2457                {
2458                   surface.Clip(null);
2459                   surface.SetBackground(background);
2460                   surface.Area(x-3, y, x+width-1, y + rowHeight-2);
2461                }
2462
2463                clip.left = x - EXTRA_SPACE / 2+1;
2464                clip.top = y;
2465                clip.right = x + width - EXTRA_SPACE/2 - 0;
2466                clip.bottom = y + rowHeight - 1;
2467                surface.Clip(&clip);
2468
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)
2472                {
2473                   if(row.header)
2474                      surface.TextFont(boldFont);
2475                   else
2476                      surface.TextFont(font);
2477
2478                   surface.SetForeground(foreground);
2479                   surface.SetBackground(background);
2480
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);
2483                   else
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);
2485                }
2486
2487                if(!isActive && dataDisplayFlags.selected && style.alwaysEdit && field.editable)
2488                   background = formColor;
2489
2490                if(!dataDisplayFlags.firstField && !dataDisplayFlags.fullRow)
2491                {
2492                   background = formColor;
2493                   foreground = this.background;
2494                }
2495
2496                x += width;// + EXTRA_SPACE;
2497
2498                if(row.header) break;
2499             }
2500          }
2501
2502          if(style.collapse)
2503          {
2504             int plusIndent = (style.treeBranch) ? (indent - 20 + 4) : indent;
2505             surface.Clip(null);
2506             if(row.subRows.first && (row.parent || !(style.treeBranch) || (style.rootCollapse)))
2507             {
2508                surface.SetForeground(row.header ? headerCollapseForeground : this.foreground);
2509                surface.Rectangle(collapseRowStart + 3 + plusIndent, y + PLUSY, collapseRowStart + 11 + plusIndent, y + PLUSY + 8);
2510
2511                surface.SetBackground(row.header ? (formColor) : (this.background)); //white
2512                surface.Area(collapseRowStart + 4 + plusIndent, y + PLUSY + 1, collapseRowStart + 10 + plusIndent, y + PLUSY + 7);
2513
2514                surface.HLine(collapseRowStart + 5 + plusIndent, collapseRowStart + 9 + plusIndent, y+PLUSY+4);
2515                if(row.collapsed)
2516                   surface.VLine(y + PLUSY + 2, y + PLUSY + 6, collapseRowStart + 7 + plusIndent);
2517             }
2518
2519          }
2520
2521          // Draw the current row stipple
2522          if(style.fullRowSelect && !(style.alwaysEdit) && (dataDisplayFlags.current) && isActive)
2523          {
2524             surface.Clip(null);
2525             if(isActive)
2526             {
2527                surface.LineStipple(0x5555);
2528                if(dataDisplayFlags.selected)
2529                   surface.SetForeground(stippleColor);
2530                else
2531                   surface.SetForeground(this.foreground);
2532             }
2533             else
2534                surface.SetForeground(selectionColor ? selectionColor : SELECTION_COLOR);
2535             surface.Rectangle(0, y, clientSize.w-1, (y + rowHeight) - 1);
2536             surface.LineStipple(0);
2537          }
2538
2539          y += rowHeight;
2540          if(y >= clientSize.h)
2541             break;
2542       }
2543       if(firstRowShown) surface.Clip(null);
2544       if(this.dragRow && this.dropIndex != -1)
2545       {
2546          int dropIndex = this.dropIndex;
2547          int y;
2548
2549          if(!style.multiSelect && currentRow.index < this.dropIndex)
2550             dropIndex++;
2551          surface.DrawingChar(223);
2552
2553          y = style.header ? rowHeight : 0;
2554          y += dropIndex * rowHeight - scroll.y;
2555
2556          surface.SetForeground(Color { 85, 85, 255 });
2557          surface.HLine(0, clientSize.w-1, y);
2558          surface.HLine(0, clientSize.w-1, y + 1);
2559       }
2560    }
2561
2562    void OnDrawOverChildren(Surface surface)
2563    {
2564       if(draggingField && this.dropField)
2565       {
2566          int position = this.dropField.x;
2567          if(draggingField.x < position)
2568             position += this.dropField.width + EXTRA_SPACE;
2569
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);
2573       }
2574       if(sortField && !style.clearHeader && style.header)
2575       {
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);
2579          int tw = 0, th = 0;
2580          if(field.header)
2581             surface.TextExtent(field.header, strlen(field.header), &tw, &th);
2582          if(tw < width - EXTRA_SPACE)
2583          {
2584             bool up = field.sortOrder == 1;
2585             int x = 4, y = 4;
2586             Box clip =
2587             {
2588                field.x + 2 - scroll.x, 0,
2589                field.x + width + EXTRA_SPACE - 1 - scroll.x, rowHeight
2590             };
2591             surface.Clip(&clip);
2592             if(field.alignment == left || field.alignment == center)
2593             {
2594                if(field.alignment == center)
2595                   x = field.x + (width + EXTRA_SPACE - tw) / 2 + tw + EXTRA_SPACE + 4;
2596                else
2597                   x = field.x + tw + EXTRA_SPACE + 4;
2598
2599                x = Min(x, field.x + width - 4);
2600             }
2601             else if(field.alignment == right)
2602             {
2603                x = field.x + width - tw - 2*EXTRA_SPACE - 4;
2604                x = Max(x, field.x + 2);
2605             }
2606             x -= scroll.x;
2607
2608             if(guiApp.textMode)
2609             {
2610                // surface.SetForeground((wmenu.selectedFlag == item) ? white : black);
2611                // surface.WriteText(clientSize.w-8, y+(wmenu.rh - 8)/2, "\020", 1);
2612             }
2613             else
2614             {
2615                if(up)
2616                {
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);
2622
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);
2628
2629                   surface.DrawLine(x, y + 6, x + 7, y + 6);
2630                }
2631                else
2632                {
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);
2638
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);
2644
2645                   surface.DrawLine(x, y, x + 7, y);
2646                }
2647             }
2648             surface.Clip(null);
2649          }
2650       }
2651
2652    }
2653
2654    void OnResize(int w, int h)
2655    {
2656       DataField field;
2657       bool showEndBevel = false;
2658       int x = 0;
2659       if(style.collapse && !style.treeBranch)
2660          x += 15;
2661       for(field = fields.first; field; field = field.next)
2662       {
2663          int width = field.width + EXTRA_SPACE;
2664          field.x = x;
2665          x += width;
2666       }
2667       width = x;
2668
2669       SetScrollArea(
2670          width,
2671          (rowCount * rowHeight) +
2672          ((style.header) ? rowHeight : 0) -
2673          ((!((clientSize.h+1) % rowHeight)) ? rowHeight : 0), true);
2674
2675       for(field = fields.first; field; field = field.next)
2676       {
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)
2680          {
2681             showEndBevel = true;
2682             if(width > 0)
2683             {
2684                field.headButton.position = { field.x, 0 };
2685                field.headButton.size = { width, rowHeight };
2686                field.headButton.visible = true;
2687             }
2688             else
2689                field.headButton.visible = false;
2690          }
2691       }
2692
2693       if(!style.fillLastField && showEndBevel && endBevel)
2694       {
2695          endBevel.position = { x, 0 };
2696          endBevel.size = { clientSize.w + 2 - x, rowHeight };
2697          endBevel.visible = true;
2698       }
2699       else if(endBevel)
2700          endBevel.visible = false;
2701
2702       if(style.alwaysEdit && editData && editData.visible)
2703       {
2704          HideEditBox(true, false, true);
2705       }
2706       else if(editData && editData.visible)
2707          RepositionFieldEditor();
2708    }
2709
2710    void AdaptToFieldWidth(DataField field, bool doScroll)
2711    {
2712       OnResize(clientSize.w, clientSize.h);
2713
2714       // Scroll appropriately
2715       if(doScroll)
2716       {
2717          if(field.x + field.width + EXTRA_SPACE - clientSize.w > scroll.x &&
2718                  field.x >= field.x + field.width + EXTRA_SPACE - clientSize.w)
2719          {
2720             SetScrollPosition(field.x + field.width + EXTRA_SPACE - clientSize.w, scroll.y);
2721          }
2722          else if(field.x + field.width + EXTRA_SPACE - clientSize.w > scroll.x ||
2723                  field.x < scroll.x)
2724             SetScrollPosition(field.x, scroll.y);
2725       }
2726       Update(null);
2727    }
2728
2729    bool HeaderPushed(Button control, int x, int y, Modifiers mods)
2730    {
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)))
2737       {
2738          if(!field)
2739             field = fields.last;
2740          else if(x < RESIZE_BORDER && field.prev)
2741             field = field.prev;
2742
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;
2748          return false;
2749       }
2750       else if(field)
2751       {
2752          if(field.fixed) return false;
2753          draggingField = field;
2754          if(style.moveFields)
2755             field.headButton.stayDown = true;
2756          else if(!style.sortable)
2757             return false;
2758       }
2759       else
2760          return false;
2761       return true;
2762    }
2763
2764    bool HeaderMouseMove(Button control, int x, int y, Modifiers mods)
2765    {
2766       if(resizingField)
2767       {
2768          // Resize left
2769          DataField field = resizingField;
2770
2771          x += control.position.x;
2772
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)
2776          {
2777             this.oldX = x - scroll.x;
2778             return true;
2779          }
2780          this.oldX = x - scroll.x;
2781
2782          field.width = this.startWidth + x - this.resizeX;
2783          field.width = Max(field.width, - EXTRA_SPACE);
2784
2785          AdaptToFieldWidth(field, true);
2786       }
2787       else if(draggingField)
2788       {
2789          x += control.position.x;
2790          if(style.moveFields)
2791          {
2792             DataField field = fields.last;
2793             int fieldX = 0;
2794             for(field = fields.first; field; field = field.next)
2795             {
2796                fieldX += ((field.width || style.resizable) ?
2797                   field.width : clientSize.w) + EXTRA_SPACE;
2798                if(fieldX > x)
2799                   break;
2800             }
2801             if(draggingField == field)
2802             {
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)
2806                {
2807                   SetScrollPosition(
2808                      field.x + field.width + EXTRA_SPACE - clientSize.w,
2809                      scroll.y);
2810                }
2811                else if(field.x + field.width + EXTRA_SPACE - clientSize.w > scroll.x ||
2812                        field.x < scroll.x)
2813                   SetScrollPosition(field.x, scroll.y);
2814                field = null;
2815             }
2816             if(this.dropField != field)
2817             {
2818                this.dropField = field;
2819                if(field)
2820                {
2821                   int position = field.x;
2822                   // Moving right
2823                   if(draggingField.x < position)
2824                   {
2825                      position += field.width + EXTRA_SPACE - clientSize.w;
2826                      if(position > scroll.x)
2827                         SetScrollPosition(position, scroll.y);
2828                   }
2829                   // Moving Left
2830                   else
2831                   {
2832                      if(position < scroll.x)
2833                         SetScrollPosition(position, scroll.y);
2834                   }
2835
2836                   this.movingFields = true;
2837                }
2838                Update(null);
2839             }
2840          }
2841       }
2842       else if(style.resizable)
2843       {
2844          DataField field = (DataField)(intptr)control.id;
2845          if(field)
2846          {
2847             if(x < RESIZE_BORDER && field.prev)
2848             {
2849                if(!field.prev.fixed)
2850                   control.cursor = guiApp.GetCursor(sizeWE);
2851             }
2852             else if(x >= control.clientSize.w - RESIZE_BORDER)
2853                control.cursor = guiApp.GetCursor(sizeWE);
2854             else
2855                control.cursor = null;
2856          }
2857          else
2858          {
2859             if(x < RESIZE_BORDER && fields.last)
2860                control.cursor = guiApp.GetCursor(sizeWE);
2861             else
2862                control.cursor = null;
2863          }
2864       }
2865       return true;
2866    }
2867
2868    bool HeaderReleased(Button control, int x, int y, Modifiers mods)
2869    {
2870       if(resizingField)
2871       {
2872          NotifyResized(master, this, resizingField, mods);
2873          resizingField = null;
2874       }
2875
2876       if(draggingField)
2877       {
2878          bool result = true;
2879
2880          if(style.moveFields)
2881          {
2882             if(dropField)
2883             {
2884                // Find which field
2885                DataField switchField = fields.last;
2886                DataField field;
2887                int fieldX = 0;
2888
2889                x += draggingField.x;
2890                for(field = fields.first; field; field = field.next)
2891                {
2892                   fieldX += ((field.width || style.resizable) ?
2893                      field.width : clientSize.w) + EXTRA_SPACE;
2894                   if(fieldX > x)
2895                   {
2896                      switchField = field;
2897                      break;
2898                   }
2899                }
2900                if(switchField && draggingField != switchField && this.dropField)
2901                {
2902                   for(field = fields.first; field; field = field.next)
2903                   {
2904                      if(field == switchField || field == draggingField)
2905                         break;
2906                   }
2907
2908                   // Switch field first: move before
2909                   if(field == switchField)
2910                      draggingField.Move(switchField.prev);
2911                   // Dragged field first: move after
2912                   else
2913                      draggingField.Move(switchField);
2914
2915                   NotifyMovedField(master, this, draggingField, mods);
2916                }
2917                draggingField.headButton.stayDown = false;
2918             }
2919             if(movingFields)
2920                result = false;
2921             dropField = null;
2922             movingFields = false;
2923          }
2924          draggingField = null;
2925          return result;
2926       }
2927       return true;
2928    }
2929
2930    bool HeaderClicked(Button control, int x, int y, Modifiers mods)
2931    {
2932       if(style.header && !this.dropField && style.sortable)
2933       {
2934          DataField field = (DataField)(intptr)control.id;
2935          if(sortField == field)
2936             field.sortOrder *= -1;
2937          else
2938             sortField = field;
2939          if(field)
2940          {
2941             Sort(sortField, field.sortOrder);
2942             NotifySort(master, this, field, mods);
2943          }
2944       }
2945       return true;
2946    }
2947
2948    bool HeaderDoubleClicked(Button control, int x, int y, Modifiers mods)
2949    {
2950       if(style.resizable)
2951       {
2952          DataField field = (DataField)(intptr)control.id;
2953          if(field)
2954          {
2955             if(x < RESIZE_BORDER && field.prev)
2956                field = field.prev;
2957             else if(x >= control.clientSize.w - RESIZE_BORDER);
2958             else
2959                field = null;
2960          }
2961          else
2962          {
2963             if(x < RESIZE_BORDER && fields.last)
2964                field = fields.last;
2965             else
2966                field = null;
2967          }
2968          if(field)
2969             field.AutoSize();
2970       }
2971       return false;
2972    }
2973
2974    watch(visible)
2975    {
2976       if(style.freeSelect)
2977       {
2978          if(!visible)
2979          {
2980             ReleaseCapture();
2981             this.rolledOver = this.dragging = false;
2982          }
2983          /*else
2984             Capture();*/
2985       }
2986    };
2987
2988    bool OnLoadGraphics()
2989    {
2990       display.FontExtent(fontObject, "W", 1, null, &fontH);
2991       if(!style.heightSet)
2992       {
2993          rowHeight = Max(fontH + 2, 16) + (style.alwaysEdit ? 1 : 0);
2994          SetScrollLineStep(8, rowHeight);
2995       }
2996       return true;
2997    }
2998
2999    void OnApplyGraphics()
3000    {
3001       SetScrollLineStep(8, rowHeight);
3002       if(style.header)
3003       {
3004          DataField field;
3005          for(field = fields.first; field; field = field.next)
3006          {
3007             if(field.headButton)
3008             {
3009                field.headButton.bevel = (!guiApp.textMode && !style.clearHeader);
3010                if(guiApp.textMode)
3011                   field.headButton.background = Color { 0, 170, 0 };
3012             }
3013          }
3014       }
3015       OnResize(clientSize.w, clientSize.h);
3016    }
3017
3018    bool OnResizing(int *w, int *h)
3019    {
3020       if(rows.first)
3021       {
3022          if(!initSize.w && (!anchor.left.type || !anchor.right.type) && !*w)
3023          {
3024             // Use widest item
3025             DataRow row;
3026             int maxWidth = 0;
3027             Font font = fontObject;
3028             Font boldFont = this.boldFont.font;
3029             Display display = this.display;
3030
3031             for(row = rows.first; row; row = row.GetNextRow())
3032             {
3033                Bitmap icon = row.icon ? row.icon.bitmap : null;
3034                int x = -scroll.x + EXTRA_SPACE / 2-1;
3035                DataField field;
3036                int indent = 0;
3037                DataRow parent;
3038                for(parent = row.parent; parent; parent = parent.parent)
3039                {
3040                   if(!parent.header)
3041                   {
3042                      if(style.treeBranch)
3043                         indent += 20;
3044                      else
3045                         indent += 15;
3046                   }
3047                }
3048                if(style.rootCollapse) indent += 20;
3049                x += indent;
3050                if(style.collapse && !(style.treeBranch)) x += 15;
3051                if(icon)
3052                   x += 20;
3053
3054                // Compute the rows size
3055                for(field = fields.first; field; field = field.next)
3056                {
3057                   if(((style.resizable && (!(style.alwaysEdit) || field.next)) || field.width) && !row.header)
3058                      x += field.width - (field.prev ? 0 : indent);
3059                   else
3060                   {
3061                      ListBoxCell cell;
3062                      uint index;
3063                      for(index = 0, cell = row.cells.first; index != field.index; index++, cell = cell.next);
3064
3065                      // Should always be as many cells in the row as fields in the listbox
3066                      if(cell && cell.isSet && field.dataType)
3067                      {
3068                         static char tempString[4096];
3069                         const char * string;
3070                         int tw, th;
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);
3073                         else
3074                            string = ((const char *(*)(void *, void *, char *, void *, bool *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString])(field.dataType, cell.data, tempString, field.userData, null);
3075                         /* GCC-4.4 Bug!
3076                         if(!string) string = "";
3077                         display.FontExtent(row.header ? boldFont : font, string, strlen(string), &tw, &th);
3078                         */
3079                         if(string)
3080                            display.FontExtent(row.header ? boldFont : font, string, strlen(string), &tw, &th);
3081                         else
3082                            display.FontExtent(row.header ? boldFont : font, "", 0, &tw, &th);
3083                         x += tw;
3084                      }
3085                      if(row.header) break;
3086                   }
3087                   x += EXTRA_SPACE;
3088                }
3089                maxWidth = Max(maxWidth, x);
3090             }
3091             *w = maxWidth;
3092          }
3093          if(!*h)
3094          {
3095             *h = Min(this.maxShown, this.rowCount) * rowHeight;
3096          }
3097       }
3098       else
3099       {
3100          if(!*w) *w = rowHeight * 5;
3101          if(!*h) *h = rowHeight * 5;
3102       }
3103       return true;
3104    }
3105
3106    watch(font)
3107    {
3108       FontResource font = this.font;
3109       FontResource boldFont
3110       {
3111          faceName = font.faceName, size = font.size, bold = true
3112       };
3113       AddResource(boldFont);
3114       RemoveResource(this.boldFont);
3115       this.boldFont = boldFont;
3116
3117       OnLoadGraphics();
3118
3119       SetInitSize(initSize);
3120    };
3121
3122    bool OnMouseMove(int x, int y, Modifiers mods)
3123    {
3124       bool isTimer = false;
3125       int realX = x, realY = y;
3126
3127       if(insideNotifySelect) return true;
3128
3129       if(style.alwaysEdit && style.resizable &&
3130          resizingField && !(mods.isSideEffect))
3131       {
3132         // Resize left
3133          DataField field = resizingField;
3134          field.width = this.startWidth + x - this.resizeX;
3135          field.width = Max(field.width, - EXTRA_SPACE);
3136
3137          AdaptToFieldWidth(field, true);
3138       }
3139
3140       cursor = null;
3141       if(style.alwaysEdit && style.resizable)
3142       {
3143          int vx = -scroll.x - 1;
3144          DataField field;
3145
3146          if(style.collapse && !(style.treeBranch))
3147             vx += 15;
3148
3149          for(field = fields.first; field; field = field.next)
3150          {
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);
3153
3154             if(field.prev)
3155             {
3156                if(Abs(x - vx) < 2)
3157                {
3158                   cursor = guiApp.GetCursor(sizeWE);
3159                   break;
3160                }
3161             }
3162             vx += width + EXTRA_SPACE;
3163          }
3164       }
3165
3166       if((editData && editData.visible) || (style.alwaysEdit))
3167          return true;
3168
3169       if(x == MAXINT && y == MAXINT)
3170       {
3171          x = this.mouseX;
3172          y = this.mouseY;
3173          isTimer = true;
3174       }
3175
3176       // ADDED THIS CHECK FOR FieldDropBox LEAKS
3177       if(/*!mods.isSideEffect && */(this.rolledOver || !this.dragging))
3178       {
3179          int rowY = (style.header) ? rowHeight : 0;
3180          DataRow row = null;
3181          int rowIndex;
3182
3183          mouseX = x;
3184          mouseY = y;
3185
3186          if(this.dragging &&
3187             ((vertScroll && vertScroll.visible &&
3188              (y < 0 || y >= clientSize.h)) ||
3189              (horzScroll && horzScroll.visible &&
3190              (x < 0 || x >= clientSize.w))))
3191          {
3192             timer.Start();
3193             if(isTimer)
3194             {
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);
3201             }
3202          }
3203          else
3204             timer.Stop();
3205
3206          // This must be done after the scrolling took place
3207          rowIndex = firstRowShown ? firstRowShown.index : -1;
3208          y = Max(y, 0);
3209          y = Min(y, clientSize.h-1);
3210          for(row = firstRowShown; row; row = row.GetNextRow(), rowIndex ++)
3211          {
3212             rowY += rowHeight;
3213             if(rowY > y)
3214             {
3215                break;
3216             }
3217          }
3218
3219          if(row && currentRow != row)
3220          {
3221             if(this.dragRow && style.moveRows)
3222             {
3223                if(row.selectedFlag == selected || row.selectedFlag == tempSelected)
3224                   rowIndex = -1;
3225                else if(style.multiSelect)
3226                {
3227                   DataRow thisRow;
3228                   for(thisRow = rows.first; thisRow; thisRow = thisRow.GetNextRow())
3229                      if((thisRow.selectedFlag == selected || thisRow.selectedFlag == tempSelected) ||
3230                         thisRow == row)
3231                         break;
3232                   if(thisRow != row)
3233                      rowIndex++;
3234                }
3235                if(this.dropIndex != rowIndex)
3236                {
3237                   this.dropIndex = rowIndex;
3238                   this.editRow = null;
3239                   Update(null);
3240                   this.movedRow = true;
3241                }
3242             }
3243             else if((style.freeSelect  || this.dragging) && ((realX>= 0 && realY >= 0 && realX< clientSize.w && realY < clientSize.h) || this.rolledOver))
3244             {
3245                if(!(style.multiSelect))
3246                {
3247                   if(currentRow)currentRow.selectedFlag = unselected;
3248                   if(row)row.selectedFlag = selected;
3249                }
3250                currentRow = row;
3251
3252                if(style.multiSelect)
3253                {
3254                   DataRow selRow;
3255
3256                   for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3257                   {
3258                      if(selRow.selectedFlag == tempUnselected) selRow.selectedFlag = selected;
3259                      else if(selRow.selectedFlag == tempSelected) selRow.selectedFlag = unselected;
3260                   }
3261
3262                   if(rowIndex >= clickedRow.index)
3263                   {
3264                      for(selRow = clickedRow; selRow; selRow = selRow.GetNextRow())
3265                      {
3266                         selRow.selectedFlag = (selRow.selectedFlag == selected) ? tempUnselected : tempSelected;
3267                         if(selRow == row)
3268                            break;
3269                      }
3270                   }
3271                   else
3272                   {
3273                      for(selRow = row; selRow; selRow = selRow.GetNextRow())
3274                      {
3275                         selRow.selectedFlag = (selRow.selectedFlag == selected) ? tempUnselected : tempSelected;
3276                         if(selRow == clickedRow)
3277                            break;
3278                      }
3279                   }
3280                }
3281                Update(null);
3282                if(style.freeSelect)
3283                   NotifyHighlight(master, this, currentRow, mods);
3284                else
3285                {
3286                   insideNotifySelect = true;
3287                   NotifySelect(master, this, currentRow, mods);
3288                   insideNotifySelect = false;
3289                }
3290
3291                if(style.alwaysEdit && currentRow)
3292                   currentRow.Edit(currentField);
3293             }
3294          }
3295       }
3296       return true;
3297    }
3298
3299    bool OnMouseOver(int x, int y, Modifiers mods)
3300    {
3301       if(this.dragging)
3302          this.rolledOver = true;
3303       return true;
3304    }
3305
3306    bool OnActivate(bool active, Window swap, bool * goOnWithActivation, bool direct)
3307    {
3308       // TOCHECK: WAS THIS MISSING ? CHECK FOR SLOWDOWN
3309       if(!active) Update(null);
3310
3311       if(!active && (!swap || !swap.isModal))
3312       {
3313          // true: destroy edit box
3314          HideEditBox(true, true, false);
3315       }
3316       else if(!swap || !swap.isModal)
3317       {
3318          // Bring back edit box
3319          if(currentRow && style.alwaysEdit)
3320          {
3321             currentRow.Edit(currentField ? currentField : null);
3322          }
3323          Update(null);
3324       }
3325       return true; //NotifyActivate(master, this, active, swap, 0);
3326    }
3327
3328
3329    bool OnButtonDown(int x, int y, Modifiers mods, bool right)
3330    {
3331       bool result = true;
3332       // Check to see if we're dragging the vertical divider
3333       if(style.alwaysEdit && style.resizable && !right)
3334       {
3335          int vx = -scroll.x - 1;// + EXTRA_SPACE;// / 2 - 1;
3336          DataField field;
3337
3338          if(style.collapse && !(style.treeBranch))
3339             vx += 15;
3340
3341          for(field = fields.first; field; field = field.next)
3342          {
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);
3345
3346             if(field.prev)
3347             {
3348                if(Abs(x - vx) < 2)
3349                {
3350                   resizingField = field.prev;
3351                   this.resizeX = x;
3352                   this.startWidth = resizingField.width;
3353                   Capture();
3354                   SetMouseRangeToClient();
3355                   break;
3356                }
3357             }
3358             vx += width + EXTRA_SPACE;
3359          }
3360       }
3361
3362       if(!(style.freeSelect))
3363       {
3364          int rowY = (style.header) ? rowHeight : 0;
3365          DataRow row = null;
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;
3372
3373          if(style.multiSelect)
3374          {
3375             if(!right)
3376             {
3377                DataRow row;
3378                if(!(mods.shift))
3379                {
3380                   for(row = rows.first; row; row = row.GetNextRow())
3381                   {
3382                      if(row.selectedFlag == tempSelected)
3383                         row.selectedFlag = selected;
3384                      else if(row.selectedFlag == tempUnselected)
3385                         row.selectedFlag = unselected;
3386                      if(row.selectedFlag == selected)
3387                         numSelected++;
3388                   }
3389                }
3390             }
3391          }
3392
3393          for(row = firstRowShown; row; row = row.GetNextRow(), rowIndex ++)
3394          {
3395             rowY += rowHeight;
3396             if(rowY > y || (style.multiSelect && !row.GetNextRow()))
3397             {
3398                int plusIndent = 0;
3399                if(style.treeBranch)
3400                {
3401                   DataRow parent;
3402                   for(parent = (style.rootCollapse) ? row.parent : (row.parent ? row.parent.parent : null); parent; parent = parent.parent)
3403                      if(!parent.header)
3404                         plusIndent += 20;
3405                   plusIndent += 4;
3406                }
3407
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))
3411                */
3412                if(style.collapse &&
3413                   (x >= rowStart && y >= rowY - rowHeight && x <= rowStart + 18 + plusIndent && y <= rowY + rowHeight-1))
3414                {
3415                   if(row.subRows.first && (row.parent || !(style.treeBranch) || (style.rootCollapse)) && x >= plusIndent)
3416                      row.collapsed = !row.collapsed;
3417                   return false;
3418                }
3419                else
3420                {
3421                   if(rowY > y)
3422                   {
3423                      newCurrentRow = row;
3424                   }
3425                   if(style.multiSelect)
3426                   {
3427                      // Deselect everything if user didn't clicked on a row
3428                      if(y >= rowY)
3429                      {
3430                         DataRow selRow;
3431                         for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3432                            selRow.selectedFlag = unselected;
3433                         clickedRow = row;
3434                         //this.clickedRowIndex = rowIndex;
3435                      }
3436                      else if(style.moveRows && !(mods.shift) &&
3437                         (row.selectedFlag == selected || row.selectedFlag == tempSelected) &&
3438                         !right && !(mods.isActivate))
3439                         moveMultiple = true;
3440                      else
3441                      {
3442                         DataRow selRow;
3443                         if(right)
3444                         {
3445                            if(row.selectedFlag == tempUnselected || row.selectedFlag == unselected)
3446                            {
3447                               for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3448                                  selRow.selectedFlag = unselected;
3449                               row.selectedFlag = selected;
3450                            }
3451                            clickedRow = row;
3452                            //this.clickedRowIndex = rowIndex;
3453                         }
3454                         else
3455                         {
3456                            if(!(mods.ctrl))
3457                            {
3458                               for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3459                                  selRow.selectedFlag = unselected;
3460                            }
3461                            else
3462                            {
3463                               for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3464                               {
3465                                  if(selRow != clickedRow)
3466                                  {
3467                                     if(selRow.selectedFlag == tempUnselected) selRow.selectedFlag = selected;
3468                                     else if(selRow.selectedFlag == tempSelected) selRow.selectedFlag = unselected;
3469                                  }
3470                               }
3471                            }
3472
3473                            if(mods.shift)
3474                            {
3475                               if(rowIndex >= clickedRow.index)
3476                               {
3477                                  for(selRow = clickedRow; selRow; selRow = selRow.GetNextRow())
3478                                  {
3479                                     if(mods.ctrl)
3480                                     {
3481                                        if(selRow != clickedRow)
3482                                        {
3483                                           if(selRow.selectedFlag)
3484                                              selRow.selectedFlag = tempUnselected;
3485                                           else
3486                                              selRow.selectedFlag = tempSelected;
3487                                        }
3488                                     }
3489                                     else
3490                                        selRow.selectedFlag = selected;
3491                                     if(selRow == row)
3492                                        break;
3493                                  }
3494                               }
3495                               else
3496                               {
3497                                  for(selRow = row; selRow; selRow = selRow.GetNextRow())
3498                                  {
3499                                     if(mods.ctrl)
3500                                     {
3501                                        if(selRow != clickedRow)
3502                                        {
3503                                           if(selRow.selectedFlag)
3504                                              selRow.selectedFlag = tempUnselected;
3505                                           else
3506                                              selRow.selectedFlag = tempSelected;
3507                                        }
3508                                     }
3509                                     else
3510                                        selRow.selectedFlag = selected;
3511                                     if(selRow == clickedRow)
3512                                        break;
3513                                  }
3514                               }
3515                            }
3516                            else
3517                            {
3518                               if(mods.ctrl)
3519                               {
3520                                  if(row.selectedFlag)
3521                                     row.selectedFlag = tempUnselected;
3522                                  else row.selectedFlag = tempSelected;
3523                               }
3524                               else
3525                                  row.selectedFlag = tempSelected;
3526                               clickedRow = row;
3527                               //this.clickedRowIndex = rowIndex;
3528                            }
3529                         }
3530                      }
3531                   }
3532                }
3533                break;
3534             }
3535          }
3536
3537          // true: destroy edit box
3538          if(newCurrentRow)
3539          {
3540             incref newCurrentRow;
3541          }
3542
3543          if(currentRow != newCurrentRow)
3544             HideEditBox(true, true, false);
3545
3546          if(newCurrentRow)
3547          {
3548             if(newCurrentRow._refCount <= 1)
3549                delete newCurrentRow;
3550             else
3551                newCurrentRow._refCount--;
3552          }
3553
3554          if(newCurrentRow)
3555          {
3556             if(!(style.multiSelect))
3557             {
3558                if(currentRow) currentRow.selectedFlag = unselected;
3559                if(newCurrentRow) newCurrentRow.selectedFlag = selected;
3560             }
3561          }
3562
3563          if(currentRow != newCurrentRow)
3564          {
3565             /*
3566             // true: destroy edit box
3567             if(newCurrentRow)
3568             {
3569                //incref newCurrentRow;
3570                incref newCurrentRow;
3571             }
3572
3573             HideEditBox(true, true, false);
3574             */
3575
3576             if(newCurrentRow)
3577             {
3578                int headerSize = ((style.header) ? rowHeight : 0);
3579                int height = clientSize.h + 1 - headerSize;
3580
3581                /*if(newCurrentRow._refCount <= 1)
3582                   delete newCurrentRow;
3583                else
3584                {
3585                   newCurrentRow._refCount--;
3586                   //newCurrentRow._refCount--;
3587                }
3588                */
3589                currentRow = newCurrentRow;
3590
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)
3595                {
3596                   int line = currentRow ? currentRow.index * rowHeight : 0;
3597                   //SNAPUP(line, rowHeight);
3598                   SetScrollPosition(scroll.x, line);
3599                }
3600
3601                // GO THROUGH SetCurrentRow eventually?
3602                // SetCurrentRow(newCurrentRow, true);
3603             }
3604          }
3605
3606          if(style.freeSelect)
3607             NotifyHighlight(master, this, currentRow, mods);
3608          else if((moveMultiple || (!(style.multiSelect) && previousRow == currentRow)) &&
3609             newCurrentRow && !(mods.shift))
3610          {
3611             if(!right)
3612             {
3613                if(!(mods.isActivate))
3614                {
3615                   if(style.moveRows)
3616                   {
3617                      this.dragRow = currentRow;
3618                      this.dropIndex = -1;
3619                      this.movedRow = false;
3620                   }
3621                   if(editData && editData.visible && style.alwaysEdit)
3622                   {
3623                      DataField field;
3624                      int sx = -scroll.x;
3625                      int indent = 0;
3626                      DataRow parent;
3627
3628                      if(style.collapse && !style.treeBranch)
3629                         sx += 15;
3630
3631                      for(parent = newCurrentRow.parent; parent; parent = parent.parent)
3632                         if(!parent.header)
3633                            indent += (style.treeBranch) ? 20 : 15;
3634                      sx += indent;
3635
3636                      for(field = fields.first; field; field = field.next)
3637                      {
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);
3640
3641                         if(!field.prev) width -= indent;
3642                         if(x >= sx && x < sx + width)
3643                            break;
3644                         sx += width;
3645                      }
3646                      if(field == currentField)
3647                         editData.Deactivate();
3648                      else
3649                      {
3650                         currentRow.Edit(field);
3651                         editData.Activate();
3652                      }
3653                   }
3654                   else if(!(mods.ctrl) && numSelected <= 1)
3655                      this.editRow = currentRow;
3656
3657                   if(style.noDragging && newCurrentRow)
3658                     NotifyReclick(master, this, newCurrentRow, mods);
3659                }
3660                else
3661                {
3662                   // If the user clicked exactly on the edited field,
3663                   // activate it
3664                   if(editData && editData.visible && newCurrentRow)
3665                   {
3666                      DataField field;
3667                      int sx = -scroll.x;
3668                      int indent = 0;
3669
3670                      if(style.collapse && !(style.treeBranch))
3671                         sx += 15;
3672
3673                      {
3674                         DataRow parent;
3675                         for(parent = newCurrentRow.parent; parent; parent = parent.parent)
3676                            if(!parent.header)
3677                               indent += (style.treeBranch) ? 20 : 15;
3678                         sx += indent;
3679                      }
3680
3681                      for(field = fields.first; field; field = field.next)
3682                      {
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)
3687                            break;
3688                         sx += width;
3689                      }
3690
3691                      if(field) //x >= sx && x < sx + width && newCurrentRow)
3692                      {
3693                         if(field == currentField)
3694                            editData.Activate();
3695                         /*else
3696                            newCurrentRow.Edit(currentField);*/
3697                      }
3698                      else if(style.noDragging && newCurrentRow)
3699                        NotifyReclick(master, this, newCurrentRow, mods);
3700                   }
3701                   else if(style.noDragging && newCurrentRow)
3702                     NotifyReclick(master, this, newCurrentRow, mods);
3703                }
3704             }
3705          }
3706          else
3707          {
3708             DataField f = null;
3709             if(result && style.alwaysEdit && currentRow)
3710             {
3711                if(newCurrentRow)
3712                {
3713                   DataField field = null;
3714                   int sx = -scroll.x;
3715                   int indent = 0;
3716                   DataRow parent;
3717
3718                   if(style.collapse && !style.treeBranch)
3719                      sx += 15;
3720
3721                   for(parent = newCurrentRow.parent; parent; parent = parent.parent)
3722                      if(!parent.header)
3723                         indent += (style.treeBranch) ? 20 : 15;
3724                   sx += indent;
3725
3726                   for(field = fields.first; field; field = field.next)
3727                   {
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);
3730
3731                      if(!field.prev) width -= indent;
3732                      if(x >= sx && x < sx + width)
3733                      {
3734                         f = currentField = field;
3735                         break;
3736                      }
3737                      sx += width;
3738                   }
3739                }
3740             }
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)
3744             {
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);
3747
3748                // If the user clicked exactly on the edited field,
3749                // activate it
3750                if(editData && editData.visible && newCurrentRow)
3751                {
3752                   if(currentField)
3753                   {
3754                      editData.Activate();
3755                   }
3756                   else if(style.noDragging && newCurrentRow)
3757                      NotifyReclick(master, this, newCurrentRow, mods);
3758                }
3759             }
3760             else if(style.noDragging && newCurrentRow)
3761               NotifyReclick(master, this, newCurrentRow, mods);
3762          }
3763       }
3764       /*
3765          For drop box to capture...
3766       else
3767       {
3768          if(x < 0 || y < 0 || x >= clientSize.w || y >= clientSize.h)
3769          {
3770             bool goOn = true;
3771             master.parent.Activate();
3772             Update(null);
3773             ReleaseCapture();
3774             return true;
3775          }
3776       }
3777       */
3778       if(result)
3779       {
3780          if(!style.noDragging)
3781          {
3782             this.dragging = true;
3783             Capture();
3784          }
3785          if(x >= 0 && y >= 0 && x < clientSize.w && y < clientSize.h)
3786             this.rolledOver = true;
3787          Update(null);
3788       }
3789       else
3790       {
3791          this.dragging = false;
3792          OnLeftButtonUp(x, y, mods);
3793       }
3794       return result;
3795    }
3796
3797    bool OnLeftButtonDown(int x, int y, Modifiers mods)
3798    {
3799       return OnButtonDown(x,y, mods, false);
3800    }
3801
3802    bool OnLeftButtonUp(int x, int y, Modifiers mods)
3803    {
3804       if(resizingField && style.alwaysEdit)
3805       {
3806          Window::FreeMouseRange();
3807          ReleaseCapture();
3808          resizingField = null;
3809       }
3810
3811       if(dragRow || editRow)
3812       {
3813          DataRow row, switchRow = rows.last;
3814          int rowY = (style.header) ? rowHeight : 0;
3815          for(row = firstRowShown; row; row = row.GetNextRow())
3816          {
3817             rowY += rowHeight;
3818             if(rowY > y)
3819             {
3820                switchRow = row;
3821                break;
3822             }
3823          }
3824          if(this.editRow == switchRow && y >= 0)
3825          {
3826             // Find which field
3827             DataField field;
3828             int fieldX = 0;
3829             for(field = fields.first; field; field = field.next)
3830             {
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);
3833                fieldX += width;
3834
3835                if(fieldX > x + scroll.x)
3836                   break;
3837             }
3838
3839             if(field && field.editable)
3840             {
3841                // true: destroy edit box
3842                HideEditBox(true, true, false);
3843                PopupEditBox(field, false);
3844             }
3845             else if(!style.noDragging)
3846                NotifyReclick(master, this, currentRow, mods);
3847          }
3848          else if(style.moveRows && switchRow)
3849          {
3850             if(this.dragRow == switchRow && this.movedRow == false)
3851             {
3852                DataRow row = this.dragRow;
3853                DataRow selRow;
3854                if(!(mods.ctrl))
3855                {
3856                   for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3857                      selRow.selectedFlag = unselected;
3858                }
3859                else
3860                {
3861                   for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3862                   {
3863                      if(selRow != clickedRow)
3864                      {
3865                         if(selRow.selectedFlag == tempUnselected) selRow.selectedFlag = selected;
3866                         else if(selRow.selectedFlag == tempSelected) selRow.selectedFlag = unselected;
3867                      }
3868                   }
3869                }
3870                if(mods.ctrl)
3871                {
3872                   if(row.selectedFlag)
3873                      row.selectedFlag = tempUnselected;
3874                   else row.selectedFlag = tempSelected;
3875                }
3876                else
3877                   row.selectedFlag = tempSelected;
3878                clickedRow = row;
3879             }
3880             else
3881             {
3882                if(style.multiSelect)
3883                {
3884                   if(!switchRow.selectedFlag)
3885                   {
3886                      bool foundSwitch = false;
3887                      bool after = false;
3888                      DataRow next;
3889                      DataRow afterRow = switchRow.prev;
3890                      for(row = rows.first; row; row = next)
3891                      {
3892                         next = row.GetNextRow();
3893                         if(row.selectedFlag == selected || row.selectedFlag == tempSelected)
3894                         {
3895                            if(!foundSwitch && !after)
3896                            {
3897                               after = true;
3898                               afterRow = switchRow;
3899                            }
3900                            if(!after || !(row.selectedFlag == selected || row.selectedFlag == tempSelected) ||
3901                               !foundSwitch)
3902                            {
3903                               row.Move(afterRow);
3904                               afterRow = row;
3905                            }
3906                         }
3907                         else if(row == switchRow)
3908                            foundSwitch = true;
3909                      }
3910                   }
3911                }
3912                else
3913                {
3914                   for(row = rows.first; row; row = row.GetNextRow())
3915                   {
3916                      if(row == switchRow || row == this.dragRow)
3917                         break;
3918                   }
3919
3920                   // Switch row first: move before
3921                   if(row == switchRow)
3922                   {
3923                      if(NotifyMove(master, this, switchRow.prev, mods))
3924                         dragRow.Move(switchRow.prev);
3925                   }
3926                   // Dragged row first: move after
3927                   else
3928                   {
3929                      if(NotifyMove(master, this, switchRow, mods))
3930                         dragRow.Move(switchRow);
3931                   }
3932                }
3933             }
3934          }
3935          dragRow = null;
3936          editRow = null;
3937          movedRow = false;
3938          dropIndex = -1;
3939          Update(null);
3940       }
3941
3942       timer.Stop();
3943       if(this.dragging || style.freeSelect)
3944       {
3945          if(this.dragging)
3946          {
3947             this.rolledOver = this.dragging = false;
3948          }
3949          if(x >= 0 && y >= 0 && x < clientSize.w && y < clientSize.h && currentRow && style.freeSelect)
3950          {
3951             bool result;
3952             ReleaseCapture();
3953             result = NotifySelect(master, this, currentRow, mods);
3954             if(style.alwaysEdit)
3955                currentRow.Edit(currentField);
3956             return result;
3957
3958          }
3959          // if(!(style.freeSelect))
3960          ReleaseCapture();
3961       }
3962       return true;
3963    }
3964
3965    bool OnLeftDoubleClick(int x, int y, Modifiers mods)
3966    {
3967       int rowStart = -scroll.x;
3968       DataRow row;
3969       int rowY = (style.header) ? rowHeight : 0;
3970       int plusIndent = 0;
3971
3972       OnLeftButtonUp(x,y,mods);
3973       if(style.alwaysEdit)
3974       {
3975          if(!(style.collapse) || x > 15)
3976             if(editData && editData.visible)
3977             {
3978                editData.Activate();
3979                NotifyDoubleClick(master, this, x, y, mods);
3980                return false;
3981             }
3982       }
3983       for(row = firstRowShown; row; row = row.GetNextRow())
3984       {
3985          rowY += rowHeight;
3986          if(rowY > y || (style.multiSelect && !row.GetNextRow()))
3987          {
3988             if(style.treeBranch)
3989             {
3990                DataRow parent;
3991                for(parent = (style.rootCollapse) ? row.parent : (row.parent ? row.parent.parent : null); parent; parent = parent.parent)
3992                   if(!parent.header)
3993                      plusIndent += 20;
3994                plusIndent += 4;
3995             }
3996             break;
3997          }
3998       }
3999
4000       if((row && style.collapse && (x >= rowStart && x <= rowStart + 18 + plusIndent)) ||
4001          NotifyDoubleClick(master, this, x, y, mods))
4002       {
4003          if(style.collapse)
4004          {
4005             if(row && row.subRows.first)
4006             {
4007                row.collapsed = !row.collapsed;
4008                return false;
4009             }
4010          }
4011          // We need to return true here so that OnLeftButtonDown can popup the DataBox Editors
4012          return true;
4013       }
4014       return false;
4015    }
4016
4017    bool OnRightButtonDown(int x, int y, Modifiers mods)
4018    {
4019       return OnButtonDown(x,y, mods, true);
4020    }
4021
4022    bool OnRightButtonUp(int x, int y, Modifiers mods)
4023    {
4024       OnLeftButtonUp(x,y,mods);
4025       return NotifyRightClick(master, this, x, y, mods);
4026    }
4027
4028    bool GoToLetter(unichar ch, bool keyHit)
4029    {
4030       bool result = false;
4031       DataField field;
4032       bool checkNextField = true;
4033       int len = keyHit ? 0 : strlen(typedString);
4034
4035       typedString = renew typedString char[len + 2];
4036       typedString[len++] = (char)tolower(ch);         // TODO: FIX UNICODE
4037       typedString[len] = '\0';
4038
4039       for(field = fields.first; field; field = field.next)
4040       {
4041          DataRow startRow = currentRow ? currentRow : rows.first;
4042
4043          if(startRow && field.dataType && field.dataType._vTbl[__ecereVMethodID_class_OnGetString] && ch)
4044          {
4045             DataRow row, next;
4046             bool looped = false;
4047             if(len == 1 && currentRow)
4048                startRow = (next = startRow.GetNextRow(), (next ? next : rows.first));
4049
4050             for(row = startRow; row != startRow || !looped; next = row.GetNextRow(), row = next ? next : rows.first)
4051             {
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;
4056
4057                if(string && string[0])
4058                   checkNextField = false;
4059                if(string && string[0] && !strnicmp(string, typedString, len))
4060                {
4061                   if(style.multiSelect)
4062                   {
4063                      DataRow selRow;
4064
4065                      clickedRow = row;
4066                      for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
4067                         selRow.selectedFlag = unselected;
4068                      row.selectedFlag = selected;
4069                   }
4070                   SetCurrentRow(row, true);
4071                   result = true;
4072                   break;
4073                }
4074                looped = true;
4075             }
4076             typingTimer.Stop();
4077             if(this.typingTimeOut && !keyHit)
4078                typingTimer.Start();
4079             if(!result || !this.typingTimeOut || keyHit)
4080                typedString[len-1] = '\0';
4081          }
4082          if(!checkNextField || result) break;
4083       }
4084       return result;
4085    }
4086
4087    bool OnKeyDown(Key key, unichar ch)
4088    {
4089       DataField field;
4090
4091       if(key == enter || key == keyPadEnter)
4092       {
4093          if(editData && editData.visible && editData.active)
4094          {
4095             HideEditBox(true, false, false);
4096             return false;
4097          }
4098       }
4099       else if(key == escape)
4100       {
4101          if(resizingField || this.movingFields || (editData && editData.visible) || this.dragRow)
4102          {
4103             if(editData && editData.visible && style.alwaysEdit && !editData.active)
4104                return true;
4105             // false: dont destroy edit box
4106             HideEditBox(false, false, false);
4107             if(resizingField)
4108             {
4109                resizingField.width = this.startWidth;
4110                AdaptToFieldWidth(resizingField, true);
4111                resizingField = null;
4112                ReleaseCapture();
4113             }
4114             this.dragRow = null;
4115             if(this.dragging)
4116             {
4117                this.dragging = false;
4118                ReleaseCapture();
4119             }
4120
4121             this.movingFields = false;
4122             draggingField = null;
4123             Update(null);
4124             return false;
4125          }
4126       }
4127
4128       if(!currentField || !currentField.editable)
4129          for(field = fields.first; field; field = field.next)
4130          {
4131             if(field.editable)
4132             {
4133                currentField = field;
4134                break;
4135             }
4136          }
4137       if((key == f2 || (style.alwaysEdit && (key == ctrlV || key == ctrlC || key == ctrlX || key == shiftInsert || key == ctrlInsert || key == shiftDel))) &&
4138             currentField && currentField.editable)
4139       {
4140          PopupEditBox(currentField, false);
4141          if(editData && editData.visible)
4142          {
4143             if(style.alwaysEdit)
4144             {
4145                editData.Activate();
4146                if(key == ctrlV || key == ctrlC || key == ctrlX || key == shiftInsert || key == ctrlInsert || key == shiftDel)
4147                {
4148                   editData.OnKeyHit(key, ch);
4149                   StopEditing(true);
4150                }
4151                else
4152                   // For Installer to pop up file dialog
4153                   NotifyKeyDown(master, this, currentRow, key, ch);
4154             }
4155             return false;
4156          }
4157       }
4158
4159       if(!NotifyKeyDown(master, this, currentRow, key, ch))
4160          return false;
4161
4162       // Editable fields...
4163       if(currentField)
4164       {
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
4168       }
4169
4170       if(ch >=32 && ch != 128 && !key.alt && !key.ctrl && GoToLetter(ch, false))
4171       {
4172          /*if(inactive && window.state != Hidden)
4173             NotifyHighlight(master, this, currentRow, 0);
4174          else
4175          {
4176             NotifySelect(master, this, currentRow, 0);
4177          }*/
4178          return false;
4179       }
4180       return true;
4181    }
4182
4183    bool OnKeyHit(Key key, unichar ch)
4184    {
4185       if(!ch && !key.alt && !key.ctrl)
4186       {
4187          key.code = (SmartKey)key.code;
4188       }
4189       if(ch >= 32 && !key.alt && !key.ctrl && ch != 128)
4190       {
4191          DataField field;
4192          if(!currentField || !currentField.editable)
4193             for(field = fields.first; field; field = field.next)
4194             {
4195                if(field.editable)
4196                {
4197                   currentField = field;
4198                   break;
4199                }
4200             }
4201          if(currentField && currentField.editable)
4202          {
4203             if((!editData || !editData.visible) || !editData.active)
4204             {
4205                PopupEditBox(currentField, false);
4206                if(editData && editData.visible)
4207                {
4208                   editData.Activate();
4209                   editData.OnKeyHit(key, ch);
4210                }
4211             }
4212             return false;
4213          }
4214       }
4215
4216       if(editData && editData.visible && ch && !key.alt && !key.ctrl && editData.active && (key.code != tab || (editData._class == class(EditBox) && ((EditBox)editData).tabKey)))
4217          return false;
4218
4219       if(!key.alt && (style.multiSelect || !key.ctrl))
4220       {
4221          switch(key.code)
4222          {
4223             case left:
4224                if(style.alwaysEdit)
4225                {
4226                   if(currentField)
4227                   {
4228                      DataField field;
4229                      for(field = currentField.prev; field; field = field.prev)
4230                      {
4231                         if(field.editable)
4232                         {
4233                            currentField = field;
4234                            HideEditBox(true, true, false);
4235                            PopupEditBox(currentField, false);
4236                            return false;
4237                         }
4238                      }
4239                   }
4240                }
4241                if(style.collapse && currentRow /*&& !currentField*/)  // THIS PREVENTED COLLAPSING THE PROPERTY SHEET
4242                {
4243                   if(currentRow.subRows.first && !currentRow.collapsed)
4244                   {
4245                      currentRow.collapsed = true;
4246                   }
4247                   else if(currentRow.parent)
4248                      SetCurrentRow(currentRow.parent, true);
4249                   return false;
4250                }
4251                break;
4252             case right:
4253                if(style.collapse && currentRow && currentRow.subRows.first)
4254                {
4255                   if(currentRow.collapsed)
4256                      currentRow.collapsed = false;
4257                   else
4258                      SetCurrentRow(currentRow.subRows.first, true);
4259                   return false;
4260                }
4261                else if(style.alwaysEdit)
4262                {
4263                   if(currentField)
4264                   {
4265                      DataField field;
4266                      for(field = currentField.next; field; field = field.next)
4267                      {
4268                         if(field.editable)
4269                         {
4270                            currentField = field;
4271                            HideEditBox(true, true, false);
4272                            PopupEditBox(currentField, false);
4273                            break;
4274                         }
4275                      }
4276                   }
4277                }
4278                break;
4279             case down: case up:
4280             case pageDown: case pageUp:
4281             case end: case home:
4282             {
4283                int headerSize = ((style.header) ? rowHeight : 0);
4284                int height = clientSize.h + 1 - headerSize;
4285                DataRow oldRow;
4286
4287                // true: destroy edit box
4288                // !!! TESTING true HERE !!!
4289                HideEditBox(true, true, false);
4290                // HideEditBox(false, true, false);
4291
4292                oldRow = currentRow;
4293
4294                SNAPDOWN(height, rowHeight);
4295                if((!currentRow || key.code == home) && key.code != end)
4296                {
4297                   currentRow = rows.first;
4298                }
4299                else
4300                {
4301                   DataRow next;
4302                   switch(key.code)
4303                   {
4304                      case down:
4305                         if(!(style.multiSelect) && currentRow && !currentRow.selectedFlag)
4306                            next = currentRow;
4307                         else
4308                            next = currentRow.GetNextRow();
4309                         if(next)
4310                         {
4311                            currentRow = next;
4312                         }
4313                         break;
4314                      case up:
4315                         if(!(style.multiSelect) && currentRow && !currentRow.selectedFlag)
4316                            next = currentRow;
4317                         else
4318                            next = currentRow.GetPrevRow();
4319
4320                         if(next)
4321                         {
4322                            currentRow = next;
4323                         }
4324                         break;
4325                      case end:
4326                         currentRow = lastRow.GetLastRow();
4327                         break;
4328                      case pageUp:
4329                      {
4330                         int c;
4331                         for(c = 0;
4332                         currentRow && (next = currentRow.GetPrevRow()) && c < height / rowHeight;
4333                             c++, currentRow = next);
4334                         break;
4335                      }
4336                      case pageDown:
4337                      {
4338                         int c;
4339                         for(c = 0;
4340                             currentRow && (next = currentRow.GetNextRow()) && c < height / rowHeight;
4341                             c++, currentRow = next);
4342                         break;
4343                      }
4344                   }
4345                }
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);
4350
4351                if(style.multiSelect)
4352                {
4353                   DataRow selRow;
4354
4355                   if(!(key.shift) && (key.ctrl))
4356                   {
4357                      DataRow row;
4358                      for(row = rows.first; row; row = row.GetNextRow())
4359                      {
4360                         if(row.selectedFlag == tempSelected)
4361                            row.selectedFlag = selected;
4362                         else if(row.selectedFlag == tempUnselected)
4363                            row.selectedFlag = unselected;
4364                      }
4365                   }
4366
4367                   if(!(key.ctrl))
4368                   {
4369                      for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
4370                         selRow.selectedFlag = unselected;
4371                   }
4372                   else
4373                   {
4374                      for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
4375                      {
4376                         if(selRow.selectedFlag == tempUnselected) selRow.selectedFlag = selected;
4377                         else if(selRow.selectedFlag == tempSelected) selRow.selectedFlag = unselected;
4378                      }
4379                   }
4380
4381                   if(key.shift)
4382                   {
4383                      if(currentRow.index >= clickedRow.index)
4384                      {
4385                         for(selRow = clickedRow; selRow; selRow = selRow.GetNextRow())
4386                         {
4387                            if(key.ctrl)
4388                            {
4389                               if(selRow.selectedFlag) selRow.selectedFlag = tempUnselected; else selRow.selectedFlag = tempSelected;
4390                            }
4391                            else
4392                               selRow.selectedFlag = selected;
4393                            if(selRow == currentRow)
4394                               break;
4395                         }
4396                      }
4397                      else
4398                      {
4399                         for(selRow = currentRow; selRow; selRow = selRow.GetNextRow())
4400                         {
4401                            if(key.ctrl)
4402                            {
4403                               if(selRow.selectedFlag) selRow.selectedFlag = tempUnselected; else selRow.selectedFlag = tempSelected;
4404                            }
4405                            else
4406                               selRow.selectedFlag = selected;
4407                            if(selRow == clickedRow)
4408                               break;
4409                         }
4410                      }
4411                   }
4412                   else
4413                   {
4414                      if(!(key.ctrl) && currentRow)
4415                      {
4416                         currentRow.selectedFlag = selected;
4417                      }
4418
4419                      clickedRow = currentRow;
4420                   }
4421                }
4422                else
4423                {
4424                   if(oldRow) oldRow.selectedFlag = unselected;
4425                   if(currentRow) currentRow.selectedFlag = selected;
4426                }
4427
4428                if(currentRow)
4429                {
4430                   if(style.freeSelect)
4431                      NotifyHighlight(master, this, currentRow, 0);
4432                   else
4433                      NotifySelect(master, this, currentRow, 0);
4434
4435                   if(style.alwaysEdit && currentRow)
4436                      currentRow.Edit(currentField /*null*/);
4437                }
4438                Update(null);
4439                return false;
4440             }
4441             case space:
4442             {
4443                if(style.multiSelect && currentRow)
4444                {
4445                   if(currentRow.selectedFlag)
4446                   {
4447                      if(key.ctrl)
4448                         currentRow.selectedFlag = unselected;
4449                   }
4450                   else
4451                      currentRow.selectedFlag = selected;
4452                   Update(null);
4453
4454                   if(style.freeSelect)
4455                      NotifyHighlight(master, this, currentRow, 0);
4456                   else
4457                      NotifySelect(master, this, currentRow, 0);
4458                }
4459                break;
4460             }
4461          }
4462       }
4463
4464       if(!NotifyKeyHit(master, this, currentRow, key, ch))
4465          return false;
4466
4467       if(ch >=32 && ch != 128 && !key.alt && !key.ctrl && GoToLetter(ch, true))
4468       {
4469          /*if(inactive && window.state != Hidden)
4470             return NotifyHighlight(master, this, currentRow, 0);
4471          else
4472          {
4473             return NotifySelect(master, this, currentRow, 0);
4474          }*/
4475          return false;
4476       }
4477       return true;
4478    }
4479
4480    void OnHScroll(ScrollBarAction action, int position, Key key)
4481    {
4482       Update(null);
4483    }
4484
4485    void OnVScroll(ScrollBarAction action, int position, Key key)
4486    {
4487       int y = 0;
4488       DataRow next;
4489
4490       for(firstRowShown = rows.first; firstRowShown; firstRowShown = next)
4491       {
4492          next = firstRowShown.GetNextRow();
4493          if(y >= position || !next) break;
4494
4495          y += rowHeight;
4496       }
4497       Update(null);
4498    }
4499
4500    OldList fields;
4501    OldList rows;
4502    int numFields;
4503    DataRow firstRowShown;
4504    DataRow clickedRow;
4505    DataRow currentRow;
4506    int width;
4507    DataField sortField;
4508    int rowCount;
4509    int rowHeight;
4510    int fontH;
4511    public double typingTimeOut;
4512    char * typedString;
4513
4514    int mouseX, mouseY;
4515
4516    Timer timer
4517    {
4518       delay = 0.1;
4519       userData = this;
4520
4521       bool DelayExpired()
4522       {
4523          Modifiers mods { };
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);
4528
4529          return true;
4530       }
4531    };
4532
4533    Timer typingTimer
4534    {
4535       delay = 0.5; // typingTimeOut
4536       userData = this;
4537
4538       bool DelayExpired()
4539       {
4540          typedString[0] = '\0';
4541
4542          // The next line was commented... Why? When commented typing words stops working ( only first character jumps )
4543          typingTimer.Stop();
4544          return true;
4545       }
4546    };
4547
4548    bool dragging, rolledOver;
4549    int numSelections;
4550    Button endBevel;
4551
4552    // For moving rows
4553    DataRow dragRow;
4554    int dropIndex;
4555    bool movedRow;
4556
4557    // For editing fields
4558    DataBox editData;
4559    DataField currentField;
4560    DataRow editRow;
4561
4562    // For moving fields
4563    DataField draggingField, dropField;
4564    bool movingFields;
4565
4566    // For resizing fields
4567    DataField resizingField;
4568    int resizeX, oldX, startWidth;
4569
4570    ListBoxBits style;
4571    FontResource boldFont;
4572    int maxShown;
4573
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;
4578 };