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