ecere/gui/controls/ListBox: Attempts to improve row moving support for TreeViews
[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
1136 #ifdef _DEBUG
1137    private void CheckConsistency()
1138    {
1139 #if 0
1140       DataRow r;
1141       int index = 0;
1142       for(r = rows.first; r; r = r.GetNextRow())
1143       {
1144          if(r.index != index++)
1145             PrintLn("bug");
1146       }
1147 #endif
1148    }
1149 #endif
1150
1151    // Methods
1152    void AddField(DataField addedField)
1153    {
1154       if(this)
1155       {
1156          DataField field;
1157          if(fields.first && ((DataField)fields.first).defaultField)
1158          {
1159             DataField defaultField = fields.first;
1160             defaultField.Free();
1161             delete defaultField;
1162          }
1163          if(!addedField)
1164          {
1165             addedField = DataField { };
1166          }
1167
1168          incref addedField;
1169          addedField.listBox = this;
1170          fields.Add(addedField);
1171
1172          addedField.sortOrder = 1;
1173          addedField.index = numFields;
1174          numFields++;
1175          if(style.header)
1176          {
1177             addedField.headButton.Destroy(0);
1178             delete addedField.headButton;
1179             addedField.headButton = Button
1180             {
1181                this;
1182                stayOnTop = true;
1183                inactive = true;
1184                dontScrollVert = true;
1185                id = (int64)(intptr)addedField;
1186                text = addedField.header;
1187                bevel = (!guiApp.textMode && !style.clearHeader);
1188                ellipsis = true;
1189                alignment = addedField.alignment;
1190                NotifyPushed = HeaderPushed;
1191                NotifyClicked = HeaderClicked;
1192                NotifyDoubleClick = HeaderDoubleClicked;
1193                NotifyReleased = HeaderReleased;
1194                NotifyMouseMove = HeaderMouseMove;
1195             };
1196             incref addedField.headButton;
1197             addedField.headButton.Create();
1198
1199             if(guiApp.textMode)
1200                addedField.headButton.background = Color { 0, 170, 0 };
1201          }
1202          if(rows.first)
1203          {
1204             DataRow row;
1205             field = addedField;
1206             for(row = rows.first; row; )
1207             {
1208                int size = (field.dataType && field.dataType.typeSize) ?
1209                   (sizeof(class ListBoxCell) + field.dataType.typeSize - sizeof(void *)) : sizeof(class ListBoxCell);
1210                ListBoxCell cell = (ListBoxCell)new0 byte[size];
1211                row.cells.Add(cell);
1212                FillBytes(cell.data, 0, size - (uint)(uintptr)&((ListBoxCell)0).data);
1213                cell.isSet = false;
1214
1215                if(row.subRows.first)
1216                   row = row.subRows.first;
1217                else
1218                {
1219                   for(; row; row = row.parent)
1220                   {
1221                      if(row.next) { row = row.next; break; }
1222                   }
1223                }
1224             }
1225          }
1226          OnResize(clientSize.w, clientSize.h);
1227       }
1228    }
1229
1230    void ClearFields()
1231    {
1232       if(this)
1233       {
1234          DataField field;
1235          Clear();    // Ensure data is cleared first
1236          while((field = fields.first))
1237          {
1238             field.Free();
1239             delete field;
1240          }
1241          endBevel.visible = false;
1242          sortField = null;
1243       }
1244    }
1245
1246    void RemoveField(DataField field)
1247    {
1248       if(this)
1249       {
1250          if(field)
1251          {
1252             int index = field.index;
1253             DataRow row;
1254
1255             if(sortField == field)
1256                sortField = null;
1257
1258             for(row = rows.first; row; )
1259             {
1260                int c;
1261                ListBoxCell cell;
1262
1263                for(cell = row.cells.first, c = 0; c < index && cell; c++, cell = cell.next);
1264                if(cell && index == c)
1265                {
1266                   if(field.dataType)
1267                   {
1268                      // TOCHECK: Is this check good? Will need incref/decref sometime?
1269                      if(field.dataType.type == normalClass || field.dataType.type == noHeadClass)
1270                      {
1271                         if(cell.data[0] && field.freeData)
1272                            ((void (*)(void *, void *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnFree])(field.dataType, cell.data[0]);
1273                      }
1274                      else
1275                         ((void (*)(void *, void *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnFree])(field.dataType, cell.data);
1276                   }
1277
1278                   row.cells.Remove(cell);
1279                   delete cell;
1280                }
1281
1282                if(row.subRows.first)
1283                   row = row.subRows.first;
1284                else
1285                {
1286                   for(; row; row = row.parent)
1287                   {
1288                      if(row.next) { row = row.next; break; }
1289                   }
1290                }
1291             }
1292
1293             field.Free();
1294             delete field;
1295          }
1296          if(!fields.count)
1297             endBevel.visible = false;
1298       }
1299    }
1300
1301    DataRow AddRowNone()
1302    {
1303       DataRow row { noneRow = true };
1304       incref row;
1305       if(row)
1306       {
1307          DataRow search;
1308
1309          row.index = 0;
1310          rows.Insert(null, row);
1311          row.listBox = this;
1312
1313          for(search = row.GetNextRow(); search; search = search.GetNextRow())
1314             search.index++;
1315
1316          this.rowCount++;
1317          row.cells.Clear();
1318
1319          firstRowShown = row;
1320
1321          SetScrollArea(
1322             width,
1323             (rowCount * rowHeight) +
1324             ((style.header) ? rowHeight : 0) -
1325             ((!((clientSize.h+1) % rowHeight)) ? rowHeight : 0), true);
1326          if(style.autoScroll)
1327             SetScrollPosition(0, MAXINT - rowHeight);
1328          modifiedDocument = true;
1329          return row;
1330       }
1331       return null;
1332    }
1333
1334    DataRow AddRow()
1335    {
1336       if(this)
1337       {
1338          if(fields.first)
1339          {
1340             DataRow row { };
1341             incref row;
1342             if(row)
1343             {
1344                DataField field;
1345                int c;
1346
1347                // Find very last row
1348                {
1349                   DataRow lastRow;
1350                   for(lastRow = rows.last; lastRow && !lastRow.collapsed && lastRow.subRows.last; lastRow = lastRow.subRows.last);
1351                   row.index = lastRow ? (lastRow.index + 1) : 0;
1352                }
1353
1354                rows.Add(row);
1355                row.listBox = this;
1356                rowCount++;
1357
1358                row.cells.Clear();
1359                for(c = 0; c<fields.count; c++)
1360                {
1361                   for(field = fields.first; field; field = field.next)
1362                      if((int)field.index == c)
1363                         break;
1364                   if(field)
1365                   {
1366                      int size = (field.dataType && field.dataType.typeSize) ?
1367                         (sizeof(class ListBoxCell) + field.dataType.typeSize - sizeof(void *)) : sizeof(class ListBoxCell);
1368                      ListBoxCell cell = (ListBoxCell) new0 byte[size];
1369                      row.cells.Add(cell);
1370                      FillBytes(cell.data, 0, size - (uint)(uintptr)&((ListBoxCell)0).data);
1371                      cell.isSet = false;
1372                   }
1373                }
1374
1375                if(!firstRowShown)
1376                {
1377                   firstRowShown = row;
1378                }
1379
1380                if(rowHeight)
1381                   SetScrollArea(
1382                      width,
1383                      (rowCount * rowHeight) +
1384                      ((style.header) ? rowHeight : 0) -
1385                      ((!((clientSize.h+1) % rowHeight)) ? rowHeight : 0), true);
1386                if(style.autoScroll)
1387                   SetScrollPosition(0, MAXINT - rowHeight);
1388                modifiedDocument = true;
1389             }
1390 #ifdef _DEBUG
1391             CheckConsistency();
1392 #endif
1393             return row;
1394          }
1395       }
1396       return null;
1397    }
1398
1399    DataRow AddRowAfter(DataRow after)
1400    {
1401       if(fields.first)
1402       {
1403          DataRow row { };
1404          incref row;
1405          if(row)
1406          {
1407             DataRow search;
1408             DataField field;
1409             int c;
1410
1411             if(after && after.subRows.first && !after.collapsed)
1412             {
1413                for(search = after.subRows.last; !search.collapsed && search.subRows.last; )
1414                   search = search.subRows.last;
1415                row.index = search.index + 1;
1416             }
1417             else
1418                row.index = after ? (after.index + 1) : 0;
1419             rows.Insert(after, row);
1420             row.listBox = this;
1421
1422             for(search = row.GetNextRow(); search; search = search.GetNextRow())
1423                search.index++;
1424
1425             this.rowCount++;
1426             row.cells.Clear();
1427             for(c = 0; c<fields.count; c++)
1428             {
1429                for(field = fields.first; field; field = field.next)
1430                   if((int)field.index == c)
1431                      break;
1432                if(field)
1433                {
1434                   int size = (field.dataType && field.dataType.typeSize) ?
1435                      (sizeof(class ListBoxCell) + field.dataType.typeSize - sizeof(void *)) : sizeof(class ListBoxCell);
1436                   ListBoxCell cell = (ListBoxCell) new0 byte[size];
1437                   row.cells.Add(cell);
1438                   FillBytes(cell.data, 0, size - (uint)(uintptr)&((ListBoxCell)0).data);
1439                   cell.isSet = false;
1440                }
1441             }
1442             if(!firstRowShown || !after)
1443             {
1444                firstRowShown = row;
1445             }
1446
1447             SetScrollArea(
1448                width,
1449                (rowCount * rowHeight) +
1450                ((style.header) ? rowHeight : 0) -
1451                ((!((clientSize.h+1) % rowHeight)) ? rowHeight : 0), true);
1452             if(style.autoScroll)
1453                SetScrollPosition(0, MAXINT - rowHeight);
1454             modifiedDocument = true;
1455 #ifdef _DEBUG
1456             CheckConsistency();
1457 #endif
1458             return row;
1459          }
1460       }
1461       return null;
1462    }
1463
1464    DataRow AddStringf(const char * format, ...)
1465    {
1466       if(this)
1467       {
1468          DataRow row;
1469
1470          char string[MAX_F_STRING];
1471          va_list args;
1472
1473          va_start(args, format);
1474          vsnprintf(string, sizeof(string), format ? format : "", args);
1475          string[sizeof(string)-1] = 0;
1476          va_end(args);
1477
1478          row = AddRow();
1479          row.SetData(fields.first, string);
1480          return row;
1481       }
1482       return null;
1483    }
1484
1485    DataRow AddString(const char * string)
1486    {
1487       if(this)
1488       {
1489          DataRow row;
1490          row = AddRow();
1491          row.SetData(fields.first, string);
1492          return row;
1493       }
1494       return null;
1495    }
1496
1497    void SelectRow(DataRow row)
1498    {
1499       SetCurrentRow(row, true);
1500    }
1501
1502    void DeleteRow(DataRow row)
1503    {
1504       if(!row) row = currentRow;
1505       if(row)
1506       {
1507          DataRow sub, next, search;
1508          // Trying to move this here (Messed up deleting watches)
1509          //HideEditBox(false, false, true);
1510
1511          // Delete Sub Rows
1512          for(sub = row.subRows.first; sub; sub = next)
1513          {
1514             next = sub.next;
1515             DeleteRow(sub);
1516          }
1517
1518          if(row.parent.IsExpanded())
1519          {
1520             for(search = row.GetNextRow(); search; search = search.GetNextRow())
1521                search.index--;
1522             this.rowCount--;
1523          }
1524
1525          HideEditBox(false, false, true);
1526
1527          if(row == clickedRow)
1528          {
1529             clickedRow = row.GetNextRow();
1530             if(!clickedRow)
1531                clickedRow = row.GetPrevRow();
1532          }
1533
1534          if(row == currentRow)
1535          {
1536             DataRow newCurrentRow = row.GetNextRow();
1537             if(!newCurrentRow)
1538                newCurrentRow = row.GetPrevRow();
1539             SetCurrentRow(newCurrentRow, true);
1540          }
1541
1542          if(row == firstRowShown)
1543          {
1544             firstRowShown = row.GetPrevRow();
1545             if(!firstRowShown)
1546                firstRowShown = row.GetNextRow();
1547          }
1548
1549          (row.parent ? row.parent.subRows: rows).Remove(row);
1550          delete row;
1551
1552          //HideEditBox(false, false, true);
1553
1554          SetScrollArea(
1555             this.width,
1556             (this.rowCount * rowHeight) +
1557             ((style.header) ? rowHeight : 0) -
1558             ((!((clientSize.h+1) % rowHeight)) ? rowHeight : 0), true);
1559
1560          modifiedDocument = true;
1561
1562          Update(null);
1563 #ifdef _DEBUG
1564          CheckConsistency();
1565 #endif
1566       }
1567    }
1568
1569    DataRow FindRow(int64 tag)
1570    {
1571       if(this)
1572       {
1573          DataRow row = null;
1574          for(row = rows.first; row; row = row.next)
1575          {
1576             if(!row.noneRow && row.tag == tag)
1577                break;
1578          }
1579          return row;
1580       }
1581       return null;
1582    }
1583
1584    DataRow FindString(const char * searchedString)
1585    {
1586       DataField field;
1587       bool checkNextField = true;
1588
1589       for(field = fields.first; field; field = field.next)
1590       {
1591          if(field.dataType._vTbl[__ecereVMethodID_class_OnGetString])
1592          {
1593             DataRow row;
1594             for(row = rows.first; row; row = row.GetNextRow())
1595             {
1596                if(!row.noneRow)
1597                {
1598                   void * data = row.GetData(field);
1599                   char tempString[1024] = "";
1600                   bool needClass = false;
1601                   const char * string = ((const char *(*)(void *, void *, char *, void *, bool *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString])(field.dataType, data, tempString, null, &needClass);
1602
1603                   if(string && string[0])
1604                      checkNextField = false;
1605                   if(string && string[0] && !strcmp(string, searchedString))
1606                      return row;
1607                }
1608             }
1609          }
1610          if(!checkNextField) break;
1611       }
1612       return null;
1613    }
1614
1615    DataRow FindSubString(const char * subString)
1616    {
1617       DataField field;
1618       bool checkNextField = true;
1619       int len = subString ? strlen(subString) : 0;
1620
1621       if(len)
1622       {
1623          for(field = fields.first; field; field = field.next)
1624          {
1625             if(field.dataType._vTbl[__ecereVMethodID_class_OnGetString])
1626             {
1627                DataRow row;
1628                for(row = rows.first; row; row = row.GetNextRow())
1629                {
1630                   if(!row.noneRow)
1631                   {
1632                      void * data = row.GetData(field);
1633                      char tempString[1024] = "";
1634                      bool needClass = false;
1635                      const char * string = ((const char *(*)(void *, void *, char *, void *, bool *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString])(field.dataType, data, tempString, null, &needClass);
1636
1637                      if(string && string[0])
1638                         checkNextField = false;
1639                      if(string && string[0] && !strncmp(string, subString, len))
1640                         return row;
1641                   }
1642                }
1643             }
1644             if(!checkNextField) break;
1645          }
1646       }
1647       return null;
1648    }
1649
1650    DataRow FindSubStringi(const char * subString)
1651    {
1652       DataField field;
1653       bool checkNextField = true;
1654       int len = subString ? strlen(subString) : 0;
1655       DataRow result = null;
1656       const char * bestResult = null;
1657       int bestLen = 0;
1658
1659       if(len)
1660       {
1661          for(field = fields.first; field; field = field.next)
1662          {
1663             if(field.dataType._vTbl[__ecereVMethodID_class_OnGetString])
1664             {
1665                DataRow row;
1666                for(row = rows.first; row; row = row.GetNextRow())
1667                {
1668                   if(!row.noneRow)
1669                   {
1670                      void * data = row.GetData(field);
1671                      char tempString[1024] = "";
1672                      bool needClass = false;
1673                      const char * string = ((const char *(*)(void *, void *, char *, void *, bool *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString])(field.dataType, data, tempString, null, &needClass);
1674
1675                      if(string && string[0])
1676                         checkNextField = false;
1677                      if(string && string[0])
1678                      {
1679                         int stringLen = strlen(string);
1680
1681                         if(!strnicmp(string, subString, Min(len, stringLen)))
1682                         {
1683                            if(bestLen < Min(len, stringLen))
1684                               bestResult = 0;
1685                            if(!bestResult || strcmpi(string, bestResult) < 0)
1686                            {
1687                               bestLen = Min(len, stringLen);
1688                               bestResult = string;
1689                               result = row;
1690                            }
1691                         }
1692                      }
1693                   }
1694                }
1695             }
1696             if(!checkNextField) break;
1697          }
1698       }
1699       return result;
1700    }
1701
1702    DataRow FindSubStringAfter(DataRow after, const char * subString)
1703    {
1704       DataField field;
1705       bool checkNextField = true;
1706       int len = subString ? strlen(subString) : 0;
1707
1708       if(len)
1709       {
1710          for(field = fields.first; field; field = field.next)
1711          {
1712             if(field.dataType._vTbl[__ecereVMethodID_class_OnGetString])
1713             {
1714                DataRow row;
1715                for(row = after.GetNextRow(); row && row != after; row = row.GetNextRow(), (!row) ? (row = rows.first) : null)
1716                {
1717                   if(!row.noneRow)
1718                   {
1719                      void * data = row.GetData(field);
1720                      char tempString[1024] = "";
1721                      bool needClass = false;
1722                      const char * string = ((const char *(*)(void *, void *, char *, void *, bool *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString])(field.dataType, data, tempString, null, &needClass);
1723
1724                      if(string && string[0])
1725                         checkNextField = false;
1726                      if(string && string[0] && !strncmp(string, subString, len))
1727                         return row;
1728                   }
1729                }
1730             }
1731             if(!checkNextField) break;
1732          }
1733       }
1734       return null;
1735    }
1736
1737    DataRow FindSubRow(int64 tag)
1738    {
1739       if(this)
1740       {
1741          DataRow row = null;
1742
1743          for(row = rows.first; row; row = row.next)
1744          {
1745             if(!row.noneRow && row.tag == tag)
1746                break;
1747             if(!row.noneRow && row.subRows.first)
1748             {
1749                DataRow subRow = row.FindSubRow(tag);
1750                if(subRow)
1751                   return subRow;
1752             }
1753          }
1754          return row;
1755       }
1756       return null;
1757    }
1758
1759    void Clear()
1760    {
1761       if(this)
1762       {
1763          Window master = this.master;
1764
1765          HideEditBox(false, true, false);
1766          editData.Destroy(0);
1767
1768          firstRowShown = currentRow = null;
1769          ClearEx();
1770
1771          if(master)
1772          {
1773             if(style.freeSelect)
1774                NotifyHighlight(master, this, currentRow ? currentRow : null, 0);
1775             else
1776                NotifySelect(master, this, currentRow ? currentRow : null, 0);
1777          }
1778
1779          if(style.alwaysEdit && currentRow)
1780             currentRow.Edit(currentField);
1781
1782
1783          this.rowCount = 0;
1784
1785          SetScrollArea(
1786                this.width,
1787                (this.rowCount * rowHeight) +
1788                ((style.header) ? rowHeight : 0) -
1789                ((rowHeight && !((clientSize.h+1) % rowHeight)) ? rowHeight : 0), true);
1790          Update(null);
1791       }
1792    }
1793
1794    void Sort(DataField field, int order)
1795    {
1796       if(this)
1797       {
1798          DataRow search;
1799          int headerSize = ((style.header) ? rowHeight : 0);
1800          int height = clientSize.h + 1 - headerSize;
1801
1802          if(!field) field = fields.first;
1803          sortField = field;
1804          field.sortOrder = order ? order : 1;
1805          rows.Sort(DataRow::Compare, field);
1806
1807          for(search = rows.first; search; search = search.next)
1808             search._SortSubRows(field, order);
1809
1810          {
1811             int index = 0;
1812             for(search = rows.first; search; search = search.GetNextRow())
1813                search.index = index++;
1814          }
1815
1816          if(currentRow && currentRow.index * rowHeight > scroll.y + height - rowHeight)
1817             SetScrollPosition(scroll.x, currentRow.index * rowHeight - height + rowHeight);
1818          else if(!currentRow || currentRow.index * rowHeight < scroll.y)
1819             SetScrollPosition(scroll.x, currentRow ? currentRow.index * rowHeight : 0);
1820
1821          OnVScroll(0, scroll.y, 0);
1822
1823          // SetScrollPosition(0, scroll.y);
1824          // Update(null);
1825       }
1826    }
1827
1828    void StopEditing(bool save)
1829    {
1830       HideEditBox(save, false, true);
1831    }
1832
1833    void GetMultiSelection(OldList list)
1834    {
1835       list.Clear();
1836       if(this && style.multiSelect)
1837       {
1838          DataRow row;
1839
1840          for(row = rows.first; row; row = row.GetNextRow())
1841          {
1842             if(row.selectedFlag == selected || row.selectedFlag == tempSelected)
1843             {
1844                list.Add(OldLink { data = row });
1845             }
1846          }
1847       }
1848    }
1849
1850    // Convenience Current Row Methods
1851    void * SetData(DataField field, any_object data)
1852    {
1853       return currentRow.SetData(field, data);
1854    }
1855
1856    any_object GetData(DataField field)
1857    {
1858       return (void *)currentRow.GetData(field);
1859    }
1860
1861    int64 GetTag()
1862    {
1863       return currentRow ? currentRow.tag : 0;
1864    }
1865
1866 private:
1867    ListBox()
1868    {
1869       DataField defaultField { };
1870       rows.offset = (uint)(uintptr)&((DataRow)0).prev;
1871       fields.offset = (uint)(uintptr)&((DataField)0).prev;
1872       style.fullRowSelect = true;
1873       style.fillLastField = true;
1874       style.expandOnAdd = true;
1875       typingTimeOut = 0.5;
1876       rowHeight = 16;   // Stuff depending on creation and default property checking
1877       maxShown = 10;
1878
1879       defaultField.defaultField = true;
1880
1881       AddField(defaultField);
1882
1883       typedString = new char[1];
1884       typedString[0] = '\0';
1885       dropIndex = -1;
1886       return true;
1887    }
1888
1889    ~ListBox()
1890    {
1891       DataField field;
1892
1893       delete editData;
1894       delete typedString;
1895       delete endBevel;
1896
1897       ClearEx();
1898
1899       while((field = fields.first))
1900       {
1901          // fields.Remove(field);
1902          field.Free();
1903          delete field;
1904       }
1905    }
1906
1907    void ClearEx()
1908    {
1909       DataRow row;
1910       clickedRow = null;
1911       while((row = rows.first))
1912       {
1913          rows.Remove(row);
1914          delete row;
1915       }
1916    }
1917
1918    ListBoxCell GetCell(DataRow * row, DataField * field)
1919    {
1920       ListBoxCell cell = null;
1921       if(!*row) *row = currentRow;
1922       if(*row)
1923       {
1924          if(!*field) *field = this ? currentField : null;
1925          if(!*field && this)
1926          {
1927             for(*field = fields.first; (*field).index != 0; *field = (*field).next);
1928          }
1929          if(*field)
1930          {
1931             uint index;
1932             if(field->listBox == this)
1933             {
1934                for(index = 0, cell = (*row).cells.first; cell && index != (*field).index; index++, cell = cell.next);
1935             }
1936          }
1937       }
1938       return cell;
1939    }
1940
1941    void HideEditBox(bool save, bool alwaysStopEdit, bool repositionOnly)
1942    {
1943       if(editData && editData.visible)
1944       {
1945          if(save)
1946             editData.SaveData();
1947
1948          editData.visible = false;
1949          NotifyEditDone(master, this, currentRow);
1950
1951          // ENSURE DATA BOX IS NOT VISIBLE
1952          editData.visible = false;
1953
1954          if(style.alwaysEdit && !alwaysStopEdit)
1955          {
1956             /*
1957             int height = rowHeight - (style.alwaysEdit ? 1 : 0);
1958             int y = currentRow.index * rowHeight + (style.header ? rowHeight : 0);
1959             int x = currentField.x;
1960             int width = (!currentField.next && style.fillLastField && (!hasHorzScroll || clientSize.w - currentField.x > currentField.width + EXTRA_SPACE)) ?
1961                   clientSize.w - currentField.x : (currentField.width + EXTRA_SPACE);
1962
1963             if(!style.alwaysEdit)
1964             {
1965                editData.position = { x, y };
1966                editData.size = { width, height };
1967             }
1968             else
1969             {
1970                editData.position = { x, y - editData.clientStart.y };
1971                editData.size = { width, height + editData.clientStart.y * 2 };
1972             }
1973             editData.visible = true;
1974             if(style.alwaysEdit)
1975                editData.Deactivate();
1976             */
1977             PopupEditBox(currentField, repositionOnly);
1978          }
1979
1980          /*else
1981             currentField = null;*/
1982       }
1983    }
1984
1985    void SetCurrentRow(DataRow row, bool notify)
1986    {
1987       if(this && (currentRow != row || (currentRow && currentRow.selectedFlag == unselected)))
1988       {
1989          int headerSize = ((style.header) ? rowHeight : 0);
1990          int height = clientSize.h + 1 - headerSize;
1991
1992          // true: destroy edit box
1993          HideEditBox(true, true, false);
1994
1995          if(!(style.multiSelect) && currentRow)
1996             currentRow.selectedFlag = unselected;
1997
1998          currentRow = row;
1999
2000          if(style.multiSelect)
2001          {
2002             DataRow selRow;
2003
2004             for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
2005                selRow.selectedFlag = unselected;
2006             if(currentRow)
2007                currentRow.selectedFlag = selected;
2008
2009             clickedRow = row;
2010          }
2011          else if(currentRow)
2012             currentRow.selectedFlag = selected;
2013
2014          if(currentRow && currentRow.index * rowHeight > scroll.y + height - rowHeight)
2015             SetScrollPosition(scroll.x,
2016                currentRow.index * rowHeight - height + rowHeight);
2017          else if(!currentRow || currentRow.index * rowHeight < scroll.y)
2018          {
2019             int line = currentRow ? currentRow.index * rowHeight : 0;
2020             //SNAPUP(line, rowHeight);
2021             SetScrollPosition(scroll.x, line);
2022          }
2023
2024          if(notify)
2025          {
2026             Window master = this.master;
2027             if(master)
2028             {
2029                if(style.freeSelect && visible)
2030                   NotifyHighlight(master, this, currentRow ? currentRow : null, 0);
2031                else
2032                   NotifySelect(master, this, currentRow ? currentRow : null, 0);
2033                if(style.alwaysEdit && currentRow)
2034                   currentRow.Edit(currentField);
2035             }
2036          }
2037
2038          Update(null);
2039       }
2040    }
2041
2042    void RepositionFieldEditor()
2043    {
2044       if(editData && editData.visible)
2045       {
2046          int height = rowHeight - (style.alwaysEdit ? 1 : 0);
2047          int x = 0;
2048          int y = currentRow.index * rowHeight + (style.header ? rowHeight : 0);
2049          int width = 0;
2050          DataField field;
2051
2052          if(style.collapse && !(style.treeBranch))
2053             x += 15;
2054          for(field = fields.first; field; field = field.next)
2055          {
2056             width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ?
2057                clientSize.w - field.x : (field.width + EXTRA_SPACE);
2058             if(field == currentField) break;
2059             x += width;
2060          }
2061          if(!style.alwaysEdit)
2062          {
2063             editData.position = { x, y - editData.clientStart.y };
2064             editData.size = { width, height + editData.clientStart.y * 2 };
2065          }
2066          else
2067          {
2068             editData.position = { x, y };
2069             editData.size = { width, height };
2070          }
2071       }
2072    }
2073
2074    void PopupEditBox(DataField whichField, bool repositionOnly)
2075    {
2076       if((!editData || !editData.visible || currentField != whichField) && currentRow)
2077       {
2078          // true: destroy edit box
2079          HideEditBox(true, true, false);
2080          if(whichField)
2081          {
2082             int height = rowHeight - (style.alwaysEdit ? 1 : 0);
2083             int x = 0;
2084             int y = currentRow.index * rowHeight + (style.header ? rowHeight : 0);
2085             int width = 0;
2086             //void * data = currentRow.GetData(whichField);
2087             DataField field;
2088             DataRow row = null;
2089             ListBoxCell cell;
2090
2091             if(style.collapse && !(style.treeBranch))
2092                x += 15;
2093
2094             for(field = fields.first; field; field = field.next)
2095             {
2096                width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ?
2097                   clientSize.w - field.x : (field.width + EXTRA_SPACE);
2098                if(field == whichField) break;
2099                x += width;
2100             }
2101
2102             currentField = whichField;
2103             cell = GetCell(&row, &currentField);
2104
2105             if(!editData)
2106             {
2107                editData = DataBox
2108                {
2109                   this;
2110                   background = dataBoxBackground;
2111                   foreground = dataBoxForeground;
2112
2113                   bool NotifyChanged(DataBox dataBox, bool closingDropDown)
2114                   {
2115                      DataRow row = null;
2116                      DataField field = null;
2117                      ListBoxCell cell = GetCell(&row, &field);
2118                      if(cell)
2119                      {
2120                         cell.isSet = true;
2121                         modifiedDocument = true;
2122                         Update(null);
2123                         NotifyChanged(master, this, currentRow);
2124                      }
2125                      return true;
2126                   }
2127
2128                   bool NotifyModified()
2129                   {
2130                      //DataRow row = null;
2131                      //DataField field = null;
2132                      //ListBoxCell cell = GetCell(&row, &field);
2133                      //cell.isSet = true;
2134                      modifiedDocument = true;
2135                      //Update(null);
2136                      NotifyModified(master, this, currentRow);
2137                      return true;
2138                   }
2139
2140                   bool OnKeyDown(Key key, unichar ch)
2141                   {
2142                      bool result = DataBox::OnKeyDown(key, ch);
2143                      if(visible && active)   // Added this check here, because we will not use enter/escape otherwise, and lose DataBox's result
2144                      {
2145                         if((SmartKey)key == enter || (SmartKey)key == escape)
2146                            return true;
2147                      }
2148                      return result;
2149                   }
2150                };
2151                incref editData;
2152             }
2153             else
2154                editData.Destroy(0);
2155             editData.type = whichField.dataType;
2156             editData.fieldData = whichField.userData;
2157             editData.borderStyle = style.alwaysEdit ? 0 : deep;
2158             editData.data = cell ? cell.data : null;
2159
2160             if(!repositionOnly)
2161                // Might not really need this anymore...
2162                NotifyEditing(master, this, currentRow);
2163
2164             editData.Create();
2165             if(!style.alwaysEdit)
2166             {
2167                editData.position = { x, y - editData.clientStart.y };
2168                editData.size = { width, height + editData.clientStart.y * 2 };
2169             }
2170             else
2171             {
2172                editData.position = { x, y };
2173                editData.size = { width, height };
2174             }
2175             if(!repositionOnly)
2176                editData.Refresh();
2177             editData.visible = true;
2178
2179             if(style.alwaysEdit)
2180                editData.Deactivate();
2181
2182             //   MOVED THIS HIGHER FOR DATALIST EDITOR
2183             if(!repositionOnly)
2184                // Might not really need this anymore...
2185                NotifyEdited(master, this, currentRow);
2186          }
2187       }
2188    }
2189
2190    void OnRedraw(Surface surface)
2191    {
2192       DataRow row;
2193       int y = (style.header) ? rowHeight : 0;
2194       bool isActive = active;
2195       Font font = fontObject;
2196       Font boldFont = this.boldFont.font;
2197
2198
2199       // Draw gray grid
2200       if(style.alwaysEdit && style.fullRowSelect)
2201       {
2202          // Horizontal lines
2203          int y = (style.header) ? rowHeight : 0;
2204          int x = -scroll.x - 1;// + EXTRA_SPACE;// / 2 - 1;
2205          int w = clientSize.w;
2206          int h = clientSize.h;
2207          DataRow row;
2208          DataField field;
2209
2210          // Fill out indent column
2211          if(style.collapse && !(style.treeBranch) && (style.header || rows.first))
2212          {
2213             x += 15;
2214             surface.SetBackground(formColor);
2215             surface.Area(-scroll.x, 0, x, clientSize.h);
2216          }
2217
2218          surface.SetForeground(formColor);
2219          for(row = firstRowShown; row; row = row.GetNextRow())
2220          {
2221             y += rowHeight;
2222             surface.HLine(x + 1, w-1, y-1);
2223             if(y >= h)
2224                break;
2225          }
2226
2227          // Vertical lines
2228          for(field = fields.first; field; field = field.next)
2229          {
2230             int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ?
2231                clientSize.w - field.x : (field.width + EXTRA_SPACE);
2232             if(field.prev && y > 0)
2233                surface.VLine(0, y-1, x);
2234             x += width;
2235          }
2236       }
2237
2238       surface.foreground = this.foreground;
2239       surface.TextOpacity(false);
2240
2241       // Draw the tree branches
2242       if(style.treeBranch)
2243       {
2244          int y = -scroll.y + ((style.header) ? rowHeight : 0);
2245          surface.LineStipple(0x5555);
2246          surface.SetForeground(branchesColor);
2247          for(row = rows.first; row; row = row.GetNextRow() )
2248          {
2249             int x = -scroll.x + EXTRA_SPACE / 2-1;
2250             int rowStart = -scroll.x;
2251             int indent = 0;
2252             DataRow parent;
2253             int plusIndent;
2254
2255             for(parent = row.parent; parent; parent = parent.parent)
2256                if(!parent.header) indent += 20;
2257             if(style.rootCollapse) indent += 20;
2258
2259             plusIndent = (style.treeBranch) ? (indent - 20 + 4) : indent;
2260
2261             x += indent;
2262
2263             // Vertical line
2264             if(row.subRows.first)
2265             {
2266                int numRows = 0;
2267                int y1 = y + PLUSY + 4;
2268                int y2;
2269                DataRow child;
2270
2271                for(child = row.collapsed ? null : row.subRows.first; child && child != row; )
2272                {
2273                   numRows++;
2274                   if(child.subRows.first && !child.collapsed && child != row.subRows.last)
2275                      child = child.subRows.first;
2276                   else if(child.next)
2277                      child = child.next;
2278                   else
2279                   {
2280                      for(; child && child != row; child = child.parent)
2281                      {
2282                         if(child.next)
2283                         {
2284                            child = child.next;
2285                            break;
2286                         }
2287                      }
2288                   }
2289                }
2290                y2 = y + numRows * rowHeight + PLUSY + 4;
2291                surface.VLine(y1, y2, rowStart + plusIndent + 7 + 20);
2292             }
2293             surface.HLine(rowStart + plusIndent + 7, rowStart + indent, y + PLUSY + 4);
2294
2295             y += rowHeight;
2296             if(y >= clientSize.h)
2297                break;
2298          }
2299          // Root Vertical Lines
2300          if(style.rootCollapse && rows.first)
2301          {
2302             int numRows = 0;
2303             int y1, y2;
2304             DataRow child;
2305             y = -scroll.y + ((style.header) ? rowHeight : 0);
2306             y1 = y + PLUSY + 4;
2307             for(child = rows.first; child && child != rows.last; )
2308             {
2309                numRows++;
2310                if(child.subRows.first && !child.collapsed && child != rows.last)
2311                   child = child.subRows.first;
2312                else if(child.next)
2313                   child = child.next;
2314                else
2315                {
2316                   for(; child; child = child.parent)
2317                   {
2318                      if(child.next)
2319                      {
2320                         child = child.next;
2321                         break;
2322                      }
2323                   }
2324                }
2325             }
2326             y2 = y + numRows * rowHeight + PLUSY + 4;
2327             surface.VLine(y1, y2, -scroll.x + 11);
2328          }
2329          surface.LineStipple(0);
2330       }
2331
2332       for(row = firstRowShown; row; row = row.GetNextRow() )
2333       {
2334          int x = -scroll.x + EXTRA_SPACE / 2-1;
2335          DataField field;
2336          ListBoxCell cell;
2337          Color foreground = this.foreground /*black*/, background = this.background /*white*/;
2338          DataDisplayFlags dataDisplayFlags = 0;
2339          int rowStart = -scroll.x;
2340          int indent = 0;
2341          DataRow parent;
2342          Bitmap icon = row.icon ? row.icon.bitmap : null;
2343          int collapseRowStart = 0;
2344          bool lastWasHeader = row.header;
2345
2346          for(parent = row.parent; parent; parent = parent.parent)
2347          {
2348             if(!parent.header || lastWasHeader)
2349             {
2350                if(style.treeBranch)
2351                   indent += 20;
2352                else
2353                   indent += 15;
2354             }
2355          }
2356          if(style.rootCollapse) indent += 20;
2357          x += indent;
2358
2359          dataDisplayFlags.fullRow = style.fullRowSelect;
2360          dataDisplayFlags.active = isActive;
2361          dataDisplayFlags.header = row.header;
2362
2363          surface.Clip(null);
2364          if(style.collapse)
2365          {
2366             collapseRowStart = rowStart;
2367
2368             if(!(style.treeBranch))
2369             {
2370                x += 15;
2371                rowStart += 15;
2372             }
2373          }
2374
2375          if(style.multiSelect)
2376          {
2377             dataDisplayFlags.selected = row.selectedFlag == selected || row.selectedFlag == tempSelected;
2378             dataDisplayFlags.current = row == currentRow || (!currentRow && row == firstRowShown);
2379          }
2380          else
2381          {
2382             dataDisplayFlags.selected = row.selectedFlag == selected || row.selectedFlag == tempSelected;
2383             dataDisplayFlags.current = row == currentRow || (!currentRow && row == firstRowShown);
2384             /*
2385             if(row == currentRow)
2386             {
2387                dataDisplayFlags.current = true;
2388                dataDisplayFlags.selectedFlag = true;
2389             }
2390             else if(!currentRow && row == firstRowShown)
2391             {
2392                dataDisplayFlags.current = true;
2393             }*/
2394          }
2395
2396          surface.TextOpacity(true);
2397
2398          background = this.background;
2399          foreground = this.foreground;
2400
2401          // Draw the current row background
2402          if(row.header)
2403          {
2404             Color colors[] = { formColor, azure, mistyRose, linen, floralWhite, lavender, lavenderBlush, lemonChiffon };
2405             int level = 0;
2406             DataRow p = row;
2407             while((p = p.parent)) level++;
2408             background = colors[level % (sizeof(colors)/sizeof(colors[0]))];
2409             surface.SetBackground(background);
2410             surface.Area(rowStart, y, clientSize.w, (y + rowHeight) - 1);
2411             foreground = branchesColor;
2412          }
2413          else if(dataDisplayFlags.selected)
2414          {
2415             if(dataDisplayFlags.selected && (isActive || style.alwaysHL || (style.alwaysEdit && style.fullRowSelect)))
2416             {
2417                if(!isActive && style.alwaysEdit)
2418                   background = formColor;
2419                else
2420                   background = selectionColor ? selectionColor : SELECTION_COLOR;
2421                if(style.fullRowSelect)
2422                {
2423                   int offset = (style.alwaysEdit) ? 2 : 1;
2424                   surface.SetBackground(background);
2425                   surface.Area(rowStart, y, clientSize.w, (y + rowHeight) - offset);
2426                }
2427                if(isActive || !(style.alwaysEdit))
2428                   foreground = selectionText ? selectionText : SELECTION_TEXT;
2429                else
2430                   foreground = branchesColor;
2431             }
2432          }
2433
2434          if(icon)
2435          {
2436             surface.Blit(icon, x + (20 - icon.width) /2,y + 2,0,0, icon.width, icon.height);
2437             x += 20;
2438          }
2439
2440          if(row.noneRow)
2441          {
2442             int width = clientSize.w;
2443             Box clip;
2444             dataDisplayFlags.firstField = true;
2445             clip.left = x - EXTRA_SPACE / 2+1;
2446             clip.top = y;
2447             clip.right = x + width - EXTRA_SPACE/2 - 0;
2448             clip.bottom = y + rowHeight - 1;
2449             surface.Clip(&clip);
2450
2451             surface.TextFont(font);
2452
2453             surface.SetForeground(foreground);
2454             surface.SetBackground(background);
2455
2456             ((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);
2457          }
2458          else
2459          {
2460             if(opacity < 1) surface.TextOpacity(false);
2461             // Draw the rows
2462             for(field = fields.first; field; field = field.next)
2463             {
2464                uint index;
2465                int width = ((!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) || row.header) ?
2466                   clientSize.w - field.x : (field.width + EXTRA_SPACE);
2467
2468                // Box clip = { x, y+1, x + field.width - EXTRA_SPACE - 1, y + rowHeight - 2 };
2469                Box clip;
2470
2471                //width -= EXTRA_SPACE;
2472
2473                if(!field.prev) width -= indent;
2474
2475
2476                dataDisplayFlags.firstField = field.prev ? false : true;
2477
2478                if(!dataDisplayFlags.firstField && !dataDisplayFlags.fullRow)
2479                {
2480                   background = this.background;
2481                   foreground = this.foreground;
2482                }
2483
2484                if(!isActive && dataDisplayFlags.selected && style.alwaysEdit && field.editable && opacity)
2485                {
2486                   surface.Clip(null);
2487                   surface.SetBackground(background);
2488                   surface.Area(x-3, y, x+width-1, y + rowHeight-2);
2489                }
2490
2491                clip.left = x - EXTRA_SPACE / 2+1;
2492                clip.top = y;
2493                clip.right = x + width - EXTRA_SPACE/2 - 0;
2494                clip.bottom = y + rowHeight - 1;
2495                surface.Clip(&clip);
2496
2497                for(index = 0, cell = row.cells.first; cell && index != field.index; index++, cell = cell.next);
2498                // Should always be as many cells in the row as fields in the listbox
2499                if(cell && cell.isSet && field.dataType)
2500                {
2501                   if(row.header)
2502                      surface.TextFont(boldFont);
2503                   else
2504                      surface.TextFont(font);
2505
2506                   surface.SetForeground(foreground);
2507                   surface.SetBackground(background);
2508
2509                   if(field.dataType.type == noHeadClass || field.dataType.type == normalClass)
2510                      ((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);
2511                   else
2512                      ((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);
2513                }
2514
2515                if(!isActive && dataDisplayFlags.selected && style.alwaysEdit && field.editable)
2516                   background = formColor;
2517
2518                if(!dataDisplayFlags.firstField && !dataDisplayFlags.fullRow)
2519                {
2520                   background = formColor;
2521                   foreground = this.background;
2522                }
2523
2524 #ifdef _DEBUG
2525                // surface.WriteTextf(x + 100, y,  "ix: %d", row.index);
2526 #endif
2527
2528                x += width;// + EXTRA_SPACE;
2529
2530                if(row.header) break;
2531             }
2532          }
2533
2534          if(style.collapse)
2535          {
2536             int plusIndent = (style.treeBranch) ? (indent - 20 + 4) : indent;
2537             surface.Clip(null);
2538             if(row.subRows.first && (row.parent || !(style.treeBranch) || (style.rootCollapse)))
2539             {
2540                surface.SetForeground(row.header ? headerCollapseForeground : this.foreground);
2541                surface.Rectangle(collapseRowStart + 3 + plusIndent, y + PLUSY, collapseRowStart + 11 + plusIndent, y + PLUSY + 8);
2542
2543                surface.SetBackground(row.header ? (formColor) : (this.background)); //white
2544                surface.Area(collapseRowStart + 4 + plusIndent, y + PLUSY + 1, collapseRowStart + 10 + plusIndent, y + PLUSY + 7);
2545
2546                surface.HLine(collapseRowStart + 5 + plusIndent, collapseRowStart + 9 + plusIndent, y+PLUSY+4);
2547                if(row.collapsed)
2548                   surface.VLine(y + PLUSY + 2, y + PLUSY + 6, collapseRowStart + 7 + plusIndent);
2549             }
2550
2551          }
2552
2553          // Draw the current row stipple
2554          if(style.fullRowSelect && !(style.alwaysEdit) && (dataDisplayFlags.current) && isActive)
2555          {
2556             surface.Clip(null);
2557             if(isActive)
2558             {
2559                surface.LineStipple(0x5555);
2560                if(dataDisplayFlags.selected)
2561                   surface.SetForeground(stippleColor);
2562                else
2563                   surface.SetForeground(this.foreground);
2564             }
2565             else
2566                surface.SetForeground(selectionColor ? selectionColor : SELECTION_COLOR);
2567             surface.Rectangle(0, y, clientSize.w-1, (y + rowHeight) - 1);
2568             surface.LineStipple(0);
2569          }
2570
2571          y += rowHeight;
2572          if(y >= clientSize.h)
2573             break;
2574       }
2575       if(firstRowShown) surface.Clip(null);
2576       if(this.dragRow && this.dropIndex != -1)
2577       {
2578          int dropIndex = this.dropIndex;
2579          int y;
2580
2581          if(!style.multiSelect && currentRow.index < this.dropIndex)
2582             dropIndex++;
2583          surface.DrawingChar(223);
2584
2585          y = style.header ? rowHeight : 0;
2586          y += dropIndex * rowHeight - scroll.y;
2587
2588          surface.SetForeground(Color { 85, 85, 255 });
2589          surface.HLine(0, clientSize.w-1, y);
2590          surface.HLine(0, clientSize.w-1, y + 1);
2591       }
2592    }
2593
2594    void OnDrawOverChildren(Surface surface)
2595    {
2596       if(draggingField && this.dropField)
2597       {
2598          int position = this.dropField.x;
2599          if(draggingField.x < position)
2600             position += this.dropField.width + EXTRA_SPACE;
2601
2602          surface.SetForeground(Color { 85, 85, 255 });
2603          surface.VLine(0, rowHeight - 1, position - scroll.x - 2);
2604          surface.VLine(0, rowHeight - 1, position - scroll.x);
2605       }
2606       if(sortField && !style.clearHeader && style.header)
2607       {
2608          DataField field = sortField;
2609          int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ?
2610             clientSize.w - field.x : (field.width + EXTRA_SPACE);
2611          int tw = 0, th = 0;
2612          if(field.header)
2613             surface.TextExtent(field.header, strlen(field.header), &tw, &th);
2614          if(tw < width - EXTRA_SPACE)
2615          {
2616             bool up = field.sortOrder == 1;
2617             int x = 4, y = 4;
2618             Box clip =
2619             {
2620                field.x + 2 - scroll.x, 0,
2621                field.x + width + EXTRA_SPACE - 1 - scroll.x, rowHeight
2622             };
2623             surface.Clip(&clip);
2624             if(field.alignment == left || field.alignment == center)
2625             {
2626                if(field.alignment == center)
2627                   x = field.x + (width + EXTRA_SPACE - tw) / 2 + tw + EXTRA_SPACE + 4;
2628                else
2629                   x = field.x + tw + EXTRA_SPACE + 4;
2630
2631                x = Min(x, field.x + width - 4);
2632             }
2633             else if(field.alignment == right)
2634             {
2635                x = field.x + width - tw - 2*EXTRA_SPACE - 4;
2636                x = Max(x, field.x + 2);
2637             }
2638             x -= scroll.x;
2639
2640             if(guiApp.textMode)
2641             {
2642                // surface.SetForeground((wmenu.selectedFlag == item) ? white : black);
2643                // surface.WriteText(clientSize.w-8, y+(wmenu.rh - 8)/2, "\020", 1);
2644             }
2645             else
2646             {
2647                if(up)
2648                {
2649                   surface.SetForeground(Color { 128,128,128 } );
2650                   surface.DrawLine(x + 3, y, x, y + 5);
2651                   surface.PutPixel(x + 1, y + 5);
2652                   surface.PutPixel(x + 1, y + 3);
2653                   surface.PutPixel(x + 2, y + 1);
2654
2655                   surface.SetForeground(white);
2656                   surface.DrawLine(x + 4, y, x + 7, y + 5);
2657                   surface.PutPixel(x + 6, y + 5);
2658                   surface.PutPixel(x + 6, y + 3);
2659                   surface.PutPixel(x + 5, y + 1);
2660
2661                   surface.DrawLine(x, y + 6, x + 7, y + 6);
2662                }
2663                else
2664                {
2665                   surface.SetForeground(Color { 128,128,128 });
2666                   surface.DrawLine(x + 3, y+6, x, y+1);
2667                   surface.PutPixel(x + 1, y+1);
2668                   surface.PutPixel(x + 1, y+3);
2669                   surface.PutPixel(x + 2, y+5);
2670
2671                   surface.SetForeground(white);
2672                   surface.DrawLine(x + 4, y+6, x + 7, y+1);
2673                   surface.PutPixel(x + 6, y+1);
2674                   surface.PutPixel(x + 6, y+3);
2675                   surface.PutPixel(x + 5, y+5);
2676
2677                   surface.DrawLine(x, y, x + 7, y);
2678                }
2679             }
2680             surface.Clip(null);
2681          }
2682       }
2683
2684    }
2685
2686    void OnResize(int w, int h)
2687    {
2688       DataField field;
2689       bool showEndBevel = false;
2690       int x = 0;
2691       if(style.collapse && !style.treeBranch)
2692          x += 15;
2693       for(field = fields.first; field; field = field.next)
2694       {
2695          int width = field.width + EXTRA_SPACE;
2696          field.x = x;
2697          x += width;
2698       }
2699       width = x;
2700
2701       SetScrollArea(
2702          width,
2703          (rowCount * rowHeight) +
2704          ((style.header) ? rowHeight : 0) -
2705          ((!((clientSize.h+1) % rowHeight)) ? rowHeight : 0), true);
2706
2707       for(field = fields.first; field; field = field.next)
2708       {
2709          int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ?
2710             clientSize.w - field.x : (field.width + EXTRA_SPACE);
2711          if(style.header && field.headButton)
2712          {
2713             showEndBevel = true;
2714             if(width > 0)
2715             {
2716                field.headButton.position = { field.x, 0 };
2717                field.headButton.size = { width, rowHeight };
2718                field.headButton.visible = true;
2719             }
2720             else
2721                field.headButton.visible = false;
2722          }
2723       }
2724
2725       if(!style.fillLastField && showEndBevel && endBevel)
2726       {
2727          endBevel.position = { x, 0 };
2728          endBevel.size = { clientSize.w + 2 - x, rowHeight };
2729          endBevel.visible = true;
2730       }
2731       else if(endBevel)
2732          endBevel.visible = false;
2733
2734       if(style.alwaysEdit && editData && editData.visible)
2735       {
2736          HideEditBox(true, false, true);
2737       }
2738       else if(editData && editData.visible)
2739          RepositionFieldEditor();
2740    }
2741
2742    void AdaptToFieldWidth(DataField field, bool doScroll)
2743    {
2744       OnResize(clientSize.w, clientSize.h);
2745
2746       // Scroll appropriately
2747       if(doScroll)
2748       {
2749          if(field.x + field.width + EXTRA_SPACE - clientSize.w > scroll.x &&
2750                  field.x >= field.x + field.width + EXTRA_SPACE - clientSize.w)
2751          {
2752             SetScrollPosition(field.x + field.width + EXTRA_SPACE - clientSize.w, scroll.y);
2753          }
2754          else if(field.x + field.width + EXTRA_SPACE - clientSize.w > scroll.x ||
2755                  field.x < scroll.x)
2756             SetScrollPosition(field.x, scroll.y);
2757       }
2758       Update(null);
2759    }
2760
2761    bool HeaderPushed(Button control, int x, int y, Modifiers mods)
2762    {
2763       DataField field = (DataField)(intptr)control.id;
2764       // false: dont destroy edit box
2765       HideEditBox(true, false, true);
2766       if(style.resizable && ((!field && x < RESIZE_BORDER && fields.last) ||
2767          (field && x < RESIZE_BORDER && field.prev) ||
2768          (field && x >= control.clientSize.w - RESIZE_BORDER)))
2769       {
2770          if(!field)
2771             field = fields.last;
2772          else if(x < RESIZE_BORDER && field.prev)
2773             field = field.prev;
2774
2775          if(field.fixed) return false;
2776          resizingField = field;
2777          this.resizeX = x + control.position.x;
2778          this.startWidth = field.width;
2779          this.oldX = x - scroll.x;
2780          return false;
2781       }
2782       else if(field)
2783       {
2784          if(field.fixed) return false;
2785          draggingField = field;
2786          if(style.moveFields)
2787             field.headButton.stayDown = true;
2788          else if(!style.sortable)
2789             return false;
2790       }
2791       else
2792          return false;
2793       return true;
2794    }
2795
2796    bool HeaderMouseMove(Button control, int x, int y, Modifiers mods)
2797    {
2798       if(resizingField)
2799       {
2800          // Resize left
2801          DataField field = resizingField;
2802
2803          x += control.position.x;
2804
2805          // Tweak to prevent shrinking field if we're actually moving right
2806          if(x - scroll.x > this.oldX &&
2807             this.startWidth + x - this.resizeX < field.width)
2808          {
2809             this.oldX = x - scroll.x;
2810             return true;
2811          }
2812          this.oldX = x - scroll.x;
2813
2814          field.width = this.startWidth + x - this.resizeX;
2815          field.width = Max(field.width, - EXTRA_SPACE);
2816
2817          AdaptToFieldWidth(field, true);
2818       }
2819       else if(draggingField)
2820       {
2821          x += control.position.x;
2822          if(style.moveFields)
2823          {
2824             DataField field = fields.last;
2825             int fieldX = 0;
2826             for(field = fields.first; field; field = field.next)
2827             {
2828                fieldX += ((field.width || style.resizable) ?
2829                   field.width : clientSize.w) + EXTRA_SPACE;
2830                if(fieldX > x)
2831                   break;
2832             }
2833             if(draggingField == field)
2834             {
2835                // Reset scroll position
2836                if(field.x + field.width + EXTRA_SPACE - clientSize.w > scroll.x &&
2837                        field.x >= field.x + field.width + EXTRA_SPACE - clientSize.w)
2838                {
2839                   SetScrollPosition(
2840                      field.x + field.width + EXTRA_SPACE - clientSize.w,
2841                      scroll.y);
2842                }
2843                else if(field.x + field.width + EXTRA_SPACE - clientSize.w > scroll.x ||
2844                        field.x < scroll.x)
2845                   SetScrollPosition(field.x, scroll.y);
2846                field = null;
2847             }
2848             if(this.dropField != field)
2849             {
2850                this.dropField = field;
2851                if(field)
2852                {
2853                   int position = field.x;
2854                   // Moving right
2855                   if(draggingField.x < position)
2856                   {
2857                      position += field.width + EXTRA_SPACE - clientSize.w;
2858                      if(position > scroll.x)
2859                         SetScrollPosition(position, scroll.y);
2860                   }
2861                   // Moving Left
2862                   else
2863                   {
2864                      if(position < scroll.x)
2865                         SetScrollPosition(position, scroll.y);
2866                   }
2867
2868                   this.movingFields = true;
2869                }
2870                Update(null);
2871             }
2872          }
2873       }
2874       else if(style.resizable)
2875       {
2876          DataField field = (DataField)(intptr)control.id;
2877          if(field)
2878          {
2879             if(x < RESIZE_BORDER && field.prev)
2880             {
2881                if(!field.prev.fixed)
2882                   control.cursor = guiApp.GetCursor(sizeWE);
2883             }
2884             else if(x >= control.clientSize.w - RESIZE_BORDER)
2885                control.cursor = guiApp.GetCursor(sizeWE);
2886             else
2887                control.cursor = null;
2888          }
2889          else
2890          {
2891             if(x < RESIZE_BORDER && fields.last)
2892                control.cursor = guiApp.GetCursor(sizeWE);
2893             else
2894                control.cursor = null;
2895          }
2896       }
2897       return true;
2898    }
2899
2900    bool HeaderReleased(Button control, int x, int y, Modifiers mods)
2901    {
2902       if(resizingField)
2903       {
2904          NotifyResized(master, this, resizingField, mods);
2905          resizingField = null;
2906       }
2907
2908       if(draggingField)
2909       {
2910          bool result = true;
2911
2912          if(style.moveFields)
2913          {
2914             if(dropField)
2915             {
2916                // Find which field
2917                DataField switchField = fields.last;
2918                DataField field;
2919                int fieldX = 0;
2920
2921                x += draggingField.x;
2922                for(field = fields.first; field; field = field.next)
2923                {
2924                   fieldX += ((field.width || style.resizable) ?
2925                      field.width : clientSize.w) + EXTRA_SPACE;
2926                   if(fieldX > x)
2927                   {
2928                      switchField = field;
2929                      break;
2930                   }
2931                }
2932                if(switchField && draggingField != switchField && this.dropField)
2933                {
2934                   for(field = fields.first; field; field = field.next)
2935                   {
2936                      if(field == switchField || field == draggingField)
2937                         break;
2938                   }
2939
2940                   // Switch field first: move before
2941                   if(field == switchField)
2942                      draggingField.Move(switchField.prev);
2943                   // Dragged field first: move after
2944                   else
2945                      draggingField.Move(switchField);
2946
2947                   NotifyMovedField(master, this, draggingField, mods);
2948                }
2949                draggingField.headButton.stayDown = false;
2950             }
2951             if(movingFields)
2952                result = false;
2953             dropField = null;
2954             movingFields = false;
2955          }
2956          draggingField = null;
2957          return result;
2958       }
2959       return true;
2960    }
2961
2962    bool HeaderClicked(Button control, int x, int y, Modifiers mods)
2963    {
2964       if(style.header && !this.dropField && style.sortable)
2965       {
2966          DataField field = (DataField)(intptr)control.id;
2967          if(sortField == field)
2968             field.sortOrder *= -1;
2969          else
2970             sortField = field;
2971          if(field)
2972          {
2973             Sort(sortField, field.sortOrder);
2974             NotifySort(master, this, field, mods);
2975          }
2976       }
2977       return true;
2978    }
2979
2980    bool HeaderDoubleClicked(Button control, int x, int y, Modifiers mods)
2981    {
2982       if(style.resizable)
2983       {
2984          DataField field = (DataField)(intptr)control.id;
2985          if(field)
2986          {
2987             if(x < RESIZE_BORDER && field.prev)
2988                field = field.prev;
2989             else if(x >= control.clientSize.w - RESIZE_BORDER);
2990             else
2991                field = null;
2992          }
2993          else
2994          {
2995             if(x < RESIZE_BORDER && fields.last)
2996                field = fields.last;
2997             else
2998                field = null;
2999          }
3000          if(field)
3001             field.AutoSize();
3002       }
3003       return false;
3004    }
3005
3006    watch(visible)
3007    {
3008       if(style.freeSelect)
3009       {
3010          if(!visible)
3011          {
3012             ReleaseCapture();
3013             this.rolledOver = this.dragging = false;
3014          }
3015          /*else
3016             Capture();*/
3017       }
3018    };
3019
3020    bool OnLoadGraphics()
3021    {
3022       display.FontExtent(fontObject, "W", 1, null, &fontH);
3023       if(!style.heightSet)
3024       {
3025          rowHeight = Max(fontH + 2, 16) + (style.alwaysEdit ? 1 : 0);
3026          SetScrollLineStep(8, rowHeight);
3027       }
3028       return true;
3029    }
3030
3031    void OnApplyGraphics()
3032    {
3033       SetScrollLineStep(8, rowHeight);
3034       if(style.header)
3035       {
3036          DataField field;
3037          for(field = fields.first; field; field = field.next)
3038          {
3039             if(field.headButton)
3040             {
3041                field.headButton.bevel = (!guiApp.textMode && !style.clearHeader);
3042                if(guiApp.textMode)
3043                   field.headButton.background = Color { 0, 170, 0 };
3044             }
3045          }
3046       }
3047       OnResize(clientSize.w, clientSize.h);
3048    }
3049
3050    bool OnResizing(int *w, int *h)
3051    {
3052       if(rows.first)
3053       {
3054          if(!initSize.w && (!anchor.left.type || !anchor.right.type) && !*w)
3055          {
3056             // Use widest item
3057             DataRow row;
3058             int maxWidth = 0;
3059             Font font = fontObject;
3060             Font boldFont = this.boldFont.font;
3061             Display display = this.display;
3062
3063             for(row = rows.first; row; row = row.GetNextRow())
3064             {
3065                Bitmap icon = row.icon ? row.icon.bitmap : null;
3066                int x = -scroll.x + EXTRA_SPACE / 2-1;
3067                DataField field;
3068                int indent = 0;
3069                DataRow parent;
3070                for(parent = row.parent; parent; parent = parent.parent)
3071                {
3072                   if(!parent.header)
3073                   {
3074                      if(style.treeBranch)
3075                         indent += 20;
3076                      else
3077                         indent += 15;
3078                   }
3079                }
3080                if(style.rootCollapse) indent += 20;
3081                x += indent;
3082                if(style.collapse && !(style.treeBranch)) x += 15;
3083                if(icon)
3084                   x += 20;
3085
3086                // Compute the rows size
3087                for(field = fields.first; field; field = field.next)
3088                {
3089                   if(((style.resizable && (!(style.alwaysEdit) || field.next)) || field.width) && !row.header)
3090                      x += field.width - (field.prev ? 0 : indent);
3091                   else
3092                   {
3093                      ListBoxCell cell;
3094                      uint index;
3095                      for(index = 0, cell = row.cells.first; index != field.index; index++, cell = cell.next);
3096
3097                      // Should always be as many cells in the row as fields in the listbox
3098                      if(cell && cell.isSet && field.dataType)
3099                      {
3100                         static char tempString[4096];
3101                         const char * string;
3102                         int tw, th;
3103                         if(field.dataType.type == normalClass || field.dataType.type == noHeadClass)
3104                            string = ((const char *(*)(void *, void *, char *, void *, bool *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString])(field.dataType, cell.data[0], tempString, field.userData, null);
3105                         else
3106                            string = ((const char *(*)(void *, void *, char *, void *, bool *))(void *)field.dataType._vTbl[__ecereVMethodID_class_OnGetString])(field.dataType, cell.data, tempString, field.userData, null);
3107                         /* GCC-4.4 Bug!
3108                         if(!string) string = "";
3109                         display.FontExtent(row.header ? boldFont : font, string, strlen(string), &tw, &th);
3110                         */
3111                         if(string)
3112                            display.FontExtent(row.header ? boldFont : font, string, strlen(string), &tw, &th);
3113                         else
3114                            display.FontExtent(row.header ? boldFont : font, "", 0, &tw, &th);
3115                         x += tw;
3116                      }
3117                      if(row.header) break;
3118                   }
3119                   x += EXTRA_SPACE;
3120                }
3121                maxWidth = Max(maxWidth, x);
3122             }
3123             *w = maxWidth;
3124          }
3125          if(!*h)
3126          {
3127             *h = Min(this.maxShown, this.rowCount) * rowHeight;
3128          }
3129       }
3130       else
3131       {
3132          if(!*w) *w = rowHeight * 5;
3133          if(!*h) *h = rowHeight * 5;
3134       }
3135       return true;
3136    }
3137
3138    watch(font)
3139    {
3140       FontResource font = this.font;
3141       FontResource boldFont
3142       {
3143          faceName = font.faceName, size = font.size, bold = true
3144       };
3145       AddResource(boldFont);
3146       RemoveResource(this.boldFont);
3147       this.boldFont = boldFont;
3148
3149       OnLoadGraphics();
3150
3151       SetInitSize(initSize);
3152    };
3153
3154    bool OnMouseMove(int x, int y, Modifiers mods)
3155    {
3156       bool isTimer = false;
3157       int realX = x, realY = y;
3158
3159       if(insideNotifySelect) return true;
3160
3161       if(style.alwaysEdit && style.resizable &&
3162          resizingField && !(mods.isSideEffect))
3163       {
3164         // Resize left
3165          DataField field = resizingField;
3166          field.width = this.startWidth + x - this.resizeX;
3167          field.width = Max(field.width, - EXTRA_SPACE);
3168
3169          AdaptToFieldWidth(field, true);
3170       }
3171
3172       cursor = null;
3173       if(style.alwaysEdit && style.resizable)
3174       {
3175          int vx = -scroll.x - 1;
3176          DataField field;
3177
3178          if(style.collapse && !(style.treeBranch))
3179             vx += 15;
3180
3181          for(field = fields.first; field; field = field.next)
3182          {
3183             int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ?
3184                clientSize.w - field.x : (field.width + EXTRA_SPACE);
3185
3186             if(field.prev)
3187             {
3188                if(Abs(x - vx) < 2)
3189                {
3190                   cursor = guiApp.GetCursor(sizeWE);
3191                   break;
3192                }
3193             }
3194             vx += width + EXTRA_SPACE;
3195          }
3196       }
3197
3198       if((editData && editData.visible) || (style.alwaysEdit))
3199          return true;
3200
3201       if(x == MAXINT && y == MAXINT)
3202       {
3203          x = this.mouseX;
3204          y = this.mouseY;
3205          isTimer = true;
3206       }
3207
3208       // ADDED THIS CHECK FOR FieldDropBox LEAKS
3209       if(/*!mods.isSideEffect && */(this.rolledOver || !this.dragging))
3210       {
3211          int rowY = (style.header) ? rowHeight : 0;
3212          DataRow row = null;
3213          int rowIndex;
3214
3215          mouseX = x;
3216          mouseY = y;
3217
3218          if(this.dragging &&
3219             ((vertScroll && vertScroll.visible &&
3220              (y < 0 || y >= clientSize.h)) ||
3221              (horzScroll && horzScroll.visible &&
3222              (x < 0 || x >= clientSize.w))))
3223          {
3224             timer.Start();
3225             if(isTimer)
3226             {
3227                if(vertScroll && vertScroll.visible &&
3228                   (y < 0 || y >= clientSize.h))
3229                   vertScroll.Action((y<0)?up:down, 0, 0);
3230                if(horzScroll && horzScroll.visible &&
3231                   (x < 0 || x >= clientSize.w))
3232                   horzScroll.Action((x<0)?up:down, 0, 0);
3233             }
3234          }
3235          else
3236             timer.Stop();
3237
3238          // This must be done after the scrolling took place
3239          rowIndex = firstRowShown ? firstRowShown.index : -1;
3240          y = Max(y, 0);
3241          y = Min(y, clientSize.h-1);
3242          for(row = firstRowShown; row; row = row.GetNextRow(), rowIndex ++)
3243          {
3244             rowY += rowHeight;
3245             if(rowY > y)
3246             {
3247                break;
3248             }
3249          }
3250
3251          if(row && currentRow != row)
3252          {
3253             if(this.dragRow && style.moveRows)
3254             {
3255                if(row.selectedFlag == selected || row.selectedFlag == tempSelected)
3256                   rowIndex = -1;
3257                else if(style.multiSelect)
3258                {
3259                   DataRow thisRow;
3260                   for(thisRow = rows.first; thisRow; thisRow = thisRow.GetNextRow())
3261                      if((thisRow.selectedFlag == selected || thisRow.selectedFlag == tempSelected) ||
3262                         thisRow == row)
3263                         break;
3264                   if(thisRow != row)
3265                      rowIndex++;
3266                }
3267                if(this.dropIndex != rowIndex)
3268                {
3269                   this.dropIndex = rowIndex;
3270                   this.editRow = null;
3271                   Update(null);
3272                   this.movedRow = true;
3273                }
3274             }
3275             else if((style.freeSelect  || this.dragging) && ((realX>= 0 && realY >= 0 && realX< clientSize.w && realY < clientSize.h) || this.rolledOver))
3276             {
3277                if(!(style.multiSelect))
3278                {
3279                   if(currentRow)currentRow.selectedFlag = unselected;
3280                   if(row)row.selectedFlag = selected;
3281                }
3282                currentRow = row;
3283
3284                if(style.multiSelect)
3285                {
3286                   DataRow selRow;
3287
3288                   for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3289                   {
3290                      if(selRow.selectedFlag == tempUnselected) selRow.selectedFlag = selected;
3291                      else if(selRow.selectedFlag == tempSelected) selRow.selectedFlag = unselected;
3292                   }
3293
3294                   if(rowIndex >= clickedRow.index)
3295                   {
3296                      for(selRow = clickedRow; selRow; selRow = selRow.GetNextRow())
3297                      {
3298                         selRow.selectedFlag = (selRow.selectedFlag == selected) ? tempUnselected : tempSelected;
3299                         if(selRow == row)
3300                            break;
3301                      }
3302                   }
3303                   else
3304                   {
3305                      for(selRow = row; selRow; selRow = selRow.GetNextRow())
3306                      {
3307                         selRow.selectedFlag = (selRow.selectedFlag == selected) ? tempUnselected : tempSelected;
3308                         if(selRow == clickedRow)
3309                            break;
3310                      }
3311                   }
3312                }
3313                Update(null);
3314                if(style.freeSelect)
3315                   NotifyHighlight(master, this, currentRow, mods);
3316                else
3317                {
3318                   insideNotifySelect = true;
3319                   NotifySelect(master, this, currentRow, mods);
3320                   insideNotifySelect = false;
3321                }
3322
3323                if(style.alwaysEdit && currentRow)
3324                   currentRow.Edit(currentField);
3325             }
3326          }
3327       }
3328       return true;
3329    }
3330
3331    bool OnMouseOver(int x, int y, Modifiers mods)
3332    {
3333       if(this.dragging)
3334          this.rolledOver = true;
3335       return true;
3336    }
3337
3338    bool OnActivate(bool active, Window swap, bool * goOnWithActivation, bool direct)
3339    {
3340       // TOCHECK: WAS THIS MISSING ? CHECK FOR SLOWDOWN
3341       if(!active) Update(null);
3342
3343       if(!active && (!swap || !swap.isModal))
3344       {
3345          // true: destroy edit box
3346          HideEditBox(true, true, false);
3347       }
3348       else if(!swap || !swap.isModal)
3349       {
3350          // Bring back edit box
3351          if(currentRow && style.alwaysEdit)
3352          {
3353             currentRow.Edit(currentField ? currentField : null);
3354          }
3355          Update(null);
3356       }
3357       return true; //NotifyActivate(master, this, active, swap, 0);
3358    }
3359
3360
3361    bool OnButtonDown(int x, int y, Modifiers mods, bool right)
3362    {
3363       bool result = true;
3364       // Check to see if we're dragging the vertical divider
3365       if(style.alwaysEdit && style.resizable && !right)
3366       {
3367          int vx = -scroll.x - 1;// + EXTRA_SPACE;// / 2 - 1;
3368          DataField field;
3369
3370          if(style.collapse && !(style.treeBranch))
3371             vx += 15;
3372
3373          for(field = fields.first; field; field = field.next)
3374          {
3375             int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ?
3376                clientSize.w - field.x : (field.width + EXTRA_SPACE);
3377
3378             if(field.prev)
3379             {
3380                if(Abs(x - vx) < 2)
3381                {
3382                   resizingField = field.prev;
3383                   this.resizeX = x;
3384                   this.startWidth = resizingField.width;
3385                   Capture();
3386                   SetMouseRangeToClient();
3387                   break;
3388                }
3389             }
3390             vx += width + EXTRA_SPACE;
3391          }
3392       }
3393
3394       if(!(style.freeSelect))
3395       {
3396          int rowY = (style.header) ? rowHeight : 0;
3397          DataRow row = null;
3398          int rowIndex = firstRowShown ? firstRowShown.index : -1;
3399          DataRow previousRow = currentRow;
3400          DataRow newCurrentRow = null;
3401          bool moveMultiple = false;
3402          int numSelected = 0;
3403          int rowStart = -scroll.x;
3404
3405          if(style.multiSelect)
3406          {
3407             if(!right)
3408             {
3409                DataRow row;
3410                if(!(mods.shift))
3411                {
3412                   for(row = rows.first; row; row = row.GetNextRow())
3413                   {
3414                      if(row.selectedFlag == tempSelected)
3415                         row.selectedFlag = selected;
3416                      else if(row.selectedFlag == tempUnselected)
3417                         row.selectedFlag = unselected;
3418                      if(row.selectedFlag == selected)
3419                         numSelected++;
3420                   }
3421                }
3422             }
3423          }
3424
3425          for(row = firstRowShown; row; row = row.GetNextRow(), rowIndex ++)
3426          {
3427             rowY += rowHeight;
3428             if(rowY > y || (style.multiSelect && !row.GetNextRow()))
3429             {
3430                int plusIndent = 0;
3431                if(style.treeBranch)
3432                {
3433                   DataRow parent;
3434                   for(parent = (style.rootCollapse) ? row.parent : (row.parent ? row.parent.parent : null); parent; parent = parent.parent)
3435                      if(!parent.header)
3436                         plusIndent += 20;
3437                   plusIndent += 4;
3438                }
3439
3440                /*    THIS WAS TOO STRICT:
3441                if(style.collapse && row.subRows.first && (row.parent || !(style.treeBranch) || (style.rootCollapse)) &&
3442                   (x >= rowStart + 3 + plusIndent && y >= rowY - rowHeight + PLUSY && x <= rowStart + 11 + plusIndent && y <= rowY - rowHeight + PLUSY + 8))
3443                */
3444                if(style.collapse &&
3445                   (x >= rowStart && y >= rowY - rowHeight && x <= rowStart + 18 + plusIndent && y <= rowY + rowHeight-1))
3446                {
3447                   if(row.subRows.first && (row.parent || !(style.treeBranch) || (style.rootCollapse)) && x >= plusIndent)
3448                      row.collapsed = !row.collapsed;
3449                   return false;
3450                }
3451                else
3452                {
3453                   if(rowY > y)
3454                   {
3455                      newCurrentRow = row;
3456                   }
3457                   if(style.multiSelect)
3458                   {
3459                      // Deselect everything if user didn't clicked on a row
3460                      if(y >= rowY)
3461                      {
3462                         DataRow selRow;
3463                         for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3464                            selRow.selectedFlag = unselected;
3465                         clickedRow = row;
3466                         //this.clickedRowIndex = rowIndex;
3467                      }
3468                      else if(style.moveRows && !(mods.shift) &&
3469                         (row.selectedFlag == selected || row.selectedFlag == tempSelected) &&
3470                         !right && !(mods.isActivate))
3471                         moveMultiple = true;
3472                      else
3473                      {
3474                         DataRow selRow;
3475                         if(right)
3476                         {
3477                            if(row.selectedFlag == tempUnselected || row.selectedFlag == unselected)
3478                            {
3479                               for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3480                                  selRow.selectedFlag = unselected;
3481                               row.selectedFlag = selected;
3482                            }
3483                            clickedRow = row;
3484                            //this.clickedRowIndex = rowIndex;
3485                         }
3486                         else
3487                         {
3488                            if(!(mods.ctrl))
3489                            {
3490                               for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3491                                  selRow.selectedFlag = unselected;
3492                            }
3493                            else
3494                            {
3495                               for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3496                               {
3497                                  if(selRow != clickedRow)
3498                                  {
3499                                     if(selRow.selectedFlag == tempUnselected) selRow.selectedFlag = selected;
3500                                     else if(selRow.selectedFlag == tempSelected) selRow.selectedFlag = unselected;
3501                                  }
3502                               }
3503                            }
3504
3505                            if(mods.shift)
3506                            {
3507                               if(rowIndex >= clickedRow.index)
3508                               {
3509                                  for(selRow = clickedRow; selRow; selRow = selRow.GetNextRow())
3510                                  {
3511                                     if(mods.ctrl)
3512                                     {
3513                                        if(selRow != clickedRow)
3514                                        {
3515                                           if(selRow.selectedFlag)
3516                                              selRow.selectedFlag = tempUnselected;
3517                                           else
3518                                              selRow.selectedFlag = tempSelected;
3519                                        }
3520                                     }
3521                                     else
3522                                        selRow.selectedFlag = selected;
3523                                     if(selRow == row)
3524                                        break;
3525                                  }
3526                               }
3527                               else
3528                               {
3529                                  for(selRow = row; selRow; selRow = selRow.GetNextRow())
3530                                  {
3531                                     if(mods.ctrl)
3532                                     {
3533                                        if(selRow != clickedRow)
3534                                        {
3535                                           if(selRow.selectedFlag)
3536                                              selRow.selectedFlag = tempUnselected;
3537                                           else
3538                                              selRow.selectedFlag = tempSelected;
3539                                        }
3540                                     }
3541                                     else
3542                                        selRow.selectedFlag = selected;
3543                                     if(selRow == clickedRow)
3544                                        break;
3545                                  }
3546                               }
3547                            }
3548                            else
3549                            {
3550                               if(mods.ctrl)
3551                               {
3552                                  if(row.selectedFlag)
3553                                     row.selectedFlag = tempUnselected;
3554                                  else row.selectedFlag = tempSelected;
3555                               }
3556                               else
3557                                  row.selectedFlag = tempSelected;
3558                               clickedRow = row;
3559                               //this.clickedRowIndex = rowIndex;
3560                            }
3561                         }
3562                      }
3563                   }
3564                }
3565                break;
3566             }
3567          }
3568
3569          // true: destroy edit box
3570          if(newCurrentRow)
3571          {
3572             incref newCurrentRow;
3573          }
3574
3575          if(currentRow != newCurrentRow)
3576             HideEditBox(true, true, false);
3577
3578          if(newCurrentRow)
3579          {
3580             if(newCurrentRow._refCount <= 1)
3581                delete newCurrentRow;
3582             else
3583                newCurrentRow._refCount--;
3584          }
3585
3586          if(newCurrentRow)
3587          {
3588             if(!(style.multiSelect))
3589             {
3590                if(currentRow) currentRow.selectedFlag = unselected;
3591                if(newCurrentRow) newCurrentRow.selectedFlag = selected;
3592             }
3593          }
3594
3595          if(currentRow != newCurrentRow)
3596          {
3597             /*
3598             // true: destroy edit box
3599             if(newCurrentRow)
3600             {
3601                //incref newCurrentRow;
3602                incref newCurrentRow;
3603             }
3604
3605             HideEditBox(true, true, false);
3606             */
3607
3608             if(newCurrentRow)
3609             {
3610                int headerSize = ((style.header) ? rowHeight : 0);
3611                int height = clientSize.h + 1 - headerSize;
3612
3613                /*if(newCurrentRow._refCount <= 1)
3614                   delete newCurrentRow;
3615                else
3616                {
3617                   newCurrentRow._refCount--;
3618                   //newCurrentRow._refCount--;
3619                }
3620                */
3621                currentRow = newCurrentRow;
3622
3623                if(currentRow && currentRow.index * rowHeight > scroll.y + height - rowHeight)
3624                   SetScrollPosition(scroll.x,
3625                      currentRow.index * rowHeight - height + rowHeight);
3626                else if(!currentRow || currentRow.index * rowHeight < scroll.y)
3627                {
3628                   int line = currentRow ? currentRow.index * rowHeight : 0;
3629                   //SNAPUP(line, rowHeight);
3630                   SetScrollPosition(scroll.x, line);
3631                }
3632
3633                // GO THROUGH SetCurrentRow eventually?
3634                // SetCurrentRow(newCurrentRow, true);
3635             }
3636          }
3637
3638          if(style.freeSelect)
3639             NotifyHighlight(master, this, currentRow, mods);
3640          else if((moveMultiple || (!(style.multiSelect) && previousRow == currentRow)) &&
3641             newCurrentRow && !(mods.shift))
3642          {
3643             if(!right)
3644             {
3645                if(!(mods.isActivate))
3646                {
3647                   if(style.moveRows)
3648                   {
3649                      this.dragRow = currentRow;
3650                      this.dropIndex = -1;
3651                      this.movedRow = false;
3652                   }
3653                   if(editData && editData.visible && style.alwaysEdit)
3654                   {
3655                      DataField field;
3656                      int sx = -scroll.x;
3657                      int indent = 0;
3658                      DataRow parent;
3659
3660                      if(style.collapse && !style.treeBranch)
3661                         sx += 15;
3662
3663                      for(parent = newCurrentRow.parent; parent; parent = parent.parent)
3664                         if(!parent.header)
3665                            indent += (style.treeBranch) ? 20 : 15;
3666                      sx += indent;
3667
3668                      for(field = fields.first; field; field = field.next)
3669                      {
3670                         int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ?
3671                            clientSize.w - field.x : (field.width + EXTRA_SPACE);
3672
3673                         if(!field.prev) width -= indent;
3674                         if(x >= sx && x < sx + width)
3675                            break;
3676                         sx += width;
3677                      }
3678                      if(field == currentField)
3679                         editData.Deactivate();
3680                      else
3681                      {
3682                         currentRow.Edit(field);
3683                         editData.Activate();
3684                      }
3685                   }
3686                   else if(!(mods.ctrl) && numSelected <= 1)
3687                      this.editRow = currentRow;
3688
3689                   if(style.noDragging && newCurrentRow)
3690                     NotifyReclick(master, this, newCurrentRow, mods);
3691                }
3692                else
3693                {
3694                   // If the user clicked exactly on the edited field,
3695                   // activate it
3696                   if(editData && editData.visible && newCurrentRow)
3697                   {
3698                      DataField field;
3699                      int sx = -scroll.x;
3700                      int indent = 0;
3701
3702                      if(style.collapse && !(style.treeBranch))
3703                         sx += 15;
3704
3705                      {
3706                         DataRow parent;
3707                         for(parent = newCurrentRow.parent; parent; parent = parent.parent)
3708                            if(!parent.header)
3709                               indent += (style.treeBranch) ? 20 : 15;
3710                         sx += indent;
3711                      }
3712
3713                      for(field = fields.first; field; field = field.next)
3714                      {
3715                         int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ?
3716                            clientSize.w - field.x : (field.width + EXTRA_SPACE);
3717                         if(!field.prev) width -= indent;
3718                         if(x >= sx && x < sx + width && newCurrentRow)
3719                            break;
3720                         sx += width;
3721                      }
3722
3723                      if(field) //x >= sx && x < sx + width && newCurrentRow)
3724                      {
3725                         if(field == currentField)
3726                            editData.Activate();
3727                         /*else
3728                            newCurrentRow.Edit(currentField);*/
3729                      }
3730                      else if(style.noDragging && newCurrentRow)
3731                        NotifyReclick(master, this, newCurrentRow, mods);
3732                   }
3733                   else if(style.noDragging && newCurrentRow)
3734                     NotifyReclick(master, this, newCurrentRow, mods);
3735                }
3736             }
3737          }
3738          else
3739          {
3740             DataField f = null;
3741             if(result && style.alwaysEdit && currentRow)
3742             {
3743                if(newCurrentRow)
3744                {
3745                   DataField field = null;
3746                   int sx = -scroll.x;
3747                   int indent = 0;
3748                   DataRow parent;
3749
3750                   if(style.collapse && !style.treeBranch)
3751                      sx += 15;
3752
3753                   for(parent = newCurrentRow.parent; parent; parent = parent.parent)
3754                      if(!parent.header)
3755                         indent += (style.treeBranch) ? 20 : 15;
3756                   sx += indent;
3757
3758                   for(field = fields.first; field; field = field.next)
3759                   {
3760                      int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ?
3761                         clientSize.w - field.x : (field.width + EXTRA_SPACE);
3762
3763                      if(!field.prev) width -= indent;
3764                      if(x >= sx && x < sx + width)
3765                      {
3766                         f = currentField = field;
3767                         break;
3768                      }
3769                      sx += width;
3770                   }
3771                }
3772             }
3773             // Moved NotifySelect after setting currentField for the NotifySelect implementation to be aware of which field is now selected (e.g. WatchesView)
3774             result = NotifySelect(master, this, currentRow, mods);
3775             if(result && style.alwaysEdit && currentRow)
3776             {
3777                // In case the user specifically clicked on a field (f is set), override any change to currentField that NotifySelect could have done
3778                currentRow.Edit(f ? f : currentField);
3779
3780                // If the user clicked exactly on the edited field,
3781                // activate it
3782                if(editData && editData.visible && newCurrentRow)
3783                {
3784                   if(currentField)
3785                   {
3786                      editData.Activate();
3787                   }
3788                   else if(style.noDragging && newCurrentRow)
3789                      NotifyReclick(master, this, newCurrentRow, mods);
3790                }
3791             }
3792             else if(style.noDragging && newCurrentRow)
3793               NotifyReclick(master, this, newCurrentRow, mods);
3794          }
3795       }
3796       /*
3797          For drop box to capture...
3798       else
3799       {
3800          if(x < 0 || y < 0 || x >= clientSize.w || y >= clientSize.h)
3801          {
3802             bool goOn = true;
3803             master.parent.Activate();
3804             Update(null);
3805             ReleaseCapture();
3806             return true;
3807          }
3808       }
3809       */
3810       if(result)
3811       {
3812          if(!style.noDragging)
3813          {
3814             this.dragging = true;
3815             Capture();
3816          }
3817          if(x >= 0 && y >= 0 && x < clientSize.w && y < clientSize.h)
3818             this.rolledOver = true;
3819          Update(null);
3820       }
3821       else
3822       {
3823          this.dragging = false;
3824          OnLeftButtonUp(x, y, mods);
3825       }
3826       return result;
3827    }
3828
3829    bool OnLeftButtonDown(int x, int y, Modifiers mods)
3830    {
3831       return OnButtonDown(x,y, mods, false);
3832    }
3833
3834    bool OnLeftButtonUp(int x, int y, Modifiers mods)
3835    {
3836       if(resizingField && style.alwaysEdit)
3837       {
3838          Window::FreeMouseRange();
3839          ReleaseCapture();
3840          resizingField = null;
3841       }
3842
3843       if(dragRow || editRow)
3844       {
3845          DataRow row, switchRow = rows.last;
3846          int rowY = (style.header) ? rowHeight : 0;
3847          for(row = firstRowShown; row; row = row.GetNextRow())
3848          {
3849             rowY += rowHeight;
3850             if(rowY > y)
3851             {
3852                switchRow = row;
3853                break;
3854             }
3855          }
3856          if(this.editRow == switchRow && y >= 0)
3857          {
3858             // Find which field
3859             DataField field;
3860             int fieldX = 0;
3861             for(field = fields.first; field; field = field.next)
3862             {
3863                int width = (!field.next && style.fillLastField && (!hasHorzScroll || clientSize.w - field.x > field.width + EXTRA_SPACE)) ?
3864                   clientSize.w - field.x : (field.width + EXTRA_SPACE);
3865                fieldX += width;
3866
3867                if(fieldX > x + scroll.x)
3868                   break;
3869             }
3870
3871             if(field && field.editable)
3872             {
3873                // true: destroy edit box
3874                HideEditBox(true, true, false);
3875                PopupEditBox(field, false);
3876             }
3877             else if(!style.noDragging)
3878                NotifyReclick(master, this, currentRow, mods);
3879          }
3880          else if(style.moveRows && switchRow)
3881          {
3882             if(this.dragRow == switchRow && this.movedRow == false)
3883             {
3884                DataRow row = this.dragRow;
3885                DataRow selRow;
3886                if(!(mods.ctrl))
3887                {
3888                   for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3889                      selRow.selectedFlag = unselected;
3890                }
3891                else
3892                {
3893                   for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
3894                   {
3895                      if(selRow != clickedRow)
3896                      {
3897                         if(selRow.selectedFlag == tempUnselected) selRow.selectedFlag = selected;
3898                         else if(selRow.selectedFlag == tempSelected) selRow.selectedFlag = unselected;
3899                      }
3900                   }
3901                }
3902                if(mods.ctrl)
3903                {
3904                   if(row.selectedFlag)
3905                      row.selectedFlag = tempUnselected;
3906                   else row.selectedFlag = tempSelected;
3907                }
3908                else
3909                   row.selectedFlag = tempSelected;
3910                clickedRow = row;
3911             }
3912             else
3913             {
3914                if(style.multiSelect)
3915                {
3916                   if(!switchRow.selectedFlag)
3917                   {
3918                      bool foundSwitch = false;
3919                      bool after = false;
3920                      DataRow next;
3921                      DataRow afterRow = switchRow.prev;
3922                      for(row = rows.first; row; row = next)
3923                      {
3924                         next = row.GetNextRow();
3925                         if(row.selectedFlag == selected || row.selectedFlag == tempSelected)
3926                         {
3927                            if(!foundSwitch && !after)
3928                            {
3929                               after = true;
3930                               afterRow = switchRow;
3931                            }
3932                            if(!after || !(row.selectedFlag == selected || row.selectedFlag == tempSelected) ||
3933                               !foundSwitch)
3934                            {
3935                               row.Move(afterRow);
3936                               afterRow = row;
3937                            }
3938                         }
3939                         else if(row == switchRow)
3940                            foundSwitch = true;
3941                      }
3942                   }
3943                }
3944                else
3945                {
3946                   for(row = rows.first; row; row = row.GetNextRow())
3947                   {
3948                      if(row == switchRow || row == this.dragRow)
3949                         break;
3950                   }
3951
3952                   // Switch row first: move before
3953                   if(row == switchRow)
3954                   {
3955                      DataRow actualMoveRow;
3956
3957                      if(!switchRow.prev && switchRow.parent == dragRow.parent)
3958                         actualMoveRow = null;
3959                      else
3960                      {
3961                         actualMoveRow = switchRow.prev ? switchRow.prev : switchRow;
3962                         while(actualMoveRow && actualMoveRow.parent != dragRow.parent && actualMoveRow.parent)
3963                            actualMoveRow = actualMoveRow.parent;
3964                      }
3965
3966                      if(!actualMoveRow || (actualMoveRow && actualMoveRow.parent == dragRow.parent))
3967                         if(NotifyMove(master, this, actualMoveRow, mods))
3968                         {
3969                            dragRow.Move(actualMoveRow);
3970                         }
3971                   }
3972                   // Dragged row first: move after
3973                   else
3974                   {
3975                      DataRow actualMoveRow = switchRow;
3976                      DataRow nextRow = switchRow.GetNextRow();
3977
3978                      while(nextRow && nextRow.parent != dragRow.parent)
3979                         nextRow = nextRow.parent ? nextRow.parent.next : null;
3980                      if(nextRow)
3981                         actualMoveRow = nextRow.prev;
3982
3983                      if(!nextRow)
3984                         actualMoveRow = dragRow.parent ? dragRow.parent.subRows.last : rows.last;
3985
3986                      if(!actualMoveRow || (actualMoveRow != dragRow && actualMoveRow.parent == dragRow.parent))
3987                         if(NotifyMove(master, this, actualMoveRow, mods))
3988                         {
3989                            dragRow.Move(actualMoveRow);
3990                         }
3991                   }
3992                }
3993             }
3994          }
3995          dragRow = null;
3996          editRow = null;
3997          movedRow = false;
3998          dropIndex = -1;
3999          Update(null);
4000       }
4001
4002       timer.Stop();
4003       if(this.dragging || style.freeSelect)
4004       {
4005          if(this.dragging)
4006          {
4007             this.rolledOver = this.dragging = false;
4008          }
4009          if(x >= 0 && y >= 0 && x < clientSize.w && y < clientSize.h && currentRow && style.freeSelect)
4010          {
4011             bool result;
4012             ReleaseCapture();
4013             result = NotifySelect(master, this, currentRow, mods);
4014             if(style.alwaysEdit)
4015                currentRow.Edit(currentField);
4016             return result;
4017
4018          }
4019          // if(!(style.freeSelect))
4020          ReleaseCapture();
4021       }
4022       return true;
4023    }
4024
4025    bool OnLeftDoubleClick(int x, int y, Modifiers mods)
4026    {
4027       int rowStart = -scroll.x;
4028       DataRow row;
4029       int rowY = (style.header) ? rowHeight : 0;
4030       int plusIndent = 0;
4031
4032       OnLeftButtonUp(x,y,mods);
4033       if(style.alwaysEdit)
4034       {
4035          if(!(style.collapse) || x > 15)
4036             if(editData && editData.visible)
4037             {
4038                editData.Activate();
4039                NotifyDoubleClick(master, this, x, y, mods);
4040                return false;
4041             }
4042       }
4043       for(row = firstRowShown; row; row = row.GetNextRow())
4044       {
4045          rowY += rowHeight;
4046          if(rowY > y || (style.multiSelect && !row.GetNextRow()))
4047          {
4048             if(style.treeBranch)
4049             {
4050                DataRow parent;
4051                for(parent = (style.rootCollapse) ? row.parent : (row.parent ? row.parent.parent : null); parent; parent = parent.parent)
4052                   if(!parent.header)
4053                      plusIndent += 20;
4054                plusIndent += 4;
4055             }
4056             break;
4057          }
4058       }
4059
4060       if((row && style.collapse && (x >= rowStart && x <= rowStart + 18 + plusIndent)) ||
4061          NotifyDoubleClick(master, this, x, y, mods))
4062       {
4063          if(style.collapse)
4064          {
4065             if(row && row.subRows.first)
4066             {
4067                row.collapsed = !row.collapsed;
4068                return false;
4069             }
4070          }
4071          // We need to return true here so that OnLeftButtonDown can popup the DataBox Editors
4072          return true;
4073       }
4074       return false;
4075    }
4076
4077    bool OnRightButtonDown(int x, int y, Modifiers mods)
4078    {
4079       return OnButtonDown(x,y, mods, true);
4080    }
4081
4082    bool OnRightButtonUp(int x, int y, Modifiers mods)
4083    {
4084       OnLeftButtonUp(x,y,mods);
4085       return NotifyRightClick(master, this, x, y, mods);
4086    }
4087
4088    bool GoToLetter(unichar ch, bool keyHit)
4089    {
4090       bool result = false;
4091       DataField field;
4092       bool checkNextField = true;
4093       int len = keyHit ? 0 : strlen(typedString);
4094
4095       typedString = renew typedString char[len + 2];
4096       typedString[len++] = (char)tolower(ch);         // TODO: FIX UNICODE
4097       typedString[len] = '\0';
4098
4099       for(field = fields.first; field; field = field.next)
4100       {
4101          DataRow startRow = currentRow ? currentRow : rows.first;
4102
4103          if(startRow && field.dataType && field.dataType._vTbl[__ecereVMethodID_class_OnGetString] && ch)
4104          {
4105             DataRow row, next;
4106             bool looped = false;
4107             if(len == 1 && currentRow)
4108                startRow = (next = startRow.GetNextRow(), (next ? next : rows.first));
4109
4110             for(row = startRow; row != startRow || !looped; next = row.GetNextRow(), row = next ? next : rows.first)
4111             {
4112                void * data = row.GetData(field);
4113                char tempString[1024] = "";
4114                bool needClass = false;
4115                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;
4116
4117                if(string && string[0])
4118                   checkNextField = false;
4119                if(string && string[0] && !strnicmp(string, typedString, len))
4120                {
4121                   if(style.multiSelect)
4122                   {
4123                      DataRow selRow;
4124
4125                      clickedRow = row;
4126                      for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
4127                         selRow.selectedFlag = unselected;
4128                      row.selectedFlag = selected;
4129                   }
4130                   SetCurrentRow(row, true);
4131                   result = true;
4132                   break;
4133                }
4134                looped = true;
4135             }
4136             typingTimer.Stop();
4137             if(this.typingTimeOut && !keyHit)
4138                typingTimer.Start();
4139             if(!result || !this.typingTimeOut || keyHit)
4140                typedString[len-1] = '\0';
4141          }
4142          if(!checkNextField || result) break;
4143       }
4144       return result;
4145    }
4146
4147    bool OnKeyDown(Key key, unichar ch)
4148    {
4149       DataField field;
4150
4151       if(key == enter || key == keyPadEnter)
4152       {
4153          if(editData && editData.visible && editData.active)
4154          {
4155             HideEditBox(true, false, false);
4156             return false;
4157          }
4158       }
4159       else if(key == escape)
4160       {
4161          if(resizingField || this.movingFields || (editData && editData.visible) || this.dragRow)
4162          {
4163             if(editData && editData.visible && style.alwaysEdit && !editData.active)
4164                return true;
4165             // false: dont destroy edit box
4166             HideEditBox(false, false, false);
4167             if(resizingField)
4168             {
4169                resizingField.width = this.startWidth;
4170                AdaptToFieldWidth(resizingField, true);
4171                resizingField = null;
4172                ReleaseCapture();
4173             }
4174             this.dragRow = null;
4175             if(this.dragging)
4176             {
4177                this.dragging = false;
4178                ReleaseCapture();
4179             }
4180
4181             this.movingFields = false;
4182             draggingField = null;
4183             Update(null);
4184             return false;
4185          }
4186       }
4187
4188       if(!currentField || !currentField.editable)
4189          for(field = fields.first; field; field = field.next)
4190          {
4191             if(field.editable)
4192             {
4193                currentField = field;
4194                break;
4195             }
4196          }
4197       if((key == f2 || (style.alwaysEdit && (key == ctrlV || key == ctrlC || key == ctrlX || key == shiftInsert || key == ctrlInsert || key == shiftDel))) &&
4198             currentField && currentField.editable)
4199       {
4200          PopupEditBox(currentField, false);
4201          if(editData && editData.visible)
4202          {
4203             if(style.alwaysEdit)
4204             {
4205                editData.Activate();
4206                if(key == ctrlV || key == ctrlC || key == ctrlX || key == shiftInsert || key == ctrlInsert || key == shiftDel)
4207                {
4208                   editData.OnKeyHit(key, ch);
4209                   StopEditing(true);
4210                }
4211                else
4212                   // For Installer to pop up file dialog
4213                   NotifyKeyDown(master, this, currentRow, key, ch);
4214             }
4215             return false;
4216          }
4217       }
4218
4219       if(!NotifyKeyDown(master, this, currentRow, key, ch))
4220          return false;
4221
4222       // Editable fields...
4223       if(currentField)
4224       {
4225          if(style.alwaysEdit && editData && editData.visible)
4226             return editData.OnKeyDown(key, ch);
4227          return true;   // We want to pick up the OnKeyHit to replace contents, but skip GoToLetter
4228       }
4229
4230       if(ch >=32 && ch != 128 && !key.alt && !key.ctrl && GoToLetter(ch, false))
4231       {
4232          /*if(inactive && window.state != Hidden)
4233             NotifyHighlight(master, this, currentRow, 0);
4234          else
4235          {
4236             NotifySelect(master, this, currentRow, 0);
4237          }*/
4238          return false;
4239       }
4240       return true;
4241    }
4242
4243    bool OnKeyHit(Key key, unichar ch)
4244    {
4245       if(!ch && !key.alt && !key.ctrl)
4246       {
4247          key.code = (SmartKey)key.code;
4248       }
4249       if(ch >= 32 && !key.alt && !key.ctrl && ch != 128)
4250       {
4251          DataField field;
4252          if(!currentField || !currentField.editable)
4253             for(field = fields.first; field; field = field.next)
4254             {
4255                if(field.editable)
4256                {
4257                   currentField = field;
4258                   break;
4259                }
4260             }
4261          if(currentField && currentField.editable)
4262          {
4263             if((!editData || !editData.visible) || !editData.active)
4264             {
4265                PopupEditBox(currentField, false);
4266                if(editData && editData.visible)
4267                {
4268                   editData.Activate();
4269                   editData.OnKeyHit(key, ch);
4270                }
4271             }
4272             return false;
4273          }
4274       }
4275
4276       if(editData && editData.visible && ch && !key.alt && !key.ctrl && editData.active && (key.code != tab || (editData._class == class(EditBox) && ((EditBox)editData).tabKey)))
4277          return false;
4278
4279       if(!key.alt && (style.multiSelect || !key.ctrl))
4280       {
4281          switch(key.code)
4282          {
4283             case left:
4284                if(style.alwaysEdit)
4285                {
4286                   if(currentField)
4287                   {
4288                      DataField field;
4289                      for(field = currentField.prev; field; field = field.prev)
4290                      {
4291                         if(field.editable)
4292                         {
4293                            currentField = field;
4294                            HideEditBox(true, true, false);
4295                            PopupEditBox(currentField, false);
4296                            return false;
4297                         }
4298                      }
4299                   }
4300                }
4301                if(style.collapse && currentRow /*&& !currentField*/)  // THIS PREVENTED COLLAPSING THE PROPERTY SHEET
4302                {
4303                   if(currentRow.subRows.first && !currentRow.collapsed)
4304                   {
4305                      currentRow.collapsed = true;
4306                   }
4307                   else if(currentRow.parent)
4308                      SetCurrentRow(currentRow.parent, true);
4309                   return false;
4310                }
4311                break;
4312             case right:
4313                if(style.collapse && currentRow && currentRow.subRows.first)
4314                {
4315                   if(currentRow.collapsed)
4316                      currentRow.collapsed = false;
4317                   else
4318                      SetCurrentRow(currentRow.subRows.first, true);
4319                   return false;
4320                }
4321                else if(style.alwaysEdit)
4322                {
4323                   if(currentField)
4324                   {
4325                      DataField field;
4326                      for(field = currentField.next; field; field = field.next)
4327                      {
4328                         if(field.editable)
4329                         {
4330                            currentField = field;
4331                            HideEditBox(true, true, false);
4332                            PopupEditBox(currentField, false);
4333                            break;
4334                         }
4335                      }
4336                   }
4337                }
4338                break;
4339             case down: case up:
4340             case pageDown: case pageUp:
4341             case end: case home:
4342             {
4343                int headerSize = ((style.header) ? rowHeight : 0);
4344                int height = clientSize.h + 1 - headerSize;
4345                DataRow oldRow;
4346
4347                // true: destroy edit box
4348                // !!! TESTING true HERE !!!
4349                HideEditBox(true, true, false);
4350                // HideEditBox(false, true, false);
4351
4352                oldRow = currentRow;
4353
4354                SNAPDOWN(height, rowHeight);
4355                if((!currentRow || key.code == home) && key.code != end)
4356                {
4357                   currentRow = rows.first;
4358                }
4359                else
4360                {
4361                   DataRow next;
4362                   switch(key.code)
4363                   {
4364                      case down:
4365                         if(!(style.multiSelect) && currentRow && !currentRow.selectedFlag)
4366                            next = currentRow;
4367                         else
4368                            next = currentRow.GetNextRow();
4369                         if(next)
4370                         {
4371                            currentRow = next;
4372                         }
4373                         break;
4374                      case up:
4375                         if(!(style.multiSelect) && currentRow && !currentRow.selectedFlag)
4376                            next = currentRow;
4377                         else
4378                            next = currentRow.GetPrevRow();
4379
4380                         if(next)
4381                         {
4382                            currentRow = next;
4383                         }
4384                         break;
4385                      case end:
4386                         currentRow = lastRow.GetLastRow();
4387                         break;
4388                      case pageUp:
4389                      {
4390                         int c;
4391                         for(c = 0;
4392                         currentRow && (next = currentRow.GetPrevRow()) && c < height / rowHeight;
4393                             c++, currentRow = next);
4394                         break;
4395                      }
4396                      case pageDown:
4397                      {
4398                         int c;
4399                         for(c = 0;
4400                             currentRow && (next = currentRow.GetNextRow()) && c < height / rowHeight;
4401                             c++, currentRow = next);
4402                         break;
4403                      }
4404                   }
4405                }
4406                if(currentRow && currentRow.index * rowHeight > scroll.y + height - rowHeight)
4407                   SetScrollPosition(scroll.x, currentRow.index * rowHeight - height + rowHeight);
4408                else if(!currentRow || currentRow.index * rowHeight < scroll.y)
4409                   SetScrollPosition(scroll.x, currentRow ? currentRow.index * rowHeight : 0);
4410
4411                if(style.multiSelect)
4412                {
4413                   DataRow selRow;
4414
4415                   if(!(key.shift) && (key.ctrl))
4416                   {
4417                      DataRow row;
4418                      for(row = rows.first; row; row = row.GetNextRow())
4419                      {
4420                         if(row.selectedFlag == tempSelected)
4421                            row.selectedFlag = selected;
4422                         else if(row.selectedFlag == tempUnselected)
4423                            row.selectedFlag = unselected;
4424                      }
4425                   }
4426
4427                   if(!(key.ctrl))
4428                   {
4429                      for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
4430                         selRow.selectedFlag = unselected;
4431                   }
4432                   else
4433                   {
4434                      for(selRow = rows.first; selRow; selRow = selRow.GetNextRow())
4435                      {
4436                         if(selRow.selectedFlag == tempUnselected) selRow.selectedFlag = selected;
4437                         else if(selRow.selectedFlag == tempSelected) selRow.selectedFlag = unselected;
4438                      }
4439                   }
4440
4441                   if(key.shift)
4442                   {
4443                      if(currentRow.index >= clickedRow.index)
4444                      {
4445                         for(selRow = clickedRow; selRow; selRow = selRow.GetNextRow())
4446                         {
4447                            if(key.ctrl)
4448                            {
4449                               if(selRow.selectedFlag) selRow.selectedFlag = tempUnselected; else selRow.selectedFlag = tempSelected;
4450                            }
4451                            else
4452                               selRow.selectedFlag = selected;
4453                            if(selRow == currentRow)
4454                               break;
4455                         }
4456                      }
4457                      else
4458                      {
4459                         for(selRow = currentRow; selRow; selRow = selRow.GetNextRow())
4460                         {
4461                            if(key.ctrl)
4462                            {
4463                               if(selRow.selectedFlag) selRow.selectedFlag = tempUnselected; else selRow.selectedFlag = tempSelected;
4464                            }
4465                            else
4466                               selRow.selectedFlag = selected;
4467                            if(selRow == clickedRow)
4468                               break;
4469                         }
4470                      }
4471                   }
4472                   else
4473                   {
4474                      if(!(key.ctrl) && currentRow)
4475                      {
4476                         currentRow.selectedFlag = selected;
4477                      }
4478
4479                      clickedRow = currentRow;
4480                   }
4481                }
4482                else
4483                {
4484                   if(oldRow) oldRow.selectedFlag = unselected;
4485                   if(currentRow) currentRow.selectedFlag = selected;
4486                }
4487
4488                if(currentRow)
4489                {
4490                   if(style.freeSelect)
4491                      NotifyHighlight(master, this, currentRow, 0);
4492                   else
4493                      NotifySelect(master, this, currentRow, 0);
4494
4495                   if(style.alwaysEdit && currentRow)
4496                      currentRow.Edit(currentField /*null*/);
4497                }
4498                Update(null);
4499                return false;
4500             }
4501             case space:
4502             {
4503                if(style.multiSelect && currentRow)
4504                {
4505                   if(currentRow.selectedFlag)
4506                   {
4507                      if(key.ctrl)
4508                         currentRow.selectedFlag = unselected;
4509                   }
4510                   else
4511                      currentRow.selectedFlag = selected;
4512                   Update(null);
4513
4514                   if(style.freeSelect)
4515                      NotifyHighlight(master, this, currentRow, 0);
4516                   else
4517                      NotifySelect(master, this, currentRow, 0);
4518                }
4519                break;
4520             }
4521          }
4522       }
4523
4524       if(!NotifyKeyHit(master, this, currentRow, key, ch))
4525          return false;
4526
4527       if(ch >=32 && ch != 128 && !key.alt && !key.ctrl && GoToLetter(ch, true))
4528       {
4529          /*if(inactive && window.state != Hidden)
4530             return NotifyHighlight(master, this, currentRow, 0);
4531          else
4532          {
4533             return NotifySelect(master, this, currentRow, 0);
4534          }*/
4535          return false;
4536       }
4537       return true;
4538    }
4539
4540    void OnHScroll(ScrollBarAction action, int position, Key key)
4541    {
4542       Update(null);
4543    }
4544
4545    void OnVScroll(ScrollBarAction action, int position, Key key)
4546    {
4547       int y = 0;
4548       DataRow next;
4549
4550       for(firstRowShown = rows.first; firstRowShown; firstRowShown = next)
4551       {
4552          next = firstRowShown.GetNextRow();
4553          if(y >= position || !next) break;
4554
4555          y += rowHeight;
4556       }
4557       Update(null);
4558    }
4559
4560    OldList fields;
4561    OldList rows;
4562    int numFields;
4563    DataRow firstRowShown;
4564    DataRow clickedRow;
4565    DataRow currentRow;
4566    int width;
4567    DataField sortField;
4568    int rowCount;
4569    int rowHeight;
4570    int fontH;
4571    double typingTimeOut;
4572    char * typedString;
4573
4574    int mouseX, mouseY;
4575
4576    Timer timer
4577    {
4578       delay = 0.1;
4579       userData = this;
4580
4581       bool DelayExpired()
4582       {
4583          Modifiers mods { };
4584          if(guiApp.GetKeyState(shift)) mods.shift = true;
4585          if(guiApp.GetKeyState(alt)) mods.alt = true;
4586          if(guiApp.GetKeyState(control)) mods.ctrl = true;
4587          OnMouseMove(MAXINT, MAXINT, mods);
4588
4589          return true;
4590       }
4591    };
4592
4593    Timer typingTimer
4594    {
4595       delay = 0.5; // typingTimeOut
4596       userData = this;
4597
4598       bool DelayExpired()
4599       {
4600          typedString[0] = '\0';
4601
4602          // The next line was commented... Why? When commented typing words stops working ( only first character jumps )
4603          typingTimer.Stop();
4604          return true;
4605       }
4606    };
4607
4608    bool dragging, rolledOver;
4609    int numSelections;
4610    Button endBevel;
4611
4612    // For moving rows
4613    DataRow dragRow;
4614    int dropIndex;
4615    bool movedRow;
4616
4617    // For editing fields
4618    DataBox editData;
4619    DataField currentField;
4620    DataRow editRow;
4621
4622    // For moving fields
4623    DataField draggingField, dropField;
4624    bool movingFields;
4625
4626    // For resizing fields
4627    DataField resizingField;
4628    int resizeX, oldX, startWidth;
4629
4630    ListBoxBits style;
4631    FontResource boldFont;
4632    int maxShown;
4633
4634    // Only used for OnMouseMove so far, for avoiding problems with consequential mouse moves
4635    bool insideNotifySelect;
4636    ColorAlpha selectionColor, selectionText, stippleColor;
4637    stippleColor = 0xFFFFFF80;
4638 };