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