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