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