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